[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = [\"run\", \"-p\", \"xtask\", \"--\"]\nbuild-docker-image = [\"xtask\", \"build-docker-image\"]\n"
  },
  {
    "path": ".changes/1006.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"add CentOS7-compatible target for aarch64\",\n    \"issues\": [528]\n}\n"
  },
  {
    "path": ".changes/1018.json",
    "content": "{\n    \"description\": \"deny installation of armhf debian packages for the arm-unknown-linux-gnueabihf target.\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1023.json",
    "content": "{\n    \"description\": \"support different Android NDK, API, and Android versions using Docker build args.\",\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/1024.json",
    "content": "{\n    \"description\": \"`rust-std` is no longer downloaded when using `build-std = true`\",\n    \"type\": \"fixed\",\n    \"breaking\": false\n}"
  },
  {
    "path": ".changes/1028-1132.json",
    "content": "[\n    {\n        \"description\": \"link to libgcc for armv5te-unknown-linux-musleabi.\",\n        \"type\": \"fixed\"\n    },\n    {\n        \"description\": \"add C++ support for FreeBSD targets.\",\n        \"type\": \"added\"\n    },\n    {\n        \"description\": \"test dynamic library support for Android targets in CI.\",\n        \"type\": \"internal\"\n    },\n    {\n        \"description\": \"test partial C++ support for mips64el-unknown-linux-muslabi64 in CI.\",\n        \"type\": \"internal\"\n    },\n    {\n        \"description\": \"convert mips64el-unknown-linux-muslabi64 to a hard-float toolchain to match the rust target.\",\n        \"type\": \"changed\",\n        \"breaking\": true\n    },\n    {\n        \"description\": \"convert mips64el-unknown-linux-muslabi64 to use the mips64r2 architecture, identical to the rust target.\",\n        \"type\": \"changed\",\n        \"breaking\": true\n    },\n    {\n        \"description\": \"convert mips-unknown-linux-musl and mipsel-unknown-linux-musl to use the mips32r2 architecture, identical to the rust targets.\",\n        \"type\": \"changed\",\n        \"breaking\": true\n    }\n]\n"
  },
  {
    "path": ".changes/1032.json",
    "content": "{\n    \"description\": \"allow disabling buildkit for container engines lacking buildkit support.\",\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/1033.json",
    "content": "{\n    \"description\": \"fix --cache-from using podman.\",\n    \"type\": \"fixed\",\n    \"issues\": [1031]\n}\n"
  },
  {
    "path": ".changes/1038-1220-1482.json",
    "content": "[\n    {\n        \"description\": \"bump MSRV to 1.77.2\",\n        \"type\": \"internal\"\n    }\n]\n"
  },
  {
    "path": ".changes/1039.json",
    "content": "{\n    \"description\": \"support overlay and fuse-overlayfs storage drivers\",\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/1049-1142.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"stop parsing arguments to `cross run` after `--`\",\n    \"issues\": [1048, 1141]\n}\n"
  },
  {
    "path": ".changes/1054.json",
    "content": "{\n    \"type\": \"internal\",\n    \"description\": \"change the unique container ID to be unique based off the toolchain and system time.\"\n}\n"
  },
  {
    "path": ".changes/1057.json",
    "content": "{\n    \"description\": \"fix mount paths outside of the workspace mount directory on Windows and those provided as a WSL path.\",\n    \"type\": \"fixed\",\n    \"issues\": [1145, 1156]\n}\n"
  },
  {
    "path": ".changes/1063-1125-1134.json",
    "content": "[\n    {\n        \"type\": \"changed\",\n        \"description\": \"changed musl targets to use static-pie linkage by default, consistent with Alpine.\",\n        \"breaking\": true\n    },\n    {\n        \"type\": \"fixed\",\n        \"description\": \"fixed C++ support for musl targets.\",\n        \"issues\": [902],\n        \"breaking\": true\n    },\n    {\n        \"type\": \"fixed\",\n        \"description\": \"use a linker script for musl libstdc++ to ensure the archive links to libc, libm, and libgcc as needed.\",\n        \"issues\": [1124]\n    }\n]\n"
  },
  {
    "path": ".changes/1073.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"passthrough cross environment variables by default\"\n}\n"
  },
  {
    "path": ".changes/1078.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"Fix custom image names for images with a trailing '-' character.\\ncustom images in packages with the `^[^0-9-].*([^A-Za-z_]*-)|(-[^A-Za-z_]*)$` package name pattern are now supported.\",\n    \"issues\": [1077]\n}\n"
  },
  {
    "path": ".changes/1084.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"fixed remote docker data volume paths on windows\",\n    \"issues\": [1081]\n}\n"
  },
  {
    "path": ".changes/1085.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"support custom toolchains without rustup.\"\n}\n"
  },
  {
    "path": ".changes/1105.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"explicitly prefer `-ar` to `-gcc-ar`.\",\n    \"issues\": [1100]\n}\n"
  },
  {
    "path": ".changes/1112.json",
    "content": "[\n    {\n        \"description\": \"fixed CMake support for Android and newlib targets.\",\n        \"type\": \"fixed\",\n        \"issues\": [1110]\n    },\n    {\n        \"description\": \"added C++ support for newlib targets.\",\n        \"type\": \"added\"\n    }\n]\n"
  },
  {
    "path": ".changes/1118.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"added ARMv8-M newlib targets.\",\n    \"issues\": [1116]\n}\n"
  },
  {
    "path": ".changes/1123.json",
    "content": "{\n    \"description\": \"support external C/C++ dependencies using C11/C++11 threads for MinGW targets by switching linkers from `*-gcc` to `*gcc-posix`.\",\n    \"type\": \"added\",\n    \"issues\": [1122],\n    \"breaking\": true\n}\n"
  },
  {
    "path": ".changes/1138.json",
    "content": "{\n    \"description\": \"explicitly specify ar for all toolchains\",\n    \"type\": \"changed\",\n    \"issues\": [1137]\n}\n"
  },
  {
    "path": ".changes/1159.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"use `[workspace.metadata.cross]` as an alternative to `Cross.toml`\"\n}\n"
  },
  {
    "path": ".changes/1160.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"don't warn when toolchain version is explicitly provided.\",\n    \"issues\": [1148]\n}\n"
  },
  {
    "path": ".changes/1164.json",
    "content": "{\n    \"type\": \"internal\",\n    \"description\": \"add fallback mirrors for FreeBSD packages.\",\n    \"issues\": [1162]\n}\n"
  },
  {
    "path": ".changes/1166.json",
    "content": "{\n    \"description\": \"freebsd: include memstat in build image to fix build with libc 0.2.138 and up.\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1183.json",
    "content": "{\n    \"description\": \"resolve issue when using `pre-build` and `image.toolchain` in `Cargo.toml`\",\n    \"type\": \"fixed\",\n    \"issues\": [1182]\n}\n"
  },
  {
    "path": ".changes/1199.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"use current target_dir path when copying build artifacts back\",\n    \"issues\": [1103]\n}\n"
  },
  {
    "path": ".changes/1207.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"properly copy directories when using `CROSS_REMOTE`\",\n    \"issues\": [1206]\n}\n"
  },
  {
    "path": ".changes/1220.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"don't pass `--target-dir` in remote context when it's not needed\",\n    \"issues\": [1218]\n}\n"
  },
  {
    "path": ".changes/1246.json",
    "content": "{\n    \"description\": \"fix support for bare metal targets other than thumb\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1248.json",
    "content": "{\n    \"description\": \"customize `--cache-from` and `--cache-to` options for `build-docker-image`\",\n    \"type\": \"internal\"\n}\n"
  },
  {
    "path": ".changes/1260.json",
    "content": "{\n    \"description\": \"fix podman bind mounts on macOS by removing SELinux labels.\",\n    \"issues\": [756],\n    \"type\": \"fixed\",\n    \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/1265.json",
    "content": "{\n    \"description\": \"replace `ctrlc` signal handler with `signal-hook`\",\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1271.json",
    "content": "{\n    \"description\": \"add aarch64-unknown-freebsd image.\",\n    \"type\": \"added\",\n    \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/1280.json",
    "content": "{\n  \"description\": \"Allow to run arbitrary commands in containers using `cross-util run ...`\",\n  \"issues\": [1266],\n  \"type\": \"added\",\n  \"breaking\": false\n}"
  },
  {
    "path": ".changes/1317.json",
    "content": "{\n  \"description\": \"Re-add PKG_CONFIG_PATH for the arm-unknown-linux-gnueabihf target.\",\n  \"issues\": [1316],\n  \"type\": \"fixed\",\n  \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/1325.json",
    "content": "{\n    \"description\": \"create CACHEDIR.TAG during custom docker build\",\n    \"type\": \"fixed\",\n    \"issues\": [1324]\n}\n"
  },
  {
    "path": ".changes/1330-1349.json",
    "content": "{\n    \"description\": \"update cargo-zigbuild from 0.17.2 to 0.17.3\",\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1333.json",
    "content": "{\n    \"description\": \"set arm-unknown-linux-gnueabihf to use glibc 2.31\",\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1340.json",
    "content": "{\n    \"description\": \"don't error when a non-provided target is used with only a dockerfile specified\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1342.json",
    "content": "[\n    {\n        \"description\": \"fix `--list` showing cross commands for the host\",\n        \"type\": \"fixed\"\n    },\n    {\n        \"description\": \"add `rustdoc` as a supported cargo subcommand\",\n        \"type\": \"added\"\n    }\n]\n"
  },
  {
    "path": ".changes/1346.json",
    "content": "{\n  \"description\": \"bump musl to 1.2.3, like done in rust 1.71\",\n  \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1348.json",
    "content": "{\n  \"description\": \"add `libexecinfo.so` in netbsd\",\n  \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/1373.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"fix wineboot silently failing on qemu\",\n    \"issues\": [1372]\n}\n"
  },
  {
    "path": ".changes/1374-1490.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"update wine to 9.0.0.0\",\n    \"issues\": [1372]\n}\n"
  },
  {
    "path": ".changes/1385.json",
    "content": "{\n    \"description\": \"Set PKG_CONFIG_PATH in the FreeBSD Dockerfile.\",\n    \"issues\": [1384],\n    \"type\": \"fixed\"\n}\n\n"
  },
  {
    "path": ".changes/1391.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"Add `-idirafter/usr/include` to bindgen's clang invocation on GNU/Linux\",\n    \"issues\": [1389]\n}\n"
  },
  {
    "path": ".changes/1399.json",
    "content": "{\n  \"description\": \"fix creating `initrd` when using debian `ports` mirror and compressed kernel modules\",\n  \"issues\": [1399],\n  \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1403-1411.json",
    "content": "[{\n    \"type\": \"fixed\",\n    \"description\": \"switch to freebsd 13.2 following 12.4 EoL\",\n    \"issues\": [1390]\n},\n {\n     \"type\": \"added\",\n     \"description\": \"Added helper script `/freebsd-install-package.sh` and friends to install freebsd packages, see [wiki](https://github.com/cross-rs/cross/wiki/FAQ#custom-images)\"\n }]\n"
  },
  {
    "path": ".changes/1420.json",
    "content": "{\n  \"description\": \"set the required environment variables in the runner\",\n  \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1458.json",
    "content": "{\n  \"description\": \"add gfortran for target *-gnu*, *-musl*, *-freebsd*, *-solaris*, *-dragonfly*, *-illumos*, *-netbsd*\",\n  \"issues\": [1457],\n  \"type\": \"added\"\n\n}\n"
  },
  {
    "path": ".changes/1465.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"add loongarch64 support\",\n    \"issues\": [1404]\n}\n"
  },
  {
    "path": ".changes/1466.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"Upgrade qemu and integrate qemu-user runners for loongarch64-linux-gnu\",\n    \"issues\": [1467]\n}\n"
  },
  {
    "path": ".changes/1468.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"use defconfig for ct-ng, minimizing the config\",\n    \"issues\": [1335]\n}\n"
  },
  {
    "path": ".changes/1481.json",
    "content": "{\n    \"description\": \"allow pass-through environment variables to contain numbers\",\n    \"type\": \"fixed\"\n}"
  },
  {
    "path": ".changes/1483.json",
    "content": "{\n    \"description\": \"Fix paths when using `CROSS_CONTAINER_IN_CONTAINER`\",\n    \"type\": \"fixed\"\n}"
  },
  {
    "path": ".changes/1485.json",
    "content": "{\n    \"description\": \"Use `/proc/self/mountinfo` as a fallback for `docker inspect` if using `HOSTNAME` fails\",\n    \"issues\": [1321],\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1488.json",
    "content": "{\n    \"description\": \"Allow `build-std` to take an array of crate names\",\n    \"issues\": [896],\n    \"type\": \"changed\",\n\t\"breaking\": true\n}\n"
  },
  {
    "path": ".changes/1489.json",
    "content": "{\n    \"description\": \"Simplify config internals\",\n    \"type\": \"internal\"\n}\n"
  },
  {
    "path": ".changes/1491.json",
    "content": "{\n    \"description\": \"Allow specifying only a tag or subtarget for images in config\",\n    \"issues\": [1169],\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1494.json",
    "content": "{\n    \"description\": \"Fix `zig.image` precedence\",\n    \"type\": \"fixed\",\n    \"breaking\": true\n}\n"
  },
  {
    "path": ".changes/1495.json",
    "content": "{\n    \"description\": \"Fix `*-solaris` targets\",\n    \"issues\": [1424],\n    \"type\": \"fixed\",\n    \"breaking\": true\n}\n"
  },
  {
    "path": ".changes/1525.json",
    "content": "{\n    \"description\": \"Fix riscv64gc-unknown-linux-gnu target\",\n    \"issues\": [1423],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1540.json",
    "content": "{\n    \"description\": \"Upgrade the crosstool for the loongarch64-unknown-linux-gnu target\",\n    \"issues\": [1538],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1557.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"add loongarch64-linux-musl support\",\n    \"issues\": [1556]\n}\n"
  },
  {
    "path": ".changes/1574.json",
    "content": "{\n  \"type\": \"fixed\",\n  \"description\": \"`dockerfile` path is now relative to cargo workspace root\"\n}\n"
  },
  {
    "path": ".changes/1623.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"fixed an issue where cross would look for metadata.cross outside the workspace\"\n  }\n"
  },
  {
    "path": ".changes/1625.json",
    "content": "{\n    \"description\": \"bump ct-ng to 1.27.0\",\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1630.json",
    "content": "{\n  \"type\": \"added\",\n  \"description\": \"Add docker platform support for `linux/arm64/v8` target for many Ubuntu-based targets\"\n}\n"
  },
  {
    "path": ".changes/1641.json",
    "content": "{\n    \"description\": \"Update binutils to 2.38 in illumos and solaris\",\n    \"issues\": [1639],\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/1647.json",
    "content": "{\n    \"description\": \"use `-q` when gathering the installed toolchains\",\n    \"issues\": [1645],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/1651.json",
    "content": "{\n    \"description\": \"Fix BINDGEN_EXTRA_CLANG_ARGS for x86_64-unknown-illumos\",\n    \"issues\": [1644],\n    \"type\": \"fixed\",\n    \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/1664.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"add riscv64gc-unknown-linux-musl support\",\n    \"issues\": [1197]\n}\n"
  },
  {
    "path": ".changes/1674.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"use zstd to extract the compressed file as FreeBSD do so. also, bump the gcc version\"\n}\n"
  },
  {
    "path": ".changes/1681.json",
    "content": "{\n    \"description\": \"also recognize active and default as installed toolchains\",\n    \"issues\": [1645, 1678],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/554.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"add the `--force-non-host` flag for newer rustup versions\",\n    \"issues\": [536]\n}\n"
  },
  {
    "path": ".changes/589.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"Ensure podman builds use non-interactive package installs.\",\n    \"issues\": [589]\n}\n"
  },
  {
    "path": ".changes/591-1095.json",
    "content": "[\n    {\n        \"description\": \"update Ubuntu images to 20.04 LTS.\",\n        \"type\": \"changed\",\n        \"breaking\": true,\n        \"issues\": [417, 517, 556, 616]\n    },\n    {\n        \"description\": \"remove Linux image from `mips-unknown-linux-gnu`.\",\n        \"type\": \"removed\",\n        \"breaking\": true\n    },\n    {\n        \"type\": \"changed\",\n        \"description\": \"upgraded most linux images to use a 5.x kernel instead of a 4.x kernel.\",\n        \"breaking\": true\n    },\n    {\n        \"type\": \"changed\",\n        \"description\": \"updated powerpc64, riscv64, and sparc64 `*-linux-gnu` images to use a 6.x kernel instead of a 4.x kernel.\",\n        \"breaking\": true,\n        \"issues\": [1094]\n    }\n]\n"
  },
  {
    "path": ".changes/661.json",
    "content": "{\n    \"description\": \"When cross has given a warning, it will now quit instead of continuing with `cargo` when run in CI or with `CROSS_NO_WARNINGS=1`\",\n    \"breaking\": true,\n    \"type\": \"changed\"\n}\n"
  },
  {
    "path": ".changes/817.json",
    "content": "[\n  {\n    \"description\": \"Images can now specify a certain toolchain via `target.{target}.image.toolchain`\",\n    \"breaking\": true,\n    \"type\": \"added\"\n  },\n  {\n    \"description\": \"made `cross +channel` parsing more compliant to parsing a toolchain\",\n    \"type\": \"fixed\"\n  },\n  {\n    \"description\": \"`pre-build` and `dockerfile` now uses buildx/buildkit\",\n    \"breaking\": true,\n    \"type\": \"changed\"\n  }\n]\n"
  },
  {
    "path": ".changes/880-1111.json",
    "content": "{\n    \"description\": \"added a zig-based image (v0.10.0), allowing multiple targets to be built from the same image, using cargo-zigbuild (v0.14.1).\",\n    \"type\": \"added\",\n    \"issues\": [860, 1109]\n}\n"
  },
  {
    "path": ".changes/918.json",
    "content": "{\n    \"description\": \"use JSON-based files to autogenerate CHANGELOG.md\",\n    \"issues\": [662],\n    \"type\": \"internal\"\n}\n"
  },
  {
    "path": ".changes/931.json",
    "content": "{\n    \"description\": \"deny installation of debian packages that conflict with our cross-compiler toolchains.\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/934.json",
    "content": "{\n    \"type\": \"added\",\n    \"description\": \"add support for pre-build hooks with remote docker\",\n    \"issues\": [928]\n}\n"
  },
  {
    "path": ".changes/937.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"support Windows-style and absolute manifest paths\",\n    \"issues\": [935]\n}\n"
  },
  {
    "path": ".changes/939.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"Remove `/project` mounting, instead always mount in the same path as on the host\",\n    \"issues\": [938]\n}\n"
  },
  {
    "path": ".changes/942.json",
    "content": "[\n    {\n        \"description\": \"use non-canonical paths for mount locations.\",\n        \"type\": \"changed\",\n        \"issues\": [920]\n    },\n    {\n        \"description\": \"fixed DeviceNS drive parsing in creating WSL-style paths on windows.\",\n        \"type\": \"fixed\"\n    },\n    {\n        \"description\": \"fixed the environment variable name for mounted volumes.\",\n        \"type\": \"fixed\"\n    }\n]\n"
  },
  {
    "path": ".changes/945.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"fix pre-build hook image naming with podman\",\n    \"issues\": [944]\n}\n"
  },
  {
    "path": ".changes/947.json",
    "content": "[\n    {\n        \"type\": \"internal\",\n        \"description\": \"resolve symlinks to cargo and xargo home before mounting\",\n        \"issues\": [373]\n    },\n    {\n        \"type\": \"fixed\",\n        \"description\": \"mount cargo, xargo, and the sysroot at the same path as on the host to avoid unnecessary recompilation.\",\n        \"issues\": [551]\n    }\n]\n"
  },
  {
    "path": ".changes/952.json",
    "content": "{\n    \"description\": \"run non-x86 binaries natively if on a compatible host.\",\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/955.json",
    "content": "{\n    \"description\": \"Update QEMU to version 7.0.0 for Ubuntu targets\",\n    \"type\": \"changed\",\n    \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/956.json",
    "content": "{\n  \"description\": \"use Wine from Ubuntu 20.04 WineHQ repo\",\n  \"type\": \"changed\",\n  \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/957-1393.json",
    "content": "{\n    \"description\": \"use latest Debian kernel images\",\n    \"type\": \"changed\",\n    \"breaking\": false\n}\n"
  },
  {
    "path": ".changes/962.json",
    "content": "{\n    \"description\": \"fix SELinux labels to allow use in multiple containers and/or the host filesystem.\",\n    \"type\": \"fixed\",\n    \"issues\": [961]\n}\n"
  },
  {
    "path": ".changes/964.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"don't keep stdin open when running containers.\"\n}\n"
  },
  {
    "path": ".changes/968.json",
    "content": "{\n    \"description\": \"only print rustup --verbose if `-vv` or `CROSS_VERBOSE=1` is used\",\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/974.json",
    "content": "[\n    {\n        \"description\": \"change `mips64-unknown-linux-muslabi64` target to hard-float target.\",\n        \"type\": \"fixed\",\n        \"issues\": [906]\n    },\n    {\n        \"description\": \"build static libgcc and link to static libgcc for `mips64-unknown-linux-muslabi64` target.\",\n        \"type\": \"fixed\"\n    }\n]\n"
  },
  {
    "path": ".changes/982.json",
    "content": "{\n    \"type\": \"internal\",\n    \"description\": \"use generic dockerfiles for when the toolchain and image platfom match.\"\n}\n"
  },
  {
    "path": ".changes/984.json",
    "content": "{\n    \"description\": \"added linux images for `riscv64gc-unknown-linux-gnu` to allow full system emulation.\",\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/987.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"link to libgcc for `armv5te-unknown-linux-musleabi` to fix missing `__sync_X_and_fetch` builtins.\",\n    \"issues\": [367]\n}\n"
  },
  {
    "path": ".changes/989.json",
    "content": "{\n    \"type\": \"changed\",\n    \"description\": \"add default nix_store value to solve nix-related issues\",\n    \"issues\": [260]\n}\n"
  },
  {
    "path": ".changes/994.json",
    "content": "{\n    \"type\": \"fixed\",\n    \"description\": \"fixed wrong path used for target when pre-building in container in container mode\",\n    \"issues\": [993]\n}\n"
  },
  {
    "path": ".changes/README.md",
    "content": "# Changes\n\nThis stores changes to automatically generate the changelog, to avoid merge conflicts. Files should be in a JSON format, with the following format:\n\n```json\n{\n    \"description\": \"single-line description to add to the CHANGELOG.\",\n    \"issues\": [894],\n    \"type\": \"added\",\n    \"breaking\": false\n}\n```\n\nValid types are:\n- added (Added)\n- changed (Changed)\n- fixed (Fixed)\n- removed (Removed)\n- internal (Internal)\n\n`breaking` is optional and defaults to false. if `breaking` is present for any active changes, a `BREAKING:` notice will be added at the start of the entry. `issues` is also optional, and is currently unused, and is an array of issues fixed by the PR, and defaults to an empty array.\n\nThe file numbers should be `${pr}.json`. The `pr` is optional, and if not, an issue number should be used, in the `_${issue}.json` format. We also support multiple PRs per entry, using the `${pr1}-${pr2}-(...).json` format.\n\nIf multiple changes are made in a single PR, you can also pass an array of entries:\n\n```json\n[\n    {\n        \"description\": \"this is one added entry.\",\n        \"issues\": [630],\n        \"type\": \"added\"\n    },\n    {\n        \"description\": \"this is another added entry.\",\n        \"issues\": [642],\n        \"type\": \"added\"\n    },\n    {\n        \"description\": \"this is a fixed entry that has no attached issue.\",\n        \"type\": \"fixed\"\n    }\n]\n```\n\nSee [template](/.changes/template) for sample object and array-based changes.\n"
  },
  {
    "path": ".changes/template/940.json",
    "content": "[\n    {\n        \"description\": \"this is one added entry.\",\n        \"issues\": [630],\n        \"type\": \"added\"\n    },\n    {\n        \"description\": \"this is another added entry.\",\n        \"issues\": [642],\n        \"type\": \"added\"\n    },\n    {\n        \"description\": \"this is a fixed entry that has no attached issue.\",\n        \"type\": \"fixed\"\n    },\n    {\n        \"description\": \"this is a breaking change.\",\n        \"issues\": [679],\n        \"breaking\": true,\n        \"type\": \"changed\"\n    }\n]\n"
  },
  {
    "path": ".changes/template/978.json",
    "content": "{\n    \"description\": \"sample description for a PR adding one CHANGELOG entry.\",\n    \"issues\": [437],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".changes/template/979-981.json",
    "content": "{\n    \"description\": \"this has 2 PRs associated.\",\n    \"issues\": [441],\n    \"type\": \"added\"\n}\n"
  },
  {
    "path": ".changes/template/CHANGELOG.md",
    "content": "# Change Log\n\nThis is a template changelog. This represents an older state of this repository, used to test parsing/formatting.\n\n<!-- next-header -->\n\n## [Unreleased] - ReleaseDate\n\n### Added\n\n- #905 - added `qemu-runner` for musl images, allowing use of native or emulated runners.\n- #905 - added qemu emulation to `i586-unknown-linux-gnu`, `i686-unknown-linux-musl`, and `i586-unknown-linux-gnu`, so they can run on an `x86` CPU, rather than an `x86_64` CPU.\n- #900 - add the option to skip copying build artifacts back to host when using remote cross via `CROSS_REMOTE_SKIP_BUILD_ARTIFACTS`.\n- #891 - support custom user namespace overrides by setting the `CROSS_CONTAINER_USER_NAMESPACE` environment variable. \n- #890 - support rootless docker via the `CROSS_ROOTLESS_CONTAINER_ENGINE` environment variable.\n- #878 - added an image `ghcr.io/cross-rs/cross` containing cross.\n\n### Changed\n\n- #869 - ensure cargo configuration environment variable flags are passed to the docker container.\n- #859 - added color diagnostic output and error messages.\n\n### Fixed\n\n- #905 - fixed running dynamically-linked libraries for all musl targets except `x86_64-unknown-linux-musl`.\n- #904 - ensure `cargo metadata` works by using the same channel.\n- #904 - fixed the path for workspace volumes and passthrough volumes with docker-in-docker.\n- #898 - fix the path to the mount root with docker-in-docker if mounting volumes.\n- #897 - ensure `target.$(...)` config options override `build` ones when parsing strings and vecs.\n- #895 - convert filenames in docker tags to ASCII lowercase and ignore invalid characters\n- #885 - handle symlinks when using remote docker.\n- #868 - ignore the `CARGO` environment variable.\n- #867 - fixed parsing of `build.env.passthrough` config values.\n\n## [v0.2.2] - 2022-06-24\n\n### Added\n\n- #803 - added `CROSS_CUSTOM_TOOLCHAIN` to disable automatic installation of components for use with tools like `cargo-bisect-rustc`\n- #795 - added images for additional toolchains maintained by cross-rs.\n- #792 - added `CROSS_CONTAINER_IN_CONTAINER` environment variable to replace `CROSS_DOCKER_IN_DOCKER`.\n- #785 - added support for remote container engines through data volumes through setting the `CROSS_REMOTE` environment variable. also adds in utility commands to create and remove persistent data volumes.\n- #782 - added `build-std` config option, which builds the rust standard library from source if enabled.\n- #678 - Add optional `target.{target}.dockerfile[.file]`, `target.{target}.dockerfile.context` and `target.{target}.dockerfile.build-args` to invoke docker/podman build before using an image.\n- #678 - Add `target.{target}.pre-build` config for running commands before building the image.\n- #772 - added `CROSS_CONTAINER_OPTS` environment variable to replace `DOCKER_OPTS`.\n- #767, #788 - added the `cross-util` and `xtask` commands.\n- #842 - Add `Cargo.toml` as configuration source\n- #745 - added `thumbv7neon-*` targets.\n- #741 - added `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-musleabi` targets.\n- #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`.\n- #719 - add `--list` to known subcommands.\n- #681 - Warn on unknown fields and confusable targets\n- #624 - Add `build.default-target`\n- #647 - Add `mips64-unknown-linux-muslabi64` and `mips64el-unknown-linux-muslabi64` support\n- #543 - Added environment variables to control the UID and GID in the container\n- #524 - docker: Add Nix Store volume support\n- Added support for mounting volumes.\n- #684 - Enable cargo workspaces to work from any path in the workspace, and make path dependencies mount seamlessly. Also added support for private SSH dependencies.\n\n### Changed\n\n- #838 - re-enabled the solaris targets.\n- #807 - update Qemu to 6.1.0 on images using Ubuntu 18.04+ with python3.6+.\n- #775 - forward Cargo exit code to host\n- #762 - re-enabled `x86_64-unknown-dragonfly` target.\n- #747 - reduced android image sizes.\n- #746 - limit image permissions for android images.\n- #377 - update WINE versions to 7.0.\n- #734 - patch `arm-unknown-linux-gnueabihf` to build for ARMv6, and add architecture for crosstool-ng-based images.\n- #709 - Update Emscripten targets to `emcc` version 3.1.10\n- #707, #708 - Set `BINDGEN_EXTRA_CLANG_ARGS` environment variable to pass sysroot to `rust-bindgen`\n- #696 - bump freebsd to 12.3\n- #629 - Update Android NDK version and API version\n- #497 - don't set RUSTFLAGS in aarch64-musl image\n- #492 - Add cmake to FreeBSD images\n- #748 - allow definitions in the environment variable passthrough\n\n### Fixed\n\n- #836 - write a `CACHEDIR.TAG` when creating the target directory, similar to `cargo`.\n- #804 - allow usage of env `CARGO_BUILD_TARGET` as an alias for `CROSS_BUILD_TARGET`\n- #792 - fixed container-in-container support when using podman.\n- #781 - ensure `target.$(...)` config options override `build` ones.\n- #771 - fix parsing of `DOCKER_OPTS`.\n- #727 - add `PKG_CONFIG_PATH` to all `*-linux-gnu` images.\n- #722 - boolean environment variables are evaluated as truthy or falsey.\n- #720 - add android runner to preload `libc++_shared.so`.\n- #725 - support `CROSS_DEBUG` and `CROSS_RUNNER` on android images.\n- #714 - use host target directory when falling back to host cargo.\n- #713 - convert relative target directories to absolute paths.\n- #501 (reverted, see #764) - x86_64-linux: lower glibc version requirement to 2.17 (compatible with centos 7)\n- #500 - use runner setting specified in Cross.toml\n- #498 - bump linux-image version to fix CI\n- Re-enabled `powerpc64-unknown-linux-gnu` image\n- Re-enabled `sparc64-unknown-linux-gnu` image\n- #582 - Added `libprocstat.so` to FreeBSD images\n- #665 - when not using [env.volumes](https://github.com/cross-rs/cross#mounting-volumes-into-the-build-environment), mount project in /project\n- #494 - Parse Cargo's --manifest-path option to determine mounted docker root\n\n### Removed\n\n- #718 - remove deb subcommand.\n\n### Internal\n\n- #856 - remove use of external wslpath and create internal helper that properly handles UNC paths.\n- #828 - assume paths are Unicode and provide better error messages for path encoding errors.\n- #787 - add installer for git hooks.\n- #786, #791 - Migrate build script to rust: `cargo build-docker-image $TARGET`\n- #730 - make FreeBSD builds more resilient.\n- #670 - Use serde for deserialization of Cross.toml\n- Change rust edition to 2021 and bump MSRV for the cross binary to 1.58.1\n- #654 - Use color-eyre for error reporting\n- #658 - Upgrade dependencies\n- #652 - Allow trying individual targets via bors.\n- #650 - Improve Docker caching.\n- #609 - Switch to Github Actions and GHCR.\n- #588 - fix ci: bump openssl version in freebsd again\n- #552 - Added CHANGELOG.md automation\n- #534 - fix image builds with update of dependencies\n- #502 - fix ci: bump openssl version in freebsd\n- #489 - Add support for more hosts and simplify/unify host support checks\n- #477 - Fix Docker/Podman links in README\n- #476 - Use Rustlang mirror for Sabotage linux tarballs\n- Bump nix dependency to `0.22.1`\n- Bump musl version to 1.1.24.\n\n## [v0.2.1] - 2020-06-30\n\n- Disabled `powerpc64-unknown-linux-gnu` image.\n- Disabled `sparc64-unknown-linux-gnu` image.\n- Disabled `x86_64-unknown-dragonfly` image.\n- Removed CI testing for `i686-apple-darwin`.\n\n## [v0.2.0] - 2020-02-22\n\n- Removed OpenSSL from all images.\n- Added support for Podman.\n- Bumped all images to at least Ubuntu 16.04.\n\n## [v0.1.16] - 2019-09-17\n\n- Bump OpenSSL version to 1.0.2t.\n- Re-enabled `asmjs-unknown-emscripten` target.\n- Default to `native` runner instead of `qemu-user` for certain targets.\n\n## [v0.1.15] - 2019-09-04\n\n- Images are now hosted at https://hub.docker.com/r/rustembedded/cross.\n- Bump OpenSSL version to 1.0.2p.\n- Bump musl version to 1.1.20.\n- Bump Ubuntu to 18.04 to all musl targets.\n- Bump gcc version to 6.3.0 for all musl targets.\n- OpenSSL support for the `arm-unknown-linux-musleabi` target.\n- OpenSSL support for the `armv7-unknown-linux-musleabihf` target.\n- Build and test support for `aarch64-unknown-linux-musl`, `arm-unknown-linux-musleabihf`,\n  `armv5te-unknown-linux-musleabi`, `i586-unknown-linux-musl`, `mips-unknown-linux-musl`,\n  add `mipsel-unknown-linux-musl` targets.\n\n## [v0.1.14] - 2017-11-22\n\n### Added\n\n- Support for the `i586-unknown-linux-gnu` target.\n\n### Changed\n\n- Downgraded the Solaris toolchains from 2.11 to 2.10 to make the binaries produced by Cross more\n  compatible (this version matches what rust-lang/rust is using).\n\n## [v0.1.13] - 2017-11-08\n\n### Added\n\n- Support for the custom [`deb`] subcommand.\n\n[`deb`]: https://github.com/mmstick/cargo-deb\n\n- Partial `test` / `run` support for android targets. Using the android API via `cross run` / `cross test` is _not_ supported because Cross is using QEMU instead of the official Android emulator.\n\n- Partial support for the `sparcv9-sun-solaris` and `x86_64-sun-solaris` targets. `cross test` and\n  `cross run` doesn't work for these new targets.\n\n- OpenSSL support for the `i686-unknown-linux-musl` target.\n\n### Changed\n\n- Bump OpenSSL version to 1.0.2m.\n\n## [v0.1.12] - 2017-09-22\n\n### Added\n\n- Support for `cross check`. This subcommand won't use any Docker container.\n\n### Changed\n\n- `binfmt_misc` is not required on the host for toolchain v1.19.0 and newer.\n  With these toolchains `binfmt_misc` interpreters don't need to be installed\n  on the host saving a _privileged_ docker run which some systems don't allow.\n\n## [v0.1.11] - 2017-06-10\n\n### Added\n\n- Build and test support for `i686-pc-windows-gnu`, `x86_64-pc-windows-gnu`,\n  `asmjs-unknown-emscripten` and `wasm-unknown-emscripten`.\n\n- Build support for `aarch64-linux-android`, `arm-linux-androideabi`,\n  `armv7-linux-androideabi`, `x86_64-linux-android` and `i686-linux-android`\n\n- A `build.env.passthrough` / `build.target.*.passthrough` option to Cross.toml\n  to support passing environment variables from the host to the Docker image.\n\n### Changed\n\n- Bumped OpenSSL version to 1.0.2k\n- Bumped QEMU version to 2.9.0\n\n## [v0.1.10] - 2017-04-02\n\n### Added\n\n- Cross compilation support for `x86_64-pc-windows-gnu`\n\n- Cross compilation support for Android targets\n\n### Changed\n\n- Bumped OpenSSL version to 1.0.2k\n\n## [v0.1.9] - 2017-02-08\n\n### Added\n\n- Support for ARM MUSL targets.\n\n### Changed\n\n- The automatic lockfile update that happens every time `cross` is invoked\n  should no longer hit the network when there's no git dependency to add/update.\n\n- The QEMU_STRACE variable is passed to the underlying Docker container. Paired\n  with `cross run`, this lets you get a trace of system call from the execution\n  of \"foreign\" (non x86_64) binaries.\n\n## [v0.1.8] - 2017-01-21\n\n### Added\n\n- Support for custom targets. Cross will now also try to use a docker image for\n  them. As with the built-in targets, one can override the image using\n  `[target.{}.image]` in Cross.toml.\n\n### Changed\n\n- Moved to a newer Xargo: v0.3.5\n\n## [v0.1.7] - 2017-01-19\n\n### Changed\n\n- Moved to a newer Xargo: v0.3.4\n\n### Fixed\n\n- QEMU interpreters were being register when not required, e.g. for the\n  `x86_64-unknown-linux-gnu` target.\n\n## [v0.1.6] - 2017-01-14\n\n### Fixed\n\n- Stable releases were picking the wrong image (wrong tag: 0.1.5 instead of\n  v0.1.5)\n\n## [v0.1.5] - 2017-01-14 [YANKED]\n\n### Added\n\n- `cross run` support for the thumb targets.\n\n- A `build.xargo` / `target.$TARGET.xargo` option to Cross.toml to use Xargo\n  instead of Cargo.\n\n- A `target.$TARGET.image` option to override the Docker image used for\n  `$TARGET`.\n\n- A `sparc64-unknown-linux-gnu` environment.\n\n- A `x86_64-unknown-dragonfly` environment.\n\n### Changed\n\n- Building older versions (<0.7.0) of the `openssl` crate is now supported.\n\n- Before Docker is invoked, `cross` will _always_ (re)generate the lockfile to\n  avoid errors later on due to read/write permissions. This removes the need to\n  call `cargo generate-lockfile` before `cross` in _all_ cases.\n\n## [v0.1.4] - 2017-01-07\n\n### Added\n\n- Support for the `arm-unknown-linux-gnueabi` target\n\n- `cross build` support for:\n  - `i686-unknown-freebsd`\n  - `x86_64-unknown-freebsd`\n  - `x86_64-unknown-netbsd`\n\n### Changed\n\n- It's no longer necessary to call `cargo generate-lockfile` before using\n  `cross` as `cross` will now take care of creating a lockfile when necessary.\n\n- The C environments for the `thumb` targets now include newlib (`libc.a`,\n  `libm.a`, etc.)\n\n### Fixed\n\n- A segfault when `cross` was trying to figure out the name of the user that\n  called it.\n\n## [v0.1.3] - 2017-01-01\n\n### Changed\n\n- Fix the `i686-unknown-linux-musl` target\n\n## [v0.1.2] - 2016-12-31\n\n### Added\n\n- Support for `i686-unknown-linux-musl`\n- Support for `cross build`ing crates for the `thumbv*-none-eabi*` targets.\n\n## [v0.1.1] - 2016-12-28\n\n### Added\n\n- Support for `x86_64-unknown-linux-musl`\n- Print shell commands when the verbose flag is used.\n- Support crossing from x86_64 osx to i686 osx\n\n## v0.1.0 - 2016-12-26\n\n- Initial release. Supports 12 targets.\n\n<!-- prettier-ignore-start -->\n<!-- next-url -->\n\n[Unreleased]: https://github.com/cross-rs/cross/compare/v0.2.2...HEAD\n\n[v0.2.2]: https://github.com/cross-rs/cross/compare/v0.2.1...v0.2.2\n[v0.2.1]: https://github.com/cross-rs/cross/compare/v0.2.0...v0.2.1\n[v0.2.0]: https://github.com/cross-rs/cross/compare/v0.1.16...v0.2.0\n[v0.1.16]: https://github.com/cross-rs/cross/compare/v0.1.15...v0.1.16\n[v0.1.15]: https://github.com/cross-rs/cross/compare/v0.1.14...v0.1.15\n[v0.1.14]: https://github.com/cross-rs/cross/compare/v0.1.13...v0.1.14\n[v0.1.13]: https://github.com/cross-rs/cross/compare/v0.1.12...v0.1.13\n[v0.1.12]: https://github.com/cross-rs/cross/compare/v0.1.11...v0.1.12\n[v0.1.11]: https://github.com/cross-rs/cross/compare/v0.1.10...v0.1.11\n[v0.1.10]: https://github.com/cross-rs/cross/compare/v0.1.9...v0.1.10\n[v0.1.9]: https://github.com/cross-rs/cross/compare/v0.1.8...v0.1.9\n[v0.1.8]: https://github.com/cross-rs/cross/compare/v0.1.7...v0.1.8\n[v0.1.7]: https://github.com/cross-rs/cross/compare/v0.1.6...v0.1.7\n[v0.1.6]: https://github.com/cross-rs/cross/compare/v0.1.5...v0.1.6\n[v0.1.5]: https://github.com/cross-rs/cross/compare/v0.1.4...v0.1.5\n[v0.1.4]: https://github.com/cross-rs/cross/compare/v0.1.3...v0.1.4\n[v0.1.3]: https://github.com/cross-rs/cross/compare/v0.1.2...v0.1.3\n[v0.1.2]: https://github.com/cross-rs/cross/compare/v0.1.1...v0.1.2\n[v0.1.1]: https://github.com/cross-rs/cross/compare/v0.1.0...v0.1.1\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": ".changes/template/issue440.json",
    "content": "{\n    \"description\": \"no associated PR.\",\n    \"issues\": [440],\n    \"type\": \"fixed\"\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n[*.{rs,py,sh,md,toml,yml,js}]\ncharset = utf-8\n\n[*.{rs,py,sh,yml,js}]\ntrim_trailing_whitespace = true\n\n[*.{rs,py,sh}]\nindent_style = space\nindent_size = 4\n\n[*.yml]\nindent_style = space\nindent_size = 2\n\n[Dockerfile.*]\ncharset = utf-8\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n\nDockerfile.* linguist-language=Dockerfile eol=lf\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @cross-rs/maintainers\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/a_blank_issue.md",
    "content": "---\nname: Blank Issue\nabout: Create a blank issue.\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/b_issue_report.yml",
    "content": "name: Issue report\ndescription: Report something that is broken, missing or wrong.\n\nbody:\n  - type: checkboxes\n    id: acknowledgments\n    attributes:\n        label: Checklist\n        options:\n            - label: I've looked through the [issues and pull requests](https://github.com/cross-rs/cross/issues?q=) for similar reports\n              required: true\n  - type: textarea\n    id: description\n    validations:\n        required: true\n    attributes:\n        label: Describe your issue\n        description: |\n            Write a description of your issue. Include error messages if there are any.\n        placeholder: |\n            Examples:\n              - cross no longer compiles my project after upgrade.\n              - I have trouble doing X.\n              - Feature X has stopped working for me.\n\n  - type: dropdown\n    id: target\n    attributes:\n      label: What target(s) are you cross-compiling for?\n      description: You may select more than one or none if this is not applicable (N/A).\n      multiple: true\n      options:\n        - aarch64-linux-android\n        - aarch64-unknown-linux-gnu\n        - aarch64-unknown-linux-musl\n        - aarch64-unknown-freebsd\n        - arm-linux-androideabi\n        - arm-unknown-linux-gnueabi\n        - arm-unknown-linux-gnueabihf\n        - arm-unknown-linux-musleabi\n        - arm-unknown-linux-musleabihf\n        - armv5te-unknown-linux-gnueabi\n        - armv5te-unknown-linux-musleabi\n        - armv7-linux-androideabi\n        - armv7-unknown-linux-gnueabihf\n        - armv7-unknown-linux-musleabihf\n        - asmjs-unknown-emscripten\n        - i586-unknown-linux-gnu\n        - i586-unknown-linux-musl\n        - i686-linux-android\n        - i686-pc-windows-gnu\n        - i686-unknown-freebsd\n        - i686-unknown-linux-gnu\n        - i686-unknown-linux-musl\n        - loongarch64-unknown-linux-gnu\n        - loongarch64-unknown-linux-musl\n        - mips64el-unknown-linux-gnuabi64\n        - mips64el-unknown-linux-muslabi64\n        - mips64-unknown-linux-gnuabi64\n        - mips64-unknown-linux-muslabi64\n        - mipsel-unknown-linux-gnu\n        - mipsel-unknown-linux-musl\n        - mips-unknown-linux-gnu\n        - mips-unknown-linux-musl\n        - powerpc64le-unknown-linux-gnu\n        - powerpc64-unknown-linux-gnu\n        - powerpc-unknown-linux-gnu\n        - riscv64gc-unknown-linux-gnu\n        - riscv64gc-unknown-linux-musl\n        - s390x-unknown-linux-gnu\n        - sparc64-unknown-linux-gnu\n        - sparcv9-sun-solaris\n        - thumbv6m-none-eabi\n        - thumbv7em-none-eabi\n        - thumbv7em-none-eabihf\n        - thumbv7m-none-eabi\n        - wasm32-unknown-emscripten\n        - x86_64-linux-android\n        - x86_64-pc-windows-gnu\n        - x86_64-pc-solaris\n        - x86_64-unknown-freebsd\n        - x86_64-unknown-linux-gnu\n        - x86_64-unknown-linux-musl\n        - x86_64-unknown-netbsd\n        - other (specify in description)\n\n  - type: checkboxes\n    id: host-system\n    attributes:\n      label: Which operating system is the host (e.g computer cross is on) running?\n      description: You may select more than one or none if N/A.\n      options:\n        - label: macOS\n        - label: Windows\n        - label: Linux / BSD\n        - label: other OS (specify in description)\n\n  - type: checkboxes\n    id: host-arch\n    attributes:\n      label: What architecture is the host?\n      description: You may select more than one or none if N/A.\n      options:\n        - label: x86_64 / AMD64\n        - label: arm32\n        - label: arm64 (including Mac M1)\n\n  - type: checkboxes\n    id: container-engine\n    attributes:\n      label: What container engine is cross using?\n      description: You may select more than one or none if N/A.\n      options:\n        - label: docker\n        - label: podman\n        - label: other container engine (specify in description)\n\n  - type: input\n    id: cross-version\n    validations:\n        required: true\n    attributes:\n        label: cross version\n        description: The version of cross given with `cross -V` or `cross --version`\n        placeholder: cross 0.4.2 (c8df353 2025-01-01)\n\n  - type: textarea\n    id: mcve\n    attributes:\n      label: Example\n      description: Please provide a short, complete example of the issue if possible.\n      placeholder: |\n          ```sh\n          git clone https://github.com/ghost/my_mcve\n          cross build --target x86_65-unknown-linux-gnu\n          ```\n\n          Gives the error message `could not discover target specification`\n\n  - type: textarea\n    id: additional\n    attributes:\n      label: Additional information / notes\n      description: Provide any information you think is relevant to the issue\n      placeholder: It builds on `asmjs-unknown-emscripten`\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Question?\n    url: https://matrix.to/#/#cross-rs:matrix.org\n    about: Reach out to us on our Matrix room if you want!\n  - name: Question!\n    url: https://github.com/cross-rs/cross/discussions/categories/q-a\n    about: Or try the Q&A discussions\n  - name: FAQ\n    url: https://github.com/cross-rs/cross/wiki/FAQ\n    about: Frequently Asked Questions are compiled here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/z_feature_request.yml",
    "content": "name: Feature Request\ndescription: Leave a feature request if you have a suggestion to improve cross and/or the rust ecosystem\nlabels: [\"feature-request\"]\n\nbody:\n  - type: checkboxes\n    id: acknowledgments\n    attributes:\n        label: Checklist\n        options:\n            - label: I've looked through the [issues and pull requests](https://github.com/cross-rs/cross/issues?q=) for similar request\n              required: true\n            - label: This feature could be solved with a [custom image](https://github.com/cross-rs/cross/blob/main/docs/custom_images.md#custom-images) (optional)\n  - type: textarea\n    id: description\n    validations:\n        required: true\n    attributes:\n        label: Describe your request\n        description: |\n            Write a description of what your feature would do.\n            If you have an idea how to solve this, feel free to leave a comment on the feature request after creating it.\n\n            If you have tried solved this but couldn't, explain what you tried and how or why it didn't work. We want to help!\n        placeholder: |\n            Examples:\n              - cross should be able to do X.\n              - Add more emojis to the error printing.\n              - Implement support for cargo feature X.\n  - type: textarea\n    id: motivation\n    validations:\n        required: false\n    attributes:\n        label: Describe why this would be a good inclusion for `cross`\n        description: |\n            Why should this be added?\n"
  },
  {
    "path": ".github/actions/cargo-install-upload-artifacts/action.yml",
    "content": "name: Upload cargo install artifacts\ndescription: Upload `cargo install` artifacts\ninputs:\n  target:\n    description: 'Target'\n    required: true\n\nruns:\n  using: composite\n  steps:\n    - name: Setup\n      id: metadata\n      run: |\n        metadata=\"$(cargo metadata --format-version 1 --no-deps)\"\n\n        package_name=\"cross\"\n        echo \"package-name=${package_name}\" >> $GITHUB_OUTPUT\n\n        out_dir=\"$(mktemp -d)\"\n        artifacts_dir=\"$(mktemp -d)\"\n\n        if which cygpath; then\n          out_dir=\"$(cygpath -w \"${out_dir}\")\"\n          artifacts_dir=\"$(cygpath -w \"${artifacts_dir}\")\"\n        fi\n\n        echo \"out-dir=${out_dir}\" >> $GITHUB_OUTPUT\n        echo \"artifacts-dir=${artifacts_dir}\" >> $GITHUB_OUTPUT\n      shell: bash\n    - run: rm -rf .git\n      shell: bash\n    - name: Build with all features\n      run:\n        cargo install\n          --locked\n          --path .\n          --target ${{ inputs.target }}\n          --all-features\n          --root ${{ steps.metadata.outputs.out-dir }}\n          --bins\n      shell: ${{ contains(runner.os, 'windows') && 'pwsh' || 'bash' }}\n      env:\n        RUSTFLAGS: \"\" # Make sure to unset RUSTFLAGS\n\n    - name: Archive artifacts\n      id: archive\n      run: |\n        set -x\n\n        if which cygpath; then\n          out_dir=\"$(cygpath -u \"${out_dir}\")\"\n          artifacts_dir=\"$(cygpath -u \"${artifacts_dir}\")\"\n        fi\n\n        artifact_name=\"${package_name}-${target}\"\n        artifact_path=\"${artifacts_dir}/${artifact_name}.tar.gz\"\n\n        pushd \"${out_dir}/bin\"\n        tar -cvzf \"${artifact_path}\" *\n        popd\n\n        tar -tf \"${artifact_path}\"\n        ls -al \"${artifact_path}\"\n\n        if which cygpath; then\n          artifact_path=\"$(cygpath -w \"${artifact_path}\")\"\n        fi\n\n        echo \"name=${artifact_name}\" >> $GITHUB_OUTPUT\n        echo \"path=${artifact_path}\" >> $GITHUB_OUTPUT\n      env:\n        package_name: ${{ steps.metadata.outputs.package-name }}\n        out_dir: ${{ steps.metadata.outputs.out-dir }}\n        artifacts_dir: ${{ steps.metadata.outputs.artifacts-dir }}\n        target: ${{ inputs.target }}\n      shell: bash\n\n    - name: Upload artifacts\n      if: ${{ steps.archive.outputs.path }}\n      uses: actions/upload-artifact@v4\n      with:\n        name: ${{ steps.archive.outputs.name }}\n        path: ${{ steps.archive.outputs.path }}\n        if-no-files-found: error\n"
  },
  {
    "path": ".github/actions/cargo-llvm-cov/action.yml",
    "content": "name: llvm coverage\ndescription: Sets up everything that is needed for coverage. Makes artifacts available for processing later, prefixed with _coverage-\ninputs:\n  name:\n    description: 'the name of the artifact'\n    required: true\noutputs:\n  artifact-name:\n    description: 'the name of the artifact'\n    value: ${{ steps.cov.outputs.artifact-name }}\nruns:\n  using: composite\n  steps:\n    - name: Install cargo-llvm-cov\n      uses: taiki-e/install-action@v2\n      with:\n        tool: cargo-llvm-cov@0.5.3\n    - run: rustup component add llvm-tools-preview\n      shell: bash\n    - name: LLVM instrument coverage\n      id: cov\n      uses: ./.github/actions/post\n      if: always()\n      with:\n        main: |\n          pwd=$(pwd)\n          if which cygpath; then\n            pwd=\"$(cygpath -w \"$(pwd)\")\"\n          fi\n          echo RUSTFLAGS=\" -C instrument-coverage --remap-path-prefix ${pwd}=\" >> $GITHUB_ENV\n          echo LLVM_PROFILE_FILE=\"${pwd}/target/cross-%m.profraw\" >> $GITHUB_ENV\n          echo CARGO_INCREMENTAL=\"0\" >> $GITHUB_ENV\n          echo RUST_TEST_THREADS=\"1\" >> $GITHUB_ENV\n          echo \"artifact-name=_coverage-${name}\" >> $GITHUB_OUTPUT\n        post: |\n          # XXX(emilgardis): Upload early?\n          pwd=$(pwd)\n          if which cygpath; then\n            pwd=\"$(cygpath -w \"$(pwd)\")\"\n          fi\n          export LLVM_PROFILE_FILE=\"${pwd}/target/cross-%m.profraw\"\n          export CARGO_LLVM_COV_TARGET_DIR=\"${pwd}/target\"\n          mkdir coverage\n          echo $(ls target)\n          cargo llvm-cov report --remap-path-prefix --lcov --output-path \"coverage/lcov.${name}.info\" -vv || ( echo \"::error title=Coverage merge failed::\" && exit 0 )\n          rm target/*.profraw\n          npm install @actions/artifact\n          npm install glob\n\n          cat <<-EOT | node - || ( echo \"::error title=Coverage upload failed::\" && exit 0 )\n          (async function main() {\n              var artifact = require('@actions/artifact');\n              var glob = require('glob')\n              const artifactClient = artifact.create();\n              const artifactName = '_coverage-' + process.env.name;\n              const files = glob.sync(\"coverage/*\");\n              if (!files.length) {\n                process.exit(0);\n              }\n              console.log(\"${files}\")\n              const options = { retentionDays: 2 };\n              const upload = await artifactClient.uploadArtifact(artifactName, files, \"coverage\", options);\n              })()\n          EOT\n      env:\n        name: ${{ inputs.name }}\n"
  },
  {
    "path": ".github/actions/cargo-publish/action.yml",
    "content": "name: Publish crate\ndescription: Publish crate on crates.io and create release on github\ninputs:\n  cargo-registry-token:\n    description: 'API token for crates.io'\n    required: true\n  github-token:\n    description: 'Github API token'\n    required: true\n\nruns:\n  using: composite\n  steps:\n    - name: Read changelog\n      id: changelog-reader\n      uses: mindsers/changelog-reader-action@v2\n      with:\n        # validation_depth: 10\n        version: ${{ (github.ref_type == 'tag' && !contains(github.ref_name, '-') && github.ref_name) || 'Unreleased' }}\n        path: ./CHANGELOG.md\n\n    - name: Download artifacts\n      id: download-artifacts\n      uses: actions/download-artifact@v4\n      with:\n        path:\n          ${{ runner.temp }}/artifacts\n\n    - name: Log into crates.io\n      if: >\n        github.event_name == 'push' && (\n          github.ref == format('refs/heads/{0}', github.event.repository.default_branch) ||\n          startsWith(github.ref, 'refs/tags/v')\n        )\n      run: cargo login ${{ inputs.cargo-registry-token }}\n      shell: bash\n\n    - run: gh release delete --yes Unreleased || exit 0\n      if: >\n        github.event_name == 'push' &&\n          github.ref == format('refs/heads/{0}', github.event.repository.default_branch)\n      shell: bash\n      env:\n        GITHUB_TOKEN: ${{ inputs.github-token }}\n\n    - name: Create GitHub release\n      if: >\n        github.event_name == 'push' && (\n          github.ref == format('refs/heads/{0}', github.event.repository.default_branch) ||\n          startsWith(github.ref, 'refs/tags/v')\n        )\n      uses: softprops/action-gh-release@v1\n      with:\n        tag_name: ${{ (github.ref_type == 'tag' && github.ref_name) || 'Unreleased' }}\n        body: ${{ steps.changelog-reader.outputs.changes }}\n        prerelease: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref_name, '-') }}\n        draft: ${{ !startsWith(github.ref, 'refs/tags/v') && steps.changelog-reader.outputs.status == 'unreleased' }}\n        files: |\n          ${{ steps.download-artifacts.outputs.download-path }}/cross-*/*\n\n    - name: Publish crate\n      run: cargo publish ${{ !startsWith(github.ref, 'refs/tags/v') && '--dry-run' || '' }}\n      shell: bash\n"
  },
  {
    "path": ".github/actions/post/action.yml",
    "content": "# adapted from https://github.com/actions/runner/issues/1478\nname: With post step\ndescription: 'Generic JS Action to execute a main command and set a command in a post step.'\ninputs:\n  main:\n    description: 'Main command/script.'\n    required: true\n  post:\n    description: 'Post command/script.'\n    required: true\nruns:\n  using: 'node20'\n  main: 'main.js'\n  post: 'main.js'\n"
  },
  {
    "path": ".github/actions/post/main.js",
    "content": "// adapted from https://github.com/actions/runner/issues/1478\nconst { exec } = require(\"child_process\");\n\nfunction run(cmd) {\n  exec(cmd, { shell: \"bash\" }, (error, stdout, stderr) => {\n    if (stdout.length != 0) {\n      console.log(`${stdout}`);\n    }\n    if (stderr.length != 0) {\n      console.error(`${stderr}`);\n    }\n    if (error) {\n      process.exitCode = error.code;\n      console.error(`${error}`);\n    }\n  });\n}\n\nif (process.env[`STATE_POST`] != undefined) {\n  // Are we in the 'post' step?\n  run(process.env.INPUT_POST);\n} else {\n  // Otherwise, this is the main step\n  console.log(`POST=true >> $GITHUB_STATE`);\n  run(process.env.INPUT_MAIN);\n}\n"
  },
  {
    "path": ".github/actions/setup-rust/action.yml",
    "content": "name: Setup Rust\ndescription: Setup Rust with specified toolchain, target and components.\ninputs:\n  toolchain:\n    description: 'Toolchain'\n    default: stable\n    required: true\n  target:\n    description: 'Target'\n    required: false\n  components:\n    description: 'Components'\n    required: false\n\nruns:\n  using: composite\n  steps:\n    - name: Install Rust toolchain\n      uses: dtolnay/rust-toolchain@master\n      with:\n        toolchain: ${{ inputs.toolchain }}\n        target: ${{ inputs.target }}\n        components: ${{ inputs.components }}\n\n    - name: Install rust matcher\n      run: echo \"::add-matcher::.github/actions/setup-rust/rust.json\"\n      shell: bash\n\n    - uses: Swatinem/rust-cache@v2.2.0\n"
  },
  {
    "path": ".github/actions/setup-rust/rust.json",
    "content": "{\n  \"problemMatcher\": [\n    {\n      \"owner\": \"rust\",\n      \"pattern\": [\n        {\n          \"regexp\": \"^(warning|warn|error)(\\\\[(.*)\\\\])?: (.*)$\",\n          \"severity\": 1,\n          \"message\": 4,\n          \"code\": 3\n        },\n        {\n          \"regexp\": \"^([\\\\s->=]*(.*):(\\\\d*):(\\\\d*)|.*)$\",\n          \"file\": 2,\n          \"line\": 3,\n          \"column\": 4\n        }\n      ]\n    },\n    {\n      \"owner\": \"cross-rs\",\n      \"pattern\": [\n        {\n          \"regexp\": \"^\\\\[cross\\\\] (warning|error): (.*)$\",\n          \"severity\": 1,\n          \"message\": 2\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/bors.toml",
    "content": "block_labels = [\"needs-decision\"]\ndelete_merged_branches = true\nrequired_approvals = 0\nuse_codeowners = false\nstatus = [\"conclusion\"]\ntimeout_sec = 21600\n"
  },
  {
    "path": ".github/codecov.yml",
    "content": "comment: false\ncodecov:\n  branch: main\ncoverage:\n  status:\n    project:\n      default:\n        informational: true\n    patch:\n      default:\n        informational: true\nflag_management:\n  default_rules: # the rules that will be followed for any flag added, generally.\n    carryforward: true\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/actions/cargo-install-upload-artifacts\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/actions/cargo-llvm-cov\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/actions/cargo-publish\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/actions/post\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/actions/setup-rust\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      github_actions:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/changelog.yml.x",
    "content": "on:\n  pull_request:\n    types: [labeled, unlabeled, opened, synchronize, reopened]\n\nname: Changelog check\n\njobs:\n  changelog:\n    name: Changelog check\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: ./.github/actions/setup-rust\n\n      - name: Get Changed Files\n        id: files\n        uses: tj-actions/changed-files@v41\n        with:\n          separator: ';'\n          files: |\n            .changes/*.json\n\n      - name: Validate Changelog\n        id: changelog\n        run: |\n          set -x\n          set -e\n          IFS=';' read -a added_modified <<< '${{ steps.files.outputs.all_changed_files }}'\n          IFS=';' read -a removed <<< '${{ steps.files.outputs.deleted_files }}'\n          added_count=${#added_modified[@]}\n          removed_count=${#removed[@]}\n          if ${{ !contains(github.event.pull_request.labels.*.name, 'no changelog' ) }}; then\n            if [[ \"$added_count\" -eq \"0\" ]] && [[ \"$removed_count\" -eq \"0\" ]]; then\n              echo \"Must add or remove changes or add the 'no changelog' label\"\n              exit 1\n            else\n              cargo xtask changelog validate\n            fi\n          fi\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "on:\n  workflow_call:\n    inputs:\n      matrix-args:\n        required: false\n        type: string\n        description: Arguments to pass to `cargo xtask ci-job target-matrix`\n      checkout-ref:\n        required: false\n        type: string\n        description: Used to checkout a specific ref, instead of the default ref with `actions/checkout` action\n  pull_request:\n  merge_group:\n  push:\n    branches: [main, staging, trying]\n    tags:\n      - \"v*.*.*\"\n\nname: CI\n\nenv:\n  CARGO_NET_RETRY: 3\n  CARGO_HTTP_CHECK_REVOKE: false\n  CROSS_IMAGE: ghcr.io/${{ github.repository_owner }}\n\njobs:\n  shellcheck:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - name: Run ShellCheck\n        uses: Azbagheri/shell-linter@latest\n        with:\n          exclude-paths: \".github/CODEOWNERS,LICENSE-APACHE,LICENSE-MIT\"\n\n  cargo-deny:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        ref: ${{ inputs.checkout-ref }}\n    - uses: EmbarkStudios/cargo-deny-action@v2\n\n  fmt:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - uses: ./.github/actions/setup-rust\n        with:\n          components: rustfmt\n\n      - name: Run rustfmt\n        run: cargo fmt -- --check\n\n  clippy:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os:\n          - macos-latest\n          - ubuntu-latest\n          - windows-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - uses: ./.github/actions/setup-rust\n        with:\n          components: clippy\n          toolchain: 1.92.0 # MSRV, Minimally Supported Rust Version. Make sure to update README.md and clippy.toml\n      - name: Run clippy\n        run: cargo clippy --locked --all-targets --all-features --workspace -- -D warnings\n  test:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os:\n          - macos-latest\n          - ubuntu-latest\n          - windows-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - uses: ./.github/actions/setup-rust\n      - uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: test-${{matrix.os}}\n\n      - name: Run unit tests\n        run: cargo test --locked --all-targets --workspace --all-features\n        timeout-minutes: 10\n  check:\n    runs-on: ubuntu-latest\n    outputs:\n      is-latest: ${{ steps.check.outputs.is-latest }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n      - run: cargo xtask ci-job check\n        id: check\n  generate-matrix:\n    runs-on: ubuntu-latest\n    outputs:\n      matrix: ${{ steps.generate-matrix.outputs.matrix }}\n      tests: ${{ steps.generate-matrix.outputs.tests }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n\n      - name: Generate matrix\n        id: generate-matrix\n        run: cargo xtask ci-job target-matrix ${{ github.event_name == 'merge_group' && format('--merge-group {0}', github.ref) || '' }} ${{ inputs.matrix-args || '' }}\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n  build:\n    name: target (${{ matrix.pretty }},${{ matrix.os }})\n    runs-on: ${{ matrix.os }}\n    needs: [shellcheck, test, generate-matrix, check]\n    if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' || github.event_name == 'issue_comment' || github.event_name == 'schedule')  && needs.generate-matrix.outputs.matrix != '{}' && needs.generate-matrix.outputs.matrix != '[]' && needs.generate-matrix.outputs.matrix != ''\n    concurrency:\n      group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.pretty }}\n      cancel-in-progress: false\n    strategy:\n      fail-fast: false\n      matrix:\n        include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}\n    outputs:\n      has-image: ${{ steps.prepare-meta.outputs.has-image }}\n      images: ${{ steps.build-docker-image.outputs.images && fromJSON(steps.build-docker-image.outputs.images)  }}\n      coverage-artifact: ${{ steps.cov.outputs.artifact-name }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - uses: ./.github/actions/setup-rust\n\n      - name: Set up Docker Buildx\n        if: runner.os == 'Linux'\n        uses: docker/setup-buildx-action@v2\n\n      - name: Build xtask\n        run: cargo build -p xtask\n\n      - name: Prepare Meta\n        id: prepare-meta\n        timeout-minutes: 60\n        run: cargo xtask ci-job prepare-meta \"${TARGET}${SUB:+.$SUB}\"\n        env:\n          TARGET: ${{ matrix.target }}\n          SUB: ${{ matrix.sub }}\n        shell: bash\n\n      - name: LLVM instrument coverage\n        id: cov\n        uses: ./.github/actions/cargo-llvm-cov\n        if: steps.prepare-meta.outputs.has-image  && steps.prepare-meta.outputs.test-variant != 'zig'\n        with:\n          name: cross-${{matrix.pretty}}\n\n      - name: Install cross\n        if: matrix.deploy\n        run: cargo install --path . --force --debug\n\n      - name: Docker Meta\n        if: steps.prepare-meta.outputs.has-image\n        id: docker-meta\n        uses: docker/metadata-action@v4\n        with:\n          images: |\n            name=${{ steps.prepare-meta.outputs.image }}\n          labels: |\n            ${{ fromJSON(steps.prepare-meta.outputs.labels) }}\n      - name: Build Docker image\n        id: build-docker-image\n        if: steps.prepare-meta.outputs.has-image\n        timeout-minutes: 120\n        run: cargo xtask build-docker-image -v \"${TARGET}${SUB:+.$SUB}\" ${{ matrix.verbose && '-v' || ''  }}\n        env:\n          TARGET: ${{ matrix.target }}\n          SUB: ${{ matrix.sub }}\n          LABELS: ${{ steps.docker-meta.outputs.labels }}\n          LATEST: ${{ needs.check.outputs.is-latest || 'false' }}\n          CROSS_DEBUG: ${{ matrix.cross-debug }}\n        shell: bash\n      - name: Set Docker image for test\n        if: steps.prepare-meta.outputs.has-image\n        run: |\n          TARGET_VAR=\"cross_target_${TARGET//-/_}_image\"\n          echo \"${TARGET_VAR^^}=${IMAGE}\" | tee -a \"${GITHUB_ENV}\"\n        env:\n          TARGET: ${{ matrix.target }}\n          IMAGE: ${{ steps.build-docker-image.outputs.image }}\n        shell: bash\n      - name: Test Image\n        if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant == 'default'\n        run: ./ci/test.sh\n        env:\n          TARGET: ${{ matrix.target }}\n          CPP: ${{ matrix.cpp }}\n          DYLIB: ${{ matrix.dylib }}\n          STD: ${{ matrix.std }}\n          BUILD_STD: ${{ matrix.build-std }}\n          RUN: ${{ matrix.run }}\n          RUNNERS: ${{ matrix.runners }}\n          CROSS_DEBUG: ${{ matrix.cross-debug }}\n        shell: bash\n      - uses: ./.github/actions/cargo-install-upload-artifacts\n        if: matrix.deploy\n        with:\n          target: ${{ matrix.target }}\n\n      - name: Test Zig Image\n        if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant == 'zig'\n        run: ./ci/test-zig-image.sh\n        shell: bash\n\n      - name: Test Cross Image\n        if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant == 'cross'\n        run: ./ci/test-cross-image.sh\n        env:\n          TARGET: 'aarch64-unknown-linux-gnu'\n          IMAGE: 'ghcr.io/${{ github.repository_owner }}/aarch64-unknown-linux-gnu:main'\n        shell: bash\n\n      - name: Login to GitHub Container Registry\n        if: steps.prepare-meta.outputs.has-image\n        uses: docker/login-action@v2\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Push image to GitHub Container Registry\n        if: >\n          (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') &&\n          steps.prepare-meta.outputs.has-image && (\n            github.ref == format('refs/heads/{0}', github.event.repository.default_branch) ||\n            startsWith(github.ref, 'refs/tags/v')\n          )\n        run: cargo xtask build-docker-image -v --push \"${TARGET}${SUB:+.$SUB}\"\n        env:\n          TARGET: ${{ matrix.target }}\n          SUB: ${{ matrix.sub }}\n          LABELS: ${{ steps.docker-meta.outputs.labels }}\n          LATEST: ${{ needs.check.outputs.is-latest || 'false' }}\n        shell: bash\n\n  # we should always have an artifact from a previous build.\n  remote:\n    needs: [test, check, generate-matrix]\n    if: fromJson(needs.generate-matrix.outputs.tests).remote\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n\n      - name: LLVM instrument coverage\n        uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: integration-remote\n\n      - name: Run Remote Test\n        env:\n          TARGET: aarch64-unknown-linux-gnu\n        run: ./ci/test-remote.sh\n        shell: bash\n\n  bisect:\n    needs: [test, check, generate-matrix]\n    if: fromJson(needs.generate-matrix.outputs.tests).bisect\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n\n      - name: LLVM instrument coverage\n        uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: integration-bisect\n\n      - name: Run Bisect Test\n        env:\n          TARGET: aarch64-unknown-linux-gnu\n        run: ./ci/test-bisect.sh\n        shell: bash\n\n  foreign:\n    needs: [test, check, generate-matrix]\n    if: fromJson(needs.generate-matrix.outputs.tests).foreign\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n\n      - name: LLVM instrument coverage\n        uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: integration-bisect\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v2\n        with:\n          platforms: arm64\n      - name: Set up docker buildx\n        uses: docker/setup-buildx-action@v2\n        id: buildx\n        with:\n          install: true\n      - name: Run Foreign toolchain test\n        run: ./ci/test-foreign-toolchain.sh\n        shell: bash\n\n  docker-in-docker:\n    needs: [test, check, generate-matrix]\n    if: fromJson(needs.generate-matrix.outputs.tests).docker-in-docker\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n\n      - name: LLVM instrument coverage\n        uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: integration-docker-in-docker\n\n      - name: Run Docker-in-Docker Test\n        env:\n          TARGET: aarch64-unknown-linux-gnu\n          IMAGE: 'ghcr.io/${{ github.repository_owner }}/aarch64-unknown-linux-gnu:main'\n        run: ./ci/test-docker-in-docker.sh\n        shell: bash\n\n  podman:\n    name: podman\n    runs-on: ubuntu-latest\n    needs: [shellcheck, test, check, generate-matrix]\n    if: fromJson(needs.generate-matrix.outputs.tests).podman\n    strategy:\n      fail-fast: false\n    outputs:\n      coverage-artifact: ${{ steps.cov.outputs.artifact-name }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n\n      - uses: ./.github/actions/setup-rust\n\n      - name: Install Podman\n        env:\n          DEBIAN_FRONTEND: noninteractive\n        run: |\n          sudo apt-get update\n          sudo apt-get install podman --no-install-recommends --assume-yes\n\n      - name: LLVM instrument coverage\n        id: cov\n        uses: ./.github/actions/cargo-llvm-cov\n        with:\n          name: cross-podman-aarch64-unknown-linux-gnu\n\n      - name: Install cross\n        run: cargo install --path . --force --debug\n\n      - name: Run Podman Test\n        run: ./ci/test-podman.sh\n        env:\n          CROSS_CONTAINER_ENGINE: podman\n          TARGET: aarch64-unknown-linux-gnu\n        shell: bash\n\n  publish:\n    needs: [build, check, fmt, clippy, cargo-deny]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n      - uses: ./.github/actions/cargo-publish\n        with:\n          cargo-registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n\n  conclusion:\n    needs: [shellcheck, fmt, clippy, test, generate-matrix, build, publish, check, remote, bisect, docker-in-docker, foreign, podman]\n    if: always()\n    runs-on: ubuntu-latest\n    steps:\n      - name: Result\n        run: |\n          jq -C <<< \"${needs}\"\n\n          # Check if all needs were successful or skipped.\n          \"$(jq -r 'all(.result as $result | ([\"success\", \"skipped\"] | contains([$result])))' <<< \"${needs}\")\"\n        env:\n          needs: ${{ toJson(needs) }}\n\n  code-cov:\n    name: Coverage\n    needs: [test, build, conclusion, generate-matrix]\n    # should check that there are any artifacts, if not skip\n    if: always() && (needs.build.result == 'success' || needs.build.result == 'skipped') && needs.test.result == 'success'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ inputs.checkout-ref }}\n      - uses: ./.github/actions/setup-rust\n      - uses: actions/download-artifact@v4\n        with:\n          path: ${{ runner.temp }}/artifacts\n      - name: Grab PR number\n        run: echo \"pr=$(echo ${commit_message} | sed -ne 's/.*#\\(.*\\):/\\1/p')\" >> $GITHUB_OUTPUT\n        id: pr-number\n        if: ${{ !github.event.pull_request.number }}\n        env:\n          commit_message: >\n            ${{\n              ((\n                startsWith(github.event.head_commit.message, 'Try #') &&\n                github.event.head_commit.author.username == 'bors[bot]'\n              ) && github.event.head_commit.message) || ''\n            }}\n      - name: Upload to codecov.io\n        run: |\n          set -x\n          curl -Os https://uploader.codecov.io/latest/linux/codecov\n          chmod +x codecov\n          sha_rev=$(git rev-parse HEAD)\n          for file in $(find ${artifacts} -name \"lcov.*.info\" -exec ls {} +); do\n            name=$(echo $file | awk -F'.' '{print $2}');\n            ./codecov -F $name ${pr:+-P ${pr}} -f $file --sha ${sha_rev} -n $name;\n          done\n        env:\n          pr: ${{  steps.pr-number.outputs.pr }}\n          artifacts: ${{ runner.temp }}/artifacts\n"
  },
  {
    "path": ".github/workflows/try.yml",
    "content": "name: Try\non:\n  issue_comment:\n    types: [created]\njobs:\n  acknowledge:\n    runs-on: ubuntu-latest\n    if: github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && (contains(toJson(github.event.comment.body), '\\n/ci try') || startsWith(github.event.comment.body, '/ci try'))\n    steps:\n      - uses: actions/checkout@v3\n      - name: Acknowledge command\n        id: acknowledge\n        run: |\n          gh pr comment ${{ github.event.issue.number }} --body \"<!--try-ack-comment-->\n          Starting try run. [Link to action](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}?pr=${{ github.event.issue.number }})\"\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        continue-on-error: true\n  try:\n    if: github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && (contains(toJson(github.event.comment.body), '\\n/ci try') || startsWith(github.event.comment.body, '/ci try'))\n    uses: ./.github/workflows/ci.yml\n    with:\n      matrix-args: try --comment \"${{ github.event.comment.body }}\" --pr ${{ github.event.issue.number }}\n      checkout-ref: refs/pull/${{ github.event.issue.number }}/head\n  comment:\n    needs: [try, acknowledge]\n    if: always() && needs.try.result != 'skipped'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Minimize existing comments\n        run: |\n          COMMENTS=$(gh pr view ${{ github.event.issue.number }} --json comments --jq '.comments[] | select((.body | contains(\"<!--try-conclusion-comment-->\") or contains(\"<!--try-ack-comment-->\")) and (.author.login == \"github-actions\") and (.isMinimized | not)) | .id')\n          while read -r comment_id; do\n            gh api graphql -f query='mutation { minimizeComment(input: { classifier: OUTDATED, subjectId: \"'\"$comment_id\"'\" }) { minimizedComment { isMinimized } } }'\n          done <<< \"$COMMENTS\"\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        continue-on-error: true\n      # comment on the PR with the result and links to the logs using gh cli\n      # Something like `### Try build: [{result}]({link_to_logs})`\n      # the url to the logs are on jobs[name=\"try\"].url gathered with `gh run view ${{ github.run_id }} --json jobs`\n      - name: Comment on PR\n        run: |\n          PR_ID=${{ github.event.issue.number }}\n          gh run view ${{ github.run_id }} --json jobs |\\\n          jq -r --arg pr_id \"$PR_ID\" --arg comment \"${{ github.event.comment.html_url }}\" '\n          def box: .conclusion | if . == \"success\" then \"✔️ \" elif . == \"skipped\" then \"🛇 \" else \"❌ \" end;\n          def reason: if .conclusion == \"failure\" and (.steps | length > 0) then .steps[] | select(.conclusion == \"failure\") | .name else \"\" end;\n          def job_to_md: . | \"- [\\(.name)](\\(.url)?pr=\\($pr_id)\\(.conclusion | if . == \"success\" then \"#step:10:1)\" else \"#)\" end) - \\(box) \\(reason)\";\n          def wrap_if_needed:\n            (.[0].conclusion | if . == \"success\" then \"#### Successful Jobs\\n\\n\" else \"#### Failed Jobs\\n\\n\" end) +\n            if length > 10 then\n              \"<details>\\n<summary>List</summary>\\n\\n\\(map(job_to_md) | join(\"\\n\"))\\n\\n</details>\\n\"\n            else\n              map(job_to_md) | join(\"\\n\") + \"\\n\"\n            end;\n          \"<!--try-conclusion-comment-->\\n## [Try](\\(.jobs[] | select(.name == \"try / generate-matrix\") | .url + \"#step:4:18\")) run for [comment](\\($comment))\\n\\n\" +\n          \"\\(.jobs[] | select(.name == \"try / conclusion\") | job_to_md)\\n\\n\" +\n          ([.jobs[] | select(.name | startswith(\"try / target\")) | select(.name | contains(\"matrix.pretty\") | not ) | . as $job |\n          {conclusion: $job.conclusion, name: ($job.name | capture(\"\\\\((?<name>[^,]+),.*\") | .name), url: $job.url, steps: $job.steps } ] |\n          group_by(if .conclusion == \"success\" then \"success\" else \"failure\" end) |\n          map(wrap_if_needed) |\n          join(\"\\n\"))' |\\\n          gh pr comment \"$PR_ID\" --body \"$(< /dev/stdin)\"\n        env:\n          GH_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/weekly.yml",
    "content": "on:\n  schedule:\n    - cron:  '0 0 * * 5'\n  workflow_dispatch:\n    inputs:\n      targets:\n        required: false\n        description: 'check these space or comma separated targets, supports wildcard *'\n\nname: Check\n\nenv:\n  CARGO_NET_RETRY: 3\n  CARGO_HTTP_CHECK_REVOKE: false\n\njobs:\n  weekly:\n    uses: ./.github/workflows/ci.yml\n    with:\n      matrix-args: --weekly\n      checkout-ref: ${{ github.ref }}\n  wiki:\n    name: Ensure wiki is valid\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: ./.github/actions/setup-rust\n      - run: git clone ${wikirepo}\n        shell: bash\n        env:\n          wikirepo: https://github.com/${{ github.repository }}.wiki.git\n      - run: cargo test toml_check -- --nocapture\n  cargo-deny:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n    - uses: EmbarkStudios/cargo-deny-action@v1\n"
  },
  {
    "path": ".gitignore",
    "content": "**/target/\n**/.idea/\n**/.vscode/*.*\n**/*.log\n/cargo-timing*.html\nCHANGELOG.md.draft\n\n# python stuff\n__pycache__/\n.pytest_cache/\n*.py[cod]\n*$py.class\n*.egg-info/\n*.egg\n.tox\n\n#--------------------------------------------------#\n# The following was generated with gitignore.nvim: #\n#--------------------------------------------------#\n# Gitignore for the following technologies: Vim\n\n# Swap\n[._]*.s[a-v][a-z]\n!*.svg  # comment out if you don't need vector files\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\nSessionx.vim\n\n# Temporary\n.netrwhist\n*~\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"docker/cross-toolchains\"]\n\tpath = docker/cross-toolchains\n\turl = https://github.com/cross-rs/cross-toolchains.git\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\nAll notable changes to this project will be documented in this file. This is an automatically-generated document: entries are added via changesets present in the `.changes` directory.\nThis project adheres to [Semantic Versioning](http://semver.org/).\n\n<!-- next-header -->\n\n## [Unreleased] - ReleaseDate\n\n## [v0.2.5] - 2023-02-04\n\n## Fixed\n\n- #962 - fix SELinux labels to allow use in multiple containers and/or the host filesystem.\n- #1166 - freebsd: include memstat in build image to fix build with libc 0.2.138 and up.\n- #1183 - resolve issue when using `pre-build` in `Cargo.toml`\n\n## [v0.2.4] - 2022-07-10\n\n### Fixed\n\n- #930 - fix any parsing of 1-character subcommands\n- #929 - Fixed issue where `--verbose` would not output data when it should\n\n## [v0.2.3] - 2022-07-09\n\n### Added\n\n- #921 - use `CARGO_TERM_VERBOSE`, `CARGO_TERM_QUIET`, and `CARGO_TERM_COLOR` environment variables for cross terminal output.\n- #913 - added the `x86_64-unknown-illumos` target.\n- #910 - `pre-build` can now take a string pointing to a script file to run.\n- #905 - added `qemu-runner` for musl images, allowing use of native or emulated runners.\n- #905 - added qemu emulation to `i586-unknown-linux-gnu`, `i686-unknown-linux-musl`, and `i586-unknown-linux-gnu`, so they can run on an `x86` CPU, rather than an `x86_64` CPU.\n- #900 - add the option to skip copying build artifacts back to host when using remote cross via `CROSS_REMOTE_SKIP_BUILD_ARTIFACTS`.\n- #891 - support custom user namespace overrides by setting the `CROSS_CONTAINER_USER_NAMESPACE` environment variable.\n- #890 - support rootless docker via the `CROSS_ROOTLESS_CONTAINER_ENGINE` environment variable.\n- #878 - added an image `ghcr.io/cross-rs/cross` containing cross.\n\n### Changed\n\n- #869 - ensure cargo configuration environment variable flags are passed to the docker container.\n- #859 - added color diagnostic output and error messages.\n\n### Fixed\n\n- #905 - fixed running dynamically-linked libraries for all musl targets except `x86_64-unknown-linux-musl`.\n- #904 - ensure `cargo metadata` works by using the same channel.\n- #904 - fixed the path for workspace volumes and passthrough volumes with docker-in-docker.\n- #898 - fix the path to the mount root with docker-in-docker if mounting volumes.\n- #897 - ensure `target.$(...)` config options override `build` ones when parsing strings and vecs.\n- #895 - convert filenames in docker tags to ASCII lowercase and ignore invalid characters\n- #885 - handle symlinks when using remote docker.\n- #868 - ignore the `CARGO` environment variable.\n- #867 - fixed parsing of `build.env.passthrough` config values.\n\n## [v0.2.2] - 2022-06-24\n\n### Added\n\n- #803 - added `CROSS_CUSTOM_TOOLCHAIN` to disable automatic installation of components for use with tools like `cargo-bisect-rustc`\n- #795 - added images for additional toolchains maintained by cross-rs.\n- #792 - added `CROSS_CONTAINER_IN_CONTAINER` environment variable to replace `CROSS_DOCKER_IN_DOCKER`.\n- #785 - added support for remote container engines through data volumes through setting the `CROSS_REMOTE` environment variable. also adds in utility commands to create and remove persistent data volumes.\n- #782 - added `build-std` config option, which builds the rust standard library from source if enabled.\n- #678 - Add optional `target.{target}.dockerfile[.file]`, `target.{target}.dockerfile.context` and `target.{target}.dockerfile.build-args` to invoke docker/podman build before using an image.\n- #678 - Add `target.{target}.pre-build` config for running commands before building the image.\n- #772 - added `CROSS_CONTAINER_OPTS` environment variable to replace `DOCKER_OPTS`.\n- #767, #788 - added the `cross-util` and `xtask` commands.\n- #842 - Add `Cargo.toml` as configuration source\n- #745 - added `thumbv7neon-*` targets.\n- #741 - added `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-musleabi` targets.\n- #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`.\n- #719 - add `--list` to known subcommands.\n- #681 - Warn on unknown fields and confusable targets\n- #624 - Add `build.default-target`\n- #647 - Add `mips64-unknown-linux-muslabi64` and `mips64el-unknown-linux-muslabi64` support\n- #543 - Added environment variables to control the UID and GID in the container\n- #524 - docker: Add Nix Store volume support\n- Added support for mounting volumes.\n- #684 - Enable cargo workspaces to work from any path in the workspace, and make path dependencies mount seamlessly. Also added support for private SSH dependencies.\n\n### Changed\n\n- #838 - re-enabled the solaris targets.\n- #807 - update Qemu to 6.1.0 on images using Ubuntu 18.04+ with python3.6+.\n- #775 - forward Cargo exit code to host\n- #762 - re-enabled `x86_64-unknown-dragonfly` target.\n- #747 - reduced android image sizes.\n- #746 - limit image permissions for android images.\n- #377 - update WINE versions to 7.0.\n- #734 - patch `arm-unknown-linux-gnueabihf` to build for ARMv6, and add architecture for crosstool-ng-based images.\n- #709 - Update Emscripten targets to `emcc` version 3.1.10\n- #707, #708 - Set `BINDGEN_EXTRA_CLANG_ARGS` environment variable to pass sysroot to `rust-bindgen`\n- #696 - bump freebsd to 12.3\n- #629 - Update Android NDK version and API version\n- #497 - don't set RUSTFLAGS in aarch64-musl image\n- #492 - Add cmake to FreeBSD images\n- #748 - allow definitions in the environment variable passthrough\n\n### Fixed\n\n- #836 - write a `CACHEDIR.TAG` when creating the target directory, similar to `cargo`.\n- #804 - allow usage of env `CARGO_BUILD_TARGET` as an alias for `CROSS_BUILD_TARGET`\n- #792 - fixed container-in-container support when using podman.\n- #781 - ensure `target.$(...)` config options override `build` ones.\n- #771 - fix parsing of `DOCKER_OPTS`.\n- #727 - add `PKG_CONFIG_PATH` to all `*-linux-gnu` images.\n- #722 - boolean environment variables are evaluated as truthy or falsey.\n- #720 - add android runner to preload `libc++_shared.so`.\n- #725 - support `CROSS_DEBUG` and `CROSS_RUNNER` on android images.\n- #714 - use host target directory when falling back to host cargo.\n- #713 - convert relative target directories to absolute paths.\n- #501 (reverted, see #764) - x86_64-linux: lower glibc version requirement to 2.17 (compatible with centos 7)\n- #500 - use runner setting specified in Cross.toml\n- #498 - bump linux-image version to fix CI\n- Re-enabled `powerpc64-unknown-linux-gnu` image\n- Re-enabled `sparc64-unknown-linux-gnu` image\n- #582 - Added `libprocstat.so` to FreeBSD images\n- #665 - when not using [env.volumes](https://github.com/cross-rs/cross#mounting-volumes-into-the-build-environment), mount project in /project\n- #494 - Parse Cargo's --manifest-path option to determine mounted docker root\n\n### Removed\n\n- #718 - remove deb subcommand.\n\n### Internal\n\n- #856 - remove use of external wslpath and create internal helper that properly handles UNC paths.\n- #828 - assume paths are Unicode and provide better error messages for path encoding errors.\n- #787 - add installer for git hooks.\n- #786, #791 - Migrate build script to rust: `cargo build-docker-image $TARGET`\n- #730 - make FreeBSD builds more resilient.\n- #670 - Use serde for deserialization of Cross.toml\n- Change rust edition to 2021 and bump MSRV for the cross binary to 1.58.1\n- #654 - Use color-eyre for error reporting\n- #658 - Upgrade dependencies\n- #652 - Allow trying individual targets via bors.\n- #650 - Improve Docker caching.\n- #609 - Switch to Github Actions and GHCR.\n- #588 - fix ci: bump openssl version in freebsd again\n- #552 - Added CHANGELOG.md automation\n- #534 - fix image builds with update of dependencies\n- #502 - fix ci: bump openssl version in freebsd\n- #489 - Add support for more hosts and simplify/unify host support checks\n- #477 - Fix Docker/Podman links in README\n- #476 - Use Rustlang mirror for Sabotage linux tarballs\n- Bump nix dependency to `0.22.1`\n- Bump musl version to 1.1.24.\n\n## [v0.2.1] - 2020-06-30\n\n- Disabled `powerpc64-unknown-linux-gnu` image.\n- Disabled `sparc64-unknown-linux-gnu` image.\n- Disabled `x86_64-unknown-dragonfly` image.\n- Removed CI testing for `i686-apple-darwin`.\n\n## [v0.2.0] - 2020-02-22\n\n- Removed OpenSSL from all images.\n- Added support for Podman.\n- Bumped all images to at least Ubuntu 16.04.\n\n## [v0.1.16] - 2019-09-17\n\n- Bump OpenSSL version to 1.0.2t.\n- Re-enabled `asmjs-unknown-emscripten` target.\n- Default to `native` runner instead of `qemu-user` for certain targets.\n\n## [v0.1.15] - 2019-09-04\n\n- Images are now hosted at <https://hub.docker.com/r/rustembedded/cross>.\n- Bump OpenSSL version to 1.0.2p.\n- Bump musl version to 1.1.20.\n- Bump Ubuntu to 18.04 to all musl targets.\n- Bump gcc version to 6.3.0 for all musl targets.\n- OpenSSL support for the `arm-unknown-linux-musleabi` target.\n- OpenSSL support for the `armv7-unknown-linux-musleabihf` target.\n- Build and test support for `aarch64-unknown-linux-musl`, `arm-unknown-linux-musleabihf`,\n  `armv5te-unknown-linux-musleabi`, `i586-unknown-linux-musl`, `mips-unknown-linux-musl`,\n  add `mipsel-unknown-linux-musl` targets.\n\n## [v0.1.14] - 2017-11-22\n\n### Added\n\n- Support for the `i586-unknown-linux-gnu` target.\n\n### Changed\n\n- Downgraded the Solaris toolchains from 2.11 to 2.10 to make the binaries produced by Cross more\n  compatible (this version matches what rust-lang/rust is using).\n\n## [v0.1.13] - 2017-11-08\n\n### Added\n\n- Support for the custom [`deb`] subcommand.\n\n[`deb`]: https://github.com/mmstick/cargo-deb\n\n- Partial `test` / `run` support for android targets. Using the android API via `cross run` / `cross test` is _not_ supported because Cross is using QEMU instead of the official Android emulator.\n\n- Partial support for the `sparcv9-sun-solaris` and `x86_64-sun-solaris` targets. `cross test` and\n  `cross run` doesn't work for these new targets.\n\n- OpenSSL support for the `i686-unknown-linux-musl` target.\n\n### Changed\n\n- Bump OpenSSL version to 1.0.2m.\n\n## [v0.1.12] - 2017-09-22\n\n### Added\n\n- Support for `cross check`. This subcommand won't use any Docker container.\n\n### Changed\n\n- `binfmt_misc` is not required on the host for toolchain v1.19.0 and newer.\n  With these toolchains `binfmt_misc` interpreters don't need to be installed\n  on the host saving a _privileged_ docker run which some systems don't allow.\n\n## [v0.1.11] - 2017-06-10\n\n### Added\n\n- Build and test support for `i686-pc-windows-gnu`, `x86_64-pc-windows-gnu`,\n  `asmjs-unknown-emscripten` and `wasm-unknown-emscripten`.\n\n- Build support for `aarch64-linux-android`, `arm-linux-androideabi`,\n  `armv7-linux-androideabi`, `x86_64-linux-android` and `i686-linux-android`\n\n- A `build.env.passthrough` / `build.target.*.passthrough` option to Cross.toml\n  to support passing environment variables from the host to the Docker image.\n\n### Changed\n\n- Bumped OpenSSL version to 1.0.2k\n- Bumped QEMU version to 2.9.0\n\n## [v0.1.10] - 2017-04-02\n\n### Added\n\n- Cross compilation support for `x86_64-pc-windows-gnu`\n\n- Cross compilation support for Android targets\n\n### Changed\n\n- Bumped OpenSSL version to 1.0.2k\n\n## [v0.1.9] - 2017-02-08\n\n### Added\n\n- Support for ARM MUSL targets.\n\n### Changed\n\n- The automatic lockfile update that happens every time `cross` is invoked\n  should no longer hit the network when there's no git dependency to add/update.\n\n- The QEMU_STRACE variable is passed to the underlying Docker container. Paired\n  with `cross run`, this lets you get a trace of system call from the execution\n  of \"foreign\" (non x86_64) binaries.\n\n## [v0.1.8] - 2017-01-21\n\n### Added\n\n- Support for custom targets. Cross will now also try to use a docker image for\n  them. As with the built-in targets, one can override the image using\n  `[target.{}.image]` in Cross.toml.\n\n### Changed\n\n- Moved to a newer Xargo: v0.3.5\n\n## [v0.1.7] - 2017-01-19\n\n### Changed\n\n- Moved to a newer Xargo: v0.3.4\n\n### Fixed\n\n- QEMU interpreters were being register when not required, e.g. for the\n  `x86_64-unknown-linux-gnu` target.\n\n## [v0.1.6] - 2017-01-14\n\n### Fixed\n\n- Stable releases were picking the wrong image (wrong tag: 0.1.5 instead of\n  v0.1.5)\n\n## [v0.1.5] - 2017-01-14 [YANKED]\n\n### Added\n\n- `cross run` support for the thumb targets.\n\n- A `build.xargo` / `target.$TARGET.xargo` option to Cross.toml to use Xargo\n  instead of Cargo.\n\n- A `target.$TARGET.image` option to override the Docker image used for\n  `$TARGET`.\n\n- A `sparc64-unknown-linux-gnu` environment.\n\n- A `x86_64-unknown-dragonfly` environment.\n\n### Changed\n\n- Building older versions (<0.7.0) of the `openssl` crate is now supported.\n\n- Before Docker is invoked, `cross` will _always_ (re)generate the lockfile to\n  avoid errors later on due to read/write permissions. This removes the need to\n  call `cargo generate-lockfile` before `cross` in _all_ cases.\n\n## [v0.1.4] - 2017-01-07\n\n### Added\n\n- Support for the `arm-unknown-linux-gnueabi` target\n\n- `cross build` support for:\n  - `i686-unknown-freebsd`\n  - `x86_64-unknown-freebsd`\n  - `x86_64-unknown-netbsd`\n\n### Changed\n\n- It's no longer necessary to call `cargo generate-lockfile` before using\n  `cross` as `cross` will now take care of creating a lockfile when necessary.\n\n- The C environments for the `thumb` targets now include newlib (`libc.a`,\n  `libm.a`, etc.)\n\n### Fixed\n\n- A segfault when `cross` was trying to figure out the name of the user that\n  called it.\n\n## [v0.1.3] - 2017-01-01\n\n### Changed\n\n- Fix the `i686-unknown-linux-musl` target\n\n## [v0.1.2] - 2016-12-31\n\n### Added\n\n- Support for `i686-unknown-linux-musl`\n- Support for `cross build`ing crates for the `thumbv*-none-eabi*` targets.\n\n## [v0.1.1] - 2016-12-28\n\n### Added\n\n- Support for `x86_64-unknown-linux-musl`\n- Print shell commands when the verbose flag is used.\n- Support crossing from x86_64 osx to i686 osx\n\n## v0.1.0 - 2016-12-26\n\n- Initial release. Supports 12 targets.\n\n<!-- prettier-ignore-start -->\n<!-- next-url -->\n\n[Unreleased]: https://github.com/cross-rs/cross/compare/v0.2.5...HEAD\n\n[v0.2.5]: https://github.com/cross-rs/cross/compare/v0.2.4...v0.2.5\n\n[v0.2.4]: https://github.com/cross-rs/cross/compare/v0.2.3...v0.2.4\n\n[v0.2.3]: https://github.com/cross-rs/cross/compare/v0.2.2...v0.2.3\n\n[v0.2.2]: https://github.com/cross-rs/cross/compare/v0.2.1...v0.2.2\n[v0.2.1]: https://github.com/cross-rs/cross/compare/v0.2.0...v0.2.1\n[v0.2.0]: https://github.com/cross-rs/cross/compare/v0.1.16...v0.2.0\n[v0.1.16]: https://github.com/cross-rs/cross/compare/v0.1.15...v0.1.16\n[v0.1.15]: https://github.com/cross-rs/cross/compare/v0.1.14...v0.1.15\n[v0.1.14]: https://github.com/cross-rs/cross/compare/v0.1.13...v0.1.14\n[v0.1.13]: https://github.com/cross-rs/cross/compare/v0.1.12...v0.1.13\n[v0.1.12]: https://github.com/cross-rs/cross/compare/v0.1.11...v0.1.12\n[v0.1.11]: https://github.com/cross-rs/cross/compare/v0.1.10...v0.1.11\n[v0.1.10]: https://github.com/cross-rs/cross/compare/v0.1.9...v0.1.10\n[v0.1.9]: https://github.com/cross-rs/cross/compare/v0.1.8...v0.1.9\n[v0.1.8]: https://github.com/cross-rs/cross/compare/v0.1.7...v0.1.8\n[v0.1.7]: https://github.com/cross-rs/cross/compare/v0.1.6...v0.1.7\n[v0.1.6]: https://github.com/cross-rs/cross/compare/v0.1.5...v0.1.6\n[v0.1.5]: https://github.com/cross-rs/cross/compare/v0.1.4...v0.1.5\n[v0.1.4]: https://github.com/cross-rs/cross/compare/v0.1.3...v0.1.4\n[v0.1.3]: https://github.com/cross-rs/cross/compare/v0.1.2...v0.1.3\n[v0.1.2]: https://github.com/cross-rs/cross/compare/v0.1.1...v0.1.2\n[v0.1.1]: https://github.com/cross-rs/cross/compare/v0.1.0...v0.1.1\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# The Rust Code of Conduct\n\n## Conduct\n\n* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.\n* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.\n* Please be kind and courteous. There's no need to be mean or rude.\n* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.\n* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.\n* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term \"harassment\" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.\n* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Tools team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.\n* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.\n\n## Moderation\n\nThese are the policies for upholding our community's standards of conduct.\n\n1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)\n2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.\n3. Moderators will first respond to such remarks with a warning.\n4. If the warning is unheeded, the user will be \"kicked,\" i.e., kicked out of the communication channel to cool off.\n5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.\n6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.\n7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.\n8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.\n\nIn the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.\n\nAnd if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.\n\nThe enforcement policies listed above apply to the cross-rs [GitHub repositories]\nand the cross-rs [Matrix room].\n\n*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*\n\n[team]: https://github.com/cross-rs\n[GitHub repositories]: https://github.com/cross-rs\n[Matrix room]: https://matrix.to/#/#cross-rs:matrix.org\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Thank you for looking to contribute to cross. Have a new feature you'd like to add? Know how to fix an open bug? Want to add an image for a new target? We host documentation for how to contribute on our [wiki](https://github.com/cross-rs/cross/wiki/Contributing).\n\nPlease read our [code of conduct](https://github.com/cross-rs/cross/blob/main/CODE_OF_CONDUCT.md) so our community stays positive and welcoming. If you have any additional questions, please feel free to ask in either our [discussions](https://github.com/cross-rs/cross/discussions) or our [Matrix room](https://matrix.to/#/#cross-rs:matrix.org).\n\n<!--\nNOTE: we don't have specific information provided here since this\nwould require us to check the contributing changes into our Git\nhistory and therefore CI, which would make any changes to the\nrepository require separate commits or additional changes to the\ncode base, making either more difficult for new contributors or\nadding unnecessary commits.\n-->\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nbuild = \"src/build.rs\"\ndescription = \"Zero setup cross compilation and cross testing\"\ndocumentation = \"https://github.com/cross-rs/cross\"\nkeywords = [\"cross\", \"compilation\", \"testing\", \"tool\"]\nlicense = \"MIT OR Apache-2.0\"\nname = \"cross\"\nrepository = \"https://github.com/cross-rs/cross\"\nversion = \"0.2.5\"\nedition = \"2021\"\ninclude = [\n  \"src/**/*\",\n  \"docs/*.md\",\n  \"Cargo.toml\",\n  \"Cargo.lock\",\n  \"LICENSE*\",\n  \"README.md\",\n  \"assets/*\",\n]\nrust-version = \"1.92.0\"\n\n[features]\ndefault = []\ndev = []\n\n[workspace]\nmembers = [\"xtask\"]\n\n[dependencies]\nis-terminal = \"0.4.2\"\nclap = { version = \"4.1.4\", features = [\"derive\"] }\ncolor-eyre = { version = \"0.6.5\", default-features = false, features = [\"track-caller\"] }\neyre = \"0.6.8\"\nthiserror = \"2.0\"\nhome = \"0.5.4\"\nrustc_version = \"0.4.0\"\ntoml = \"0.9\"\nwhich = \"8.0\"\nshell-escape = \"0.1.5\"\nserde = { version = \"1.0.152\", features = [\"derive\"] }\nserde_json = { version = \"1.0.91\", features = [\"raw_value\"] }\nserde_ignored = \"0.1.7\"\nshell-words = \"1.1.0\"\nconst-sha1 = \"0.3\"\nsignal-hook = { version = \"0.4\" }\ndirectories = \"6.0\"\nwalkdir = { version = \"2.3.2\", optional = true }\ntempfile = \"3.3.0\"\nowo-colors = { version = \"4.0\", features = [\"supports-colors\"] }\nsemver = \"1.0.16\"\nis_ci = \"1.1.1\"\n\n[target.'cfg(not(windows))'.dependencies]\nnix = { version = \"0.30\", default-features = false, features = [\"user\"] }\nlibc = \"0.2.139\"\n\n[target.'cfg(windows)'.dependencies]\nwinapi = { version = \"0.3.9\", features = [\"winbase\"] }\ndunce = \"1.0.3\"\n\n[profile.release]\nlto = true\n\n[dev-dependencies]\nregex = \"1.7.1\"\nonce_cell = \"1.17.0\"\nignore = \"0.4.20\"\n\n[package.metadata.release]\npush = false\npublish = false\ntag = false\nconsolidate-commits = false\npre-release-hook = [\"cargo\", \"xtask\", \"changelog\", \"build\", \"--release\", \"{{version}}\"]\npre-release-commit-message = \"release version {{version}}\"\n\n[[package.metadata.release.pre-release-replacements]]\nfile = \"CHANGELOG.md\"\nsearch = \"\\\\.\\\\.\\\\.HEAD\"\nreplace = \"...v{{version}}\"\nexactly = 1\n\n[[package.metadata.release.pre-release-replacements]]\nfile = \"CHANGELOG.md\"\nsearch = \"<!-- next-url -->\"\nreplace = \"<!-- next-url -->\\n\\n[Unreleased]: https://github.com/cross-rs/{{crate_name}}/compare/v{{version}}...HEAD\"\nexactly = 1\n\n[[package.metadata.release.pre-release-replacements]]\nfile = \"docs/config_file.md\"\nsearch = \"(# Translates to `.*?:).*?(-centos`)\"\nreplace = \"${1}{{version}}$2\"\nexactly = 1\n\n[package.metadata.binstall]\npkg-url = \"{ repo }/releases/download/v{ version }/{ name }-{ target }.tar.gz\"\nbin-dir = \"{ bin }{ binary-ext }\"\npkg-fmt = \"tgz\"\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2017-2022 by the respective authors\nCopyright (c) 2016 Jorge Aparicio\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![crates.io](https://img.shields.io/crates/v/cross.svg)](https://crates.io/crates/cross)\n[![crates.io](https://img.shields.io/crates/d/cross.svg)](https://crates.io/crates/cross)\n[![CI](https://github.com/cross-rs/cross/actions/workflows/ci.yml/badge.svg?query=branch%3Amain)](https://github.com/cross-rs/cross/actions/workflows/ci.yml?query=branch)\n[![Matrix](https://img.shields.io/matrix/cross-rs:matrix.org)](https://matrix.to/#/#cross-rs:matrix.org)\n\n# `cross`\n\n> “Zero setup” cross compilation and “cross testing” of Rust crates\n\nThis project is developed and maintained by the [cross-rs] team.\nIt was previously maintained by the Rust Embedded Working Group Tools team.\nNew contributors are welcome! Please join our [Matrix room] and say hi.\n\n<p align=\"center\">\n<img\n  alt=\"`cross test`ing a crate for the aarch64-unknown-linux-gnu target\"\n  src=\"assets/cross-test.png\"\n  title=\"`cross test`ing a crate for the aarch64-unknown-linux-gnu target\"\n>\n<br>\n<em>`cross test`ing a crate for the aarch64-unknown-linux-gnu target</em>\n</p>\n\n## Features\n\n- `cross` will provide all the ingredients needed for cross compilation without\n  touching your system installation.\n\n- `cross` provides an environment, cross toolchain and cross compiled libraries,\n  that produces the most portable binaries.\n\n- “cross testing”, `cross` can test crates for architectures other than i686 and\n  x86_64.\n\n- The stable, beta and nightly channels are supported.\n\n## Dependencies\n\nSee our [Getting Started](./docs/getting-started.md) guide for detailed\ninstallation instructions.\n\n- [rustup](https://rustup.rs/)\n- A Linux kernel with [binfmt_misc] support is required for cross testing.\n\nOne of these container engines is required. If both are installed, `cross` will\ndefault to `docker`.\n\n- [Docker]. Note that on Linux non-sudo users need to be in the `docker` group or use rootless docker.\n  Read the container engine [install guide][docker_install] for the required installation and post-installation steps. Requires version 20.10 (API 1.40) or later.\n- [Podman]. Requires version 3.4.0 or later.\n\n## Installation\n\n```sh\ncargo install cross --git https://github.com/cross-rs/cross\n```\n\nIt's also possible to directly download the pre-compiled [release\nbinaries](https://github.com/cross-rs/cross/releases) or use\n[cargo-binstall](https://github.com/cargo-bins/cargo-binstall).\n\n## Usage\n\n`cross` has the exact same CLI as [Cargo](https://github.com/rust-lang/cargo)\nbut relies on Docker or Podman. For Docker, you'll have to start\nthe daemon before you can use it.\n\n```\n# (ONCE PER BOOT, on Linux)\n# Start the Docker daemon, if it's not already running using systemd\n# on WSL2 and other systems using SysVinit, use `sudo service docker start`.\n$ sudo systemctl start docker\n\n# MAGIC! This Just Works\n$ cross build --target aarch64-unknown-linux-gnu\n\n# EVEN MORE MAGICAL! This also Just Works\n$ cross test --target mips64-unknown-linux-gnuabi64\n\n# Obviously, this also Just Works\n$ cross rustc --target powerpc-unknown-linux-gnu --release -- -C lto\n```\n\nAdditional documentation can be found on the\n[wiki](https://github.com/cross-rs/cross/wiki) or the `docs/` subfolder.\n\n## Configuration\n\n### Configuring cross behavior\n\nYou have four options to configure `cross`. All of these options use the TOML\nformat for configuration and the possible configuration values are documented\n[here][config_file].\n\n#### Option 1: Configuring `cross` directly in your `Cargo.toml`\n\nYou can directly set [configuration values][config_file] in your `Cargo.toml`\nfile, under the `[workspace.metadata.cross]` table, i.e. key prefix. An example\nconfig snippet would look like this:\n\n```toml,cargo\n[workspace.metadata.cross.target.aarch64-unknown-linux-gnu]\n# Install libssl-dev:arm64, see <https://github.com/cross-rs/cross/blob/main/docs/custom_images.md#adding-dependencies-to-existing-images>\npre-build = [\n    \"dpkg --add-architecture $CROSS_DEB_ARCH\",\n    \"apt-get update && apt-get --assume-yes install libssl-dev:$CROSS_DEB_ARCH\"\n]\n[workspace.metadata.cross.target.armv7-unknown-linux-gnueabi]\nimage = \"my/image:latest\"\n[workspace.metadata.cross.build]\nenv.volumes = [\"A_DIRECTORY=/path/to/volume\"]\n```\n\n#### Option 2: Configuring `cross` via a `Cross.toml` file\n\nYou can put your [configuration][config_file] inside a `Cross.toml` file\nin your project root directory.\n\n#### Option 3: Using `CROSS_CONFIG` to specify the location of your configuration\n\nBy setting the `CROSS_CONFIG` environment variable, you can tell `cross` where\nit should search for the config file. This way you are not limited to a\n`Cross.toml` file in the project root.\n\n#### Option 4: Configuring `cross` through environment variables\n\nBesides the TOML-based configuration files, config can be passed through\n[environment variables][docs_env_vars], too.\n\n\n### Docker in Docker\n\nWhen running `cross` from inside a container, `cross` needs access to\nthe hosts docker daemon itself. This is normally achieved by mounting the\ndocker daemons socket `/var/run/docker.sock`. For example:\n\n```\n$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v .:/project \\\n  -w /project my/development-image:tag cross build --target mips64-unknown-linux-gnuabi64\n```\n\nThe image running `cross` requires the rust development tools to be installed.\n\nWith this setup `cross` must find and mount the correct host paths into the\ncontainer used for cross compilation. This includes the original project\ndirectory as well as the root path of the parent container to give access to\nthe rust build tools.\n\nTo inform `cross` that it is running inside a container set\n`CROSS_CONTAINER_IN_CONTAINER=true`.\n\nA development or CI container can be created like this:\n\n```\nFROM rust:1\n\n# set CROSS_CONTAINER_IN_CONTAINER to inform `cross` that it is executed from within a container\nENV CROSS_CONTAINER_IN_CONTAINER=true\n\n# install `cross`\nRUN cargo install cross\n\n...\n\n```\n\n**Limitations**: Finding the mount point for the containers root directory is\ncurrently only available for the overlayfs2 storage driver. In order to access\nthe parent containers rust setup, the child container mounts the parents\noverlayfs. The parent must not be stopped before the child container, as the\noverlayfs can not be unmounted correctly by Docker if the child container still\naccesses it.\n\n\n### Explicitly choose the container engine\n\nBy default, `cross` tries to use [Docker] or [Podman], in that order.\nIf you want to choose a container engine explicitly, you can set the\nbinary name (or path) using the `CROSS_CONTAINER_ENGINE`\nenvironment variable.\n\nFor example in case you want use [Podman], you can set `CROSS_CONTAINER_ENGINE=podman`.\n\n\n## Supported targets\n\nA target is considered as “supported” if `cross` can cross compile a\n“non-trivial” (binary) crate, usually Cargo, for that target.\n\nTesting support (`cross test`) is more complicated. It relies on [QEMU]\nemulation, so testing may fail due to QEMU bugs rather than bugs in your crate.\nThat said, a target has a ✓ in `test` column of the table below if it can run\nthe [`compiler-builtins`] test suite.\n\n[QEMU]: https://www.qemu.org/\n[`compiler-builtins`]: https://github.com/rust-lang-nursery/compiler-builtins\n\nAlso, testing is very slow. `cross test` runs units tests *sequentially* because\nQEMU gets upset when you spawn multiple threads. This means that, if one of your\nunit tests spawns threads, then it's more likely to fail or, worst, never\nterminate.\n\n| Target                                 |  libc  |  GCC   | C++ | QEMU  | `test` |\n|----------------------------------------|-------:|-------:|:---:|------:|:------:|\n| `aarch64-linux-android` [1]            | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `aarch64-unknown-linux-gnu`            | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `aarch64-unknown-linux-gnu:centos` [7] | 2.17   | 4.8.5  |     | 4.2.1 |   ✓    |\n| `aarch64-unknown-linux-musl`           | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `aarch64_be-unknown-linux-gnu`         | 2.36   | 14.2.0 | ✓   | 6.1.0 |   ✓    |\n| `arm-linux-androideabi` [1]            | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `arm-unknown-linux-gnueabi`            | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `arm-unknown-linux-gnueabihf`          | 2.31   | 8.5.0  | ✓   | 6.1.0 |   ✓    |\n| `arm-unknown-linux-musleabi`           | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `arm-unknown-linux-musleabihf`         | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `armv5te-unknown-linux-gnueabi`        | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `armv5te-unknown-linux-musleabi`       | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `armv7-linux-androideabi` [1]          | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `armv7-unknown-linux-gnueabi`          | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `armv7-unknown-linux-gnueabihf`        | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `armv7-unknown-linux-musleabi`         | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `armv7-unknown-linux-musleabihf`       | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `i586-unknown-linux-gnu`               | 2.31   | 9.4.0  | ✓   | N/A   |   ✓    |\n| `i586-unknown-linux-musl`              | 1.2.3  | 9.2.0  | ✓   | N/A   |   ✓    |\n| `i686-unknown-freebsd`                 | 1.6    | 13.3.0 | ✓   | N/A   |        |\n| `i686-linux-android` [1]               | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `i686-pc-windows-gnu`                  | N/A    | 9.4    | ✓   | N/A   |   ✓    |\n| `i686-unknown-linux-gnu`               | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `loongarch64-unknown-linux-gnu`        | 2.36   | 14.2.0 | ✓   | 8.2.2 |   ✓    |\n| `loongarch64-unknown-linux-musl`       | 1.2.5  | 14.2.0 | ✓   | 8.2.2 |   ✓    |\n| `mips-unknown-linux-gnu`               | 2.30   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `mips-unknown-linux-musl`              | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `mips64-unknown-linux-gnuabi64`        | 2.30   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `mips64-unknown-linux-muslabi64`       | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `mips64el-unknown-linux-gnuabi64`      | 2.30   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `mips64el-unknown-linux-muslabi64`     | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `mipsel-unknown-linux-gnu`             | 2.30   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `mipsel-unknown-linux-musl`            | 1.2.3  | 9.2.0  | ✓   | 6.1.0 |   ✓    |\n| `powerpc-unknown-linux-gnu`            | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `powerpc64-unknown-linux-gnu`          | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `powerpc64le-unknown-linux-gnu`        | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `riscv64gc-unknown-linux-gnu`          | 2.35   | 11.4.0 | ✓   | 8.2.2 |   ✓    |\n| `riscv64gc-unknown-linux-musl`         | 1.2.5  | 14.2.0 | ✓   | 8.2.2 |   ✓    |\n| `s390x-unknown-linux-gnu`              | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `sparc64-unknown-linux-gnu`            | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `sparcv9-sun-solaris`                  | 1.22.7 | 8.4.0  | ✓   | N/A   |        |\n| `thumbv6m-none-eabi` [4]               | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv7em-none-eabi` [4]              | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv7em-none-eabihf` [4]            | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv7m-none-eabi` [4]               | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv7neon-linux-androideabi` [1]    | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `thumbv7neon-unknown-linux-gnueabihf`  | 2.31   | 9.4.0  | ✓   | N/A   |   ✓    |\n| `thumbv8m.base-none-eabi` [4]          | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv8m.main-none-eabi` [4]          | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `thumbv8m.main-none-eabihf` [4]        | 3.3.0  | 9.2.1  |     | N/A   |        |\n| `wasm32-unknown-emscripten` [6]        | 3.1.14 | 15.0.0 | ✓   | N/A   |   ✓    |\n| `x86_64-linux-android` [1]             | 9.0.8  | 9.0.8  | ✓   | 6.1.0 |   ✓    |\n| `x86_64-pc-windows-gnu`                | N/A    | 9.3    | ✓   | N/A   |   ✓    |\n| `x86_64-pc-solaris`                    | 1.22.7 | 8.4.0  | ✓   | N/A   |        |\n| `x86_64-unknown-freebsd`               | 1.6    | 13.3.0 | ✓   | N/A   |        |\n| `x86_64-unknown-dragonfly` [2] [3]     | 6.0.1  | 10.3.0 | ✓   | N/A   |        |\n| `x86_64-unknown-illumos`               | 1.20.4 | 8.4.0  | ✓   | N/A   |        |\n| `x86_64-unknown-linux-gnu`             | 2.31   | 9.4.0  | ✓   | 6.1.0 |   ✓    |\n| `x86_64-unknown-linux-gnu:centos` [5]  | 2.17   | 4.8.5  | ✓   | 4.2.1 |   ✓    |\n| `x86_64-unknown-linux-musl`            | 1.2.3  | 9.2.0  | ✓   | N/A   |   ✓    |\n| `x86_64-unknown-netbsd` [3]            | 9.2.0  | 9.4.0  | ✓   | N/A   |        |\n<!--| `asmjs-unknown-emscripten` [7]       | 3.1.14 | 15.0.0  | ✓   | N/A   |   ✓    |-->\n\n[1] libc = bionic; Only works with native tests, that is, tests that do not\n    depends on the Android Runtime. For i686 some tests may fails with the\n    error `assertion failed: signal(libc::SIGPIPE, libc::SIG_IGN) !=\n    libc::SIG_ERR`, see [issue\n    #140](https://github.com/cross-rs/cross/issues/140) for more information.\n\n[2] No `std` component available.\n\n[3] For some \\*BSD and Solaris targets, the libc column indicates the OS\n    release version from which libc was extracted.\n\n[4] libc = newlib\n\n[5] Must change\n    `image = \"ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main-centos\"` in\n    `Cross.toml` for `[target.x86_64-unknown-linux-gnu]` to use the\n    CentOS7-compatible target.\n\n[6] libc = emscripten and GCC = clang\n\n[7] Must change\n    `image = \"ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main-centos\"` in\n    `Cross.toml` for `[target.aarch64-unknown-linux-gnu]` to use the\n    CentOS7-compatible target.\n\n<!--[7] libc = emscripten and GCC = clang. The Docker images for these targets are currently not built automatically\ndue to a [compiler bug](https://github.com/rust-lang/rust/issues/98216), you will have to build them yourself for now.-->\n\nAdditional Dockerfiles for other targets can be found in\n[cross-toolchains](https://github.com/cross-rs/cross-toolchains). These include\nMSVC and Apple Darwin targets, which we cannot ship pre-built images of.\n\n\n## Debugging\n\n### QEMU_STRACE (v0.1.9+)\n\nYou can set the QEMU_STRACE variable when you use `cross run` to get a backtrace\nof system calls from “foreign” (non x86_64) binaries.\n\n```\n$ cargo new --bin hello && cd $_\n\n$ QEMU_STRACE=1 cross run --target aarch64-unknown-linux-gnu\n9 brk(NULL) = 0x0000004000023000\n9 uname(0x4000823128) = 0\n(..)\n9 write(1,0xa06320,14)Hello, world!\n = 14\n9 sigaltstack(0x4000823588,(nil)) = 0\n9 munmap(0x0000004000b16000,16384) = 0\n9 exit_group(0)\n```\n\n## Minimum Supported Rust Version (MSRV)\n\nThis crate is guaranteed to compile on stable Rust 1.92.0 and up. It *might*\ncompile with older versions but that may change in any new patch release.\n\nSome cross-compilation targets require a later Rust version, and using Xargo\nrequires a nightly Rust toolchain.\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or\n  http://www.apache.org/licenses/LICENSE-2.0)\n- MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n## Code of Conduct\n\nContribution to this crate is organized under the terms of the [Rust Code of\nConduct][CoC], the maintainer of this crate, the [cross-rs] team, promises\nto intervene to uphold that code of conduct.\n\n[CoC]: CODE_OF_CONDUCT.md\n[cross-rs]: https://github.com/cross-rs\n[Docker]: https://www.docker.com\n[Podman]: https://podman.io\n[Matrix room]: https://matrix.to/#/#cross-rs:matrix.org\n[docker_install]: https://github.com/cross-rs/cross/wiki/Getting-Started#installing-a-container-engine\n[binfmt_misc]: https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html\n[config_file]: ./docs/config_file.md\n[docs_env_vars]: ./docs/environment_variables.md\n"
  },
  {
    "path": "ci/build_release.sh",
    "content": "#!/usr/bin/env bash\n\nset -eux\n\ncargo install --path . --force\n\nif [[ \"${TARGET}\" = \"x86_64-unknown-linux-gnu\" ]]; then\n    # for this particular target, `cross` command below will try to reuse the artifacts (e.g. build\n    # script binaries) generated by the previous `cargo` command. that will fail to run because the\n    # cross environment has glibc 2.17 and the CI env (ubuntu 20.04) produces binaries that depend\n    # on glibc 2.18+\n    # to avoid the issue discard the build script artifacts produced by the previous command\n    cargo clean\nfi\n\ncross build --target \"${TARGET}\" --release\n\nrm -rf \"${BUILD_BINARIESDIRECTORY}\"\nmkdir \"${BUILD_BINARIESDIRECTORY}\"\n\nif [[ -f \"target/${TARGET}/release/cross.exe\" ]]; then\n    mv \"target/${TARGET}/release/cross.exe\" \"${BUILD_BINARIESDIRECTORY}/\"\nelse\n    mv \"target/${TARGET}/release/cross\" \"${BUILD_BINARIESDIRECTORY}/\"\nfi\n"
  },
  {
    "path": "ci/shared.sh",
    "content": "#!/usr/bin/env bash\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\nPROJECT_HOME=$(dirname \"${ci_dir}\")\nexport PROJECT_HOME\nCARGO_TMP_DIR=\"${PROJECT_HOME}/target/tmp\"\nexport CARGO_TMP_DIR\n\nif [[ -n \"${CROSS_CONTAINER_ENGINE}\" ]]; then\n    CROSS_ENGINE=\"${CROSS_CONTAINER_ENGINE}\"\nelif command -v docker >/dev/null 2>&1; then\n    CROSS_ENGINE=docker\nelse\n    CROSS_ENGINE=podman\nfi\nexport CROSS_ENGINE\n\nfunction retry {\n    local tries=\"${TRIES-5}\"\n    local timeout=\"${TIMEOUT-1}\"\n    local try=0\n    local exit_code=0\n\n    while (( try < tries )); do\n        if \"${@}\"; then\n            return 0\n        else\n            exit_code=$?\n        fi\n\n        sleep \"${timeout}\"\n        echo \"Retrying ...\" 1>&2\n        try=$(( try + 1 ))\n        timeout=$(( timeout * 2 ))\n    done\n\n    return ${exit_code}\n}\n\nfunction mkcargotemp {\n    local td=\n    td=\"$CARGO_TMP_DIR\"/$(mktemp -u \"${@}\" | xargs basename)\n    mkdir -p \"$td\"\n    echo '# Cargo.toml\n    [workspace]\n    members = [\"'\"$(basename \"$td\")\"'\"]\n    ' > \"$CARGO_TMP_DIR\"/Cargo.toml\n    echo \"$td\"\n}\n\nfunction binary_path() {\n    local binary=\"${1}\"\n    local home=\"${2}\"\n    local build_mode=\"${3}\"\n    local cross=\"${home}/target/${build_mode}/${binary}\"\n\n    case \"$OSTYPE\" in\n        msys*|cygwin*)\n            cross=\"${cross}.exe\"\n            ;;\n        *)\n            ;;\n    esac\n\n    echo \"${cross}\"\n}\n"
  },
  {
    "path": "ci/test-bisect.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1091,SC1090\n\n# test to see that custom toolchains work\n\nset -x\nset -eo pipefail\n\nif [[ -z \"${TARGET}\" ]]; then\n    export TARGET=\"aarch64-unknown-linux-gnu\"\nfi\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\n\nmain() {\n    local td=\n    local err=\n\n    retry cargo fetch\n    cargo build\n    cargo install cargo-bisect-rustc --debug\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS\n\n    td=\"$(mktemp -d)\"\n    git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\n\n    pushd \"${td}\"\n    retry cargo fetch\n    # shellcheck disable=SC2016\n    echo '#!/usr/bin/env bash\nexport CROSS_CUSTOM_TOOLCHAIN=1\n\"${CROSS}\" run --target '\"${TARGET}\"'\ncargo -V | grep 2022-06\n' > bisect.sh\n    chmod +x bisect.sh\n\n    if ! err=$(cargo-bisect-rustc --start 2022-07-01 --end 2022-07-03 --script=./bisect.sh --target \"${TARGET}\" 2>&1); then\n        if [[ \"${err}\" != *\"does not reproduce the regression\"* ]]; then\n            echo \"${err}\"\n            exit 1\n        fi\n    else\n        echo \"should have failed, instead succeeded\" 1>&2\n        exit 1\n    fi\n    popd\n\n    rm -rf \"${td}\"\n}\n\nmain\n"
  },
  {
    "path": "ci/test-cross-image.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2086,SC1091,SC1090\n\nset -x\nset -eo pipefail\n\nif [[ -z \"${TARGET}\" ]]; then\n    export TARGET=\"aarch64-unknown-linux-gnu\"\nfi\n# ^^subst is not supported on macOS bash (bash <4)\n# shellcheck disable=SC2155\nexport TARGET_UPPER=$(echo \"$TARGET\" | awk '{print toupper($0)}')\n\nif [[ \"${IMAGE}\" ]]; then\n    # shellcheck disable=SC2140\n    export \"CROSS_TARGET_${TARGET_UPPER//-/_}_IMAGE\"=\"${IMAGE}\"\nfi\n\nif [[ -z \"${CROSS_TARGET_CROSS_IMAGE}\" ]]; then\n    CROSS_TARGET_CROSS_IMAGE=\"ghcr.io/cross-rs/cross:main\"\nfi\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nmain() {\n\n    docker run --rm -e TARGET -e \"CROSS_TARGET_${TARGET_UPPER//-/_}_IMAGE\" \\\n        -v /var/run/docker.sock:/var/run/docker.sock \\\n        \"${CROSS_TARGET_CROSS_IMAGE}\" sh -c '\n#!/usr/bin/env sh\ntd=\"$(mktemp -d)\"\ngit clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\ncd \"${td}\"\ncross run --target \"${TARGET}\"\n'\ntd=\"$(mkcargotemp -d)\"\ngit clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\ncd \"${td}\"\necho '# Cross.toml\n[target.'${TARGET}']\npre-build = [\"exit 0\"]\n' > Cross.toml\ndocker run --rm -e TARGET -e CROSS_CONTAINER_IN_CONTAINER=1 -e \"CROSS_TARGET_${TARGET_UPPER//-/_}_IMAGE\" \\\n        -v /var/run/docker.sock:/var/run/docker.sock \\\n        -v $PWD:/mount -w /mount \\\n        \"${CROSS_TARGET_CROSS_IMAGE}\" cross build --target \"${TARGET}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "ci/test-docker-in-docker.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1004,SC1091,SC1090\n\n# test to see that running docker-in-docker works\n\nset -x\nset -eo pipefail\n\nif [[ -z \"${TARGET}\" ]]; then\n    export TARGET=\"aarch64-unknown-linux-gnu\"\nfi\n# ^^subst is not supported on macOS bash (bash <4)\n# shellcheck disable=SC2155\nexport TARGET_UPPER=$(echo \"$TARGET\" | awk '{print toupper($0)}')\n\nif [[ \"${IMAGE}\" ]]; then\n    # shellcheck disable=SC2140\n    export \"CROSS_TARGET_${TARGET_UPPER//-/_}_IMAGE\"=\"${IMAGE}\"\nfi\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nmain() {\n    docker run --platform linux/amd64 -v \"${PROJECT_HOME}\":\"${PROJECT_HOME}\" -w \"${PROJECT_HOME}\" \\\n        --rm -e TARGET -e TARGET_UPPER -e RUSTFLAGS -e RUST_TEST_THREADS \\\n        -e LLVM_PROFILE_FILE -e CARGO_INCREMENTAL \\\n        -e \"CROSS_TARGET_${TARGET_UPPER//-/_}_IMAGE\" \\\n        -v /var/run/docker.sock:/var/run/docker.sock \\\n        docker:20.10-dind sh -c '\n#!/usr/bin/env sh\nset -x\nset -euo pipefail\n\napk add curl\ncurl --proto \"=https\" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y\nsource \"${HOME}/.cargo/env\"\n\n# building on release is slow\napk add libgcc gcc musl-dev\ncargo test --workspace\ncargo install --path . --force --debug\n\nexport CROSS_CONTAINER_IN_CONTAINER=1\n\napk add git\ntd=\"$(mktemp -d)\"\ngit clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\ncd \"${td}\"\ncross run --target \"${TARGET}\" --verbose\n\ntd=\"$(mktemp -d)\"\ngit clone --depth 1 https://github.com/cross-rs/test-workspace \"${td}\"\ncd \"${td}\"\ncross build --target \"${TARGET}\" --workspace \\\n    --manifest-path=\"./workspace/Cargo.toml\" --verbose\neval CROSS_TARGET_${TARGET_UPPER//-/_}_PRE_BUILD=\"exit\" cross build --target \"${TARGET}\" --workspace \\\n    --manifest-path=\"./workspace/Cargo.toml\" --verbose\ncd workspace\ncross build --target \"${TARGET}\" --workspace --verbose\neval CROSS_TARGET_${TARGET_UPPER//-/_}_PRE_BUILD=\"exit\" cross build --target \"${TARGET}\" --workspace --verbose\ncd binary\ncross run --target \"${TARGET}\" --verbose\neval CROSS_TARGET_${TARGET_UPPER//-/_}_PRE_BUILD=\"exit\" cross run --target \"${TARGET}\" --verbose\n'\n}\n\nmain\n"
  },
  {
    "path": "ci/test-foreign-toolchain.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1091,SC1090\n\n# test to see that foreign toolchains work\n\nset -x\nset -eo pipefail\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nmain() {\n    local td=\n\n    retry cargo fetch\n    cargo build\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS\n\n    td=\"$(mkcargotemp -d)\"\n\n    pushd \"${td}\"\n    cargo init --bin --name foreign_toolchain\n    # shellcheck disable=SC2016\n    echo '# Cross.toml\n[build]\ndefault-target = \"x86_64-unknown-linux-musl\"\n\n[target.\"x86_64-unknown-linux-musl\"]\nimage.name = \"alpine:edge\"\nimage.toolchain = [\"x86_64-unknown-linux-musl\"]\npre-build = [\"apk add --no-cache gcc musl-dev\"]' >\"${CARGO_TMP_DIR}\"/Cross.toml\n\n    \"${CROSS}\" run -v\n\n    local tmp_basename\n    tmp_basename=$(basename \"${CARGO_TMP_DIR}\")\n    \"${CROSS_ENGINE}\" images --format '{{.Repository}}:{{.Tag}}' --filter 'label=org.cross-rs.for-cross-target' | grep \"cross-custom-${tmp_basename}\" | xargs -t \"${CROSS_ENGINE}\" rmi\n\n    echo '# Cross.toml\n[build]\ndefault-target = \"x86_64-unknown-linux-gnu\"\n\n[target.x86_64-unknown-linux-gnu]\npre-build = [\n    \"apt-get update && apt-get install -y libc6 g++-x86-64-linux-gnu libc6-dev-amd64-cross\",\n]\n\n[target.x86_64-unknown-linux-gnu.env]\npassthrough = [\n    \"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc\",\n    \"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=/qemu-runner x86_64\",\n    \"CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc\",\n    \"CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++\",\n]\n\n[target.x86_64-unknown-linux-gnu.image]\nname = \"ubuntu:20.04\"\ntoolchain = [\"aarch64-unknown-linux-gnu\"]\n    ' >\"${CARGO_TMP_DIR}\"/Cross.toml\n\n    \"${CROSS}\" build -v\n\n    \"${CROSS_ENGINE}\" images --format '{{.Repository}}:{{.Tag}}' --filter 'label=org.cross-rs.for-cross-target' | grep \"cross-custom-${tmp_basename}\" | xargs \"${CROSS_ENGINE}\" rmi\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nmain\n"
  },
  {
    "path": "ci/test-podman.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1091,SC1090\n\n# test to see that running and building images with podman works.\n\nset -x\nset -eo pipefail\n\nexport CROSS_CONTAINER_ENGINE=podman\nif [[ -z \"${TARGET}\" ]]; then\n    export TARGET=\"aarch64-unknown-linux-gnu\"\nfi\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nmain() {\n    local td=\n    local parent=\n    local target=\"${TARGET}\"\n\n    retry cargo fetch\n    cargo build\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS\n\n    td=\"$(mkcargotemp -d)\"\n    parent=$(dirname \"${td}\")\n    pushd \"${td}\"\n    cargo init --bin --name \"hello\" .\n\n    echo '[build]\npre-build = [\"apt-get update\"]' > \"${parent}/Cross.toml\"\n\n    CROSS_CONTAINER_ENGINE=\"${CROSS_ENGINE}\" \"${CROSS}\" build --target \"${target}\" --verbose\n\n    popd\n    rm -rf \"${td}\"\n}\n\nmain\n"
  },
  {
    "path": "ci/test-remote.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1091,SC1090\n\n# test to see that remote docker support works.\n\nset -x\nset -eo pipefail\n\nexport CROSS_REMOTE=1\nif [[ -z \"${TARGET}\" ]]; then\n    export TARGET=\"aarch64-unknown-linux-gnu\"\nfi\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nmain() {\n    local err=\n\n    retry cargo fetch\n    cargo build\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS\n    CROSS_UTIL=$(binary_path cross-util \"${PROJECT_HOME}\" debug)\n    export CROSS_UTIL\n\n    # if the create volume fails, ensure it exists.\n    if ! err=$(\"${CROSS_UTIL}\" volumes create 2>&1 >/dev/null); then\n        if [[ \"${err}\" != *\"already exists\"* ]]; then\n            echo \"${err}\"\n            exit 1\n        fi\n    fi\n    cross_test_cpp\n    \"${CROSS_UTIL}\" volumes remove\n\n    # ensure the data volume was removed.\n    cross_test_cpp\n}\n\ncross_test_cpp() {\n    local td=\n    td=\"$(mkcargotemp -d)\"\n\n    git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\n\n    pushd \"${td}\"\n    retry cargo fetch\n    \"${CROSS}\" run --target \"${TARGET}\" | grep \"Hello, world!\"\n    sed -i 's/Hello, world/Hello, test/g' hellopp.cc\n    \"${CROSS}\" run --target \"${TARGET}\" | grep \"Hello, test!\"\n    popd\n\n    rm -rf \"${td}\"\n}\n\nmain\n"
  },
  {
    "path": "ci/test-zig-image.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2086,SC1091,SC1090\n\nset -x\nset -eo pipefail\n\n# NOTE: \"${@}\" is an unbound variable for bash 3.2, which is the\n# installed version on macOS. likewise, \"${var[@]}\" is an unbound\n# error if var is an empty array.\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\n# zig cc is very slow: only use a few targets.\nTARGETS=(\n    \"aarch64-unknown-linux-gnu\"\n    \"aarch64-unknown-linux-musl\"\n    # disabled, see https://github.com/cross-rs/cross/issues/1425\n    #\"i586-unknown-linux-gnu\"\n    #\"i586-unknown-linux-musl\"\n)\n\n# on CI, it sets `CROSS_TARGET_ZIG_IMAGE` rather than `CROSS_BUILD_ZIG_IMAGE`\nif [[ -n \"${CROSS_TARGET_ZIG_IMAGE}\" ]]; then\n    export CROSS_BUILD_ZIG_IMAGE=\"${CROSS_TARGET_ZIG_IMAGE}\"\n    unset CROSS_TARGET_ZIG_IMAGE\nfi\n\nmain() {\n    export CROSS_BUILD_ZIG=1\n\n    local td=\n    local target=\n\n    retry cargo fetch\n    cargo build\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS\n\n    td=\"$(mktemp -d)\"\n    git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\n    pushd \"${td}\"\n\n    for target in \"${TARGETS[@]}\"; do\n        CROSS_CONTAINER_ENGINE=\"${CROSS_ENGINE}\" \"${CROSS}\" build --target \"${target}\" --verbose\n        # note: ensure #724 doesn't replicate during CI.\n        # https://github.com/cross-rs/cross/issues/724\n        cargo clean\n    done\n\n    popd\n    rm -rf \"${td}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "ci/test.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2086,SC1091,SC1090\n\nset -x\nset -eo pipefail\n\n# NOTE: \"${@}\" is an unbound variable for bash 3.2, which is the\n# installed version on macOS. likewise, \"${var[@]}\" is an unbound\n# error if var is an empty array.\n\nci_dir=$(dirname \"${BASH_SOURCE[0]}\")\nci_dir=$(realpath \"${ci_dir}\")\n. \"${ci_dir}\"/shared.sh\n\nworkspace_test() {\n    \"${CROSS[@]}\" build --target \"${TARGET}\" --workspace \"$@\" ${CROSS_FLAGS}\n    \"${CROSS[@]}\" run --target \"${TARGET}\" -p binary \"$@\" ${CROSS_FLAGS}\n    \"${CROSS[@]}\" run --target \"${TARGET}\" --bin dependencies \\\n        --features=dependencies \"$@\" ${CROSS_FLAGS}\n}\n\nmain() {\n    local td=\n\n    retry cargo fetch\n    cargo build\n\n    # Unset RUSTFLAGS\n    export RUSTFLAGS=\"\"\n\n    export QEMU_STRACE=1\n\n    # ensure we have the proper toolchain and optional rust flags\n    CROSS=$(binary_path cross \"${PROJECT_HOME}\" debug)\n    export CROSS=(\"${CROSS}\")\n    export CROSS_FLAGS=\"-v\"\n    if (( ${BUILD_STD:-0} )); then\n        # use build-std instead of xargo, due to xargo being\n        # maintenance-only. build-std requires a nightly compiler\n        rustup toolchain add nightly\n        CROSS_FLAGS=\"${CROSS_FLAGS} -Zbuild-std\"\n        CROSS+=(\"+nightly\")\n        if [[ \"${TARGET}\" == *\"mips\"* ]]; then\n            # workaround for https://github.com/cross-rs/cross/issues/1322 & https://github.com/rust-lang/rust/issues/108835\n            [[ ! \"$RUSTFLAGS\" =~ opt-level ]] && export RUSTFLAGS=\"${RUSTFLAGS:+$RUSTFLAGS }-C opt-level=1\"\n        fi\n    elif ! (( ${STD:-0} )); then\n        # don't use xargo: should have native support just from rustc\n        rustup toolchain add nightly\n        CROSS+=(\"+nightly\")\n    fi\n\n    if (( ${STD:-0} )); then\n        # test `cross check`\n        td=$(mkcargotemp -d)\n        cargo init --lib --name foo \"${td}\"\n        pushd \"${td}\"\n        echo '#![no_std]' > src/lib.rs\n        \"${CROSS[@]}\" check --target \"${TARGET}\" ${CROSS_FLAGS}\n        popd\n        rm -rf \"${td}\"\n    else\n        # `cross build` test for targets where `std` is not available\n        td=$(mkcargotemp -d)\n\n        git clone \\\n            --depth 1 \\\n            --recursive \\\n            https://github.com/rust-lang-nursery/compiler-builtins \"${td}\"\n\n        pushd \"${td}\"\n        retry cargo fetch\n        # don't use xargo: should have native support just from rustc\n        rustup toolchain add nightly\n        cross_build --lib --target \"${TARGET}\"\n        popd\n\n        rm -rf \"${td}\"\n    fi\n\n    # `cross build` test for the other targets\n    if [[ \"${TARGET}\" == *-unknown-emscripten ]]; then\n        td=$(mkcargotemp -d)\n\n        pushd \"${td}\"\n        cargo init --lib --name foo .\n        retry cargo fetch\n        cross_build --target \"${TARGET}\"\n        popd\n\n        rm -rf \"${td}\"\n    # thumb targets are tested in later steps\n    elif [[ \"${TARGET}\" != thumb* ]]; then\n        td=$(mkcargotemp -d)\n\n        pushd \"${td}\"\n        # test that linking works\n        cargo init --bin --name hello .\n        retry cargo fetch\n        cross_build --target \"${TARGET}\"\n        popd\n\n        rm -rf \"${td}\"\n    fi\n\n    if (( ${RUN:-0} )); then\n        # `cross test` test\n        if (( ${DYLIB:-0} )); then\n            td=$(mkcargotemp -d)\n\n            pushd \"${td}\"\n            cargo init --lib --name foo .\n            cross_test --target \"${TARGET}\"\n            cross_bench --target \"${TARGET}\"\n            popd\n\n            rm -rf \"${td}\"\n        fi\n\n        # `cross run` test\n        case \"${TARGET}\" in\n            thumb*-none-eabi*)\n                td=$(mkcargotemp -d)\n\n                git clone \\\n                    --depth 1 \\\n                    --recursive \\\n                    https://github.com/japaric/cortest \"${td}\"\n\n                pushd \"${td}\"\n                cross_run --target \"${TARGET}\" --example hello --release\n                popd\n\n                rm -rf \"${td}\"\n            ;;\n            *)\n                td=$(mkcargotemp -d)\n\n                cargo init --bin --name hello \"${td}\"\n\n                pushd \"${td}\"\n                mkdir examples tests\n                echo \"fn main() { println!(\\\"Example!\\\"); }\" > examples/e.rs\n                echo \"#[test] fn t() {}\" > tests/t.rs\n                cross_run --target \"${TARGET}\"\n                cross_run --target \"${TARGET}\" --example e\n                cross_test --target \"${TARGET}\"\n                cross_bench --target \"${TARGET}\"\n                popd\n\n                rm -rf \"${td}\"\n                td=$(mkcargotemp -d)\n                git clone \\\n                    --depth 1 \\\n                    --recursive \\\n                    https://github.com/cross-rs/test-workspace \"${td}\"\n\n                pushd \"${td}\"\n                TARGET=\"${TARGET}\" workspace_test --manifest-path=\"./workspace/Cargo.toml\"\n                pushd \"workspace\"\n                TARGET=\"${TARGET}\" workspace_test\n                pushd \"binary\"\n                \"${CROSS[@]}\" run --target \"${TARGET}\" ${CROSS_FLAGS}\n                popd\n                popd\n                popd\n            ;;\n        esac\n\n    fi\n\n    # Test C++ support in a no_std context\n    if (( ${CPP:-0} )); then\n        td=\"$(mkcargotemp -d)\"\n\n        git clone --depth 1 https://github.com/cross-rs/rust-cpp-accumulate \"${td}\"\n\n        pushd \"${td}\"\n        retry cargo fetch\n        cross_build --target \"${TARGET}\"\n        popd\n\n        rm -rf \"${td}\"\n    fi\n\n    # Test C++ support\n    if (( ${STD:-0} )) && (( ${CPP:-0} )); then\n        td=\"$(mkcargotemp -d)\"\n\n        git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word \"${td}\"\n\n        pushd \"${td}\"\n        retry cargo fetch\n        if (( ${RUN:-0} )); then\n            cross_run --target \"${TARGET}\"\n        else\n            cross_build --target \"${TARGET}\"\n        fi\n        popd\n\n        rm -rf \"${td}\"\n    fi\n\n    # special tests for a shared C runtime, since we disable the shared c++ runtime\n    # https://github.com/cross-rs/cross/issues/902\n    if [[ \"${TARGET}\" == *-linux-musl* ]]; then\n        td=$(mkcargotemp -d)\n\n        pushd \"${td}\"\n        cargo init --bin --name hello .\n        retry cargo fetch\n        RUSTFLAGS=\"$RUSTFLAGS -C target-feature=-crt-static\" \\\n            cross_build --target \"${TARGET}\"\n        popd\n\n        rm -rf \"${td}\"\n    fi\n\n    # test cmake support\n    td=\"$(mkcargotemp -d)\"\n\n    git clone \\\n        --recursive \\\n        --depth 1 \\\n        https://github.com/cross-rs/rust-cmake-hello-world \"${td}\"\n\n    pushd \"${td}\"\n    retry cargo fetch\n    if [[ \"${TARGET}\" == \"arm-linux-androideabi\" ]]; then\n        # ARMv5te isn't supported anymore by Android, which produces missing\n        # symbol errors with re2 like `__libcpp_signed_lock_free`.\n        cross_run --target \"${TARGET}\" --features=tryrun\n    elif (( ${STD:-0} )) && (( ${RUN:-0} )) && (( ${CPP:-0} )); then\n        cross_run --target \"${TARGET}\" --features=re2,tryrun\n    elif (( ${STD:-0} )) && (( ${CPP:-0} )); then\n        cross_build --target \"${TARGET}\" --features=re2\n    elif (( ${STD:-0} )) && (( ${RUN:-0} )); then\n        cross_run --target \"${TARGET}\" --features=tryrun\n    elif (( ${STD:-0} )); then\n        cross_build --target \"${TARGET}\" --features=tryrun\n    else\n        cross_build --lib --target \"${TARGET}\"\n    fi\n    popd\n\n    rm -rf \"${td}\"\n\n    # test running binaries with cleared environment\n    # Command is not implemented for wasm32-unknown-emscripten\n    if (( ${RUN:-0} )) && [[ \"${TARGET}\" != \"wasm32-unknown-emscripten\" ]]; then\n        td=\"$(mkcargotemp -d)\"\n        pushd \"${td}\"\n        cargo init --bin --name foo .\n        mkdir src/bin\n        upper_target=$(echo \"${TARGET}\" | tr '[:lower:]' '[:upper:]' | tr '-' '_')\n        cat <<EOF > src/bin/launch.rs\nfn main() {\n    let runner = std::env::var(\"CARGO_TARGET_${upper_target}_RUNNER\");\n    let mut command = if let Ok(runner) = runner {\n        runner.split(' ').map(str::to_string).collect()\n    } else {\n        vec![]\n    };\n    let executable = format!(\"/target/${TARGET}/debug/foo{}\", std::env::consts::EXE_SUFFIX);\n    command.push(executable.to_string());\n    let status = dbg!(std::process::Command::new(&command[0])\n        .args(&command[1..])\n        .env_clear()) // drop all environment variables\n    .status()\n    .unwrap();\n    std::process::exit(status.code().unwrap());\n}\nEOF\n        cross_build --target \"${TARGET}\"\n        cross_run --target \"${TARGET}\" --bin launch\n        popd\n        rm -rf \"${td}\"\n    fi\n}\n\ncross_build() {\n    \"${CROSS[@]}\" build \"$@\" ${CROSS_FLAGS}\n}\n\ncross_run() {\n    if [[ -z \"${RUNNERS:-}\" ]]; then\n        \"${CROSS[@]}\" run \"$@\" ${CROSS_FLAGS}\n    else\n        for runner in ${RUNNERS}; do\n            echo -e \"[target.${TARGET}]\\nrunner = \\\"${runner}\\\"\" > \"${CARGO_TMP_DIR}\"/Cross.toml\n            \"${CROSS[@]}\" run \"$@\" ${CROSS_FLAGS}\n        done\n    fi\n}\n\ncross_test() {\n    if [[ -z \"${RUNNERS:-}\" ]]; then\n        \"${CROSS[@]}\" test \"$@\" ${CROSS_FLAGS}\n    else\n        for runner in ${RUNNERS}; do\n            echo -e \"[target.${TARGET}]\\nrunner = \\\"${runner}\\\"\" > \"${CARGO_TMP_DIR}\"/Cross.toml\n            \"${CROSS[@]}\" test \"$@\" ${CROSS_FLAGS}\n        done\n    fi\n}\n\ncross_bench() {\n    if [[ -z \"${RUNNERS:-}\" ]]; then\n        \"${CROSS[@]}\" bench \"$@\" ${CROSS_FLAGS}\n    else\n        for runner in ${RUNNERS}; do\n            echo -e \"[target.${TARGET}]\\nrunner = \\\"${runner}\\\"\" > \"${CARGO_TMP_DIR}\"/Cross.toml\n            \"${CROSS[@]}\" bench \"$@\" ${CROSS_FLAGS}\n        done\n    fi\n}\n\nmain\n"
  },
  {
    "path": "clippy.toml",
    "content": "disallowed-methods = [\n    { path = \"std::path::Path::display\", reason = \"incorrect handling of non-Unicode paths, use path.to_utf8() or debug (`{path:?}`) instead\" },\n]\nallow-unwrap-in-tests = true\nmsrv = \"1.92.0\"\n"
  },
  {
    "path": "crosstool-ng/arm-unknown-linux-gnueabihf.config.in",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_DOWNLOAD_AGENT_CURL=y\nCT_ARCH_ARM=y\nCT_ARCH_ARCH=\"armv6\"\nCT_ARCH_FPU=\"vfp\"\nCT_ARCH_FLOAT_HW=y\nCT_KERNEL_LINUX=y\n%CT_LINUX_V%\n%CT_LINUX%\nCT_BINUTILS_V_2_32=y\n%CT_GLIBC_V%\n%CT_GLIBC%\n%CT_GCC_V%\n%CT_GCC%\nCT_CC_LANG_CXX=y\nCT_GETTEXT_V_0_19_8_1=y\nCT_GMP_V_6_1=y\nCT_ISL_V_0_20=y\nCT_LIBICONV_V_1_15=y\nCT_NCURSES_V_6_1=y\n"
  },
  {
    "path": "crosstool-ng/loongarch64-unknown-linux-gnu.config.in",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_EXPERIMENTAL=y\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_ARCH_LOONGARCH=y\n# CT_DEMULTILIB is not set\nCT_ARCH_USE_MMU=y\nCT_ARCH_ARCH=\"loongarch64\"\nCT_KERNEL_LINUX=y\n%CT_LINUX_V%\n%CT_LINUX%\n%CT_GLIBC_V%\n%CT_GLIBC%\n%CT_GCC_V%\n%CT_GCC%\nCT_CC_GCC_ENABLE_DEFAULT_PIE=y\nCT_CC_LANG_CXX=y\n"
  },
  {
    "path": "crosstool-ng/loongarch64-unknown-linux-musl.config.in",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_EXPERIMENTAL=y\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_ARCH_LOONGARCH=y\n# CT_DEMULTILIB is not set\nCT_ARCH_USE_MMU=y\nCT_ARCH_ARCH=\"loongarch64\"\nCT_KERNEL_LINUX=y\n%CT_LINUX_V%\n%CT_LINUX%\nCT_LIBC_MUSL=y\n%CT_MUSL_V%\n%CT_GCC_V%\n%CT_GCC%\nCT_CC_GCC_ENABLE_DEFAULT_PIE=y\nCT_CC_LANG_CXX=y\n"
  },
  {
    "path": "deny.toml",
    "content": "[graph]\n# only check for the targets we currently publish\ntargets = [\n  { triple = \"x86_64-apple-darwin\" },\n  { triple = \"x86_64-unknown-linux-gnu\" },\n  { triple = \"x86_64-unknown-linux-musl\" },\n  { triple = \"x86_64-pc-windows-msvc\" },\n]\n\n[advisories]\nversion = 2\n\n[bans]\nmultiple-versions = \"deny\"\ndeny = []\nskip-tree = [\n    { name = \"supports-color\", depth = 1, version = \"2.1.0\", reason = \"owo-colors pulls in two versions of supports-color\" },\n]\n\n[sources]\nunknown-registry = \"deny\"\nunknown-git = \"deny\"\nallow-git = []\n\n[licenses]\nversion = 2\n# need this since to suppress errors in case we add crates with these allowed licenses\nunused-allowed-license = \"allow\"\nconfidence-threshold = 0.93\nallow = [\n  \"Apache-2.0\",\n  \"MIT\",\n  \"CC0-1.0\",\n  \"ISC\",\n  \"0BSD\",\n  \"BSD-2-Clause\",\n  \"BSD-3-Clause\",\n  \"Unlicense\",\n  \"Unicode-DFS-2016\",\n  \"Unicode-3.0\",\n  \"MPL-2.0\",\n]\n\n[licenses.private]\nignore = true\n"
  },
  {
    "path": "docker/.dockerignore",
    "content": "# don't copy any of the python artifacts to the docker context\n__pycache__/\n.pytest_cache/\n*.py[cod]\n*$py.class\n**/*.egg-info/\n*.egg\n.tox\n\n# also skip our test suite\nandroid/tests/\n"
  },
  {
    "path": "docker/.gitattributes",
    "content": "* text eol=lf\n"
  },
  {
    "path": "docker/Dockerfile.aarch64-linux-android",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh aarch64\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh arm64\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh arm64\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh arm64\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64-linux-android-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh aarch64 aarch64-linux-android\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\nENV CROSS_TARGET_RUNNER=\"/android-runner aarch64\"\nENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_AARCH64_LINUX_ANDROID_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_aarch64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_linux_android=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_linux_android=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include\"/ \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=aarch64 \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-DANDROID -ffunction-sections -fdata-sections -fPIC\"\n"
  },
  {
    "path": "docker/Dockerfile.aarch64-unknown-freebsd",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN echo \"export ARCH=aarch64\" > /freebsd-arch.sh\nCOPY freebsd-common.sh /\nCOPY freebsd.sh /\nRUN /freebsd.sh\n\nCOPY freebsd-install.sh /\nCOPY freebsd-extras.sh /\nRUN /freebsd-extras.sh\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64-unknown-freebsd13-\nENV CROSS_SYSROOT=/usr/local/aarch64-unknown-freebsd13\n\nCOPY freebsd-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nCOPY freebsd-fetch-best-mirror.sh /\nCOPY freebsd-setup-packagesite.sh /\nCOPY freebsd-install-package.sh /\n\nENV CARGO_TARGET_AARCH64_UNKNOWN_FREEBSD_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    AR_aarch64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_aarch64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_unknown_freebsd=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_unknown_freebsd=\"--sysroot=$CROSS_SYSROOT\" \\\n    AARCH64_UNKNOWN_FREEBSD_OPENSSL_DIR=\"$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=FreeBSD \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=amd64 \\\n    CROSS_CMAKE_CRT=freebsd \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.aarch64-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-aarch64-linux-gnu \\\n    gfortran-aarch64-linux-gnu \\\n    libc6-dev-arm64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=arm64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-aarch64-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh aarch64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh aarch64\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64-linux-gnu-\nENV CROSS_SYSROOT=/usr/aarch64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner aarch64\"\nENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/aarch64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=aarch64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.aarch64-unknown-linux-gnu.centos",
    "content": "FROM ubuntu:20.04 AS base\n\nCOPY lib.sh /\nCOPY linux-image.sh /\nRUN /linux-image.sh aarch64\n\nFROM centos:7\n\n# From https://github.com/rust-lang/rust/blob/672e3aaf28ab1e5cbe80b3ff012cd3a8e4ef98af/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile#L9-L12\n# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault.\nRUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \\\n  -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!'\nRUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nCOPY qemu.sh /\nRUN /qemu.sh aarch64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY --from=0 /qemu /qemu\n\nCOPY linux-runner base-runner.sh /\n\nCOPY aarch64-linux-gnu-glibc.sh /\nRUN /aarch64-linux-gnu-glibc.sh\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64-linux-gnu-\nENV CROSS_SYSROOT=/usr/aarch64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner aarch64\"\nENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=aarch64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.aarch64-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh aarch64\n\nCOPY musl.sh /\nRUN /musl.sh TARGET=aarch64-linux-musl\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64-linux-musl-\nENV CROSS_SYSROOT=/usr/local/aarch64-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT aarch64\n\nCOPY musl-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner aarch64\"\nENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_aarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_aarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=aarch64 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\" \\\n    CROSS_BUILTINS_PATCHED_MINOR_VERSION=48\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.aarch64_be-unknown-linux-gnu",
    "content": "FROM ubuntu:24.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG VERBOSE\nCOPY crosstool-ng.sh /\nCOPY crosstool-config/aarch64_be-unknown-linux-gnu.config /\nRUN /crosstool-ng.sh aarch64_be-unknown-linux-gnu.config 5\n\nENV PATH=/x-tools/aarch64_be-unknown-linux-gnu/bin/:$PATH\n\nCOPY qemu.sh /\nRUN /qemu.sh aarch64_be\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=aarch64_be-unknown-linux-gnu-\nENV CROSS_SYSROOT=/x-tools/aarch64_be-unknown-linux-gnu/aarch64_be-unknown-linux-gnu/sysroot/\nENV CROSS_TARGET_RUNNER=\"/qemu-runner aarch64_be\"\nENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_aarch64_be_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_aarch64_be_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_aarch64_be_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_aarch64_be_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_aarch64_be_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=aarch64_be \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.arm-linux-androideabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh arm\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh arm\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh arm\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-androideabi-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh arm arm-linux-androideabi\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\nENV CROSS_TARGET_RUNNER=\"/android-runner arm\"\nENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_arm_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_arm_linux_androideabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_arm_linux_androideabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include/\" \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv5te \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"--target=arm-linux-androideabi -DANDROID -ffunction-sections -fdata-sections -fPIC --target=arm-linux-androideabi\"\n"
  },
  {
    "path": "docker/Dockerfile.arm-unknown-linux-gnueabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-arm-linux-gnueabi \\\n    gfortran-arm-linux-gnueabi \\\n    libc6-dev-armel-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=armel /deny-debian-packages.sh \\\n    binutils \\\n    binutils-arm-linux-gnueabi\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-gnueabi-\nENV CROSS_SYSROOT=/usr/arm-linux-gnueabi\nENV CROSS_TARGET_RUNNER=\"/qemu-runner arm\"\nENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_arm_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_arm_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_arm_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_arm_unknown_linux_gnueabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_arm_unknown_linux_gnueabi=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabi/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfloat-abi=soft\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.arm-unknown-linux-gnueabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG VERBOSE\nCOPY crosstool-ng.sh /\nCOPY crosstool-config/arm-unknown-linux-gnueabihf.config /\nRUN /crosstool-ng.sh arm-unknown-linux-gnueabihf.config 5\n\nENV PATH=/x-tools/arm-unknown-linux-gnueabihf/bin/:$PATH\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=armhf /deny-debian-packages.sh\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-unknown-linux-gnueabihf-\nENV CROSS_SYSROOT=/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot/\nENV CROSS_TARGET_RUNNER=\"/qemu-runner armhf\"\nENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_arm_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_arm_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_arm_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_arm_unknown_linux_gnueabihf=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_arm_unknown_linux_gnueabihf=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabihf/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.arm-unknown-linux-musleabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=arm-linux-musleabi \\\n    \"COMMON_CONFIG += --with-arch=armv6 \\\n                      --with-float=soft \\\n                      --with-mode=arm\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-musleabi-\nENV CROSS_SYSROOT=/usr/local/arm-linux-musleabi\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner arm\"\nENV CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_arm_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_arm_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_arm_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_arm_unknown_linux_musleabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_arm_unknown_linux_musleabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfloat-abi=soft\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.arm-unknown-linux-musleabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=arm-linux-musleabihf \\\n    \"COMMON_CONFIG += --with-arch=armv6 \\\n                      --with-fpu=vfp \\\n                      --with-float=hard \\\n                      --with-mode=arm\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-musleabihf-\nENV CROSS_SYSROOT=/usr/local/arm-linux-musleabihf\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT armhf\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner armhf\"\nENV CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARM_UNKNOWN_LINUX_MUSLEABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_arm_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_arm_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_arm_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_arm_unknown_linux_musleabihf=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_arm_unknown_linux_musleabihf=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv5te-unknown-linux-gnueabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-arm-linux-gnueabi \\\n    gfortran-arm-linux-gnueabi \\\n    crossbuild-essential-armel \\\n    libc6-dev-armel-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=armel /deny-debian-packages.sh \\\n    binutils \\\n    binutils-arm-linux-gnueabi\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-gnueabi-\nENV CROSS_SYSROOT=/usr/arm-linux-gnueabi\nENV CROSS_TARGET_RUNNER=\"/qemu-runner arm\"\nENV CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_GNUEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_GNUEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv5te_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv5te_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv5te_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv5te_unknown_linux_gnueabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv5te_unknown_linux_gnueabi=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabi/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv5te -marm -mfloat-abi=soft\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv5te-unknown-linux-musleabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY musl.sh /\nRUN /musl.sh TARGET=arm-linux-musleabi \"COMMON_CONFIG += --with-arch=armv5te --with-float=soft --with-mode=arm\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-musleabi-\nENV CROSS_SYSROOT=/usr/local/arm-linux-musleabi\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT arm\n\nCOPY musl-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner arm\"\nENV CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_MUSLEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    CARGO_TARGET_ARMV5TE_UNKNOWN_LINUX_MUSLEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv5te_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv5te_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv5te_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv5te_unknown_linux_musleabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv5te_unknown_linux_musleabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv5te -marm -mfloat-abi=soft\" \\\n    CROSS_BUILTINS_PATCHED_MINOR_VERSION=65\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv7-linux-androideabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh arm\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh arm\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh arm\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-androideabi-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh arm arm-linux-androideabi\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\nENV CROSS_TARGET_RUNNER=\"/android-runner arm\"\nENV CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_armv7_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_armv7_linux_androideabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv7_linux_androideabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include/\" \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv7-a \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"--target=arm-linux-androideabi -DANDROID -ffunction-sections -fdata-sections -fPIC --target=armv7-linux-androideabi\"\n"
  },
  {
    "path": "docker/Dockerfile.armv7-unknown-linux-gnueabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get install --assume-yes --no-install-recommends \\\n    g++-arm-linux-gnueabi \\\n    gfortran-arm-linux-gnueabi \\\n    libc6-dev-armel-cross\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-gnueabi-\nENV CROSS_SYSROOT=/usr/arm-linux-gnueabi\nENV CROSS_TARGET_RUNNER=\"/qemu-runner armv7\"\nENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv7_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv7_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv7_unknown_linux_gnueabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv7_unknown_linux_gnueabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv7_unknown_linux_gnueabi=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabi/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv7-a\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv7-unknown-linux-gnueabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-arm-linux-gnueabihf \\\n    gfortran-arm-linux-gnueabihf \\\n    libc6-dev-armhf-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=armhf /deny-debian-packages.sh \\\n    binutils \\\n    binutils-arm-linux-gnueabihf\n\nCOPY qemu.sh /\nRUN /qemu.sh arm softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh armv7\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-\nENV CROSS_SYSROOT=/usr/arm-linux-gnueabihf\nENV CROSS_TARGET_RUNNER=\"/linux-runner armv7hf\"\nENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv7_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv7_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv7_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv7_unknown_linux_gnueabihf=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv7_unknown_linux_gnueabihf=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabihf/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv7-a -mfpu=vfpv3-d16\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv7-unknown-linux-musleabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=arm-linux-musleabi \\\n    \"COMMON_CONFIG += --with-arch=armv7-a \\\n                      --with-float=soft \\\n                      --with-mode=thumb \\\n                      --with-mode=arm\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-musleabi-\nENV CROSS_SYSROOT=/usr/local/arm-linux-musleabi\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT arm\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner armv7\"\nENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv7_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv7_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv7_unknown_linux_musleabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv7_unknown_linux_musleabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv7_unknown_linux_musleabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv7-a\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.armv7-unknown-linux-musleabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=arm-linux-musleabihf \\\n    \"COMMON_CONFIG += --with-arch=armv7-a \\\n                      --with-float=hard \\\n                      --with-mode=thumb \\\n                      --with-fpu=vfp\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-musleabihf-\nENV CROSS_SYSROOT=/usr/local/arm-linux-musleabihf\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT armhf\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner armv7hf\"\nENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_armv7_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_armv7_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_armv7_unknown_linux_musleabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_armv7_unknown_linux_musleabihf=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_armv7_unknown_linux_musleabihf=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv7-a -mfpu=vfpv3-d16\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.asmjs-unknown-emscripten",
    "content": "FROM emscripten/emsdk:3.1.14\nWORKDIR /\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    libxml2 \\\n    python\n\nENV CROSS_TOOLCHAIN_PREFIX=em\nENV CROSS_SYSROOT=/emsdk/upstream/emscripten/cache/sysroot\nENV CROSS_TARGET_RUNNER=\"node\"\nENV CARGO_TARGET_ASMJS_UNKNOWN_EMSCRIPTEN_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    BINDGEN_EXTRA_CLANG_ARGS_asmjs_unknown_emscripten=\"--sysroot=$CROSS_SYSROOT\" \\\n    CMAKE_TOOLCHAIN_FILE_asmjs_unknown_emscripten=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake\n"
  },
  {
    "path": "docker/Dockerfile.cross",
    "content": "FROM ubuntu:20.04 AS rust\nENV DEBIAN_FRONTEND=noninteractive\nCOPY docker/lib.sh docker/cross.sh /\nCOPY ./ /project\nRUN /cross.sh /project\n\n# we build our images in 2 steps, to ensure we have a compact\n# image, since we want to add our current subdirectory\nFROM ubuntu:20.04 AS base\nCOPY --from=rust /root/.cargo /root/.cargo\nCOPY --from=rust /root/.rustup /root/.rustup\n\n# need some basic devtools, and requirements for docker\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    ca-certificates \\\n    curl \\\n    git\n\nRUN curl -fsSL https://get.docker.com | sh\n\nENV CROSS_CONTAINER_IN_CONTAINER=1 \\\n    PATH=/root/.cargo/bin:$PATH\n"
  },
  {
    "path": "docker/Dockerfile.i586-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-i686-linux-gnu \\\n    gfortran-i686-linux-gnu \\\n    libc6-dev-i386-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=i386 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-i686-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh i386\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=i686-linux-gnu-\nENV CROSS_SYSROOT=/usr/i686-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/qemu-runner i586\"\nENV CARGO_TARGET_I586_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_I586_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i586_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i586_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i586_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_i586_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i586_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/i386-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m32 -march=pentium\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.i586-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY musl.sh /\nRUN /musl.sh TARGET=i586-linux-musl\n\nCOPY qemu.sh /\nRUN /qemu.sh i386\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=i586-linux-musl-\nENV CROSS_SYSROOT=/usr/local/i586-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT i386\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner i586\"\nENV CARGO_TARGET_I586_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_I586_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i586_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i586_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i586_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_i586_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i586_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m32 -march=pentium -Wl,-melf_i386\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.i686-linux-android",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\n# We could supposedly directly run i686 binaries like we do for x86_64, but\n# doing so generates an assertion failure:\n#   ... assertion failed: signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR\n#   ... src/libstd/sys/unix/mod.rs\n#   fatal runtime error: failed to initiate panic, error 5\n#\n# Running with qemu works as expected. it also ensures that're we're\n# running on a CPU that only supports 32-bit x86 systems.\nCOPY qemu.sh /\nRUN /qemu.sh i386\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh x86\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh x86\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh x86\n\nENV CROSS_TOOLCHAIN_PREFIX=i686-linux-android-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh i386 i686-linux-android\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\nENV CROSS_TARGET_RUNNER=\"/android-runner i686\"\nENV CARGO_TARGET_I686_LINUX_ANDROID_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_I686_LINUX_ANDROID_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_i686_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_i686_linux_android=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i686_linux_android=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include/\" \\\n    LIBZ_SYS_STATIC=1 \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=i686 \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"--target=i686-linux-android -DANDROID -ffunction-sections -fdata-sections -fPIC --target=i686-linux-android\"\n"
  },
  {
    "path": "docker/Dockerfile.i686-pc-windows-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN dpkg --add-architecture i386 && apt-get update && \\\n    apt-get install --assume-yes --no-install-recommends libz-mingw-w64-dev\n\nCOPY wine.sh /\nRUN /wine.sh\n\n# Build mingw tools using dwarf exceptions\nCOPY mingw.sh /\nRUN bash mingw.sh\n\n# run-detectors are responsible for calling the correct interpreter for exe\n# files. For some reason it does not work inside a docker container (it works\n# fine in the host). So we replace the usual paths of run-detectors to run wine\n# directly. This only affects the guest, we are not messing up with the host.\n#\n# See /usr/share/doc/binfmt-support/detectors\nRUN mkdir -p /usr/lib/binfmt-support/ && \\\n    rm -f /usr/lib/binfmt-support/run-detectors /usr/bin/run-detectors && \\\n    ln -s /usr/bin/wine /usr/lib/binfmt-support/run-detectors && \\\n    ln -s /usr/bin/wine /usr/bin/run-detectors\n\nCOPY windows-entry.sh /\nENTRYPOINT [\"/windows-entry.sh\"]\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\n# for why we always link with pthread support, see:\n# https://github.com/cross-rs/cross/pull/1123#issuecomment-1312287148\nENV CROSS_TOOLCHAIN_PREFIX=i686-w64-mingw32-\nENV CROSS_TOOLCHAIN_SUFFIX=-posix\nENV CROSS_SYSROOT=/usr/i686-w64-mingw32\nENV CROSS_TARGET_RUNNER=\"env -u CARGO_TARGET_I686_PC_WINDOWS_GNU_RUNNER wine\"\nENV CARGO_TARGET_I686_PC_WINDOWS_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CARGO_TARGET_I686_PC_WINDOWS_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i686_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i686_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CXX_i686_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CMAKE_TOOLCHAIN_FILE_i686_pc_windows_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i686_pc_windows_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Windows \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -m32\"\n"
  },
  {
    "path": "docker/Dockerfile.i686-unknown-freebsd",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN echo \"export ARCH=i686\" > /freebsd-arch.sh\nCOPY freebsd-common.sh /\nCOPY freebsd.sh /\nRUN /freebsd.sh\n\nCOPY freebsd-install.sh /\nCOPY freebsd-extras.sh /\nRUN /freebsd-extras.sh\n\nENV CROSS_TOOLCHAIN_PREFIX=i686-unknown-freebsd13-\nENV CROSS_SYSROOT=/usr/local/i686-unknown-freebsd13\n\nCOPY freebsd-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nCOPY freebsd-fetch-best-mirror.sh /\nCOPY freebsd-setup-packagesite.sh /\nCOPY freebsd-install-package.sh /\n\nENV CARGO_TARGET_I686_UNKNOWN_FREEBSD_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    AR_i686_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i686_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i686_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_i686_unknown_freebsd=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i686_unknown_freebsd=\"--sysroot=$CROSS_SYSROOT\" \\\n    I686_UNKNOWN_FREEBSD_OPENSSL_DIR=\"$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=FreeBSD \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=freebsd \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m32\"\n"
  },
  {
    "path": "docker/Dockerfile.i686-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-i686-linux-gnu \\\n    gfortran-i686-linux-gnu \\\n    libc6-dev-i386-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=i386 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-i686-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh i386 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh i686\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=i686-linux-gnu-\nENV CROSS_SYSROOT=/usr/i686-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner i686\"\nENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i686_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i686_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i686_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_i686_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i686_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/i386-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m32 -march=i686\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.i686-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY musl.sh /\nRUN /musl.sh TARGET=i686-linux-musl\n\nCOPY qemu.sh /\nRUN /qemu.sh i386\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=i686-linux-musl-\nENV CROSS_SYSROOT=/usr/local/i686-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT i386\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner i686\"\nENV CARGO_TARGET_I686_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_I686_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_i686_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_i686_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_i686_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_i686_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_i686_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m32 -march=i686 -Wl,-melf_i386\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.loongarch64-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG VERBOSE\nCOPY crosstool-ng.sh /\nCOPY crosstool-config/loongarch64-unknown-linux-gnu.config /\nRUN /crosstool-ng.sh loongarch64-unknown-linux-gnu.config 5\n\nENV PATH=/x-tools/loongarch64-unknown-linux-gnu/bin/:$PATH\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=loong64 /deny-debian-packages.sh\n\nCOPY qemu.sh /\nRUN /qemu.sh loongarch64\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=loongarch64-unknown-linux-gnu-\nENV CROSS_SYSROOT=/x-tools/loongarch64-unknown-linux-gnu/loongarch64-unknown-linux-gnu/sysroot/\nENV CROSS_TARGET_RUNNER=\"/qemu-runner loongarch64\"\nENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_loongarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_loongarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_loongarch64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_loongarch64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_loongarch64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=loongarch64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN mv $CROSS_SYSROOT/lib/* $CROSS_SYSROOT/lib64/\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.loongarch64-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG VERBOSE\nCOPY crosstool-ng.sh /\nCOPY crosstool-config/loongarch64-unknown-linux-musl.config /\nRUN /crosstool-ng.sh loongarch64-unknown-linux-musl.config 5\n\nENV PATH=/x-tools/loongarch64-unknown-linux-musl/bin/:$PATH\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=loong64 /deny-debian-packages.sh\n\nCOPY qemu.sh /\nRUN /qemu.sh loongarch64\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\nENV PATH=/x-tools/loongarch64-unknown-linux-musl/bin/:$PATH\n\nENV CROSS_TOOLCHAIN_PREFIX=loongarch64-unknown-linux-musl-\nENV CROSS_SYSROOT=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/\nENV CROSS_TARGET_RUNNER=\"/qemu-runner loongarch64\"\nENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_loongarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_loongarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_loongarch64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_loongarch64_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_loongarch64_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=loongarch64 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get install --assume-yes --no-install-recommends \\\n    g++-mips-linux-gnu \\\n    gfortran-mips-linux-gnu \\\n    libc6-dev-mips-cross\n\nCOPY qemu.sh /\nRUN /qemu.sh mips\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=mips-linux-gnu-\nENV CROSS_SYSROOT=/usr/mips-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mips\"\nENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/mips-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh mips\n\n# this is a soft-float target for the mips32r2 architecture\n# https://github.com/rust-lang/rust/blob/75d3027fb5ce1af6712e4503c9574802212101bd/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs#L7\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=mips-linux-muslsf \\\n    \"COMMON_CONFIG += -with-arch=mips32r2\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=mips-linux-muslsf-\nENV CROSS_SYSROOT=/usr/local/mips-linux-muslsf\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT mips-sf\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mips\"\nENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPS_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips64-unknown-linux-gnuabi64",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-mips64-linux-gnuabi64 \\\n    gfortran-mips64-linux-gnuabi64 \\\n    libc6-dev-mips64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=mips64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-mips64-linux-gnuabi64\n\nCOPY qemu.sh /\nRUN /qemu.sh mips64\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=mips64-linux-gnuabi64-\nENV CROSS_SYSROOT=/usr/mips64-linux-gnuabi64\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mips64\"\nENV CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips64_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips64_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips64_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips64_unknown_linux_gnuabi64=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips64_unknown_linux_gnuabi64=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/mips64-linux-gnuabi64/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips64-unknown-linux-muslabi64",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh mips64\n\n# this is a hard-float target for the mips64r2 architecture\n# https://github.com/rust-lang/rust/blob/75d3027fb5ce1af6712e4503c9574802212101bd/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs#L7\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=mips64-linux-musl \\\n    \"COMMON_CONFIG += -with-arch=mips64r2\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=mips64-linux-musl-\nENV CROSS_SYSROOT=/usr/local/mips64-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT mips64\nRUN mkdir -p $CROSS_SYSROOT/usr/lib64\n# needed for the C/C++ runners\nRUN ln -s $CROSS_SYSROOT/usr/lib/libc.so $CROSS_SYSROOT/usr/lib64/libc.so\nRUN ln -s $CROSS_SYSROOT/usr/lib/libc.so.1 $CROSS_SYSROOT/usr/lib64/libc.so.1\n\nCOPY musl-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mips64\"\nENV CARGO_TARGET_MIPS64_UNKNOWN_LINUX_MUSLABI64_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    CARGO_TARGET_MIPS64_UNKNOWN_LINUX_MUSLABI64_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips64_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips64_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips64_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips64_unknown_linux_muslabi64=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips64_unknown_linux_muslabi64=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips64 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\" \\\n    CROSS_BUILTINS_PATCHED_MINOR_VERSION=65\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips64el-unknown-linux-gnuabi64",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-mips64el-linux-gnuabi64 \\\n    gfortran-mips64el-linux-gnuabi64 \\\n    libc6-dev-mips64el-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=mips64el /deny-debian-packages.sh \\\n    binutils \\\n    binutils-mips64el-linux-gnuabi64\n\nCOPY qemu.sh /\nRUN /qemu.sh mips64el softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh mips64el\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=mips64el-linux-gnuabi64-\nENV CROSS_SYSROOT=/usr/mips64el-linux-gnuabi64\nENV CROSS_TARGET_RUNNER=\"/linux-runner mips64el\"\nENV CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips64el_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips64el_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips64el_unknown_linux_gnuabi64=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips64el_unknown_linux_gnuabi64=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips64el_unknown_linux_gnuabi64=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/mips64el-linux-gnuabi64/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.mips64el-unknown-linux-muslabi64",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh mips64el\n\n# this is a hard-float target for the mips64r2 architecture\n# https://github.com/rust-lang/rust/blob/75d3027fb5ce1af6712e4503c9574802212101bd/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs#L6\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=mips64el-linux-musl \\\n    \"COMMON_CONFIG += -with-arch=mips64r2\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=mips64el-linux-musl-\nENV CROSS_SYSROOT=/usr/local/mips64el-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT mips64el\nRUN mkdir -p $CROSS_SYSROOT/usr/lib64\n# needed for the C/C++ runners\nRUN ln -s $CROSS_SYSROOT/usr/lib/libc.so $CROSS_SYSROOT/usr/lib64/libc.so\nRUN ln -s $CROSS_SYSROOT/usr/lib/libc.so.1 $CROSS_SYSROOT/usr/lib64/libc.so.1\n\nCOPY musl-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mips64el\"\nENV CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_MUSLABI64_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_MUSLABI64_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mips64el_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mips64el_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mips64el_unknown_linux_muslabi64=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mips64el_unknown_linux_muslabi64=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mips64el_unknown_linux_muslabi64=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips64 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\" \\\n    CROSS_BUILTINS_PATCHED_MINOR_VERSION=65\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.mipsel-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-mipsel-linux-gnu \\\n    gfortran-mipsel-linux-gnu \\\n    libc6-dev-mipsel-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=mipsel /deny-debian-packages.sh \\\n    binutils \\\n    binutils-mipsel-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh mipsel softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh mipsel\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=mipsel-linux-gnu-\nENV CROSS_SYSROOT=/usr/mipsel-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner mipsel\"\nENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mipsel_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mipsel_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mipsel_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mipsel_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mipsel_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/mipsel-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.mipsel-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh mipsel\n\n# this is a soft-float target for the mips32r2 architecture\n# https://github.com/rust-lang/rust/blob/75d3027fb5ce1af6712e4503c9574802212101bd/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs#L6\nCOPY musl.sh /\nRUN /musl.sh \\\n    TARGET=mipsel-linux-muslsf \\\n    \"COMMON_CONFIG += -with-arch=mips32r2\"\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=mipsel-linux-muslsf-\nENV CROSS_SYSROOT=/usr/local/mipsel-linux-muslsf\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT mipsel-sf\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner mipsel\"\nENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_mipsel_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_mipsel_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_mipsel_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_mipsel_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_mipsel_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=mips \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.native",
    "content": "# This dockerfile is used when the target matches the images platform in `build-docker-image`\nFROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG TARGETARCH\nARG TARGETVARIANT\nARG CROSS_TARGET_TRIPLE\n\nCOPY qemu.sh native-qemu.sh /\nRUN /native-qemu.sh\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh native-linux-image.sh /\nRUN /native-linux-image.sh\n\nCOPY linux-runner native-linux-runner base-runner.sh /\n\nENV CROSS_TARGETARCH=$TARGETARCH\nENV CROSS_TARGETVARIANT=$TARGETVARIANT\nENV CROSS_TOOLCHAIN_PREFIX=\nENV CROSS_SYSROOT=/\nENV CARGO_TARGET_${CROSS_TARGET_TRIPLE}_RUNNER=\"/native-linux-runner\"\n\nRUN sed -e \"s#@DEFAULT_CROSS_TARGETARCH@#$CROSS_TARGETARCH#g\" -i /native-linux-runner\nRUN sed -e \"s#@DEFAULT_CROSS_TARGETVARIANT@#$CROSS_TARGETVARIANT#g\" -i /native-linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.native.centos",
    "content": "FROM ubuntu:20.04 AS base\nENV DEBIAN_FRONTEND=noninteractive\n\nARG TARGETARCH\nARG TARGETVARIANT\nARG CROSS_TARGET_TRIPLE\n\nCOPY lib.sh /\nCOPY linux-image.sh native-linux-image.sh /\nRUN /native-linux-image.sh\n\nFROM centos:7\n\n# From https://github.com/rust-lang/rust/blob/672e3aaf28ab1e5cbe80b3ff012cd3a8e4ef98af/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile#L9-L12\n# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault.\nRUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \\\n  -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!'\nRUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\n# these need to be present in **both** FROM sections\nARG TARGETARCH\nARG TARGETVARIANT\nARG CROSS_TARGET_TRIPLE\n\nCOPY qemu.sh native-qemu.sh /\nRUN /native-qemu.sh\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY --from=0 /qemu /qemu\n\nCOPY linux-runner native-linux-runner base-runner.sh /\n\nENV CROSS_TARGETARCH=$TARGETARCH\nENV CROSS_TARGETVARIANT=$TARGETVARIANT\nENV CROSS_TOOLCHAIN_PREFIX=\nENV CROSS_SYSROOT=/\nENV CARGO_TARGET_${CROSS_TARGET_TRIPLE}_RUNNER=\"/native-linux-runner\"\n\nRUN sed -e \"s#@DEFAULT_CROSS_TARGETARCH@#$CROSS_TARGETARCH#g\" -i /native-linux-runner\nRUN sed -e \"s#@DEFAULT_CROSS_TARGETVARIANT@#$CROSS_TARGETVARIANT#g\" -i /native-linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.powerpc-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-powerpc-linux-gnu \\\n    gfortran-powerpc-linux-gnu \\\n    libc6-dev-powerpc-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=powerpc /deny-debian-packages.sh \\\n    binutils \\\n    binutils-powerpc-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh ppc softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh powerpc\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=powerpc-linux-gnu-\nENV CROSS_SYSROOT=/usr/powerpc-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner powerpc\"\nENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_powerpc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_powerpc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_powerpc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_powerpc_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_powerpc_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/powerpc-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=ppc \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.powerpc64-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-powerpc64-linux-gnu \\\n    gfortran-powerpc64-linux-gnu \\\n    libc6-dev-ppc64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=ppc64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-powerpc64-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh ppc64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh powerpc64\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=powerpc64-linux-gnu-\nENV CROSS_SYSROOT=/usr/powerpc64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner powerpc64\"\nENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_powerpc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_powerpc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_powerpc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_powerpc64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_powerpc64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/powerpc64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=ppc64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.powerpc64le-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-powerpc64le-linux-gnu \\\n    gfortran-powerpc64le-linux-gnu \\\n    libc6-dev-ppc64el-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=ppc64el /deny-debian-packages.sh \\\n    binutils \\\n    binutils-powerpc64le-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh ppc64le softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh powerpc64le\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=powerpc64le-linux-gnu-\nENV CROSS_SYSROOT=/usr/powerpc64le-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner powerpc64le\"\nENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_powerpc64le_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_powerpc64le_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_powerpc64le_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_powerpc64le_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_powerpc64le_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/powerpc64le-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=ppc64le \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.riscv64gc-unknown-linux-gnu",
    "content": "FROM ubuntu:22.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n    g++-riscv64-linux-gnu \\\n    gfortran-riscv64-linux-gnu \\\n    libc6-dev-riscv64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=riscv64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-riscv64-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh riscv64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh riscv64\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=riscv64-linux-gnu-\nENV CROSS_SYSROOT=/usr/riscv64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner riscv64\"\nENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_riscv64gc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_riscv64gc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_riscv64gc_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_riscv64gc_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/riscv64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=riscv64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=rv64gc -mabi=lp64d -mcmodel=medany\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.riscv64gc-unknown-linux-musl",
    "content": "FROM ubuntu:24.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n    bzip2 \\\n    adduser\n\nARG VERBOSE\nCOPY crosstool-ng.sh /\nCOPY crosstool-config/riscv64gc-unknown-linux-musl.config /\nRUN /crosstool-ng.sh riscv64gc-unknown-linux-musl.config 5\n\nENV PATH=/x-tools/riscv64-unknown-linux-musl/bin/:$PATH\n\nCOPY qemu.sh /\nRUN /qemu.sh riscv64\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\nENV PATH=/x-tools/riscv64-unknown-linux-musl/bin/:$PATH\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=riscv64-unknown-linux-musl-\nENV CROSS_SYSROOT=/x-tools/riscv64-unknown-linux-musl/riscv64-unknown-linux-musl/sysroot/\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner riscv64\"\nENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_riscv64gc_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_riscv64gc_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_riscv64gc_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_riscv64gc_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=riscv64gc \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=rv64gc -mabi=lp64d -mcmodel=medany\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.s390x-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-s390x-linux-gnu \\\n    gfortran-s390x-linux-gnu \\\n    libc6-dev-s390x-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=s390x /deny-debian-packages.sh \\\n    binutils \\\n    binutils-s390x-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh s390x softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh s390x\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=s390x-linux-gnu-\nENV CROSS_SYSROOT=/usr/s390x-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner s390x\"\nENV CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_s390x_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_s390x_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_s390x_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_s390x_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_s390x_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/s390x-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=s390x \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.sparc64-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-sparc64-linux-gnu \\\n    gfortran-sparc64-linux-gnu \\\n    libc6-dev-sparc64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=sparc64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-sparc64-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh sparc64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh sparc64\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=sparc64-linux-gnu-\nENV CROSS_SYSROOT=/usr/sparc64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner sparc64\"\nENV CARGO_TARGET_SPARC64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_SPARC64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_sparc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_sparc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_sparc64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_sparc64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_sparc64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/sparc64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=sparc64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.sparcv9-sun-solaris",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY solaris.sh /\nRUN /solaris.sh sparcv9 sun\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=sparcv9-sun-solaris2.10-\nENV CROSS_SYSROOT=/usr/local/sparcv9-sun-solaris2.10\nENV CARGO_TARGET_SPARCV9_SUN_SOLARIS_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    AR_sparcv9_sun_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_sparcv9_sun_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_sparcv9_sun_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_sparcv9_sun_solaris=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_sparcv9_sun_solaris=\"--sysroot=$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=SunOS \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=sparc64 \\\n    CROSS_CMAKE_CRT=solaris \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv6m-none-eabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m1 \\\n    AR_thumbv6m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv6m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv6m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_thumbv6m_none_eabi=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV6M_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv6-m \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -march=armv6s-m\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv7em-none-eabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m4 \\\n    AR_thumbv7em_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv7em_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv7em_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_thumbv7em_none_eabi=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV7EM_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv7e-m \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -march=armv7e-m\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv7em-none-eabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m4 \\\n    AR_thumbv7em_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv7em_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv7em_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_thumbv7em_none_eabihf=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV7EM_NONE_EABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv7e-m \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -mfloat-abi=hard -march=armv7e-m -mfpu=fpv4-sp-d16\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv7m-none-eabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m3 \\\n    AR_thumbv7m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv7m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv7m_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_thumbv7m_none_eabi=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv7-m \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -march=armv7-m\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv7neon-linux-androideabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh arm\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh arm\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh arm\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-androideabi-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh arm arm-linux-androideabi\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\n# likewise, the toolchains expect the prefix `thumbv7neon-linux-androideabi`,\n# which we don't have, so just export every possible variable, such as AR.\n# Also export all target binutils just in case required.\nENV CROSS_TARGET_RUNNER=\"/android-runner arm\"\nENV CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_thumbv7neon_linux_androideabi=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_thumbv7neon_linux_androideabi=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_linux_androideabi=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include/\" \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=armv7-a \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"--target=arm-linux-androideabi -DANDROID -ffunction-sections -fdata-sections -fPIC --target=thumbv7neon-linux-androideabi\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-arm-linux-gnueabihf \\\n    gfortran-arm-linux-gnueabihf \\\n    libc6-dev-armhf-cross\n\nCOPY qemu.sh /\nRUN /qemu.sh arm softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh armv7\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\n# Export all target binutils just in case required.\nENV CROSS_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-\nENV CROSS_SYSROOT=/usr/arm-linux-gnueabihf\nENV CROSS_TARGET_RUNNER=\"/linux-runner armv7hf\"\nENV CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_thumbv7neon_unknown_linux_gnueabihf=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_thumbv7neon_unknown_linux_gnueabihf=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_unknown_linux_gnueabihf=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/arm-linux-gnueabihf/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=arm \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -march=armv7-a -mfpu=vfpv3-d16 -mfpu=neon-vfpv4 -mthumb -mfloat-abi=hard\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.thumbv8m.base-none-eabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m23 \\\n    AR_thumbv8m.base_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv8m.base_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv8m.base_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    # cmake-rs does not accept CMAKE_TOOLCHAIN_FILE_thumbv8m.base_none_eabi\n    TARGET_CMAKE_TOOLCHAIN_FILE=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV8M.BASE_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=\"armv8-m.base\" \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -march=armv8-m.base\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv8m.main-none-eabi",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m33 \\\n    AR_thumbv8m.main_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv8m.main_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv8m.main_none_eabi=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    # cmake-rs does not accept CMAKE_TOOLCHAIN_FILE_thumbv8m.main_none_eabi\n    TARGET_CMAKE_TOOLCHAIN_FILE=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV8M.BASE_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=\"armv8-m.main\" \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -march=armv8-m.main\"\n"
  },
  {
    "path": "docker/Dockerfile.thumbv8m.main-none-eabihf",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    gcc-arm-none-eabi \\\n    libnewlib-arm-none-eabi \\\n    libstdc++-arm-none-eabi-newlib\n\nCOPY qemu.sh /\nRUN /qemu.sh arm\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=arm-none-eabi-\nENV CROSS_SYSROOT=\"/usr/lib/arm-none-eabi\"\nENV CROSS_TARGET_RUNNER=qemu-arm\nENV QEMU_CPU=cortex-m33 \\\n    AR_thumbv8m.main_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_thumbv8m.main_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_thumbv8m.main_none_eabihf=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    # cmake-rs does not accept CMAKE_TOOLCHAIN_FILE_thumbv8m.main_none_eabihf\n    TARGET_CMAKE_TOOLCHAIN_FILE=/opt/toolchain.cmake \\\n    CARGO_TARGET_THUMBV8M.BASE_NONE_EABI_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Generic \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=\"armv8-m.main\" \\\n    CROSS_CMAKE_CRT=newlib \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -mthumb -mfloat-abi=hard -march=armv8-m.main -mfpu=fpv5-sp-d16\"\n"
  },
  {
    "path": "docker/Dockerfile.wasm32-unknown-emscripten",
    "content": "FROM emscripten/emsdk:3.1.14\nWORKDIR /\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n  libxml2 \\\n  python\n\nENV CROSS_TOOLCHAIN_PREFIX=em\nENV CROSS_SYSROOT=/emsdk/upstream/emscripten/cache/sysroot\nENV CROSS_TARGET_RUNNER=\"node\"\nENV CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    BINDGEN_EXTRA_CLANG_ARGS_wasm32_unknown_emscripten=\"--sysroot=$CROSS_SYSROOT\" \\\n    CMAKE_TOOLCHAIN_FILE_wasm32_unknown_emscripten=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-linux-android",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\n# Using qemu allows older host cpus (without sse4) to execute the target binaries\nCOPY qemu.sh /\nRUN /qemu.sh x86_64\n\nARG ANDROID_NDK=r25b\nARG ANDROID_SDK=28\nARG ANDROID_VERSION=9.0.0_r1\nARG ANDROID_SYSTEM_NONE=0\nARG ANDROID_SYSTEM_COMPLETE=0\nARG PYTHON_TMPDIR=/tmp/android\n\nCOPY validate-android-args.sh /\nRUN /validate-android-args.sh x86_64\n\nCOPY android-ndk.sh /\nRUN /android-ndk.sh x86_64\nENV PATH=$PATH:/android-ndk/bin\n\nCOPY android-system.sh /\nRUN mkdir -p $PYTHON_TMPDIR\nCOPY android $PYTHON_TMPDIR\nRUN /android-system.sh x86_64\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-linux-android-\nENV CROSS_SYSROOT=/android-ndk/sysroot\nENV CROSS_ANDROID_SDK=$ANDROID_SDK\nCOPY android-symlink.sh /\nRUN /android-symlink.sh x86_64 x86_64-linux-android\n\nCOPY android-runner /\nCOPY android.cmake /opt/toolchain.cmake\n\n# Libz is distributed in the android ndk, but for some unknown reason it is not\n# found in the build process of some crates, so we explicit set the DEP_Z_ROOT\nENV CROSS_TARGET_RUNNER=\"/android-runner x86_64\"\nENV CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_X86_64_LINUX_ANDROID_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    AS_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"as \\\n    CC_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    LD_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ld \\\n    NM_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"nm \\\n    OBJCOPY_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objcopy \\\n    OBJDUMP_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"objdump \\\n    RANLIB_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"ranlib \\\n    READELF_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"readelf \\\n    SIZE_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"size \\\n    STRINGS_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strings \\\n    STRIP_x86_64_linux_android=\"$CROSS_TOOLCHAIN_PREFIX\"strip \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_linux_android=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_linux_android=\"--sysroot=$CROSS_SYSROOT\" \\\n    DEP_Z_INCLUDE=\"$CROSS_SYSROOT/usr/include/\" \\\n    RUST_TEST_THREADS=1 \\\n    HOME=/tmp/ \\\n    TMPDIR=/tmp/ \\\n    ANDROID_DATA=/ \\\n    ANDROID_DNS_MODE=local \\\n    ANDROID_ROOT=/system \\\n    CROSS_CMAKE_SYSTEM_NAME=Android \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=android \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"--target=x86_64-linux-android -DANDROID -ffunction-sections -fdata-sections -fPIC --target=x86_64-linux-android\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-pc-solaris",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY solaris.sh /\nRUN /solaris.sh x86_64 pc\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-pc-solaris2.10-\nENV CROSS_SYSROOT=/usr/local/x86_64-pc-solaris2.10\nENV CARGO_TARGET_X86_64_PC_SOLARIS_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    AR_x86_64_pc_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_pc_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_pc_solaris=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_pc_solaris=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_solaris=\"--sysroot=$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=SunOS \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=solaris \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-pc-windows-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN dpkg --add-architecture i386 && apt-get update && \\\n    apt-get install --assume-yes --no-install-recommends libz-mingw-w64-dev\n\nCOPY wine.sh /\nRUN /wine.sh\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends g++-mingw-w64-x86-64 gfortran-mingw-w64-x86-64\n\n# run-detectors are responsible for calling the correct interpreter for exe\n# files. For some reason it does not work inside a docker container (it works\n# fine in the host). So we replace the usual paths of run-detectors to run wine\n# directly. This only affects the guest, we are not messing up with the host.\n#\n# See /usr/share/doc/binfmt-support/detectors\nRUN mkdir -p /usr/lib/binfmt-support/ && \\\n    rm -f /usr/lib/binfmt-support/run-detectors /usr/bin/run-detectors && \\\n    ln -s /usr/bin/wine /usr/lib/binfmt-support/run-detectors && \\\n    ln -s /usr/bin/wine /usr/bin/run-detectors\n\nCOPY windows-entry.sh /\nENTRYPOINT [\"/windows-entry.sh\"]\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\n# for why we always link with pthread support, see:\n# https://github.com/cross-rs/cross/pull/1123#issuecomment-1312287148\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-w64-mingw32-\nENV CROSS_TOOLCHAIN_SUFFIX=-posix\nENV CROSS_SYSROOT=/usr/x86_64-w64-mingw32\nENV CROSS_TARGET_RUNNER=\"env -u CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER wine\"\nENV CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_x86_64_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CXX_x86_64_pc_windows_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++\"$CROSS_TOOLCHAIN_SUFFIX\" \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_pc_windows_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Windows \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=AMD64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-dragonfly",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY dragonfly.sh /\nRUN /dragonfly.sh 5\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-unknown-dragonfly-\nENV CROSS_SYSROOT=/usr/local/x86_64-unknown-dragonfly\nENV CARGO_TARGET_X86_64_UNKNOWN_DRAGONFLY_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    AR_x86_64_unknown_dragonfly=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_dragonfly=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_dragonfly=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_dragonfly=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_dragonfly=\"--sysroot=$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=DragonFly \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=dragonfly \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-freebsd",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN echo \"export ARCH=x86_64\" > /freebsd-arch.sh\nCOPY freebsd-common.sh /\nCOPY freebsd.sh /\nRUN /freebsd.sh\n\nCOPY freebsd-install.sh /\nCOPY freebsd-extras.sh /\nRUN /freebsd-extras.sh\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-unknown-freebsd13-\nENV CROSS_SYSROOT=/usr/local/x86_64-unknown-freebsd13\n\nCOPY freebsd-gcc.sh /usr/bin/\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nCOPY freebsd-fetch-best-mirror.sh /\nCOPY freebsd-setup-packagesite.sh /\nCOPY freebsd-install-package.sh /\n\nENV CARGO_TARGET_X86_64_UNKNOWN_FREEBSD_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc.sh \\\n    AR_x86_64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_freebsd=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_freebsd=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_freebsd=\"--sysroot=$CROSS_SYSROOT\" \\\n    X86_64_UNKNOWN_FREEBSD_OPENSSL_DIR=\"$CROSS_SYSROOT\" \\\n    PKG_CONFIG_PATH=\"${CROSS_SYSROOT}/libdata/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=FreeBSD \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=amd64 \\\n    CROSS_CMAKE_CRT=freebsd \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-illumos",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY illumos.sh /\nRUN /illumos.sh x86_64\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-unknown-illumos-\nENV CROSS_SYSROOT=/usr/local/x86_64-unknown-illumos/sysroot\nENV PATH=$PATH:/usr/local/x86_64-unknown-illumos/bin/ \\\n    CARGO_TARGET_X86_64_UNKNOWN_ILLUMOS_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    AR_x86_64_unknown_illumos=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_illumos=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_illumos=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_illumos=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_illumos=\"--sysroot=$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=illumos \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=solaris \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-linux-gnu",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nRUN apt-get update && apt-get install --assume-yes --no-install-recommends \\\n    g++-x86-64-linux-gnu \\\n    gfortran-x86-64-linux-gnu \\\n    libc6-dev-amd64-cross\n\nCOPY deny-debian-packages.sh /\nRUN TARGET_ARCH=amd64 /deny-debian-packages.sh \\\n    binutils \\\n    binutils-x86-64-linux-gnu\n\nCOPY qemu.sh /\nRUN /qemu.sh x86_64 softmmu\n\nCOPY dropbear.sh /\nRUN /dropbear.sh\n\nCOPY linux-image.sh /\nRUN /linux-image.sh x86_64\n\nCOPY linux-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-linux-gnu-\nENV CROSS_SYSROOT=/usr/x86_64-linux-gnu\nENV CROSS_TARGET_RUNNER=\"/linux-runner x86_64\"\nENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_x86_64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_linux_gnu=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_linux_gnu=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_linux_gnu=\"--sysroot=$CROSS_SYSROOT -idirafter/usr/include\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    RUST_TEST_THREADS=1 \\\n    PKG_CONFIG_PATH=\"/usr/lib/x86_64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}\" \\\n    PKG_CONFIG_ALLOW_CROSS=1 \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=gnu \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /linux-runner\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-linux-gnu.centos",
    "content": "# HACK: this file is currently never used and only exists for usage with `Dockerfile.native.centos`\n# it will be supported for aarch64 hosts, see #751 and #975\nFROM ubuntu:20.04 AS base\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-linux-musl",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY musl.sh /\nRUN /musl.sh TARGET=x86_64-linux-musl\n\nCOPY qemu.sh /\nRUN /qemu.sh x86_64\n\nCOPY tidyup.sh /\nRUN /tidyup.sh\n\nFROM scratch AS final\nCOPY --from=build / /\nCMD [\"/bin/bash\"]\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-linux-musl-\nENV CROSS_SYSROOT=/usr/local/x86_64-linux-musl\nCOPY musl-symlink.sh /\nRUN /musl-symlink.sh $CROSS_SYSROOT x86_64\n\nCOPY qemu-runner base-runner.sh /\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TARGET_RUNNER=\"/qemu-runner x86_64\"\nENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUNNER=\"$CROSS_TARGET_RUNNER\" \\\n    AR_x86_64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_linux_musl=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_linux_musl=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_linux_musl=\"--sysroot=$CROSS_SYSROOT\" \\\n    QEMU_LD_PREFIX=\"$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=Linux \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=musl \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n\nRUN sed -e \"s#@DEFAULT_QEMU_LD_PREFIX@#$QEMU_LD_PREFIX#g\" -i /qemu-runner\n"
  },
  {
    "path": "docker/Dockerfile.x86_64-unknown-netbsd",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nCOPY netbsd.sh /\nRUN /netbsd.sh\n\nCOPY toolchain.cmake /opt/toolchain.cmake\n\nENV CROSS_TOOLCHAIN_PREFIX=x86_64-unknown-netbsd-\nENV CROSS_SYSROOT=/usr/local/x86_64-unknown-netbsd\nENV CARGO_TARGET_X86_64_UNKNOWN_NETBSD_LINKER=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    AR_x86_64_unknown_netbsd=\"$CROSS_TOOLCHAIN_PREFIX\"ar \\\n    CC_x86_64_unknown_netbsd=\"$CROSS_TOOLCHAIN_PREFIX\"gcc \\\n    CXX_x86_64_unknown_netbsd=\"$CROSS_TOOLCHAIN_PREFIX\"g++ \\\n    CMAKE_TOOLCHAIN_FILE_x86_64_unknown_netbsd=/opt/toolchain.cmake \\\n    BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_netbsd=\"--sysroot=$CROSS_SYSROOT\" \\\n    CROSS_CMAKE_SYSTEM_NAME=NetBSD \\\n    CROSS_CMAKE_SYSTEM_PROCESSOR=x86_64 \\\n    CROSS_CMAKE_CRT=netbsd \\\n    CROSS_CMAKE_OBJECT_FLAGS=\"-ffunction-sections -fdata-sections -fPIC -m64\"\n"
  },
  {
    "path": "docker/Dockerfile.zig",
    "content": "FROM ubuntu:20.04 AS cross-base\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY common.sh lib.sh /\nRUN /common.sh\n\nCOPY cmake.sh /\nRUN /cmake.sh\n\nFROM cross-base AS build\n\nARG TARGETPLATFORM\nCOPY zig.sh /\nRUN /zig.sh $TARGETPLATFORM\n\n# we don't export `BINDGEN_EXTRA_CLANG_ARGS`, `QEMU_LD_PREFIX`, or\n# `PKG_CONFIG_PATH` since zig doesn't have a traditional sysroot structure,\n# and we're not using standard, shared packages. none of the packages\n# have runners either, since they do not ship with the required\n# dynamic linker (`ld-linux-${arch}.so`).\nENV PATH=$PATH:/opt/zig\n"
  },
  {
    "path": "docker/aarch64-linux-gnu-glibc.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nunpack_rpm() {\n    local package=\"${1}\"\n    curl --retry 3 \"https://vault.centos.org/altarch/7/os/aarch64/Packages/${package}\" -O\n    rpm2cpio \"${package}\" | cpio -idmv\n}\n\nsymlink_gcc_lib() {\n    local prefix=\"${1}\"\n    shift\n    local srcfile=\"${1}\"\n    shift\n    local dstdir=\"/usr/lib/gcc/aarch64-linux-gnu\"\n\n    ln -s \"${prefix}/lib/${srcfile}\" \"${dstdir}/4.8.2/${srcfile}\"\n    ln -s \"${prefix}/lib/${srcfile}\" \"${dstdir}/4.8.5/${srcfile}\"\n\n    local dstfile\n    for dstfile in \"${@}\"; do\n        ln -s \"${prefix}/lib/${srcfile}\" \"${dstdir}/4.8.2/${dstfile}\"\n        ln -s \"${prefix}/lib/${srcfile}\" \"${dstdir}/4.8.5/${dstfile}\"\n    done\n}\n\ncp_gcc_archive() {\n    local name=\"${1}\"\n    local srcdir=\"usr/lib/gcc/aarch64-redhat-linux/\"\n    local dstdir=\"/usr/lib/gcc/aarch64-linux-gnu/\"\n    cp \"${srcdir}/4.8.2/${name}\" \"${dstdir}/4.8.2/${name}\"\n    cp \"${srcdir}/4.8.5/${name}\" \"${dstdir}/4.8.5/${name}\"\n}\n\nmain() {\n    set_centos_ulimit\n    yum install -y epel-release\n    yum install -y gcc-aarch64-linux-gnu gcc-c++-aarch64-linux-gnu gfortran-c++-aarch64-linux-gnu binutils-aarch64-linux-gnu binutils gcc-c++ glibc-devel\n    yum clean all\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    local target=aarch64-linux-gnu\n    local prefix=\"/usr/${target}\"\n    local kernel_v4=\"4.18.20\"\n\n    curl --retry 3 \"https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-${kernel_v4}.tar.xz\" -O\n    tar -xvf \"linux-${kernel_v4}.tar.xz\"\n    pushd \"linux-${kernel_v4}\"\n    make ARCH=arm64 INSTALL_HDR_PATH=\"${prefix}\" headers_install\n    popd\n\n    curl --retry 3 http://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.xz -O\n    tar -xvf glibc-2.17.tar.xz\n    mkdir build\n    pushd build\n    CC=/usr/bin/aarch64-linux-gnu-gcc \\\n        CXX=/usr/bin/aarch64-linux-gnu-g++ \\\n        LD=/usr/bin/aarch64-linux-gnu-ld \\\n        AR=/usr/bin/aarch64-linux-gnu-ar \\\n        RANLIB=/usr/bin/aarch64-linux-gnu-ranlib \\\n        ../glibc-2.17/configure \\\n        --prefix=\"${prefix}\" \\\n        --build=\"${MACHTYPE}\" \\\n        --host=\"${target}\" \\\n        --target=\"${target}\" \\\n        --with-arch=aarch64 \\\n        --with-headers=\"${prefix}/include\" \\\n        --libdir=\"${prefix}/lib\" \\\n        --libexecdir=\"${prefix}/lib\"\n\n    make -j && make install\n    popd\n\n    mkdir -p \"${prefix}\"/{include,lib}\n    mkdir -p \"/usr/lib/gcc/aarch64-linux-gnu\"/{4.8.2,4.8.5}\n\n    mkdir libgcc\n    pushd libgcc\n    unpack_rpm \"libgcc-4.8.5-44.el7.aarch64.rpm\"\n    mv lib64/* \"${prefix}/lib\"\n    # C++ support needs `libgcc.so`, even though it warns about `libgcc_s.so`\n    symlink_gcc_lib \"${prefix}\" \"libgcc_s.so.1\" \"libgcc_s.so\" \"libgcc.so\"\n    popd\n\n    mkdir libstdcpp\n    pushd libstdcpp\n    unpack_rpm \"libstdc++-4.8.5-44.el7.aarch64.rpm\"\n    unpack_rpm \"libstdc++-devel-4.8.5-44.el7.aarch64.rpm\"\n    unpack_rpm \"libstdc++-static-4.8.5-44.el7.aarch64.rpm\"\n    mv usr/include/* \"${prefix}/include\"\n    mv usr/lib64/* \"${prefix}/lib\"\n    symlink_gcc_lib \"${prefix}\" \"libstdc++.so.6\" \"libstdc++.so\"\n    cp_gcc_archive \"libstdc++.a\"\n    cp_gcc_archive \"libsupc++.a\"\n    popd\n\n    local cpp_include=/usr/aarch64-linux-gnu/include/c++\n    local cpp_482=\"${cpp_include}/4.8.2\"\n    local cpp_485=\"${cpp_include}/4.8.5\"\n    local redhat_482=\"${cpp_482}/aarch64-redhat-linux\"\n    local redhat_485=\"${cpp_485}/aarch64-redhat-linux\"\n    mv \"${redhat_482}/bits\"/* \"${cpp_482}/bits\"\n    mv \"${redhat_482}/ext\"/* \"${cpp_482}/ext\"\n    # these are currently empty, but might contain content later\n    mv \"${redhat_485}/bits\"/* \"${cpp_485}/bits\" || true\n    mv \"${redhat_485}/ext\"/* \"${cpp_485}/ext\" || true\n\n    popd\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/android/README.md",
    "content": "android\n=======\n\nUtilities for working with the Android project, particularly, for modifying and working with the Android build system. This facilitates modifying both Soong and Make build files, to remove unittests to speed up builds and minimize the number of dependencies.\n"
  },
  {
    "path": "docker/android/android/__init__.py",
    "content": "import sys\n\n# we run this script once every build, and we'd rather\n# have much smaller image sizes, so copying without\n# any bytecode is a better idea.\nsys.dont_write_bytecode = True\n\n__version__ = '0.0.0-dev.0'\n__version_info__ = (0, 0, 0, 'dev.0')\n__license__ = 'MIT OR Apache-2.0'\n\n__all__ = [\n    \"make\",\n    \"soong\",\n]\n"
  },
  {
    "path": "docker/android/android/make.py",
    "content": "'''\n    make\n    ====\n\n    utilities to process makefiles. this parser is not sophisticated\n    nor correct, but it tries to avoid a few common pitfalls by\n    handling conditional blocks, and first separating all conditional\n    blocks into sections, and then parsing comment blocks within those\n    sections.\n\n    validate conditional directives are:\n    - ifeq\n    - ifneq\n    - ifdef\n    - ifndef\n    - else\n    - endif\n\n    makefiles are whitespace-sensitive, but not with leading whitespace\n    for conditional directives. for example, this is valid (replacing the\n    spaces with tabs):\n\n        # ---------------\n        # Section 1.\n        # ---------------\n        ifneq ($(USE_A),)\n            # -----------\n            # Section 2.\n            # -----------\n            ifneq ($(USE_B),)\n                SOURCES=b.cc\n            else\n                SOURCES=a.cc\n            endif\n        else\n            SOURCES=c.cc\n        endif\n\n    our goals are fairly different from a regular parser: we want to detect\n    and excise sections based on the comments, while ensuring that we do\n    not produce invalid output. other than unbalanced conditional directives,\n    we do not actually care about the actual contents.\n\n    for this, we use a 3 step parsing approach:\n    1. break up document into blocks separated by directives\n        - each block can be a regular or directive block\n        - directive blocks have a start and end directive as well as contents\n        - directives can be infinitely nested: the contents can also be a list\n    2. break each text block based on comment sections\n    3. group blocks within comment sections\n\n    for example, in the above, we want the entire makefile to be inside the\n    section 1 comment block, so removing it would remove that whole tree.\n    similarly, the inner directive block should be inside the section 2\n    comment block. we would therefore produce something like this:\n\n        CommentBlock: Section 1\n          Directive Block:\n            start=ifneq ($(USE_A),)\n            end=endif\n            children:\n              CommentBlock: Section 2\n                Directive Block:\n                  start=ifneq ($(USE_B),)\n                  end=endif\n                  children:\n                    Block: `SOURCES=b.cc\\nelse\\nSOURCES=a.cc`\n              Block: `else\\nSOURCES=c.cc`\n'''\n\nimport re2 as re\n\nfrom . import util\n\n\ndef loads(contents, *_, **__):\n    return Makefile.loads(contents)\n\n\ndef load(fp, *_, **__):\n    return Makefile.load(fp)\n\n\ndef dumps(makefile, *_, **__):\n    return makefile.dumps()\n\n\ndef dump(makefile, fp, *_, **__):\n    return makefile.dump(fp)\n\n\nclass Makefile(list):\n    @staticmethod\n    def loads(contents, *_, **__):\n        directives = _split_directives(iter(contents.splitlines()))[0]\n        blocks = directives.split_comments()\n        blocks.group_comments()\n\n        return Makefile(blocks)\n\n    @staticmethod\n    def load(fp, *_, **__):\n        return Makefile.loads(fp.read())\n\n    def dumps(self, *_, **__):\n        return str(self)\n\n    def dump(self, fp, *_, **__):\n        fp.write(self.dumps() + '\\n')\n\n    def filter(self, op):\n        return _filter_list(self, op)\n\n    def recurse(self, max_depth=-1, depth=0):\n        yield from _recurse_list(self, max_depth, depth)\n\n    def __repr__(self):\n        return f'Makefile({str(self)})'\n\n    def __str__(self):\n        return '\\n'.join([str(i) for i in self])\n\n\nclass Node:\n    def is_block(self):\n        return False\n\n    def is_block_list(self):\n        return False\n\n    def is_comment(self):\n        return False\n\n    def is_directive(self):\n        return False\n\n    def is_test(self):\n        return False\n\n    def is_benchmark(self):\n        return False\n\n    def is_dev(self):\n        return self.is_test() or self.is_benchmark()\n\n    def has_block_list(self):\n        return False\n\n    def filter(self, op):\n        raise NotImplementedError\n\n    def recurse(self, max_depth=-1, depth=0):\n        raise NotImplementedError\n\n\nclass Block(str, Node):\n    @property\n    def child(self):\n        return str(self)\n\n    def __repr__(self):\n        return f'Block({str(self)})'\n\n    def __str__(self):\n        return super().__str__()\n\n    def is_block(self):\n        return True\n\n    def split_comments(self):\n        return _split_comments(str(self))\n\n    def group_comments(self):\n        pass\n\n    def filter(self, op):\n        return op(self)\n\n\nclass BlockList(list, Node):\n    def __init__(self, *args, **kwds):\n        super().__init__(*args, **kwds)\n        assert all([isinstance(i, Node) for i in self])\n\n    @property\n    def child(self):\n        return self\n\n    def __repr__(self):\n        return f'BlockList({str(self)})'\n\n    def __str__(self):\n        return '\\n'.join([str(i) for i in self])\n\n    def is_block_list(self):\n        return True\n\n    def split_comments(self):\n        return BlockList(util.flatten([i.split_comments() for i in self]))\n\n    def group_comments(self):\n        self[:] = _group_comments(self)\n\n    def filter(self, op):\n        return _filter_list(self, op)\n\n    def recurse(self, max_depth=-1, depth=0):\n        yield from _recurse_list(self, max_depth, depth)\n\n\nclass CommentBlock(Node):\n    # the child is either a Block or BlockList\n    def __init__(self, comment, title, child):\n        assert isinstance(child, Node)\n\n        self.comment = comment\n        self.title = title\n        self.child = child\n\n    def __eq__(self, other):\n        return (self.comment, self.title, self.child) == (other.comment, other.title, other.child)\n\n    def __repr__(self):\n        return f'CommentBlock({str(self)})'\n\n    def __str__(self):\n        return f'{self.comment}\\n{str(self.child)}'\n\n    def is_comment(self):\n        return True\n\n    def is_test(self):\n        return self.title is not None and util.is_test(self.title)\n\n    def is_benchmark(self):\n        return self.title is not None and util.is_benchmark(self.title)\n\n    def has_block_list(self):\n        return self.child.is_block_list()\n\n    def split_comments(self):\n        raise NotImplementedError('cannot split comments in split comment block')\n\n    def group_comments(self):\n        raise NotImplementedError('grouping comments should be done outside a comment block')\n\n    def flatten_single(self):\n        if isinstance(self.child, list) and len(self.child) == 1:\n            self.child = self.child[0]\n\n    def filter(self, op):\n        return op(self) and self.child.filter(op)\n\n\nclass DirectiveBlock(Node):\n    # the child is either a Block or BlockList\n    def __init__(self, start, end, child):\n        assert isinstance(child, Node)\n        if isinstance(child, list) and len(child) == 1:\n            child = child[0]\n\n        self.start = start\n        self.end = end\n        self.child = child\n\n    def __eq__(self, other):\n        return (self.start, self.end, self.child) == (other.start, other.end, other.child)\n\n    def __repr__(self):\n        return f'DirectiveBlock({str(self)})'\n\n    def __str__(self):\n        result = f'{self.start}\\n{str(self.child)}'\n        if self.end is not None:\n            result += f'\\n{self.end}'\n        return result\n\n    def is_directive(self):\n        return True\n\n    def has_block_list(self):\n        return self.child.is_block_list()\n\n    def split_comments(self):\n        child = self.child.split_comments()\n        # every caller expects a list, so we return a single-element list\n        return BlockList([DirectiveBlock(self.start, self.end, child)])\n\n    def group_comments(self):\n        self.child.group_comments()\n        self.flatten_single()\n\n    def flatten_single(self):\n        if isinstance(self.child, list) and len(self.child) == 1:\n            self.child = self.child[0]\n\n    def filter(self, op):\n        return op(self) and self.child.filter(op)\n\n\n# split on comment sections, for example the below will split on the\n# benchmarks section.\n#\n#   LOCAL_PATH := $(call my-dir)\n#\n#   # -----------------------------------------------------------------------------\n#   # Benchmarks.\n#   # -----------------------------------------------------------------------------\n#\n#   test_tags := tests\ndef _split_comments(contents):\n    def new_comment(match, nxt=None):\n        comment = match.group(1)\n        groups = match.groups()[1:]\n        lines = [i for i in groups if i is not None]\n        title = '\\n'.join([re.sub(r'[ \\t]*#[ \\t]*', '', i) for i in lines])\n        if nxt is None:\n            data = contents[match.end():]\n        else:\n            data = contents[match.end():nxt.start()]\n        if nxt is not None:\n            assert data.endswith('\\n')\n            data = data[:-1]\n        return CommentBlock(comment, title, Block(data))\n\n    # if we just have 1 or 2 characters, can falsely match.\n    # headers can be `# -----`, `# ======`, or `########`.\n    # the title can be prefixed, suffixed, or sandwiched by the header.\n    def title_pattern():\n        line = fr'{sp}*#{sp}*{comment}'\n        return fr'(?:(?:{line}{nl})*{line})'\n\n    def sandwich_pattern(sep):\n        # matches header-title-header\n        title = title_pattern()\n        return fr'{sp}*{sep}{nl}({title}){nl}{sp}*{sep}'\n\n    def suffix_pattern(sep):\n        # matches title-header\n        title = title_pattern()\n        return fr'({title}){nl}{sp}*{sep}'\n\n    def prefix_pattern(sep):\n        # matches header-title, needs to be last due to greedy regex\n        title = title_pattern()\n        return fr'{sp}*{sep}{nl}({title})'\n\n    def sep_pattern(sep):\n        sandwich = sandwich_pattern(sep)\n        suffix = suffix_pattern(sep)\n        prefix = prefix_pattern(sep)\n        return fr'(?:{sandwich})|(?:{prefix})|(?:{suffix})'\n\n    def create_pattern(*seps):\n        groups = []\n        for sep in seps:\n            groups.append(fr'(?:{sep_pattern(sep)})')\n        return fr'(?m)^({\"|\".join(groups)}){nl}?'\n\n    sep1 = r'#\\s+={5,}'\n    sep2 = r'#\\s+-{5,}'\n    sep3 = r'#{6,}'\n    sp = r'[ \\t]'\n    nl = r'(?:\\r\\n|\\r|\\n)'\n    # can have empty headers, such as `#####\\n#`\n    comment = r'[^\\x00-\\x08\\x0A-\\x1F]*'\n    pattern = create_pattern(sep1, sep2, sep3)\n\n    blocks = BlockList()\n    if not contents:\n        return blocks\n\n    matches = list(re.finditer(pattern, contents))\n    if len(matches) == 0:\n        blocks.append(Block(contents))\n    else:\n        first = matches[0]\n        last = matches[-1]\n        if first.start() != 0:\n            assert contents[first.start() - 1] == '\\n'\n            blocks.append(Block(contents[:first.start() - 1]))\n        for (match, nxt) in util.windows(matches, 2):\n            blocks.append(new_comment(match, nxt))\n        blocks.append(new_comment(last))\n\n    return blocks\n\n\n# lines is an iterable over each line in the content. splits like something\n# above into a start token of `ifneq ($(ENV2),)`, and end of `endif`,\n# and the internal contents as a `Block`.\n#\n#   ifneq ($(ENV2),)\n#       benchmark_src_files += bench1.cc\n#   else\n#       benchmark_src_files += bench2.cc\n#   endif\ndef _split_directives(lines, in_scope=False):\n    def add_current(blocks, current):\n        if current:\n            blocks.append(Block('\\n'.join(current)))\n\n    # we ignore else since removing it won't actually affect the code\n    start_directives = ('ifeq', 'ifneq', 'ifdef', 'ifndef')\n    end_directives = ('endif',)\n\n    blocks = BlockList()\n    current = []\n    for line in lines:\n        trimmed = line.lstrip()\n        if trimmed.startswith(start_directives):\n            start = line\n            add_current(blocks, current)\n            child, end = _split_directives(lines, True)\n            directive = DirectiveBlock(start, end, child)\n            directive.flatten_single()\n            blocks.append(directive)\n            current = []\n        elif in_scope and trimmed.startswith(end_directives):\n            end = line\n            add_current(blocks, current)\n            return blocks, end\n        else:\n            current.append(line)\n\n    add_current(blocks, current)\n\n    return blocks, None\n\n\n# this groups directives and comments so any directives within a\n# comment block are properly grouped. say i have the following:\n#\n#   LOCAL_PATH := $(call my-dir)\n#\n#   # -----------------------------------------------------------------------------\n#   # Section 1.\n#   # -----------------------------------------------------------------------------\n#   LOCAL_SRC_FILES := src.c\n#   ifneq ($(ENV2),)\n#       benchmark_src_files += bench1.cc\n#   else\n#       benchmark_src_files += bench2.cc\n#   endif\n#\n#   # -----------------------------------------------------------------------------\n#   # Section 2.\n#   # -----------------------------------------------------------------------------\n#   LOCAL_CFLAGS := $(test_c_flags)\n#\n#   normally, we'd have 5 sections: block, comment, directive, block, comment\n#   however, we want to group it in block, comment, comment, where the directive\n#   and subsequent block are in the comment.\ndef _group_comments(blocks):\n    def add_current(result, current):\n        if isinstance(current.child, list) and len(current.child) == 1:\n            current.child = current.child[0]\n        result.append(current)\n\n    def new_comment(block):\n        current = CommentBlock(block.comment, block.title, BlockList())\n        if block.child:\n            current.child.append(block.child)\n        return current\n\n    result = BlockList()\n    current = BlockList()\n    for block in blocks:\n        # any comments cannot have been grouped already, so we assume str values\n        assert not block.is_comment() or isinstance(block.child, str)\n        assert not block.is_block_list()\n        if not block.is_comment():\n            block.group_comments()\n\n        if current.is_comment() and block.is_comment():\n            # new comment replaces the old one\n            current.flatten_single()\n            result.append(current)\n            current = new_comment(block)\n        elif block.is_comment():\n            # first comment block seen in the file\n            result += current\n            current = new_comment(block)\n        elif current.is_comment():\n            # regular block after a comment block\n            current.child.append(block)\n        else:\n            # regular block before any comment blocks\n            current.append(block)\n\n    if current.is_comment():\n        current.flatten_single()\n        result.append(current)\n    else:\n        result += current\n\n    return result\n\n\n# retain all items matching the condition in a list\ndef _filter_list(lst, op):\n    # use slice assignment to ensure this happens in-place\n    lst[:] = [i for i in lst if i.filter(op)]\n    return lst\n\n\n# yield iteratively all child blocks\ndef _recurse_list(lst, max_depth=-1, depth=0):\n    if depth != max_depth:\n        for node in lst:\n            yield node\n            if node.has_block_list():\n                yield from node.child.recurse(max_depth, depth + 1)\n"
  },
  {
    "path": "docker/android/android/soong.py",
    "content": "'''\n    soong\n    =====\n\n    utilities to process soong blueprint files. these are a go-like,\n    json-like data file format similar. they support nested maps, lists,\n    bools, strings, and use of variables. for example:\n\n        array = [\"...\"]\n        cc_defaults {\n          name: \"target\",\n          options: array,\n          flags: [\"...\"],\n        }\n        cc_test {\n          name: \"test\",\n          defaults: [\"target\"],\n          srcs: [\"test.cc\"],\n          nested: {\n            array: {\n              option: false,\n            },\n          },\n        }\n\n    the specification can be found below:\n    https://source.android.com/docs/core/tests/development/blueprints\n    https://android.googlesource.com/platform/build/soong/+/refs/heads/master/README.md\n\n    they also support single-line C++-style and multiline C-style comments.\n    the valid types are:\n        - bool (`true`, `false`)\n        - int\n        - string\n        - list (of strings)\n        - map\n\n    both lists and maps support optional trailing commas. any value type\n    can be present in a map, while only strings are allowed in lists.\n    integers, strings, arrays and maps also also support the `+` operator,\n    where `+` sums up integers. for strings and arrays, it appends the new\n    data. for maps, it produces the union of both keys, and for keys present\n    in both, it appends the value on the right-operand to the value in the\n    left one.\n\n    variable assignment produces immutable types, except for the `+=` operator.\n    `+=` does the described operation above in-place.\n\n    this parser doesn't need to be exactly correct: it does not need to reject\n    subtley invalid input. for example `name = {  }` may or may not be correct,\n    but it's fine to accept it as long as we output it identically. this is\n    supposed to handle all correct input and outputs it as correct output:\n    it doesn't need to validate type correctness.\n\n    this uses LALR parsing since it makes the grammar very easy to define and\n    the parsing simple. since the build step and repository synchronization\n    is much slower, the performance here is practically irrelevant.\n'''\n\nimport json\nimport sys\n\nimport sly\n\nfrom . import util\n\n# dictionaries got insertion order in 3.6, guaranteed in 3.7\nassert sys.version_info >= (3, 6)\n\n# base character defs\n_H = r'[0-9a-f]'\n_NL = r'\\n|\\r\\n|\\r|\\f'\n_UNICODE = fr'\\\\{_H}{1,6}(\\r\\n|[ \\t\\r\\n\\f])?'\n_ESCAPE = r'{_UNICODE}|\\\\[^\\r\\n\\f0-9a-f]'\n_SINGLELINE_COMMENT = r'\\/\\/.*'\n# can't use reflags without setting them for all, so do manual dotall\n_MULTILINE_COMMENT = r'\\/\\*[\\u0000-\\U0010FFFF]*?\\*\\/'\n_COMMENT = fr'(?:{_SINGLELINE_COMMENT})|(?:{_MULTILINE_COMMENT})'\n\n\ndef loads(contents, *_, **__):\n    return Ast.loads(contents)\n\n\ndef load(fp, *_, **__):\n    return Ast.load(fp)\n\n\ndef dumps(soong, pretty=True, indent=4, *_, **__):\n    return soong.dumps(pretty, indent)\n\n\ndef dump(soong, fp, pretty=True, indent=4, *_, **__):\n    return soong.dump(fp, pretty, indent)\n\n\nclass Lexer(sly.Lexer):\n    tokens = {\n        BOOL,\n        INTEGER,\n        IDENT,\n        STRING,\n        LBRACKET,\n        RBRACKET,\n        LBRACE,\n        RBRACE,\n        COLON,\n        COMMA,\n        EQUALS,\n        PLUS,\n    }\n    ignore = ' \\t'\n    ignore_comment = _COMMENT\n\n    # Tokens\n    # this uses a string regex based on the CSS2.1 grammar\n    STRING = fr'\"([^\\n\\r\\f\\\\\"]|\\\\{_NL}|{_ESCAPE})*\"'\n    INTEGER = r'\\d+'\n    BOOL = '(?:true)|(?:false)'\n    IDENT = r'[a-zA-Z_][a-zA-Z0-9_]*'\n    LBRACKET = r'\\['\n    RBRACKET = r'\\]'\n    LBRACE = r'\\{'\n    RBRACE = r'\\}'\n    COLON = r':'\n    COMMA = r','\n    EQUALS = r'='\n    PLUS = r'\\+'\n\n    @_(r'\\n+')\n    def newline(self, token):\n        self.lineno += token.value.count('\\n')\n\n    def error(self, token):\n        raise ValueError(f'Illegal character \\'{token.value[0]}\\'')\n\n\nclass Parser(sly.Parser):\n    tokens = Lexer.tokens\n\n    precedence = (\n        ('left', PLUS),\n    )\n\n    @_('rules')\n    def ast(self, prod):\n        return Ast(prod.rules)\n\n    @_('empty')\n    def ast(self, prod):\n        return Ast()\n\n    @_('rules rule')\n    def rules(self, prod):\n        return prod.rules + [prod.rule]\n\n    @_('rule')\n    def rules(self, prod):\n        return [prod.rule]\n\n    @_('assignment', 'binary_operator_assignment', 'scope')\n    def rule(self, prod):\n        return prod[0]\n\n    @_('ident EQUALS expr')\n    def assignment(self, prod):\n        return Assignment(prod.ident, prod.expr)\n\n    @_('ident PLUS EQUALS expr')\n    def binary_operator_assignment(self, prod):\n        return BinaryOperatorAssignment(\n            prod.ident,\n            f'{prod[1]}{prod[2]}',\n            prod.expr,\n        )\n\n    @_('expr PLUS expr')\n    def binary_operator(self, prod):\n        return BinaryOperator(prod[0], prod[1], prod[2])\n\n    @_('ident map')\n    def scope(self, prod):\n        return Scope(prod.ident, prod.map)\n\n    @_('LBRACE pairs RBRACE', 'LBRACE pairs COMMA RBRACE')\n    def map(self, prod):\n        return Map(prod.pairs)\n\n    @_('LBRACE RBRACE')\n    def map(self, prod):\n        return Map()\n\n    @_('pairs COMMA pair')\n    def pairs(self, prod):\n        return prod.pairs + [prod.pair]\n\n    @_('pair')\n    def pairs(self, prod):\n        return [prod.pair]\n\n    @_('ident COLON expr', 'ident EQUALS expr')\n    def pair(self, prod):\n        return (prod.ident, MapValue(prod[1], prod.expr))\n\n    @_('ident', 'binary_operator', 'map', 'list', 'string', 'integer', 'bool')\n    def expr(self, prod):\n        return prod[0]\n\n    @_('LBRACKET sequence RBRACKET', 'LBRACKET sequence COMMA RBRACKET')\n    def list(self, prod):\n        return List(prod.sequence)\n\n    @_('LBRACKET RBRACKET')\n    def list(self, prod):\n        return List()\n\n    @_('sequence COMMA list_item')\n    def sequence(self, prod):\n        return prod.sequence + [prod.list_item]\n\n    @_('list_item')\n    def sequence(self, prod):\n        return [prod.list_item]\n\n    @_('list_item PLUS list_item')\n    def list_item(self, prod):\n        return BinaryOperator(prod[0], '+', prod[2])\n\n    @_('string', 'ident', 'map')\n    def list_item(self, prod):\n        return prod[0]\n\n    @_('IDENT')\n    def ident(self, prod):\n        return Ident(prod.IDENT)\n\n    @_('STRING')\n    def string(self, prod):\n        return String(prod.STRING)\n\n    @_('INTEGER')\n    def integer(self, prod):\n        return Integer(prod.INTEGER)\n\n    @_('BOOL')\n    def bool(self, prod):\n        return Bool(json.loads(prod.BOOL))\n\n    # needed in case no tokens are produced\n    @_('')\n    def empty(self, p):\n        pass\n\n    def error(self, token):\n        raise ValueError(f'Illegal token {repr(token)}')\n\n\nclass Node:\n    def is_assignment(self):\n        return False\n\n    def is_binary_operator_assignment(self):\n        return False\n\n    def is_binary_operator(self):\n        return False\n\n    def is_scope(self):\n        return False\n\n    def is_map(self):\n        return False\n\n    def is_list(self):\n        return False\n\n    def is_map_value(self):\n        return False\n\n    def is_ident(self):\n        return False\n\n    def is_string(self):\n        return False\n\n    def is_integer(self):\n        return False\n\n    def is_bool(self):\n        return False\n\n\nclass Ast(list, Node):\n    def __init__(self, values=None):\n        if values is None:\n            values = []\n        valid_nodes = (Assignment, BinaryOperatorAssignment, Scope)\n        assert all(isinstance(i, valid_nodes) for i in values)\n        super().__init__(values)\n\n    def __repr__(self):\n        return f'Ast({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        assert depth == 0\n        return '\\n'.join([i.to_str(pretty, indent, depth) for i in self])\n\n    @staticmethod\n    def loads(contents, *_, **__):\n        lexer = Lexer()\n        tokens = lexer.tokenize(contents)\n        parser = Parser()\n        return parser.parse(tokens)\n\n    @staticmethod\n    def load(fp, *_, **__):\n        return Ast.loads(fp.read())\n\n    def dumps(self, pretty=True, indent=4, *_, **__):\n        return self.to_str(pretty, indent)\n\n    def dump(self, fp, pretty=True, indent=4, *_, **__):\n        # always write a trailing newline\n        fp.write(self.dumps(pretty, indent) + '\\n')\n\n    def filter(self, op):\n        # use slice assignment to ensure this happens in-place\n        self[:] = [i for i in self if op(i)]\n\n\nclass Assignment(Node):\n    def __init__(self, name, expr):\n        self.name = name\n        self.expr = expr\n\n    def __repr__(self):\n        return f'Assignment({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        return f'{str(self.name)} = {self.expr.to_str(pretty, indent, depth)}'\n\n    def is_assignment(self):\n        return True\n\n    def __eq__(self, other):\n        return (self.name, self.expr) == (other.name, other.expr)\n\n\nclass BinaryOperatorAssignment(Node):\n    def __init__(self, name, op, expr):\n        self.name = name\n        self.op = op\n        self.expr = expr\n\n    def __repr__(self):\n        return f'BinaryOperatorAssignment({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        expr = self.expr.to_str(pretty, indent, depth)\n        return f'{str(self.name)} {self.op} {expr}'\n\n    def is_binary_operator_assignment(self):\n        return True\n\n    def __eq__(self, other):\n        return (self.name, self.op, self.expr) == (other.name, other.op, other.expr)\n\n\nclass BinaryOperator(Node):\n    def __init__(self, lhs, op, rhs):\n        self.lhs = lhs\n        self.op = op\n        self.rhs = rhs\n\n    def __repr__(self):\n        return f'BinaryOperator({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        lhs = self.lhs.to_str(pretty, indent, depth)\n        rhs = self.rhs.to_str(pretty, indent, depth)\n        return f'{lhs} {self.op} {rhs}'\n\n    def is_binary_operator(self):\n        return True\n\n    def str_op(self, cmp):\n        return (\n            (self.lhs.is_string() and self.lhs.str_op(cmp))\n            or (self.rhs.is_string() and self.rhs.str_op(cmp))\n        )\n\n    def __eq__(self, other):\n        return (self.lhs, self.op, self.rhs) == (other.lhs, other.op, other.rhs)\n\n\nclass Scope(Node):\n    def __init__(self, name, map):\n        self.name = name\n        self.map = map\n\n    def __repr__(self):\n        return f'Scope({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        return f'{str(self.name)} {self.map.to_str(pretty, indent, depth)}'\n\n    def is_scope(self):\n        return True\n\n    def __eq__(self, other):\n        return (self.name, self.map) == (other.name, other.map)\n\n    def is_art_check(self):\n        return 'art-check' in self.name.lower() or self.map.is_art_check()\n\n    def is_test(self):\n        return util.is_test(self.name) or self.map.is_test()\n\n    def is_benchmark(self):\n        return util.is_benchmark(self.name) or self.map.is_benchmark()\n\n    def is_dev(self):\n        return self.is_art_check() or self.is_test() or self.is_benchmark()\n\n\nclass Map(dict, Node):\n    def __repr__(self):\n        return f'Map({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        fmt = lambda x: x.to_str(pretty, indent, depth + 1)\n        result = '{'\n        pairs = [f'{fmt(k)}{fmt(v)}' for k, v in self.items()]\n        if len(self) == 0:\n            result += '}'\n        elif pretty:\n            result += '\\n'\n            for pair in pairs:\n                result += _indent(indent, depth + 1) + f'{pair},\\n'\n            result += _indent(indent, depth) + '}'\n        else:\n            result += ', '.join(pairs) + '}'\n\n        return result\n\n    def is_map(self):\n        return True\n\n    def is_art_check(self):\n        name = self.get('name')\n        if name is None:\n            return False\n        return 'art-check' in name.value.lower()\n\n    def is_test(self):\n        name = self.get('name')\n        if name is None:\n            return False\n        # cannot remove `py2-c-module-_ctypes_test` type tests,\n        # since they're needed to be linked in the final binary.\n        lower = name.value.lower()\n        return util.is_test(lower) and 'py2-c-module' not in lower\n\n    def is_benchmark(self):\n        name = self.get('name')\n        if name is None:\n            return False\n        return util.is_benchmark(name.value)\n\n    def is_dev(self):\n        return self.is_test() or self.is_benchmark()\n\n    def filter(self, op):\n        filtered = {k: v for k, v in self.items() if op(k, v)}\n        self.clear()\n        self.update(filtered)\n\n    def recurse(self, max_depth=-1, depth=0):\n        # recursively find all key/value pairs the current and any submaps\n        if depth != max_depth:\n            for key, value in self.items():\n                yield (key, value, depth + 1, self)\n                if value.value.is_map():\n                    yield from value.value.recurse(max_depth, depth + 1)\n\n\nclass List(list, Node):\n    def __repr__(self):\n        return f'List({str(self)})'\n\n    def __str__(self):\n        return self.to_str(pretty=False)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        def fmt(x):\n            if x.is_map():\n                return x.to_str(pretty, indent, depth)\n            return x.to_str(pretty, indent, depth + 1)\n        result = '['\n        if len(self) <= 1 or not pretty:\n            result += ', '.join([fmt(i) for i in self]) + ']'\n        else:\n            result += '\\n'\n            for element in self:\n                result += _indent(indent, depth + 1) + f'{fmt(element)},\\n'\n            result += _indent(indent, depth) + ']'\n\n        return result\n\n    def is_list(self):\n        return True\n\n    def filter(self, op):\n        # use slice assignment to ensure this happens in-place\n        self[:] = [i for i in self if op(i)]\n\n\nclass MapValue(Node):\n    def __init__(self, delimiter, value):\n        # map key/value separators can be `:` or `=`.\n        assert delimiter in (':', '=')\n        self.delimiter = delimiter\n        self.value = value\n\n    def __repr__(self):\n        return f'MapValue({str(self)})'\n\n    def __str__(self):\n        return self.to_str(False)\n\n    def __eq__(self, other):\n        # delimiter doesn't matter for equality comparison\n        if isinstance(other, MapValue):\n            return self.value == other.value\n        return self.value == other\n\n    def __len__(self):\n        return len(self.value)\n\n    def to_str(self, pretty=True, indent=4, depth=0):\n        value = self.value.to_str(pretty, indent, depth)\n        if self.delimiter == '=':\n            return f' = {value}'\n        return f': {value}'\n\n    def str_op(self, cmp):\n        return self.value.str_op(cmp)\n\n    def is_map_value(self):\n        return True\n\n    def filter(self, op):\n        self.value.filter(op)\n\n\nclass Ident(str, Node):\n    def __repr__(self):\n        return f'Ident({str(self)})'\n\n    def __str__(self):\n        return super().__str__()\n\n    def to_str(self, *_, **__):\n        return str(self)\n\n    def is_ident(self):\n        return True\n\n\nclass String(str, Node):\n    def __repr__(self):\n        return f'String({self.to_str()})'\n\n    def to_str(self, *_, **__):\n        return f'{super().__str__()}'\n\n    def str_op(self, cmp):\n        return cmp(self)\n\n    def __str__(self):\n        # `\"target\"` should be shown as `'target'`, not `'\"target\"'`\n        return super().__str__()[1:-1]\n\n    def __eq__(self, other):\n        if type(other) is String:\n            return str(self) == str(other)\n        # we want to be compare equal to the string's value\n        return str(self) == other\n\n    def __ne__(self, other):\n        # need to override `__ne__` which normally uses a pyslot\n        return not self.__eq__(other)\n\n    def is_string(self):\n        return True\n\n\nclass Integer(int, Node):\n    def __repr__(self):\n        return f'Integer({str(self)})'\n\n    def __str__(self):\n        return str(int(self))\n\n    def to_str(self, *_, **__):\n        return str(self)\n\n    def is_integer(self):\n        return True\n\n\nclass Bool(Node):\n    def __init__(self, value=False):\n        self.value = value\n\n    def __bool__(self):\n        return self.value\n\n    def __repr__(self):\n        return f'Bool({json.dumps(self.value)})'\n\n    def __str__(self):\n        return json.dumps(self.value)\n\n    def to_str(self, *_, **__):\n        return str(self)\n\n    def is_bool(self):\n        return True\n\n    def __eq__(self, other):\n        return self.value == other.value\n\n\ndef _indent(indent=4, depth=0, char=' '):\n    return char * indent * depth\n"
  },
  {
    "path": "docker/android/android/util.py",
    "content": "import re2 as re\n\n\ndef windows(sequence, count):\n    for i in range(len(sequence) - count + 1):\n        yield sequence[i:i + count]\n\n\ndef flatten(lst):\n    return [i for sublist in lst for i in sublist]\n\n\ndef _is_match(pattern, string):\n    return re.search(pattern, string) is not None\n\n\ndef is_test(string):\n    # need to consider that works like `latest` exist\n    # also need to consider `non-test` for `fmtlib`.\n    if 'non-test' in string.lower():\n        return False\n    pattern = r'(?i)(?:^|[^A-Za-z0-9]|g)test'\n    return _is_match(pattern, string)\n\n\ndef is_benchmark(string):\n    pattern = r'(?i)(?:^|[^A-Za-z0-9])benchmark'\n    return _is_match(pattern, string)\n"
  },
  {
    "path": "docker/android/pyproject.toml",
    "content": "[project]\nname = \"android\"\nversion = \"0.0.0-dev.0\"\nlicense = { text = \"MIT OR Apache-2.0\" }\ndependencies = [\"sly==0.4\", \"google-re2==1.0\"]\n\n[build-system]\nrequires = [\n    \"setuptools >= 35.0.2\",\n    \"setuptools_scm >= 2.0.0, <3\"\n]\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "docker/android/scripts/build-system.py",
    "content": "#!/usr/bin/env python\n'''\n    Remove most unittests from Android soong blueprint\n    files, most of which are identified via a `cc_test*`\n    scope identifier, as well as some additional `subdirs`\n    identifiers and Makefile specifiers.\n\n    This also allows you to backup and restore these scripts.\n    The build files are automatically backed up by default.\n'''\n\nimport argparse\nimport glob\nimport os\nimport shutil\nimport subprocess\nimport sys\n\nSCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__))\nPROJECT_DIR = os.path.dirname(SCRIPTS_DIR)\nsys.path.insert(0, PROJECT_DIR)\n\nimport android\nimport android.make\nimport android.soong\n\n\ndef print_verbose(message, verbose):\n    if verbose:\n        print(message)\n\n\ndef backup(src, args, *_):\n    dst = src + '.bak'\n    print_verbose(f'creating backup of file \"{src}\" at \"{dst}\"', args.verbose)\n    shutil.copy2(src, dst)\n\n\ndef restore(dst, args, *_):\n    src = dst + '.bak'\n    if os.path.exists(src):\n        print_verbose(f'restoring from backup \"{src}\" to \"{dst}\"', args.verbose)\n        shutil.copy2(src, dst)\n\n\ndef filter_map(map, remove):\n    keys = list(map)\n    for key in keys:\n        if not item_op(map[key].value, remove):\n            del map[key]\n    return True\n\n\ndef filter_list(lst, remove):\n    lst.filter(lambda x: item_op(x, remove))\n    return True\n\n\ndef item_op(item, remove):\n    if item.is_map():\n        return filter_map(item, remove)\n    elif item.is_list():\n        return filter_list(item, remove)\n    elif item.is_string() or item.is_binary_operator():\n        return item.str_op(lambda y: not any(i in y.lower() for i in remove))\n    raise TypeError(f'got unexpected type of {type(item)}')\n\n\ndef remove_soong_tests(path, args, *_):\n    print_verbose(f'removing soong tests from \"{path}\"', args.verbose)\n    with open(path) as file:\n        ast = android.soong.load(file)\n    # remove the test or benchmark scopes, IE, this with `cc_test`\n    # or those with `{name: \"test\"}`, etc.\n    ast.filter(lambda x: not (x.is_scope() and x.is_dev()))\n    # need to remove test and benchmark subdirs\n    test_names = ('test', 'benchmark')\n    subdirs = [i for i in ast if i.name == 'subdirs']\n    for sub in subdirs:\n        assert type(sub.expr) is android.soong.List\n        filter_list(sub.expr, test_names)\n    # remove gtest dependencies from regular targets.\n    for node in ast:\n        map = None\n        if not node.is_scope() and not node.expr.is_map():\n            continue\n        if node.is_scope():\n            map = node.map\n        else:\n            map = node.expr\n        test_names = ('libgtest', 'test-proto', 'starlarktest')\n        for key, value, *_ in map.recurse():\n            if value.value.is_list():\n                if key == 'testSrcs':\n                    value.value.clear()\n                else:\n                    filter_list(value, test_names)\n\n    with open(path, 'w') as file:\n        ast.dump(file)\n\n\ndef remove_makefile_tests(path, args, *_):\n    print_verbose(f'removing makefile tests from \"{path}\"', args.verbose)\n    with open(path) as file:\n        makefile = android.make.load(file)\n    makefile.filter(lambda x: not x.is_dev())\n    with open(path, 'w') as file:\n        makefile.dump(file)\n\n\ndef remove_tests(path, args, processor):\n    if os.path.exists(path + '.bak'):\n        restore(path, args)\n    elif not args.disable_backup:\n        backup(path, args)\n    processor(path, args)\n\n\ndef stash(root):\n    git_glob = f'{root}/**/.git'\n    for path in glob.iglob(git_glob, recursive=True):\n        os.chdir(os.path.dirname(path))\n        subprocess.check_call(['git', 'stash'])\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    action_group = parser.add_mutually_exclusive_group(required=True)\n    action_group.add_argument(\n        '--backup',\n        help='backup build files',\n        action='store_true',\n    )\n    action_group.add_argument(\n        '--restore',\n        help='restore build files',\n        action='store_true',\n    )\n    action_group.add_argument(\n        '--remove-tests',\n        help='remove most tests from the build system.',\n        action='store_true',\n    )\n    action_group.add_argument(\n        '--stash',\n        help='stash all local changes.',\n        action='store_true',\n    )\n    parser.add_argument(\n        '--disable-backup',\n        help='disable automatic backup of build files during processing.',\n        action='store_false',\n    )\n    flags_group = parser.add_mutually_exclusive_group()\n    flags_group.add_argument(\n        '--soong-only',\n        help='only process soong build files.',\n        action='store_true',\n    )\n    flags_group.add_argument(\n        '--makefile-only',\n        help='only process makefiles.',\n        action='store_true',\n    )\n    parser.add_argument(\n        '-V',\n        '--version',\n        action='version',\n        version=android.__version__\n    )\n    parser.add_argument(\n        '-v',\n        '--verbose',\n        help='display verbose diagnostic info.',\n        action='store_true',\n    )\n    args = parser.parse_args()\n    if args.backup:\n        action = backup\n    elif args.restore:\n        action = restore\n    elif args.remove_tests:\n        action = remove_tests\n    elif args.stash:\n        action = stash\n\n    # root_dir is only available 3.10+\n    root = os.environ.get('ANDROID_ROOT')\n    if root is None:\n        root = os.getcwd()\n    if args.stash:\n        return stash(root)\n\n    if not args.makefile_only:\n        soong_glob = f'{root}/**/Android.bp'\n        for path in glob.iglob(soong_glob, recursive=True):\n            action(path, args, remove_soong_tests)\n\n    if not args.soong_only:\n        make_glob = f'{root}/**/Android.mk'\n        for path in glob.iglob(make_glob, recursive=True):\n            action(path, args, remove_makefile_tests)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "docker/android/tests/Addition.bp",
    "content": "// special file testing `+` and `+=` operators\n// this is assignment + add assignment\nlist = [\"value1\"]\nlist += [\"value2\"]\nnumber = 1\nnumber += 2\nstring = \"string\"\nstring += \"_suffix\"\nscope {\n    name: \"target\",\n}\nscope += {\n    name: \"_suffix\",\n    srcs: [\n        // sequence items just have to evaluate to strings\n        \"tree.cc\" + string,\n        \"lib.cc\",\n    ],\n}\n// this is addition with lhs idents\nlhs_sum = number + 4\nlhs_string = string + \"_suffix\"\nlhs_list = list + [\"value3\"]\nlhs_scope = scope + {\n    name: \"_suffix\",\n    cflags: [\n        \"-Wall\",\n    ],\n}\n// this is addition with rhs idents\nrhs_sum = 4 + number\nrhs_string = \"prefix_\" + string\nrhs_list = [\"value0\"] + list\nrhs_scope = {\n    name: \"_suffix\",\n    cflags: [\n        \"-Wall\",\n    ],\n} + scope\n// this is addition with both being non-idents\nexpr_sum = 4 + 1\nexpr_string = \"prefix_\" + \"suffix\"\nexpr_list = [\"value0\"] + [\"value1\"]\nexpr_scope = {} + {\n    name: \"target\",\n}\n// test multiple binary ops\ntri_sum = 4 + 1 + 2\ntri_string = \"prefix_\" + \"middle\" + \"_suffix\"\ntri_list = [\"value0\"] + [\"value1\"] + [\"value2\"]\ntri_scope = {} + {\n    name: \"target\",\n} + {}\n// test sequence lhs and rhs strings\nhome = \"dir/\"\ntest = \"test.c\"\n\nfiles = [\n    home + \"file.c\",\n    \"test/\" + test,\n    home + test,\n]\n"
  },
  {
    "path": "docker/android/tests/Android.bp",
    "content": "// sample heading comment\nsample_array = [\n    \"value1\",\n    \"value2\",\n]\n/**\n    sample\n    multiline\n    comment\n */\ncc_defaults {\n    name: \"target\",\n    cflags: [\n        \"-Wall\",\n        \"-fstrict-aliasing\",\n    ],\n    option: true,\n    tidy_checks: sample_array,\n    tidy_checks_as_errors: sample_array,\n    array: [\n        \"-short\",\n        \"--root='/path/to/dir'\",\n    ],\n}\ncc_library_static {\n    name: \"static_lib\",\n    srcs: [\n        \"tree.cc\",\n        \"lib.cc\",\n    ],\n    include_dirs: [\"bionic/libc\"],\n    export_include_dirs: [\".\"],\n}\ncc_library {\n    name: \"lib\",\n    srcs: [\n        \"tree.cc\",\n        \"lib.cc\",\n    ],\n    include_dirs: [\"bionic/libc\"],\n    export_include_dirs: [\".\"],\n}\ncc_test {\n    name: \"test\",\n    defaults: [\"target\"],\n    srcs: [\"test.cc\"],\n    nested: {\n        array: {\n            option: false,\n        },\n    },\n}\ncc_test_host {\n    name: \"host_test\",\n    include_dirs: [\"path/to/lib\"],\n    compile_multilib: \"64\",\n    static_libs: [\n        \"libm\",\n        \"libz\",\n    ],\n    host_ldlibs: [\n        \"-ldl\",\n        \"-lzstd\",\n        \"-l\" + \"z\",\n    ],\n    shared_libs: [],\n    cflags = [\n        \"-Wall\",\n        \"-fstrict-aliasing\",\n    ],\n}\ncc_defaults {\n    name: \"custom\",\n    shared_libs: [\"libcustom\"],\n    whole_static_libs: [\n        \"libz\",\n        \"libgtest_main\",\n    ],\n    host_ldlibs: [\"-lgtest\"],\n}\n"
  },
  {
    "path": "docker/android/tests/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nLOCAL_SRC_FILES := config.c\nLOCAL_MODULE := config\nLOCAL_SHARED_LIBRARIES := libcutils\nLOCAL_CFLAGS := -Werror\n\ninclude $(BUILD_HOST_EXECUTABLE)\n\nLOCAL_PATH := $(call my-dir)\n\n# -----------------------------------------------------------------------------\n# Benchmarks.\n# -----------------------------------------------------------------------------\n\ntest_tags := tests\n\nbenchmark_c_flags := \\\n\t-Wall -Wextra \\\n\t-Werror \\\n\t-fno-builtin \\\n\nbenchmark_src_files := \\\n\tbenchmark_main.cc \\\n\tbench.cc\n\n# Build benchmarks.\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := benchmarks\nLOCAL_MODULE_TAGS := tests\nLOCAL_CFLAGS += $(benchmark_c_flags)\nLOCAL_SHARED_LIBRARIES += libm libdl\nLOCAL_SRC_FILES := $(benchmark_src_files)\n\n# -----------------------------------------------------------------------------\n# Unit tests.\n# -----------------------------------------------------------------------------\n\ntest_c_flags := \\\n\t-g \\\n\t-Wall \\\n\t-Werror\n\n##################################\n# test executable\nLOCAL_MODULE := module\nLOCAL_SRC_FILES := src.c\nLOCAL_SHARED_LIBRARIES := libcutils\nLOCAL_CFLAGS := $(test_c_flags)\nLOCAL_MODULE_RELATIVE_PATH := config-tests\n\n# Unit tests.\n# =========================================================\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := init_tests\nLOCAL_SRC_FILES := \\\n\tinit_parser_test.cc \\\n\tproperty_service_test.cc \\\n\tservice_test.cc \\\n\tutil_test.cc \\\n\n##################################\n# test executable\nLOCAL_MODULE := module\nLOCAL_SRC_FILES := src.c\nLOCAL_SHARED_LIBRARIES := libcutils\nLOCAL_CFLAGS := $(test_c_flags)\nLOCAL_MODULE_RELATIVE_PATH := config-tests\nLOCAL_SHARED_LIBRARIES += \\\n\tlibcutils \\\n\tlibbase \\\n\nLOCAL_STATIC_LIBRARIES := libinit\nLOCAL_SANITIZE := integer\nLOCAL_CLANG := true\nLOCAL_CPPFLAGS := -Wall -Wextra -Werror\ninclude $(BUILD_NATIVE_TEST)\n\n# Other section.\n# =========================================================\ninclude $(call all-makefiles-under,$(LOCAL_PATH))\n\n# =============================================================================\n# Unit tests.\n# =============================================================================\n\ntest_c_flags := \\\n\t-g \\\n\t-Wall \\\n\t-Werror\n\n##################################\n# test executable\nLOCAL_MODULE := mod2\nLOCAL_SRC_FILES := mod.c\nLOCAL_SHARED_LIBRARIES := libcutils\nLOCAL_CFLAGS := $(test_c_flags)\nLOCAL_MODULE_RELATIVE_PATH := mod2-tests\n"
  },
  {
    "path": "docker/android/tests/Comments.mk",
    "content": "# 1) sample grouping:\n#    - text + suffix\n#    - some more text (the format)\n#    - API and policy info\n#    - more API + policy info\n"
  },
  {
    "path": "docker/android/tests/Empty.bp",
    "content": "// this file only has comments\n"
  },
  {
    "path": "docker/android/tests/Empty.mk",
    "content": "\n"
  },
  {
    "path": "docker/android/tests/FakeTitle.mk",
    "content": "########################################################################\n#\nLOCAL_PATH := $(call my-dir)\n"
  },
  {
    "path": "docker/android/tests/Grouped.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n# -----------------------------------------------------------------------------\n# Section 1.\n# -----------------------------------------------------------------------------\nLOCAL_SRC_FILES := src.c\nifneq ($(ENV1),)\n\t# -----------------------------------------------------------------------------\n\t# Section 2.\n\t# -----------------------------------------------------------------------------\n\tifneq ($(ENV2),)\n\t\tbenchmark_src_files += bench1.cc\n\telse\n\t\tbenchmark_src_files += bench2.cc\n\tendif\nelse\n\tbenchmark_src_files += bench3.cc\nendif\n\n# -----------------------------------------------------------------------------\n# Section 3.\n# -----------------------------------------------------------------------------\nLOCAL_CFLAGS := $(test_c_flags)\n"
  },
  {
    "path": "docker/android/tests/ListMap.bp",
    "content": "// this contains a list of maps\nscope {\n    key: [\n        {\n            name: \"art\",\n            deps: [\"dependency\"],\n        },\n    ],\n}\n"
  },
  {
    "path": "docker/android/tests/Multiline.mk",
    "content": "# this is a special makefile checking support for multiline comments\n\nLOCAL_PATH := $(call my-dir)\n\nifneq ($(ENV1),)\n\n###########################################################\n# new rules\n# $(1): rule 1\n# $(2): rule 2\n###########################################################\n\ninclude $(call all-makefiles-under,$(LOCAL_PATH))\n\nendif\n"
  },
  {
    "path": "docker/android/tests/Nested.mk",
    "content": "# this is a special makefile checking we handle nested\n# conditionals properly, that removing sections won't\n# cause unequal conditional blocks. it may still lead\n# to missing definitions, but it won't fail due to\n# unmatched if and endif directives.\n\nLOCAL_PATH := $(call my-dir)\n\nifneq ($(ENV1),)\n\n# -----------------------------------------------------------------------------\n# Benchmarks.\n# -----------------------------------------------------------------------------\n\ntest_tags := tests\n\nbenchmark_c_flags := \\\n\t-Wall -Wextra \\\n\t-Werror \\\n\t-fno-builtin \\\n\nbenchmark_src_files := benchmark_main.cc\nifneq ($(ENV2),)\n\tbenchmark_src_files += bench1.cc\nelse\n\tbenchmark_src_files += bench2.cc\nendif\n\n# Build benchmarks.\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := benchmarks\nLOCAL_MODULE_TAGS := tests\nLOCAL_CFLAGS += $(benchmark_c_flags)\nLOCAL_SHARED_LIBRARIES += libm libdl\nLOCAL_SRC_FILES := $(benchmark_src_files)\n\nendif\n\n# Other section.\n# =========================================================\ninclude $(call all-makefiles-under,$(LOCAL_PATH))\n\n# =============================================================================\n# Unit tests.\n# =============================================================================\n\ntest_c_flags := \\\n\t-g \\\n\t-Wall \\\n\t-Werror\n\n##################################\n# test executable\nLOCAL_MODULE := mod2\nLOCAL_SRC_FILES := mod.c\nLOCAL_SHARED_LIBRARIES := libcutils\nLOCAL_CFLAGS := $(test_c_flags)\nLOCAL_MODULE_RELATIVE_PATH := mod2-tests\n"
  },
  {
    "path": "docker/android/tests/NonTest.bp",
    "content": "cc_defaults {\n    name: \"lib-non-test-defaults\",\n    cflags: [\"-Wall\"],\n    srcs: [\"src/libc.cc\"],\n    min_sdk_version: \"29\",\n}\n"
  },
  {
    "path": "docker/android/tests/README.md",
    "content": "android\n=======\n\nContains sample Soong blueprint files and Makefiles to test removal of unittests for build configurations.\n\nThis requires a Python3 interpreter, and therefore is not run as part of the core test suite. Running the test suite requires:\n- sly >= 0.4\n- google-re2 >= 1.0\n- pytest >= 7\n- toml >= 0.10\n\nThe module itself and the scripts only require:\n- python >= 3.6\n- sly >= 0.4\n- google-re2 >= 1.0\n\ngoogle-re2 is needed to avoid backtracking regexes, which destroy performance on near-misses for section headers. The below example, if provided with 10,000 characters after the header, will likely never complete. With re2, this completes nearly instantly.\n\n```Makefile\n########################################################################\n#\n....\n```\n"
  },
  {
    "path": "docker/android/tests/Single.mk",
    "content": "# this is a special makefile without any blocks\n\nLOCAL_PATH := $(call my-dir)\n\ntest_tags := tests\n\nbenchmark_c_flags := \\\n\t-Wall -Wextra \\\n\t-Werror \\\n\t-fno-builtin \\\n\nbenchmark_src_files := \\\n\tbenchmark_main.cc \\\n\tbench.cc\n\n# Build benchmarks.\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := benchmarks\nLOCAL_MODULE_TAGS := tests\nLOCAL_CFLAGS += $(benchmark_c_flags)\nLOCAL_SHARED_LIBRARIES += libm libdl\nLOCAL_SRC_FILES := $(benchmark_src_files)\n"
  },
  {
    "path": "docker/android/tests/test_make.py",
    "content": "import copy\nimport os\nimport sys\n\nTEST_DIR = os.path.dirname(os.path.realpath(__file__))\nPROJECT_DIR = os.path.dirname(TEST_DIR)\nsys.path.insert(0, PROJECT_DIR)\n\nfrom android import make\n\n\ndef test():\n    path = os.path.join(TEST_DIR, 'Android.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    stripped = contents[:-1]\n    assert repr(makefile) == f'Makefile({stripped})'\n    assert str(makefile) == stripped\n    assert len(makefile) == 9\n\n    assert not makefile[0].is_dev()\n    assert makefile[1].is_dev()\n    assert makefile[1].is_benchmark()\n    assert makefile[2].is_dev()\n    assert makefile[2].is_test()\n    assert makefile[6].title == 'Other section.'\n\n    filtered = copy.deepcopy(makefile)\n    filtered.filter(lambda x: not x.is_dev())\n    assert type(filtered) is make.Makefile\n    assert len(filtered) == 2\n    assert not filtered[0].is_comment()\n    assert filtered[1].title == 'Other section.'\n\n    assert makefile == make.load(open(path))\n    assert contents == makefile.dumps() + '\\n'\n\n\ndef test_nested():\n    path = os.path.join(TEST_DIR, 'Nested.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 6\n\n    assert makefile[0].is_block()\n    assert makefile[0].child.startswith('# this is a special makefile')\n\n    assert makefile[1].is_directive()\n    assert len(makefile[1].child) == 2\n    assert makefile[1].child[0].is_block()\n    assert makefile[1].child[1].is_comment()\n    assert makefile[1].child[1].title == 'Benchmarks.'\n\n    outer = makefile[1].child[1]\n    assert len(outer.child) == 3\n    assert outer.child[0].is_block()\n    assert outer.child[1].is_directive()\n    assert outer.child[2].is_block()\n\n    inner = outer.child[1]\n    assert inner.child.is_block()\n\n\ndef test_comments():\n    path = os.path.join(TEST_DIR, 'Comments.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 1\n\n    assert makefile[0].is_block()\n    assert makefile[0].child.startswith('# 1) sample grouping:')\n\n\ndef test_grouped():\n    path = os.path.join(TEST_DIR, 'Grouped.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 3\n\n    assert makefile[0].is_block()\n    assert makefile[0].child.startswith('LOCAL_PATH := $(call my-dir)')\n\n    comment = makefile[1]\n    assert comment.is_comment()\n    assert len(comment.child) == 3\n    assert comment.child[0].child.startswith('LOCAL_SRC_FILES := src.c')\n    assert comment.child[1].is_directive()\n    assert len(comment.child[2].child) == 0\n\n    directives = comment.child[1]\n    inner_comment = directives.child\n    assert inner_comment.is_comment()\n    assert len(inner_comment.child) == 2\n    assert inner_comment.child[0].is_directive()\n    assert inner_comment.child[1].child.startswith('else')\n\n    inner = inner_comment.child[0]\n    assert inner.child.lstrip().startswith('benchmark_src_files')\n\n    assert makefile[2].is_comment()\n\n\ndef test_recurse():\n    path = os.path.join(TEST_DIR, 'Nested.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    nodes = list(makefile.recurse())\n    assert len(nodes) == 11\n\n    assert nodes[0] == makefile[0]\n    assert nodes[1] == makefile[1]\n    assert nodes[2] == makefile[1].child[0]\n    assert nodes[3] == makefile[1].child[1]\n    assert nodes[4] == makefile[1].child[1].child[0]\n    assert nodes[5] == makefile[1].child[1].child[1]\n    assert nodes[6] == makefile[1].child[1].child[2]\n    assert nodes[7] == makefile[2]\n    assert nodes[8] == makefile[3]\n    assert nodes[9] == makefile[4]\n    assert nodes[10] == makefile[5]\n\n\ndef test_multiline():\n    path = os.path.join(TEST_DIR, 'Multiline.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 2\n\n    assert makefile[0].is_block()\n    assert makefile[0].child.startswith('# this is a special makefile')\n\n    assert makefile[1].is_directive()\n    comment = makefile[1].child[1]\n    assert comment.is_comment()\n    assert comment.title == 'new rules\\n$(1): rule 1\\n$(2): rule 2'\n    assert str(comment.child).startswith('\\ninclude')\n\n\ndef test_fake_title():\n    path = os.path.join(TEST_DIR, 'FakeTitle.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 1\n\n    comment = makefile[0]\n    assert comment.is_comment()\n    assert comment.title == ''\n    assert str(comment.child).startswith('LOCAL_PATH := $(call my-dir)')\n\n\ndef test_filter():\n    path = os.path.join(TEST_DIR, 'Nested.mk')\n    contents = open(path).read()\n    makefile = make.loads(contents)\n    assert str(makefile) + '\\n' == contents\n    assert len(makefile) == 6\n    assert makefile[1].is_directive()\n    assert len(makefile[1].child) == 2\n\n    filtered = copy.deepcopy(makefile)\n    filtered.filter(lambda x: not x.is_dev())\n    assert len(filtered) == 4\n    assert filtered[0].is_block()\n    assert filtered[1].is_directive()\n    assert filtered[2].is_block()\n    assert filtered[3].is_comment()\n\n    directive = filtered[1]\n    assert len(directive.child) == 1\n    assert directive.child[0].is_block()\n\n    assert filtered[3].title.lstrip().startswith('Other section.')\n\n\ndef test_split_directives():\n    path = os.path.join(TEST_DIR, 'Nested.mk')\n    contents = open(path).read()\n    iterable = iter(contents.splitlines())\n    blocks = make._split_directives(iterable)[0]\n    assert len(blocks) == 3\n\n    assert blocks[0].is_block()\n    assert blocks[0].startswith('# this is a special makefile')\n\n    assert blocks[2].is_block()\n    assert blocks[2].lstrip().startswith('# Other section.')\n\n    assert not blocks[1].is_comment()\n    assert blocks[1].is_directive()\n    assert blocks[1].has_block_list()\n\n    directives = blocks[1].child\n    assert len(directives) == 3\n    assert directives[0].is_block()\n    assert directives[1].is_directive()\n    assert directives[2].is_block()\n\n    assert not directives[1].child.has_block_list()\n    assert directives[1].child.lstrip().startswith('benchmark_src_files')\n\n    path = os.path.join(TEST_DIR, 'Grouped.mk')\n    contents = open(path).read()\n    iterable = iter(contents.splitlines())\n    blocks = make._split_directives(iterable)[0]\n    assert len(blocks) == 3\n\n    assert blocks[0].is_block()\n    assert blocks[1].is_directive()\n    assert blocks[2].is_block()\n\n    directives = blocks[1].child\n    assert len(directives) == 3\n    assert directives[0].is_block()\n    assert directives[1].is_directive()\n    assert directives[2].is_block()\n\n\ndef test_split_comments():\n    path = os.path.join(TEST_DIR, 'Android.mk')\n    contents = open(path).read()\n    blocks = make._split_comments(contents)\n    assert repr(blocks) == f'BlockList({contents})'\n    assert str(blocks) == contents\n    assert len(blocks) == 9\n\n    assert not blocks[0].is_dev()\n    assert blocks[1].is_dev()\n    assert blocks[1].is_benchmark()\n    assert blocks[1].title == 'Benchmarks.'\n    assert blocks[2].is_dev()\n    assert blocks[2].is_test()\n    assert blocks[2].title == 'Unit tests.'\n    assert blocks[3].is_test()\n    assert blocks[3].title == 'test executable'\n    assert blocks[4].is_test()\n    assert blocks[4].title == 'Unit tests.'\n    assert blocks[5].is_test()\n    assert blocks[5].title == 'test executable'\n    assert not blocks[6].is_dev()\n    assert blocks[6].title == 'Other section.'\n    assert blocks[7].is_test()\n    assert blocks[7].title == 'Unit tests.'\n    assert blocks[8].is_test()\n    assert blocks[8].title == 'test executable'\n\n    path = os.path.join(TEST_DIR, 'Empty.mk')\n    contents = open(path).read()\n    blocks = make._split_comments(contents)\n    assert len(blocks) == 1\n    assert repr(blocks) == 'BlockList(\\n)'\n    assert str(blocks) == '\\n'\n    assert str(blocks[0]) == '\\n'\n\n    blocks = make._split_comments('')\n    assert len(blocks) == 0\n    assert repr(blocks) == 'BlockList()'\n    assert str(blocks) == ''\n\n\ndef test_block():\n    data = '''LOCAL_PATH := $(call my-dir)\ninclude $(CLEAR_VARS)'''\n    block = make.Block(data)\n    assert repr(block) == f'Block({data})'\n    assert str(block) == data\n    assert block.is_block()\n    assert not block.is_block_list()\n    assert not block.is_comment()\n    assert not block.is_directive()\n    assert not block.is_dev()\n\n\ndef test_block_list():\n    data1 = 'LOCAL_PATH := $(call my-dir)'\n    data2 = 'test_tags := tests'\n    blocks = make.BlockList([make.Block(data1), make.Block(data2)])\n    assert repr(blocks) == f'BlockList({data1}\\n{data2})'\n    assert str(blocks) == f'{data1}\\n{data2}'\n    assert not blocks.is_block()\n    assert blocks.is_block_list()\n    assert not blocks.is_comment()\n    assert not blocks.is_directive()\n    assert not blocks.is_dev()\n\n\ndef test_comment_block():\n    # single block\n    comment = '''# -----------------------------------------------------------------------------\n# Benchmarks.\n# -----------------------------------------------------------------------------\n'''\n    title = 'Benchmarks.'\n    data = 'test_tags := tests'\n    block = make.CommentBlock(comment, title, make.Block(data))\n    assert repr(block) == f'CommentBlock({comment}\\n{data})'\n    assert str(block) == f'{comment}\\n{data}'\n    assert not block.is_block()\n    assert not block.is_block_list()\n    assert block.is_comment()\n    assert not block.is_directive()\n    assert block.is_dev()\n\n    title = 'Other Section.'\n    blocks = make.BlockList([\n        make.Block('LOCAL_PATH := $(call my-dir)'),\n        make.Block('test_tags := tests'),\n    ])\n    block = make.CommentBlock(comment, title, blocks)\n    assert repr(block) == f'CommentBlock({comment}\\n{str(blocks)})'\n    assert str(block) == f'{comment}\\n{str(blocks)}'\n    assert not block.is_block()\n    assert not block.is_block_list()\n    assert block.is_comment()\n    assert not block.is_directive()\n    assert not block.is_dev()\n\n\ndef test_directive_block():\n    start_inner = '    ifneq ($(USE_B),)'\n    end_inner = '    endif'\n    data_inner = '''        SOURCES=b.cc\n    else\n        SOURCES=a.cc'''\n    inner = make.DirectiveBlock(start_inner, end_inner, make.Block(data_inner))\n    str_inner = f'{start_inner}\\n{data_inner}\\n{end_inner}'\n    assert repr(inner) == f'DirectiveBlock({str_inner})'\n    assert str(inner) == str_inner\n    assert not inner.is_block()\n    assert not inner.is_block_list()\n    assert not inner.is_comment()\n    assert inner.is_directive()\n    assert not inner.is_dev()\n\n    data_else = '''else\n    SOURCES=c.cc'''\n    else_block = make.Block(data_else)\n    blocks = make.BlockList([inner, else_block])\n    str_blocks = '\\n'.join([str(i) for i in blocks])\n    assert repr(blocks) == f'BlockList({str_blocks})'\n    assert str(blocks) == str_blocks\n\n    start = 'ifneq ($(USE_A),)'\n    end = 'endif'\n    block = make.DirectiveBlock(start, end, blocks)\n    str_block = f'{start}\\n{str_blocks}\\n{end}'\n    assert repr(block) == f'DirectiveBlock({str_block})'\n    assert str(block) == str_block\n    assert not block.is_block()\n    assert not block.is_block_list()\n    assert not block.is_comment()\n    assert block.is_directive()\n    assert not block.is_dev()\n"
  },
  {
    "path": "docker/android/tests/test_metadata.py",
    "content": "import os\nimport sys\n\nimport toml\n\nTEST_DIR = os.path.dirname(os.path.realpath(__file__))\nPROJECT_DIR = os.path.dirname(TEST_DIR)\nsys.path.insert(0, PROJECT_DIR)\n\nimport android\n\n\n# ensure our pyproject and module metadata don't go out-of-date\ndef test_metadata():\n    pyproject_path = open(os.path.join(PROJECT_DIR, 'pyproject.toml'))\n    pyproject = toml.load(pyproject_path)\n    project = pyproject['project']\n    assert project['name'] == android.__name__\n    assert project['version'] == android.__version__\n    assert project['license']['text'] == android.__license__\n\n    version, dev = android.__version__.split('-')\n    major, minor, patch = [int(i) for i in version.split('.')]\n    assert (major, minor, patch, dev) == android.__version_info__\n"
  },
  {
    "path": "docker/android/tests/test_soong.py",
    "content": "import copy\nimport os\nimport sys\n\nTEST_DIR = os.path.dirname(os.path.realpath(__file__))\nPROJECT_DIR = os.path.dirname(TEST_DIR)\nsys.path.insert(0, PROJECT_DIR)\n\nfrom android import soong\n\n\ndef test():\n    path = os.path.join(TEST_DIR, 'Android.bp')\n    contents = open(path).read()\n    lexer = soong.Lexer()\n    tokens = list(lexer.tokenize(contents))\n    assert (tokens[0].type, tokens[0].value) == ('IDENT', 'sample_array')\n    assert (tokens[51].type, tokens[51].value) == ('IDENT', 'srcs')\n    assert (tokens[52].type, tokens[52].value) == ('COLON', ':')\n    assert (tokens[53].type, tokens[53].value) == ('LBRACKET', '[')\n    assert (tokens[54].type, tokens[54].value) == ('STRING', '\"tree.cc\"')\n\n    parser = soong.Parser()\n    result = parser.parse(iter(tokens))\n    assert len(result) == 7\n\n    assert result[0].is_assignment()\n    assert result[0].to_str() == '''sample_array = [\n    \"value1\",\n    \"value2\",\n]'''\n\n    assert result[1].is_scope()\n    assert result[1].name == 'cc_defaults'\n    assert result[1].name.is_ident()\n    assert result[1].map['name'] == 'target'\n    assert result[1].map['tidy_checks'] == 'sample_array'\n    assert result[1].map.get('srcs') is None\n    assert result[1].map.is_map()\n\n    assert result[2].is_scope()\n    assert result[2].name == 'cc_library_static'\n    assert result[2].map['name'] == 'static_lib'\n\n    ast = soong.loads(contents)\n    assert ast == result\n    ast = soong.load(open(path))\n    assert ast == result\n    lines = contents.splitlines()\n    assert ast.dumps() == '\\n'.join(lines[1:5] + lines[10:])\n\n    assert ast[4].is_test()\n    assert ast[4].map.is_test()\n\n    filtered = copy.deepcopy(ast)\n    filtered.filter(lambda x: not (x.is_scope() and x.is_dev()))\n    assert type(filtered) is soong.Ast\n    assert len(filtered) == 5\n    assert filtered == ast[:4] + [ast[6]]\n\n    map = filtered[1].map\n    assert 'cflags' in map\n    map.filter(lambda k, v: k != 'cflags')\n    assert 'cflags' not in map\n    assert len(map['array']) == 2\n    map['array'].filter(lambda x: x != '-short')\n    assert len(map['array']) == 1\n\n    custom = filtered[4].map\n    assert 'whole_static_libs' in custom\n    custom['whole_static_libs'].filter(lambda x: x.str_op(lambda y: 'gtest' not in y.lower()))\n    assert custom['whole_static_libs'] == ['libz']\n\n    assert 'host_ldlibs' in custom\n    custom['host_ldlibs'].filter(lambda x: x.str_op(lambda y: 'gtest' not in y.lower()))\n    assert custom['host_ldlibs'] == []\n\n\ndef test_addition():\n    path = os.path.join(TEST_DIR, 'Addition.bp')\n    ast = soong.load(open(path))\n    assert len(ast) == 27\n    assert ast[0].is_assignment()\n    assert ast[1].is_binary_operator_assignment()\n    assert ast[2].is_assignment()\n    assert ast[3].is_binary_operator_assignment()\n    assert ast[4].is_assignment()\n    assert ast[5].is_binary_operator_assignment()\n    assert ast[6].is_scope()\n    assert ast[7].is_binary_operator_assignment()\n    assert ast[8].expr.is_binary_operator()\n\n    assert ast[0].name == 'list'\n    assert ast[0].expr == ['value1']\n    assert ast[1].name == 'list'\n    assert ast[1].op == '+='\n    assert ast[1].expr == ['value2']\n\n    assert ast[8].expr.lhs == 'number'\n    assert ast[8].expr.op == '+'\n    assert ast[8].expr.rhs == 4\n    assert ast[11].expr.lhs == 'scope'\n    assert ast[11].expr.op == '+'\n    assert ast[11].expr.rhs.is_map()\n\n    assert ast[12].expr.lhs == 4\n    assert ast[12].expr.op == '+'\n    assert ast[12].expr.rhs == 'number'\n    assert ast[15].expr.lhs.is_map()\n    assert ast[15].expr.op == '+'\n    assert ast[15].expr.rhs == 'scope'\n\n    assert ast[16].expr.lhs == 4\n    assert ast[16].expr.op == '+'\n    assert ast[16].expr.rhs == 1\n    assert ast[19].expr.lhs == {}\n    assert ast[19].expr.op == '+'\n    assert ast[19].expr.rhs == {'name': 'target'}\n\n    assert ast[20].expr.lhs.is_binary_operator()\n    assert ast[20].expr.lhs.lhs == 4\n    assert ast[20].expr.lhs.rhs == 1\n    assert ast[20].expr.op == '+'\n    assert ast[20].expr.rhs == 2\n\n    assert ast[26].name == 'files'\n    assert ast[26].expr.is_list()\n    assert len(ast[26].expr) == 3\n\n    assert ast[26].expr[0].lhs == 'home'\n    assert ast[26].expr[0].lhs.is_ident()\n    assert ast[26].expr[0].rhs == 'file.c'\n    assert ast[26].expr[0].rhs.is_string()\n\n    assert ast[26].expr[1].lhs == 'test/'\n    assert ast[26].expr[1].lhs.is_string()\n    assert ast[26].expr[1].rhs == 'test'\n    assert ast[26].expr[1].rhs.is_ident()\n\n    assert ast[26].expr[2].lhs == 'home'\n    assert ast[26].expr[2].lhs.is_ident()\n    assert ast[26].expr[2].rhs == 'test'\n    assert ast[26].expr[2].rhs.is_ident()\n\n    # test a few binops, just in case\n    binop = ast[26].expr[1]\n    assert binop.str_op(lambda x: 'test' in x.lower())\n    assert binop.lhs.str_op(lambda x: 'test' in x.lower())\n\n\ndef test_empty():\n    path = os.path.join(TEST_DIR, 'Empty.bp')\n    ast = soong.load(open(path))\n    assert len(ast) == 0\n\n\ndef test_list_map_parse():\n    path = os.path.join(TEST_DIR, 'ListMap.bp')\n    ast = soong.load(open(path))\n    assert len(ast) == 1\n\n    scope = ast[0]\n    assert scope.is_scope()\n    assert scope.name == 'scope'\n    map = scope.map['key']\n\n    assert map.value.is_list()\n    assert len(map.value) == 1\n    assert map.value[0].is_map()\n\n    inner = map.value[0]\n    assert len(inner) == 2\n    assert inner['name'] == 'art'\n    assert inner['deps'].value == soong.List([soong.String('\"dependency\"')])\n\n\ndef test_is_non_test():\n    path = os.path.join(TEST_DIR, 'NonTest.bp')\n    ast = soong.load(open(path))\n    assert len(ast) == 1\n\n    scope = ast[0]\n    assert scope.is_scope()\n    assert scope.name == 'cc_defaults'\n    assert scope.map['name'].value == 'lib-non-test-defaults'\n\n\ndef test_ast():\n    array = soong.List([soong.String('\"value1\"'), soong.String('\"value2\"')])\n    assignment = soong.Assignment(soong.Ident('name'), array)\n    value = soong.MapValue('=', soong.String('\"value\"'))\n    map = soong.Map({soong.Ident('key'): value})\n    scope = soong.Scope(soong.Ident('name'), map)\n    ast = soong.Ast([assignment, scope])\n    assert repr(ast) == '''Ast(name = [\"value1\", \"value2\"]\nname {key = \"value\"})'''\n    assert str(ast) == '''name = [\"value1\", \"value2\"]\nname {key = \"value\"}'''\n    assert ast.to_str() == '''name = [\n    \"value1\",\n    \"value2\",\n]\nname {\n    key = \"value\",\n}'''\n\n\ndef test_assignment():\n    array = soong.List([soong.String('\"value1\"'), soong.String('\"value2\"')])\n    assignment = soong.Assignment(soong.Ident('name'), array)\n    assert repr(assignment) == 'Assignment(name = [\"value1\", \"value2\"])'\n    assert str(assignment) == 'name = [\"value1\", \"value2\"]'\n    assert assignment.to_str(pretty=False) == 'name = [\"value1\", \"value2\"]'\n    assert assignment.to_str() == '''name = [\n    \"value1\",\n    \"value2\",\n]'''\n    assert assignment.to_str(depth=1) == '''name = [\n        \"value1\",\n        \"value2\",\n    ]'''\n\n\ndef test_binary_operator_assignment():\n    ident = soong.Ident('name')\n    expr = soong.Integer('1')\n    assignment = soong.BinaryOperatorAssignment(ident, '+=', expr)\n    assert repr(assignment) == 'BinaryOperatorAssignment(name += 1)'\n    assert str(assignment) == 'name += 1'\n    assert assignment.to_str(pretty=False) == 'name += 1'\n    assert assignment.to_str() == 'name += 1'\n\n\ndef test_binary_operator():\n    ident = soong.Ident('name')\n    expr = soong.Integer('1')\n    operator = soong.BinaryOperator(ident, '+', expr)\n    assert repr(operator) == 'BinaryOperator(name + 1)'\n    assert str(operator) == 'name + 1'\n    assert operator.to_str(pretty=False) == 'name + 1'\n    assert operator.to_str() == 'name + 1'\n\n\ndef test_scope():\n    value = soong.MapValue(':', soong.String('\"value\"'))\n    map = soong.Map({soong.Ident('key'): value})\n    scope = soong.Scope(soong.Ident('name'), map)\n    assert repr(scope) == 'Scope(name {key: \"value\"})'\n    assert str(scope) == 'name {key: \"value\"}'\n    assert scope.to_str(pretty=False) == 'name {key: \"value\"}'\n    assert scope.to_str() == '''name {\n    key: \"value\",\n}'''\n    assert scope.to_str(depth=1) == '''name {\n        key: \"value\",\n    }'''\n\n\ndef test_map():\n    value = soong.MapValue(':', soong.String('\"value\"'))\n    map = soong.Map({soong.Ident('key'): value})\n    assert repr(map) == 'Map({key: \"value\"})'\n    assert str(map) == '{key: \"value\"}'\n    assert map.to_str(pretty=False) == '{key: \"value\"}'\n    assert map.to_str() == '''{\n    key: \"value\",\n}'''\n    assert map.to_str(depth=1) == '''{\n        key: \"value\",\n    }'''\n\n    map = soong.Map()\n    assert str(map) == '{}'\n    assert map.to_str() == '{}'\n\n\ndef test_recurse():\n    path = os.path.join(TEST_DIR, 'Android.bp')\n    ast = soong.load(open(path))\n    cc_defaults = ast[1]\n    assert cc_defaults.name == 'cc_defaults'\n    for (key, value, depth, parent) in cc_defaults.map.recurse():\n        assert depth == 1\n\n    cc_test = ast[4]\n    assert cc_test.name == 'cc_test'\n    seen = []\n    for (key, value, depth, parent) in cc_test.map.recurse():\n        if depth > 1 and parent.is_map():\n            seen.append(key)\n    assert seen == ['array', 'option']\n\n\ndef test_list():\n    sequence = soong.List([soong.String('\"value1\"'), soong.String('\"value2\"')])\n    assert repr(sequence) == 'List([\"value1\", \"value2\"])'\n    assert str(sequence) == '[\"value1\", \"value2\"]'\n    assert sequence.to_str(pretty=False) == '[\"value1\", \"value2\"]'\n    assert sequence.to_str() == '''[\n    \"value1\",\n    \"value2\",\n]'''\n    assert sequence.to_str(depth=1) == '''[\n        \"value1\",\n        \"value2\",\n    ]'''\n\n    sequence = soong.List([soong.String('\"value\"')])\n    assert repr(sequence) == 'List([\"value\"])'\n    assert str(sequence) == '[\"value\"]'\n    assert sequence.to_str() == '[\"value\"]'\n\n    sequence = soong.List([])\n    assert sequence.to_str() == '[]'\n\n\ndef test_map_value():\n    value = soong.MapValue(':', soong.String('\"value\"'))\n    assert repr(value) == 'MapValue(: \"value\")'\n    assert str(value) == ': \"value\"'\n    assert value.to_str() == ': \"value\"'\n\n    value = soong.MapValue('=', soong.String('\"value\"'))\n    assert repr(value) == 'MapValue( = \"value\")'\n    assert str(value) == ' = \"value\"'\n    assert value.to_str() == ' = \"value\"'\n\n\ndef test_list_map():\n    value = soong.MapValue(':', soong.String('\"value\"'))\n    map = soong.Map({soong.Ident('key'): value})\n    sequence = soong.List([map])\n    assert repr(sequence) == 'List([{key: \"value\"}])'\n    assert str(sequence) == '[{key: \"value\"}]'\n    assert sequence.to_str(pretty=False) == '[{key: \"value\"}]'\n    assert sequence.to_str() == '''[{\n    key: \"value\",\n}]'''\n\n\ndef test_ident():\n    ident = soong.Ident('name')\n    assert repr(ident) == 'Ident(name)'\n    assert str(ident) == 'name'\n    assert ident.to_str() == 'name'\n\n\ndef test_string():\n    string = soong.String('\"value1\"')\n    assert repr(string) == 'String(\"value1\")'\n    assert str(string) == 'value1'\n    assert string.to_str() == '\"value1\"'\n\n\ndef test_integer():\n    number = soong.Integer('3')\n    assert repr(number) == 'Integer(3)'\n    assert str(number) == '3'\n    assert number.to_str() == '3'\n\n\ndef test_bool():\n    boolean = soong.Bool(True)\n    assert repr(boolean) == 'Bool(true)'\n    assert str(boolean) == 'true'\n    assert boolean.to_str() == 'true'\n"
  },
  {
    "path": "docker/android/tests/test_util.py",
    "content": "import os\nimport sys\n\nTEST_DIR = os.path.dirname(os.path.realpath(__file__))\nPROJECT_DIR = os.path.dirname(TEST_DIR)\nsys.path.insert(0, PROJECT_DIR)\n\nfrom android import util\n\n\ndef test_is_test():\n    assert not util.is_test('lib-non-test-defaults')\n    assert util.is_test('art-tests')\n    assert util.is_test('libgtest')\n    assert util.is_test('libgtest_main')\n    assert util.is_test('extra-tests')\n\n\ndef test_is_benchmark():\n    assert util.is_benchmark('benchmark')\n    assert util.is_benchmark('benchmarks')\n    assert util.is_benchmark('-benchmarks')\n    assert not util.is_benchmark('gbenchmarks')\n"
  },
  {
    "path": "docker/android/tox.ini",
    "content": "[tox]\nenvlist = py36,py3\nskip_missing_interpreters = True\nisolated_build = True\n\n[testenv]\ndeps =\n    sly >= 0.4\n    google-re2 >= 1.0\n    pytest\n    toml\ncommands = pytest -o cache_dir={toxworkdir}/.pytest_cache\npassenv =\n    PYTHONDONTWRITEBYTECODE\n    PYTHONPYCACHEPREFIX\n    PYTHON_EGG_CACHE\n\n[flake8]\nmax-line-length = 100\nignore =\n    # we use lambdas for short, one-line conditions and formatters\n    E731\n    # opt-in to new behavior with operators after line breaks\n    W503\nper-file-ignores =\n    # the sly grammar uses variables before they are defined via a metaclass\n    # likewise, it uses redefinitions to extend parsers via SLR grammar\n    android/soong.py: F811 F821\n    # need to add the project to the path for our tests and scripts\n    tests/*.py: E402\n    scripts/*.py: E402\n"
  },
  {
    "path": "docker/android-ndk.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local arch=\"${1}\"\n\n    # python3 is still needed for newer NDK versions, just since it\n    # simplifies making symlinks even though the toolchain is prebuilt\n    install_packages curl python python3\n    get_ndk_info\n    if [[ \"${NDK_VERSION}\" -le 9 ]]; then\n        install_packages bzip2\n    else\n        install_packages unzip\n    fi\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n    curl --retry 3 -sSfL \"${NDK_URL}\" -O\n    if [[ \"${NDK_VERSION}\" -le 9 ]]; then\n        tar -xjf \"${NDK_FILENAME}\"\n    else\n        unzip -q \"${NDK_FILENAME}\"\n    fi\n    rm \"${NDK_FILENAME}\"\n    pushd \"android-ndk-${ANDROID_NDK}\"\n    # android NDK versions <= 13 error without the verbose flag\n    local build_cmd=\n    local api=\n    if [[ \"${NDK_VERSION}\" -le 11 ]]; then\n        build_cmd=make-standalone-toolchain.sh\n        api=--platform=\"android-${ANDROID_SDK}\"\n    else\n        build_cmd=make_standalone_toolchain.py\n        api=--api=\"${ANDROID_SDK}\"\n    fi\n    \"./build/tools/${build_cmd}\" \\\n        --install-dir=/android-ndk \\\n        --arch=\"${arch}\" \\\n        \"${api}\" \\\n        --verbose\n\n    # the android bash script installs the executables with 750, not 755\n    # permissions, and the other files without read permissions.\n    if [[ \"${NDK_VERSION}\" -le 11 ]]; then\n        chmod -R 755 /android-ndk/bin\n        chmod -R 755 /android-ndk/libexec\n        chmod -R +r /android-ndk\n    fi\n\n    # clean up unused toolchains to reduce image size\n    local triple\n    local triples\n    local triple_arch=\"${arch}\"\n    case \"${arch}\" in\n        arm64)\n            triple_arch=\"aarch64\"\n            ;;\n        x86)\n            triple_arch=\"i686\"\n            ;;\n    esac\n    triples=(\n        \"aarch64-linux-android\"\n        \"arm-linux-androideabi\"\n        \"i686-linux-android\"\n        \"x86_64-linux-android\"\n    )\n    for triple in \"${triples[@]}\"; do\n        if ! [[ \"${triple}\" =~ ^\"${triple_arch}\".* ]]; then\n            rm -rf \"/android-ndk/sysroot/usr/lib/${triple}\"\n        fi\n    done\n\n    purge_packages\n\n    popd\n    popd\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nget_ndk_info() {\n    local ndk_os=linux\n    local ndk_platform=\"${ndk_os}-x86_64\"\n    # format is generally r21d, r25b, etc. it can however, be r24, for example.\n    NDK_VERSION=$(echo \"${ANDROID_NDK}\" | tr -dc '0-9')\n    # android NDK 23 and higher moved from `linux-x86_64` to `linux`\n    if [[ \"${NDK_VERSION}\" -ge 23 ]]; then\n        NDK_FILENAME=\"android-ndk-${ANDROID_NDK}-${ndk_os}.zip\"\n    elif [[ \"${NDK_VERSION}\" -le 9 ]]; then\n        NDK_FILENAME=\"android-ndk-${ANDROID_NDK}-${ndk_platform}.tar.bz2\"\n    else\n        NDK_FILENAME=\"android-ndk-${ANDROID_NDK}-${ndk_platform}.zip\"\n    fi\n    if [[ \"${NDK_VERSION}\" -le 9 ]]; then\n        NDK_URL=\"https://dl.google.com/android/ndk/${NDK_FILENAME}\"\n    else\n        NDK_URL=\"https://dl.google.com/android/repository/${NDK_FILENAME}\"\n    fi\n    export NDK_VERSION\n    export NDK_FILENAME\n    export NDK_URL\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/android-runner",
    "content": "#!/usr/bin/env bash\n\nset -e\n\n# arch in the rust target\narch=\"${1}\"\nshift\n\nif [ -n \"${CROSS_DEBUG}\" ]; then\n    set -x\nfi\n\nif [ \"${CROSS_RUNNER}\" = \"\" ]; then\n    CROSS_RUNNER=qemu-user\nfi\n\n# select android abi, and find the shared libc++ library\nandroid_abi=\"${arch}-linux-android\"\nqarch=\"${arch}\"\ncase \"${arch}\" in\n    arm)\n        android_abi=\"arm-linux-androideabi\"\n        ;;\n    i686)\n        qarch=\"i386\"\n        qemu_args=(\"-cpu\" \"n270\")\n        ;;\n    x86_64)\n        qemu_args=(\"-cpu\" \"qemu64,+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt\")\n        ;;\nesac\nlibdir=\"/android-ndk/sysroot/usr/lib/${android_abi}\"\n\n# Android 5.x doesn't support C++.\nif [[ -f \"${libdir}/libc++_shared.so\" ]]; then\n    export LD_PRELOAD=\"${libdir}/libc++_shared.so\"\nfi\ncase \"${CROSS_RUNNER}\" in\n    native)\n        exec \"${@}\"\n        ;;\n    qemu-user)\n        exec \"qemu-${qarch}\" \"${qemu_args[@]}\" \"${@}\"\n        ;;\n    *)\n        echo \"Invalid runner: \\\"${CROSS_RUNNER}\\\"\";\n        echo \"Valid runners are: native and qemu-user\"\n        exit 1\n        ;;\nesac\n"
  },
  {
    "path": "docker/android-symlink.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2125,SC2207\n\nset -x\nset -euo pipefail\n\nmain() {\n    local arch=\"${1}\"\n    local target=\"${2}\"\n    local libdir=\"/android-ndk/lib64/clang/\"*\"/lib/linux/${arch}/\"\n    local expanded=($(echo \"/android-ndk/lib64/clang/\"*\"/lib/linux/${arch}/\"))\n\n    if [[ \"${#expanded[@]}\" == \"1\" ]] && [[ \"${expanded[0]}\" != \"${libdir}\" ]]; then\n        libdir=$(realpath \"/android-ndk/lib64/clang/\"*\"/lib/linux/${arch}/\")\n\n        # In Android NDK versions r23-beta3, libgcc has been replaced by libunwind\n        # Older Rust versions always link to libgcc, so we need a symlink.\n        # https://github.com/rust-lang/rust/pull/85806\n        if [[ -f \"${libdir}/libunwind.a\" ]]; then\n            ln -s \"${libdir}/libunwind.a\" \"${libdir}/libgcc.a\"\n        fi\n    fi\n\n    # older SDK versions install the libraries directly in the lib directory.\n    local sysroot=/android-ndk/sysroot\n    local ndk_libdir=\"${sysroot}/usr/lib/${target}/\"\n    if [[ \"${ANDROID_SYSTEM_NONE}\" != \"1\" ]]; then\n        if [[ -d \"${ndk_libdir}/\" ]]; then\n            cp \"${ndk_libdir}/${ANDROID_SDK}/libz.so\" /system/lib/\n        else\n            cp \"${sysroot}/usr/lib/libz.so\" /system/lib/\n        fi\n    fi\n\n    # later NDK versions switch to using `llvm-${tool}` rather than `${target}-tool`\n    # want to ensure we just have backwards-compatible aliases\n    local tool=\n    local tool_src=\n    local tool_dst=\n    for tool in ar as nm objcopy objdump ranlib readelf size string strip; do\n        tool_src=\"/android-ndk/bin/llvm-${tool}\"\n        tool_dst=\"/android-ndk/bin/${target}-${tool}\"\n        if [[ ! -f \"${tool_dst}\" ]] && [[ -f \"${tool_src}\" ]]; then\n            ln -s \"${tool_src}\" \"${tool_dst}\"\n        elif [[ \"${tool}\" == \"ld\" ]] && [[ ! -f \"${tool_dst}\" ]]; then\n            ln -s \"/android-ndk/bin/${tool}\" \"${tool_dst}\"\n        fi\n    done\n\n    # this is required for CMake builds, since the first pass doesn't\n    # add on the SDK API level to the linker search path. for example,\n    # it will set the linker search path to `${sysroot}/usr/lib/${target}/`,\n    # but not to `${sysroot}/usr/lib/${target}/${ANDROID_SDK}`. this isn't\n    # fixable seemingly with **any** environment variable or CMake option:\n    # cmake with `CMAKE_ANDROID_STANDALONE_TOOLCHAIN` seemingly ignores:\n    #   - `LD_LIBRARY_PATH`\n    #   - `CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES`\n    #   - `CMAKE_C_COMPILER`\n    #   - `CMAKE_CXX_COMPILER`\n    #\n    # running the cmake configuration a second time works, but this isn't\n    # adequate. the resulting config sets `CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES`\n    # but this is ignored in our toolchain file. likewise, not testing the\n    # compiler via `set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)` fails\n    # because during the build it will not add the API level to the linker\n    # search path.\n    local lib=\n    local libname=\n    if [[ -d \"${ndk_libdir}\" ]] && [[ -d \"${ndk_libdir}/${ANDROID_SDK}\" ]]; then\n        for lib in \"${ndk_libdir}/${ANDROID_SDK}\"/*; do\n            libname=$(basename \"${lib}\")\n            if [[ ! -f \"${ndk_libdir}/${libname}\" ]]; then\n                ln -s \"${lib}\" \"${ndk_libdir}/${libname}\"\n            fi\n        done\n    fi\n\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/android-system.sh",
    "content": "#!/usr/bin/env bash\n# The API level details are mentioned here:\n#   https://developer.android.com/studio/releases/platforms\n# These are controlled by `ANDROID_VERSION` and `ANDROID_SDK`,\n# for example, `ANDROID_SDK=30` and `ANDROID_VERSION=11.0.0_r48`.\n#\n# You can also build the entire Android source tree with\n# `ANDROID_SYSTEM_COMPLETE`, or skip it altogether with\n# `ANDROID_SYSTEM_NONE`. Note that runners will not be\n# available if the the Android system is not built.\n#\n# The versions are:\n#   5.0: 21 (tested at NDK 10e and r13b, 5.0.0_r1)\n#   5.1: 22 (tested at NDK r21d, 5.1.1_r38, unused DT)\n#   6.0: 23 (tested at NDK r21dm 6.0.1_r81)\n#   7.0: 24 (tested at NDK r21d, 7.0.0_r36)\n#   7.1: 25 (tested at NDK r21d, 7.1.2_r39, not supported)\n#   8.0: 26 (tested at NDK r21d, 8.0.0_r51)\n#   8.1: 27 (tested at NDK r21d, 8.1.0_r81)\n#   9.0: 28 (tested at NDK r21d and r25b, 9.0.0_r1)\n#   10.0: 29 (tested at NDK r25b, 10.0.0_r47)\n#   11.0: 30 (tested at NDK r25b, 11.0.0_r48)\n#   12.0: 31 (unable to build at 12.0.0_r34)\n#   12.1: 32 (unable to build at 12.1.0_r27)\n#   13.0: 33\n#\n#   API level 25 seems to be missing from Android NDK versions,\n#   and therefore is not supported.\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    export ARCH=\"${1}\"\n    MAJOR_VERSION=$(echo \"${ANDROID_VERSION}\" | cut -d '.' -f 1)\n    MINOR_VERSION=$(echo \"${ANDROID_VERSION}\" | cut -d '.' -f 2)\n    TAG=\"android-${ANDROID_VERSION}\"\n\n    export MAJOR_VERSION\n    export MINOR_VERSION\n    export TAG\n\n    if [[ \"${ANDROID_SYSTEM_NONE}\" == \"1\" ]]; then\n        rm -rf \"${PYTHON_TMPDIR}\"\n        rm \"${0}\"\n        return\n    fi\n\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" != \"1\" ]] && [[ \"${MAJOR_VERSION}\" -ge 12 ]]; then\n        echo \"Android versions 12 and higher couple APEX tightly into the build system.\" 1>&2\n        echo \"These are currently unsupported, and are unlikely to ever be supported.\" 1>&2\n        echo \"Try using a complete Android system build or disable building Android system.\" 1>&2\n        echo \"Note that a complete Android system build is slow and creates massive images.\" 1>&2\n        echo \"Disabling the Android system build will prevent the use of Android runners.\" 1>&2\n        echo \"If you want support for newer versions, contributions are always appreciated.\" 1>&2\n        exit 1\n    elif [[ \"${MAJOR_VERSION}\" -eq 7 ]] && [[ \"${MINOR_VERSION}\" -eq 1 ]]; then\n        echo \"Android version 7.1 is not supported.\" 1>&2\n        exit 1\n    fi\n\n    local td\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    fake_java\n\n    install_packages ca-certificates \\\n        curl \\\n        gcc-multilib \\\n        git \\\n        g++-multilib \\\n        libncurses5 \\\n        libtinfo5 \\\n        make \\\n        openssh-client \\\n        python \\\n        python3 \\\n        xz-utils\n\n    curl --retry 3 -sSfL https://storage.googleapis.com/git-repo-downloads/repo -O\n    chmod +x repo\n    python3 ./repo init -u https://android.googlesource.com/platform/manifest -b \"${TAG}\"\n\n    local tools=(\n        cat chmod chown cmp cp ctrlaltdel date df dmesg du hd id ifconfig\n        iftop insmod ioctl ionice kill ln log ls lsmod lsof lsusb md5 mkdir\n        mount mv nandread netstat notify printenv ps reboot renice rm rmdir\n        rmmod route schedtop sendevent setconsole setprop sleep smd start\n        stop sync top touch umount uptime vmstat watchprops wipe\n    )\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" == \"1\" ]]; then\n        android_repo_complete\n    else\n        case \"${MAJOR_VERSION}\" in\n            5)\n                android_repo_v5\n                tools+=(dd getevent getprop grep newfs_msdos)\n                ;;\n            6)\n                android_repo_v6\n                ;;\n            7)\n                android_repo_v7\n                ;;\n            8)\n                android_repo_v8\n                ;;\n            9)\n                android_repo_v9\n                ;;\n            10)\n                android_repo_v10\n                ;;\n            11)\n                android_repo_v11\n                ;;\n            *)\n                echo \"Currently unsupported Android version ${MAJOR_VERSION}.\" 1>&2\n                echo \"Please submit a feature request if you need support.\" 1>&2\n                exit 1\n                ;;\n        esac\n    fi\n\n    build_android\n    install_android \"${tools[@]}\"\n\n    remove_java\n    purge_packages\n\n    popd\n\n    rm -rf \"${td}\"\n    rm -rf \"${PYTHON_TMPDIR}\"\n    rm \"${0}\"\n}\n\n# java isn't required for the build, but the build expects to\n# find a java compiler. the supported android versions are:\n# https://source.android.com/docs/setup/start/older-versions\n#   Android 7: OpenJDK-8\nfake_java() {\n    local java_type=\n    local java_version=\n    local jre_info=\n    local build_info=\n\n    case \"${MAJOR_VERSION}\" in\n        5|6)\n            java_type=java\n            java_version=1.7.0\n            jre_info=\"IcedTea 2.6.9\"\n            build_info=\"build 24.131-b00, mixed mode\"\n            ;;\n        *)\n            java_type=openjdk\n            java_version=1.8.0_342\n            jre_info=\"build 1.8.0_342-8u342-b07-0ubuntu1~20.04-b07\"\n            build_info=\"build 25.342-b07, mixed mode\"\n            ;;\n    esac\n\n    cat << EOF > /usr/bin/java\n#!/usr/bin/env bash\necho \"${java_type} version \\\"${java_version}\\\"\"\necho \"OpenJDK Runtime Environment (${jre_info})\"\necho \"OpenJDK 64-Bit Server VM (${build_info})\"\nEOF\n\n    cat << EOF > /usr/bin/javac\n#!/usr/bin/env bash\necho \"javac ${java_version}\"\nEOF\n\n    chmod +x /usr/bin/java\n    chmod +x /usr/bin/javac\n\n    # more faking\n    export ANDROID_JAVA_HOME=/tmp\n    mkdir -p /tmp/lib/\n    touch /tmp/lib/tools.jar\n}\n\nremove_java() {\n    rm /usr/bin/java\n    rm /usr/bin/javac\n    rm /tmp/lib/tools.jar\n}\n\nbuild_android() {\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" != \"1\" ]]; then\n        export ALLOW_MISSING_DEPENDENCIES=true\n    fi\n\n    set +u\n    # shellcheck disable=SC1091\n    source build/envsetup.sh\n    lunch \"aosp_${ARCH}-user\"\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" != \"1\" ]]; then\n        mmma bionic/\n        mmma external/mksh/\n        mmma system/core/toolbox/\n    else\n        mma\n    fi\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" != \"1\" ]] && [[ \"${MAJOR_VERSION}\" -ge 11 ]]; then\n        # for some reason, building bionic doesn't build linker64 on the first pass\n        # doing a partial build and a rebuild is just as fast though.\n        rm -rf out/target/product/generic\n        mmma bionic/\n    fi\n    set -u\n}\n\ninstall_android() {\n    local outdir=\n    if [[ \"${ARCH}\" = \"arm\" ]]; then\n        outdir=out/target/product/generic\n    else\n        outdir=\"out/target/product/generic_${ARCH}\"\n    fi\n    mv \"${outdir}/system/\" /\n    if [[ \"${ANDROID_SYSTEM_COMPLETE}\" == \"1\" ]] && [[ -d \"${outdir}/apex\" ]]; then\n        # can use the APEX linker, no need to use the bootstrap one\n        mv \"${outdir}/apex/\" /\n    elif [[ \"${MAJOR_VERSION}\" -ge 10 ]]; then\n        symlink_bootstrap\n    fi\n\n    # list from https://elinux.org/Android_toolbox\n    local tool=\n    for tool in \"${@}\"; do\n        if [[ ! -f \"/system/bin/${tool}\" ]]; then\n            ln -s /system/bin/toolbox \"/system/bin/${tool}\"\n        fi\n    done\n\n    echo \"127.0.0.1 localhost\" > /system/etc/hosts\n}\n\nsymlink_bootstrap() {\n    # for Android 10+, we need to use the bootstrap linker rather than\n    # the APEX linker, which is gigantic. we also symlink the ASAN\n    # linker just in case using the bootstrapped one.\n    local linker\n    local file\n\n    if compgen -G /system/bin/bootstrap/* >/dev/null 2>&1; then\n        for linker in /system/bin/bootstrap/*; do\n            file=$(basename \"${linker}\")\n            unlink \"/system/bin/${file}\"\n            ln -s \"/system/bin/bootstrap/${file}\" \"/system/bin/${file}\"\n        done\n    fi\n\n    # also need to ensure the shared libraries aren't symlinks\n    local lib\n    local libdir\n    for libdir in /system/lib{,64}; do\n        if compgen -G \"${libdir}/bootstrap/\"* >/dev/null 2>&1; then\n            for lib in \"${libdir}/bootstrap/\"*; do\n                file=$(basename \"${lib}\")\n                unlink \"${libdir}/${file}\"\n                ln -s \"${libdir}/bootstrap/${file}\" \"${libdir}/${file}\"\n            done\n        fi\n    done\n}\n\n# this are the minimum set of modules that are need to build bionic\n# this was created by trial and error. this is based on the minimum\n# set of modules required for each android version, starting with\n# a minimal number of dependencies. for android 10+ versions, we use\n# the bootstrap linker rather than the APEX linker for non-complete\n# system builds, as the APEX linker drags in nearly the entire Android\n# runtime, requiring 60+GB images. for minimal builds, we need to avoid\n# APEX altogether, and this gets trickier starting in Android 10\n# and much more difficult in newer versions.\n\nandroid_repo_complete() {\n    python3 ./repo sync -c\n}\n\n# tested on 5.0.0_r1 (SDK 21)\n# tested on 5.1.1_r38 (SDK 22)\nandroid_repo_v5() {\n    sync bionic\n    sync build\n    sync external/compiler-rt\n    sync external/jemalloc\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libselinux\n    sync external/mksh\n    sync external/openssl\n    sync external/pcre\n    sync external/stlport\n    sync prebuilts/clang/linux-x86/host/3.5\n    sync system/core\n\n    case \"${ARCH}\" in\n        arm)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8\n        ;;\n        arm64)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/linker/tests/Android.mk\n    rm bionic/tests/Android.mk\n    rm bionic/benchmarks/Android.mk\n\n    # patch the linker to avoid the error\n    # FATAL: kernel did not supply AT_SECURE\n    sed -i -e 's/if (!kernel_supplied_AT_SECURE)/if (false)/g' bionic/linker/linker_environ.cpp\n}\n\n# tested on 6.0.1_r81 (SDK 23)\nandroid_repo_v6() {\n    sync bionic\n    sync build\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/jemalloc\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libselinux\n    sync external/mksh\n    sync external/pcre\n    sync external/safe-iop\n    sync external/zlib\n    sync libnativehelper\n    sync prebuilts/clang/linux-x86/host/3.6\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8\n    sync prebuilts/misc\n    sync system/core\n\n    case \"${ARCH}\" in\n        arm)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/linker/tests/Android.mk\n    rm bionic/tests/Android.mk\n    rm bionic/benchmarks/Android.mk\n    # we don't need the relocation packer, and removing\n    # the unittests from it is a bit of work.\n    rm bionic/tools/relocation_packer/Android.mk\n}\n\n# tested on 7.0.0_r36 (SDK 24)\n# tested on 7.1.2_r39 (SDK 25, not supported)\n#   API level 25, requires for Android 7.1, is not provided in NDKs\nandroid_repo_v7() {\n    sync bionic\n    sync build\n    sync build/kati\n    sync external/boringssl\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/jemalloc\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libselinux\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/mksh\n    sync external/pcre\n    sync external/safe-iop\n    sync external/zlib\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8\n    sync prebuilts/misc\n    sync prebuilts/ndk\n    sync prebuilts/ninja/linux-x86\n    sync system/core\n\n    case \"${ARCH}\" in\n        arm)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/linker/tests/Android.mk\n    rm bionic/tests/Android.mk\n    rm bionic/benchmarks/Android.mk\n    rm prebuilts/misc/common/android-support-test/Android.mk\n    # we don't need the relocation packer, and removing\n    # the unittests from it is a bit of work.\n    rm bionic/tools/relocation_packer/Android.mk\n\n    remove_tests\n}\n\n# tested on 8.0.0_r51 (SDK 26)\n# tested on 8.1.0_r81 (SDK 27)\nandroid_repo_v8() {\n    # need to build LLVM components, or libLLVM is disabled.\n    export FORCE_BUILD_LLVM_COMPONENTS=true\n\n    sync bionic\n    sync build/blueprint\n    sync build/make\n    sync build/soong\n    sync external/boringssl\n    sync external/clang\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/jemalloc\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libevent\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/lzma\n    sync external/mksh\n    sync external/pcre\n    sync external/safe-iop\n    sync external/selinux\n    sync external/zlib\n    sync libnativehelper\n    sync prebuilts/build-tools\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8\n    sync prebuilts/go/linux-x86\n    # we only need the relocation packer binary. everything else\n    # interferes with the build, so we remove the makefiles below.\n    sync prebuilts/misc\n    sync prebuilts/ndk\n    sync system/core\n    sync toolchain/binutils\n\n    case \"${ARCH}\" in\n        arm)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/linker/tests/Android.mk\n    rm bionic/tests/Android.mk\n    rm bionic/tests/Android.bp\n    rm bionic/benchmarks/Android.bp\n    rm bionic/tests/libs/Android.bp\n\n    # remove extra utilities\n    rm system/core/libgrallocusage/Android.bp\n    rm system/core/libmemtrack/Android.bp\n    rm system/core/libsysutils/Android.bp\n    local path=\n    find prebuilts/misc/ -name 'Android.mk' | while IFS= read -r path; do\n        rm \"${path}\"\n    done\n\n    # avoid java dependencies\n    rm external/lzma/Java/Tukaani/Android.mk\n\n    remove_tests\n}\n\n# tested on 9.0.0_r1 (SDK 28)\nandroid_repo_v9() {\n    sync art\n    sync bionic\n    sync build/blueprint\n    sync build/make\n    sync build/soong\n    sync external/clang\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/jemalloc\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/lzma\n    sync external/mksh\n    sync external/safe-iop\n    sync external/valgrind\n    sync external/vixl\n    sync external/zlib\n    sync frameworks/hardware/interfaces\n    sync hardware/interfaces\n    sync libnativehelper\n    sync prebuilts/build-tools\n    sync prebuilts/clang-tools\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8\n    sync prebuilts/go/linux-x86\n    sync prebuilts/misc\n    sync prebuilts/sdk\n    sync system/core\n    sync system/libhidl\n    sync system/tools/hidl\n\n    case \"${ARCH}\" in\n        arm)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/linker/tests/Android.mk\n    rm bionic/tests/Android.mk\n    rm bionic/tests/Android.bp\n    rm bionic/benchmarks/Android.bp\n    rm bionic/tests/libs/Android.bp\n    rm bionic/tests/headers/Android.bp\n    rm bionic/tests/headers/posix/Android.bp\n\n    remove_tests\n}\n\n# tested on 10.0.0_r47 (SDK 29)\nandroid_repo_v10() {\n    sync art\n    sync bionic\n    sync build/blueprint\n    sync build/make\n    sync build/soong\n    sync external/clang\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/golang-protobuf\n    sync external/jemalloc\n    sync external/jemalloc_new\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/lzma\n    sync external/mksh\n    sync external/vixl\n    sync external/zlib\n    sync libnativehelper\n    sync prebuilts/build-tools\n    sync prebuilts/clang-tools\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8\n    sync prebuilts/go/linux-x86\n    sync prebuilts/ndk\n    sync prebuilts/sdk\n    sync prebuilts/vndk/v28\n    sync system/core\n    sync system/sepolicy\n\n    case \"${ARCH}\" in\n        arm)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/tests/Android.mk\n    rm bionic/tests/Android.bp\n    rm bionic/benchmarks/Android.bp\n    rm bionic/tests/libs/Android.bp\n    rm bionic/tests/headers/Android.bp\n    rm bionic/tests/headers/posix/Android.bp\n\n    remove_tests\n}\n\nandroid_repo_v11() {\n    sync art\n    sync bionic\n    sync bootable/recovery\n    sync build/blueprint\n    sync build/make\n    sync build/soong\n    sync external/clang\n    sync external/compiler-rt\n    sync external/elfutils\n    sync external/fmtlib\n    sync external/golang-protobuf\n    sync external/gwp_asan\n    sync external/jemalloc\n    sync external/jemalloc_new\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/lzma\n    sync external/mksh\n    sync external/scudo\n    sync external/zlib\n    sync prebuilts/build-tools\n    sync prebuilts/clang-tools\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8\n    sync prebuilts/go/linux-x86\n    sync prebuilts/sdk\n    sync prebuilts/vndk/v28\n    sync prebuilts/vndk/v29\n    sync system/core\n    sync system/sepolicy\n\n    case \"${ARCH}\" in\n        arm)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/tests/Android.mk\n    rm bionic/tests/Android.bp\n    rm bionic/benchmarks/Android.bp\n    rm bionic/tests/libs/Android.bp\n    rm bionic/tests/headers/Android.bp\n    rm bionic/tests/headers/posix/Android.bp\n\n    # make sure we don't build benchmarks or apex\n    rm -r bionic/apex\n    rm -r bionic/benchmarks/\n\n    # libziparchive has tests in the header, remove them\n    local libziparchive_h=\"system/core/libziparchive/include/ziparchive/zip_writer.h\"\n    sed -i -e 's/#include <gtest\\/gtest_prod.h>//g' \"${libziparchive_h}\"\n    sed -i -e 's/FRIEND_TEST(zipwriter, WriteToUnseekableFile);//g' \"${libziparchive_h}\"\n\n    remove_tests\n}\n\nandroid_repo_v12() {\n    # FIXME: this is a work in progress, and is unlikely to ever\n    # be completed, since apex is now heavily integrated into the\n    # build system. `external/mksh` and `system/core/toolbox` build,\n    # however, `bionic`, the most import module, does not.\n    #\n    # the error messages are of the following:\n    #   internal error: panic in GenerateBuildActions for module \"com.android.example.apex\" variant \"android_common_com.android.example.apex_image\"\n    # fixing this requires either a comprehensive removal of APEX from the build\n    # or adding numerous APEX dependencies, which defeats the purpose of a\n    # minimal bionic build.\n    sync art\n    sync bionic\n    sync build/blueprint\n    sync build/make\n    sync build/soong\n    sync external/apache-xml\n    sync external/bouncycastle\n    sync external/clang\n    sync external/compiler-rt\n    sync external/conscrypt\n    sync external/elfutils\n    sync external/fmtlib\n    sync external/golang-protobuf\n    sync external/gwp_asan\n    sync external/icu\n    sync external/jemalloc\n    sync external/jemalloc_new\n    sync external/libcxx\n    sync external/libcxxabi\n    sync external/libunwind\n    sync external/libunwind_llvm\n    sync external/llvm\n    sync external/lzma\n    sync external/mksh\n    sync external/okhttp\n    sync external/scudo\n    sync external/starlark-go\n    sync external/zlib\n    sync libcore\n    sync prebuilts/build-tools\n    sync prebuilts/clang-tools\n    sync prebuilts/clang/host/linux-x86\n    sync prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8\n    sync prebuilts/go/linux-x86\n    sync prebuilts/sdk\n    sync prebuilts/vndk/v28\n    sync prebuilts/vndk/v29\n    sync prebuilts/vndk/v30\n    sync system/core\n    sync system/libbase\n    sync system/linkerconfig\n    sync system/logging\n    sync system/sepolicy\n    sync system/tools/xsdc\n    sync tools/metalava\n    # these tools also seem to be required, since apex is now tightly\n    # coupled with the bionic build. unfortunately, we want to avoid\n    # building apex at all costs.\n    #sync system/apex\n    #sync system/tools/aidl\n\n    case \"${ARCH}\" in\n        arm)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n        ;;\n        arm64)\n            sync external/arm-optimized-routines\n            sync prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9\n            sync prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9\n        ;;\n        x86)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n        x86_64)\n            sync prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9\n        ;;\n    esac\n\n    # avoid build tests\n    rm bionic/tests/Android.mk\n    rm bionic/tests/Android.bp\n    rm bionic/benchmarks/Android.bp\n    rm bionic/tests/libs/Android.bp\n    rm bionic/tests/headers/Android.bp\n    rm bionic/tests/headers/posix/Android.bp\n\n    # make sure we don't build benchmarks or apex\n    rm -r bionic/apex\n    rm -r bionic/benchmarks/\n    rm -r bionic/tests/\n    rm -r system/linkerconfig/testmodules\n\n    remove_tests\n}\n\nremove_tests() {\n    install_packages python3-pip\n\n    local version=\n    version=$(python3 -c 'import sys\nmajor = sys.version_info.major\nminor = sys.version_info.minor\nprint(f\"{major}.{minor}\")')\n    set +u\n    export PYTHONPATH=\"${PYTHON_TMPDIR}/lib/python${version}/site-packages/:${PYTHONPATH}\"\n    set -u\n    mkdir -p \"${PYTHON_TMPDIR}\"\n    python3 -m pip install sly==0.4.0 --prefix \"${PYTHON_TMPDIR}\"\n    python3 -m pip install google-re2==1.0 --prefix \"${PYTHON_TMPDIR}\"\n\n    python3 \"${PYTHON_TMPDIR}/scripts/build-system.py\" \\\n        --remove-tests \\\n        --verbose\n}\n\nsync() {\n    python3 ./repo sync -c --no-clone-bundle \"${1}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/android.cmake",
    "content": "# toolchain file for android targets, see #1110\n\nset(CMAKE_SYSTEM_NAME \"$ENV{CROSS_CMAKE_SYSTEM_NAME}\")\nset(CMAKE_SYSTEM_PROCESSOR \"$ENV{CROSS_CMAKE_SYSTEM_PROCESSOR}\")\nset(CMAKE_ANDROID_STANDALONE_TOOLCHAIN /android-ndk)\nset(CMAKE_ANDROID_API \"$ENV{CROSS_ANDROID_SDK}\")\nif(DEFINED ENV{CROSS_TARGET_RUNNER})\n    set(runner \"$ENV{CROSS_TARGET_RUNNER}\")\n    separate_arguments(runner)\n    set(CMAKE_CROSSCOMPILING_EMULATOR ${runner})\nendif()\n\n# these are cached so any build system that compiled outside of the rust\n# build system, such as a third-party cmake build and install of a shared\n# library, will still work. however, cmake-rs can override these values\nif(DEFINED ENV{CROSS_CMAKE_OBJECT_FLAGS})\n    set(CMAKE_C_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"C Compiler options\")\n    set(CMAKE_CXX_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"C++ Compiler options\")\n    set(CMAKE_ASM_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"ASM Compiler options\")\nendif()\n\nset(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\nset(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)\nset(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)\nset(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)\n"
  },
  {
    "path": "docker/apt-cross-essential.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local narch\n    local -a packages\n\n    narch=\"$(dpkg --print-architecture)\"\n    packages+=(\"libc6-dev-${TARGET_ARCH}-cross:${narch}\")\n\n    # Install crossbuild-essential if CROSSBUILD_ESSENTIAL is set\n    if [ -n \"${CROSSBUILD_ESSENTIAL:-}\" ]; then\n        packages+=(\"crossbuild-essential-${TARGET_ARCH}:${narch}\")\n    fi\n\n    if ! command -v \"${CROSS_TOOLCHAIN_PREFIX}g++\" &>/dev/null; then\n      packages+=(\"g++-${TARGET_TRIPLE}:${narch}\")\n    fi\n\n    if ! command -v \"${CROSS_TOOLCHAIN_PREFIX}gfortran\" &>/dev/null; then\n      packages+=(\"gfortran-${TARGET_TRIPLE}:${narch}\")\n    fi\n\n    install_packages \"${packages[@]}\"\n\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/base-runner.sh",
    "content": "#!/usr/bin/env bash\n\nhost_architecture() {\n    # there's numerous compatibility modes, so we want\n    # to ensure that these are valid. we also want to\n    # use dpkg if it's available since it gives hard-float\n    # information on compatible architectures\n    local host\n    local arch\n    if dpkg >/dev/null 2>&1; then\n        host=$(dpkg --print-architecture)\n        arch=\"${host}\"\n    else\n        host=$(uname -m)\n        arch=\"${host}\"\n\n        case \"${arch}\" in\n            aarch64|armv8b|armv8l)\n                arch=arm64\n                ;;\n            aarch64_be)\n                arch=arm64be\n                ;;\n            arm*)\n                arch=unknown\n                ;;\n            ppc)\n                arch=powerpc\n                ;;\n            ppc64le)\n                arch=ppc64el\n                ;;\n            s390)\n                arch=s390x\n                ;;\n            i?86)\n                arch=i386\n                ;;\n            x64|x86_64)\n                arch=amd64\n                ;;\n            *)\n                ;;\n        esac\n    fi\n\n    echo \"${arch}\"\n}\n\nnormalize_arch() {\n    local arch=\"${1}\"\n    local debian\n\n    debian=\"${arch}\"\n    case \"${arch}\" in\n        aarch64)\n            debian=arm64\n            ;;\n        x86_64)\n            debian=amd64\n            ;;\n        arm)\n            debian=armel\n            ;;\n        armv7)\n            debian=arm\n            ;;\n        armv7hf)\n            debian=armhf\n            ;;\n        i?86)\n            debian=i386\n            ;;\n        powerpc64)\n            debian=ppc64\n            ;;\n        powerpc64le)\n            debian=ppc64el\n            ;;\n        riscv64*)\n            debian=riscv64\n            ;;\n        *)\n            ;;\n    esac\n\n    echo \"${debian}\"\n}\n\nis_native_binary() {\n    # determines if the binary can run natively on the host\n    local arch=\"${1}\"\n    local host\n    local target\n    host=$(host_architecture)\n    target=$(normalize_arch \"${arch}\")\n\n    # FIXME: this is not comprehensive. add more compatible architectures.\n    case \"${host}\" in\n        amd64)\n            if [[ \"${target}\" == i386 ]] || [[ \"${target}\" == amd64 ]]; then\n                return 0\n            fi\n            ;;\n        *)\n            if [[ \"${host}\" == \"${target}\" ]]; then\n                return 0\n            fi\n            ;;\n    esac\n\n    return 1\n}\n\nqemu_arch() {\n    # select qemu arch\n    local arch=\"${1}\"\n    local qarch=\"${arch}\"\n    case \"${arch}\" in\n        arm|armhf|armv7|armv7hf)\n            qarch=\"arm\"\n            ;;\n        i?86)\n            qarch=\"i386\"\n            ;;\n        powerpc)\n            qarch=\"ppc\"\n            ;;\n        powerpc64)\n            qarch=\"ppc64\"\n            ;;\n        powerpc64le)\n            if [ \"${CROSS_RUNNER}\" = \"qemu-user\" ]; then\n                qarch=\"ppc64le\"\n            else\n                qarch=\"ppc64\"\n            fi\n            ;;\n    esac\n\n    echo \"${qarch}\"\n}\n"
  },
  {
    "path": "docker/cmake.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local version=3.23.1\n\n    install_packages curl\n\n    local td\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    local cmake_arch\n    local cmake_sha256\n\n    local narch\n    narch=\"$(uname -p)\"\n\n    echo \"[INFO] $narch\"\n\n    case \"${narch}\" in\n        x86_64)\n            cmake_arch=\"linux-x86_64\"\n            cmake_sha256=\"da2a9b18c3bfb136917fa1a579aa5316b01c1d6c111043d03f18877ff05bda30\"\n            ;;\n        arm64)\n            cmake_arch=\"linux-aarch64\"\n            cmake_sha256=\"86122bdfd030208aa36705ef421a218ccec52a14368020b2d67043af5e45490b\"\n            ;;\n        *)\n            echo \"Unsupported architecture: ${narch}\" 1>&2\n            exit 1\n            ;;\n     esac\n\n    curl --retry 3 -sSfL \"https://github.com/Kitware/CMake/releases/download/v${version}/cmake-${version}-${cmake_arch}.sh\" -o cmake.sh\n    sha256sum --check <<<\"${cmake_sha256}  cmake.sh\"\n    sh cmake.sh --skip-license --prefix=/usr/local\n    cmake --version\n\n    popd\n\n    purge_packages\n\n    rm -rf \"${td}\"\n    rm -rf /var/lib/apt/lists/*\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/common.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\n# For non-native architectures, look for packages on ports.ubuntu.com instead.\n# This is important if you enable additional architectures so you can install libraries to cross-compile against.\n# Look for 'dpkg --add-architecture' in the README for more details.\nif grep -i ubuntu /etc/os-release >/dev/null; then\n    NATIVE_ARCH=$(dpkg --print-architecture)\n\n    if [ \"$NATIVE_ARCH\" = \"amd64\" ]; then\n        sed 's/http:\\/\\/\\(.*\\).ubuntu.com\\/ubuntu\\//[arch-=amd64,i386] http:\\/\\/ports.ubuntu.com\\/ubuntu-ports\\//g' /etc/apt/sources.list > /etc/apt/sources.list.d/ports.list\n        sed -i 's/http:\\/\\/\\(.*\\).ubuntu.com\\/ubuntu\\//[arch=amd64,i386] http:\\/\\/\\1.archive.ubuntu.com\\/ubuntu\\//g' /etc/apt/sources.list\n    else\n        sed -i \"s/http:\\/\\/\\(.*\\).ubuntu.com\\/ubuntu\\//[arch-=${NATIVE_ARCH}] http:\\/\\/ports.ubuntu.com\\/ubuntu-ports\\//g\" /etc/apt/sources.list\n        sed -i \"s/http:\\/\\/\\(.*\\).ubuntu.com\\/ubuntu\\//[arch=${NATIVE_ARCH}] http:\\/\\/\\1.archive.ubuntu.com\\/ubuntu\\//g\" /etc/apt/sources.list\n    fi\nfi\n\ninstall_packages \\\n    autoconf \\\n    automake \\\n    binutils \\\n    ca-certificates \\\n    curl \\\n    file \\\n    gcc \\\n    git \\\n    libtool \\\n    m4 \\\n    make\n\nif_centos install_packages \\\n    clang-devel \\\n    gcc-c++ \\\n    gcc-gfortran \\\n    glibc-devel \\\n    pkgconfig\n\nif_ubuntu install_packages \\\n    g++ \\\n    gfortran \\\n    libc6-dev \\\n    libclang-dev \\\n    pkg-config\n"
  },
  {
    "path": "docker/cross.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC1090,SC1091\n\nset -x\nset -euo pipefail\n\n. lib.sh\n\nmain() {\n    local project_dir=\"${1}\"\n\n    install_packages ca-certificates curl gcc libc6-dev\n\n    cd \"${project_dir}\"\n    curl --proto \"=https\" --tlsv1.2 --retry 3 -sSfL https://sh.rustup.rs | sh -s -- -y\n    source \"${HOME}\"/.cargo/env\n    cargo install --path . --locked\n\n    purge_packages\n\n    rm -rf \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/crosstool-config/aarch64_be-unknown-linux-gnu.config",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_DOWNLOAD_AGENT_CURL=y\nCT_ARCH_ARM=y\nCT_ARCH_64=y\nCT_ARCH_BE=y\nCT_ARCH_FLOAT_HW=y\nCT_KERNEL_LINUX=y\nCT_LINUX_V_5_19=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"5.19.16\"\nCT_LINUX_later_than_4_8=y\nCT_LINUX_4_8_or_later=y\nCT_LINUX_later_than_3_7=y\nCT_LINUX_3_7_or_later=y\nCT_LINUX_later_than_3_2=y\nCT_LINUX_3_2_or_later=y\nCT_GLIBC_V_2_36=y\n# CT_GLIBC_NO_VERSIONS is not set\nCT_GLIBC_VERSION=\"2.36\"\nCT_GLIBC_2_17_or_later=y\nCT_GLIBC_later_than_2_14=y\nCT_GLIBC_2_14_or_later=y\nCT_GCC_V_14=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"14.2.0\"\nCT_GCC_later_than_7=y\nCT_GCC_7_or_later=y\nCT_GCC_later_than_6=y\nCT_GCC_6_or_later=y\nCT_GCC_later_than_5=y\nCT_GCC_5_or_later=y\nCT_GCC_later_than_4_9=y\nCT_GCC_4_9_or_later=y\nCT_GCC_later_than_4_8=y\nCT_GCC_4_8_or_later=y\nCT_CC_LANG_CXX=y\nCT_CC_LANG_FORTRAN=y\n"
  },
  {
    "path": "docker/crosstool-config/arm-unknown-linux-gnueabihf.config",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_DOWNLOAD_AGENT_CURL=y\nCT_ARCH_ARM=y\nCT_ARCH_ARCH=\"armv6\"\nCT_ARCH_FPU=\"vfp\"\nCT_ARCH_FLOAT_HW=y\nCT_KERNEL_LINUX=y\nCT_LINUX_V_4_19=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"4.19.287\"\nCT_LINUX_later_than_4_8=y\nCT_LINUX_4_8_or_later=y\nCT_LINUX_later_than_3_7=y\nCT_LINUX_3_7_or_later=y\nCT_LINUX_later_than_3_2=y\nCT_LINUX_3_2_or_later=y\nCT_BINUTILS_V_2_32=y\nCT_GLIBC_V_2_31=y\n# CT_GLIBC_NO_VERSIONS is not set\nCT_GLIBC_VERSION=\"2.31\"\nCT_GLIBC_2_17_or_later=y\nCT_GLIBC_later_than_2_14=y\nCT_GLIBC_2_14_or_later=y\nCT_GCC_V_8=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"8.5.0\"\nCT_GCC_later_than_7=y\nCT_GCC_7_or_later=y\nCT_GCC_later_than_6=y\nCT_GCC_6_or_later=y\nCT_GCC_later_than_5=y\nCT_GCC_5_or_later=y\nCT_GCC_later_than_4_9=y\nCT_GCC_4_9_or_later=y\nCT_GCC_later_than_4_8=y\nCT_GCC_4_8_or_later=y\nCT_CC_LANG_CXX=y\nCT_GETTEXT_V_0_19_8_1=y\nCT_GMP_V_6_1=y\nCT_ISL_V_0_20=y\nCT_LIBICONV_V_1_15=y\nCT_NCURSES_V_6_1=y\nCT_CC_LANG_FORTRAN=y\n"
  },
  {
    "path": "docker/crosstool-config/loongarch64-unknown-linux-gnu.config",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_EXPERIMENTAL=y\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_ARCH_LOONGARCH=y\n# CT_DEMULTILIB is not set\nCT_ARCH_USE_MMU=y\nCT_ARCH_ARCH=\"loongarch64\"\nCT_KERNEL_LINUX=y\nCT_LINUX_V_5_19=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"5.19.16\"\nCT_LINUX_later_than_4_8=y\nCT_LINUX_4_8_or_later=y\nCT_LINUX_later_than_3_7=y\nCT_LINUX_3_7_or_later=y\nCT_LINUX_later_than_3_2=y\nCT_LINUX_3_2_or_later=y\nCT_GLIBC_V_2_36=y\n# CT_GLIBC_NO_VERSIONS is not set\nCT_GLIBC_VERSION=\"2.36\"\nCT_GLIBC_2_17_or_later=y\nCT_GLIBC_later_than_2_14=y\nCT_GLIBC_2_14_or_later=y\nCT_GCC_V_14=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"14.2.0\"\nCT_GCC_later_than_7=y\nCT_GCC_7_or_later=y\nCT_GCC_later_than_6=y\nCT_GCC_6_or_later=y\nCT_GCC_later_than_5=y\nCT_GCC_5_or_later=y\nCT_GCC_later_than_4_9=y\nCT_GCC_4_9_or_later=y\nCT_GCC_later_than_4_8=y\nCT_GCC_4_8_or_later=y\nCT_CC_GCC_ENABLE_DEFAULT_PIE=y\nCT_CC_LANG_CXX=y\nCT_CC_LANG_FORTRAN=y\n"
  },
  {
    "path": "docker/crosstool-config/loongarch64-unknown-linux-musl.config",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_EXPERIMENTAL=y\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_ARCH_LOONGARCH=y\n# CT_DEMULTILIB is not set\nCT_ARCH_USE_MMU=y\nCT_ARCH_ARCH=\"loongarch64\"\nCT_KERNEL_LINUX=y\nCT_LINUX_V_5_19=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"5.19.16\"\nCT_LINUX_later_than_4_8=y\nCT_LINUX_4_8_or_later=y\nCT_LINUX_later_than_3_7=y\nCT_LINUX_3_7_or_later=y\nCT_LINUX_later_than_3_2=y\nCT_LINUX_3_2_or_later=y\nCT_LIBC_MUSL=y\nCT_MUSL_V_1_2_5=y\n# CT_MUSL_NO_VERSIONS is not set\nCT_MUSL_VERSION=\"1.2.5\"\nCT_GCC_V_14=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"14.2.0\"\nCT_GCC_later_than_7=y\nCT_GCC_7_or_later=y\nCT_GCC_later_than_6=y\nCT_GCC_6_or_later=y\nCT_GCC_later_than_5=y\nCT_GCC_5_or_later=y\nCT_GCC_later_than_4_9=y\nCT_GCC_4_9_or_later=y\nCT_GCC_later_than_4_8=y\nCT_GCC_4_8_or_later=y\nCT_CC_GCC_ENABLE_DEFAULT_PIE=y\nCT_CC_LANG_CXX=y\nCT_CC_LANG_FORTRAN=y\n"
  },
  {
    "path": "docker/crosstool-config/riscv64gc-unknown-linux-musl.config",
    "content": "CT_CONFIG_VERSION=\"4\"\nCT_EXPERIMENTAL=y\nCT_PREFIX_DIR=\"/x-tools/${CT_TARGET}\"\nCT_ARCH_RISCV=y\n# CT_DEMULTILIB is not set\nCT_ARCH_USE_MMU=y\nCT_ARCH_ARCH=\"rv64g\"\nCT_ARCH_64=y\nCT_KERNEL_LINUX=y\nCT_LINUX_V_5_19=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"5.19.16\"\nCT_LINUX_later_than_4_8=y\nCT_LINUX_4_8_or_later=y\nCT_LINUX_later_than_3_7=y\nCT_LINUX_3_7_or_later=y\nCT_LINUX_later_than_3_2=y\nCT_LINUX_3_2_or_later=y\nCT_LIBC_MUSL=y\nCT_MUSL_V_1_2_5=y\n# CT_MUSL_NO_VERSIONS is not set\nCT_MUSL_VERSION=\"1.2.5\"\nCT_GCC_V_14=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"14.2.0\"\nCT_GCC_later_than_7=y\nCT_GCC_7_or_later=y\nCT_GCC_later_than_6=y\nCT_GCC_6_or_later=y\nCT_GCC_later_than_5=y\nCT_GCC_5_or_later=y\nCT_GCC_later_than_4_9=y\nCT_GCC_4_9_or_later=y\nCT_GCC_later_than_4_8=y\nCT_GCC_4_8_or_later=y\nCT_CC_GCC_ENABLE_DEFAULT_PIE=y\nCT_CC_LANG_CXX=y\nCT_CC_LANG_FORTRAN=y\n"
  },
  {
    "path": "docker/crosstool-ng.sh",
    "content": "#!/bin/bash\n\nset -x\nset -eo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nsilence_stdout() {\n    if [[ \"${VERBOSE}\" == \"1\" ]]; then\n        \"${@}\"\n    else\n        \"${@}\" >/dev/null\n    fi\n}\n\nmain() {\n    local config=\"${1}\"\n    local nproc=\"${2}\"\n    local ctng_version=${3:-crosstool-ng-1.27.0}\n    local ctng_url=\"https://github.com/crosstool-ng/crosstool-ng\"\n    local username=crosstool\n    local crosstooldir=/opt/crosstool\n    local buildir\n    local srcdir=\"/home/${username}/src\"\n    local dstdir=\"/x-tools\"\n    local sleep=15s\n    local timeout=5m\n\n    install_packages \\\n        adduser \\\n        autoconf \\\n        bison \\\n        bzip2 \\\n        curl \\\n        flex \\\n        gawk \\\n        help2man \\\n        libncurses-dev \\\n        libtool-bin \\\n        patch \\\n        python3 \\\n        python3-dev \\\n        python3-pip \\\n        rsync \\\n        texinfo \\\n        wget \\\n        unzip \\\n        xz-utils\n\n    # configure and install crosstool-ng\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    mkdir \"crosstool-ng-${ctng_version}\"\n    pushd \"crosstool-ng-${ctng_version}\"\n    git init\n    git fetch --depth=1 \"${ctng_url}\" \"${ctng_version}\"\n    git reset --hard FETCH_HEAD\n    ./bootstrap\n    ./configure --prefix=\"${crosstooldir}\"\n    make -j\"${nproc}\"\n    make install\n\n    popd\n    popd\n\n    # configure and install our toolchain\n    buildir=\"$(mktemp -d)\"\n\n    # copy our config files, and make sure the l\n    # crosstool-ng can't be run as root, so we do this instead.\n    adduser --disabled-password --gecos \"\" \"${username}\"\n    chown -R \"${username}\":\"${username}\" \"${buildir}\"\n    pushd \"${buildir}\"\n    cp /\"${config}\" .config\n    chown \"${username}\":\"${username}\" .config\n    su \"${username}\" -c \"${crosstooldir}/bin/ct-ng olddefconfig\"\n\n    # the download steps can stall indefinitely, so we want to set a timeout to\n    # ensure it always completes. we therefore attempt to  download until\n    # this step completes or fails. the built toolchain installs to `/x-tools`.\n    mkdir -p \"${dstdir}\"\n    chown -R \"${username}\":\"${username}\" \"${dstdir}\"\n    local step=companion_tools_for_build\n    su \"${username}\" -c \"mkdir -p ${srcdir}\"\n    download() {\n        # timeout is a command, not a built-in, so it won't\n        # work with any bash functions: must call a command.\n        timeout \"${timeout}\" \\\n            su \"${username}\" -c \\\n            \"STOP=${step} CT_DEBUG_CT_SAVE_STEPS=1 ${crosstooldir}/bin/ct-ng build.${nproc}\"\n    }\n\n    while silence_stdout download; [ $? -eq 124 ]; do\n        # Indicates a timeout, repeat the command.\n        sleep \"${sleep}\"\n    done\n    silence_stdout su \"${username}\" \\\n        -c \"CT_DEBUG_CT_SAVE_STEPS=1 ${crosstooldir}/bin/ct-ng build.${nproc}\"\n\n    popd\n\n    purge_packages\n\n    rm -rf \"${srcdir}\"\n    rm -rf \"${buildir}\"\n    rm -rf \"${crosstooldir}\"\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/deny-debian-packages.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\ndeny_package() {\n    local package=\"${1}\"\n    local filename=\"${2}\"\n    echo \"Package: ${package}:${TARGET_ARCH}\nPin: release *\nPin-Priority: -1\" > \"/etc/apt/preferences.d/${filename}\"\n}\n\nmain() {\n    if [[ $# -eq 0 ]]; then\n        deny_package '*' \"all-packages\"\n    else\n        local package\n        for package in \"${@}\"; do\n            deny_package \"${package}\" \"${package}\"\n            echo \"${package}\"\n        done\n    fi\n\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/dragonfly.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local nproc=\n    local binutils=2.32 \\\n        dragonfly=6.0.1_REL \\\n        gcc=10.3.0 \\\n        target=x86_64-unknown-dragonfly\n    if [[ $# != \"0\" ]]; then\n        nproc=\"${1}\"\n    fi\n\n    install_packages libarchive-tools \\\n        bzip2 \\\n        ca-certificates \\\n        curl \\\n        g++ \\\n        make \\\n        patch \\\n        wget \\\n        xz-utils\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n    mkdir \"${td}\"/{binutils,gcc}{,-build} \"${td}/dragonfly\"\n\n    download_binutils \"${binutils}\" \"bz2\"\n    tar -C \"${td}/binutils\" --strip-components=1 -xjf \"binutils-${binutils}.tar.bz2\"\n\n    download_gcc \"${gcc}\" \"gz\"\n    tar -C \"${td}/gcc\" --strip-components=1 -xf \"gcc-${gcc}.tar.gz\"\n\n    cd gcc\n    sed -i -e 's/ftp:/https:/g' ./contrib/download_prerequisites\n    ./contrib/download_prerequisites\n    patch libstdc++-v3/configure <<'EOF'\n47159c47159\n<   *-freebsd*)\n---\n>   *-freebsd* | *-dragonfly*)\nEOF\n    cd ..\n\n    local mirrors=(\n        \"https://mirror-master.dragonflybsd.org/iso-images\"\n        \"https://avalon.dragonflybsd.org/iso-images/\"\n    )\n    download_mirrors \"\" \"dfly-x86_64-${dragonfly}.iso.bz2\" \"${mirrors[@]}\"\n    bzcat \"dfly-x86_64-${dragonfly}.iso.bz2\" | bsdtar xf - -C \"${td}/dragonfly\" ./usr/include ./usr/lib ./lib\n\n    cd binutils-build\n    ../binutils/configure \\\n        --target=\"${target}\"\n    make \"-j${nproc}\"\n    make install\n    cd ..\n\n    # note: shell expansions can't be quoted\n    local destdir=\"/usr/local/${target}\"\n    cp -r \"${td}/dragonfly/usr/include\" \"${destdir}\"/\n    cp \"${td}/dragonfly/lib/libc.so.8\" \"${destdir}/lib\"\n    cp \"${td}/dragonfly/lib/libm.so.4\" \"${destdir}/lib\"\n    cp \"${td}/dragonfly/lib/libutil.so.4\" \"${destdir}/lib\"\n    cp \"${td}/dragonfly/usr/lib/libexecinfo.so.1\" \"${destdir}/lib\"\n    cp \"${td}/dragonfly/usr/lib/libpthread.so\" \"${destdir}/lib/libpthread.so\"\n    cp \"${td}/dragonfly/usr/lib/librt.so.0\" \"${destdir}/lib\"\n    cp \"${td}\"/dragonfly/usr/lib/lib{c,m,util,kvm}.a \"${destdir}/lib\"\n    cp \"${td}/dragonfly/usr/lib/thread/libthread_xu.so.2\" \"${destdir}/lib/libpthread.so.0\"\n    cp \"${td}\"/dragonfly/usr/lib/{crt1,Scrt1,crti,crtn}.o \"${destdir}/lib/\"\n\n    ln -s libc.so.8 \"${destdir}/lib/libc.so\"\n    ln -s libexecinfo.so.1 \"${destdir}/lib/libexecinfo.so\"\n    ln -s libm.so.4 \"${destdir}/lib/libm.so\"\n    ln -s librt.so.0 \"${destdir}/lib/librt.so\"\n    ln -s libutil.so.4 \"${destdir}/lib/libutil.so\"\n\n    cd gcc-build\n    ../gcc/configure \\\n        --disable-libada \\\n        --disable-libcilkrts \\\n        --disable-libgomp \\\n        --disable-libquadmath \\\n        --disable-libquadmath-support \\\n        --disable-libsanitizer \\\n        --disable-libssp \\\n        --disable-libvtv \\\n        --disable-lto \\\n        --disable-multilib \\\n        --disable-nls \\\n        --enable-languages=c,c++,fortran \\\n        --target=\"${target}\"\n    make \"-j${nproc}\"\n    make install\n    cd ..\n\n    # rust incorrectly adds link args to libgcc_pic, which is no longer\n    # a present target, and it should link to libgcc_s.\n    # https://github.com/rust-lang/rust/blob/60361f2/library/unwind/build.rs#L23-L38\n    ln -s \"${destdir}\"/lib/libgcc_s.so \"${destdir}\"/lib/libgcc_pic.so\n\n    # clean up\n    popd\n\n    purge_packages\n\n    # store the version info for the dragonfly release\n    echo \"${dragonfly}\" > /opt/dragonfly-version\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/dropbear.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local version=2022.82\n    local mirrors=(\n        \"https://matt.ucc.asn.au/dropbear/releases\"\n        \"https://mirror.dropbear.nl/mirror\"\n    )\n\n    install_packages \\\n        autoconf \\\n        automake \\\n        bzip2 \\\n        curl \\\n        make\n\n    if_centos install_packages zlib-devel\n    if_ubuntu install_packages zlib1g-dev\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    download_mirrors \"\" \"dropbear-${version}.tar.bz2\" \"${mirrors[@]}\"\n    tar --strip-components=1 -xjf \"dropbear-${version}.tar.bz2\"\n\n    # Remove some unwanted message\n    sed -i '/skipping hostkey/d' cli-kex.c\n    sed -i '/failed to identify current user/d' cli-runopts.c\n\n    ./configure \\\n        --disable-syslog \\\n        --disable-shadow \\\n        --disable-lastlog \\\n        --disable-utmp \\\n        --disable-utmpx \\\n        --disable-wtmp \\\n        --disable-wtmpx \\\n        --disable-pututline \\\n        --disable-pututxline\n\n    make \"-j$(nproc)\" PROGRAMS=dbclient\n    cp dbclient /usr/local/bin/\n\n    purge_packages\n\n    popd\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/emscripten.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    install_packages ca-certificates \\\n        curl \\\n        git \\\n        libxml2 \\\n        python\n\n    cd /\n    git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable\n    cd /emsdk-portable\n\n    ./emsdk install 1.38.46-upstream\n    ./emsdk activate 1.38.46-upstream\n\n    # Compile and cache libc\n    echo 'int main() {}' > a.c\n    emcc a.c\n    emcc -s BINARYEN=1 a.c\n    echo -e '#include <iostream>\\n void hello(){ std::cout << std::endl; }' > a.cpp\n    emcc a.cpp\n    emcc -s BINARYEN=1 a.cpp\n    rm -f a.*\n\n    # Make emsdk usable by any user\n    chmod a+rwX -R \"${EMSDK}\"\n\n    purge_packages\n\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/freebsd-common.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. /freebsd-arch.sh\n\nexport FREEBSD_ARCH=\ncase \"${ARCH}\" in\n    aarch64) # releases are under http://ftp.freebsd.org/pub/FreeBSD/releases/\n        FREEBSD_ARCH=arm64 # http://ftp.freebsd.org/pub/FreeBSD/releases/arm64/\n        ;;\n    x86_64)\n        FREEBSD_ARCH=amd64\n        ;;\n    i686)\n        FREEBSD_ARCH=i386\n        ;;\nesac\n\nexport FREEBSD_MAJOR=13\n"
  },
  {
    "path": "docker/freebsd-extras.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. /lib.sh\n# shellcheck disable=SC1091\n. /freebsd-common.sh\n# shellcheck disable=SC1091\n. /freebsd-install.sh\n\ncase \"${FREEBSD_ARCH}\" in\n    arm64) # extras mirrors are under https://pkg.freebsd.org/\n        FREEBSD_ARCH=aarch64 #  https://pkg.freebsd.org/FreeBSD:13:aarch64/\n        ;;\nesac\n\nmain() {\n    apt-get update && apt-get install --assume-yes --no-install-recommends \\\n        curl \\\n        dnsutils \\\n        jq \\\n        xz-utils \\\n        zstd\n\n    local url=\n    url=$(fetch_best_freebsd_mirror)\n    FREEBSD_MIRROR=\"${url}\" setup_freebsd_packagesite\n    FREEBSD_MIRROR=\"${url}\" install_freebsd_package openssl sqlite3\n\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/freebsd-fetch-best-mirror.sh",
    "content": "#!/bin/bash\nset -e\n\n# shellcheck disable=SC1091\n. /freebsd-install.sh\nfetch_best_freebsd_mirror \"$@\"\n"
  },
  {
    "path": "docker/freebsd-gcc.sh",
    "content": "#!/bin/bash\n\n# the freebsd images need libstdc++ to be linked as well\n# otherwise, we get `undefined reference to `std::ios_base::Init::Init()'`\n\nset -euo pipefail\n\nmain() {\n    if [[ $# -eq 0 ]]; then\n        exec \"${CROSS_TOOLCHAIN_PREFIX}gcc\"\n    else\n        exec \"${CROSS_TOOLCHAIN_PREFIX}gcc\" \"${@}\" -lc++ -lstdc++\n    fi\n}\n\nmain \"$@\"\n"
  },
  {
    "path": "docker/freebsd-install-package.sh",
    "content": "#!/bin/bash\nset -e\n\n# shellcheck disable=SC1091\n. /freebsd-install.sh\ninstall_freebsd_package \"$@\"\n"
  },
  {
    "path": "docker/freebsd-install.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. /freebsd-common.sh\n\n# list of SRV records to query if the default mirror fails\nFREEBSD_HTTP_TCP_SOURCES=(\n    # these return all mirrors, including local ones\n    \"_http._tcp.pkg.all.freebsd.org\"\n    # this only returns geodns mirrors\n    \"_http._tcp.pkg.freebsd.org\"\n)\nFREEBSD_PACKAGEDIR=\"/opt/freebsd-packagesite\"\nFREEBSD_PACKAGESITE=\"${FREEBSD_PACKAGEDIR}/packagesite.yaml\"\nFREEBSD_TARGET=\"${ARCH}-unknown-freebsd${FREEBSD_MAJOR}\"\nFREEBSD_DEFAULT_MIRROR=\"pkg.freebsd.org\"\n# NOTE: these mirrors were known to work as of 2022-11-28.\n# no availability guarantees are made for any of them.\nFREEBSD_BACKUP_MIRRORS=(\n    \"pkg0.syd.freebsd.org\"\n    \"pkg0.bme.freebsd.org\"\n    \"pkg0.bra.freebsd.org\"\n    \"pkg0.fra.freebsd.org\"\n    \"pkg0.jinx.freebsd.org\"\n    \"pkg0.kul.freebsd.org\"\n    \"pkg0.kwc.freebsd.org\"\n    \"pkg0.nyi.freebsd.org\"\n    \"pkg0.tuk.freebsd.org\"\n    \"pkg0.twn.freebsd.org\"\n)\n\n# NOTE: out of convention, we use `url` for mirrors with the scheme,\n# and `mirror` for those without the scheme for consistent naming.\nfreebsd_package_source() {\n    local url=\"${1}\"\n    echo \"${url}/FreeBSD:${FREEBSD_MAJOR}:${FREEBSD_ARCH}/quarterly\"\n}\n\nfreebsd_mirror_works() {\n    local mirror=\"${1}\"\n    local scheme=\"${2}\"\n    local pkg_source=\n\n    # meta.conf is a small file for quick confirmation the mirror works\n    pkg_source=$(freebsd_package_source \"${scheme}://${mirror}\")\n    local path=\"${pkg_source}/meta.conf\"\n\n    timeout 20s curl --retry 3 -sSfL \"${path}\" >/dev/null 2>&1\n}\n\n_fetch_best_freebsd_mirror() {\n    # in case if the default mirror is down, we can use various known\n    # fallbacks, or at worst, SRV fallbacks to find the ideal package\n    # site. no individual mirror other than the default mirror is\n    # guaranteed to exist, so we use a tiered approach. only\n    # the default mirror supports https.\n    if freebsd_mirror_works \"${FREEBSD_DEFAULT_MIRROR}\" \"https\"; then\n        echo \"https://${FREEBSD_DEFAULT_MIRROR}\"\n        return 0\n    fi\n\n    # if we've gotten here, it could be a DNS issue, so using a DNS\n    # resolver to fetch SRV fallbacks may not work. let's first try\n    # a few previously tested mirrors and see if any work.\n    local mirror=\n    for mirror in \"${FREEBSD_BACKUP_MIRRORS[@]}\"; do\n        if freebsd_mirror_works \"${mirror}\" \"http\"; then\n            echo \"http://${mirror}\"\n            return 0\n        fi\n    done\n\n    local http_tcp_source=\n    local response=\n    local lines=\n    # shellcheck disable=SC2016\n    local regex='/\\d+\\s+\\d+\\s+\\d+\\s+(.*)\\./; print $1'\n    for http_tcp_source in \"${FREEBSD_HTTP_TCP_SOURCES[@]}\"; do\n        # the output will have the following format, but we only want the\n        # target and ignore everything else:\n        #   $priority $port $weight $target.\n        #\n        # some output may not match, so we skip those lines, for example:\n        #   96.47.72.71\n        response=$(dig +short srv \"${http_tcp_source}\")\n        readarray -t lines <<< \"${response}\"\n        for line in \"${lines[@]}\"; do\n            mirror=$(echo \"${line}\" | perl -nle \"${regex}\")\n            if [[ -n \"${mirror}\" ]]; then\n                if freebsd_mirror_works \"${mirror}\" \"http\"; then\n                    echo \"http://${mirror}\"\n                    return 0\n                fi\n            fi\n        done\n    done\n\n    echo -e \"\\e[31merror:\\e[0m could not find a working FreeBSD package mirror.\" 1>&2\n    exit 1\n}\n\nfetch_best_freebsd_mirror() {\n    set +e\n    _fetch_best_freebsd_mirror\n    code=$?\n    set -e\n\n    return \"${code}\"\n}\n\nsetup_freebsd_packagesite() {\n    local url=\"${FREEBSD_MIRROR:-}\"\n    local pkg_source=\n\n    if [[ -z \"${url}\" ]]; then\n        url=$(fetch_best_freebsd_mirror)\n    fi\n    pkg_source=$(freebsd_package_source \"${url}\")\n\n    mkdir -p \"${FREEBSD_PACKAGEDIR}\"\n    curl --retry 3 -sSfL \"${pkg_source}/packagesite.pkg\" -O\n    tar -C \"${FREEBSD_PACKAGEDIR}\" --zstd -xf packagesite.pkg\n}\n\n# don't provide the mirror as a positional argument, so it can be optional\ninstall_freebsd_package() {\n    local url=\"${FREEBSD_MIRROR:-}\"\n    local pkg_source=\n    local name\n    local path\n    local pkg\n    local td\n    local destdir=\"/usr/local/${FREEBSD_TARGET}\"\n\n    if [[ -z \"${url}\" ]]; then\n        url=$(fetch_best_freebsd_mirror)\n    fi\n    pkg_source=$(freebsd_package_source \"${url}\")\n\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    for name in \"${@}\"; do\n        path=$(jq -c '. | select ( .name == \"'\"${name}\"'\" ) | .repopath' \"${FREEBSD_PACKAGESITE}\")\n        if [[ -z \"${path}\" ]]; then\n            echo \"Unable to find package ${name}\" >&2\n            exit 1\n        fi\n        path=${path//'\"'/}\n        pkg=$(basename \"${path}\")\n\n        mkdir \"${td}\"/package\n        curl --retry 3 -sSfL \"${pkg_source}/${path}\" -O\n        tar -C \"${td}/package\" -xJf \"${pkg}\"\n        cp -r \"${td}/package/usr/local\"/* \"${destdir}\"/\n\n        rm \"${td:?}/${pkg}\"\n        rm -rf \"${td:?}/package\"\n    done\n\n    # clean up\n    popd\n    rm -rf \"${td:?}\"\n}\n"
  },
  {
    "path": "docker/freebsd-setup-packagesite.sh",
    "content": "#!/bin/bash\nset -e\n\n# shellcheck disable=SC1091\n. /freebsd-install.sh\nsetup_freebsd_packagesite \"$@\"\n"
  },
  {
    "path": "docker/freebsd.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. /freebsd-common.sh\n# shellcheck disable=SC1091\n. /lib.sh\n\n# we prefer those closer in geography to the US. they're triaged in\n# order of ease of use, reliability, and then geography. the mirror\n# list is at https://docs.freebsd.org/en/books/handbook/mirrors/.\n# these mirrors were known to work as of 2022-11-27. this does\n# not include any mirrors that are known to be rate-limited or\n# commercial. everything returns HTML output.\nMIRRORS=(\n    # this is a guaranteed mirror, unlike those below.\n    \"http://ftp.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp11.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp3.br.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.uk.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.nl.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp6.fr.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp1.de.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.de.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp5.de.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.ru.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.gr.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp4.za.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp2.za.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp4.tw.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp3.jp.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp6.jp.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.uk.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.nl.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.fr.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.at.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.dk.freebsd.org/FreeBSD/releases\"\n    \"http://ftp.cz.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.se.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.lv.freebsd.org/freebsd/releases\"\n    \"http://ftp.pl.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.ua.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.gr.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.ru.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.nz.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.kr.freebsd.org/pub/FreeBSD/releases\"\n    \"http://ftp.jp.freebsd.org/pub/FreeBSD/releases\"\n)\n\nmax_freebsd() {\n    local best=\n    local minor=0\n    local version=\n    local release_major=\n    local release_minor=\n    for release in \"${@}\"; do\n        version=$(echo \"${release}\" | cut -d '-' -f 1)\n        release_major=$(echo \"${version}\"| cut -d '.' -f 1)\n        release_minor=$(echo \"${version}\"| cut -d '.' -f 2)\n        if [ \"${release_major}\" == \"${FREEBSD_MAJOR}\" ] && [ \"${release_minor}\" -gt \"${minor}\" ]; then\n            best=\"${release}\"\n            minor=\"${release_minor}\"\n        fi\n    done\n    if [[ -z \"$best\" ]]; then\n        echo -e \"\\e[31merror:\\e[0m could not find best release for FreeBSD ${FREEBSD_MAJOR}.\" 1>&2\n        exit 1\n    fi\n    echo \"${best}\"\n}\n\nlatest_freebsd() {\n    local mirror=\"${1}\"\n    local response=\n    local line=\n    local lines=\n    local releases=\n    local max_release=\n\n    response=$(curl -4 --retry 3 -sSfL \"${mirror}/${FREEBSD_ARCH}/\" | grep RELEASE)\n    if [[ \"${response}\" != *RELEASE* ]]; then\n        echo -e \"\\e[31merror:\\e[0m could not find a candidate release for FreeBSD ${FREEBSD_MAJOR}.\" 1>&2\n        exit 1\n    fi\n    readarray -t lines <<< \"${response}\"\n\n    # shellcheck disable=SC2016\n    local regex='/<a.*?>\\s*(\\d+\\.\\d+-RELEASE)\\s*\\/?\\s*<\\/a>/; print $1'\n    # not all lines will match: some return `*-RELEASE/` as a line\n    if [[ \"${response}\" == *\"<a\"* ]]; then\n        # have HTML output, need to extract it via a regex\n        releases=()\n        for line in \"${lines[@]}\"; do\n            if [[ \"${line}\" == *\"<a\"* ]]; then\n                # because of the pattern we're extracting, this can't split\n                # shellcheck disable=SC2207\n                releases+=($(echo \"${line}\" | perl -nle \"${regex}\"))\n            fi\n        done\n    else\n        releases=(\"${lines[@]}\")\n    fi\n\n    max_release=$(max_freebsd \"${releases[@]}\")\n    echo \"${max_release//-RELEASE/}\"\n}\n\n_freebsd_mirror() {\n    local mirror=\n    local code=\n\n    for mirror in \"${MIRRORS[@]}\"; do\n        # we need a timeout in case the server is down to avoid\n        # infinitely hanging. timeout error code is always 124\n        # these mirrors can be quite slow, so have a long timeout\n        timeout 20s curl -4 --retry 3 -sSfL \"${mirror}/${FREEBSD_ARCH}/\" >/dev/null\n        code=$?\n        if [[ \"${code}\" == 0 ]]; then\n            echo \"${mirror}\"\n            return 0\n        elif [[ \"${code}\" != 124 ]]; then\n            echo -e \"\\e[1;33mwarning:\\e[0m mirror ${mirror} does not seem to work.\" 1>&2\n        fi\n    done\n\n    echo -e \"\\e[31merror:\\e[0m could not find a working FreeBSD mirror.\" 1>&2\n    exit 1\n}\n\nfreebsd_mirror() {\n    set +e\n    _freebsd_mirror\n    code=$?\n    set -e\n\n    return \"${code}\"\n}\n\nmirror=$(freebsd_mirror)\nbase_release=$(latest_freebsd \"${mirror}\")\nbsd_url=\"${mirror}/${FREEBSD_ARCH}/${base_release}-RELEASE\"\n\nmain() {\n    local binutils=2.40 \\\n        gcc=13.3.0 \\\n        target=\"${ARCH}-unknown-freebsd${FREEBSD_MAJOR}\"\n\n    install_packages ca-certificates \\\n        curl \\\n        g++ \\\n        make \\\n        wget \\\n        texinfo \\\n        xz-utils \\\n        bzip2\n\n    local td\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    mkdir \"${td}\"/{binutils,gcc}{,-build} \"${td}/freebsd\"\n\n    download_binutils \"${binutils}\" \"gz\"\n    tar -C \"${td}/binutils\" --strip-components=1 -xf \"binutils-${binutils}.tar.gz\"\n\n    download_gcc \"${gcc}\" \"gz\"\n    tar -C \"${td}/gcc\" --strip-components=1 -xf \"gcc-${gcc}.tar.gz\"\n\n    cd gcc\n    sed -i -e 's/ftp:/https:/g' ./contrib/download_prerequisites\n    ./contrib/download_prerequisites\n    cd ..\n\n    curl -4 --retry 3 -sSfL \"${bsd_url}/base.txz\" -O\n    tar -C \"${td}/freebsd\" -xJf base.txz ./usr/include ./usr/lib ./lib\n\n    cd binutils-build\n    ../binutils/configure \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    cd ..\n\n    local destdir=\"/usr/local/${target}\"\n    cp -r \"${td}/freebsd/usr/include\" \"${destdir}\"\n    cp -r \"${td}/freebsd/lib/\"* \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib/libc++.so.1\" \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib/libc++.a\" \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib/libcxxrt.a\" \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib/libcxxrt.so\" \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib/libcompiler_rt.a\" \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib\"/lib{c,util,m,ssp_nonshared,memstat}.a \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib\"/lib{rt,execinfo,procstat}.so \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib\"/libmemstat.so.3 \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib\"/{crt1,Scrt1,crti,crtn}.o \"${destdir}/lib\"\n    cp \"${td}/freebsd/usr/lib\"/libkvm.a \"${destdir}/lib\"\n\n    local lib=\n    local base=\n    local link=\n    for lib in \"${destdir}/lib/\"*.so.*; do\n        base=$(basename \"${lib}\")\n        link=\"${base}\"\n        # not strictly necessary since this will always work, but good fallback\n        while [[ \"${link}\" == *.so.* ]]; do\n            link=\"${link%.*}\"\n        done\n\n        # just extra insurance that we won't try to overwrite an existing file\n        local dstlink=\"${destdir}/lib/${link}\"\n        if [[ -n \"${link}\" ]] && [[ \"${link}\" != \"${base}\" ]] && [[ ! -f \"${dstlink}\" ]]; then\n            ln -s \"${base}\" \"${dstlink}\"\n        fi\n    done\n\n    ln -s libthr.so.3 \"${destdir}/lib/libpthread.so\"\n\n    cd gcc-build\n    ../gcc/configure \\\n        --disable-libada \\\n        --disable-libcilkrt \\\n        --disable-libcilkrts \\\n        --disable-libgomp \\\n        --disable-libquadmath \\\n        --disable-libquadmath-support \\\n        --disable-libsanitizer \\\n        --disable-libssp \\\n        --disable-libvtv \\\n        --disable-lto \\\n        --disable-nls \\\n        --disable-multilib \\\n        --enable-languages=c,c++,fortran \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    cd ..\n\n    # clean up\n    popd\n\n    purge_packages\n\n    # store the version info for the FreeBSD release\n    bsd_revision=$(curl -4 --retry 3 -sSfL \"${bsd_url}/REVISION\")\n    echo \"${base_release} (${bsd_revision})\" > /opt/freebsd-version\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/illumos.sh",
    "content": "#!/usr/bin/env bash\n# This script is based off of rust-lang/rust's implementation.\n#   https://github.com/rust-lang/rust/blob/47f291ec2d9d6e4820cca517e69b3efddec40c20/src/ci/docker/scripts/illumos-toolchain.sh\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local arch=\"${1}\"\n    local binutils=2.38\n    local gcc=8.4.0\n    local target=\"${arch}-unknown-illumos\"\n    local build_target=\"${arch}-pc-solaris2.10\"\n    local prefix=\"/usr/local/${target}\"\n    local sysroot_dir=\"${prefix}/sysroot\"\n    local real_sum\n\n    install_packages ca-certificates \\\n        curl \\\n        g++ \\\n        make \\\n        texinfo \\\n        wget \\\n        xz-utils\n\n    local td\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    mkdir \"${td}\"/{binutils,gcc}{,-build} \"${td}/illumos\"\n\n    local binutils_sum=\"e316477a914f567eccc34d5d29785b8b0f5a10208d36bbacedcc39048ecfe024\"\n    download_binutils \"${binutils}\" \"xz\"\n    real_sum=$(sha256sum \"binutils-${binutils}.tar.xz\" | cut -d ' ' -f 1)\n    if [[ \"${binutils_sum}\" != \"${real_sum}\" ]]; then\n        echo \"Error: invalid hash for binutils.\" >&2\n        exit 1\n    fi\n    tar -C \"${td}/binutils\" --strip-components=1 -xJf \"binutils-${binutils}.tar.xz\"\n\n    local gcc_sum=\"e30a6e52d10e1f27ed55104ad233c30bd1e99cfb5ff98ab022dc941edd1b2dd4\"\n    download_gcc \"${gcc}\" \"xz\"\n    real_sum=$(sha256sum \"gcc-${gcc}.tar.xz\" | cut -d ' ' -f 1)\n    if [[ \"${gcc_sum}\" != \"${real_sum}\" ]]; then\n        echo \"Error: invalid hash for gcc.\" >&2\n        exit 1\n    fi\n    tar -C \"${td}/gcc\" --strip-components=1 -xJf \"gcc-${gcc}.tar.xz\"\n\n    pushd gcc\n    sed -i -e 's/ftp:/https:/g' ./contrib/download_prerequisites\n    ./contrib/download_prerequisites\n    popd\n\n    local mach\n    case \"${arch}\" in\n        x86_64)\n            mach='i386'\n            ;;\n        *)\n            echo \"ERROR: unknown architecture: ${arch}\" >&2\n            exit 1\n            ;;\n    esac\n\n    local sysroot_version=\"20181213-de6af22ae73b-v1\"\n    local sysroot_file=\"illumos-sysroot-${mach}-${sysroot_version}.tar.gz\"\n    local sysroot_repo=\"https://github.com/illumos/sysroot\"\n    local sysroot_sum=\"ee792d956dfa6967453cebe9286a149143290d296a8ce4b8a91d36bea89f8112\"\n    curl --retry 3 -sSfL \"${sysroot_repo}/releases/download/${sysroot_version}/${sysroot_file}\" -O\n    real_sum=$(sha256sum \"${sysroot_file}\" | cut -d ' ' -f 1)\n    if [[ \"${sysroot_sum}\" != \"${real_sum}\" ]]; then\n        echo \"Error: invalid hash for illumos sysroot.\" >&2\n        exit 1\n    fi\n    mkdir -p \"${sysroot_dir}\"\n    pushd \"${sysroot_dir}\"\n    tar -xzf \"${td}/${sysroot_file}\"\n    popd\n\n    mkdir -p \"${prefix}\"\n    pushd binutils-build\n    ../binutils/configure \\\n        --target=\"${build_target}\" \\\n        --prefix=\"${prefix}\" \\\n        --program-prefix=\"${target}-\" \\\n        --with-sysroot=\"${sysroot_dir}\"\n    make \"-j$(nproc)\"\n    make install\n    popd\n\n    # note: solaris2.10 is obsolete, so we can't upgrade to GCC 10 till then.\n    # for gcc 9.4.0, need `--enable-obsolete`.\n    export CFLAGS='-fPIC'\n    export CXXFLAGS='-fPIC'\n    export CXXFLAGS_FOR_TARGET='-fPIC'\n    export CFLAGS_FOR_TARGET='-fPIC'\n    mkdir -p \"${prefix}\"\n    pushd gcc-build\n    ../gcc/configure \\\n        --prefix=\"${prefix}\" \\\n        --target=\"${build_target}\" \\\n        --program-prefix=\"${target}-\" \\\n        --with-sysroot=\"${sysroot_dir}\" \\\n        --enable-languages=c,c++,fortran \\\n        --disable-libada \\\n        --disable-libcilkrts \\\n        --disable-libgomp \\\n        --disable-libquadmath \\\n        --disable-libquadmath-support \\\n        --disable-libsanitizer \\\n        --disable-libssp \\\n        --disable-libvtv \\\n        --disable-lto \\\n        --disable-multilib \\\n        --disable-shared \\\n        --disable-nls \\\n        --enable-tls \\\n        --with-gnu-as \\\n        --with-gnu-ld\n    make \"-j$(nproc)\"\n    make install\n    popd\n\n    # clean up\n    popd\n\n    purge_packages\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/lib.sh",
    "content": "#!/usr/bin/env bash\n# shellcheck disable=SC2294\n\npurge_list=()\n\nset_centos_ulimit() {\n    # this is a bug affecting buildkit with yum when ulimit is unlimited\n    # https://github.com/docker/buildx/issues/379#issuecomment-1196517905\n    ulimit -n 1024000\n}\n\ninstall_packages() {\n    if grep -i ubuntu /etc/os-release; then\n        apt-get update\n\n        for pkg in \"${@}\"; do\n            if ! dpkg -L \"${pkg}\" >/dev/null 2>/dev/null; then\n                apt-get install --assume-yes --no-install-recommends \"${pkg}\"\n\n                purge_list+=( \"${pkg}\" )\n            fi\n        done\n    else\n        set_centos_ulimit\n        for pkg in \"${@}\"; do\n            if ! yum list installed \"${pkg}\" >/dev/null 2>/dev/null; then\n                yum install -y \"${pkg}\"\n\n                purge_list+=( \"${pkg}\" )\n            fi\n        done\n    fi\n}\n\npurge_packages() {\n    if (( ${#purge_list[@]} )); then\n        if grep -i ubuntu /etc/os-release; then\n            apt-get purge --assume-yes --auto-remove \"${purge_list[@]}\"\n        else\n            yum remove -y \"${purge_list[@]}\"\n        fi\n    fi\n}\n\nif_centos() {\n    if grep -q -i centos /etc/os-release; then\n        eval \"${@}\"\n    fi\n}\n\nif_ubuntu() {\n    if grep -q -i ubuntu /etc/os-release; then\n        eval \"${@}\"\n    fi\n}\n\nif_ubuntu_ge() {\n    if grep -q -i ubuntu /etc/os-release; then\n        local ver\n        ver=\"$(source /etc/os-release; echo $VERSION_ID)\"\n        if dpkg --compare-versions \"$ver\" \"ge\" \"$1\"; then\n            shift\n            eval \"${@}\"\n        fi\n    fi\n}\n\n\nGNU_MIRRORS=(\n    \"https://ftp.gnu.org/gnu/\"\n    \"https://ftpmirror.gnu.org/\"\n)\n\ndownload_mirrors() {\n    local relpath=\"${1}\"\n    shift\n    local filename=\"${1}\"\n    shift\n\n    for mirror in \"${@}\"; do\n        if curl -4 --retry 3 -sSfL \"${mirror}/${relpath}/${filename}\" -O; then\n            break\n        fi\n    done\n    if [[ ! -f \"${filename}\" ]]; then\n        echo \"Unable to download ${filename}\" >&2\n        exit 1\n    fi\n}\n\ndownload_binutils() {\n    local mirror\n    local version=\"${1}\"\n    local ext=\"${2}\"\n    local filename=\"binutils-${version}.tar.${ext}\"\n\n    download_mirrors \"binutils\" \"${filename}\" \"${GNU_MIRRORS[@]}\"\n}\n\ndownload_gcc() {\n    local mirror\n    local version=\"${1}\"\n    local ext=\"${2}\"\n    local filename=\"gcc-${version}.tar.${ext}\"\n\n    download_mirrors \"gcc/gcc-${version}\" \"${filename}\" \"${GNU_MIRRORS[@]}\"\n}\n\ndocker_to_qemu_arch() {\n    local arch=\"${1}\"\n    case \"${arch}\" in\n        arm64)\n            echo \"aarch64\"\n            ;;\n        386)\n            echo \"i386\"\n            ;;\n        amd64)\n            echo \"x86_64\"\n            ;;\n        arm|ppc64le|riscv64|s390x)\n            echo \"${arch}\"\n            ;;\n        *)\n            echo \"Unknown Docker image architecture, got \\\"${arch}\\\".\" >&2\n            exit 1\n            ;;\n    esac\n}\n\ndocker_to_linux_arch() {\n    # variant may not be provided\n    local oldstate\n    oldstate=\"$(set +o)\"\n    set +u\n\n    local arch=\"${1}\"\n    local variant=\"${2}\"\n    case \"${arch}\" in\n        arm64)\n            echo \"aarch64\"\n            ;;\n        386)\n            echo \"i686\"\n            ;;\n        amd64)\n            echo \"x86_64\"\n            ;;\n        ppc64le)\n            echo \"powerpc64le\"\n            ;;\n        arm)\n            case \"${variant}\" in\n                v6)\n                    echo \"arm\"\n                    ;;\n                \"\"|v7)\n                    echo \"armv7\"\n                    ;;\n                *)\n                    echo \"Unknown Docker image variant, got \\\"${variant}\\\".\" >&2\n                    exit 1\n                    ;;\n            esac\n            ;;\n        riscv64|s390x)\n            echo \"${arch}\"\n            ;;\n        *)\n            echo \"Unknown Docker image architecture, got \\\"${arch}\\\".\" >&2\n            exit 1\n            ;;\n    esac\n\n    eval \"${oldstate}\"\n}\n\nfind_argument() {\n    # Extracts the value from an argument of the form VARIABLE=VALUE\n    local needle=\"$1\"\n    local return_var=\"$2\"\n    shift 2\n    local prefix=\"${needle}=\"\n    for var in \"${@}\"; do\n        case \"$var\" in\n            \"$prefix\"*)\n                eval \"$return_var=${var#\"${prefix}\"}\"\n                return 0 ;;\n            *)           ;;\n        esac\n    done\n    echo \"Missing argument ${needle}\"\n    exit 1\n}\n\nsymlinkify_if_same() {\n    local file1=\"$1\"\n    local file2=\"$2\"\n    # Only make a symlink if the files are identical, and the destination file isn't already a symlink\n    if [ ! -L \"${file2}\" ] && cmp \"$file1\" \"$file2\"; then\n        ln -sf \"$file1\" \"$file2\"\n    fi\n}\n\nsymlinkify_and_strip_toolchain() {\n    local target=\"$1\"\n    local gcc_ver=\"$2\"\n\n    local target_bin=\"/usr/local/${target}/bin\"\n    local local_bin=\"/usr/local/bin\"\n\n    # The first set of tools appear as /usr/local/bin/<target>-<tool> and /usr/local/<target>/bin/<tool>\n\n    # Special case: ld is itself usually hardlinked to ld.bfd\n    symlinkify_if_same \"${local_bin}/ld\" \"${local_bin}/ld.bfd\"\n\n    # Turn hard links or otherwise identical files into symlinks\n    for tool in ar  as  ld  ld.bfd  nm  objcopy  objdump  ranlib  readelf  strip; do\n        local src=\"${local_bin}/${target}-${tool}\"\n        local dest=\"${target_bin}/${tool}\"\n        symlinkify_if_same \"${src}\" \"${dest}\"\n        strip \"${src}\"\n    done\n\n    # The second set of tools only appear as /usr/local/bin/<target>-<tool>\n\n    # Special case: c++ and g++ are usually the same file\n    symlinkify_if_same \"${local_bin}/${target}-c++\" \"${local_bin}/${target}-g++\"\n    # Special case: gcc and gcc-<version>\n    symlinkify_if_same \"${local_bin}/${target}-gcc\" \"${local_bin}/${target}-gcc-${gcc_ver}\"\n\n    for tool in  addr2line  c++ c++filt  cpp  elfedit  g++  gcc  gcc-${gcc_ver}  gcc-ar  gcc-nm  gcc-ranlib  gcov  gcov-dump  gcov-tool  gfortran  gprof  size  strings; do\n        strip \"${local_bin}/${target}-${tool}\"\n    done\n}\n"
  },
  {
    "path": "docker/linux-image.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmax_kernel_version() {\n    # kernel versions have the following format:\n    #   `5.10.0-10-$arch`, where the `$arch` may be optional.\n    local IFS=$'\\n'\n    local -a versions\n    local major=0\n    local minor=0\n    local patch=0\n    local release=0\n    local index=0\n    local version\n    local x\n    local y\n    local z\n    local r\n    local is_larger\n\n    read -r -d '' -a versions <<<\"$1\"\n    for i in \"${!versions[@]}\"; do\n        version=\"${versions[$i]}\"\n        x=$(echo \"$version\" | cut -d '.' -f 1)\n        y=$(echo \"$version\" | cut -d '.' -f 2)\n        z=$(echo \"$version\" | cut -d '.' -f 3 | cut -d '-' -f 1)\n        r=$(echo \"$version\" | cut -d '-' -f 2)\n        is_larger=\n\n        if [ \"$x\" -gt \"$major\" ]; then\n            is_larger=1\n        elif [ \"$x\" -eq \"$major\" ] && [ \"$y\" -gt \"$minor\" ]; then\n            is_larger=1\n        elif [ \"$x\" -eq \"$major\" ] && [ \"$y\" -eq \"$minor\" ] && [ \"$z\" -gt \"$patch\" ]; then\n            is_larger=1\n        elif [ \"$x\" -eq \"$major\" ] && [ \"$y\" -eq \"$minor\" ] && [ \"$z\" -eq \"$patch\" ] && [ \"$r\" -gt \"$release\" ]; then\n            is_larger=1\n        fi\n\n        if [ -n \"$is_larger\" ]; then\n            index=\"$i\"\n            major=\"$x\"\n            minor=\"$y\"\n            patch=\"$z\"\n            release=\"$r\"\n        fi\n    done\n\n    echo \"${versions[index]}\"\n}\n\nmain() {\n    # arch in the rust target\n    local arch=\"${1}\" \\\n        kversion=5.10.0-34\n\n    local debsource=\"deb http://http.debian.net/debian/ bullseye main\"\n    debsource=\"${debsource}\\ndeb http://security.debian.org/ bullseye-security main\"\n\n    local dropbear=\"dropbear-bin\"\n\n    local -a deps\n    local kernel=\n    local libgcc=\"libgcc-s1\"\n    local ncurses=\n\n    # select debian arch and kernel version\n    case \"${arch}\" in\n    aarch64)\n        arch=arm64\n        kernel=\"${kversion}-arm64\"\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    armv7)\n        arch=armhf\n        kernel='5.*-armmp'\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    i686)\n        arch=i386\n        kernel=\"${kversion}-686\"\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    mips)\n        # mips was discontinued in bullseye, so we have to use buster.\n        libgcc=\"libgcc1\"\n        debsource=\"deb http://http.debian.net/debian/ buster main\"\n        debsource=\"${debsource}\\ndeb http://security.debian.org/ buster/updates main\"\n        kernel='4.*-4kc-malta'\n        ncurses=\"=6.1*\"\n        ;;\n    mipsel)\n        kernel='5.*-4kc-malta'\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    mips64el)\n        kernel='5.*-5kc-malta'\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    powerpc)\n        # there is no buster powerpc port, so we use jessie\n        # use a more recent kernel from backports\n        kversion='4.9.0-0.bpo.6'\n        kernel=\"${kversion}-powerpc\"\n        debsource=\"deb http://archive.debian.org/debian jessie main\"\n        debsource=\"${debsource}\\ndeb http://archive.debian.org/debian jessie-backports main\"\n        debsource=\"${debsource}\\ndeb http://ftp.ports.debian.org/debian-ports unstable main\"\n        debsource=\"${debsource}\\ndeb http://ftp.ports.debian.org/debian-ports unreleased main\"\n\n        # archive.debian.org Release files are expired.\n        echo \"Acquire::Check-Valid-Until false;\" | tee -a /etc/apt/apt.conf.d/10-nocheckvalid\n        echo \"APT::Get::AllowUnauthenticated true;\" | tee -a /etc/apt/apt.conf.d/10-nocheckvalid\n        echo \"Acquire::AllowInsecureRepositories True;\" | tee -a /etc/apt/apt.conf.d/10-nocheckvalid\n\n        dropbear=\"dropbear\"\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    powerpc64)\n        # there is no stable port\n        arch=ppc64\n        # https://packages.debian.org/en/sid/linux-image-powerpc64\n        kernel='6.*-powerpc64'\n        debsource=\"deb http://ftp.ports.debian.org/debian-ports unstable main\"\n        debsource=\"${debsource}\\ndeb http://ftp.ports.debian.org/debian-ports unreleased main\"\n        # sid version of dropbear requires these dependencies\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    powerpc64le)\n        arch=ppc64el\n        kernel='5.*-powerpc64le'\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    riscv64)\n        kernel='6.*-riscv64'\n        debsource=\"deb http://deb.debian.org/debian unstable main\"\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    s390x)\n        arch=s390x\n        kernel='5.*-s390x'\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    sparc64)\n        # there is no stable port\n        # https://packages.debian.org/en/sid/linux-image-sparc64\n        kernel='6.*-sparc64'\n        debsource=\"deb http://ftp.ports.debian.org/debian-ports unstable main\"\n        debsource=\"${debsource}\\ndeb http://ftp.ports.debian.org/debian-ports unreleased main\"\n        # sid version of dropbear requires these dependencies\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    x86_64)\n        arch=amd64\n        kernel=\"${kversion}-amd64\"\n        deps=(libcrypt1:\"${arch}\")\n        ;;\n    *)\n        echo \"Invalid arch: ${arch}\"\n        exit 1\n        ;;\n    esac\n\n    install_packages ca-certificates \\\n        curl \\\n        cpio \\\n        sharutils \\\n        gnupg\n\n    # conflicting versions of some packages will be installed already for the host platform,\n    # we need to remove the system installs later. since apt relies\n    # on these packages, we need to download them and reinstall\n    # using dpkg later, since we cannot redownload via apt.\n    local dpkg_arch\n    dpkg_arch=$(dpkg --print-architecture)\n    local libgcc_packages=(\"${libgcc}:${arch}\" \"libstdc++6:${arch}\")\n    if [[ \"${arch}\" == \"${dpkg_arch}\" ]]; then\n        local libgcc_root=/qemu/libgcc\n        mkdir -p \"${libgcc_root}\"\n        pushd \"${libgcc_root}\"\n        apt-get -d --no-install-recommends download \"${libgcc_packages[@]}\"\n        popd\n    fi\n\n    # Download packages\n    mv /etc/apt/sources.list /etc/apt/sources.list.bak\n    mv /etc/apt/sources.list.d /etc/apt/sources.list.d.bak\n    echo -e \"${debsource}\" >/etc/apt/sources.list\n\n    # Old ubuntu does not support --add-architecture, so we directly change multiarch file\n    if [ -f /etc/dpkg/dpkg.cfg.d/multiarch ]; then\n        cp /etc/dpkg/dpkg.cfg.d/multiarch /etc/dpkg/dpkg.cfg.d/multiarch.bak\n    fi\n    dpkg --add-architecture \"${arch}\" || echo \"foreign-architecture ${arch}\" >/etc/dpkg/dpkg.cfg.d/multiarch\n\n    # Add Debian keys.\n    curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/archive-key-{7.0,8,9,10,11,12}.asc' -O\n    curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/archive-key-{8,9,10,11,12}-security.asc' -O\n    curl --retry 3 -sSfL 'https://ftp-master.debian.org/keys/release-{7,8,9,10,11,12}.asc' -O\n    curl --retry 3 -sSfL 'https://www.ports.debian.org/archive_{2020,2021,2022,2023,2024,2025}.key' -O\n\n    for key in *.asc *.key; do\n        apt-key add \"${key}\"\n        rm \"${key}\"\n    done\n\n    # allow apt-get to retry downloads\n    echo 'APT::Acquire::Retries \"3\";' >/etc/apt/apt.conf.d/80-retries\n\n    apt-get update\n\n    mkdir -p \"/qemu/${arch}\"\n    chmod 777 /qemu \"/qemu/${arch}\"\n\n    # Need to limit the kernel version and select the best version\n    # if we have a wildcard. This is because some matches, such as\n    # `linux-image-4.*-4kc-malta` can match more than 1 package,\n    # which will prevent further steps from working.\n    if [[ \"$kernel\" == *'*'* ]]; then\n        # Need an exact match for start and end, to avoid debug kernels.\n        # Afterwards, need to do a complex sort for the best kernel version,\n        # since the sort is non-trivial and must extract subcomponents.\n        packages=$(apt-cache search ^linux-image-\"$kernel$\" --names-only)\n        names=$(echo \"$packages\" | cut -d ' ' -f 1)\n        kversions=\"${names//linux-image-/}\"\n        kernel=$(max_kernel_version \"$kversions\")\n    fi\n\n    cd \"/qemu/${arch}\"\n    apt-get -d --no-install-recommends download \\\n        ${deps[@]+\"${deps[@]}\"} \\\n        \"busybox:${arch}\" \\\n        \"${dropbear}:${arch}\" \\\n        \"libtommath1:${arch}\" \\\n        \"libtomcrypt1:${arch}\" \\\n        \"libgmp10:${arch}\" \\\n        \"libc6:${arch}\" \\\n        \"linux-image-${kernel}:${arch}\" \\\n        ncurses-base\"${ncurses}\" \\\n        \"zlib1g:${arch}\"\n\n    if [[ \"${arch}\" != \"${dpkg_arch}\" ]]; then\n        apt-get -d --no-install-recommends download \"${libgcc_packages[@]}\"\n    else\n        # host arch has conflicting versions of the packages installed\n        # this prevents us from downloading them, so we need to\n        # simply grab the last version from the debian sources.\n        # we're search for a paragraph with:\n        #   Maintainer: Debian\n        # but not\n        #   Original-Maintainer: Debian\n        #\n        # then, we extract the version record and download **only**\n        # packages matching that specific version.\n        local version_info\n        local version_record\n        local version\n        for package in \"${libgcc_packages[@]}\"; do\n            version_info=$(apt-cache show \"${package}\")\n            version_record=$(echo \"${version_info}\" | perl -n00e 'print if /^Maintainer: Debian/m')\n            version=$(echo \"${version_record}\" | grep 'Version: ' | cut -d ' ' -f 2)\n            apt-get -d --no-install-recommends download \"${package}=${version}\"\n        done\n\n        # now, if we don't remove the system installs, qemu-system won't\n        # be able to find these libgcc packages after building, since it\n        # will prefer the system packages, which it can't find later.\n        # removing these packages needs to occur after download via apt,\n        # since apt-get relies on libgcc_s1 and libstdc++6.\n        dpkg -r --force-depends \"${libgcc_packages[@]}\"\n    fi\n    cd /qemu\n\n    # Install packages\n    root=\"root-${arch}\"\n    mkdir -p \"${root}\"/{bin,etc/dropbear,root,sys,dev,proc,sbin,tmp,usr/{bin,sbin},var/log}\n    for deb in \"${arch}\"/*deb; do\n        dpkg -x \"${deb}\" \"${root}\"/\n    done\n\n    cp \"${root}/boot/vmlinu\"* kernel\n\n    # initrd\n    mkdir -p \"${root}/modules\"\n    if ls -d \"${root}/usr/lib/modules\"/*/kernel; then\n        prefix='/usr'\n    else\n        prefix=''\n    fi\n    cp -v \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/drivers/net/net_failover.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/drivers/net/virtio_net.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/drivers/virtio/* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/fs/netfs/netfs.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/fs/9p/9p.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/fs/fscache/fscache.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/net/9p/9pnet.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/net/9p/9pnet_virtio.ko* \\\n        \"${root}${prefix}/lib/modules\"/*/kernel/net/core/failover.ko* \\\n        \"${root}/modules\" || true # some file may not exist\n    rm -rf \"${root:?}/boot\"\n    rm -rf \"${root:?}${prefix}/lib/modules\"\n\n    cat <<'EOF' >\"${root}/etc/hosts\"\n127.0.0.1 localhost qemu\nEOF\n\n    cat <<'EOF' >\"$root/etc/hostname\"\nqemu\nEOF\n\n    cat <<'EOF' >\"$root/etc/passwd\"\nroot::0:0:root:/root:/bin/sh\nEOF\n\n    cat <<'EOF' | uudecode -o \"$root/etc/dropbear/dropbear_rsa_host_key\"\nbegin 600 dropbear_rsa_host_key\nM````!W-S:\"UR<V$````#`0`!```!`0\"N!-<%K,3Z.!Z,OEMB2.N\\O.$IWQ*F\nM#5%(_;(^2YKY_J_.RQW/7U@_MK&J#!Z0_\\;EH#98ZW*E1\\.<FF%P/*Y.W56-\nM31.'EJE`TN@=T5EC(8\"Y%3'ZBYH)^WIVJ]S*G/_;#RH\\_?S\"U^1L_<<.F`O+\nMZVI?*]\\KTDOT&QV0#B-M;\"%_7:\\>+3[X=QMH,B<HM$+0E[\\B6*^!XKLR@V,K\nM)<V80HHK:_#;D]26XKN&CB./EZAC%4)78R!G\"\"4HT@UK<5I4B^$/\"\"`,?*\\T\nM>*4$RYULV,V3X6]K:7@Q?80\"#WXGGQZNFN6CZ7LTDX(F6J[\\]F5<0`HEOF:Z\nMX;^53`L'4I/A```!``$L:$Z*#6<^3@+O%.[-#/5H+.C'3\\#QQZN[1;J>L`8I\nMZ_&T'!\"J'/Y+?R?55G:M^=]R*-&I3TOJYZA8@&H51ZOAF59'1_>>Z@?E4#)$\nMQU)X/RWH51ZB5KSDWJS:D'7GD(!?NAY`C'7\\)I:_4)J\")QBV/P\"RJQGHG'%B\nM1BT2LE6676>`1K,0\\NIMZTKQNB(IC+88<7#8%_-=P<&6<\"9LH>60TSS?3?-C\nMN`T36YB/3^<(Q;`N1NT>I9EZS`BAC^-?.:,R\\7EL\"<4>7E=]^1]B\\K9])AQU\nMBM\\]M;4V(S(6KH-I.4[6>9E+@\\UEM.J6:[2LUEEJDG:G:+:/EVF^Y75@(S$`\nM``\"!`.O+KW=&*CBCHL\"11&SVO4/K]$R-]7MV7,3RR)Q[X'0;6.?4JHW!3VR6\nM*FGBY--37ZD-+UV.8_+\"$<?B\"#&K$.[V)F7V2\\UY!7(0FZ@A2`0ADDY*J-_B\nM4AU&.*GP#F/!I([:?E],.>6PH9)(/E.\\G19#G0K`LRM?JWS!58&;D0C1````\nM@0\"\\[@NYWSTW(?Q@:_A*1Y3/AKYO5?S=0\"<2>#V-AH6W-NCSDTSRP=2D79FS\nM\"D?[;.)V>8'#9&I3\"MU@+:2\\Z%$0-MG0+J'(0>T1_C6?*C=4U0I$DI<=@D]1\nH_&DE8Y(OT%%EPG]!$H&5HX*),_D1A2\\P=R.7G'`0L%YM-79Y\"T\">$0``\n`\nend\nEOF\n\n    # dropbear complains when this file is missing\n    touch \"${root}/var/log/lastlog\"\n\n    if [[ -e \"${root}/usr/bin/busybox\" ]]; then\n        busybox='/usr/bin/busybox'\n    else\n        busybox='/bin/busybox'\n    fi\n    cat <<EOF >\"${root}/init\"\n#!${busybox} sh\n\nset -e\n\n${busybox} --install\n\nmount -t devtmpfs devtmpfs /dev\nmount -t proc none /proc\nmount -t sysfs none /sys\nmkdir /dev/pts\nmount -t devpts none /dev/pts/\n\n# some archs does not have virtio modules\n# fscache is builtin on riscv64\ninsmod /modules/failover.ko || insmod /modules/failover.ko.xz || true\ninsmod /modules/net_failover.ko || insmod /modules/net_failover.ko.xz || true\ninsmod /modules/virtio.ko || insmod /modules/virtio.ko.xz || true\ninsmod /modules/virtio_ring.ko || insmod /modules/virtio_ring.ko.xz || true\ninsmod /modules/virtio_mmio.ko || insmod /modules/virtio_mmio.ko.xz || true\ninsmod /modules/virtio_pci_legacy_dev.ko || insmod /modules/virtio_pci_legacy_dev.ko.xz || true\ninsmod /modules/virtio_pci_modern_dev.ko || insmod /modules/virtio_pci_modern_dev.ko.xz || true\ninsmod /modules/virtio_pci.ko || insmod /modules/virtio_pci.ko.xz || true\ninsmod /modules/virtio_net.ko || insmod /modules/virtio_net.ko.xz || true\ninsmod /modules/netfs.ko || insmod /modules/netfs.ko.xz || true\ninsmod /modules/fscache.ko || insmod /modules/fscache.ko.xz || true\ninsmod /modules/9pnet.ko || insmod /modules/9pnet.ko.xz\ninsmod /modules/9pnet_virtio.ko || insmod /modules/9pnet_virtio.ko.xz || true\ninsmod /modules/9p.ko || insmod /modules/9p.ko.xz\n\nifconfig lo 127.0.0.1\nifconfig eth0 10.0.2.15\nroute add default gw 10.0.2.2 eth0\n\nmkdir /target\nmount -t 9p -o trans=virtio target /target -oversion=9p2000.u || true\n\nexec dropbear -F -E -B\nEOF\n\n    if [[ \"${arch}\" == \"riscv64\" ]]; then\n        # Symlink dynamic loader to /lib/ld-linux-riscv64-lp64d.so.1\n        mkdir -p \"${root}/lib\"\n        ln -s /usr/lib/riscv64-linux-gnu/ld-linux-riscv64-lp64d.so.1 \"${root}/lib/ld-linux-riscv64-lp64d.so.1\"\n    fi\n\n    chmod +x \"${root}/init\"\n    cd \"${root}\"\n    find . | cpio --create --format='newc' --quiet | gzip >../initrd.gz\n    cd -\n\n    if [[ \"${arch}\" == \"${dpkg_arch}\" ]]; then\n        # need to reinstall these packages, since basic utilities rely on them.\n        pushd \"${libgcc_root}\"\n        dpkg -i --force-depends \"${libgcc_root}\"/*.deb\n        popd\n        rm -rf \"${libgcc_root}\"\n    fi\n\n    # Clean up\n    rm -rf \"/qemu/${root}\" \"/qemu/${arch}\"\n    mv -f /etc/apt/sources.list.bak /etc/apt/sources.list\n    mv -f /etc/apt/sources.list.d.bak /etc/apt/sources.list.d\n    if [ -f /etc/dpkg/dpkg.cfg.d/multiarch.bak ]; then\n        mv /etc/dpkg/dpkg.cfg.d/multiarch.bak /etc/dpkg/dpkg.cfg.d/multiarch\n    fi\n    if [ -f /etc/apt/apt.conf.d/10-nocheckvalid ]; then\n        rm /etc/apt/apt.conf.d/10-nocheckvalid\n    fi\n    # can fail if arch is used (image arch, such as amd64 and/or i386)\n    dpkg --remove-architecture \"${arch}\" || true\n    apt-get update\n\n    # need to reinstall the removed libgcc packages, which are required for apt\n    if [[ \"${arch}\" == \"${dpkg_arch}\" ]]; then\n        apt-get install --no-install-recommends --assume-yes \"${packages[@]}\"\n    fi\n\n    purge_packages\n\n    ls -lh /qemu\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/linux-runner",
    "content": "#!/usr/bin/env bash\n\nset -e\n\n# shellcheck disable=SC1091\n. /base-runner.sh\n\nLOG=/tmp/qemu.log\nLOCK=/tmp/qemu.lock\n\nif [ -n \"${CROSS_DEBUG}\" ]; then\n    set -x\nfi\n\n# arch in the rust target\narch=\"${1}\"\nshift\n\nif [[ -z \"${CROSS_RUNNER}\" ]]; then\n    if is_native_binary \"${arch}\"; then\n        CROSS_RUNNER=native\n    else\n        CROSS_RUNNER=qemu-user\n    fi\nfi\n\n# Ensure that the correct prefix is set even if the user has cleared the env.\n# `@DEFAULT_QEMU_LD_PREFIX@` is replaced during image build.\nexport QEMU_LD_PREFIX=${QEMU_LD_PREFIX:-@DEFAULT_QEMU_LD_PREFIX@}\n\nqarch=$(qemu_arch \"${arch}\")\ncase \"${CROSS_RUNNER}\" in\n    native)\n        exec \"${@}\"\n        ;;\n    qemu-user)\n        exec \"qemu-${qarch}\" \"${@}\"\n        ;;\n    qemu-system)\n        true\n        ;;\n    *)\n        echo \"Invalid runner: \\\"${CROSS_RUNNER}\\\"\";\n        echo \"Valid runners are: native, qemu-user and qemu-system\"\n        exit 1\n        ;;\nesac\n\nn=\"$(nproc)\"\nmemory=1G\ndriver9p=\"virtio-9p-pci\"\ndrivernet=\"virtio-net-pci\"\n\n# select qemu parameters\ncase \"${arch}\" in\n    aarch64|aarch64_be)\n        # 8 is the max number of cpu supported by qemu-aarch64\n        n=$(( n > 8 ? 8 : n ))\n        opt=\"-machine virt -cpu cortex-a57\"\n        ;;\n    armv7hf)\n        opt=\"-machine virt\"\n        driver9p=\"virtio-9p-device\"\n        drivernet=\"virtio-net-device\"\n        ;;\n    i686)\n        opt=\"-append console=ttyS0\"\n        ;;\n    mips|mipsel)\n        # avoid kernel error\n        # https://blahcat.github.io/2017/07/14/building-a-debian-stretch-qemu-image-for-mipsel/\n        opt=\"-append nokaslr\"\n        n=1\n        ;;\n    mips64el)\n        # avoid kernel error\n        # https://blahcat.github.io/2017/07/14/building-a-debian-stretch-qemu-image-for-mipsel/\n        opt=\"-append nokaslr -cpu MIPS64R2-generic\"\n        n=1\n        ;;\n    powerpc)\n        opt=\"-append console=ttyPZ0\"\n        n=1\n        ;;\n    riscv64)\n        opt=\"-machine virt\"\n        ;;\n    powerpc64|powerpc64le)\n        opt=\"-append console=hvc0 --nodefaults -serial stdio\"\n        ;;\n    s390x)\n        n=1\n        driver9p=\"virtio-9p-ccw\"\n        drivernet=\"virtio-net-ccw\"\n        ;;\n    sparc64)\n        n=1\n        driver9p+=\",bus=pciB\"\n        drivernet+=\",bus=pciB\"\n        ;;\n    x86_64)\n        opt=\"-append console=ttyS0\"\n        ;;\nesac\n\n(\n    flock -n 200 || exit 0\n\n    echo Booting QEMU virtual machine with $n cpus...\n\n    QEMU_CMD=\"qemu-system-${qarch} \\\n        -m ${memory} \\\n        -smp ${n} \\\n        -nographic \\\n        -monitor none \\\n        -netdev user,id=net0,hostfwd=tcp::10022-:22 \\\n        -device ${drivernet},netdev=net0 \\\n        -kernel /qemu/kernel \\\n        -initrd /qemu/initrd.gz \\\n        ${opt} \\\n        -fsdev local,id=fs0,path=/target,security_model=mapped \\\n        -device ${driver9p},fsdev=fs0,mount_tag=target\"\n\n    touch \"${LOG}\"\n    if [[ -n \"${CROSS_DEBUG}\" ]]; then\n        (${QEMU_CMD} 2>&1 | tee -a \"${LOG}\") &\n    else\n        ${QEMU_CMD} >> \"${LOG}\" 2>&1 &\n    fi\n    qemu_pid=$!\n\n    # wait for dropbear\n    for _ in $(seq 240); do\n        if grep -q \"Not backgrounding\" \"${LOG}\"; then\n            READY=1\n            break\n        elif ! (ps -p \"${qemu_pid}\" >/dev/null 2>&1); then\n            # qemu command failed and exited early\n            exit 1\n        fi\n        sleep 0.5s\n    done\n\n    if [ -z \"${READY}\" ]; then\n        if [ -n \"${CROSS_DEBUG}\" ]; then\n            echo \"Not ready but continuing because CROSS_DEBUG is set\"\n        else\n            echo \"Qemu is not ready after ${SECONDS} seconds...\"\n            echo \"Set the environment variable CROSS_DEBUG=1 to debug\"\n            echo \"Last 100 lines of qemu output:\"\n            tail -n 100 \"${LOG}\"\n            exit 1\n        fi\n    fi\n\n    echo \"Booted in ${SECONDS} seconds\"\n\n) 200>\"${LOCK}\"\n\nif [[ -t 1 ]] && [[ -t 2 ]]; then\n  tty_flag='-t'\nfi\n\nexec dbclient ${tty_flag} -p 10022 -y -y root@localhost \"${@}\"\n"
  },
  {
    "path": "docker/mingw.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    # Ubuntu mingw packages for i686 uses sjlj exceptions, but rust target\n    # i686-pc-windows-gnu uses dwarf exceptions. So we build mingw packages\n    # that are compatible with rust.\n\n    # Enable source\n    sed -i 's/# deb-src/deb-src/g' /etc/apt/sources.list\n    apt-get update\n\n    # Install mingw (with sjlj exceptions) to get the dependencies right\n    # Later we replace these packages with the new ones\n    apt-get install --assume-yes --no-install-recommends g++-mingw-w64-i686\n\n    local dependencies=(build-essential)\n    while IFS='' read -r dep; do dependencies+=(\"${dep}\"); done < \\\n        <(apt-cache showsrc gcc-mingw-w64-i686 | grep Build | cut -d: -f2 | tr , '\\n' | cut -d' ' -f2 | sort | uniq)\n\n    install_packages \"${dependencies[@]}\"\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    apt-get source gcc-mingw-w64-i686\n    pushd gcc-mingw-w64-*\n\n    # We are using dwarf exceptions instead of sjlj\n    sed -i -e 's/libgcc_s_sjlj-1/libgcc_s_dw2-1/g' debian/gcc-mingw-w64-i686.install.in\n\n    # Only build i686 packages (disable x86_64)\n    patch -p0 <<'EOF'\n--- debian/control.template.ori\t2018-03-12 16:25:30.000000000 +0000\n+++ debian/control.template\t2018-03-12 16:25:30.000000000 +0000\n@@ -1,7 +1,6 @@\n Package: @@PACKAGE@@-mingw-w64\n Architecture: all\n Depends: @@PACKAGE@@-mingw-w64-i686,\n-         @@PACKAGE@@-mingw-w64-x86-64,\n          ${misc:Depends}\n Recommends: @@RECOMMENDS@@\n Built-Using: gcc-@@VERSION@@ (= ${gcc:Version})\n@@ -32,22 +31,3 @@\n  This package contains the @@LANGUAGE@@ compiler, supporting\n  cross-compiling to 32-bit MinGW-w64 targets.\n Build-Profiles: <!stage1>\n-\n-Package: @@PACKAGE@@-mingw-w64-x86-64\n-Architecture: any\n-Depends: @@DEPENDS64@@,\n-         ${misc:Depends},\n-         ${shlibs:Depends}\n-Suggests: gcc-@@VERSION@@-locales (>= ${local:Version})\n-Breaks: @@BREAKS64@@\n-Conflicts: @@CONFLICTS64@@\n-Replaces: @@REPLACES64@@\n-Built-Using: gcc-@@VERSION@@ (= ${gcc:Version})\n-Description: GNU @@LANGUAGE@@ compiler for MinGW-w64 targeting Win64\n- MinGW-w64 provides a development and runtime environment for 32- and\n- 64-bit (x86 and x64) Windows applications using the Windows API and\n- the GNU Compiler Collection (gcc).\n- .\n- This package contains the @@LANGUAGE@@ compiler, supporting\n- cross-compiling to 64-bit MinGW-w64 targets.\n-Build-Profiles: <!stage1>\nEOF\n\n    # Disable build of fortran,objc,obj-c++ and use configure options\n    # --disable-sjlj-exceptions --with-dwarf2\n    patch -p0 <<'EOF'\n--- debian/rules.ori\t2018-03-12 16:25:30.000000000 +0000\n+++ debian/rules\t2018-03-12 16:25:30.000000000 +0000\n@@ -58,7 +58,7 @@\n     INSTALL_TARGET := install-gcc\n else\n # Build the full GCC.\n-    languages := c,c++,fortran,objc,obj-c++,ada\n+    languages := c,c++\n     BUILD_TARGET :=\n     INSTALL_TARGET := install install-lto-plugin\n endif\n@@ -85,7 +85,7 @@\n \tsed -i 's/@@VERSION@@/$(target_version)/g' debian/control\n \ttouch $@\n\n-targets := i686-w64-mingw32 x86_64-w64-mingw32\n+targets := i686-w64-mingw32\n threads := posix win32\n\n # Hardening on the host, none on the target\n@@ -220,6 +220,10 @@\n # Enable libatomic\n CONFFLAGS += \\\n \t--enable-libatomic\n+# Enable dwarf exceptions\n+CONFFLAGS += \\\n+\t--disable-sjlj-exceptions \\\n+\t--with-dwarf2\n # Enable experimental::filesystem and std::filesystem\n CONFFLAGS += \\\n \t--enable-libstdcxx-filesystem-ts=yes\nEOF\n\n    # Need symlinks for specific autoconf versions, since it\n    # attempts to use autoconf2.69 and autom4te2.69.\n    ln -s /usr/bin/autoconf /usr/bin/autoconf2.69\n    ln -s /usr/bin/autom4te /usr/bin/autom4te2.69\n\n    # Build the modified mingw packages\n    MAKEFLAGS=--silent dpkg-buildpackage -nc -B --jobs=auto\n\n    # Replace installed mingw packages with the new ones\n    dpkg -i ../g*-mingw-w64-i686*.deb ../gcc-mingw-w64-base*.deb\n\n    purge_packages\n\n    popd\n    popd\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n\n    # Unlink our temporary aliases\n    unlink /usr/bin/autoconf2.69\n    unlink /usr/bin/autom4te2.69\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/musl-gcc.sh",
    "content": "#!/bin/bash\n\n# this linker works around missing builtins in older rust versions.\n# we also have custom linker scripts for our static libstdc++ for all versions\n# which is found in `musl-symlink.sh`.\n#\n# for other targets, issues in older versions of compiler-builtins require\n# manually linking to libgcc to compensate for missing builtins.\n# target-specific details include:\n#\n# aarch64-unknown-linux-musl (fixed in 1.48)\n#   https://github.com/rust-lang/compiler-builtins/pull/377\n#\n# armv5te-unknown-linux-musleabi (fixed in 1.65)\n#   missing sync `sync_X_and_fetch`\n#   https://github.com/rust-lang/compiler-builtins/pull/484\n#\n# mips64-unknown-linux-muslabi64, mips64el-unknown-linux-muslabi64  (fixed in 1.65)\n#   missing soft-fp routine `__trunctfsf2`\n#   https://github.com/rust-lang/compiler-builtins/pull/483\n\nset -euo pipefail\n\nmain() {\n    local minor\n    local patched_minor=\"${CROSS_BUILTINS_PATCHED_MINOR_VERSION:-0}\"\n    minor=$(rustc_minor_version)\n\n    if [[ $# -eq 0 ]] || [[ \"${minor}\" -ge \"${patched_minor}\" ]]; then\n        exec \"${CROSS_TOOLCHAIN_PREFIX}\"gcc \"$@\"\n    else\n        exec \"${CROSS_TOOLCHAIN_PREFIX}\"gcc \"$@\" -lgcc -static-libgcc\n    fi\n}\n\n# FIXME: the rest of the contents of this file can be removed later on,\n# especially after 0.3.0 has been released so we can ensure everyone is\n# using a cross version at least as recent as images requiring the rust\n# versions provided as environment variables. these functions are wrappers\n# around these environment variables for backwards compatibility.\n# https://github.com/cross-rs/cross/issues/1046\n\n# NOTE: this will fail if rustc does not provide version\n# info, which may happen with a custom toolchain.\nrustc_version() {\n    rustc -Vv | grep '^release:' | cut -d ':' -f2\n}\n\nrustc_major_version() {\n    if [[ -z \"${CROSS_RUSTC_MAJOR_VERSION:-}\" ]]; then\n        CROSS_RUSTC_MAJOR_VERSION=$(rustc_version | cut -d '.' -f1)\n        export CROSS_RUSTC_MAJOR_VERSION\n    fi\n    echo \"${CROSS_RUSTC_MAJOR_VERSION}\"\n}\n\nrustc_minor_version() {\n    if [[ -z \"${CROSS_RUSTC_MINOR_VERSION:-}\" ]]; then\n        CROSS_RUSTC_MINOR_VERSION=$(rustc_version | cut -d '.' -f2)\n        export CROSS_RUSTC_MINOR_VERSION\n    fi\n    echo \"${CROSS_RUSTC_MINOR_VERSION}\"\n}\n\nrustc_patch_version() {\n    if [[ -z \"${CROSS_RUSTC_PATCH_VERSION:-}\" ]]; then\n        CROSS_RUSTC_PATCH_VERSION=$(rustc_version | cut -d '.' -f3)\n        export CROSS_RUSTC_PATCH_VERSION\n    fi\n    echo \"${CROSS_RUSTC_PATCH_VERSION}\"\n}\n\nmain \"$@\"\n"
  },
  {
    "path": "docker/musl-symlink.sh",
    "content": "#!/usr/bin/env bash\n# Create necessary symlinks for musl images to run\n# dynamically-linked binaries.\n# Just to be careful, we need this in a few locations,\n# relative to the musl sysroot.\n#   /lib/ld-musl-armhf.so\n#   /lib/ld-musl-armhf.so.1\n#   /usr/lib/ld.so\n#   /usr/lib/ld.so.1\n#   /usr/lib/libc.so\n#   /usr/lib/libc.so.1\n\nset -x\nset -euo pipefail\n\nmain() {\n    local sysroot=\"${1}\"\n    local arch=\"${2}\"\n    local src\n    local dst\n    local dsts\n\n    # ignore any failures here\n    local ld_arch=\"${arch//_/-}\"\n    mkdir -p \"$sysroot/usr/lib\"\n    src=\"$sysroot/lib/libc.so\"\n    dsts=(\n        \"/lib/ld-musl-${arch}.so\"\n        \"/lib/ld-musl-${arch}.so.1\"\n        \"$sysroot/lib/ld-musl-${arch}.so\"\n        \"$sysroot/lib/ld-musl-${arch}.so.1\"\n        \"$sysroot/usr/lib/ld.so\"\n        \"$sysroot/usr/lib/ld.so.1\"\n        \"$sysroot/usr/lib/libc.so\"\n        \"$sysroot/usr/lib/libc.so.1\"\n        # this specifically is a workaround for ARM64, which\n        # for some reason links to `ld-linux-aarch64.so`, but\n        # it is a valid musl binary. trying to use `libc6-dev:arm64`\n        # shows it has an invalid ELF header.\n        \"$sysroot/lib/ld-linux-${ld_arch}.so\"\n        \"$sysroot/lib/ld-linux-${ld_arch}.so.1\"\n    )\n    for dst in \"${dsts[@]}\"; do\n        # force a link if the dst does not exist or is broken\n        if [[ -L \"${dst}\" ]] && [[ ! -a \"${dst}\" ]]; then\n            ln -sf \"${src}\" \"${dst}\"\n        elif [[ ! -f \"${dst}\" ]]; then\n            ln -s \"${src}\" \"${dst}\"\n        fi\n    done\n\n    # ensure we statically link libstdc++, so avoid segfaults with c++\n    # https://github.com/cross-rs/cross/issues/902\n    rm \"${sysroot}\"/lib/libstdc++.so* || true\n\n    # now, we create a linker script that adds all the required dependencies\n    # because we link to a static libstdc++ to avoid runtime issues and\n    # with the shared libstdc++, we can have missing symbols that are reference\n    # in libstdc++, such as those from libc like `setlocale` and `__cxa_atexit`,\n    # as well as those from libgcc, like `__extendsftf2`. all musl targets\n    # can require symbols from libc, however, only the following are known\n    # to require symbols from libgcc:\n    #   - aarch64-unknown-linux-musl\n    #   - mips64-unknown-linux-muslabi64\n    #   - mips64el-unknown-linux-muslabi64\n    echo '/* cross-rs linker script\n * this allows us to statically link libstdc++ to avoid segfaults\n * https://github.com/cross-rs/cross/issues/902\n */\nGROUP ( libstdc++.a AS_NEEDED( -lgcc -lc -lm ) )\n' > \"${sysroot}\"/lib/libstdc++.so.6.0.27\n    ln -s libstdc++.so.6.0.27 \"${sysroot}\"/lib/libstdc++.so.6\n    ln -s libstdc++.so.6.0.27 \"${sysroot}\"/lib/libstdc++.so\n\n    echo \"${sysroot}/lib\" >> \"/etc/ld-musl-${arch}.path\"\n\n    rm -rf \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/musl.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nhide_output() {\n    set +x\n    trap \"\n        echo 'ERROR: An error was encountered with the build.'\n        cat /tmp/build.log\n        exit 1\n    \" ERR\n    bash -c 'while true; do sleep 30; echo $(date) - building ...; done' &\n    PING_LOOP_PID=$!\n    \"${@}\" &> /tmp/build.log\n    trap - ERR\n    kill \"${PING_LOOP_PID}\"\n    set -x\n}\n\nmain() {\n    local version=fe91582\n\n    install_packages ca-certificates curl build-essential\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n    curl --retry 3 -sSfL \"https://github.com/richfelker/musl-cross-make/archive/${version}.tar.gz\" -O\n    tar --strip-components=1 -xzf \"${version}.tar.gz\"\n\n    # Don't depend on the mirrors of sabotage linux that musl-cross-make uses.\n    local linux_headers_site=https://ci-mirrors.rust-lang.org/rustc/sabotage-linux-tarballs\n    local linux_ver=headers-4.19.88\n    local gcc_ver=9.2.0\n    local target\n    find_argument TARGET target \"${@}\"\n\n    # alpine GCC is built with `--enable-default-pie`, so we want to\n    # ensure we use that. we want support for shared runtimes except for\n    # libstdc++, however, the only way to do that is to simply remove\n    # the shared libraries later. on alpine, binaries use static-pie\n    # linked, so our behavior has maximum portability, and is consistent\n    # with popular musl distros.\n    hide_output make install \"-j$(nproc)\" \\\n        GCC_VER=${gcc_ver} \\\n        MUSL_VER=1.2.3 \\\n        BINUTILS_VER=2.33.1 \\\n        DL_CMD='curl --retry 3 -sSfL -C - -o' \\\n        LINUX_HEADERS_SITE=\"${linux_headers_site}\" \\\n        LINUX_VER=\"${linux_ver}\" \\\n        OUTPUT=/usr/local/ \\\n        \"GCC_CONFIG += --enable-default-pie --enable-languages=c,c++,fortran\" \\\n        \"${@}\"\n\n    purge_packages\n\n    popd\n\n    symlinkify_and_strip_toolchain \"${target}\" \"${gcc_ver}\"\n\n    for dir in /usr/local/libexec/gcc/\"${target}\"/*; do\n        pushd \"${dir}\" || exit 1\n        strip cc1 cc1plus collect2 f951 lto1 lto-wrapper liblto_plugin.so.0.0.0\n        popd || exit 1\n    done\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/native-linux-image.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -eo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local arch\n    arch=$(docker_to_linux_arch \"${TARGETARCH}\" \"${TARGETVARIANT}\")\n    /linux-image.sh \"${arch}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/native-linux-runner",
    "content": "#!/usr/bin/env bash\n\nset -eo pipefail\n\n# shellcheck disable=SC1091\n. /lib.sh\n\nmain() {\n    local arch\n    arch=$(docker_to_linux_arch \"${CROSS_TARGETARCH:-@DEFAULT_CROSS_TARGETARCH@}\" \"${CROSS_TARGETVARIANT:-@DEFAULT_CROSS_TARGETVARIANT@}\")\n\n    if [[ -z \"${CROSS_RUNNER}\" ]]; then\n        export CROSS_RUNNER=native\n    fi\n\n    exec /linux-runner \"${arch}\" \"${@}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/native-qemu.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local arch\n    arch=$(docker_to_qemu_arch \"${TARGETARCH}\")\n    /qemu.sh \"${arch}\" softmmu\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/netbsd.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local binutils=2.36.1 \\\n        gcc=9.4.0 \\\n        target=x86_64-unknown-netbsd\n\n    install_packages bzip2 \\\n        ca-certificates \\\n        curl \\\n        g++ \\\n        make \\\n        patch \\\n        texinfo \\\n        wget \\\n        xz-utils\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    mkdir \"${td}\"/{binutils,gcc}{,-build} \"${td}/netbsd\"\n\n    download_binutils \"${binutils}\" \"bz2\"\n    tar -C \"${td}/binutils\" --strip-components=1 -xjf \"binutils-${binutils}.tar.bz2\"\n\n    download_gcc \"${gcc}\" \"xz\"\n    tar -C \"${td}/gcc\" --strip-components=1 -xJf \"gcc-${gcc}.tar.xz\"\n\n    pushd \"${td}\"\n\n    pushd gcc\n    sed -i -e 's/ftp:/https:/g' ./contrib/download_prerequisites\n    ./contrib/download_prerequisites\n    local patches=(\n        https://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/lang/gcc9/patches/patch-libstdc++-v3_config_os_bsd_netbsd_ctype__configure__char.cc\n        https://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/lang/gcc9/patches/patch-libstdc++-v3_config_os_bsd_netbsd_ctype__base.h\n        https://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/lang/gcc8/patches/patch-libgfortran_io_io.h\n    )\n\n    local patch\n    for patch in \"${patches[@]}\"; do\n        local patch_file\n        patch_file=\"$(mktemp)\"\n        curl --retry 3 -sSfL \"${patch}\" -o \"${patch_file}\"\n        patch -Np0 < \"${patch_file}\"\n        rm \"${patch_file}\"\n    done\n    popd\n\n    local mirrors=(\n        \"ftp://ftp.netbsd.org\"\n        \"https://cdn.NetBSD.org\"\n    )\n    download_mirrors \"pub/NetBSD/NetBSD-9.3/amd64/binary/sets\" \"base.tar.xz\" \"${mirrors[@]}\"\n    tar -C \"${td}/netbsd\" -xJf base.tar.xz ./usr/include ./usr/lib ./lib\n\n    download_mirrors \"pub/NetBSD/NetBSD-9.3/amd64/binary/sets\" \"comp.tar.xz\" \"${mirrors[@]}\"\n    tar -C \"${td}/netbsd\" -xJf comp.tar.xz ./usr/include ./usr/lib\n\n    pushd binutils-build\n    ../binutils/configure \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    popd\n\n    local destdir=\"/usr/local/${target}\"\n    cp -r \"${td}/netbsd/usr/include\" \"${destdir}\"/\n    ls -all \"${td}/netbsd/usr/lib\"\n    cp \"${td}/netbsd/lib/libc.so.12.213\" \"${destdir}/lib\"\n    cp \"${td}/netbsd/lib/libm.so.0.12\" \"${destdir}/lib\"\n    cp \"${td}/netbsd/lib/libutil.so.7.24\" \"${destdir}/lib\"\n    cp \"${td}/netbsd/lib/libpthread.so.1.4\" \"${destdir}/lib\"\n    cp \"${td}/netbsd/usr/lib/librt.so.1.1\" \"${destdir}/lib\"\n    cp \"${td}/netbsd/usr/lib\"/lib{c,m,pthread}{,_p}.a \"${destdir}/lib\"\n    cp \"${td}/netbsd/usr/lib\"/libexecinfo.so \"${destdir}/lib\"\n    cp \"${td}/netbsd/usr/lib\"/{crt0,crti,crtn,crtbeginS,crtendS,crtbegin,crtend,gcrt0}.o \"${destdir}/lib\"\n\n    ln -s libc.so.12.213 \"${destdir}/lib/libc.so\"\n    ln -s libc.so.12.213 \"${destdir}/lib/libc.so.12\"\n    ln -s libm.so.0.12 \"${destdir}/lib/libm.so\"\n    ln -s libm.so.0.12 \"${destdir}/lib/libm.so.0\"\n    ln -s libpthread.so.1.4 \"${destdir}/lib/libpthread.so\"\n    ln -s libpthread.so.1.4 \"${destdir}/lib/libpthread.so.1\"\n    ln -s librt.so.1.1 \"${destdir}/lib/librt.so\"\n    ln -s libutil.so.7.24 \"${destdir}/lib/libutil.so\"\n    ln -s libutil.so.7.24 \"${destdir}/lib/libutil.so.7\"\n\n    pushd gcc-build\n    # remove the environment variables after bumping the gcc version to 11.\n    target_configargs=\"ac_cv_func_newlocale=no ac_cv_func_freelocale=no ac_cv_func_uselocale=no\" ../gcc/configure \\\n        --disable-libada \\\n        --disable-libcilkrt \\\n        --disable-libcilkrts \\\n        --disable-libgomp \\\n        --disable-libquadmath \\\n        --disable-libquadmath-support \\\n        --disable-libsanitizer \\\n        --disable-libssp \\\n        --disable-libvtv \\\n        --disable-lto \\\n        --disable-multilib \\\n        --disable-nls \\\n        --enable-languages=c,c++,fortran \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    popd\n\n    # clean up\n    popd\n\n    purge_packages\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/qemu-runner",
    "content": "#!/usr/bin/env bash\n# A very lightweight version of linux-runner that\n# doesn't support system emulation. Just useful\n# to allow native or qemu-user mode emulation.\n\nset -e\n\n# shellcheck disable=SC1091\n. /base-runner.sh\n\nif [ -n \"${CROSS_DEBUG}\" ]; then\n    set -x\nfi\n\n# arch in the rust target\narch=\"${1}\"\nshift\n\nif [[ -z \"${CROSS_RUNNER}\" ]]; then\n    if is_native_binary \"${arch}\"; then\n        CROSS_RUNNER=native\n    else\n        CROSS_RUNNER=qemu-user\n    fi\nfi\n\n# Ensure that the correct prefix is set even if the user has cleared the env.\n# `@DEFAULT_QEMU_LD_PREFIX@` is replaced during image build.\nexport QEMU_LD_PREFIX=${QEMU_LD_PREFIX:-@DEFAULT_QEMU_LD_PREFIX@}\n\nqarch=$(qemu_arch \"${arch}\")\ncase \"${CROSS_RUNNER}\" in\n    native)\n        exec \"${@}\"\n        ;;\n    qemu-user)\n        exec \"qemu-${qarch}\" \"${@}\"\n        ;;\n    *)\n        echo \"Invalid runner: \\\"${CROSS_RUNNER}\\\"\";\n        echo \"Valid runners are: native and qemu-user\"\n        exit 1\n        ;;\nesac\n"
  },
  {
    "path": "docker/qemu.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nbuild_static_libffi () {\n    local version=3.0.13\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n\n    curl --retry 3 -sSfL \"https://github.com/libffi/libffi/archive/refs/tags/v${version}.tar.gz\" -O -L\n    tar --strip-components=1 -xzf \"v${version}.tar.gz\"\n    ./configure --prefix=\"$td\"/lib --disable-builddir --disable-shared --enable-static\n    make \"-j$(nproc)\"\n    install -m 644 ./.libs/libffi.a /usr/local/lib/\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nbuild_static_libmount () {\n    local version_spec=2.23.2\n    local version=2.23\n\n    if_ubuntu_ge 22.04 version_spec=2.37.2\n    if_ubuntu_ge 22.04 version=2.37\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://kernel.org/pub/linux/utils/util-linux/v${version}/util-linux-${version_spec}.tar.xz\" -O -L\n    tar --strip-components=1 -xJf \"util-linux-${version_spec}.tar.xz\"\n    ./configure --disable-shared --enable-static --without-ncurses\n    make \"-j$(nproc)\" mount blkid\n    install -m 644 ./.libs/*.a /usr/local/lib/\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\n\nbuild_static_libattr() {\n    local version=2.4.46\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    set_centos_ulimit\n    yum install -y gettext\n\n    curl --retry 3 -sSfL \"https://download.savannah.nongnu.org/releases/attr/attr-${version}.src.tar.gz\" -O\n    tar --strip-components=1 -xzf \"attr-${version}.src.tar.gz\"\n    cp /usr/share/automake*/config.* .\n\n    ./configure\n    make \"-j$(nproc)\"\n    install -m 644 ./libattr/.libs/libattr.a /usr/local/lib/\n\n    yum remove -y gettext\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nbuild_static_libcap() {\n    local version=2.22\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-${version}.tar.xz\" -O\n    tar --strip-components=1 -xJf \"libcap-${version}.tar.xz\"\n    make \"-j$(nproc)\"\n    install -m 644 libcap/libcap.a /usr/local/lib/\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nbuild_static_pixman() {\n    local version=0.34.0\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://www.cairographics.org/releases/pixman-${version}.tar.gz\" -O\n    tar --strip-components=1 -xzf \"pixman-${version}.tar.gz\"\n    ./configure\n    make \"-j$(nproc)\"\n    install -m 644 ./pixman/.libs/libpixman-1.a /usr/local/lib/\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nbuild_static_slirp() {\n    local version=4.1.0\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://gitlab.freedesktop.org/slirp/libslirp//-/archive/v${version}/libslirp-v${version}.tar.gz\" -O\n    tar -xzf \"libslirp-v${version}.tar.gz\"\n    meson setup -Ddefault_library=static libslirp-v${version} build\n    ninja -C build\n    install -m 644 ./build/libslirp.a /usr/local/lib/\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nmain() {\n    local version=5.1.0\n\n    if_centos version=4.2.1\n\n    local arch=\"${1}\" \\\n        softmmu=\"${2:-}\"\n\n    install_packages \\\n        autoconf \\\n        automake \\\n        bison \\\n        bzip2 \\\n        curl \\\n        flex \\\n        libtool \\\n        make \\\n        patch \\\n        python3 \\\n\n    if_centos install_packages \\\n        gcc-c++ \\\n        pkgconfig \\\n        xz \\\n        glib2-devel \\\n        glib2-static \\\n        glibc-static \\\n        libattr-devel \\\n        libcap-devel \\\n        libfdt-devel \\\n        pcre-static \\\n        pixman-devel \\\n        libselinux-devel \\\n        libselinux-static \\\n        libffi \\\n        libuuid-devel \\\n        libblkid-devel \\\n        libmount-devel \\\n        zlib-devel \\\n        zlib-static\n\n    ### See this thread in case there are other issues regarding Savannah from gnu\n    ### https://lists.nongnu.org/archive/html/savannah-hackers-public/2025-04/msg00003.html\n    ### This link should be a good reference for the config.git repository: https://cgit.git.savannah.gnu.org/cgit/config.git/\n\n    if_centos 'curl --retry 3 -sSfL \"https://gitweb.git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD\" -o /usr/share/automake*/config.guess'\n    if_centos 'curl --retry 3 -sSfL \"https://gitweb.git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD\" -o /usr/share/automake*/config.sub'\n\n    # these are not packaged as static libraries in centos; build them manually\n    if_centos build_static_libffi\n    if_centos build_static_libmount\n    if_centos build_static_libattr\n    if_centos build_static_libcap\n    if_centos build_static_pixman\n\n    if_ubuntu install_packages \\\n        g++ \\\n        pkg-config \\\n        xz-utils \\\n        libattr1-dev \\\n        libcap-ng-dev \\\n        libffi-dev \\\n        libglib2.0-dev \\\n        libpixman-1-dev \\\n        libselinux1-dev \\\n        zlib1g-dev\n\n    # ubuntu no longer provides statically linked libmount\n    if_ubuntu_ge 22.04 build_static_libmount\n\n    # if we have python3.6+, we can install qemu 7.0.0, which needs ninja-build\n    # ubuntu 16.04 only provides python3.5, so remove when we have a newer qemu.\n    is_ge_python36=$(python3 -c \"import sys; print(int(sys.version_info >= (3, 6)))\")\n    if [[ \"${is_ge_python36}\" == \"1\" ]]; then\n        if_ubuntu version=7.0.0\n        if_ubuntu install_packages ninja-build\n    fi\n\n    # if we have python3.8+, we can install qemu 8.2.2, which needs ninja-build,\n    # meson, python3-pip and libslirp-dev.\n    # ubuntu 16.04 only provides python3.5, so remove when we have a newer qemu.\n    is_ge_python38=$(python3 -c \"import sys; print(int(sys.version_info >= (3, 8)))\")\n    if [[ \"${is_ge_python38}\" == \"1\" ]]; then\n        if_ubuntu version=8.2.2\n        if_ubuntu install_packages ninja-build meson python3-pip libslirp-dev\n        if_ubuntu build_static_slirp\n    fi\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://download.qemu.org/qemu-${version}.tar.xz\" -O\n    tar --strip-components=1 -xJf \"qemu-${version}.tar.xz\"\n\n    local targets=\"${arch}-linux-user\"\n    local virtfs=\"\"\n    case \"${softmmu}\" in\n        softmmu)\n            if [ \"${arch}\" = \"ppc64le\" ]; then\n                targets=\"${targets},ppc64-softmmu\"\n            else\n                targets=\"${targets},${arch}-softmmu\"\n            fi\n            virtfs=\"--enable-virtfs\"\n            ;;\n        \"\")\n            true\n            ;;\n        *)\n            echo \"Invalid softmmu option: ${softmmu}\"\n            exit 1\n            ;;\n    esac\n\n    ./configure \\\n        --disable-kvm \\\n        --disable-vnc \\\n        --disable-guest-agent \\\n        --enable-linux-user \\\n        --static \\\n        ${virtfs} \\\n        --target-list=\"${targets}\"\n    make \"-j$(nproc)\"\n    make install\n\n    # HACK the binfmt_misc interpreter we'll use expects the QEMU binary to be\n    # in /usr/bin. Create an appropriate symlink\n    ln -s \"/usr/local/bin/qemu-${arch}\" \"/usr/bin/qemu-${arch}-static\"\n\n    purge_packages\n\n    popd\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/solaris.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local arch=\"${1}\"\n    local manufacturer=\"${2}\"\n\n    local binutils=2.38 \\\n        gcc=8.4.0 \\\n        target=\"${arch}-${manufacturer}-solaris2.10\"\n\n    install_packages bzip2 \\\n        ca-certificates \\\n        curl \\\n        dirmngr \\\n        g++ \\\n        gpg-agent \\\n        make \\\n        patch \\\n        software-properties-common \\\n        texinfo \\\n        wget \\\n        xz-utils\n\n    local td\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n\n    mkdir \"${td}\"/{binutils,gcc}{,-build} \"${td}/solaris\"\n\n    download_binutils \"${binutils}\" \"xz\"\n    tar -C \"${td}/binutils\" --strip-components=1 -xJf \"binutils-${binutils}.tar.xz\"\n\n    download_gcc \"${gcc}\" \"xz\"\n    tar -C \"${td}/gcc\" --strip-components=1 -xJf \"gcc-${gcc}.tar.xz\"\n\n    cd gcc\n    sed -i -e 's/ftp:/https:/g' ./contrib/download_prerequisites\n    ./contrib/download_prerequisites\n    cd ..\n\n    local apt_arch=\n    local lib_arch=\n    case \"${arch}\" in\n        x86_64)\n            apt_arch=solaris-i386\n            lib_arch=amd64\n            ;;\n        sparcv9)\n            apt_arch=solaris-sparc\n            lib_arch=sparcv9\n            ;;\n    esac\n\n    apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486\n    add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2 main'\n    dpkg --add-architecture \"${apt_arch}\"\n    apt-get update\n    apt-get install -y --download-only \\\n        \"libc:${apt_arch}\"            \\\n        \"liblgrp:${apt_arch}\"         \\\n        \"libm-dev:${apt_arch}\"        \\\n        \"libpthread:${apt_arch}\"      \\\n        \"libresolv:${apt_arch}\"       \\\n        \"librt:${apt_arch}\"           \\\n        \"libsendfile:${apt_arch}\"     \\\n        \"libsocket:${apt_arch}\"       \\\n        \"system-crt:${apt_arch}\"      \\\n        \"system-header:${apt_arch}\"\n\n    for deb in /var/cache/apt/archives/*\"${apt_arch}.deb\"; do\n        dpkg -x \"${deb}\" \"${td}/solaris\"\n    done\n    apt-get clean\n\n    # The -dev packages are not available from the apt repository we're using.\n    # However, those packages are just symlinks from *.so to *.so.<version>.\n    # This makes all those symlinks.\n    while IFS= read -r -d '' lib; do\n        link_name=${lib%.so.*}.so\n        [ -e \"$link_name\" ] || ln -sf \"${lib##*/}\" \"$link_name\"\n    done < <(find . -name '*.so.*' -print0)\n\n    cd binutils-build\n    ../binutils/configure \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    cd ..\n\n    # Remove Solaris 11 functions that are optionally used by libbacktrace.\n    # This is for Solaris 10 compatibility.\n    rm solaris/usr/include/link.h\n\n    patch -p0  << 'EOF'\n--- solaris/usr/include/string.h\n+++ solaris/usr/include/string10.h\n@@ -93 +92,0 @@\n-extern size_t strnlen(const char *, size_t);\nEOF\n\n    local destdir=\"/usr/local/${target}\"\n    mkdir \"${destdir}/usr\"\n    cp -r \"${td}/solaris/usr/include\" \"${destdir}/usr\"\n    mv \"${td}/solaris/usr/lib/${lib_arch}\"/* \"${destdir}/lib\"\n    mv \"${td}/solaris/lib/${lib_arch}\"/* \"${destdir}/lib\"\n\n    ln -s usr/include \"${destdir}/sys-include\"\n    ln -s usr/include \"${destdir}/include\"\n\n    # note: solaris2.10 is obsolete, so we can't upgrade to GCC 10 till then.\n    # for gcc 9.4.0, need `--enable-obsolete`\n    cd gcc-build\n    ../gcc/configure \\\n        --disable-libada \\\n        --disable-libcilkrts \\\n        --disable-libgomp \\\n        --disable-libquadmath \\\n        --disable-libquadmath-support \\\n        --disable-libsanitizer \\\n        --disable-libssp \\\n        --disable-libvtv \\\n        --disable-lto \\\n        --disable-multilib \\\n        --disable-nls \\\n        --enable-languages=c,c++,fortran \\\n        --with-gnu-as \\\n        --with-gnu-ld \\\n        --target=\"${target}\"\n    make \"-j$(nproc)\"\n    make install\n    cd ..\n\n    # clean up\n    popd\n\n    purge_packages\n\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/tidyup.sh",
    "content": "#!/usr/bin/env bash\n\nrm -rf /var/lib/apt/lists\nrm -rf /usr/local/doc\n"
  },
  {
    "path": "docker/toolchain.cmake",
    "content": "# default toolchain file for targets, see #1110\n# required so CMAKE_CROSSCOMPILING_EMULATOR is set,\n# as well for embedded systems and other targets.\n#\n# all embedded systems without an OS should set the system name to generic\n# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Cross%20Compiling%20With%20CMake.html\n\nset(CMAKE_SYSTEM_NAME \"$ENV{CROSS_CMAKE_SYSTEM_NAME}\")\nset(CMAKE_SYSTEM_PROCESSOR \"$ENV{CROSS_CMAKE_SYSTEM_PROCESSOR}\")\nif(DEFINED ENV{CROSS_TARGET_RUNNER})\n    set(runner \"$ENV{CROSS_TARGET_RUNNER}\")\n    separate_arguments(runner)\n    set(CMAKE_CROSSCOMPILING_EMULATOR ${runner})\nendif()\n\n# not all of these are standard, however, they're common enough\n# that it's good practice to define them.\nset(prefix \"$ENV{CROSS_TOOLCHAIN_PREFIX}\")\nset(suffix \"$ENV{CROSS_TOOLCHAIN_SUFFIX}\")\nset(CMAKE_C_COMPILER \"${prefix}gcc${suffix}\")\nset(CMAKE_ASM_COMPILER \"${prefix}gcc${suffix}\")\nset(CMAKE_CXX_COMPILER \"${prefix}g++${suffix}\")\nset(CMAKE_AR \"${prefix}ar\")\nset(CMAKE_LINKER \"${prefix}ld\")\nset(CMAKE_NM \"${prefix}nm\")\nset(CMAKE_OBJCOPY \"${prefix}objcopy\")\nset(CMAKE_OBJDUMP \"${prefix}objdump\")\nset(CMAKE_RANLIB \"${prefix}ranlib\")\nset(CMAKE_STRIP \"${prefix}strip\")\n\n# these are cached so any build system that compiled outside of the rust\n# build system, such as a third-party cmake build and install of a shared\n# library, will still work. however, cmake-rs can override these values\nif(DEFINED ENV{CROSS_CMAKE_OBJECT_FLAGS})\n    set(CMAKE_C_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"C Compiler options\")\n    set(CMAKE_CXX_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"C++ Compiler options\")\n    set(CMAKE_ASM_FLAGS \"$ENV{CROSS_CMAKE_OBJECT_FLAGS}\" CACHE STRING \"ASM Compiler options\")\nendif()\n\n# if cross-compiling, we need to disable where the root path\n# is found and also provide our own sysroot\nif(DEFINED ENV{CROSS_SYSROOT})\n    set(CMAKE_FIND_ROOT_PATH \"$ENV{CROSS_SYSROOT}\" \"${CMAKE_PREFIX_PATH}\")\n    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\n    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\nendif()\n\nset(crt \"$ENV{CROSS_CMAKE_CRT}\")\nif(crt STREQUAL \"newlib\")\n    # cmake normally tries to test the C and C++ compilers by building and\n    # running a binary, but this fails for bare-metal targets, since\n    # they are missing start files and potentially other symbols.\n    # choosing to make a static library causes cmake to skip the check.\n    set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)\nendif()\n"
  },
  {
    "path": "docker/validate-android-args.sh",
    "content": "#!/usr/bin/env bash\n# Ensure the NDK, SDK, and Android versions match to exit\n# before a build or even worse, a runner later fails.\n\nset -x\nset -euo pipefail\n\nmain() {\n    local arch=\"${1}\"\n\n    validate_ndk \"${arch}\"\n    validate_sdk\n    validate_system\n    validate_ndk_sdk \"${arch}\"\n    validate_sdk_system\n}\n\nvalidate_ndk() {\n    local arch=\"${1}\"\n    local ndk_version=\n    ndk_version=$(echo \"${ANDROID_NDK}\" | tr -dc '0-9')\n\n    case \"${arch}\" in\n        mips|mips64)\n            if [[ \"${ndk_version}\" -ge 17 ]]; then\n                echo \"Android NDKs r17+ removed support for MIPS architectures.\" 1>&2\n                exit 1\n            fi\n            ;;\n        *)\n            ;;\n    esac\n}\n\nvalidate_sdk() {\n    local invalid_sdk_versions=(6 7 10 11 20 25)\n    # shellcheck disable=SC2076\n    if [[ \"${invalid_sdk_versions[*]}\" =~ \"${ANDROID_SDK}\" ]]; then\n        echo \"The Android SDK version ${ANDROID_SDK} is not provided by Android and therefore not supported.\" 1>&2\n        exit 1\n    fi\n}\n\nvalidate_system() {\n    local major_version\n    major_version=$(echo \"${ANDROID_VERSION}\" | cut -d '.' -f 1)\n    if [[ \"${major_version}\" -lt 5 ]]; then\n        echo \"Invalid Android version ${ANDROID_VERSION}, must be Android 5+.\" 1>&2\n        exit 1\n    fi\n}\n\nvalidate_ndk_sdk() {\n    local arch=\"${1}\"\n    local ndk_version=\n    ndk_version=$(echo \"${ANDROID_NDK}\" | tr -dc '0-9')\n\n    # no minimum version for most 32-bit architectures\n    case \"${arch}\" in\n        arm|x86)\n            ;;\n        mips)\n            check_min_sdk_arch \"${arch}\" 9\n            ;;\n        arm64|mips64|x86_64)\n            check_min_sdk_arch \"${arch}\" 21\n            ;;\n        *)\n            echo \"Unsupported architecture, got ${arch}.\" 1>&2\n            exit 1\n            ;;\n    esac\n\n    case \"${ndk_version}\" in\n        9)\n            check_sdk_range 3 19\n            ;;\n        10)\n            check_sdk_range 3 21\n            ;;\n        11)\n            check_sdk_range 3 24\n            ;;\n        12|13|14)\n            check_sdk_range 9 24\n            ;;\n        15)\n            check_sdk_range 14 26\n            ;;\n        16)\n            check_sdk_range 14 27\n            ;;\n        17)\n            check_sdk_range 14 28\n            ;;\n        18)\n            check_sdk_range 16 28\n            ;;\n        19)\n            check_sdk_range 16 28\n            ;;\n        20)\n            check_sdk_range 16 29\n            ;;\n        21|22)\n            check_sdk_range 21 30\n            ;;\n        23)\n            check_sdk_range 21 31\n            ;;\n        24)\n            check_sdk_range 21 32\n            ;;\n        25)\n            check_sdk_range 21 33\n            ;;\n        *)\n            echo \"Currently unsupported NDK version of ${ndk_version}.\" 1>&2\n            echo \"If you would like support, please file an issue.\" 1>&2\n            exit 1\n            ;;\n    esac\n}\n\ncheck_min_sdk_arch() {\n    local arch=\"${1}\"\n    local minimum=\"${2}\"\n    if [[ \"${ANDROID_SDK}\" -lt \"${minimum}\" ]]; then\n        echo \"Invalid SDK version ${ANDROID_SDK} for architecture ${arch}\" 1>&2\n        echo \"The minimum supported SDK version is ${minimum}.\" 1>&2\n        exit 1\n    fi\n}\n\ncheck_sdk_range() {\n    local lower=\"${1}\"\n    local upper=\"${2}\"\n    if [[ \"${ANDROID_SDK}\" -lt \"${lower}\" ]] || [[ \"${ANDROID_SDK}\" -gt \"${upper}\" ]]; then\n        echo \"Invalid SDK version ${ANDROID_SDK} for NDK version ${ANDROID_NDK}\" 1>&2\n        echo \"Valid SDK versions are ${lower}-${upper}.\" 1>&2\n        exit 1\n    fi\n}\n\nvalidate_sdk_system() {\n    local major_version\n    local minor_version\n    major_version=$(echo \"${ANDROID_VERSION}\" | cut -d '.' -f 1)\n    minor_version=$(echo \"${ANDROID_VERSION}\" | cut -d '.' -f 2)\n    local system_version=\"${major_version}.${minor_version}\"\n    case \"${system_version}\" in\n        5.0)\n            check_sdk_system_equal 21\n            ;;\n        5.1)\n            check_sdk_system_equal 22\n            ;;\n        6.0)\n            check_sdk_system_equal 23\n            ;;\n        7.0)\n            check_sdk_system_equal 24\n            ;;\n        7.1)\n            check_sdk_system_equal 25\n            ;;\n        8.0)\n            check_sdk_system_equal 26\n            ;;\n        8.1)\n            check_sdk_system_equal 27\n            ;;\n        9.0)\n            check_sdk_system_equal 28\n            ;;\n        10.0)\n            check_sdk_system_equal 29\n            ;;\n        11.0)\n            check_sdk_system_equal 30\n            ;;\n        12.0)\n            check_sdk_system_equal 31\n            ;;\n        12.1)\n            # NOTE: also knows as 12L\n            check_sdk_system_equal 32\n            ;;\n        13.0)\n            check_sdk_system_equal 33\n            ;;\n        *)\n            echo \"Currently unsupported Android system version of ${system_version}.\" 1>&2\n            echo \"If you would like support, please file an issue.\" 1>&2\n            exit 1\n            ;;\n    esac\n}\n\ncheck_sdk_system_equal() {\n    local expected=(\"$@\")\n    local valid=0\n\n    for version in \"${expected[@]}\"; do\n        if [[ \"${ANDROID_SDK}\" == \"${version}\" ]]; then\n            valid=1\n        fi\n    done\n\n    if [[ \"${valid}\" -ne 1 ]]; then\n        # shellcheck disable=SC2145\n        echo \"Invalid SDK version, got ${ANDROID_SDK} and expected ${expected[@]}.\" 1>&2\n        exit 1\n    fi\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/windows-entry.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nexport HOME=/tmp/home\nmkdir -p \"${HOME}\"\n\n# Initialize the wine prefix (virtual windows installation)\nexport WINEPREFIX=/tmp/wine\nmkdir -p \"${WINEPREFIX}\"\n# FIXME: Make the wine prefix initialization faster\n# TODO: https://github.com/cross-rs/cross/issues/1372 wine fails on arm64 qemu\nwineboot &> /dev/null || true\n\n# Put libstdc++ and some other mingw dlls in WINEPATH\n# This must work for x86_64 and i686\nP1=\"$(dirname \"$(find /usr -name libwinpthread-1.dll)\")\"\n\nWINEPATH=\"$(ls -d /usr/lib/gcc/*-w64-mingw32/*posix);${P1}\"\nexport WINEPATH\n\nexec \"$@\"\n"
  },
  {
    "path": "docker/wine.sh",
    "content": "#!/bin/bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local version=\"9.0.0.0~focal-1\"\n    install_packages wget\n\n    dpkg --add-architecture i386\n\n    # add repository for latest wine version and install from source\n    # hardcode version, since we might want to avoid a version later.\n    wget -nc https://dl.winehq.org/wine-builds/winehq.key\n\n    # workaround for wine server synchronization, see #1035\n    # we need to ensure the keys are now stored in `/etc/apt/keyrings`,\n    # which were previously stored in `/usr/share/keyrings`, and ensure\n    # our sources list searches for the right location.\n    mkdir -p /etc/apt/keyrings\n    mv winehq.key /etc/apt/keyrings/winehq-archive.key\n\n    wget -nc https://dl.winehq.org/wine-builds/ubuntu/dists/focal/winehq-focal.sources\n    mv winehq-focal.sources /etc/apt/sources.list.d/\n    sed -i s@/usr/share/keyrings/@/etc/apt/keyrings/@ /etc/apt/sources.list.d/winehq-focal.sources || true\n\n    # winehq requires all the dependencies to be manually specified\n    # if we're not using the latest version of a given major version.\n    apt-get update\n    apt install --no-install-recommends --assume-yes \\\n        \"wine-stable=${version}\" \\\n        \"wine-stable-amd64=${version}\" \\\n        \"wine-stable-i386=${version}\" \\\n        \"winehq-stable=${version}\"\n\n    purge_packages\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docker/zig.sh",
    "content": "#!/usr/bin/env bash\n\nset -x\nset -eo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local platform=\"${1}\"\n    install_packages ca-certificates curl xz-utils\n\n    install_zig \"${platform}\"\n    install_zigbuild \"${platform}\"\n\n    purge_packages\n    rm \"${0}\"\n}\n\ninstall_zig() {\n    local platform=\"${1}\"\n    local version=\"0.11.0\"\n    local dst=\"/opt/zig\"\n    local arch=\n    local os=\n    local triple=\n\n    case \"${platform}\" in\n        'linux/386')\n            arch=\"i386\"\n            os=\"linux\"\n            ;;\n        'linux/amd64')\n            arch=\"x86_64\"\n            os=\"linux\"\n            ;;\n        'linux/arm64')\n            arch=\"aarch64\"\n            os=\"linux\"\n            ;;\n        'linux/riscv64')\n            arch=\"riscv64\"\n            os=\"linux\"\n            ;;\n        'linux/ppc64le')\n            triple=\"powerpc64le-linux-gnu\"\n            ;;\n        'linux/s390x')\n            triple=\"s390x-linux-gnu\"\n            ;;\n        'darwin/amd64')\n            arch=\"x86_64\"\n            os=\"macos\"\n            ;;\n        'darwin/arm64')\n            arch=\"aarch64\"\n            os=\"macos\"\n            ;;\n        # NOTE: explicitly don't support linux/arm/v6\n        *)\n            echo \"Unsupported target platform '${platform}'\" 1>&2\n            exit 1\n            ;;\n    esac\n\n    if [[ -n \"${arch}\" ]]; then\n        install_zig_tarball \"${arch}\" \"${os}\" \"${version}\" \"${dst}\"\n    else\n        install_zig_source \"${triple}\" \"${version}\" \"${dst}\"\n    fi\n}\n\ninstall_zig_tarball() {\n    local arch=\"${1}\"\n    local os=\"${2}\"\n    local version=\"${3}\"\n    local dst=\"${4}\"\n    local filename=\"zig-${os}-${arch}-${version}.tar.xz\"\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://ziglang.org/download/${version}/${filename}\" -O\n    mkdir -p \"${dst}\"\n    tar --strip-components=1 -xJf \"${filename}\" --directory \"${dst}\"\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\ninstall_zig_source() {\n    local triple=\"${1}\"\n    local version=\"${2}\"\n    local dst=\"${3}\"\n    local filename=\"zig-bootstrap-${version}.tar.xz\"\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"https://ziglang.org/download/${version}/${filename}\" -O\n    mkdir zig\n    tar --strip-components=1 -xJf \"${filename}\" --directory zig\n\n    pushd zig\n    install_packages python3 make g++\n    ./build -j5 \"${triple}\" native\n    mv \"out/zig-${triple}-native\" /opt/zig\n\n    popd\n    popd\n\n    rm -rf \"${td}\"\n}\n\ninstall_zigbuild() {\n    local platform=\"${1}\"\n    local version=\"0.17.5\"\n    local dst=\"/usr/local\"\n    local triple=\n\n    # we don't know if `linux/arm/v7` is hard-float,\n    # and we don't know the the zigbuild `apple-darwin`\n    # target doesn't manually specify the architecture.\n    case \"${platform}\" in\n        'linux/386')\n            triple=\"i686-unknown-linux-musl\"\n            ;;\n        'linux/amd64')\n            triple=\"x86_64-unknown-linux-musl\"\n            ;;\n        'linux/arm64')\n            triple=\"aarch64-unknown-linux-musl\"\n            ;;\n        *)\n            ;;\n    esac\n\n    if [[ -n \"${triple}\" ]]; then\n        install_zigbuild_tarball \"${triple}\" \"${version}\" \"${dst}\"\n    else\n        install_zigbuild_source \"${version}\" \"${dst}\"\n    fi\n}\n\ninstall_zigbuild_tarball() {\n    local triple=\"${1}\"\n    local version=\"${2}\"\n    local dst=\"${3}\"\n    local repo=\"https://github.com/messense/cargo-zigbuild\"\n    local filename=\"cargo-zigbuild-v${version}.${triple}.tar.gz\"\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    curl --retry 3 -sSfL \"${repo}/releases/download/v${version}/${filename}\" -O\n    mkdir -p \"${dst}/bin\"\n    tar -xzf \"${filename}\" --directory \"${dst}/bin\"\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\ninstall_zigbuild_source() {\n    local version=\"${1}\"\n    local dst=\"${2}\"\n\n    local td\n    td=\"$(mktemp -d)\"\n\n    pushd \"${td}\"\n\n    export RUSTUP_HOME=\"${td}/rustup\"\n    export CARGO_HOME=\"${td}/cargo\"\n\n    curl --retry 3 -sSfL https://sh.rustup.rs -o rustup-init.sh\n    sh rustup-init.sh -y --no-modify-path --profile minimal\n\n    PATH=\"${CARGO_HOME}/bin:${PATH}\" \\\n        cargo install cargo-zigbuild \\\n        --version \"${version}\" \\\n        --root \"${dst}\" \\\n        --locked\n\n    popd\n\n    rm -rf \"${td}\"\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "docs/cargo_configuration.md",
    "content": "<!--toc:start-->\n- [Configuring `cross`](#configuring-cross)\n- [Configuring Cargo through environment variables](#configuring-cargo-through-environment-variables)\n<!--toc:end-->\n\n# Configuring `cross`\n\nPlease refer to the following docs:\n\n- [config file](./config_file.md)\n- [env variables](./environment_variables.md)\n\n\n# Configuring Cargo through environment variables\n\nWhen cross-compiling, `cargo` does not use environment variables such as\n`RUSTFLAGS`, and must be provided using `CARGO_TARGET_${TARGET}_${OPTION}`.\nPlease note that some of these may be provided by the image themselves, such as\nrunners, and should be overwritten with caution. A list of important flags\nincludes:\n\n- `CARGO_TARGET_${TARGET}_LINKER`: specify a custom linker passed to rustc.\n- `CARGO_TARGET_${TARGET}_RUNNER`: specify the wrapper to run executables.\n- `CARGO_TARGET_${TARGET}_RUSTFLAGS`: add additional flags passed to rustc.\n\nAny of the following [flags][cargo-flags] can be provided, and are converted to\nuppercase. For example, changing `foo-bar` would be provided as\n`CARGO_TARGET_${TARGET}_FOO_BAR`.\n\nFor example, to run binaries on `i686-unknown-linux-gnu` with Qemu, first\ncreate a custom image containing Qemu, and run with the following command:\n\n```\nCARGO_TARGET_I686_UNKNOWN_LINUX_GNU_RUNNER=qemu-i386 cross run ...\n```\n\n\n[cargo-flags]: https://doc.rust-lang.org/cargo/reference/config.html#target\n"
  },
  {
    "path": "docs/config_file.md",
    "content": "<!--toc:start-->\n- [`build`](#build)\n- [`build.env`](#buildenv)\n- [`build.dockerfile`](#builddockerfile)\n- [`build.zig`](#buildzig)\n- [`target.TARGET`](#targettarget)\n- [`target.TARGET.pre-build`](#targettargetpre-build)\n- [`target.TARGET.image`](#targettargetimage)\n- [`target.TARGET.env`](#targettargetenv)\n- [`target.TARGET.dockerfile`](#targettargetdockerfile)\n- [`target.TARGET.zig`](#targettargetzig)\n<!--toc:end-->\n\n> **Note**: Additional configuration is available through\n> [environment variables](./environment_variables.md)\n\nYou can place a `Cross.toml` file in the root of your Cargo project or use a\n`CROSS_CONFIG` environment variable to tweak cross's behavior. You can also use\n`package.metadata.cross.KEY` in `Cargo.toml`, and the priority of settings is\nenvironment variables override `Cross.toml` options, which override\n`Cargo.toml` options. Annotated examples of both\n[`Cross.toml`][example-cross-toml] and [`Cargo.toml`][example-cargo-toml] are\nprovided.\n\nFor example, the `[build]` table in `Cross.toml` is identical to setting\n`[package.metadata.cross.build]` in `Cargo.toml`.\n\nThe `cross` configuration in the `Cross.toml` file can contain the following\nelements:\n\n\n# `build`\n\nThe `build` key allows you to set global variables, e.g.:\n\n> *NOTE*: `$CROSS_DEB_ARCH` is automatically provided by cross,\n> [see here][custom_images_automatic_arch].\n\n```toml\n[build]\nbuild-std = false                              # do not build the std library\nzig = false                                    # do not use zig cc for the builds\ndefault-target = \"x86_64-unknown-linux-gnu\"    # use this target if none is explicitly provided\npre-build = [                                  # additional commands to run prior to building the package\n    \"dpkg --add-architecture $CROSS_DEB_ARCH\", \n    \"apt-get update && apt-get --assume-yes install libssl-dev:$CROSS_DEB_ARCH\"\n]                 \n```\n\n\n# `build.env`\n\nWith the `build.env` key you can globally set volumes that should be mounted in\nthe Docker container or environment variables that should be passed through.\nFor example:\n\n```toml\n[build.env]\nvolumes = [\"VOL1_ARG\", \"VOL2_ARG=/path/to/volume\"]\npassthrough = [\"VAR1_ARG\", \"VAR2_ARG=VALUE\"]\n```\n\nNote how in the environment variable passthrough, we can provide a definition\nfor the variable as well. `VAR1_ARG` will be the value of the environment\nvariable on the host, while `VAR2_ARG` will be `VALUE`. Likewise, the path to\nthe volume for `VOL1_ARG` will be the value of the environment variable on the\nhost, while `VOL2_ARG` will be `/path/to/volume`.\n\n\n# `build.dockerfile`\n\n> If the image you want to use is already available from a container registry,\n> check out the `target.TARGET.image` option below.\n\nThe `build.dockerfile` key lets you provide a custom Docker image for all\ntargets, except those specified `target.TARGET.dockerfile`. The value can be\nprovided as either a table or a string. If `build.dockerfile` is set to a\nstring, it's equivalent to setting `build.dockerfile.file` to that value. For\nexample, using only a string:\n\n```toml\n[build]\ndockerfile = \"./Dockerfile\"\n```\n\nOr using a table:\n\n```toml\n[build.dockerfile]\nfile = \"./Dockerfile\"         # the dockerfile to use relative to the `Cargo.toml`\ncontext = \".\"                 # the context folder to build the script in. defaults to `.`\nbuild-args = { ARG1 = \"foo\" } # https://docs.docker.com/engine/reference/builder/#arg\n```\n\n`cross` will build and use the image that was built instead of the default\nimage. It's recommended to base your custom image on the default Docker image\nthat `cross` uses: `ghcr.io/cross-rs/{{TARGET}}:{{VERSION}}` (where\n`{{VERSION}}` is `cross`'s version). This way you won't have to figure out how\nto install a cross-C toolchain in your custom image.\n\n> *NOTE*: `$CROSS_DEB_ARCH` is automatically provided by cross, [see\n> here][custom_images_automatic_arch].\n\n``` Dockerfile\nFROM ghcr.io/cross-rs/aarch64-unknown-linux-gnu:latest\n\nRUN dpkg --add-architecture $CROSS_DEB_ARCH && \\\n    apt-get update && \\\n    apt-get install --assume-yes libfoo:$CROSS_DEB_ARCH\n```\n\n`cross` will provide the argument `CROSS_BASE_IMAGE` which points to the\ndefault image `cross` would use for the target. Instead of the above, you can\nalso then do the following:\n\n```Dockerfile\nARG CROSS_BASE_IMAGE\nFROM $CROSS_BASE_IMAGE\nRUN ...\n```\n\n\n# `build.zig`\n\nThe `build.zig` key lets you use `zig cc` as a cross-compiler, enabling\ncross-compilation to numerous architectures and glibc versions using a single\nDocker image. Note that `zig cc` doesn't support all targets: only a subset of\nour Linux GNU targets, so it might be better to set these values in\n`target.TARGET.zig` instead. The value can be provided as either a table, a bool,\nor a string. If `build.zig` is set to a string, it's equivalent to setting\n`build.zig.version` to that value and `build.zig.enable` to true:\n\n```toml\n[build]\nzig = \"2.17\"\n```\n\nIf `build.zig` is set to a bool, it's equivalent to setting `build.zig.enable`\nto that value:\n\n```toml\n[build]\nzig = true\n```\n\nOr using a table:\n\n```toml\n[build.zig]\nenable = true                 # enable or disable the use of zig cc\nversion = \"2.17\"              # the glibc version to use\nimage = \"myimage\"             # a custom image containing zig to use\n```\n\n\n# `target.TARGET`\n\nThe `target` key allows you to specify parameters for specific compilation\ntargets:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\nbuild-std = [\"core\", \"alloc\"]   # always build the `core` and `alloc` crates from the std library\nimage = \"test-image\"            # use a different image for the target\nrunner = \"qemu-user\"            # wrapper to run the binary (must be `qemu-system`, `qemu-user`, or `native`).\n```\n\n\n# `target.TARGET.pre-build`\n\nThe `pre-build` field can reference a file to copy and run. This file is\nrelative to the container context, which would be the workspace root, or the\ncurrent directory if `--manifest-path` is used. For more involved scripts,\nconsider using `target.TARGET.dockerfile` instead to directly control the\nexecution.\n\nThis script will be invoked as `RUN ./pre-build-script $CROSS_TARGET` where\n`$CROSS_TARGET` is the target triple.\n\n```toml\n[target.aarch64-unknown-linux-gnu]\npre-build = \"./scripts/my-script.sh\"\n```\n\n```bash\n$ cat ./scripts/my-script.sh\n#!/usr/bin/env bash\n\napt-get install libssl-dev -y\n```\n\n`pre-build` can also be a list of commands to directly run inside the image:\n\n> *NOTE*: `$CROSS_DEB_ARCH` is automatically provided by cross, [see\n> here][custom_images_automatic_arch].\n\n```toml\n[target.aarch64-unknown-linux-gnu]\npre-build = [\n    \"dpkg --add-architecture $CROSS_DEB_ARCH\",\n    \"apt-get update\",\n    \"apt-get install --assume-yes libfoo:$CROSS_DEB_ARCH\"\n]\n```\n\n\n# `target.TARGET.image`\n\n```toml\n[target.aarch64-unknown-linux-gnu]\nimage = \"my/image:latest\"\n```\n\nIn the example above, `cross` will use a image named `my/image:latest` instead of\nthe default one. Normal Docker behavior applies, so:\n\n- Docker will first look for a local image named `my/image:latest`\n- If it doesn't find a local image, then it will look in Docker Hub.\n- If only `image:latest` is specified, then Docker won't look in Docker Hub.\n- If the tag is omitted, then Docker will use the `latest` tag.\n\nIf you specify a tag but no image name, `cross` will use the default image with\nthe tag you provided:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\n# Translates to `ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge`\nimage = \":edge\"\n\n[target.x86_64-unknown-linux-musl]\n# Translates to `ghcr.io/cross-rs/x86_64-unknown-linux-musl@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99`\nimage = \"@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99\"\n```\n\nYou can also specify a subtarget with no tag nor image name:\n\n```toml\n[target.x86_64-unknown-linux-gnu]\n# Translates to `ghcr.io/cross-rs/x86_64-unknown-linux-gnu:0.3.0-centos`\nimage = \"-centos\"\n```\n\nThe `image` key can also take the toolchains/platforms supported by the image:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\nimage.name = \"alpine:edge\"\nimage.toolchain = [\"x86_64-unknown-linux-musl\", \"linux/arm64=aarch64-unknown-linux-musl\"] # Defaults to `x86_64-unknown-linux-gnu`\n```\n\n\n\n# `target.TARGET.env`\n\nThe `env` key allows you to specify environment variables that should be used\nfor a specific compilation target. This is similar to `build.env`, but allows\nyou to be more specific per target:\n\n```toml\n[target.x86_64-unknown-linux-gnu.env]\nvolumes = [\"VOL1_ARG\", \"VOL2_ARG=/path/to/volume\"]\npassthrough = [\"VAR1_ARG\", \"VAR2_ARG=VALUE\"]\n```\n\n\n# `target.TARGET.dockerfile`\n\nThe `dockerfile` key lets you provide a custom Docker image for the\ngiven target. The value can be provided as either a table or a string. If\n`target.TARGET.dockerfile` is set to a string, it's equivalent to setting\n`target.(...).dockerfile.file` to that value. For example, using only a string:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\ndockerfile = \"./Dockerfile\"\n```\n\nOr using a table:\n\n```toml\n[target.aarch64-unknown-linux-gnu.dockerfile]\nfile = \"./Dockerfile\"         # the dockerfile to use relative to the `Cargo.toml`\ncontext = \".\"                 # the context folder to build the script in. defaults to `.`\nbuild-args = { ARG1 = \"foo\" } # https://docs.docker.com/engine/reference/builder/#arg\n```\n\n\n# `target.TARGET.zig`\n\nThe `target.TARGET.zig` key lets you use `zig cc` as a cross-compiler, enabling\ncross-compilation to numerous architectures and glibc versions using a single\nDocker image. The value can be provided as either a table, a bool, or a string.\nIf `target.TARGET.zig` is set to a string, it's equivalent to setting\n`target.TARGET.zig.version` to that value and `target.TARGET.zig.enable` to\ntrue:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\nzig = \"2.17\"\n```\n\nIf `target.TARGET.zig` is set to a bool, it's equivalent to setting\n`target.TARGET.zig.enable` to that value:\n\n```toml\n[target.aarch64-unknown-linux-gnu]\nzig = true\n```\n\nOr using a table:\n\n```toml\n[target.aarch64-unknown-linux-gnu.zig]\nenable = true                 # enable or disable the use of zig cc\nversion = \"2.17\"              # the glibc version to use\nimage = \"myimage\"             # a custom image containing zig to use\n```\n\n\n\n[example-cross-toml]: https://github.com/cross-rs/wiki_assets/blob/main/Configuration/Cross.toml\n[example-cargo-toml]: https://github.com/cross-rs/wiki_assets/blob/main/Configuration/Cargo.toml\n[custom_images_automatic_arch]: ./custom_images.md#automatic-target-architecture-on-debian\n"
  },
  {
    "path": "docs/custom_images.md",
    "content": "<!--toc:start-->\n- [Automatic Target Architecture on Debian](#automatic-target-architecture-on-debian)\n- [Custom Images](#custom-images)\n  - [Adding Dependencies to Existing Images](#adding-dependencies-to-existing-images)\n  - [Custom Dockerfile](#custom-dockerfile)\n  - [Custom Image](#custom-image)\n<!--toc:end-->\n\n# Automatic Target Architecture on Debian\n\nCustom images generated from config `dockerfile` or `pre-build` keys will\nexport `CROSS_DEB_ARCH`, which allows you to install packages from\nUbuntu/Debian repositories without having to specify the exact architecture.\nYou can find an\n[example of this here](#adding-dependencies-to-existing-images).\n\n\n# Custom Images\n\n`cross` provides default Docker images for the targets listed [in the\nREADME](../README.md#supported-targets). However, it can't cover every single\nuse case out there.\n\n## Adding Dependencies to Existing Images\n\nIf you simply need to install a dependency availaible in ubuntus package\nmanager, see [`target.TARGET.pre-build`][config-target-pre-build]:\n\n```toml\n[target.x86_64-unknown-linux-gnu]\npre-build = [\n    \"dpkg --add-architecture $CROSS_DEB_ARCH\",\n    \"apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH\"\n]\n```\n\nFor FreeBSD targets, a few helper scripts are available for use in\n[`target.TARGET.pre-build`][config-target-pre-build]:\n\n```toml\n[target.x86_64-unknown-freebsd]\npre-build = [\"\"\"\nexport FREEBSD_MIRROR=$(/freebsd-fetch-best-mirror.sh) &&\n/freebsd-setup-packagesite.sh &&\n/freebsd-install-package.sh xen-tools\n\"\"\"]\n```\n\n## Custom Dockerfile\n\nFor other targets, or when the default image is not enough, you can use the\n[`target.{{TARGET}}.dockerfile`][config_target_dockerfile] field\nin `Cross.toml` to use a custom Docker image for a specific target:\n\n> *NOTE*: Refer to the [`build.dockerfile`][config_build_dockerfile] section of\n> the configuration for tips when writing your own `Dockerfile`.\n\n``` toml\n[target.aarch64-unknown-linux-gnu]\ndockerfile = \"Dockerfile\"\n```\n\n`cross` will build and use the image that was built instead of the default\nimage.\n\n\n## Custom Image\n\nIf there is a pre-built image for your specific target, you can use the\n[`target.{{TARGET}}.image`][config_target_image] field in `Cross.toml` to use\nthat instead:\n\n``` toml\n[target.aarch64-unknown-linux-gnu]\nimage = \"my/image:tag\"\n```\n\nIn thie case, `cross` will use a image named `my/image:tag` instead of the\ndefault one. Normal Docker behavior applies, so:\n\n- Docker will first look for a local image named `my/image:tag`\n- If it doesn't find a local image, then it will look in Docker Hub.\n- If only `image:tag` is specified, then Docker won't look in Docker Hub.\n- If only `tag` is omitted, then Docker will use the `latest` tag.\n\n\n\n[config-target-pre-build]: ./config_file.md#targettargetpre-build\n[config_target_dockerfile]: ./config_file.md#targettargetdockerfile\n[config_target_image]: ./config_file.md#targettargetimage\n[config_build_dockerfile]: ./config_file.md#builddockerfile\n"
  },
  {
    "path": "docs/environment_variables.md",
    "content": "<!--toc:start-->\n- [Configuring cross with environment variables](#configuring-cross-with-environment-variables)\n- [Environment-Variable passthrough](#environment-variable-passthrough)\n<!--toc:end-->\n\n# Configuring cross with environment variables\n\nCross can be further customized by setting certain environment variables.\nIn-depth documentation with examples can be found [here][env-examples].\n\n- `CROSS_CONTAINER_ENGINE`: The container engine to run cross in. Defaults to\n  `docker` then `podman`, whichever is found first (example: `docker`, see the\n  [FAQ][faq-container-engines]).\n- `NIX_STORE`: The directory for the [Nix store][nix-store] (example:\n  `/nix/store`).\n- `CROSS_CONTAINER_UID`: Set the user identifier for the cross command\n  (example: `1000`).\n- `CROSS_CONTAINER_GID`: Set the group identifier for the cross command\n  (example: `1000`).\n- `CROSS_CONTAINER_IN_CONTAINER`: Inform `cross` that it is running inside a\n  container (example: `true`, see the FAQ).\n- `CROSS_CONTAINER_OPTS`: Additional arguments to provide to the container\n  engine during `$engine run` (example: `--env MYVAR=1` where `engine=docker`).\n- `CROSS_CONFIG`: Specify the path to the `cross` config file (see [Config\n  File][cross-config-file]).\n- `CROSS_BUILD_OPTS`: Space separated flags to add when building a custom\n  image, i.e. `--network=host`\n- `CROSS_DEBUG`: Print debugging information for `cross`.\n- `CROSS_COMPATIBILITY_VERSION`: Use older `cross` behavior (example: `0.2.1`).\n- `CROSS_CUSTOM_TOOLCHAIN`: Specify that `rustup` is using a custom toolchain,\n  and therefore should not try to add targets/install components. Useful with\n  [`cargo-bisect-rustc`][cargo-bisect-rustc].\n- `CROSS_REMOTE`: Inform `cross` it is using a remote container engine, and use\n  data volumes rather than local bind mounts. See [Remote][docs-remote] for\n  more information using remote container engines.\n- `QEMU_STRACE`: Get a backtrace of system calls from “foreign” (non x86_64)\n  binaries when using `cross` run.\n- `CARGO_BUILD_TARGET`: Sets the default target, similar to specifying\n  `--target`.\n- `CROSS_BUILD_DOCKERFILE`: Specify to provide a custom Docker image for all\n  targets.\n- `CROSS_ROOTLESS_CONTAINER_ENGINE`: Specify whether to container engine runs\n  as root or is rootless. If set to `auto` or not provided, it assumes `docker`\n  runs as root and all other container engines are rootless.\n- `CROSS_CONTAINER_USER_NAMESPACE`: Custom the [container user\n  namespace][container-user-namespace]. If set to `none`, user namespaces will\n  be disabled. If not provided or set to `auto`, it will use the default\n  namespace.\n- `CROSS_CUSTOM_TOOLCHAIN_COMPAT`: A descriptive name for a custom toolchain so\n  `cross` can convert it to a fully-qualified toolchain name.\n- `CROSS_CONTAINER_ENGINE_NO_BUILDKIT`: The container engine does not have\n  `buildx` command (or BuildKit support) when building custom images.\n- `CROSS_NO_WARNINGS`: Set to `1` to panic on warnings from `cross`, before\n  building the executables.\n  Use `0` to disable this behaviour.\n  The no warnings behaviour is implicitly enabled in CI pipelines.\n\nAll config file options can also be specified using environment variables. For\nexample, setting `CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_DOCKERFILE=foo` is\nidentical to setting `target.aarch64-unknown-linux-gnu.dockerfile = foo`.\n\n\n# Environment-Variable passthrough\n\nBy default, `cross` does not pass most environment variables into the build\nenvironment from the calling shell. This is chosen as a safe default as most\nuse cases will not want the calling environment leaking into the inner\nexecution environment. There are, however, some notable exceptions: most\nenvironment variables `cross` or `cargo` reads are passed through automatically\nto the build environment. The major exceptions are variables that are set by\n`cross` or conflict with our build environment, including:\n\n- `CARGO_HOME`\n- `CARGO_TARGET_DIR`\n- `CARGO_BUILD_TARGET_DIR`\n- `CARGO_BUILD_RUSTC`\n- `CARGO_BUILD_RUSTC_WRAPPER`\n- `CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER`\n- `CARGO_BUILD_RUSTDOC`\n- `CROSS_RUNNER`\n- `CROSS_RUSTC_MAJOR_VERSION`\n- `CROSS_RUSTC_MINOR_VERSION`\n- `CROSS_RUSTC_PATCH_VERSION`\n\nOtherwise, any environment variables that start with CARGO_ or CROSS_, and a\nfew others, will be available in the build environment. For example, RUSTFLAGS\nand CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS will both be automatically\navailable in the build environment.\n\nIn the instances that you do want to pass through additional environment\nvariables, this can be done via `build.env.passthrough` in your `Cross.toml`:\n\n```toml\n[build.env]\npassthrough = [\n    \"RUST_BACKTRACE\",\n    \"RUST_LOG\",\n    \"TRAVIS\",\n]\n```\n\nTo pass variables through for one target but not others, you can use\nthis syntax instead:\n\n```toml\n[target.aarch64-unknown-linux-gnu.env]\npassthrough = [\n    \"RUST_DEBUG\",\n]\n```\n\n\n[env-examples]: https://github.com/cross-rs/wiki_assets/blob/main/Configuration/crossrc.bash_aliases\n[faq-container-engines]: https://github.com/cross-rs/cross/wiki/FAQ#explicitly-choose-the-container-engine\n[nix-store]: https://nixos.org/manual/nix/stable/introduction.html\n[cross-config-file]: ./config_file.md\n[cargo-bisect-rustc]: https://github.com/rust-lang/cargo-bisect-rustc\n[docs-remote]: ./remote.md\n[container-user-namespace]: https://docs.docker.com/engine/security/userns-remap/\n"
  },
  {
    "path": "docs/getting-started.md",
    "content": "<!--toc:start-->\n- [Installing Cross](#installing-cross)\n    - [Installing Rust via Rustup](#installing-rust-via-rustup)\n    - [Installing Cross](#installing-cross)\n- [Installing A Container Engine](#installing-a-container-engine)\n- [Cross-Compiling Your First Package](#cross-compiling-your-first-package)\n<!--toc:end-->\n\nNew to cross? Cross-compilation? Container engines? Here's how to get up-and-running.\n\n# Installing Cross\n\n## Installing Rust via Rustup\n\n`cross` requires a `rustup` installation of Rust. To do so, the recommended\ninstructions are documented [here](https://www.rust-lang.org/tools/install),\nbut might differ on some platforms. For UNIX-like systems, run the following\ncommand in a terminal and follow the instructions to install Rust and add Rust\nto the path:\n\n```bash\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n```\n\nOn Windows, download\n[rustup-init.exe](https://static.rust-lang.org/rustup/dist/i686-pc-windows-gnu/rustup-init.exe)\nor following the [other installation\nmethods](https://forge.rust-lang.org/infra/other-installation-methods.html),\nsay, to install from a package manager.\n\nOn some platforms, such as NixOS, you might need to use a package manager since\nthe default `rustup` install will fail. On NixOS, you should run the following,\nwhich will install `rustup` and the latest `stable` release of Rust.\n\n```bash\nnix-env -i rustup\nrustup toolchain install stable\n```\n\nNote that you might need additional tools on some platforms to get `rustc` and\n`cargo` working. On UNIX-like systems, this generally means an install of GCC\nor Clang. For example, on NixOS you will likely need to install GCC via\n`nix-env -i gcc` and then go into a GCC and Rust shell (`nix-shell -p gcc\nrustup`). On Alpine, you'll need to run `apk add libgcc gcc musl-dev`. Exact\ninstructions will differ by OS and Linux distro, feel free to ask on the\n[discussion](https://github.com/cross-rs/cross/discussions) or our [Matrix\nroom](https://matrix.to/#/#cross-rs:matrix.org) if you have any questions.\n\n\n## Installing Cross\n\nOnce `cargo` is installed via `rustup`, and the necessary additional tools are\npresent, you can now install `cross` via `cargo`:\n\n```bash\ncargo install cross\n# Optionally, if you have cargo-binstall, you can install via pre-built binary\ncargo binstall cross\n```\n\nOnce `cross` is installed, you need a container engine and you can start\ncross-compiling.\n\n\n# Installing A Container Engine\n\nOn Windows and macOS, we generally recommend you use Docker unless you know\nwhat you're doing. [Docker\nDesktop](https://www.docker.com/products/docker-desktop/) install instructions\ncan be found [here](https://www.docker.com/products/docker-desktop/). On Linux,\nyou can either install via [Docker\nEngine](https://docs.docker.com/engine/install/ubuntu/), [Docker\nDesktop](https://docs.docker.com/desktop/install/linux-install/) or\n[Podman](https://podman.io/getting-started/installation). We generally\nrecommend Podman, since it runs rootless by default. If you choose to use\nDocker, make sure you add users to the [docker\ngroup](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user)\nso it can be run without `sudo` (note that this has security implications) or\nuse [rootless](https://docs.docker.com/engine/security/rootless/)<sup>†</sup>\nDocker.\n\nIf you use Docker Desktop for Windows, ensure you're using the WSL2. Follow the\n[WSL2 installation\ninstructions](https://docs.microsoft.com/en-us/windows/wsl/install) to enable\nthe [WSL2 backend in docker](https://docs.docker.com/desktop/windows/wsl/).\n\nOnce your container engine is installed, you can check that it is running via:\n\n```bash\n# or use podman, if installed\n$ docker ps -a\n```\n\n<sup>†</sup>Using rootless docker also requires setting the environment\nvariable `CROSS_ROOTLESS_CONTAINER_ENGINE=1`.\n\n\n# Cross-Compiling Your First Package\n\nOnce both `cross` and the container engine are installed, you can build your\nfirst package: this is all that's required.\n\n```bash\n$ cargo init --bin hello\n$ cd hello\n$ cross run --target aarch64-unknown-linux-gnu\n   Compiling hello v0.1.0 (/project)\n    Finished dev [unoptimized + debuginfo] target(s) in 0.64s\n     Running `/linux-runner aarch64 /target/aarch64-unknown-linux-gnu/debug/hello`\nHello, world!\n```\n\nThis will automatically install the Rust target required and the Docker image\ncontaining the toolchain to cross-compile your target.\n\nIf you get an error similar to `error: toolchain\n'stable-x86_64-unknown-linux-gnu' does not support components`, try\nreinstalling that toolchain with rustup.\n\n```sh\n$ rustup toolchain uninstall stable-x86_64-unknown-linux-gnu\n$ rustup toolchain install stable-x86_64-unknown-linux-gnu --force-non-host\n```\n"
  },
  {
    "path": "docs/recipes.md",
    "content": "<!--toc:start-->\n- [OpenSSL](#openssl)\n  - [Vendored](#vendored)\n  - [Pre-build](#pre-build)\n  - [Custom dockerfile](#custom-dockerfile)\n- [sccache](#sccache)\n- [Redoxer](#redoxer)\n- [vcpkg, Meson, and Conan](#vcpkg-meson-and-conan)\n- [Using Clang and Software Collections on CentOS7](#using-clang-and-software-collections-on-centos7)\n<!--toc:end-->\n\nThis contains recipes for common logic use cases.\n\n\n# OpenSSL\n\nYou can either use the vendored or system packages for the\n[openssl](https://crates.io/crates/openssl) crate. See\n[openssl-certs](https://github.com/cross-rs/wiki_assets/tree/main/Recipes/openssl-certs)\nfor a working project.\n\n## Vendored\n\nUse the vendored feature of the openssl crate by adding the following to your\ndependencies in `Cargo.toml`:\n\n```toml,cargo\nopenssl = { version = \"0.10\", features = [\"vendored\"] }\n```\n\n## Pre-build\n\nTo install OpenSSL in an image with `apt-get` available add the following to\nyour [Cross\nconfiguration](./config_file.md):\n\n```toml\n[target.x86_64-unknown-linux-gnu]\npre-build = [\n    \"dpkg --add-architecture $CROSS_DEB_ARCH\",\n    \"apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH\"\n]\n```\n\n## Custom dockerfile\n\nA sample Dockerfile for `aarch64` with OpenSSL support is:\n\n```Dockerfile\nFROM ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge\nRUN dpkg --add-architecture arm64\nRUN apt-get update && apt-get install --assume-yes libssl-dev:arm64\n```\n\nBuild this image and use it, as is described extensively in [Custom\nImages](./custom_images.md).\n\n\n# sccache\n\nsccache support can be done either by `sccache` from source or using a pre-built binary. See [sccache](https://github.com/cross-rs/wiki_assets/tree/main/Recipes/sccache) for a working project using pre-build hooks.\n\n1. Create a script to [install](#sccache-install-script) sccache in the image, either from a [pre-built binary](#sccache-prebuilt-binary) or [from source](#sccache-from-source).\n2. Extend a [Dockerfile](#sccache-dockerfile) to install sccache in the image.\n3. Passthrough the appropriate environment variables in [Cross.toml](#sccache-cross-toml) when using sccache.\n\n<h3 id=\"sccache-install-script\">Install Script</h3>\n\nFirst, we need a script to copy into our image as `sccache.sh` (make sure the script is executable).\n\n<h4 id=\"sccache-prebuilt-binary\">Pre-Built Binary</h4>\n\n```bash\n#!/bin/bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local triple\n    local tag\n    local td\n    local url=\"https://github.com/mozilla/sccache\"\n    triple=\"${1}\"\n\n    install_packages unzip tar\n\n    # Download our package, then install our binary.\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n    tag=$(git ls-remote --tags --refs --exit-code \\\n        \"${url}\" \\\n        | cut -d/ -f3 \\\n        | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+$' \\\n        | sort --version-sort \\\n        | tail -n1)\n    curl -LSfs \"${url}/releases/download/${tag}/sccache-${tag}-${triple}.tar.gz\" \\\n        -o sccache.tar.gz\n    tar -xvf sccache.tar.gz\n    rm sccache.tar.gz\n    cp \"sccache-${tag}-${triple}/sccache\" \"/usr/bin/sccache\"\n    chmod +x \"/usr/bin/sccache\"\n\n    # clean up our install\n    purge_packages\n    popd\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n```\n\n<h4 id=\"sccache-from-source\">From Source</h4>\n\nWhen installing from source, we can toggle various features, however it is highly recommended to use the vendored OpenSSL.\n\n```bash\n#!/bin/bash\n\nset -x\nset -euo pipefail\n\n# shellcheck disable=SC1091\n. lib.sh\n\nmain() {\n    local triple\n    local tag\n    local td\n    local url=\"https://github.com/mozilla/sccache\"\n    triple=\"${1}\"\n\n    install_packages ca-certificates curl unzip\n\n    # install rust and cargo to build sccache\n    export RUSTUP_HOME=/tmp/rustup\n    export CARGO_HOME=/tmp/cargo\n    curl --retry 3 -sSfL https://sh.rustup.rs -o rustup-init.sh\n    sh rustup-init.sh -y --no-modify-path\n    rm rustup-init.sh\n    export PATH=\"${CARGO_HOME}/bin:${PATH}\"\n    rustup target add \"${triple}\"\n\n    # download the source code from the latest sccache release\n    td=\"$(mktemp -d)\"\n    pushd \"${td}\"\n    tag=$(git ls-remote --tags --refs --exit-code \\\n        \"${url}\" \\\n        | cut -d/ -f3 \\\n        | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+$' \\\n        | sort --version-sort \\\n        | tail -n1)\n    curl -LSfs \"${url}/archive/refs/tags/${tag}.zip\" \\\n        -o sccache.zip\n    unzip sccache.zip\n    mv \"sccache-${tag//v/}\" sccache\n    rm sccache.zip\n\n    # build from source for the desired architecture\n    # you can also use additional features here\n    cd sccache\n    cargo build --release --target \"${triple}\" \\\n        --features=all,\"openssl/vendored\"\n    cp \"target/${triple}/release/sccache\" \"/usr/bin/sccache\"\n\n    # clean up our install\n    rm -r \"${RUSTUP_HOME}\" \"${CARGO_HOME}\"\n    purge_packages\n    popd\n    rm -rf \"${td}\"\n    rm \"${0}\"\n}\n\nmain \"${@}\"\n```\n\n<h3 id=\"sccache-dockerfile\">Dockerfile</h3>\n\nNext, extend our Dockerfile and build our image, saved as `Dockerfile.${target}`, where `${target}` is replaced by our desired target (such as `x86_64-unknown-linux-musl`).\n\n```Dockerfile\nFROM ghcr.io/cross-rs/${target}:main\nARG DEBIAN_FRONTEND=noninteractive\n\nCOPY sccache.sh /\nRUN /sccache.sh x86_64-unknown-linux-musl\n\nENV RUSTC_WRAPPER=\"/usr/bin/sccache\"\n```\n\nBuild our Docker image with:\n\n```bash\ndocker build --tag ${target}:sccache \\\n    --file Dockerfile.${target} .\n```\n\n<h3 id=\"sccache-cross-toml\">Cross.toml</h3>\n\nNow, we need to passthrough our environment variables and ensure they're exported when running cross. In `Cross.toml`, define:\n\n```toml\n[target.${target}]\nimage = \"${target}:sccache\"\n\n[build.env]\npassthrough = [\n    \"SCCACHE_ERROR_LOG\",\n    \"SCCACHE_LOG\",\n    \"SCCACHE_AZURE_CONNECTION_STRING\",\n    \"SCCACHE_AZURE_BLOB_CONTAINER\",\n    \"SCCACHE_DIR\",\n]\n```\n\n<h3 >Building with sccache</h3>\n\nFinally, we can run cross with our `sccache` environment variables defined using `cross`:\n\n```bash\nSCCACHE_LOG=trace SCCACHE_DIR=/path/to/sccache/cache \\\n    cross build --target \"${target}\" --verbose\n```\n\n# Redoxer\n\nRedoxer support can be done by installing the necessary dependencies, Redoxer, and the Redoxer toolchain in a custom image. See [redoxer](https://github.com/cross-rs/wiki_assets/tree/main/Recipes/redoxer) for a working project using a custom Dockerfile.\n\nPlease note that this requires a base Ubuntu version of 20.04, and therefore needs you to build the images with [newer Linux versions](https://github.com/cross-rs/cross/wiki/FAQ#newer-linux-versions).\n\n# vcpkg, Meson, and Conan\n\nOften C++ projects have complex build systems, due to a myriad of dependencies, competing build systems, and the lack of a built-in package manager. Some of the most popular build systems include GNU Make, CMake, and [Meson](https://mesonbuild.com/), and the two most popular package managers are [vcpkg](https://vcpkg.io/en/index.html) and [Conan](https://conan.io/). We have an entire [project](https://github.com/cross-rs/wiki_assets/tree/main/Recipes/vcpkg) with builds using CMake + Conan, Meson + Conan, and CMake + vcpkg.\n\nAn example of building a project with an external `zlib` dependency using Meson and Conan is as follows. First, we create our Conan dependency file:\n\n**conanfile.py**\n\n```python\nfrom conans import ConanFile, Meson\n\nclass ZlibExec(ConanFile):\n    name = \"zlibexec\"\n    version = \"0.1\"\n    settings = \"os\", \"compiler\", \"build_type\", \"arch\"\n    generators = \"cmake\", \"pkg_config\"\n    requires = \"zlib/1.2.11\"\n\n    def build(self):\n        meson = Meson(self)\n        meson.configure(build_folder=\"build\")\n        meson.build()\n```\n\nNext, we need our Meson build file:\n\n**meson.build**\n\n```meson\nproject('zlibexec', 'cpp')\nexecutable('zlibexec', 'zlib.cc', dependencies: dependency('zlib'))\n```\n\nNow, we need to build our project:\n\n```bash\nmkdir build && cd build\nconan install .. --build\nmeson ..\nconan build ..\n```\n\nTo make this magic happen, the project contains [Dockerfiles](https://github.com/cross-rs/wiki_assets/blob/main/Recipes/vcpkg/aarch64.Dockerfile) with Meson, Conan, and vcpkg installed where the CMake toolchains and Meson configurations automatically cross-compile for the desire architecture. These images are [automatically](https://github.com/cross-rs/wiki_assets/blob/main/Recipes/vcpkg/Cross.toml) built when running `cross` via [pre-build hooks](https://github.com/cross-rs/cross/wiki/Configuration#custom-images). In order to integrate these builds with Rust, rather than invoking the `meson` or `cmake` commands directly, you should use [meson-rs](https://docs.rs/meson/1.0.0/meson/) and [cmake-rs](https://docs.rs/cmake/latest/cmake/) to configure and build the projects.\n\n# Using Clang and Software Collections on CentOS7\n\nIn order to use Clang on CentOS 7, you must both install the SCL repository, the LLVM toolset, and set the necessary paths to clang and LLVM. A sample Dockerfile is as follows:\n\n```Dockerfile\nFROM ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main-centos\n\nRUN yum update -y && \\\n    yum install centos-release-scl -y && \\\n    yum install llvm-toolset-7 -y\n\nENV LIBCLANG_PATH=/opt/rh/llvm-toolset-7/root/usr/lib64/ \\\n    LIBCLANG_STATIC_PATH=/opt/rh/llvm-toolset-7/root/usr/lib64/ \\\n    CLANG_PATH=/opt/rh/llvm-toolset-7/root/usr/bin/clang\n```\n\nBuild this image and use it, as is described extensively in [Custom Images](./custom_images.md).\n"
  },
  {
    "path": "docs/remote.md",
    "content": "<!--toc:start-->\n- [Getting Started](#getting-started)\n- [Data Volumes](#data-volumes)\n- [Managing Data](#managing-data)\n- [Private Dependencies](#private-dependencies)\n- [Environment Variables](#environment-variables)\n<!--toc:end-->\n\n\n# Getting Started\n\nTo inform `cross` it is using a remote container engine, set the environment\nvariable `CROSS_REMOTE=1`. Rather than use bind mounts to mount local volumes\ninto the filesystem, it copies data from the local filesystem into data volumes\non the remote host, ensuring all mounted volumes are present on the remote\nfilesystem in the same location as they would be present on the host. A sample\nuse with docker is:\n\n```bash\nCROSS_REMOTE=1 DOCKER_HOST=tcp://docker:2375/ cross build --target arm-unknown-linux-gnueabihf\n```\n\nIf using [docker\ncontexts](https://docs.docker.com/engine/context/working-with-contexts/), you\ndo not need to provide `DOCKER_HOST`. This also works with podman and\npodman-remote. For podman remote, make sure the connection is\n[added](https://docs.podman.io/en/latest/markdown/podman-system-connection-add.1.html)\nto podman, and if using a connection that requires authentication, that it is\nset to the default identity, and that you do not use an identity (to avoid\nbeing prompted for a password on every command).\n\nAn example with podman is:\n\n```bash\npodman system connection add cross tcp://localhost:8080 --default=true\nCROSS_REMOTE=1 CROSS_CONTAINER_ENGINE=podman cross build --target arm-unknown-linux-gnueabihf\n```\n\nAny command using `podman` can be replaced with `podman-remote`. Cross\nautomatically detects if `podman` or `podman-remote` is being used, and adds in\nthe `--remote` flag if needed.\n\n\n# Data Volumes\n\nSince we cannot use bind mounts directly, we create a data volume mounted at\n`/cross` in the container, and copy over all relevant data to that volume. To\nensure paths are identical with those on the host, all data is then symlinked\nto the expected paths: for example, `/cross/home/user/project` becomes\n`/home/user/project`. The files will have the expected metadata as on the host.\n\nBy default, `cross` does not copy the `cargo` registry, not the target\ndirectory. These can be enabled via the `CROSS_REMOTE_COPY_REGISTRY` and\n`CROSS_REMOTE_COPY_CACHE` environment variables. In order to minimize the\nnumber of calls to docker, which has large overhead, when copying only subsets\nof a directory, we copy all files to a temporary directory for faster\nperformance.\n\nSince copying the entire toolchain remotely can take a long time, `cross` also\nsupports persistent data volumes containing all data for the current toolchain.\nThese can be created via:\n\n```bash\ncross-util volumes crate\n```\n\n`cross` will detect if a persistent data volume is present, and prefer it over\na single-use volume. The persistent data volume will also contain the project\nfiles, and will reflect any changes to the local project by copying/removing\nchanged files on every build.\n\n\n# Managing Data\n\nDue to the addition of temporary files/directories, persistent data volumes,\nand containers that may not be cleaned up, we've added utilities to clean up\ndata cross introduces:\n\n```bash\n# VOLUMES\n# list all all persistent data volumes\n$ cross-util volumes list\ncross-stable-x86_64-unknown-linux-gnu-16b8c-fe5b13d68\n# create a persistent data volume for the current toolchain\n$ cross-util volumes create\n# remove the persistent data volume for the current toolchain\n$ cross-util volumes remove\n# remove all persistent data volumes\n$ cross-util volumes remove-all\n# prune all data volumes not currently used with a container\n# note that this affects more than just cross, and can\n# be highly destructive\n$ cross-util volumes prune\n\n# CONTAINERS\n# list all all hanging containers\n$ cross-util containers list\n# stop and remove all hanging containers\n$ cross-util containers remove-all\n```\n\n\n# Private Dependencies\n\nNote that for private dependencies, you will need to copy over the cargo\nregistry, since it uses the host toolchain, and might only support single-use\nvolumes (this has not been extensively tested):\n\n```bash\nCROSS_REMOTE_COPY_REGISTRY=1 CROSS_REMOTE=1 cross build --target arm-unknown-linux-gnueabihf\n```\n\nThis is because the private dependencies are downloaded locally (to the host\nregistry), and therefore must be updated remotely, which will not have access\nto SSH keys or other information inside the container.\n\n\n# Environment Variables\n\nRemote build behavior can be further customized by environment variables\nprovided to the build command.\n\n- `CROSS_REMOTE`: Inform cross it is using a remote container engine, and use\n  data volumes rather than local bind mounts. \n- `CROSS_REMOTE_COPY_REGISTRY`: Copy the `cargo` registry and git directories.\n  Is needed to support  private SSH dependencies.\n- `CROSS_REMOTE_COPY_CACHE`: Copy all directories, even those containing\n  `CACHETAG.DIR` (a cache directory [tag](https://bford.info/cachedir/)).\n- `CROSS_REMOTE_SKIP_BUILD_ARTIFACTS`: Do not copy any generated build\n  artifacts back to the host after finishing the build. If using persistent\n  data volumes, the artifacts will remain in the volume.\n\nFor additional environment variables, refer to the [environment variables\ndocumentation][docs-env-vars].\n\n[docs-env-vars]: ./environment_variables.md\n"
  },
  {
    "path": "docs/unstable_features.md",
    "content": "Certain unstable features can enable additional functionality useful to\ncross-compiling. Note that these are unstable, and may be removed at any time\n(particularly if the feature is stabilized or removed), and will only be used\non a nightly channel.\n\nHere is the list of currently available unstable features:\n\n- `CROSS_UNSTABLE_ENABLE_DOCTESTS`: enable or disable running doctests\n  (example: `true`)\n"
  },
  {
    "path": "rustfmt.yml",
    "content": "condense_wildcard_suffixes = true\nbrace_style = \"PreferSameLine\"\nfn_single_line = true\nwhere_single_line = true\nuse_field_init_shorthand = true\nreorder_impl_items = true\nedition = \"2021\"\nnewline_style = \"Unix\"\nformat_code_in_doc_comments = true\n"
  },
  {
    "path": "src/bin/commands/clean.rs",
    "content": "use std::fs;\n\nuse super::containers::*;\nuse super::images::*;\nuse clap::Args;\nuse cross::shell::MessageInfo;\n\n#[derive(Args, Debug)]\npub struct Clean {\n    /// Force removal of images.\n    #[clap(short, long)]\n    pub force: bool,\n    /// Remove local (development) images.\n    #[clap(short, long)]\n    pub local: bool,\n    /// Remove images. Default is a dry run.\n    #[clap(short, long)]\n    pub execute: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl Clean {\n    pub fn run(\n        &self,\n        engine: cross::docker::Engine,\n        msg_info: &mut MessageInfo,\n    ) -> cross::Result<()> {\n        let tempdir = cross::temp::dir()?;\n        match self.execute {\n            true => {\n                if tempdir.exists() {\n                    fs::remove_dir_all(tempdir)?;\n                }\n            }\n            false => msg_info.print(format_args!(\n                \"fs::remove_dir_all({})\",\n                cross::pretty_path(&tempdir, |_| false)\n            ))?,\n        }\n\n        // containers -> images -> volumes -> prune to ensure no conflicts.\n        let remove_containers = RemoveAllContainers {\n            force: self.force,\n            execute: self.execute,\n            engine: None,\n        };\n        remove_containers.run(engine.clone(), msg_info)?;\n\n        let remove_images = RemoveImages {\n            targets: vec![],\n            force: self.force,\n            local: self.local,\n            execute: self.execute,\n            engine: None,\n        };\n        remove_images.run(engine.clone(), msg_info)?;\n\n        let remove_volumes = RemoveAllVolumes {\n            force: self.force,\n            execute: self.execute,\n            engine: None,\n        };\n        remove_volumes.run(engine.clone(), msg_info)?;\n\n        let prune_volumes = PruneVolumes {\n            execute: self.execute,\n            engine: None,\n        };\n        prune_volumes.run(engine, msg_info)?;\n\n        Ok(())\n    }\n\n    pub fn engine(&self) -> Option<&str> {\n        self.engine.as_deref()\n    }\n}\n"
  },
  {
    "path": "src/bin/commands/containers.rs",
    "content": "use std::io;\n\nuse clap::{Args, Subcommand};\nuse cross::docker::ImagePlatform;\nuse cross::rustc::{QualifiedToolchain, Toolchain};\nuse cross::shell::MessageInfo;\nuse cross::{docker, CommandExt, TargetTriple};\nuse is_terminal::IsTerminal;\n\n#[derive(Args, Debug)]\npub struct ListVolumes {\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl ListVolumes {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        list_volumes(&engine, msg_info)\n    }\n}\n\n#[derive(Args, Debug)]\npub struct RemoveAllVolumes {\n    /// Force removal of volumes.\n    #[clap(short, long)]\n    pub force: bool,\n    /// Remove volumes. Default is a dry run.\n    #[clap(short, long)]\n    pub execute: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl RemoveAllVolumes {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        remove_all_volumes(self, &engine, msg_info)\n    }\n}\n\n#[derive(Args, Debug)]\npub struct PruneVolumes {\n    /// Remove volumes. Default is a dry run.\n    #[clap(short, long)]\n    pub execute: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl PruneVolumes {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        prune_volumes(self, &engine, msg_info)\n    }\n}\n\n#[derive(Args, Debug)]\npub struct CreateVolume {\n    /// If cross is running inside a container.\n    #[clap(short, long)]\n    pub docker_in_docker: bool,\n    /// If we should copy the cargo registry to the volume.\n    #[clap(short, long)]\n    pub copy_registry: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n    /// Toolchain to create a volume for\n    #[clap(long, default_value = TargetTriple::DEFAULT.triple(), )]\n    pub toolchain: String,\n}\n\nimpl CreateVolume {\n    pub fn run(\n        &self,\n        engine: docker::Engine,\n        channel: Option<&Toolchain>,\n        msg_info: &mut MessageInfo,\n    ) -> cross::Result<()> {\n        create_persistent_volume(self, &engine, channel, msg_info)\n    }\n}\n\n#[derive(Args, Debug)]\npub struct RemoveVolume {\n    /// FIXME: remove in 0.3.0, remains since it's a breaking change.\n    #[clap(long, hide = true)]\n    pub target: Option<String>,\n    /// If cross is running inside a container.\n    #[clap(short, long)]\n    pub docker_in_docker: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n    /// Toolchain to remove the volume for\n    #[clap(long, default_value = TargetTriple::DEFAULT.triple(), )]\n    pub toolchain: String,\n}\n\nimpl RemoveVolume {\n    pub fn run(\n        &self,\n        engine: docker::Engine,\n        channel: Option<&Toolchain>,\n        msg_info: &mut MessageInfo,\n    ) -> cross::Result<()> {\n        remove_persistent_volume(self, &engine, channel, msg_info)\n    }\n}\n\n#[derive(Subcommand, Debug)]\npub enum Volumes {\n    /// List cross data volumes in local storage.\n    List(ListVolumes),\n    /// Remove cross data volumes in local storage.\n    RemoveAll(RemoveAllVolumes),\n    /// Prune volumes not used by any container.\n    Prune(PruneVolumes),\n    /// Create a persistent data volume for a given toolchain.\n    Create(CreateVolume),\n    /// Remove a persistent data volume for a given toolchain.\n    Remove(RemoveVolume),\n}\nimpl Volumes {\n    pub fn run(\n        &self,\n        engine: docker::Engine,\n        channel: Option<&Toolchain>,\n        msg_info: &mut MessageInfo,\n    ) -> cross::Result<()> {\n        match self {\n            Volumes::List(args) => args.run(engine, msg_info),\n            Volumes::RemoveAll(args) => args.run(engine, msg_info),\n            Volumes::Prune(args) => args.run(engine, msg_info),\n            Volumes::Create(args) => args.run(engine, channel, msg_info),\n            Volumes::Remove(args) => args.run(engine, channel, msg_info),\n        }\n    }\n\n    pub fn engine(&self) -> Option<&str> {\n        match self {\n            Volumes::List(l) => l.engine.as_deref(),\n            Volumes::RemoveAll(l) => l.engine.as_deref(),\n            Volumes::Prune(l) => l.engine.as_deref(),\n            Volumes::Create(l) => l.engine.as_deref(),\n            Volumes::Remove(l) => l.engine.as_deref(),\n        }\n    }\n\n    // FIXME: remove this in v0.3.0.\n    pub fn docker_in_docker(&self) -> bool {\n        match self {\n            Volumes::List(_) => false,\n            Volumes::RemoveAll(_) => false,\n            Volumes::Prune(_) => false,\n            Volumes::Create(l) => l.docker_in_docker,\n            Volumes::Remove(l) => l.docker_in_docker,\n        }\n    }\n}\n\n#[derive(Args, Debug)]\npub struct ListContainers {\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl ListContainers {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        list_containers(&engine, msg_info)\n    }\n}\n\n#[derive(Args, Debug)]\npub struct RemoveAllContainers {\n    /// Force removal of containers.\n    #[clap(short, long)]\n    pub force: bool,\n    /// Remove containers. Default is a dry run.\n    #[clap(short, long)]\n    pub execute: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl RemoveAllContainers {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        remove_all_containers(self, &engine, msg_info)\n    }\n}\n\n#[derive(Subcommand, Debug)]\npub enum Containers {\n    /// List cross containers in local storage.\n    List(ListContainers),\n    /// Stop and remove cross containers in local storage.\n    RemoveAll(RemoveAllContainers),\n}\n\nimpl Containers {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        match self {\n            Containers::List(args) => args.run(engine, msg_info),\n            Containers::RemoveAll(args) => args.run(engine, msg_info),\n        }\n    }\n\n    pub fn engine(&self) -> Option<&str> {\n        match self {\n            Containers::List(l) => l.engine.as_deref(),\n            Containers::RemoveAll(l) => l.engine.as_deref(),\n        }\n    }\n}\n\nfn get_cross_volumes(\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<Vec<String>> {\n    use cross::docker::VOLUME_PREFIX;\n    let stdout = engine\n        .subcommand(\"volume\")\n        .arg(\"list\")\n        .args([\"--format\", \"{{.Name}}\"])\n        // handles simple regex: ^ for start of line.\n        .args([\"--filter\", &format!(\"name=^{VOLUME_PREFIX}\")])\n        .run_and_get_stdout(msg_info)?;\n\n    let mut volumes: Vec<String> = stdout.lines().map(|s| s.to_string()).collect();\n    volumes.sort();\n\n    Ok(volumes)\n}\n\npub fn list_volumes(engine: &docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    for line in get_cross_volumes(engine, msg_info)?.iter() {\n        msg_info.print(line)?;\n    }\n\n    Ok(())\n}\n\npub fn remove_all_volumes(\n    RemoveAllVolumes { force, execute, .. }: &RemoveAllVolumes,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let volumes = get_cross_volumes(engine, msg_info)?;\n\n    let mut command = engine.subcommand(\"volume\");\n    command.arg(\"rm\");\n    if *force {\n        command.arg(\"--force\");\n    }\n    command.args(&volumes);\n    if volumes.is_empty() {\n        Ok(())\n    } else if *execute {\n        command.run(msg_info, false)\n    } else {\n        msg_info.note(\"this is a dry run. to remove the volumes, pass the `--execute` flag.\")?;\n        command.print(msg_info)?;\n        Ok(())\n    }\n}\n\npub fn prune_volumes(\n    PruneVolumes { execute, .. }: &PruneVolumes,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut command = engine.subcommand(\"volume\");\n    command.args([\"prune\", \"--force\"]);\n    if *execute {\n        command.run(msg_info, false)\n    } else {\n        msg_info.note(\"this is a dry run. to prune the volumes, pass the `--execute` flag.\")?;\n        command.print(msg_info)?;\n        Ok(())\n    }\n}\n\npub fn create_persistent_volume(\n    CreateVolume {\n        copy_registry,\n        toolchain,\n        ..\n    }: &CreateVolume,\n    engine: &docker::Engine,\n    channel: Option<&Toolchain>,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut toolchain = toolchain_or_target(toolchain, msg_info)?;\n    if let Some(channel) = channel {\n        toolchain.channel = channel.channel.clone();\n    };\n    let mount_finder = docker::MountFinder::create(engine, msg_info)?;\n    let dirs = docker::ToolchainDirectories::assemble(&mount_finder, toolchain.clone())?;\n    let container_id = dirs.unique_container_identifier(&toolchain.host().target)?;\n    let volume_id = dirs.unique_toolchain_identifier()?;\n    let volume = docker::DockerVolume::new(engine, &volume_id);\n\n    if volume.exists(msg_info)? {\n        eyre::bail!(\"Error: volume {volume_id} already exists.\");\n    }\n\n    volume.create(msg_info)?;\n\n    // stop the container if it's already running\n    let container = docker::DockerContainer::new(engine, &container_id);\n    let state = container.state(msg_info)?;\n    if !state.is_stopped() {\n        msg_info.warn(format_args!(\"container {container_id} was running.\"))?;\n        container.stop_default(msg_info)?;\n    }\n    if state.exists() {\n        msg_info.warn(format_args!(\"container {container_id} was exited.\"))?;\n        container.remove(msg_info)?;\n    }\n\n    // create a dummy running container to copy data over\n    let mount_prefix = docker::MOUNT_PREFIX;\n    let mut docker = engine.subcommand(\"run\");\n    docker.args([\"--name\", &container_id]);\n    docker.arg(\"--rm\");\n    docker.args([\"-v\", &format!(\"{}:{}\", volume_id, mount_prefix)]);\n    docker.arg(\"-d\");\n    let is_tty =\n        io::stdin().is_terminal() && io::stdout().is_terminal() && io::stderr().is_terminal();\n    if is_tty {\n        docker.arg(\"-t\");\n    }\n    docker.arg(docker::UBUNTU_BASE);\n    if !is_tty {\n        // ensure the process never exits until we stop it\n        // we only need this infinite loop if we don't allocate\n        // a TTY. this has a few issues though: now, the\n        // container no longer responds to signals, so the\n        // container will need to be sig-killed.\n        docker.args([\"sh\", \"-c\", \"sleep infinity\"]);\n    }\n    // store first, since failing to non-existing container is fine\n    docker::ChildContainer::create(engine.clone(), container_id.clone())?;\n    docker.run_and_get_status(msg_info, true)?;\n\n    let data_volume = docker::ContainerDataVolume::new(engine, &container_id, &dirs);\n    data_volume.copy_cargo(mount_prefix, *copy_registry, msg_info)?;\n    data_volume.copy_rust(None, mount_prefix, msg_info)?;\n\n    docker::ChildContainer::finish_static(is_tty, msg_info);\n\n    Ok(())\n}\n\npub fn remove_persistent_volume(\n    RemoveVolume { toolchain, .. }: &RemoveVolume,\n    engine: &docker::Engine,\n    channel: Option<&Toolchain>,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut toolchain = toolchain_or_target(toolchain, msg_info)?;\n    if let Some(channel) = channel {\n        toolchain.channel = channel.channel.clone();\n    };\n    let mount_finder = docker::MountFinder::create(engine, msg_info)?;\n    let dirs = docker::ToolchainDirectories::assemble(&mount_finder, toolchain)?;\n    let volume_id = dirs.unique_toolchain_identifier()?;\n    let volume = docker::DockerVolume::new(engine, &volume_id);\n\n    if !volume.exists(msg_info)? {\n        eyre::bail!(\"Error: volume {volume_id} does not exist.\");\n    }\n\n    volume.remove(msg_info)?;\n\n    Ok(())\n}\n\nfn get_cross_containers(\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<Vec<String>> {\n    use cross::docker::VOLUME_PREFIX;\n    let stdout = engine\n        .subcommand(\"ps\")\n        .arg(\"-a\")\n        .args([\"--format\", \"{{.Names}}: {{.State}}\"])\n        // handles simple regex: ^ for start of line.\n        .args([\"--filter\", &format!(\"name=^{VOLUME_PREFIX}\")])\n        .run_and_get_stdout(msg_info)?;\n\n    let mut containers: Vec<String> = stdout.lines().map(|s| s.to_string()).collect();\n    containers.sort();\n\n    Ok(containers)\n}\n\npub fn list_containers(engine: &docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    for line in get_cross_containers(engine, msg_info)?.iter() {\n        msg_info.print(line)?;\n    }\n\n    Ok(())\n}\n\npub fn remove_all_containers(\n    RemoveAllContainers { force, execute, .. }: &RemoveAllContainers,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let containers = get_cross_containers(engine, msg_info)?;\n    let mut running = vec![];\n    let mut stopped = vec![];\n    for container in containers.iter() {\n        // cannot fail, formatted as {{.Names}}: {{.State}}\n        let (name, state) = container.split_once(':').unwrap();\n        let name = name.trim();\n        let state = docker::ContainerState::new(state.trim())?;\n        if state.is_stopped() {\n            stopped.push(name);\n        } else {\n            running.push(name);\n        }\n    }\n\n    let mut commands = vec![];\n    if !running.is_empty() {\n        let mut stop = engine.subcommand(\"stop\");\n        stop.args(&running);\n        commands.push(stop);\n    }\n\n    if !(stopped.is_empty() && running.is_empty()) {\n        let mut rm = engine.subcommand(\"rm\");\n        if *force {\n            rm.arg(\"--force\");\n        }\n        rm.args(&running);\n        rm.args(&stopped);\n        commands.push(rm);\n    }\n    if *execute {\n        for mut command in commands {\n            command.run(msg_info, false)?;\n        }\n    } else {\n        msg_info.note(\"this is a dry run. to remove the containers, pass the `--execute` flag.\")?;\n        for command in commands {\n            command.print(msg_info)?;\n        }\n    }\n\n    Ok(())\n}\n\nfn toolchain_or_target(\n    s: &str,\n    msg_info: &mut MessageInfo,\n) -> Result<QualifiedToolchain, color_eyre::Report> {\n    let config = cross::config::Config::new(None);\n    let mut toolchain = QualifiedToolchain::default(&config, msg_info)?;\n    let target_list = cross::rustc::target_list(msg_info)?;\n    if target_list.contains(s) {\n        toolchain.replace_host(&ImagePlatform::from_target(s.into())?);\n    } else {\n        let picked: Toolchain = s.parse()?;\n        toolchain = toolchain.with_picked(picked)?;\n    }\n\n    Ok(toolchain)\n}\n"
  },
  {
    "path": "src/bin/commands/images.rs",
    "content": "use std::collections::{BTreeMap, BTreeSet};\n\nuse clap::builder::PossibleValue;\nuse clap::{Args, Subcommand};\nuse cross::docker::{self, CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX};\nuse cross::shell::MessageInfo;\nuse cross::{CommandExt, TargetList};\n\n// known image prefixes, with their registry\n// the docker.io registry can also be implicit\n\nconst GHCR_IO: &str = docker::CROSS_IMAGE;\nconst RUST_EMBEDDED: &str = \"rustembedded/cross\";\nconst DOCKER_IO: &str = \"docker.io/rustembedded/cross\";\nconst IMAGE_PREFIXES: &[&str] = &[GHCR_IO, DOCKER_IO, RUST_EMBEDDED];\n\n#[derive(Args, Debug)]\npub struct ListImages {\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n    /// Output format\n    #[clap(long, default_value = \"human\")]\n    pub format: OutputFormat,\n    /// Only list images for specific target(s). By default, list all targets.\n    pub targets: Vec<String>,\n}\n\nimpl ListImages {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        list_images(self, &engine, msg_info)\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum OutputFormat {\n    Human,\n    Json,\n}\n\nimpl clap::ValueEnum for OutputFormat {\n    fn value_variants<'a>() -> &'a [Self] {\n        &[Self::Human, Self::Json]\n    }\n\n    fn to_possible_value(&self) -> Option<PossibleValue> {\n        match self {\n            OutputFormat::Human => Some(PossibleValue::new(\"human\")),\n            OutputFormat::Json => Some(PossibleValue::new(\"json\")),\n        }\n    }\n}\n\n#[derive(Args, Debug)]\npub struct RemoveImages {\n    /// If not provided, remove all images.\n    pub targets: Vec<String>,\n    /// Force removal of images.\n    #[clap(short, long)]\n    pub force: bool,\n    /// Remove local (development) images.\n    #[clap(short, long)]\n    pub local: bool,\n    /// Remove images. Default is a dry run.\n    #[clap(short, long)]\n    pub execute: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nimpl RemoveImages {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        if self.targets.is_empty() {\n            remove_all_images(self, &engine, msg_info)\n        } else {\n            remove_target_images(self, &engine, msg_info)\n        }\n    }\n}\n\n#[derive(Subcommand, Debug)]\npub enum Images {\n    /// List cross images in local storage.\n    List(ListImages),\n    /// Remove cross images in local storage.\n    Remove(RemoveImages),\n}\n\nimpl Images {\n    pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {\n        match self {\n            Images::List(args) => args.run(engine, msg_info),\n            Images::Remove(args) => args.run(engine, msg_info),\n        }\n    }\n\n    pub fn engine(&self) -> Option<&str> {\n        match self {\n            Images::List(l) => l.engine.as_deref(),\n            Images::Remove(l) => l.engine.as_deref(),\n        }\n    }\n}\n\n#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Serialize)]\nstruct Image {\n    repository: String,\n    tag: String,\n    // need to remove images by ID, not just tag\n    id: String,\n}\n\nimpl std::fmt::Display for Image {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if self.repository == \"<none>\" {\n            f.write_str(&self.id)\n        } else {\n            f.write_str(&self.name())\n        }\n    }\n}\n\nimpl Image {\n    fn name(&self) -> String {\n        format!(\"{}:{}\", self.repository, self.tag)\n    }\n}\n\nfn parse_image(image: &str) -> Image {\n    // this cannot panic: we've formatted our image list as `${repo}:${tag} ${id}`\n    let (repository, rest) = image.split_once(':').unwrap();\n    let (tag, id) = rest.split_once(' ').unwrap();\n    Image {\n        repository: repository.to_string(),\n        tag: tag.to_string(),\n        id: id.to_string(),\n    }\n}\n\nfn is_cross_image(repository: &str) -> bool {\n    IMAGE_PREFIXES.iter().any(|i| repository.starts_with(i))\n}\n\nfn is_local_image(tag: &str) -> bool {\n    tag.starts_with(\"local\")\n}\n\nfn get_cross_images(\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n    local: bool,\n) -> cross::Result<Vec<Image>> {\n    let mut images: BTreeSet<_> = engine\n        .subcommand(\"images\")\n        .args([\"--format\", \"{{.Repository}}:{{.Tag}} {{.ID}}\"])\n        .args([\n            \"--filter\",\n            &format!(\"label={}.for-cross-target\", cross::CROSS_LABEL_DOMAIN),\n        ])\n        .run_and_get_stdout(msg_info)?\n        .lines()\n        .map(parse_image)\n        .collect();\n\n    let stdout = engine\n        .subcommand(\"images\")\n        .args([\"--format\", \"{{.Repository}}:{{.Tag}} {{.ID}}\"])\n        .run_and_get_stdout(msg_info)?;\n    let ids: Vec<_> = images.iter().map(|i| i.id.to_string()).collect();\n    images.extend(\n        stdout\n            .lines()\n            .map(parse_image)\n            .filter(|i| !ids.iter().any(|id| id == &i.id))\n            .filter(|image| is_cross_image(&image.repository))\n            .filter(|image| local || !is_local_image(&image.tag)),\n    );\n\n    Ok(images.into_iter().collect())\n}\n\n// the old rustembedded targets had the following format:\n//  repository = (${registry}/)?rustembedded/cross\n//  tag = ${target}(-${version})?\n// the last component must match `[A-Za-z0-9_-]` and\n// we must have at least 3 components. the first component\n// may contain other characters, such as `thumbv8m.main-none-eabi`.\nfn rustembedded_target(tag: &str) -> String {\n    let is_target_char = |c: char| c == '_' || c.is_ascii_alphanumeric();\n    let mut components = vec![];\n    for (index, component) in tag.split('-').enumerate() {\n        if index <= 2 || (!component.is_empty() && component.chars().all(is_target_char)) {\n            components.push(component)\n        } else {\n            break;\n        }\n    }\n\n    components.join(\"-\")\n}\n\nfn get_image_target(\n    engine: &cross::docker::Engine,\n    image: &Image,\n    target_list: &TargetList,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<String> {\n    if let Some(stripped) = image.repository.strip_prefix(&format!(\"{GHCR_IO}/\")) {\n        return Ok(stripped.to_string());\n    } else if let Some(tag) = image.tag.strip_prefix(RUST_EMBEDDED) {\n        return Ok(rustembedded_target(tag));\n    } else if let Some(tag) = image.tag.strip_prefix(DOCKER_IO) {\n        return Ok(rustembedded_target(tag));\n    } else if image\n        .repository\n        .starts_with(CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX)\n    {\n        if let Some(target) = target_list\n            .triples\n            .iter()\n            .find(|target| image.tag.starts_with(target.as_str()))\n            .cloned()\n        {\n            return Ok(target);\n        }\n    }\n    let mut command = engine.subcommand(\"inspect\");\n    command.args([\n        \"--format\",\n        &format!(\n            r#\"{{{{index .Config.Labels \"{}.for-cross-target\"}}}}\"#,\n            cross::CROSS_LABEL_DOMAIN\n        ),\n    ]);\n    command.arg(&image.id);\n\n    let target = command.run_and_get_stdout(msg_info)?;\n    if target.trim().is_empty() {\n        eyre::bail!(\"cannot get target for image {}\", image)\n    }\n    Ok(target.trim().to_string())\n}\n\npub fn list_images(\n    ListImages {\n        targets, format, ..\n    }: &ListImages,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let cross_images = get_cross_images(engine, msg_info, true)?;\n    let target_list = msg_info.as_quiet(cross::rustc::target_list)?;\n    let mut map: BTreeMap<String, Vec<Image>> = BTreeMap::new();\n    let mut max_target_len = 0;\n    let mut max_image_len = 0;\n    for image in cross_images {\n        let target = get_image_target(engine, &image, &target_list, msg_info)?;\n        if targets.is_empty() || targets.contains(&target) {\n            if !map.contains_key(&target) {\n                map.insert(target.clone(), vec![]);\n            }\n            max_target_len = target.len().max(max_target_len);\n            max_image_len = image.name().len().max(max_image_len);\n            map.get_mut(&target).expect(\"map must have key\").push(image);\n        }\n    }\n    let mut keys: Vec<&str> = map.keys().map(|k| k.as_ref()).collect();\n    keys.sort_unstable();\n\n    match format {\n        OutputFormat::Json => {\n            msg_info.info(format_args!(\"{}\", serde_json::to_string(&map)?))?;\n        }\n        OutputFormat::Human => {\n            let print_string =\n                |col1: &str, col2: &str, fill: char, info: &mut MessageInfo| -> cross::Result<()> {\n                    let mut row = String::new();\n                    row.push('|');\n                    row.push(fill);\n                    row.push_str(col1);\n                    let spaces = max_target_len.max(col1.len()) + 1 - col1.len();\n                    for _ in 0..spaces {\n                        row.push(fill);\n                    }\n                    row.push('|');\n                    row.push(fill);\n                    row.push_str(col2);\n                    let spaces = max_image_len.max(col2.len()) + 1 - col2.len();\n                    for _ in 0..spaces {\n                        row.push(fill);\n                    }\n                    row.push('|');\n                    info.print(row)\n                };\n\n            if targets.len() != 1 {\n                print_string(\"Targets\", \"Images\", ' ', msg_info)?;\n                print_string(\"-------\", \"------\", '-', msg_info)?;\n            }\n\n            let print_single = |_: &str,\n                                image: &Image,\n                                info: &mut MessageInfo|\n             -> cross::Result<()> { info.print(image) };\n            let print_table =\n                |target: &str, image: &Image, info: &mut MessageInfo| -> cross::Result<()> {\n                    let name = image.name();\n                    print_string(target, &name, ' ', info)\n                };\n\n            for target in keys {\n                for image in map.get(target).expect(\"map must have key\").iter() {\n                    if targets.len() == 1 {\n                        print_single(target, image, msg_info)?;\n                    } else {\n                        print_table(target, image, msg_info)?;\n                    }\n                }\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn remove_images(\n    engine: &docker::Engine,\n    images: &[Image],\n    msg_info: &mut MessageInfo,\n    force: bool,\n    execute: bool,\n) -> cross::Result<()> {\n    let mut command = engine.subcommand(\"rmi\");\n    if force {\n        command.arg(\"--force\");\n    }\n    command.args(images.iter().map(|i| &i.id));\n    if images.is_empty() {\n        Ok(())\n    } else if execute {\n        command.run(msg_info, false)\n    } else {\n        msg_info.note(\"this is a dry run. to remove the images, pass the `--execute` flag.\")?;\n        command.print(msg_info)?;\n        Ok(())\n    }\n}\n\npub fn remove_all_images(\n    RemoveImages {\n        force,\n        local,\n        execute,\n        ..\n    }: &RemoveImages,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let images = get_cross_images(engine, msg_info, *local)?;\n    remove_images(engine, &images, msg_info, *force, *execute)\n}\n\npub fn remove_target_images(\n    RemoveImages {\n        targets,\n        force,\n        local,\n        execute,\n        ..\n    }: &RemoveImages,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let cross_images = get_cross_images(engine, msg_info, *local)?;\n    let target_list = msg_info.as_quiet(cross::rustc::target_list)?;\n    let mut images = vec![];\n    for image in cross_images {\n        let target = get_image_target(engine, &image, &target_list, msg_info)?;\n        if targets.contains(&target) {\n            images.push(image);\n        }\n    }\n    remove_images(engine, &images, msg_info, *force, *execute)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_rustembedded_target() {\n        let targets = [\n            \"x86_64-unknown-linux-gnu\",\n            \"x86_64-apple-darwin\",\n            \"thumbv8m.main-none-eabi\",\n        ];\n        for target in targets {\n            let versioned = format!(\"{target}-0.2.1\");\n            assert_eq!(rustembedded_target(target), target.to_string());\n            assert_eq!(rustembedded_target(&versioned), target.to_string());\n        }\n    }\n}\n"
  },
  {
    "path": "src/bin/commands/mod.rs",
    "content": "mod clean;\nmod containers;\nmod images;\nmod run;\n\npub use self::clean::*;\npub use self::containers::*;\npub use self::images::*;\npub use self::run::*;\n"
  },
  {
    "path": "src/bin/commands/run.rs",
    "content": "use clap::Args as ClapArgs;\nuse cross::config::Config;\nuse cross::shell::{MessageInfo, Verbosity};\nuse cross::{\n    cargo_metadata_with_args, cli::Args, docker, rustc, setup, toml, CommandVariant, CrossSetup,\n    Target,\n};\nuse eyre::Context;\n\n#[derive(ClapArgs, Debug)]\npub struct Run {\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n    /// Target\n    #[clap(short, long)]\n    pub target: String,\n    /// Interactive session\n    #[clap(short, long, default_value = \"false\")]\n    pub interactive: bool,\n    /// Command to run, will be run in a shell\n    #[clap(last = true)]\n    pub command: String,\n}\n\nimpl Run {\n    pub fn run(\n        &self,\n        cli: &crate::Cli,\n        engine: docker::Engine,\n        msg_info: &mut MessageInfo,\n    ) -> cross::Result<()> {\n        let target_list = rustc::target_list(&mut Verbosity::Quiet.into())?;\n        let target = Target::from(&self.target, &target_list);\n\n        let cwd = std::env::current_dir()?;\n        let host_version_meta = rustc::version_meta()?;\n\n        let args = Args {\n            cargo_args: vec![],\n            rest_args: vec![],\n            subcommand: None,\n            channel: None,\n            target: Some(target.clone()),\n            features: vec![],\n            target_dir: None,\n            manifest_path: None,\n            version: false,\n            verbose: if cli.verbose { 1 } else { 0 },\n            quiet: cli.quiet,\n            color: cli.color.clone(),\n        };\n\n        if let Some(metadata) = cargo_metadata_with_args(None, Some(&args), msg_info)? {\n            let CrossSetup { toolchain, .. } =\n                match setup(&host_version_meta, &metadata, &args, target_list, msg_info)? {\n                    Some(setup) => setup,\n                    _ => {\n                        eyre::bail!(\"Error: cannot setup cross environment\");\n                    }\n                };\n\n            let toml = toml(&metadata, msg_info)?;\n            let config = Config::new(Some(toml));\n\n            let image = match docker::get_image(&config, &target, false) {\n                Ok(i) => i,\n                Err(docker::GetImageError::NoCompatibleImages(..))\n                    if config.dockerfile(&target).is_some() =>\n                {\n                    \"scratch\".into()\n                }\n                Err(err) => {\n                    msg_info.warn(&err)?;\n                    eyre::bail!(\"Error: {}\", &err);\n                }\n            };\n\n            let image = image.to_definite_with(&engine, msg_info)?;\n\n            let paths = docker::DockerPaths::create(&engine, metadata, cwd, toolchain, msg_info)?;\n            let options = docker::DockerOptions::new(\n                engine,\n                target,\n                config,\n                image,\n                CommandVariant::Shell,\n                None,\n                self.interactive,\n            );\n\n            let mut args = vec![String::from(\"-c\")];\n            args.push(self.command.clone());\n\n            docker::run(options, paths, &args, None, msg_info)\n                .wrap_err(\"could not run container\")?;\n        }\n\n        Ok(())\n    }\n\n    pub fn engine(&self) -> Option<&str> {\n        self.engine.as_deref()\n    }\n}\n"
  },
  {
    "path": "src/bin/cross-util.rs",
    "content": "#![deny(missing_debug_implementations, rust_2018_idioms)]\n\nuse clap::{CommandFactory, Parser, Subcommand};\nuse cross::shell::MessageInfo;\nuse cross::{docker, rustc::Toolchain};\n\nmod commands;\n\nconst APP_NAME: &str = \"cross-util\";\nstatic VERSION: &str = concat!(env!(\"CARGO_PKG_VERSION\"), cross::commit_info!());\n\n#[derive(Parser, Debug)]\n#[clap(about, long_about = None, name = APP_NAME, version = VERSION)]\npub struct Cli {\n    /// Toolchain name/version to use (such as stable or 1.59.0).\n    #[clap(value_parser = is_toolchain)]\n    toolchain: Option<Toolchain>,\n    #[clap(subcommand)]\n    command: Commands,\n    /// Provide verbose diagnostic output.\n    #[clap(short, long, global = true)]\n    pub verbose: bool,\n    /// Do not print cross log messages.\n    #[clap(short, long, global = true)]\n    pub quiet: bool,\n    /// Coloring: auto, always, never\n    #[clap(long, global = true)]\n    pub color: Option<String>,\n}\n\n// hidden implied parser so we can get matches without recursion.\n#[derive(Parser, Debug)]\nstruct CliHidden {\n    #[clap(subcommand)]\n    command: Commands,\n}\n\n#[derive(Subcommand, Debug)]\nenum Commands {\n    /// Work with cross images in local storage.\n    #[clap(subcommand)]\n    Images(commands::Images),\n    /// Work with cross volumes in local storage.\n    #[clap(subcommand)]\n    Volumes(commands::Volumes),\n    /// Work with cross containers in local storage.\n    #[clap(subcommand)]\n    Containers(commands::Containers),\n    /// Run in cross container.\n    Run(commands::Run),\n    /// Clean all cross data in local storage.\n    Clean(commands::Clean),\n}\n\nfn is_toolchain(toolchain: &str) -> cross::Result<Toolchain> {\n    if toolchain.starts_with('+') {\n        Ok(toolchain.chars().skip(1).collect::<String>().parse()?)\n    } else {\n        let _ = <CliHidden as CommandFactory>::command().get_matches();\n        unreachable!();\n    }\n}\n\nfn get_container_engine(\n    engine: Option<&str>,\n    docker_in_docker: bool,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<docker::Engine> {\n    let engine = if let Some(ce) = engine {\n        which::which(ce)?\n    } else {\n        docker::get_container_engine()?\n    };\n    let in_docker = match docker_in_docker {\n        true => Some(true),\n        false => None,\n    };\n    docker::Engine::from_path(engine, in_docker, None, msg_info)\n}\n\nmacro_rules! get_engine {\n    ($args:ident, $docker_in_docker:expr, $msg_info: ident) => {{\n        get_container_engine($args.engine(), $docker_in_docker, &mut $msg_info)\n    }};\n}\n\npub fn main() -> cross::Result<()> {\n    cross::install_panic_hook()?;\n    let cli = Cli::parse();\n    let mut msg_info = MessageInfo::create(cli.verbose, cli.quiet, cli.color.as_deref())?;\n    match &cli.command {\n        Commands::Images(args) => {\n            let engine = get_engine!(args, false, msg_info)?;\n            args.run(engine, &mut msg_info)?;\n        }\n        Commands::Volumes(args) => {\n            let engine = get_engine!(args, args.docker_in_docker(), msg_info)?;\n            args.run(engine, cli.toolchain.as_ref(), &mut msg_info)?;\n        }\n        Commands::Containers(args) => {\n            let engine = get_engine!(args, false, msg_info)?;\n            args.run(engine, &mut msg_info)?;\n        }\n        Commands::Clean(args) => {\n            let engine = get_engine!(args, false, msg_info)?;\n            args.run(engine, &mut msg_info)?;\n        }\n        Commands::Run(args) => {\n            let engine = get_engine!(args, false, msg_info)?;\n            args.run(&cli, engine, &mut msg_info)?;\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/bin/cross.rs",
    "content": "#![deny(missing_debug_implementations, rust_2018_idioms)]\n\nuse std::{\n    env,\n    io::{self, Write},\n};\n\nuse cross::{\n    cargo, cli, rustc,\n    shell::{self, Verbosity},\n    OutputExt, Subcommand,\n};\n\npub fn main() -> cross::Result<()> {\n    cross::install_panic_hook()?;\n    cross::install_termination_hook()?;\n\n    let target_list = rustc::target_list(&mut Verbosity::Quiet.into())?;\n    let args = cli::parse(&target_list)?;\n    let subcommand = args.subcommand.clone();\n    let mut msg_info = shell::MessageInfo::create(args.verbose, args.quiet, args.color.as_deref())?;\n    let status = match cross::run(args, target_list, &mut msg_info)? {\n        Some(status) => status,\n        None if !msg_info.should_fail() => {\n            // if we fallback to the host cargo, use the same invocation that was made to cross\n            let argv: Vec<String> = env::args().skip(1).collect();\n            msg_info.note(\"Falling back to `cargo` on the host.\")?;\n            match subcommand {\n                Some(Subcommand::List) => {\n                    // this won't print in order if we have both stdout and stderr.\n                    let out = cargo::run_and_get_output(&argv, &mut msg_info)?;\n                    let stdout = out.stdout()?;\n                    if out.status.success() && cli::is_subcommand_list(&stdout) {\n                        cli::fmt_subcommands(&stdout, &mut msg_info)?;\n                    } else {\n                        // Not a list subcommand, which can happen with weird edge-cases.\n                        print!(\"{}\", stdout);\n                        io::stdout().flush().expect(\"could not flush\");\n                    }\n                    out.status\n                }\n                _ => cargo::run(&argv, &mut msg_info)?,\n            }\n        }\n        None => {\n            msg_info.error(\"Errors encountered before cross compilation, aborting.\")?;\n            msg_info.note(\"Disable this with `CROSS_NO_WARNINGS=0`\")?;\n            std::process::exit(1);\n        }\n    };\n    let code = status\n        .code()\n        .ok_or_else(|| eyre::Report::msg(\"Cargo process terminated by signal\"))?;\n    std::process::exit(code)\n}\n"
  },
  {
    "path": "src/build.rs",
    "content": "use std::env;\nuse std::error::Error;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\nuse std::process::Command;\n\nstruct Some {}\n\nimpl<E> From<E> for Some\nwhere\n    E: Error,\n{\n    fn from(_: E) -> Some {\n        Some {}\n    }\n}\n\nfn main() {\n    let out_dir = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n\n    File::create(out_dir.join(\"commit-info.txt\"))\n        .unwrap()\n        .write_all(commit_info().as_bytes())\n        .unwrap();\n\n    println!(\"cargo::rustc-check-cfg=cfg(cross_sandboxed)\");\n    if env::var(\"CROSS_SANDBOXED\").is_ok() {\n        println!(\"cargo:rustc-cfg=cross_sandboxed\");\n    }\n    println!(\"cargo:rerun-if-env-changed=CROSS_SANDBOXED\");\n}\n\nfn commit_info() -> String {\n    match (commit_hash(), commit_date()) {\n        (Ok(hash), Ok(date)) => format!(\" ({} {})\", hash.trim(), date.trim()),\n        _ => String::new(),\n    }\n}\n\nfn commit_hash() -> Result<String, Some> {\n    let output = Command::new(\"git\")\n        .args([\"rev-parse\", \"--short\", \"HEAD\"])\n        .output()?;\n\n    if output.status.success() {\n        Ok(String::from_utf8(output.stdout)?)\n    } else {\n        Err(Some {})\n    }\n}\n\nfn commit_date() -> Result<String, Some> {\n    let output = Command::new(\"git\")\n        .args([\"log\", \"-1\", \"--date=short\", \"--pretty=format:%cd\"])\n        .output()?;\n\n    if output.status.success() {\n        Ok(String::from_utf8(output.stdout)?)\n    } else {\n        Err(Some {})\n    }\n}\n"
  },
  {
    "path": "src/cargo.rs",
    "content": "use serde::Deserialize;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, ExitStatus};\n\nuse crate::cli::Args;\nuse crate::errors::*;\nuse crate::extensions::CommandExt;\nuse crate::shell::{self, MessageInfo};\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Subcommand {\n    Build,\n    Check,\n    Doc,\n    Run,\n    Rustdoc,\n    Rustc,\n    Test,\n    Bench,\n    Clippy,\n    Metadata,\n    List,\n    Clean,\n    Other(String),\n}\n\nimpl Subcommand {\n    #[must_use]\n    pub fn needs_docker(self, is_remote: bool) -> bool {\n        match self {\n            Subcommand::Other(_) | Subcommand::List => false,\n            Subcommand::Clean if !is_remote => false,\n            _ => true,\n        }\n    }\n\n    #[must_use]\n    pub fn needs_host(self, is_remote: bool) -> bool {\n        self == Subcommand::Clean && is_remote\n    }\n\n    #[must_use]\n    pub fn needs_interpreter(self) -> bool {\n        matches!(self, Subcommand::Run | Subcommand::Test | Subcommand::Bench)\n    }\n\n    #[must_use]\n    pub fn needs_target_in_command(self) -> bool {\n        !matches!(self, Subcommand::Metadata)\n    }\n}\n\nimpl From<&str> for Subcommand {\n    fn from(s: &str) -> Subcommand {\n        match s {\n            \"b\" | \"build\" => Subcommand::Build,\n            \"c\" | \"check\" => Subcommand::Check,\n            \"clean\" => Subcommand::Clean,\n            \"doc\" => Subcommand::Doc,\n            \"r\" | \"run\" => Subcommand::Run,\n            \"rustc\" => Subcommand::Rustc,\n            \"rustdoc\" => Subcommand::Rustdoc,\n            \"t\" | \"test\" => Subcommand::Test,\n            \"bench\" => Subcommand::Bench,\n            \"clippy\" => Subcommand::Clippy,\n            \"metadata\" => Subcommand::Metadata,\n            \"--list\" => Subcommand::List,\n            command => Subcommand::Other(command.to_owned()),\n        }\n    }\n}\n\n#[derive(Debug, Deserialize)]\npub struct CargoMetadata {\n    pub workspace_root: PathBuf,\n    pub target_directory: PathBuf,\n    pub packages: Vec<Package>,\n    pub workspace_members: Vec<String>,\n    pub metadata: Option<Box<serde_json::value::RawValue>>,\n}\n\nimpl CargoMetadata {\n    fn non_workspace_members(&self) -> impl Iterator<Item = &Package> {\n        self.packages\n            .iter()\n            .filter(|p| !self.workspace_members.iter().any(|m| m == &p.id))\n    }\n\n    pub fn path_dependencies(&self) -> impl Iterator<Item = &Path> {\n        // TODO: Also filter out things that are in workspace, but not a workspace member\n        self.non_workspace_members().filter_map(|p| p.crate_path())\n    }\n\n    #[cfg(feature = \"dev\")]\n    #[must_use]\n    pub fn get_package(&self, package: &str) -> Option<&Package> {\n        self.packages.iter().find(|p| p.name == package)\n    }\n}\n\n#[derive(Debug, Deserialize)]\npub struct Package {\n    pub id: String,\n    pub name: String,\n    pub manifest_path: PathBuf,\n    pub source: Option<String>,\n    pub version: String,\n    pub license: Option<String>,\n    pub metadata: Option<Box<serde_json::value::RawValue>>,\n}\n\nimpl Package {\n    /// Returns the absolute path to the packages manifest \"folder\"\n    fn crate_path(&self) -> Option<&Path> {\n        // when source is none, this package is a path dependency or a workspace member\n        if self.source.is_none() {\n            self.manifest_path.parent()\n        } else {\n            None\n        }\n    }\n}\n\n#[must_use]\npub fn cargo_command() -> Command {\n    Command::new(\"cargo\")\n}\n\n/// Cargo metadata with specific invocation\npub fn cargo_metadata_with_args(\n    cd: Option<&Path>,\n    args: Option<&Args>,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<CargoMetadata>> {\n    let mut command = cargo_command();\n    if let Some(channel) = args.and_then(|x| x.channel.as_deref()) {\n        command.arg(format!(\"+{channel}\"));\n    }\n    command.arg(\"metadata\").args([\"--format-version\", \"1\"]);\n    if let Some(cd) = cd {\n        command.current_dir(cd);\n    }\n    if let Some(config) = args {\n        if let Some(ref manifest_path) = config.manifest_path {\n            command.args([\"--manifest-path\".as_ref(), manifest_path.as_os_str()]);\n        }\n    } else {\n        command.arg(\"--no-deps\");\n    }\n    if let Some(target) = args.and_then(|a| a.target.as_ref()) {\n        command.args([\"--filter-platform\", target.triple()]);\n    }\n    if let Some(features) = args.map(|a| &a.features).filter(|v| !v.is_empty()) {\n        command.args([String::from(\"--features\"), features.join(\",\")]);\n    }\n    let output = command.run_and_get_output(msg_info)?;\n    if !output.status.success() {\n        msg_info.warn(\"unable to get metadata for package\")?;\n        let indented = shell::indent(&String::from_utf8(output.stderr)?, shell::default_ident());\n        msg_info.debug(indented)?;\n        return Ok(None);\n    }\n    let manifest: Option<CargoMetadata> = serde_json::from_slice(&output.stdout)?;\n    manifest\n        .map(|m| -> Result<_> {\n            Ok(CargoMetadata {\n                target_directory: args\n                    .and_then(|a| a.target_dir.clone())\n                    .unwrap_or(m.target_directory),\n                ..m\n            })\n        })\n        .transpose()\n}\n\n/// Pass-through mode\npub fn run(args: &[String], msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n    cargo_command()\n        .args(args)\n        .run_and_get_status(msg_info, false)\n}\n\n/// run cargo and get the output, does not check the exit status\npub fn run_and_get_output(\n    args: &[String],\n    msg_info: &mut MessageInfo,\n) -> Result<std::process::Output> {\n    cargo_command().args(args).run_and_get_output(msg_info)\n}\n"
  },
  {
    "path": "src/cli.rs",
    "content": "use std::env;\nuse std::path::{Path, PathBuf};\n\nuse crate::cargo::Subcommand;\nuse crate::errors::Result;\nuse crate::file::{absolute_path, PathExt};\nuse crate::rustc::TargetList;\nuse crate::shell::{self, MessageInfo};\nuse crate::Target;\n\n#[derive(Debug)]\npub struct Args {\n    pub cargo_args: Vec<String>,\n    pub rest_args: Vec<String>,\n    pub subcommand: Option<Subcommand>,\n    pub channel: Option<String>,\n    pub target: Option<Target>,\n    pub features: Vec<String>,\n    pub target_dir: Option<PathBuf>,\n    pub manifest_path: Option<PathBuf>,\n    pub version: bool,\n    pub verbose: u8,\n    pub quiet: bool,\n    pub color: Option<String>,\n}\n\npub fn is_subcommand_list(stdout: &str) -> bool {\n    stdout.starts_with(\"Installed Commands:\")\n}\n\npub fn group_subcommands(stdout: &str) -> (Vec<&str>, Vec<&str>) {\n    let mut cross = vec![];\n    let mut host = vec![];\n    for line in stdout.lines().skip(1) {\n        // trim all whitespace, then grab the command name\n        let first = line.split_whitespace().next();\n        if let Some(command) = first {\n            match Subcommand::from(command) {\n                Subcommand::Other(_) => host.push(line),\n                _ => cross.push(line),\n            }\n        }\n    }\n\n    (cross, host)\n}\n\npub fn fmt_subcommands(stdout: &str, msg_info: &mut MessageInfo) -> Result<()> {\n    let (cross, host) = group_subcommands(stdout);\n    if !cross.is_empty() {\n        msg_info.print(\"Cross Commands:\")?;\n        for line in &cross {\n            msg_info.print(line)?;\n        }\n    }\n    if !host.is_empty() {\n        msg_info.print(\"Host Commands:\")?;\n        for line in &host {\n            msg_info.print(line)?;\n        }\n    }\n    Ok(())\n}\n\nfn is_verbose(arg: &str) -> u8 {\n    match arg {\n        \"--verbose\" => 1,\n        // cargo can handle any number of \"v\"s\n        a => {\n            if a.starts_with('-')\n                && a.len() >= 2\n                && a.get(1..)\n                    .map(|a| a.chars().all(|x| x == 'v'))\n                    .unwrap_or_default()\n            {\n                // string must be of form `-v[v]*` here\n                a.len() as u8 - 1\n            } else {\n                0\n            }\n        }\n    }\n}\n\nenum ArgKind {\n    Next,\n    Equal,\n}\n\nfn is_value_arg(arg: &str, field: &str) -> Option<ArgKind> {\n    if arg == field {\n        Some(ArgKind::Next)\n    } else if arg\n        .strip_prefix(field)\n        .map(|a| a.starts_with('='))\n        .unwrap_or_default()\n    {\n        Some(ArgKind::Equal)\n    } else {\n        None\n    }\n}\n\nfn parse_next_arg<T>(\n    arg: String,\n    out: &mut Vec<String>,\n    parse: impl Fn(&str) -> Result<T>,\n    store_cb: impl Fn(String) -> Result<String>,\n    iter: &mut impl Iterator<Item = String>,\n) -> Result<Option<T>> {\n    out.push(arg);\n    match iter.next() {\n        Some(next) => {\n            let result = parse(&next)?;\n            out.push(store_cb(next)?);\n            Ok(Some(result))\n        }\n        None => Ok(None),\n    }\n}\n\nfn parse_equal_arg<T>(\n    arg: String,\n    out: &mut Vec<String>,\n    parse: impl Fn(&str) -> Result<T>,\n    store_cb: impl Fn(String) -> Result<String>,\n) -> Result<T> {\n    let (first, second) = arg.split_once('=').expect(\"argument should contain `=`\");\n    let result = parse(second)?;\n    out.push(format!(\"{first}={}\", store_cb(second.to_owned())?));\n\n    Ok(result)\n}\n\nfn parse_manifest_path(path: &str) -> Result<Option<PathBuf>> {\n    let p = PathBuf::from(path);\n    Ok(absolute_path(p).ok())\n}\n\nfn parse_target_dir(path: &str) -> Result<PathBuf> {\n    absolute_path(PathBuf::from(path))\n}\n\nfn identity(arg: String) -> Result<String> {\n    Ok(arg)\n}\n\nfn str_to_owned(arg: &str) -> Result<String> {\n    Ok(arg.to_owned())\n}\n\nfn store_manifest_path(path: String) -> Result<String> {\n    Path::new(&path).as_posix_relative()\n}\n\nfn store_target_dir(_: String) -> Result<String> {\n    Ok(\"/target\".to_owned())\n}\n\npub fn parse(target_list: &TargetList) -> Result<Args> {\n    let mut channel = None;\n    let mut target = None;\n    let mut features = Vec::new();\n    let mut manifest_path: Option<PathBuf> = None;\n    let mut target_dir = None;\n    let mut sc = None;\n    let mut cargo_args: Vec<String> = Vec::new();\n    let mut rest_args: Vec<String> = Vec::new();\n    let mut version = false;\n    let mut quiet = false;\n    let mut verbose = 0;\n    let mut color = None;\n\n    {\n        let mut args = env::args().skip(1);\n        while let Some(arg) = args.next() {\n            if arg.is_empty() {\n                continue;\n            }\n            if matches!(arg.as_str(), \"--\") {\n                rest_args.push(arg);\n                rest_args.extend(args.by_ref());\n            } else if let v @ 1.. = is_verbose(arg.as_str()) {\n                verbose += v;\n                cargo_args.push(arg);\n            } else if matches!(arg.as_str(), \"--version\" | \"-V\") {\n                version = true;\n            } else if matches!(arg.as_str(), \"--quiet\" | \"-q\") {\n                quiet = true;\n                cargo_args.push(arg);\n            } else if let Some(kind) = is_value_arg(&arg, \"--color\") {\n                color = match kind {\n                    ArgKind::Next => {\n                        match parse_next_arg(\n                            arg,\n                            &mut cargo_args,\n                            str_to_owned,\n                            identity,\n                            &mut args,\n                        )? {\n                            Some(c) => Some(c),\n                            None => shell::invalid_color(None),\n                        }\n                    }\n                    ArgKind::Equal => Some(parse_equal_arg(\n                        arg,\n                        &mut cargo_args,\n                        str_to_owned,\n                        identity,\n                    )?),\n                };\n            } else if let Some(kind) = is_value_arg(&arg, \"--manifest-path\") {\n                manifest_path = match kind {\n                    ArgKind::Next => parse_next_arg(\n                        arg,\n                        &mut cargo_args,\n                        parse_manifest_path,\n                        store_manifest_path,\n                        &mut args,\n                    )?\n                    .flatten(),\n                    ArgKind::Equal => parse_equal_arg(\n                        arg,\n                        &mut cargo_args,\n                        parse_manifest_path,\n                        store_manifest_path,\n                    )?,\n                };\n            } else if let (\"+\", ch) = arg.split_at(1) {\n                channel = Some(ch.to_owned());\n            } else if let Some(kind) = is_value_arg(&arg, \"--target\") {\n                let parse_target = |t: &str| Ok(Target::from(t, target_list));\n                target = match kind {\n                    ArgKind::Next => {\n                        parse_next_arg(arg, &mut cargo_args, parse_target, identity, &mut args)?\n                    }\n                    ArgKind::Equal => Some(parse_equal_arg(\n                        arg,\n                        &mut cargo_args,\n                        parse_target,\n                        identity,\n                    )?),\n                };\n            } else if let Some(kind) = is_value_arg(&arg, \"--features\") {\n                match kind {\n                    ArgKind::Next => {\n                        let next = parse_next_arg(\n                            arg,\n                            &mut cargo_args,\n                            str_to_owned,\n                            identity,\n                            &mut args,\n                        )?;\n                        if let Some(feature) = next {\n                            features.push(feature);\n                        }\n                    }\n                    ArgKind::Equal => {\n                        features.push(parse_equal_arg(\n                            arg,\n                            &mut cargo_args,\n                            str_to_owned,\n                            identity,\n                        )?);\n                    }\n                }\n            } else if let Some(kind) = is_value_arg(&arg, \"--target-dir\") {\n                match kind {\n                    ArgKind::Next => {\n                        target_dir = parse_next_arg(\n                            arg,\n                            &mut cargo_args,\n                            parse_target_dir,\n                            store_target_dir,\n                            &mut args,\n                        )?;\n                    }\n                    ArgKind::Equal => {\n                        target_dir = Some(parse_equal_arg(\n                            arg,\n                            &mut cargo_args,\n                            parse_target_dir,\n                            store_target_dir,\n                        )?);\n                    }\n                }\n            } else {\n                if (!arg.starts_with('-') || arg == \"--list\") && sc.is_none() {\n                    sc = Some(Subcommand::from(arg.as_ref()));\n                }\n\n                cargo_args.push(arg.clone());\n            }\n        }\n    }\n\n    Ok(Args {\n        cargo_args,\n        rest_args,\n        subcommand: sc,\n        channel,\n        target,\n        features,\n        target_dir,\n        manifest_path,\n        version,\n        verbose,\n        quiet,\n        color,\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_verbose_test() {\n        assert!(is_verbose(\"b\") == 0);\n        assert!(is_verbose(\"x\") == 0);\n        assert!(is_verbose(\"-\") == 0);\n        assert!(is_verbose(\"-V\") == 0);\n        assert!(is_verbose(\"-v\") == 1);\n        assert!(is_verbose(\"--verbose\") == 1);\n        assert!(is_verbose(\"-vvvv\") == 4);\n        assert!(is_verbose(\"-version\") == 0);\n    }\n}\n"
  },
  {
    "path": "src/config.rs",
    "content": "use crate::cross_toml::BuildStd;\nuse crate::docker::custom::PreBuild;\nuse crate::docker::{ImagePlatform, PossibleImage};\nuse crate::shell::MessageInfo;\nuse crate::{CrossToml, Result, Target, TargetList};\n\nuse std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::env;\nuse std::str::FromStr;\n\n#[derive(Debug)]\npub struct ConfVal<T> {\n    pub build: Option<T>,\n    pub target: Option<T>,\n}\n\nimpl<T> ConfVal<T> {\n    pub fn new(build: Option<T>, target: Option<T>) -> Self {\n        Self { build, target }\n    }\n\n    pub fn map<U, F: Fn(T) -> U>(self, f: F) -> ConfVal<U> {\n        ConfVal {\n            build: self.build.map(&f),\n            target: self.target.map(&f),\n        }\n    }\n}\n\nimpl<T> Default for ConfVal<T> {\n    fn default() -> Self {\n        Self::new(None, None)\n    }\n}\n\nimpl<T: PartialEq> PartialEq<(Option<T>, Option<T>)> for ConfVal<T> {\n    fn eq(&self, other: &(Option<T>, Option<T>)) -> bool {\n        self.build == other.0 && self.target == other.1\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct Environment(&'static str, Option<HashMap<&'static str, &'static str>>);\n\nimpl Environment {\n    pub(crate) fn new(map: Option<HashMap<&'static str, &'static str>>) -> Self {\n        Environment(\"CROSS\", map)\n    }\n\n    fn build_var_name(&self, name: &str) -> String {\n        format!(\"{}_{}\", self.0, name.to_ascii_uppercase().replace('-', \"_\"))\n    }\n\n    fn get_var(&self, name: &str) -> Option<String> {\n        self.1\n            .as_ref()\n            .and_then(|internal_map| internal_map.get(name).map(|v| (*v).to_owned()))\n            .or_else(|| env::var(name).ok())\n    }\n\n    fn get_values_for<T>(\n        &self,\n        var: &str,\n        target: &Target,\n        convert: impl Fn(&str) -> T,\n    ) -> ConfVal<T> {\n        let build_values = self.get_build_var(var).map(|ref s| convert(s));\n        let target_values = self.get_target_var(target, var).map(|ref s| convert(s));\n\n        ConfVal::new(build_values, target_values)\n    }\n\n    fn target_path(target: &Target, key: &str) -> String {\n        format!(\"TARGET_{target}_{key}\")\n    }\n\n    fn build_path(key: &str) -> String {\n        if !key.starts_with(\"BUILD_\") {\n            format!(\"BUILD_{key}\")\n        } else {\n            key.to_owned()\n        }\n    }\n\n    fn get_build_var(&self, key: &str) -> Option<String> {\n        self.get_var(&self.build_var_name(&Self::build_path(key)))\n    }\n\n    fn get_target_var(&self, target: &Target, key: &str) -> Option<String> {\n        self.get_var(&self.build_var_name(&Self::target_path(target, key)))\n    }\n\n    fn build_std(&self, target: &Target) -> ConfVal<BuildStd> {\n        self.get_values_for(\"BUILD_STD\", target, |v| {\n            if let Some(value) = try_bool_from_envvar(v) {\n                BuildStd::Bool(value)\n            } else {\n                BuildStd::Crates(v.split(',').map(str::to_owned).collect())\n            }\n        })\n    }\n\n    fn zig(&self, target: &Target) -> ConfVal<bool> {\n        self.get_values_for(\"ZIG\", target, bool_from_envvar)\n    }\n\n    fn zig_version(&self, target: &Target) -> ConfVal<String> {\n        self.get_values_for(\"ZIG_VERSION\", target, ToOwned::to_owned)\n    }\n\n    fn zig_image(&self, target: &Target) -> Result<ConfVal<PossibleImage>> {\n        let get_build = |env: &Environment, var: &str| env.get_build_var(var);\n        let get_target = |env: &Environment, var: &str| env.get_target_var(target, var);\n        let env_build = get_possible_image(\n            self,\n            \"ZIG_IMAGE\",\n            \"ZIG_IMAGE_TOOLCHAIN\",\n            get_build,\n            get_build,\n        )?;\n        let env_target = get_possible_image(\n            self,\n            \"ZIG_IMAGE\",\n            \"ZIG_IMAGE_TOOLCHAIN\",\n            get_target,\n            get_target,\n        )?;\n\n        Ok(ConfVal::new(env_build, env_target))\n    }\n\n    fn image(&self, target: &Target) -> Result<Option<PossibleImage>> {\n        let get_target = |env: &Environment, var: &str| env.get_target_var(target, var);\n        get_possible_image(self, \"IMAGE\", \"IMAGE_TOOLCHAIN\", get_target, get_target)\n    }\n\n    fn dockerfile(&self, target: &Target) -> ConfVal<String> {\n        self.get_values_for(\"DOCKERFILE\", target, ToOwned::to_owned)\n    }\n\n    fn dockerfile_context(&self, target: &Target) -> ConfVal<String> {\n        self.get_values_for(\"DOCKERFILE_CONTEXT\", target, ToOwned::to_owned)\n    }\n\n    fn pre_build(&self, target: &Target) -> ConfVal<PreBuild> {\n        self.get_values_for(\"PRE_BUILD\", target, |v| {\n            let v: Vec<_> = v.split('\\n').map(String::from).collect();\n            if v.len() == 1 {\n                PreBuild::Single {\n                    line: v.into_iter().next().expect(\"should contain one item\"),\n                    env: true,\n                }\n            } else {\n                PreBuild::Lines(v)\n            }\n        })\n    }\n\n    fn runner(&self, target: &Target) -> Option<String> {\n        self.get_target_var(target, \"RUNNER\")\n    }\n\n    fn passthrough(&self, target: &Target) -> ConfVal<Vec<String>> {\n        self.get_values_for(\"ENV_PASSTHROUGH\", target, split_to_cloned_by_ws)\n    }\n\n    fn volumes(&self, target: &Target) -> ConfVal<Vec<String>> {\n        self.get_values_for(\"ENV_VOLUMES\", target, split_to_cloned_by_ws)\n    }\n\n    fn target(&self) -> Option<String> {\n        self.get_build_var(\"TARGET\")\n            .or_else(|| std::env::var(\"CARGO_BUILD_TARGET\").ok())\n    }\n\n    fn doctests(&self) -> Option<bool> {\n        self.get_var(\"CROSS_UNSTABLE_ENABLE_DOCTESTS\")\n            .map(|s| bool_from_envvar(&s))\n    }\n\n    fn custom_toolchain(&self) -> bool {\n        self.get_var(\"CROSS_CUSTOM_TOOLCHAIN\")\n            .is_some_and(|s| bool_from_envvar(&s))\n    }\n\n    fn custom_toolchain_compat(&self) -> Option<String> {\n        self.get_var(\"CROSS_CUSTOM_TOOLCHAIN_COMPAT\")\n    }\n\n    fn build_opts(&self) -> Option<String> {\n        self.get_var(\"CROSS_BUILD_OPTS\")\n    }\n}\n\nfn get_possible_image(\n    env: &Environment,\n    image_var: &str,\n    toolchain_var: &str,\n    get_image: impl Fn(&Environment, &str) -> Option<String>,\n    get_toolchain: impl Fn(&Environment, &str) -> Option<String>,\n) -> Result<Option<PossibleImage>> {\n    get_image(env, image_var)\n        .map(Into::into)\n        .map(|mut i: PossibleImage| {\n            if let Some(toolchain) = get_toolchain(env, toolchain_var) {\n                i.toolchain = toolchain\n                    .split(',')\n                    .map(|t| ImagePlatform::from_target(t.trim().into()))\n                    .collect::<Result<Vec<_>>>()?;\n                Ok(i)\n            } else {\n                Ok(i)\n            }\n        })\n        .transpose()\n}\n\nfn split_to_cloned_by_ws(string: &str) -> Vec<String> {\n    string.split_whitespace().map(String::from).collect()\n}\n\n/// this takes the value of the environment variable,\n/// so you should call `bool_from_envvar(env::var(\"FOO\"))`\npub fn bool_from_envvar(envvar: &str) -> bool {\n    try_bool_from_envvar(envvar).unwrap_or(!envvar.is_empty())\n}\n\npub fn try_bool_from_envvar(envvar: &str) -> Option<bool> {\n    if let Ok(value) = bool::from_str(envvar) {\n        Some(value)\n    } else if let Ok(value) = i32::from_str(envvar) {\n        Some(value != 0)\n    } else {\n        None\n    }\n}\n\n#[derive(Debug)]\npub struct Config {\n    toml: Option<CrossToml>,\n    env: Environment,\n}\n\nimpl Config {\n    pub fn new(toml: Option<CrossToml>) -> Self {\n        Config {\n            toml,\n            env: Environment::new(None),\n        }\n    }\n\n    pub fn confusable_target(&self, target: &Target, msg_info: &mut MessageInfo) -> Result<()> {\n        if let Some(keys) = self.toml.as_ref().map(|t| t.targets.keys()) {\n            for mentioned_target in keys {\n                let mentioned_target_norm = mentioned_target\n                    .to_string()\n                    .replace(['-', '_'], \"\")\n                    .to_lowercase();\n                let target_norm = target.to_string().replace(['-', '_'], \"\").to_lowercase();\n                if mentioned_target != target && mentioned_target_norm == target_norm {\n                    msg_info.warn(format_args!(\"a target named \\\"{mentioned_target}\\\" is mentioned in the Cross configuration, but the current specified target is \\\"{target}\\\".\"))?;\n                    msg_info.status(\" > Is the target misspelled in the Cross configuration?\")?;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    fn get_from_value_inner<T, U>(\n        &self,\n        target: &Target,\n        env: impl for<'a> FnOnce(&'a Environment, &Target) -> ConfVal<T>,\n        config: impl for<'a> FnOnce(&'a CrossToml, &Target) -> ConfVal<Cow<'a, U>>,\n    ) -> Option<T>\n    where\n        U: ToOwned<Owned = T> + ?Sized,\n    {\n        let env = env(&self.env, target);\n        let toml = self\n            .toml\n            .as_ref()\n            .map(|toml| config(toml, target))\n            .unwrap_or_default();\n\n        match (env.target, toml.target) {\n            (Some(value), _) => return Some(value),\n            (None, Some(value)) => return Some(value.into_owned()),\n            (None, None) => {}\n        }\n\n        match (env.build, toml.build) {\n            (Some(value), _) => return Some(value),\n            (None, Some(value)) => return Some(value.into_owned()),\n            (None, None) => {}\n        }\n\n        None\n    }\n\n    fn vec_from_config(\n        &self,\n        target: &Target,\n        env: impl for<'a> FnOnce(&'a Environment, &Target) -> ConfVal<Vec<String>>,\n        config: impl for<'a> FnOnce(&'a CrossToml, &Target) -> ConfVal<&'a [String]>,\n        sum: bool,\n    ) -> Option<Vec<String>> {\n        if sum {\n            let mut env = env(&self.env, target);\n            if let (Some(b), Some(t)) = (&mut env.build, &mut env.target) {\n                b.append(t);\n            }\n            self.sum_of_env_toml_values(env.build, |t| config(t, target))\n        } else {\n            self.get_from_ref(target, env, config)\n        }\n    }\n\n    fn get_from_ref<T, U>(\n        &self,\n        target: &Target,\n        env: impl for<'a> FnOnce(&'a Environment, &Target) -> ConfVal<T>,\n        config: impl for<'a> FnOnce(&'a CrossToml, &Target) -> ConfVal<&'a U>,\n    ) -> Option<T>\n    where\n        U: ToOwned<Owned = T> + ?Sized,\n    {\n        self.get_from_value_inner(target, env, |toml, target| {\n            config(toml, target).map(|v| Cow::Borrowed(v))\n        })\n    }\n\n    fn get_from_value<T>(\n        &self,\n        target: &Target,\n        env: impl FnOnce(&Environment, &Target) -> ConfVal<T>,\n        config: impl FnOnce(&CrossToml, &Target) -> ConfVal<T>,\n    ) -> Option<T>\n    where\n        T: ToOwned<Owned = T>,\n    {\n        self.get_from_value_inner::<T, T>(target, env, |toml, target| {\n            config(toml, target).map(|v| Cow::Owned(v))\n        })\n    }\n\n    #[cfg(test)]\n    pub(crate) fn new_with(toml: Option<CrossToml>, env: Environment) -> Self {\n        Config { toml, env }\n    }\n\n    pub fn build_std(&self, target: &Target) -> Option<BuildStd> {\n        self.get_from_ref(target, Environment::build_std, CrossToml::build_std)\n    }\n\n    pub fn zig(&self, target: &Target) -> Option<bool> {\n        self.get_from_value(target, Environment::zig, CrossToml::zig)\n    }\n\n    pub fn zig_version(&self, target: &Target) -> Option<String> {\n        self.get_from_value(target, Environment::zig_version, CrossToml::zig_version)\n    }\n\n    pub fn zig_image(&self, target: &Target) -> Result<Option<PossibleImage>> {\n        let env = self.env.zig_image(target)?;\n        Ok(self.get_from_value(target, |_, _| env, CrossToml::zig_image))\n    }\n\n    pub fn image(&self, target: &Target) -> Result<Option<PossibleImage>> {\n        let env = self.env.image(target)?;\n        Ok(self.get_from_ref(\n            target,\n            |_, _| ConfVal::new(None, env),\n            |toml, target| ConfVal::new(None, toml.image(target)),\n        ))\n    }\n\n    pub fn runner(&self, target: &Target) -> Option<String> {\n        self.get_from_ref(\n            target,\n            |env, target| ConfVal::new(None, env.runner(target)),\n            |toml, target| ConfVal::new(None, toml.runner(target)),\n        )\n    }\n\n    pub fn doctests(&self) -> Option<bool> {\n        self.env.doctests()\n    }\n\n    pub fn custom_toolchain(&self) -> bool {\n        self.env.custom_toolchain()\n    }\n\n    pub fn custom_toolchain_compat(&self) -> Option<String> {\n        self.env.custom_toolchain_compat()\n    }\n\n    pub fn build_opts(&self) -> Option<String> {\n        self.env.build_opts()\n    }\n\n    pub fn env_passthrough(&self, target: &Target) -> Option<Vec<String>> {\n        self.vec_from_config(\n            target,\n            Environment::passthrough,\n            CrossToml::env_passthrough,\n            true,\n        )\n    }\n\n    pub fn env_volumes(&self, target: &Target) -> Option<Vec<String>> {\n        self.get_from_ref(target, Environment::volumes, CrossToml::env_volumes)\n    }\n\n    pub fn target(&self, target_list: &TargetList) -> Option<Target> {\n        if let Some(env_value) = self.env.target() {\n            return Some(Target::from(&env_value, target_list));\n        }\n        self.toml\n            .as_ref()\n            .and_then(|t| t.default_target(target_list))\n    }\n\n    pub fn dockerfile(&self, target: &Target) -> Option<String> {\n        self.get_from_ref(target, Environment::dockerfile, CrossToml::dockerfile)\n    }\n\n    pub fn dockerfile_context(&self, target: &Target) -> Option<String> {\n        self.get_from_ref(\n            target,\n            Environment::dockerfile_context,\n            CrossToml::dockerfile_context,\n        )\n    }\n\n    pub fn dockerfile_build_args(&self, target: &Target) -> Option<HashMap<String, String>> {\n        // This value does not support env variables\n        self.toml\n            .as_ref()\n            .and_then(|t| t.dockerfile_build_args(target))\n    }\n\n    pub fn pre_build(&self, target: &Target) -> Option<PreBuild> {\n        self.get_from_ref(target, Environment::pre_build, CrossToml::pre_build)\n    }\n\n    // FIXME: remove when we disable sums in 0.3.0.\n    fn sum_of_env_toml_values<'a>(\n        &'a self,\n        env_values: Option<Vec<String>>,\n        toml_getter: impl FnOnce(&'a CrossToml) -> ConfVal<&'a [String]>,\n    ) -> Option<Vec<String>> {\n        let mut defined = false;\n        let mut collect = vec![];\n        if env_values.is_some() {\n            return env_values;\n        } else if let Some(toml) = self.toml.as_ref().map(toml_getter) {\n            if let Some(build) = toml.build {\n                collect.extend(build.iter().cloned());\n                defined = true;\n            }\n\n            if let Some(target) = toml.target {\n                collect.extend(target.iter().cloned());\n                defined = true;\n            }\n        }\n\n        defined.then_some(collect)\n    }\n}\n\npub fn opt_merge<I, T: Extend<I> + IntoIterator<Item = I>>(\n    opt1: Option<T>,\n    opt2: Option<T>,\n) -> Option<T> {\n    match (opt1, opt2) {\n        (None, None) => None,\n        (None, Some(opt2)) => Some(opt2),\n        (Some(opt1), None) => Some(opt1),\n        (Some(opt1), Some(opt2)) => {\n            let mut res = opt2;\n            res.extend(opt1);\n            Some(res)\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::errors::*;\n    use crate::{Target, TargetList};\n\n    fn target_list() -> TargetList {\n        TargetList {\n            triples: vec![\n                \"aarch64-unknown-linux-gnu\".to_owned(),\n                \"armv7-unknown-linux-musleabihf\".to_owned(),\n            ],\n        }\n    }\n\n    fn target() -> Target {\n        let target_list = target_list();\n        Target::from(\"aarch64-unknown-linux-gnu\", &target_list)\n    }\n\n    fn target2() -> Target {\n        let target_list = target_list();\n        Target::from(\"armv7-unknown-linux-musleabihf\", &target_list)\n    }\n\n    mod test_environment {\n        use super::*;\n\n        #[test]\n        pub fn parse_error_in_env() -> Result<()> {\n            let mut map = std::collections::HashMap::new();\n            map.insert(\"CROSS_BUILD_STD\", \"false\");\n            map.insert(\"CROSS_BUILD_ZIG_IMAGE\", \"zig:local\");\n\n            let env = Environment::new(Some(map));\n            assert_eq!(\n                env.build_std(&target()),\n                (Some(BuildStd::Bool(false)), None)\n            );\n            assert_eq!(env.zig(&target()), (None, None));\n            assert_eq!(env.zig_version(&target()), (None, None));\n            assert_eq!(env.zig_image(&target())?, (Some(\"zig:local\".into()), None));\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn build_and_target_set_returns_tuple() -> Result<()> {\n            let mut map = std::collections::HashMap::new();\n            map.insert(\"CROSS_BUILD_ZIG\", \"true\");\n            map.insert(\"CROSS_BUILD_ZIG_VERSION\", \"2.17\");\n\n            let env = Environment::new(Some(map));\n            assert_eq!(env.zig(&target()), (Some(true), None));\n            assert_eq!(env.zig_version(&target()), (Some(\"2.17\".into()), None));\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn target_build_var_name() {\n            let map = std::collections::HashMap::new();\n\n            let env = Environment::new(Some(map));\n            assert_eq!(\n                env.build_var_name(\"target-aarch64-unknown-linux-gnu_image\"),\n                \"CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_IMAGE\"\n            );\n        }\n\n        #[test]\n        pub fn collect_passthrough() {\n            let mut map = std::collections::HashMap::new();\n            map.insert(\"CROSS_BUILD_ENV_PASSTHROUGH\", \"TEST1 TEST2\");\n            map.insert(\n                \"CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_ENV_PASSTHROUGH\",\n                \"PASS1 PASS2\",\n            );\n\n            let env = Environment::new(Some(map));\n\n            let ConfVal { build, target } = env.passthrough(&target());\n            assert!(build.as_ref().unwrap().contains(&\"TEST1\".to_owned()));\n            assert!(build.as_ref().unwrap().contains(&\"TEST2\".to_owned()));\n            assert!(target.as_ref().unwrap().contains(&\"PASS1\".to_owned()));\n            assert!(target.as_ref().unwrap().contains(&\"PASS2\".to_owned()));\n        }\n    }\n\n    #[cfg(test)]\n    mod test_config {\n        use super::*;\n\n        macro_rules! s {\n            ($x:literal) => {\n                $x.to_owned()\n            };\n        }\n\n        fn toml(content: &str) -> Result<crate::CrossToml> {\n            Ok(\n                CrossToml::parse_from_cross_str(content, None, &mut MessageInfo::default())\n                    .wrap_err(\"couldn't parse toml\")?\n                    .0,\n            )\n        }\n\n        #[test]\n        pub fn env_target_and_toml_build_pre_build_then_use_env() -> Result<()> {\n            let mut map = HashMap::new();\n            map.insert(\n                \"CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_PRE_BUILD\",\n                \"dpkg --add-architecture arm64\",\n            );\n\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_BUILD_PRE_BUILD)?), env);\n            assert_eq!(\n                config.pre_build(&target()),\n                Some(PreBuild::Single {\n                    line: s!(\"dpkg --add-architecture arm64\"),\n                    env: true\n                })\n            );\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn env_target_then_toml_target_then_env_build_then_toml_build() -> Result<()> {\n            let mut map = HashMap::new();\n            map.insert(\"CROSS_BUILD_DOCKERFILE\", \"Dockerfile3\");\n            map.insert(\n                \"CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_DOCKERFILE\",\n                \"Dockerfile4\",\n            );\n\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_BUILD_DOCKERFILE)?), env);\n            assert_eq!(config.dockerfile(&target()), Some(s!(\"Dockerfile4\")));\n            assert_eq!(config.dockerfile(&target2()), Some(s!(\"Dockerfile3\")));\n\n            let map = HashMap::new();\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_BUILD_DOCKERFILE)?), env);\n            assert_eq!(config.dockerfile(&target()), Some(s!(\"Dockerfile2\")));\n            assert_eq!(config.dockerfile(&target2()), Some(s!(\"Dockerfile1\")));\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn toml_build_passthrough_then_use_target_passthrough_both() -> Result<()> {\n            let map = HashMap::new();\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_ARRAYS_BOTH)?), env);\n            assert_eq!(\n                config.env_passthrough(&target()),\n                Some(vec![s!(\"VAR1\"), s!(\"VAR2\"), s!(\"VAR3\"), s!(\"VAR4\")])\n            );\n            assert_eq!(\n                config.env_volumes(&target()),\n                Some(vec![s!(\"VOLUME3\"), s!(\"VOLUME4\")])\n            );\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn toml_build_passthrough() -> Result<()> {\n            let map = HashMap::new();\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_ARRAYS_BUILD)?), env);\n            assert_eq!(\n                config.env_passthrough(&target()),\n                Some(vec![s!(\"VAR1\"), s!(\"VAR2\")])\n            );\n            assert_eq!(\n                config.env_volumes(&target()),\n                Some(vec![s!(\"VOLUME1\"), s!(\"VOLUME2\")])\n            );\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn toml_target_passthrough() -> Result<()> {\n            let map = HashMap::new();\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_ARRAYS_TARGET)?), env);\n            assert_eq!(\n                config.env_passthrough(&target()),\n                Some(vec![s!(\"VAR3\"), s!(\"VAR4\")])\n            );\n            assert_eq!(\n                config.env_volumes(&target()),\n                Some(vec![s!(\"VOLUME3\"), s!(\"VOLUME4\")])\n            );\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn volumes_use_env_over_toml() -> Result<()> {\n            let mut map = HashMap::new();\n            map.insert(\"CROSS_BUILD_ENV_VOLUMES\", \"VOLUME1 VOLUME2\");\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_BUILD_VOLUMES)?), env);\n            let expected = [s!(\"VOLUME1\"), s!(\"VOLUME2\")];\n\n            let result = config.env_volumes(&target()).unwrap_or_default();\n            dbg!(&result);\n            assert!(result.len() == 2);\n            assert!(result.contains(&expected[0]));\n            assert!(result.contains(&expected[1]));\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn volumes_use_toml_when_no_env() -> Result<()> {\n            let map = HashMap::new();\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_BUILD_VOLUMES)?), env);\n            let expected = [s!(\"VOLUME3\"), s!(\"VOLUME4\")];\n\n            let result = config.env_volumes(&target()).unwrap_or_default();\n            dbg!(&result);\n            assert!(result.len() == 2);\n            assert!(result.contains(&expected[0]));\n            assert!(result.contains(&expected[1]));\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn no_env_and_no_toml_default_target_then_none() -> Result<()> {\n            let config = Config::new_with(None, Environment::new(None));\n            let config_target = config.target(&target_list());\n            assert_eq!(config_target, None);\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn env_and_toml_default_target_then_use_env() -> Result<()> {\n            let mut map = HashMap::new();\n            map.insert(\"CROSS_BUILD_TARGET\", \"armv7-unknown-linux-musleabihf\");\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(Some(toml(TOML_DEFAULT_TARGET)?), env);\n\n            let config_target = config.target(&target_list()).unwrap();\n            assert_eq!(config_target.triple(), \"armv7-unknown-linux-musleabihf\");\n\n            Ok(())\n        }\n\n        #[test]\n        pub fn no_env_but_toml_default_target_then_use_toml() -> Result<()> {\n            let env = Environment::new(None);\n            let config = Config::new_with(Some(toml(TOML_DEFAULT_TARGET)?), env);\n\n            let config_target = config.target(&target_list()).unwrap();\n            assert_eq!(config_target.triple(), \"aarch64-unknown-linux-gnu\");\n\n            Ok(())\n        }\n\n        static TOML_BUILD_PRE_BUILD: &str = r#\"\n    [build]\n    pre-build = [\"apt-get update && apt-get install zlib-dev\"]\n    \"#;\n\n        static TOML_BUILD_DOCKERFILE: &str = r#\"\n    [build]\n    dockerfile = \"Dockerfile1\"\n    [target.aarch64-unknown-linux-gnu]\n    dockerfile = \"Dockerfile2\"\n    \"#;\n\n        static TOML_BUILD_VOLUMES: &str = r#\"\n    [build.env]\n    volumes = [\"VOLUME3\", \"VOLUME4\"]\n    [target.aarch64-unknown-linux-gnu]\n    \"#;\n\n        static TOML_ARRAYS_BOTH: &str = r#\"\n    [build.env]\n    passthrough = [\"VAR1\", \"VAR2\"]\n    volumes = [\"VOLUME1\", \"VOLUME2\"]\n\n    [target.aarch64-unknown-linux-gnu.env]\n    passthrough = [\"VAR3\", \"VAR4\"]\n    volumes = [\"VOLUME3\", \"VOLUME4\"]\n    \"#;\n\n        static TOML_ARRAYS_BUILD: &str = r#\"\n    [build.env]\n    passthrough = [\"VAR1\", \"VAR2\"]\n    volumes = [\"VOLUME1\", \"VOLUME2\"]\n    \"#;\n\n        static TOML_ARRAYS_TARGET: &str = r#\"\n    [target.aarch64-unknown-linux-gnu.env]\n    passthrough = [\"VAR3\", \"VAR4\"]\n    volumes = [\"VOLUME3\", \"VOLUME4\"]\n    \"#;\n\n        static TOML_DEFAULT_TARGET: &str = r#\"\n    [build]\n    default-target = \"aarch64-unknown-linux-gnu\"\n    \"#;\n    }\n}\n"
  },
  {
    "path": "src/cross_toml.rs",
    "content": "//! The `Cross.toml` configuration file.\n//!\n//! For a detailed user documentation of the file and the contents please refer to the [docs in the\n//! repo][1].\n//!\n//! [1]: https://github.com/cross-rs/cross/blob/main/docs/config_file.md\n\nuse crate::config::ConfVal;\nuse crate::docker::custom::PreBuild;\nuse crate::docker::PossibleImage;\nuse crate::shell::MessageInfo;\nuse crate::{config, errors::*};\nuse crate::{Target, TargetList};\nuse serde::de::DeserializeOwned;\nuse serde::{Deserialize, Deserializer, Serialize};\nuse std::collections::{BTreeSet, HashMap};\nuse std::str::FromStr;\n\n/// Environment configuration\n#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]\npub struct CrossEnvConfig {\n    volumes: Option<Vec<String>>,\n    passthrough: Option<Vec<String>>,\n}\n\n/// Build configuration\n#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]\n#[serde(rename_all = \"kebab-case\")]\npub struct CrossBuildConfig {\n    #[serde(default)]\n    env: CrossEnvConfig,\n    build_std: Option<BuildStd>,\n    #[serde(default, deserialize_with = \"opt_string_bool_or_struct\")]\n    zig: Option<CrossZigConfig>,\n    default_target: Option<String>,\n    #[serde(default, deserialize_with = \"opt_string_or_string_vec\")]\n    pre_build: Option<PreBuild>,\n    #[serde(default, deserialize_with = \"opt_string_or_struct\")]\n    dockerfile: Option<CrossTargetDockerfileConfig>,\n}\n\n/// Target configuration\n#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"kebab-case\")]\npub struct CrossTargetConfig {\n    build_std: Option<BuildStd>,\n    #[serde(default, deserialize_with = \"opt_string_bool_or_struct\")]\n    zig: Option<CrossZigConfig>,\n    #[serde(default, deserialize_with = \"opt_string_or_struct\")]\n    image: Option<PossibleImage>,\n    #[serde(default, deserialize_with = \"opt_string_or_struct\")]\n    dockerfile: Option<CrossTargetDockerfileConfig>,\n    #[serde(default, deserialize_with = \"opt_string_or_string_vec\")]\n    pre_build: Option<PreBuild>,\n    runner: Option<String>,\n    #[serde(default)]\n    env: CrossEnvConfig,\n}\n\n#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]\n#[serde(untagged, rename_all = \"kebab-case\")]\npub enum BuildStd {\n    Bool(bool),\n    Crates(Vec<String>),\n}\n\nimpl Default for BuildStd {\n    fn default() -> Self {\n        Self::Bool(false)\n    }\n}\n\nimpl BuildStd {\n    pub fn enabled(&self) -> bool {\n        match self {\n            Self::Bool(enabled) => *enabled,\n            Self::Crates(arr) => !arr.is_empty(),\n        }\n    }\n}\n\n/// Dockerfile configuration\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]\n#[serde(rename_all = \"kebab-case\")]\npub struct CrossTargetDockerfileConfig {\n    file: String,\n    context: Option<String>,\n    build_args: Option<HashMap<String, String>>,\n}\n\nimpl FromStr for CrossTargetDockerfileConfig {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(CrossTargetDockerfileConfig {\n            file: s.to_owned(),\n            context: None,\n            build_args: None,\n        })\n    }\n}\n\n/// Zig configuration\n#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]\n#[serde(rename_all = \"kebab-case\")]\npub struct CrossZigConfig {\n    enable: Option<bool>,\n    version: Option<String>,\n    #[serde(default, deserialize_with = \"opt_string_or_struct\")]\n    image: Option<PossibleImage>,\n}\n\nimpl From<&str> for CrossZigConfig {\n    fn from(s: &str) -> CrossZigConfig {\n        CrossZigConfig {\n            enable: Some(true),\n            version: Some(s.to_owned()),\n            image: None,\n        }\n    }\n}\n\nimpl From<bool> for CrossZigConfig {\n    fn from(s: bool) -> CrossZigConfig {\n        CrossZigConfig {\n            enable: Some(s),\n            version: None,\n            image: None,\n        }\n    }\n}\n\nimpl FromStr for CrossZigConfig {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(s.into())\n    }\n}\n\n/// Cross configuration\n#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]\npub struct CrossToml {\n    #[serde(default, rename = \"target\")]\n    pub targets: HashMap<Target, CrossTargetConfig>,\n    #[serde(default)]\n    pub build: CrossBuildConfig,\n}\n\nimpl CrossToml {\n    /// Parses the [`CrossToml`] from a string\n    pub fn parse_from_cross_str(\n        toml_str: &str,\n        source: Option<&str>,\n        msg_info: &mut MessageInfo,\n    ) -> Result<(Self, BTreeSet<String>)> {\n        let tomld = toml::Deserializer::parse(toml_str)?;\n        Self::parse_from_deserializer(tomld, source, msg_info)\n    }\n\n    /// Parses the [`CrossToml`] from a string containing the Cargo.toml contents\n    pub fn parse_from_cargo_package_str(\n        cargo_toml_str: &str,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Option<(Self, BTreeSet<String>)>> {\n        let cargo_toml: toml::Value = toml::from_str(cargo_toml_str)?;\n        let cross_metadata_opt = cargo_toml\n            .get(\"package\")\n            .and_then(|p| p.get(\"metadata\"))\n            .and_then(|m| m.get(\"cross\"));\n\n        if let Some(cross_meta) = cross_metadata_opt {\n            Ok(Some(Self::parse_from_deserializer(\n                cross_meta.clone(),\n                None,\n                msg_info,\n            )?))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Parses the [`CrossToml`] from a [`Deserializer`]\n    pub fn parse_from_deserializer<'de, D>(\n        deserializer: D,\n        source: Option<&str>,\n        msg_info: &mut MessageInfo,\n    ) -> Result<(Self, BTreeSet<String>)>\n    where\n        D: Deserializer<'de>,\n        D::Error: Send + Sync + 'static,\n    {\n        let mut unused = BTreeSet::new();\n        let cfg = serde_ignored::deserialize(deserializer, |path| {\n            unused.insert(path.to_string());\n        })?;\n\n        if !unused.is_empty() {\n            msg_info.warn(format_args!(\n                \"found unused key(s) in Cross configuration{}:\\n > {}\",\n                source.map(|s| format!(\" at {s}\")).unwrap_or_default(),\n                unused.clone().into_iter().collect::<Vec<_>>().join(\", \")\n            ))?;\n        }\n\n        Ok((cfg, unused))\n    }\n\n    /// Merges another [`CrossToml`] into `self` and returns a new merged one\n    pub fn merge(self, other: CrossToml) -> Result<CrossToml> {\n        type ValueMap = serde_json::Map<String, serde_json::Value>;\n\n        fn to_map<S: Serialize>(s: S) -> Result<ValueMap> {\n            if let Some(obj) = serde_json::to_value(s)\n                .wrap_err(\"could not convert CrossToml to serde_json::Value\")?\n                .as_object()\n            {\n                Ok(obj.clone())\n            } else {\n                eyre::bail!(\"failed to serialize CrossToml as object\");\n            }\n        }\n\n        fn from_map<D: DeserializeOwned>(map: ValueMap) -> Result<D> {\n            let value = serde_json::to_value(map)\n                .wrap_err(\"could not convert ValueMap to serde_json::Value\")?;\n            serde_json::from_value(value)\n                .wrap_err(\"could not deserialize serde_json::Value to CrossToml\")\n        }\n\n        // merge 2 objects. y has precedence over x.\n        fn merge_objects(x: &mut ValueMap, y: &ValueMap) -> Option<()> {\n            // we need to iterate over both keys, so we need a full deduplication\n            let keys: BTreeSet<String> = x.keys().chain(y.keys()).cloned().collect();\n            for key in keys {\n                let in_x = x.contains_key(&key);\n                let in_y = y.contains_key(&key);\n                if !in_x && in_y {\n                    let yk = y[&key].clone();\n                    x.insert(key, yk);\n                    continue;\n                } else if !in_y {\n                    continue;\n                }\n\n                let xk = x.get_mut(&key)?;\n                let yk = y.get(&key)?;\n                if xk.is_null() && !yk.is_null() {\n                    *xk = yk.clone();\n                    continue;\n                } else if yk.is_null() {\n                    continue;\n                }\n\n                // now we've filtered out missing keys and optional values\n                // all key/value pairs should be same type.\n                if xk.is_object() {\n                    merge_objects(xk.as_object_mut()?, yk.as_object()?)?;\n                } else {\n                    *xk = yk.clone();\n                }\n            }\n\n            Some(())\n        }\n\n        // Builds maps of objects\n        let mut self_map = to_map(self)?;\n        let other_map = to_map(other)?;\n\n        merge_objects(&mut self_map, &other_map).ok_or_else(|| eyre::eyre!(\"could not merge\"))?;\n        from_map(self_map)\n    }\n\n    /// Returns the `target.{}.image` part of `Cross.toml`\n    pub fn image(&self, target: &Target) -> Option<&PossibleImage> {\n        self.get_target(target).and_then(|t| t.image.as_ref())\n    }\n\n    /// Returns the `{}.dockerfile` or `{}.dockerfile.file` part of `Cross.toml`\n    pub fn dockerfile(&self, target: &Target) -> ConfVal<&String> {\n        self.get_ref(\n            target,\n            |b| b.dockerfile.as_ref().map(|c| &c.file),\n            |t| t.dockerfile.as_ref().map(|c| &c.file),\n        )\n    }\n\n    /// Returns the `target.{}.dockerfile.context` part of `Cross.toml`\n    pub fn dockerfile_context(&self, target: &Target) -> ConfVal<&String> {\n        self.get_ref(\n            target,\n            |b| b.dockerfile.as_ref().and_then(|c| c.context.as_ref()),\n            |t| t.dockerfile.as_ref().and_then(|c| c.context.as_ref()),\n        )\n    }\n\n    /// Returns the `target.{}.dockerfile.build_args` part of `Cross.toml`\n    pub fn dockerfile_build_args(&self, target: &Target) -> Option<HashMap<String, String>> {\n        let target = self\n            .get_target(target)\n            .and_then(|t| t.dockerfile.as_ref())\n            .and_then(|d| d.build_args.as_ref());\n\n        let build = self\n            .build\n            .dockerfile\n            .as_ref()\n            .and_then(|d| d.build_args.as_ref());\n\n        config::opt_merge(target.cloned(), build.cloned())\n    }\n\n    /// Returns the `build.dockerfile.pre-build` and `target.{}.dockerfile.pre-build` part of `Cross.toml`\n    pub fn pre_build(&self, target: &Target) -> ConfVal<&PreBuild> {\n        self.get_ref(target, |b| b.pre_build.as_ref(), |t| t.pre_build.as_ref())\n    }\n\n    /// Returns the `target.{}.runner` part of `Cross.toml`\n    pub fn runner(&self, target: &Target) -> Option<&String> {\n        self.get_target(target).and_then(|t| t.runner.as_ref())\n    }\n\n    /// Returns the `build.build-std` or the `target.{}.build-std` part of `Cross.toml`\n    pub fn build_std(&self, target: &Target) -> ConfVal<&BuildStd> {\n        self.get_ref(target, |b| b.build_std.as_ref(), |t| t.build_std.as_ref())\n    }\n\n    /// Returns the `{}.zig` or `{}.zig.version` part of `Cross.toml`\n    pub fn zig(&self, target: &Target) -> ConfVal<bool> {\n        self.get_value(\n            target,\n            |b| b.zig.as_ref().and_then(|z| z.enable),\n            |t| t.zig.as_ref().and_then(|z| z.enable),\n        )\n    }\n\n    /// Returns the `{}.zig` or `{}.zig.version` part of `Cross.toml`\n    pub fn zig_version(&self, target: &Target) -> ConfVal<String> {\n        self.get_value(\n            target,\n            |b| b.zig.as_ref().and_then(|c| c.version.clone()),\n            |t| t.zig.as_ref().and_then(|c| c.version.clone()),\n        )\n    }\n\n    /// Returns the  `{}.zig.image` part of `Cross.toml`\n    pub fn zig_image(&self, target: &Target) -> ConfVal<PossibleImage> {\n        self.get_value(\n            target,\n            |b| b.zig.as_ref().and_then(|c| c.image.clone()),\n            |t| t.zig.as_ref().and_then(|c| c.image.clone()),\n        )\n    }\n\n    /// Returns the list of environment variables to pass through for `build` and `target`\n    pub fn env_passthrough(&self, target: &Target) -> ConfVal<&[String]> {\n        self.get_ref(\n            target,\n            |build| build.env.passthrough.as_deref(),\n            |t| t.env.passthrough.as_deref(),\n        )\n    }\n\n    /// Returns the list of environment variables to pass through for `build` and `target`\n    pub fn env_volumes(&self, target: &Target) -> ConfVal<&[String]> {\n        self.get_ref(\n            target,\n            |build| build.env.volumes.as_deref(),\n            |t| t.env.volumes.as_deref(),\n        )\n    }\n\n    /// Returns the default target to build,\n    pub fn default_target(&self, target_list: &TargetList) -> Option<Target> {\n        self.build\n            .default_target\n            .as_ref()\n            .map(|t| Target::from(t, target_list))\n    }\n\n    /// Returns a reference to the [`CrossTargetConfig`] of a specific `target`\n    fn get_target(&self, target: &Target) -> Option<&CrossTargetConfig> {\n        self.targets.get(target)\n    }\n\n    fn get_value<T>(\n        &self,\n        target_triple: &Target,\n        get_build: impl Fn(&CrossBuildConfig) -> Option<T>,\n        get_target: impl Fn(&CrossTargetConfig) -> Option<T>,\n    ) -> ConfVal<T> {\n        let build = get_build(&self.build);\n        let target = self.get_target(target_triple).and_then(get_target);\n        ConfVal::new(build, target)\n    }\n\n    fn get_ref<T: ?Sized>(\n        &self,\n        target_triple: &Target,\n        get_build: impl Fn(&CrossBuildConfig) -> Option<&T>,\n        get_target: impl Fn(&CrossTargetConfig) -> Option<&T>,\n    ) -> ConfVal<&T> {\n        let build = get_build(&self.build);\n        let target = self.get_target(target_triple).and_then(get_target);\n        ConfVal::new(build, target)\n    }\n}\n\nfn opt_string_or_struct<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>\nwhere\n    T: Deserialize<'de> + std::str::FromStr<Err = std::convert::Infallible>,\n    D: serde::Deserializer<'de>,\n{\n    use std::{fmt, marker::PhantomData};\n\n    use serde::de::{self, MapAccess, Visitor};\n\n    struct StringOrStruct<T>(PhantomData<fn() -> T>);\n\n    impl<'de, T> Visitor<'de> for StringOrStruct<T>\n    where\n        T: Deserialize<'de> + FromStr<Err = std::convert::Infallible>,\n    {\n        type Value = Option<T>;\n\n        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n            formatter.write_str(\"string or map\")\n        }\n\n        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(FromStr::from_str(value).ok())\n        }\n\n        fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>\n        where\n            M: MapAccess<'de>,\n        {\n            let t: Result<T, _> =\n                Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));\n            t.map(Some)\n        }\n\n        fn visit_none<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n\n        fn visit_unit<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n    }\n\n    deserializer.deserialize_any(StringOrStruct(PhantomData))\n}\n\nfn opt_string_or_string_vec<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>\nwhere\n    T: Deserialize<'de> + std::str::FromStr<Err = std::convert::Infallible> + From<Vec<String>>,\n    D: serde::Deserializer<'de>,\n{\n    use std::{fmt, marker::PhantomData};\n\n    use serde::de::{self, SeqAccess, Visitor};\n\n    struct StringOrStringVec<T>(PhantomData<fn() -> T>);\n\n    impl<'de, T> Visitor<'de> for StringOrStringVec<T>\n    where\n        T: Deserialize<'de> + FromStr<Err = std::convert::Infallible> + From<Vec<String>>,\n    {\n        type Value = Option<T>;\n\n        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n            formatter.write_str(\"string or seq\")\n        }\n\n        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(FromStr::from_str(value).ok())\n        }\n\n        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>\n        where\n            A: SeqAccess<'de>,\n        {\n            let mut vec: Vec<String> = vec![];\n\n            while let Some(inner) = seq.next_element::<String>()? {\n                vec.push(inner);\n            }\n            Ok(Some(vec.into()))\n        }\n\n        fn visit_none<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n\n        fn visit_unit<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n    }\n\n    deserializer.deserialize_any(StringOrStringVec(PhantomData))\n}\n\nfn opt_string_bool_or_struct<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>\nwhere\n    T: Deserialize<'de> + From<bool> + std::str::FromStr<Err = std::convert::Infallible>,\n    D: serde::Deserializer<'de>,\n{\n    use std::{fmt, marker::PhantomData};\n\n    use serde::de::{self, MapAccess, Visitor};\n\n    struct StringBoolOrStruct<T>(PhantomData<fn() -> T>);\n\n    impl<'de, T> Visitor<'de> for StringBoolOrStruct<T>\n    where\n        T: Deserialize<'de> + From<bool> + std::str::FromStr<Err = std::convert::Infallible>,\n    {\n        type Value = Option<T>;\n\n        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {\n            formatter.write_str(\"string, bool, or map\")\n        }\n\n        fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(Some(From::from(value)))\n        }\n\n        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(FromStr::from_str(value).ok())\n        }\n\n        fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>\n        where\n            M: MapAccess<'de>,\n        {\n            let t: Result<T, _> =\n                Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));\n            t.map(Some)\n        }\n\n        fn visit_none<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n\n        fn visit_unit<E>(self) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            Ok(None)\n        }\n    }\n\n    deserializer.deserialize_any(StringBoolOrStruct(PhantomData))\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::docker::{ImagePlatform, ImageReference};\n\n    use super::*;\n    use crate::shell;\n\n    macro_rules! m {\n        () => {\n            MessageInfo::new(shell::ColorChoice::Never, shell::Verbosity::Quiet)\n        };\n    }\n\n    macro_rules! p {\n        ($x:literal) => {\n            $x.parse()?\n        };\n    }\n\n    #[test]\n    pub fn parse_empty_toml() -> Result<()> {\n        let cfg = CrossToml {\n            targets: HashMap::new(),\n            build: CrossBuildConfig::default(),\n        };\n        let (parsed_cfg, unused) = CrossToml::parse_from_cross_str(\"\", None, &mut m!())?;\n\n        assert_eq!(parsed_cfg, cfg);\n        assert!(unused.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn parse_build_toml() -> Result<()> {\n        let cfg = CrossToml {\n            targets: HashMap::new(),\n            build: CrossBuildConfig {\n                env: CrossEnvConfig {\n                    volumes: Some(vec![p!(\"VOL1_ARG\"), p!(\"VOL2_ARG\")]),\n                    passthrough: Some(vec![p!(\"VAR1\"), p!(\"VAR2\")]),\n                },\n                build_std: None,\n                zig: None,\n                default_target: None,\n                pre_build: Some(PreBuild::Lines(vec![p!(\"echo 'Hello World!'\")])),\n                dockerfile: None,\n            },\n        };\n\n        let test_str = r#\"\n          [build]\n          pre-build = [\"echo 'Hello World!'\"]\n\n          [build.env]\n          volumes = [\"VOL1_ARG\", \"VOL2_ARG\"]\n          passthrough = [\"VAR1\", \"VAR2\"]\n        \"#;\n        let (parsed_cfg, unused) = CrossToml::parse_from_cross_str(test_str, None, &mut m!())?;\n\n        assert_eq!(parsed_cfg, cfg);\n        assert!(unused.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn parse_target_toml() -> Result<()> {\n        let mut target_map = HashMap::new();\n        target_map.insert(\n            Target::BuiltIn {\n                triple: \"aarch64-unknown-linux-gnu\".into(),\n            },\n            CrossTargetConfig {\n                env: CrossEnvConfig {\n                    passthrough: Some(vec![p!(\"VAR1\"), p!(\"VAR2\")]),\n                    volumes: Some(vec![p!(\"VOL1_ARG\"), p!(\"VOL2_ARG\")]),\n                },\n                build_std: Some(BuildStd::Bool(true)),\n                zig: None,\n                image: Some(\"test-image\".into()),\n                runner: None,\n                dockerfile: None,\n                pre_build: Some(PreBuild::Lines(vec![])),\n            },\n        );\n        target_map.insert(\n            Target::BuiltIn {\n                triple: \"aarch64-unknown-linux-musl\".into(),\n            },\n            CrossTargetConfig {\n                env: CrossEnvConfig {\n                    passthrough: None,\n                    volumes: None,\n                },\n                build_std: None,\n                zig: Some(CrossZigConfig {\n                    enable: Some(true),\n                    version: Some(p!(\"2.17\")),\n                    image: Some(\"zig:local\".into()),\n                }),\n                image: None,\n                runner: None,\n                dockerfile: None,\n                pre_build: None,\n            },\n        );\n\n        let cfg = CrossToml {\n            targets: target_map,\n            build: CrossBuildConfig::default(),\n        };\n\n        let test_str = r#\"\n            [target.aarch64-unknown-linux-gnu.env]\n            volumes = [\"VOL1_ARG\", \"VOL2_ARG\"]\n            passthrough = [\"VAR1\", \"VAR2\"]\n            [target.aarch64-unknown-linux-gnu]\n            build-std = true\n            image = \"test-image\"\n            pre-build = []\n\n            [target.aarch64-unknown-linux-musl.zig]\n            enable = true\n            version = \"2.17\"\n            image = \"zig:local\"\n        \"#;\n        let (parsed_cfg, unused) = CrossToml::parse_from_cross_str(test_str, None, &mut m!())?;\n\n        assert_eq!(parsed_cfg, cfg);\n        assert!(unused.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn parse_mixed_toml() -> Result<()> {\n        let mut target_map = HashMap::new();\n        target_map.insert(\n            Target::BuiltIn {\n                triple: \"aarch64-unknown-linux-gnu\".into(),\n            },\n            CrossTargetConfig {\n                build_std: None,\n                zig: None,\n                image: Some(PossibleImage {\n                    reference: ImageReference::Name(\"test-image\".to_owned()),\n                    toolchain: vec![ImagePlatform::from_target(\n                        \"aarch64-unknown-linux-musl\".into(),\n                    )?],\n                }),\n                dockerfile: Some(CrossTargetDockerfileConfig {\n                    file: p!(\"Dockerfile.test\"),\n                    context: None,\n                    build_args: None,\n                }),\n                pre_build: Some(PreBuild::Lines(vec![p!(\"echo 'Hello'\")])),\n                runner: None,\n                env: CrossEnvConfig {\n                    passthrough: None,\n                    volumes: Some(vec![p!(\"VOL\")]),\n                },\n            },\n        );\n\n        let cfg = CrossToml {\n            targets: target_map,\n            build: CrossBuildConfig {\n                env: CrossEnvConfig {\n                    volumes: None,\n                    passthrough: Some(vec![]),\n                },\n                build_std: None,\n                zig: Some(CrossZigConfig {\n                    enable: None,\n                    version: None,\n                    image: Some(PossibleImage {\n                        reference: ImageReference::Name(\"zig:local\".to_owned()),\n                        toolchain: vec![ImagePlatform::from_target(\n                            \"aarch64-unknown-linux-gnu\".into(),\n                        )?],\n                    }),\n                }),\n                default_target: None,\n                pre_build: Some(PreBuild::Lines(vec![])),\n                dockerfile: None,\n            },\n        };\n\n        let test_str = r#\"\n            [build]\n            pre-build = []\n\n            [build.zig.image]\n            name = \"zig:local\"\n            toolchain = [\"aarch64-unknown-linux-gnu\"]\n\n            [build.env]\n            passthrough = []\n\n            [target.aarch64-unknown-linux-gnu]\n            dockerfile = \"Dockerfile.test\"\n            pre-build = [\"echo 'Hello'\"]\n            image.name = \"test-image\"\n            image.toolchain = [\"aarch64-unknown-linux-musl\"]\n\n            [target.aarch64-unknown-linux-gnu.env]\n            volumes = [\"VOL\"]\n        \"#;\n        let (parsed_cfg, unused) = CrossToml::parse_from_cross_str(test_str, None, &mut m!())?;\n\n        assert_eq!(parsed_cfg, cfg);\n        assert!(unused.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn parse_from_empty_cargo_toml() -> Result<()> {\n        let test_str = r#\"\n          [package]\n          name = \"cargo_toml_test_package\"\n          version = \"0.1.0\"\n\n          [dependencies]\n          cross = \"1.2.3\"\n        \"#;\n\n        let res = CrossToml::parse_from_cargo_package_str(test_str, &mut m!())?;\n        assert!(res.is_none());\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn parse_from_cargo_toml() -> Result<()> {\n        let cfg = CrossToml {\n            targets: HashMap::new(),\n            build: CrossBuildConfig {\n                env: CrossEnvConfig {\n                    passthrough: None,\n                    volumes: None,\n                },\n                build_std: Some(BuildStd::Bool(true)),\n                zig: None,\n                default_target: None,\n                pre_build: None,\n                dockerfile: None,\n            },\n        };\n\n        let test_str = r#\"\n          [package]\n          name = \"cargo_toml_test_package\"\n          version = \"0.1.0\"\n\n          [dependencies]\n          cross = \"1.2.3\"\n\n          [package.metadata.cross.build]\n          build-std = true\n        \"#;\n\n        if let Some((parsed_cfg, _unused)) =\n            CrossToml::parse_from_cargo_package_str(test_str, &mut m!())?\n        {\n            assert_eq!(parsed_cfg, cfg);\n        } else {\n            panic!(\"Parsing result is None\");\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    pub fn fully_populated_roundtrip() -> Result<()> {\n        let cfg = r#\"\n            [target.a]\n            build-std = true\n            image.name = \"local\"\n            image.toolchain = [\"x86_64-unknown-linux-gnu\"]\n            dockerfile.file = \"Dockerfile\"\n            dockerfile.context = \"..\"\n            pre-build = [\"sh\"]\n            zig = true\n\n            [target.b]\n            pre-build = \"sh\"\n            zig = \"2.17\"\n        \"#;\n\n        let (cfg, _) = CrossToml::parse_from_cross_str(cfg, None, &mut m!())?;\n        serde_json::from_value::<CrossToml>(serde_json::to_value(cfg)?)?;\n        Ok(())\n    }\n\n    #[test]\n    pub fn merge() -> Result<()> {\n        let cfg1_str = r#\"\n            [target.aarch64-unknown-linux-gnu]\n            build-std = true\n            image = \"test-image1\"\n\n            [target.aarch64-unknown-linux-gnu.env]\n            volumes = [\"VOL1_ARG\"]\n            passthrough = [\"VAR1\"]\n\n            [target.target2]\n            build-std = true\n            image = \"test-image2\"\n\n            [target.target2.env]\n            volumes = [\"VOL2_ARG\"]\n            passthrough = [\"VAR2\"]\n\n            [build]\n            build-std = true\n\n            [build.env]\n            volumes = []\n            passthrough = [\"VAR1\", \"VAR2\"]\n        \"#;\n\n        let cfg2_str = r#\"\n            [target.target2]\n            build-std = false\n            image = \"test-image2-precedence\"\n\n            [target.target2.env]\n            volumes = [\"VOL2_ARG_PRECEDENCE\"]\n            passthrough = [\"VAR2_PRECEDENCE\"]\n\n            [target.target3]\n            build-std = true\n            image = \"@sha256:test-image3\"\n\n            [target.target3.env]\n            volumes = [\"VOL3_ARG\"]\n            passthrough = [\"VAR3\"]\n\n            [build]\n            build-std = [\"core\", \"alloc\"]\n            default-target = \"aarch64-unknown-linux-gnu\"\n\n            [build.env]\n            volumes = []\n            passthrough = [\"VAR3\", \"VAR4\"]\n\n        \"#;\n\n        let cfg_expected_str = r#\"\n            [target.aarch64-unknown-linux-gnu]\n            build-std = true\n            image = \"test-image1\"\n\n            [target.aarch64-unknown-linux-gnu.env]\n            volumes = [\"VOL1_ARG\"]\n            passthrough = [\"VAR1\"]\n\n            [target.target2]\n            build-std = false\n            image = \"test-image2-precedence\"\n\n            [target.target2.env]\n            volumes = [\"VOL2_ARG_PRECEDENCE\"]\n            passthrough = [\"VAR2_PRECEDENCE\"]\n\n            [target.target3]\n            build-std = true\n            image = \"@sha256:test-image3\"\n\n            [target.target3.env]\n            volumes = [\"VOL3_ARG\"]\n            passthrough = [\"VAR3\"]\n\n            [build]\n            build-std = [\"core\", \"alloc\"]\n            default-target = \"aarch64-unknown-linux-gnu\"\n\n            [build.env]\n            volumes = []\n            passthrough = [\"VAR3\", \"VAR4\"]\n        \"#;\n\n        // Parses configs\n        let (cfg1, _) = CrossToml::parse_from_cross_str(cfg1_str, None, &mut m!())?;\n        let (cfg2, _) = CrossToml::parse_from_cross_str(cfg2_str, None, &mut m!())?;\n        let (cfg_expected, _) = CrossToml::parse_from_cross_str(cfg_expected_str, None, &mut m!())?;\n\n        // Merges config and compares\n        let cfg_merged = cfg1.merge(cfg2)?;\n        assert_eq!(cfg_expected, cfg_merged);\n\n        // need to test individual values. i've broken this down into\n        // tests on values for better error reporting\n        let build = &cfg_expected.build;\n        assert_eq!(\n            build.build_std,\n            Some(BuildStd::Crates(vec![\n                \"core\".to_owned(),\n                \"alloc\".to_owned()\n            ]))\n        );\n        assert_eq!(build.default_target, Some(p!(\"aarch64-unknown-linux-gnu\")));\n        assert_eq!(build.pre_build, None);\n        assert_eq!(build.dockerfile, None);\n        assert_eq!(build.env.passthrough, Some(vec![p!(\"VAR3\"), p!(\"VAR4\")]));\n        assert_eq!(build.env.volumes, Some(vec![]));\n\n        let targets = &cfg_expected.targets;\n        let aarch64 = &targets[&Target::new_built_in(\"aarch64-unknown-linux-gnu\")];\n        assert_eq!(aarch64.build_std, Some(BuildStd::Bool(true)));\n        assert_eq!(aarch64.image, Some(p!(\"test-image1\")));\n        assert_eq!(aarch64.pre_build, None);\n        assert_eq!(aarch64.dockerfile, None);\n        assert_eq!(aarch64.env.passthrough, Some(vec![p!(\"VAR1\")]));\n        assert_eq!(aarch64.env.volumes, Some(vec![p!(\"VOL1_ARG\")]));\n\n        let target2 = &targets[&Target::new_custom(\"target2\")];\n        assert_eq!(target2.build_std, Some(BuildStd::Bool(false)));\n        assert_eq!(target2.image, Some(p!(\"test-image2-precedence\")));\n        assert_eq!(target2.pre_build, None);\n        assert_eq!(target2.dockerfile, None);\n        assert_eq!(target2.env.passthrough, Some(vec![p!(\"VAR2_PRECEDENCE\")]));\n        assert_eq!(target2.env.volumes, Some(vec![p!(\"VOL2_ARG_PRECEDENCE\")]));\n\n        let target3 = &targets[&Target::new_custom(\"target3\")];\n        assert_eq!(target3.build_std, Some(BuildStd::Bool(true)));\n        assert_eq!(target3.image, Some(p!(\"@sha256:test-image3\")));\n        assert_eq!(target3.pre_build, None);\n        assert_eq!(target3.dockerfile, None);\n        assert_eq!(target3.env.passthrough, Some(vec![p!(\"VAR3\")]));\n        assert_eq!(target3.env.volumes, Some(vec![p!(\"VOL3_ARG\")]));\n\n        Ok(())\n    }\n\n    #[test]\n    fn pre_build_script() -> Result<()> {\n        let toml_str = r#\"\n            [target.aarch64-unknown-linux-gnu]\n            pre-build = \"./my-script.sh\"\n\n            [build]\n            pre-build = [\"echo Hello World\"]\n        \"#;\n        let (toml, unused) = CrossToml::parse_from_cross_str(toml_str, None, &mut m!())?;\n        assert!(unused.is_empty());\n        assert!(matches!(\n            toml.pre_build(&Target::new_built_in(\"aarch64-unknown-linux-gnu\")),\n            ConfVal {\n                build: Some(&PreBuild::Lines(_)),\n                target: Some(&PreBuild::Single { .. }),\n            },\n        ));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/docker/build.rs",
    "content": "use std::env;\nuse std::process::Command;\nuse std::str::FromStr;\n\nuse super::engine::Engine;\nuse crate::errors::*;\nuse crate::shell::Verbosity;\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\npub enum Progress {\n    Plain,\n    Auto,\n    Tty,\n}\n\nimpl FromStr for Progress {\n    type Err = eyre::ErrReport;\n\n    fn from_str(progress: &str) -> Result<Self> {\n        Ok(match progress {\n            \"plain\" => Progress::Plain,\n            \"auto\" => Progress::Auto,\n            \"tty\" => Progress::Tty,\n            s => eyre::bail!(\"unexpect progress type: expected plain, auto, or tty and got {s}\"),\n        })\n    }\n}\n\nimpl From<Progress> for &str {\n    fn from(progress: Progress) -> Self {\n        match progress {\n            Progress::Plain => \"plain\",\n            Progress::Auto => \"auto\",\n            Progress::Tty => \"tty\",\n        }\n    }\n}\n\npub trait BuildCommandExt {\n    fn invoke_build_command(&mut self) -> &mut Self;\n    fn progress(&mut self, progress: Option<Progress>) -> Result<&mut Self>;\n    fn verbose(&mut self, verbosity: Verbosity) -> &mut Self;\n    fn disable_scan_suggest(&mut self) -> &mut Self;\n    fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self;\n}\n\nimpl BuildCommandExt for Command {\n    fn invoke_build_command(&mut self) -> &mut Self {\n        match Engine::has_buildkit() {\n            true => self.args([\"buildx\", \"build\"]),\n            false => self.arg(\"build\"),\n        }\n    }\n\n    fn progress(&mut self, progress: Option<Progress>) -> Result<&mut Self> {\n        let progress: Progress = match progress {\n            None => env::var(\"CROSS_BUILD_PROGRESS\")\n                .as_deref()\n                .unwrap_or(\"auto\")\n                .parse()?,\n            Some(progress) => progress,\n        };\n        Ok(self.args([\"--progress\", progress.into()]))\n    }\n\n    fn verbose(&mut self, verbosity: Verbosity) -> &mut Self {\n        match verbosity {\n            Verbosity::Verbose(2..) => self.args([\"--build-arg\", \"VERBOSE=1\"]),\n            _ => self,\n        }\n    }\n\n    fn disable_scan_suggest(&mut self) -> &mut Self {\n        self.env(\"DOCKER_SCAN_SUGGEST\", \"false\")\n    }\n\n    fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self {\n        self.args([\n            \"--label\",\n            &format!(\"{}.for-cross-target={target}\", crate::CROSS_LABEL_DOMAIN,),\n        ]);\n        self.args([\n            \"--label\",\n            &format!(\"{}.runs-with={platform}\", crate::CROSS_LABEL_DOMAIN,),\n        ])\n    }\n}\n\npub trait BuildResultExt {\n    fn engine_warning(self, engine: &Engine) -> Result<()>;\n    fn buildkit_warning(self) -> Result<()>;\n}\n\nimpl BuildResultExt for Result<()> {\n    fn engine_warning(self, engine: &Engine) -> Result<()> {\n        self.with_warning(|| {\n            format!(\n                \"call to {} failed\",\n                engine\n                    .path\n                    .file_name()\n                    .and_then(|s| s.to_str())\n                    .unwrap_or(\"container engine\")\n            )\n        })\n    }\n\n    fn buildkit_warning(mut self) -> Result<()> {\n        if Engine::has_buildkit() {\n            self = self\n                .suggestion(\"is `buildx` available for the container engine?\")\n                .with_note(|| {\n                    format!(\n                        \"disable the `buildkit` dependency optionally with `{}=1`\",\n                        Engine::CROSS_CONTAINER_ENGINE_NO_BUILDKIT_ENV\n                    )\n                });\n        }\n        self\n    }\n}\n"
  },
  {
    "path": "src/docker/custom.rs",
    "content": "use std::io::Write;\nuse std::path::PathBuf;\nuse std::str::FromStr;\n\nuse crate::docker::{self, DockerOptions, DockerPaths};\nuse crate::shell::MessageInfo;\nuse crate::{errors::*, file, CommandExt, ToUtf8};\nuse crate::{CargoMetadata, TargetTriple};\n\nuse super::{\n    create_target_dir, get_image_name, path_hash, BuildCommandExt, BuildResultExt, Engine,\n    ImagePlatform,\n};\n\npub const CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX: &str = \"localhost/cross-rs/cross-custom-\";\n\n#[derive(Debug, PartialEq, Eq)]\npub enum Dockerfile<'a> {\n    File {\n        path: &'a str,\n        context: Option<&'a str>,\n        name: Option<&'a str>,\n        runs_with: &'a ImagePlatform,\n    },\n    Custom {\n        content: String,\n        runs_with: &'a ImagePlatform,\n    },\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]\npub enum PreBuild {\n    /// A path to a file to copy or a single line to `RUN` if line comes from env\n    Single { line: String, env: bool },\n    /// Lines to execute in a single `RUN`\n    Lines(Vec<String>),\n}\n\nimpl serde::Serialize for PreBuild {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        match self {\n            PreBuild::Single { line, .. } => serializer.serialize_str(line),\n            PreBuild::Lines(lines) => {\n                use serde::ser::SerializeSeq;\n                let mut seq = serializer.serialize_seq(Some(lines.len()))?;\n                for line in lines {\n                    seq.serialize_element(line)?;\n                }\n                seq.end()\n            }\n        }\n    }\n}\nimpl FromStr for PreBuild {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(PreBuild::Single {\n            line: s.to_owned(),\n            env: false,\n        })\n    }\n}\n\nimpl From<Vec<String>> for PreBuild {\n    fn from(vec: Vec<String>) -> Self {\n        PreBuild::Lines(vec)\n    }\n}\n\nimpl PreBuild {\n    #[must_use]\n    pub fn is_single(&self) -> bool {\n        matches!(self, Self::Single { .. })\n    }\n\n    #[must_use]\n    pub fn is_lines(&self) -> bool {\n        matches!(self, Self::Lines(..))\n    }\n}\n\nimpl<'a> Dockerfile<'a> {\n    pub fn build(\n        &self,\n        options: &DockerOptions,\n        paths: &DockerPaths,\n        build_args: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,\n        msg_info: &mut MessageInfo,\n    ) -> Result<String> {\n        let uses_zig = options.command_variant.uses_zig();\n        let mut docker_build = options.engine.command();\n        docker_build.invoke_build_command();\n        docker_build.disable_scan_suggest();\n        self.runs_with()\n            .specify_platform(&options.engine, &mut docker_build);\n\n        docker_build.progress(None)?;\n        docker_build.verbose(msg_info.verbosity);\n        docker_build.cross_labels(options.target.triple(), self.runs_with().target.triple());\n\n        docker_build.args([\n            \"--label\",\n            &format!(\n                \"{}.workspace_root={}\",\n                crate::CROSS_LABEL_DOMAIN,\n                paths.workspace_root().to_utf8()?\n            ),\n        ]);\n\n        let image_name = self.image_name(options.target.target(), &paths.metadata)?;\n        docker_build.args([\"--tag\", &image_name]);\n\n        for (key, arg) in build_args {\n            docker_build.args([\"--build-arg\", &format!(\"{}={}\", key.as_ref(), arg.as_ref())]);\n        }\n\n        if let Some(arch) = options.target.target().deb_arch() {\n            docker_build.args([\"--build-arg\", &format!(\"CROSS_DEB_ARCH={arch}\")]);\n        }\n\n        let path = match self {\n            Dockerfile::File { path, .. } => {\n                paths.metadata.workspace_root.join(PathBuf::from(path))\n            }\n            Dockerfile::Custom { content, .. } => {\n                let target_dir = paths\n                    .metadata\n                    .target_directory\n                    .join(options.target.to_string());\n                create_target_dir(&target_dir)?;\n                let path = target_dir.join(format!(\"Dockerfile.{}-custom\", &options.target));\n                {\n                    let mut file = file::write_file(&path, true)?;\n                    file.write_all(content.as_bytes())?;\n                }\n                path\n            }\n        };\n\n        if matches!(self, Dockerfile::File { .. }) {\n            if let Ok(cross_base_image) =\n                self::get_image_name(&options.config, &options.target, uses_zig)\n            {\n                docker_build.args([\n                    \"--build-arg\",\n                    &format!(\"CROSS_BASE_IMAGE={cross_base_image}\"),\n                ]);\n            }\n        }\n\n        docker_build.args([\"--file\".into(), path]);\n\n        if let Some(build_opts) = options.config.build_opts() {\n            docker_build.args(Engine::parse_opts(&build_opts)?);\n        }\n\n        let has_output = options\n            .config\n            .build_opts()\n            .is_some_and(|opts| opts.contains(\"--load\") || opts.contains(\"--output\"));\n        if options.engine.kind.is_docker() && !has_output {\n            docker_build.args([\"--output\", \"type=docker\"]);\n        };\n\n        if let Some(context) = self.context() {\n            docker_build.arg(context);\n        } else {\n            docker_build.arg(paths.host_root());\n        }\n\n        // FIXME: Inspect the error message, while still inheriting stdout on verbose mode to\n        // conditionally apply this suggestion and note. This could then inspect if a help string is emitted,\n        // if the daemon is not running, etc.\n        docker_build\n            .run(msg_info, true)\n            .engine_warning(&options.engine)\n            .buildkit_warning()?;\n        Ok(image_name)\n    }\n\n    pub fn image_name(\n        &self,\n        target_triple: &TargetTriple,\n        metadata: &CargoMetadata,\n    ) -> Result<String> {\n        match self {\n            Dockerfile::File {\n                name: Some(name), ..\n            } => Ok((*name).to_owned()),\n            _ => Ok(format!(\n                \"{}{package_name}:{target_triple}-{path_hash}{custom}\",\n                CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX,\n                package_name = docker_package_name(metadata),\n                path_hash = path_hash(&metadata.workspace_root, docker::PATH_HASH_SHORT)?,\n                custom = if matches!(self, Self::File { .. }) {\n                    \"\"\n                } else {\n                    \"-pre-build\"\n                }\n            )),\n        }\n    }\n\n    fn context(&self) -> Option<&'a str> {\n        match self {\n            Dockerfile::File {\n                context: Some(context),\n                ..\n            } => Some(context),\n            _ => None,\n        }\n    }\n    fn runs_with(&self) -> &ImagePlatform {\n        match self {\n            Dockerfile::File { runs_with, .. } => runs_with,\n            Dockerfile::Custom { runs_with, .. } => runs_with,\n        }\n    }\n}\n\nfn docker_package_name(metadata: &CargoMetadata) -> String {\n    // a valid image name consists of the following:\n    // - lowercase ASCII letters\n    // - digits\n    // - a period\n    // - 1-2 underscores\n    // - 1 or more hyphens (dashes)\n    docker_tag_name(\n        &metadata\n            .workspace_root\n            .file_name()\n            .expect(\"workspace_root can't end in `..`\")\n            .to_string_lossy(),\n    )\n}\n\nfn docker_tag_name(file_name: &str) -> String {\n    // a valid image name consists of the following:\n    // - lowercase ASCII letters\n    // - digits\n    // - a period\n    // - 1-2 underscores\n    // - 1 or more hyphens (dashes)\n    let mut result = String::new();\n    let mut consecutive_underscores = 0;\n    for c in file_name.chars() {\n        match c {\n            'a'..='z' | '0'..='9' | '.' | '-' => {\n                consecutive_underscores = 0;\n                result.push(c);\n            }\n            'A'..='Z' => {\n                consecutive_underscores = 0;\n                result.push(c.to_ascii_lowercase());\n            }\n            '_' => {\n                consecutive_underscores += 1;\n                if consecutive_underscores <= 2 {\n                    result.push(c);\n                }\n            }\n            // ignore any non-ascii characters\n            _ => (),\n        }\n    }\n\n    // in case our result ends in an invalid last char `-` or `.`\n    // we remove\n    // in case our result starts in a `_`, we trim it\n    // as this otherwise clashes with the `-` prefix, leading to\n    // the invalid sequence `-_`\n    result = result\n        .trim_end_matches(['.', '-'])\n        .trim_start_matches(['.', '_'])\n        .to_owned();\n\n    // in case all characters were invalid or we had all non-ASCII\n    // characters followed by a `-` or `.`, we use a non-empty filename\n    if result.is_empty() {\n        result = \"empty\".to_owned();\n    }\n\n    result\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    macro_rules! s {\n        ($s:literal) => {\n            $s.to_owned()\n        };\n    }\n\n    #[test]\n    fn docker_tag_name_test() {\n        assert_eq!(docker_tag_name(\"package\"), s!(\"package\"));\n        assert_eq!(docker_tag_name(\"pAcKaGe\"), s!(\"package\"));\n        assert_eq!(\n            docker_tag_name(\"package_안녕하세요_test\"),\n            s!(\"package__test\")\n        );\n        assert_eq!(\n            docker_tag_name(\"pAcKaGe___test_name\"),\n            s!(\"package__test_name\")\n        );\n        assert_eq!(\n            docker_tag_name(\"pAcKaGe---test.name\"),\n            s!(\"package---test.name\")\n        );\n\n        assert_eq!(docker_tag_name(\"foo-123\"), s!(\"foo-123\"));\n        assert_eq!(docker_tag_name(\"foo-123-\"), s!(\"foo-123\"));\n    }\n}\n"
  },
  {
    "path": "src/docker/engine.rs",
    "content": "use std::env;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\n\nuse crate::config::bool_from_envvar;\nuse crate::extensions::CommandExt;\nuse crate::shell::MessageInfo;\nuse crate::{errors::*, OutputExt};\n\nuse super::{Architecture, ContainerOs};\n\npub const DOCKER: &str = \"docker\";\npub const PODMAN: &str = \"podman\";\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum EngineType {\n    Docker,\n    Podman,\n    PodmanRemote,\n    Nerdctl,\n    Other,\n}\n\nimpl EngineType {\n    /// Returns `true` if the engine type is [`Podman`](Self::Podman) or [`PodmanRemote`](Self::PodmanRemote).\n    #[must_use]\n    pub const fn is_podman(&self) -> bool {\n        matches!(self, Self::Podman | Self::PodmanRemote)\n    }\n\n    /// Returns `true` if the engine type is [`Docker`](EngineType::Docker).\n    #[must_use]\n    pub const fn is_docker(&self) -> bool {\n        matches!(self, Self::Docker)\n    }\n\n    /// Returns `true` if the build command supports the `--output` flag.\n    #[must_use]\n    pub const fn supports_output_flag(&self) -> bool {\n        !matches!(self, Self::Other)\n    }\n\n    /// Returns `true` if the build command supports the `--pull` flag.\n    #[must_use]\n    pub const fn supports_pull_flag(&self) -> bool {\n        !matches!(self, Self::Nerdctl | Self::Other)\n    }\n\n    /// Returns `true` if the build command supports the `--cache-from type=` key.\n    ///\n    /// Some container engines, especially podman, do not support the `type`\n    /// key of `--cache-from` during the image build steps. They also do\n    /// not support any tags for the `--cache-from` steps either. See:\n    /// <https://docs.podman.io/en/latest/markdown/podman-build.1.html#cache-from>\n    #[must_use]\n    pub const fn supports_cache_from_type(&self) -> bool {\n        matches!(self, Self::Docker | Self::Nerdctl)\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct Engine {\n    pub kind: EngineType,\n    pub path: PathBuf,\n    pub in_docker: bool,\n    pub arch: Option<Architecture>,\n    pub os: Option<ContainerOs>,\n    pub is_remote: bool,\n    pub is_rootless: bool,\n}\n\nimpl Engine {\n    pub const CROSS_CONTAINER_ENGINE_NO_BUILDKIT_ENV: &'static str =\n        \"CROSS_CONTAINER_ENGINE_NO_BUILDKIT\";\n    pub fn new(\n        in_docker: Option<bool>,\n        is_remote: Option<bool>,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Engine> {\n        #[allow(clippy::map_err_ignore)]\n        let path = get_container_engine()\n            .map_err(|_| eyre::eyre!(\"no container engine found\"))\n            .with_suggestion(|| \"is docker or podman installed?\")?;\n        Self::from_path(path, in_docker, is_remote, msg_info)\n    }\n\n    pub fn from_path(\n        path: PathBuf,\n        in_docker: Option<bool>,\n        is_remote: Option<bool>,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Engine> {\n        let in_docker = match in_docker {\n            Some(v) => v,\n            None => Self::in_docker(msg_info)?,\n        };\n        let (kind, arch, os) = get_engine_info(&path, msg_info)?;\n        let is_rootless = is_rootless(kind).unwrap_or_else(|| is_docker_rootless(&path, msg_info));\n        let is_remote = is_remote.unwrap_or_else(Self::is_remote);\n        Ok(Engine {\n            path,\n            kind,\n            in_docker,\n            arch,\n            os,\n            is_remote,\n            is_rootless,\n        })\n    }\n\n    #[must_use]\n    pub fn needs_remote(&self) -> bool {\n        self.is_remote && self.kind == EngineType::Podman\n    }\n\n    pub fn in_docker(msg_info: &mut MessageInfo) -> Result<bool> {\n        Ok(\n            if let Ok(value) = env::var(\"CROSS_CONTAINER_IN_CONTAINER\") {\n                if env::var(\"CROSS_DOCKER_IN_DOCKER\").is_ok() {\n                    msg_info.warn(\n                        \"using both `CROSS_CONTAINER_IN_CONTAINER` and `CROSS_DOCKER_IN_DOCKER`.\",\n                    )?;\n                }\n                bool_from_envvar(&value)\n            } else if let Ok(value) = env::var(\"CROSS_DOCKER_IN_DOCKER\") {\n                // FIXME: remove this when we deprecate CROSS_DOCKER_IN_DOCKER.\n                bool_from_envvar(&value)\n            } else {\n                false\n            },\n        )\n    }\n\n    #[must_use]\n    pub fn is_remote() -> bool {\n        env::var(\"CROSS_REMOTE\")\n            .map(|s| bool_from_envvar(&s))\n            .unwrap_or_default()\n    }\n\n    #[must_use]\n    pub fn has_buildkit() -> bool {\n        !env::var(Self::CROSS_CONTAINER_ENGINE_NO_BUILDKIT_ENV)\n            .map(|x| bool_from_envvar(&x))\n            .unwrap_or_default()\n    }\n}\n\nfn is_rootless(kind: EngineType) -> Option<bool> {\n    env::var(\"CROSS_ROOTLESS_CONTAINER_ENGINE\")\n        .ok()\n        .and_then(|s| match s.as_ref() {\n            \"auto\" => None,\n            b => Some(bool_from_envvar(b)),\n        })\n        .or_else(|| (!kind.is_docker()).then_some(true))\n}\n\n#[must_use]\nfn is_docker_rootless(ce: &Path, msg_info: &mut MessageInfo) -> bool {\n    let mut cmd = Command::new(ce);\n    cmd.args([\"info\", \"-f\", \"{{.SecurityOptions}}\"])\n        .run_and_get_output(msg_info)\n        .ok()\n        .and_then(|cmd| cmd.stdout().ok())\n        .map(|out| {\n            out.to_lowercase()\n                .replace(['[', ' ', ']'], \",\")\n                .contains(\",name=rootless,\")\n        })\n        .unwrap_or_default()\n}\n\n#[test]\nfn various_is_rootless_configs() {\n    let var = \"CROSS_ROOTLESS_CONTAINER_ENGINE\";\n    let old = env::var(var);\n    env::remove_var(var);\n\n    assert!(!is_rootless(EngineType::Docker).unwrap_or(false));\n    assert!(is_rootless(EngineType::Docker).unwrap_or(true));\n\n    assert_eq!(is_rootless(EngineType::Docker), None);\n    assert_eq!(is_rootless(EngineType::Podman), Some(true));\n    assert_eq!(is_rootless(EngineType::PodmanRemote), Some(true));\n    assert_eq!(is_rootless(EngineType::Other), Some(true));\n\n    env::set_var(var, \"0\");\n    assert_eq!(is_rootless(EngineType::Docker), Some(false));\n    assert_eq!(is_rootless(EngineType::Podman), Some(false));\n    assert_eq!(is_rootless(EngineType::PodmanRemote), Some(false));\n    assert_eq!(is_rootless(EngineType::Other), Some(false));\n\n    env::set_var(var, \"1\");\n    assert_eq!(is_rootless(EngineType::Docker), Some(true));\n    assert_eq!(is_rootless(EngineType::Podman), Some(true));\n    assert_eq!(is_rootless(EngineType::PodmanRemote), Some(true));\n    assert_eq!(is_rootless(EngineType::Other), Some(true));\n\n    env::set_var(var, \"auto\");\n    assert_eq!(is_rootless(EngineType::Docker), None);\n    assert_eq!(is_rootless(EngineType::Podman), Some(true));\n    assert_eq!(is_rootless(EngineType::PodmanRemote), Some(true));\n    assert_eq!(is_rootless(EngineType::Other), Some(true));\n\n    match old {\n        Ok(v) => env::set_var(var, v),\n        Err(_) => env::remove_var(var),\n    }\n}\n\n// determine if the container engine is docker. this fixes issues with\n// any aliases (#530), and doesn't fail if an executable suffix exists.\nfn get_engine_info(\n    ce: &Path,\n    msg_info: &mut MessageInfo,\n) -> Result<(EngineType, Option<Architecture>, Option<ContainerOs>)> {\n    let stdout_help = Command::new(ce)\n        .arg(\"--help\")\n        .run_and_get_stdout(msg_info)?\n        .to_lowercase();\n\n    let kind = if stdout_help.contains(\"podman-remote\") {\n        EngineType::PodmanRemote\n    } else if stdout_help.contains(\"podman\") {\n        EngineType::Podman\n    } else if stdout_help.contains(\"nerdctl\") {\n        EngineType::Nerdctl\n    } else if stdout_help.contains(\"docker\") && !stdout_help.contains(\"emulate\") {\n        EngineType::Docker\n    } else {\n        EngineType::Other\n    };\n\n    // this can fail: podman can give partial output\n    //   linux,,,Error: template: version:1:15: executing \"version\" at <.Arch>:\n    //   can't evaluate field Arch in type *define.Version\n    let os_arch_server = engine_info(\n        ce,\n        &[\"version\", \"-f\", \"{{ .Server.Os }},,,{{ .Server.Arch }}\"],\n        \",,,\",\n        msg_info,\n    );\n\n    let (os_arch_other, os_arch_server_result) = match os_arch_server {\n        Ok(Some(os_arch)) => (Ok(Some(os_arch)), None),\n        result => {\n            if kind.is_podman() {\n                (get_podman_info(ce, msg_info), result.err())\n            } else {\n                (get_custom_info(ce, msg_info), result.err())\n            }\n        }\n    };\n\n    let os_arch = match (os_arch_other, os_arch_server_result) {\n        (Ok(os_arch), _) => os_arch,\n        (Err(e), Some(server_err)) => return Err(server_err.to_section_report().with_error(|| e)),\n        (Err(e), None) => return Err(e.to_section_report()),\n    };\n\n    let (os, arch) = os_arch.map_or(<_>::default(), |(os, arch)| (Some(os), Some(arch)));\n    Ok((kind, arch, os))\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum EngineInfoError {\n    #[error(transparent)]\n    Eyre(eyre::Report),\n    #[error(\"could not get os and arch\")]\n    CommandError(#[from] CommandError),\n}\n\nimpl EngineInfoError {\n    pub fn to_section_report(self) -> eyre::Report {\n        match self {\n            EngineInfoError::Eyre(e) => e,\n            EngineInfoError::CommandError(e) => {\n                e.to_section_report().wrap_err(\"could not get os and arch\")\n            }\n        }\n    }\n}\n\n/// Get engine info\nfn engine_info(\n    ce: &Path,\n    args: &[&str],\n    sep: &str,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {\n    let mut cmd = Command::new(ce);\n    cmd.args(args);\n    let out = cmd\n        .run_and_get_output(msg_info)\n        .map_err(EngineInfoError::Eyre)?;\n\n    cmd.status_result(msg_info, out.status, Some(&out))?;\n\n    out.stdout()?\n        .to_lowercase()\n        .trim()\n        .split_once(sep)\n        .map(|(os, arch)| -> Result<_> { Ok((ContainerOs::new(os)?, Architecture::new(arch)?)) })\n        .transpose()\n        .map_err(EngineInfoError::Eyre)\n}\n\nfn get_podman_info(\n    ce: &Path,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {\n    engine_info(ce, &[\"info\", \"-f\", \"{{ .Version.OsArch }}\"], \"/\", msg_info)\n}\n\nfn get_custom_info(\n    ce: &Path,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {\n    engine_info(\n        ce,\n        &[\"version\", \"-f\", \"{{ .Client.Os }},,,{{ .Client.Arch }}\"],\n        \",,,\",\n        msg_info,\n    )\n}\n\npub fn get_container_engine() -> Result<PathBuf, which::Error> {\n    if let Ok(ce) = env::var(\"CROSS_CONTAINER_ENGINE\") {\n        which::which(ce)\n    } else {\n        which::which(DOCKER).or_else(|_| which::which(PODMAN))\n    }\n}\n"
  },
  {
    "path": "src/docker/image.rs",
    "content": "use std::str::FromStr;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::{\n    docker::{CROSS_IMAGE, DEFAULT_IMAGE_VERSION},\n    errors::*,\n    shell::MessageInfo,\n    TargetTriple,\n};\n\nuse super::Engine;\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]\npub struct Image {\n    pub name: String,\n    // The toolchain triple the image is built for\n    pub platform: ImagePlatform,\n}\n\nimpl std::fmt::Display for Image {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(&self.name)\n    }\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub struct PossibleImage {\n    #[serde(rename = \"name\")]\n    pub reference: ImageReference,\n    // The toolchain triple the image is built for\n    pub toolchain: Vec<ImagePlatform>,\n}\n\nimpl PossibleImage {\n    pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Result<Image> {\n        let ImageReference::Name(name) = self.reference.clone() else {\n            eyre::bail!(\"cannot make definite Image from unqualified PossibleImage\");\n        };\n\n        if self.toolchain.is_empty() {\n            Ok(Image {\n                name,\n                platform: ImagePlatform::DEFAULT,\n            })\n        } else {\n            let platform = if self.toolchain.len() == 1 {\n                self.toolchain.first().expect(\"should contain at least one\")\n            } else {\n                let same_arch = self\n                    .toolchain\n                    .iter()\n                    .filter(|platform| {\n                        &platform.architecture\n                            == engine.arch.as_ref().unwrap_or(&Architecture::Amd64)\n                    })\n                    .collect::<Vec<_>>();\n\n                if same_arch.len() == 1 {\n                    // pick the platform with the same architecture\n                    same_arch.first().expect(\"should contain one element\")\n                } else if let Some(platform) = same_arch\n                    .iter()\n                    .find(|platform| &platform.os == engine.os.as_ref().unwrap_or(&Os::Linux))\n                {\n                    *platform\n                } else if let Some(platform) =\n                    same_arch.iter().find(|platform| platform.os == Os::Linux)\n                {\n                    // container engine should be fine with linux\n                    platform\n                } else {\n                    let platform = self\n                        .toolchain\n                        .first()\n                        .expect(\"should be at least one platform\");\n                    // FIXME: Don't throw away\n                    msg_info.warn(\n                        format_args!(\"could not determine what toolchain to use for image, defaulting to `{}`\", platform.target),\n                    ).ok();\n                    platform\n                }\n            };\n            Ok(Image {\n                platform: platform.clone(),\n                name,\n            })\n        }\n    }\n}\n\nimpl<T: AsRef<str>> From<T> for PossibleImage {\n    fn from(s: T) -> Self {\n        PossibleImage {\n            reference: s.as_ref().to_owned().into(),\n            toolchain: vec![],\n        }\n    }\n}\n\nimpl FromStr for PossibleImage {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(s.into())\n    }\n}\n\nimpl std::fmt::Display for PossibleImage {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(self.reference.get())\n    }\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\n#[serde(from = \"String\", untagged)]\npub enum ImageReference {\n    /// Partially qualified reference, with or without tag/digest\n    Name(String),\n    /// Unqualified reference, only a tag or digest\n    Identifier(String),\n    /// Unqualified reference, only a subtarget\n    Subtarget(String),\n}\n\nimpl ImageReference {\n    pub fn get(&self) -> &str {\n        match self {\n            Self::Name(s) => s,\n            Self::Identifier(s) => s,\n            Self::Subtarget(s) => s,\n        }\n    }\n\n    pub fn ensure_qualified(&mut self, target_name: &str) {\n        let image_name = match self {\n            Self::Name(_) => return,\n            Self::Identifier(id) => {\n                format!(\"{CROSS_IMAGE}/{target_name}{id}\")\n            }\n            Self::Subtarget(sub) => {\n                format!(\"{CROSS_IMAGE}/{target_name}:{DEFAULT_IMAGE_VERSION}{sub}\")\n            }\n        };\n\n        *self = Self::Name(image_name);\n    }\n}\n\nimpl From<String> for ImageReference {\n    fn from(s: String) -> Self {\n        if s.starts_with('-') {\n            Self::Subtarget(s)\n        } else if s.starts_with(':') || s.starts_with('@') {\n            Self::Identifier(s)\n        } else {\n            Self::Name(s)\n        }\n    }\n}\n\n/// The architecture/platform to use in the image\n///\n/// <https://github.com/containerd/containerd/blob/release/1.6/platforms/platforms.go#L63>\n#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]\n#[serde(try_from = \"String\")]\npub struct ImagePlatform {\n    /// CPU architecture, x86_64, aarch64 etc\n    pub architecture: Architecture,\n    /// The OS, i.e linux, windows, darwin\n    pub os: Os,\n    /// The platform variant, i.e v8, v7, v6 etc\n    pub variant: Option<String>,\n    pub target: TargetTriple,\n}\n\nimpl ImagePlatform {\n    pub const DEFAULT: Self = ImagePlatform::from_const_target(TargetTriple::DEFAULT);\n    pub const X86_64_UNKNOWN_LINUX_GNU: Self =\n        ImagePlatform::from_const_target(TargetTriple::X86_64UnknownLinuxGnu);\n    pub const AARCH64_UNKNOWN_LINUX_GNU: Self =\n        ImagePlatform::from_const_target(TargetTriple::Aarch64UnknownLinuxGnu);\n\n    /// Get a representative version of this platform specifier for usage in `--platform`\n    ///\n    /// Prefer using [`ImagePlatform::specify_platform`] which will supply the flag if needed\n    pub fn docker_platform(&self) -> String {\n        if let Some(variant) = &self.variant {\n            format!(\"{}/{}/{variant}\", self.os, self.architecture)\n        } else {\n            format!(\"{}/{}\", self.os, self.architecture)\n        }\n    }\n\n    /// Returns a string that can be used in codegen to represent this platform\n    pub fn to_codegen_string(&self) -> Option<&'static str> {\n        match self.target {\n            TargetTriple::X86_64UnknownLinuxGnu => Some(\"ImagePlatform::X86_64_UNKNOWN_LINUX_GNU\"),\n            TargetTriple::Aarch64UnknownLinuxGnu => {\n                Some(\"ImagePlatform::AARCH64_UNKNOWN_LINUX_GNU\")\n            }\n            _ => None,\n        }\n    }\n}\n\nimpl Default for ImagePlatform {\n    fn default() -> ImagePlatform {\n        ImagePlatform::DEFAULT\n    }\n}\n\nimpl TryFrom<String> for ImagePlatform {\n    type Error = <Self as std::str::FromStr>::Err;\n\n    fn try_from(value: String) -> Result<Self, Self::Error> {\n        value.parse()\n    }\n}\n\nimpl Serialize for ImagePlatform {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serializer.serialize_str(&format!(\"{}={}\", self.docker_platform(), self.target))\n    }\n}\n\nimpl std::str::FromStr for ImagePlatform {\n    type Err = eyre::Report;\n    // [os/arch[/variant]=]toolchain\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        use serde::de::{\n            value::{Error as SerdeError, StrDeserializer},\n            IntoDeserializer,\n        };\n\n        // Try to match the docker platform string first\n        match s {\n            \"linux/amd64\" => return Ok(Self::X86_64_UNKNOWN_LINUX_GNU),\n            \"linux/arm64\" | \"linux/arm64/v8\" => return Ok(Self::AARCH64_UNKNOWN_LINUX_GNU),\n            _ => {}\n        };\n\n        if let Some((platform, toolchain)) = s.split_once('=') {\n            let image_toolchain = toolchain.into();\n            let (os, arch, variant) = if let Some((os, rest)) = platform.split_once('/') {\n                let os: StrDeserializer<'_, SerdeError> = os.into_deserializer();\n                let (arch, variant) = if let Some((arch, variant)) = rest.split_once('/') {\n                    let arch: StrDeserializer<'_, SerdeError> = arch.into_deserializer();\n                    (arch, Some(variant))\n                } else {\n                    let arch: StrDeserializer<'_, SerdeError> = rest.into_deserializer();\n                    (arch, None)\n                };\n                (os, arch, variant)\n            } else {\n                eyre::bail!(\"invalid platform specified\")\n            };\n            Ok(ImagePlatform {\n                architecture: Architecture::deserialize(arch)?,\n                os: Os::deserialize(os)?,\n                variant: variant.map(ToOwned::to_owned),\n                target: image_toolchain,\n            })\n        } else {\n            Ok(ImagePlatform::from_target(s.into())\n                .wrap_err_with(|| format!(\"could not map `{s}` to a platform\"))?)\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum Architecture {\n    I386,\n    #[serde(alias = \"x86_64\")]\n    Amd64,\n    #[serde(alias = \"armv7\")]\n    Arm,\n    #[serde(alias = \"aarch64\")]\n    Arm64,\n    Mips,\n    Mips64,\n    Mips64Le,\n    MipsLe,\n    #[serde(alias = \"powerpc64\")]\n    Ppc64,\n    Ppc64Le,\n    #[serde(alias = \"riscv64gc\")]\n    Riscv64,\n    S390x,\n    Wasm,\n    #[serde(alias = \"loongarch64\")]\n    LoongArch64,\n}\n\nimpl Architecture {\n    pub fn from_target(target: &TargetTriple) -> Result<Self> {\n        let arch = target\n            .triple()\n            .split_once('-')\n            .ok_or_else(|| eyre::eyre!(\"malformed target\"))?\n            .0;\n        Self::new(arch)\n    }\n\n    pub fn new(s: &str) -> Result<Self> {\n        use serde::de::IntoDeserializer;\n\n        Self::deserialize(<&str as IntoDeserializer>::into_deserializer(s))\n            .wrap_err_with(|| format!(\"architecture {s} is not supported\"))\n    }\n}\n\nimpl std::fmt::Display for Architecture {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        self.serialize(f)\n    }\n}\n\n// Supported Oses are on\n// https://rust-lang.github.io/rustup-components-history/aarch64-unknown-linux-gnu.html\n// where rust, rustc and cargo is available (e.g rustup toolchain add works)\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum Os {\n    Android,\n    #[serde(alias = \"macos\")]\n    Darwin,\n    Freebsd,\n    Illumos,\n    Linux,\n    Netbsd,\n    Solaris,\n    Windows,\n    // Aix\n    // Dragonfly\n    // Ios\n    // Js\n    // Openbsd\n    // Plan9\n}\n\nimpl Os {\n    pub fn from_target(target: &TargetTriple) -> Result<Self> {\n        let mut iter = target.triple().rsplit('-');\n        Ok(\n            match (\n                iter.next().ok_or_else(|| eyre::eyre!(\"malformed target\"))?,\n                iter.next().ok_or_else(|| eyre::eyre!(\"malformed target\"))?,\n            ) {\n                (\"darwin\", _) => Os::Darwin,\n                (\"freebsd\", _) => Os::Freebsd,\n                (\"netbsd\", _) => Os::Netbsd,\n                (\"illumos\", _) => Os::Illumos,\n                (\"solaris\", _) => Os::Solaris,\n                // android targets also set linux, so must occur first\n                (\"android\", _) => Os::Android,\n                (_, \"linux\") => Os::Linux,\n                (_, \"windows\") => Os::Windows,\n                (abi, system) => {\n                    eyre::bail!(\"unsupported os in target, abi: {abi:?}, system: {system:?} \")\n                }\n            },\n        )\n    }\n\n    pub fn new(s: &str) -> Result<Self> {\n        use serde::de::IntoDeserializer;\n\n        Self::deserialize(<&str as IntoDeserializer>::into_deserializer(s))\n            .wrap_err_with(|| format!(\"architecture {s} is not supported\"))\n    }\n}\n\nimpl std::fmt::Display for Os {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        self.serialize(f)\n    }\n}\n\nimpl ImagePlatform {\n    pub fn from_target(target: TargetTriple) -> Result<Self> {\n        match target {\n            target @ TargetTriple::Other(_) => {\n                let os = Os::from_target(&target)\n                    .wrap_err(\"could not determine os in target triplet\")?;\n                let architecture = Architecture::from_target(&target)\n                    .wrap_err(\"could not determine architecture in target triplet\")?;\n                let variant = match target.triple() {\n                    // v7 is default for arm architecture, we still specify it for clarity\n                    armv7 if armv7.starts_with(\"armv7-\") => Some(\"v7\".to_owned()),\n                    arm if arm.starts_with(\"arm-\") => Some(\"v6\".to_owned()),\n                    _ => None,\n                };\n                Ok(ImagePlatform {\n                    architecture,\n                    os,\n                    variant,\n                    target,\n                })\n            }\n            target => Ok(Self::from_const_target(target)),\n        }\n    }\n    #[track_caller]\n    pub const fn from_const_target(target: TargetTriple) -> Self {\n        match target {\n            TargetTriple::Other(_) => {\n                unimplemented!()\n            }\n            TargetTriple::X86_64AppleDarwin => ImagePlatform {\n                architecture: Architecture::Amd64,\n                os: Os::Darwin,\n                variant: None,\n                target,\n            },\n            TargetTriple::Aarch64AppleDarwin => ImagePlatform {\n                architecture: Architecture::Arm64,\n                os: Os::Linux,\n                variant: None,\n                target,\n            },\n            TargetTriple::X86_64UnknownLinuxGnu => ImagePlatform {\n                architecture: Architecture::Amd64,\n                os: Os::Linux,\n                variant: None,\n                target,\n            },\n            TargetTriple::Aarch64UnknownLinuxGnu => ImagePlatform {\n                architecture: Architecture::Arm64,\n                os: Os::Linux,\n                variant: None,\n                target,\n            },\n            TargetTriple::X86_64UnknownLinuxMusl => ImagePlatform {\n                architecture: Architecture::Amd64,\n                os: Os::Linux,\n                variant: None,\n                target,\n            },\n            TargetTriple::Aarch64UnknownLinuxMusl => ImagePlatform {\n                architecture: Architecture::Arm64,\n                os: Os::Linux,\n                variant: None,\n                target,\n            },\n            TargetTriple::X86_64PcWindowsMsvc => ImagePlatform {\n                architecture: Architecture::Amd64,\n                os: Os::Windows,\n                variant: None,\n                target,\n            },\n        }\n    }\n\n    pub fn specify_platform(&self, engine: &Engine, cmd: &mut std::process::Command) {\n        if self.variant.is_none()\n            && Some(&self.architecture) == engine.arch.as_ref()\n            && Some(&self.os) == engine.os.as_ref()\n        {\n        } else {\n            cmd.args([\"--platform\", &self.docker_platform()]);\n        }\n    }\n}\n\n#[cfg(test)]\npub mod tests {\n    use super::*;\n\n    macro_rules! t {\n        ($t:literal) => {\n            TargetTriple::from($t)\n        };\n    }\n\n    macro_rules! arch {\n        ($t:literal) => {\n            Architecture::from_target(&TargetTriple::from($t))\n        };\n    }\n\n    #[test]\n    fn architecture_from_target() -> Result<()> {\n        assert_eq!(arch!(\"x86_64-apple-darwin\")?, Architecture::Amd64);\n        assert_eq!(arch!(\"arm-unknown-linux-gnueabihf\")?, Architecture::Arm);\n        assert_eq!(arch!(\"armv7-unknown-linux-gnueabihf\")?, Architecture::Arm);\n        assert_eq!(arch!(\"aarch64-unknown-linux-gnu\")?, Architecture::Arm64);\n        assert_eq!(arch!(\"aarch64-unknown-freebsd\")?, Architecture::Arm64);\n        assert_eq!(\n            arch!(\"loongarch64-unknown-linux-gnu\")?,\n            Architecture::LoongArch64\n        );\n        assert_eq!(arch!(\"mips-unknown-linux-gnu\")?, Architecture::Mips);\n        assert_eq!(\n            arch!(\"mips64-unknown-linux-gnuabi64\")?,\n            Architecture::Mips64\n        );\n        assert_eq!(\n            arch!(\"mips64le-unknown-linux-gnuabi64\")?,\n            Architecture::Mips64Le\n        );\n        assert_eq!(arch!(\"mipsle-unknown-linux-gnu\")?, Architecture::MipsLe);\n        Ok(())\n    }\n\n    #[test]\n    fn os_from_target() -> Result<()> {\n        assert_eq!(Os::from_target(&t!(\"x86_64-apple-darwin\"))?, Os::Darwin);\n        assert_eq!(Os::from_target(&t!(\"x86_64-unknown-freebsd\"))?, Os::Freebsd);\n        assert_eq!(\n            Os::from_target(&t!(\"aarch64-unknown-freebsd\"))?,\n            Os::Freebsd\n        );\n        assert_eq!(\n            Os::from_target(&t!(\"loongarch64-unknown-linux-gnu\"))?,\n            Os::Linux\n        );\n        assert_eq!(Os::from_target(&t!(\"x86_64-unknown-netbsd\"))?, Os::Netbsd);\n        assert_eq!(Os::from_target(&t!(\"sparcv9-sun-solaris\"))?, Os::Solaris);\n        assert_eq!(Os::from_target(&t!(\"sparcv9-sun-illumos\"))?, Os::Illumos);\n        assert_eq!(Os::from_target(&t!(\"aarch64-linux-android\"))?, Os::Android);\n        assert_eq!(Os::from_target(&t!(\"x86_64-unknown-linux-gnu\"))?, Os::Linux);\n        assert_eq!(Os::from_target(&t!(\"x86_64-pc-windows-msvc\"))?, Os::Windows);\n        Ok(())\n    }\n\n    #[test]\n    fn image_platform_from_docker_platform_str() -> Result<()> {\n        assert_eq!(\n            \"linux/amd64\".parse::<ImagePlatform>()?,\n            ImagePlatform::X86_64_UNKNOWN_LINUX_GNU\n        );\n\n        assert_eq!(\n            \"linux/arm64\".parse::<ImagePlatform>()?,\n            ImagePlatform::AARCH64_UNKNOWN_LINUX_GNU\n        );\n\n        assert_eq!(\n            \"linux/arm64/v8\".parse::<ImagePlatform>()?,\n            ImagePlatform::AARCH64_UNKNOWN_LINUX_GNU\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/docker/local.rs",
    "content": "use std::io;\nuse std::path::Path;\nuse std::process::{Command, ExitStatus};\nuse std::sync::atomic::Ordering;\n\nuse super::shared::*;\nuse crate::errors::Result;\nuse crate::extensions::CommandExt;\nuse crate::file::{PathExt, ToUtf8};\nuse crate::shell::MessageInfo;\nuse eyre::Context;\nuse is_terminal::IsTerminal;\n\n// NOTE: host path must be absolute\nfn mount(\n    docker: &mut Command,\n    host_path: &Path,\n    absolute_path: &Path,\n    prefix: &str,\n    selinux: &str,\n) -> Result<()> {\n    let mount_path = absolute_path.as_posix_absolute()?;\n    docker.args([\n        \"-v\",\n        &format!(\"{}:{prefix}{}{selinux}\", host_path.to_utf8()?, mount_path),\n    ]);\n    Ok(())\n}\n\npub(crate) fn run(\n    options: DockerOptions,\n    paths: DockerPaths,\n    args: &[String],\n    msg_info: &mut MessageInfo,\n) -> Result<Option<ExitStatus>> {\n    let engine = &options.engine;\n    let toolchain_dirs = paths.directories.toolchain_directories();\n    let package_dirs = paths.directories.package_directories();\n\n    let mut cmd = options.command_variant.safe_command();\n    cmd.args(args);\n\n    let mut docker = engine.subcommand(\"run\");\n    docker.add_userns();\n\n    // Podman on macOS doesn't support selinux labels, see issue #756\n    #[cfg(target_os = \"macos\")]\n    let (selinux, selinux_ro) = if engine.kind.is_podman() {\n        (\"\", \":ro\")\n    } else {\n        (\":z\", \":z,ro\")\n    };\n    #[cfg(not(target_os = \"macos\"))]\n    let (selinux, selinux_ro) = (\":z\", \":z,ro\");\n\n    options\n        .image\n        .platform\n        .specify_platform(&options.engine, &mut docker);\n    docker.add_envvars(&options, toolchain_dirs, msg_info)?;\n\n    docker.add_mounts(\n        &options,\n        &paths,\n        |docker, host, absolute| mount(docker, host, absolute, \"\", selinux),\n        |_| {},\n        msg_info,\n    )?;\n\n    let container_id = toolchain_dirs.unique_container_identifier(options.target.target())?;\n    docker.args([\"--name\", &container_id]);\n    docker.arg(\"--rm\");\n\n    docker\n        .add_seccomp(engine.kind, &options.target, &paths.metadata)\n        .wrap_err(\"when copying seccomp profile\")?;\n    docker.add_user_id(engine.is_rootless);\n\n    docker\n        .args([\n            \"-v\",\n            &format!(\n                \"{}:{}{selinux}\",\n                toolchain_dirs.cargo_host_path()?,\n                toolchain_dirs.cargo_mount_path()\n            ),\n        ])\n        // Prevent `bin` from being mounted inside the Docker container.\n        .args([\"-v\", &format!(\"{}/bin\", toolchain_dirs.cargo_mount_path())]);\n\n    let host_root = paths.mount_finder.find_mount_path(package_dirs.host_root());\n    docker.args([\n        \"-v\",\n        &format!(\n            \"{}:{}{selinux}\",\n            host_root.to_utf8()?,\n            package_dirs.mount_root()\n        ),\n    ]);\n\n    let sysroot = paths\n        .mount_finder\n        .find_mount_path(toolchain_dirs.get_sysroot());\n    docker\n        .args([\n            \"-v\",\n            &format!(\n                \"{}:{}{selinux_ro}\",\n                sysroot.to_utf8()?,\n                toolchain_dirs.sysroot_mount_path()\n            ),\n        ])\n        .args([\n            \"-v\",\n            &format!(\"{}:/target{selinux}\", package_dirs.target().to_utf8()?),\n        ]);\n    docker.add_cwd(&paths)?;\n\n    // When running inside NixOS or using Nix packaging we need to add the Nix\n    // Store to the running container so it can load the needed binaries.\n    if let Some(nix_store) = toolchain_dirs.nix_store() {\n        docker.args([\n            \"-v\",\n            &format!(\n                \"{}:{}{selinux}\",\n                nix_store.to_utf8()?,\n                nix_store.as_posix_absolute()?\n            ),\n        ]);\n    }\n\n    if io::stdin().is_terminal() && io::stdout().is_terminal() && io::stderr().is_terminal() {\n        docker.arg(\"-t\");\n    }\n\n    if options.interactive {\n        docker.arg(\"-i\");\n    }\n\n    let mut image_name = options.image.name.clone();\n    if options.needs_custom_image() {\n        image_name = options\n            .custom_image_build(&paths, msg_info)\n            .wrap_err(\"when building custom image\")?;\n    }\n\n    ChildContainer::create(engine.clone(), container_id)?;\n    if msg_info.should_fail() {\n        return Ok(None);\n    }\n    let status = docker\n        .arg(&image_name)\n        .add_build_command(toolchain_dirs, &cmd)\n        .run_and_get_status(msg_info, false);\n\n    // `cargo` generally returns 0 or 101 on completion, but isn't guaranteed\n    // to. `ExitStatus::code()` may be None if a signal caused the process to\n    // terminate or it may be a known interrupt return status (130, 137, 143).\n    // simpler: just test if the program termination handler was called.\n    // SAFETY: an atomic load.\n    let is_terminated = crate::errors::TERMINATED.load(Ordering::SeqCst);\n    if !is_terminated {\n        ChildContainer::exit_static();\n    }\n\n    status.map(Some)\n}\n"
  },
  {
    "path": "src/docker/mod.rs",
    "content": "mod build;\npub(crate) mod custom;\nmod engine;\nmod image;\nmod local;\nmod provided_images;\npub mod remote;\nmod shared;\n\npub use self::build::{BuildCommandExt, BuildResultExt, Progress};\npub use self::engine::*;\npub use self::provided_images::PROVIDED_IMAGES;\npub use self::shared::*;\n\npub use image::{\n    Architecture, Image, ImagePlatform, ImageReference, Os as ContainerOs, PossibleImage,\n};\n\nuse std::process::ExitStatus;\n\nuse crate::errors::*;\nuse crate::shell::MessageInfo;\n\n#[derive(Debug)]\npub struct ProvidedImage {\n    /// The `name` of the image, usually the target triplet\n    pub name: &'static str,\n    pub platforms: &'static [ImagePlatform],\n    pub sub: Option<&'static str>,\n}\n\nimpl ProvidedImage {\n    pub fn image_name(&self, repository: &str, tag: &str) -> String {\n        image_name(self.name, self.sub, repository, tag)\n    }\n\n    pub fn default_image_name(&self) -> String {\n        self.image_name(CROSS_IMAGE, DEFAULT_IMAGE_VERSION)\n    }\n}\n\npub fn image_name(target: &str, sub: Option<&str>, repository: &str, tag: &str) -> String {\n    if let Some(sub) = sub {\n        format!(\"{repository}/{target}:{tag}-{sub}\")\n    } else {\n        format!(\"{repository}/{target}:{tag}\")\n    }\n}\n\n// TODO: The Option here in the result should be removed and Result::Error replaced with a enum to properly signal error\n\n// Ok(None) means that the command failed, due to a warning or error, when `msg_info.should_fail() == true`\npub fn run(\n    options: DockerOptions,\n    paths: DockerPaths,\n    args: &[String],\n    subcommand: Option<crate::Subcommand>,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<ExitStatus>> {\n    if cfg!(target_os = \"windows\") && options.in_docker() {\n        msg_info.fatal(\n            \"running cross insider a container running windows is currently unsupported\",\n            1,\n        );\n    }\n    if options.is_remote() {\n        remote::run(options, paths, args, subcommand, msg_info)\n            .wrap_err(\"could not complete remote run\")\n    } else {\n        local::run(options, paths, args, msg_info)\n    }\n}\n"
  },
  {
    "path": "src/docker/provided_images.rs",
    "content": "#![doc = \"*** AUTO-GENERATED, do not touch. Run `cargo xtask codegen` to update ***\"]\nuse super::{ImagePlatform, ProvidedImage};\n\n#[rustfmt::skip]\npub static PROVIDED_IMAGES: &[ProvidedImage] = &[\n        ProvidedImage {\n            name: \"x86_64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: Some(\"centos\")\n        },\n        ProvidedImage {\n            name: \"aarch64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"aarch64_be-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"arm-unknown-linux-gnueabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"arm-unknown-linux-gnueabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv7-unknown-linux-gnueabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv7-unknown-linux-gnueabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv7neon-unknown-linux-gnueabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i586-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i686-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"loongarch64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"loongarch64-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"mips-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"mipsel-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"mips64-unknown-linux-gnuabi64\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"mips64el-unknown-linux-gnuabi64\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"powerpc-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"powerpc64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"powerpc64le-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"riscv64gc-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"riscv64gc-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"s390x-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"sparc64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"aarch64-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"arm-unknown-linux-musleabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"arm-unknown-linux-musleabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv5te-unknown-linux-gnueabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv5te-unknown-linux-musleabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv7-unknown-linux-musleabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv7-unknown-linux-musleabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i586-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i686-unknown-linux-musl\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"aarch64-linux-android\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"arm-linux-androideabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"armv7-linux-androideabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv7neon-linux-androideabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i686-linux-android\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-linux-android\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-pc-windows-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i686-pc-windows-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"wasm32-unknown-emscripten\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-dragonfly\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"i686-unknown-freebsd\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-freebsd\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"aarch64-unknown-freebsd\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-netbsd\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"sparcv9-sun-solaris\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-pc-solaris\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"x86_64-unknown-illumos\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv6m-none-eabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv7em-none-eabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv7em-none-eabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv7m-none-eabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv8m.base-none-eabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv8m.main-none-eabi\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"thumbv8m.main-none-eabihf\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"zig\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: None\n        },\n        ProvidedImage {\n            name: \"aarch64-unknown-linux-gnu\",\n            platforms: &[ImagePlatform::X86_64_UNKNOWN_LINUX_GNU],\n            sub: Some(\"centos\")\n        },\n];\n"
  },
  {
    "path": "src/docker/remote.rs",
    "content": "use std::collections::BTreeMap;\nuse std::io::{self, BufRead, Read, Write};\nuse std::path::Path;\nuse std::process::{Command, ExitStatus};\nuse std::{env, fs, time};\n\nuse eyre::Context;\nuse is_terminal::IsTerminal;\n\nuse super::engine::Engine;\nuse super::shared::*;\nuse crate::config::bool_from_envvar;\nuse crate::errors::Result;\nuse crate::extensions::CommandExt;\nuse crate::file::{self, PathExt, ToUtf8};\nuse crate::rustc::{self, QualifiedToolchain, VersionMetaExt};\nuse crate::shell::MessageInfo;\nuse crate::temp;\nuse crate::TargetTriple;\n\n// prevent further commands from running if we handled\n// a signal earlier, and the volume is exited.\n// this isn't required, but avoids unnecessary\n// commands while the container is cleaning up.\nmacro_rules! bail_container_exited {\n    () => {{\n        if !ChildContainer::exists_static() {\n            eyre::bail!(\"container already exited due to signal\");\n        }\n    }};\n}\n\n#[track_caller]\nfn subcommand_or_exit(engine: &Engine, cmd: &str) -> Result<Command> {\n    bail_container_exited!();\n    Ok(engine.subcommand(cmd))\n}\n\npub fn posix_parent(path: &str) -> Option<&str> {\n    Path::new(path).parent()?.to_str()\n}\n\nimpl<'a, 'b, 'c> ContainerDataVolume<'a, 'b, 'c> {\n    // NOTE: `reldir` should be a relative POSIX path to the root directory\n    // on windows, this should be something like `mnt/c`. that is, all paths\n    // inside the container should not have the mount prefix.\n    #[track_caller]\n    fn create_dir(\n        &self,\n        reldir: &str,\n        mount_prefix: &str,\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        // make our parent directory if needed\n        subcommand_or_exit(self.engine, \"exec\")?\n            .arg(self.container)\n            .args([\"sh\", \"-c\", &format!(\"mkdir -p '{mount_prefix}/{reldir}'\")])\n            .run_and_get_status(msg_info, false)\n    }\n\n    /// Copy files for a docker volume\n    ///\n    /// `reldst` has the same caveats as `reldir` in [`Self::create_dir`].\n    ///\n    /// ## Note\n    ///\n    /// if copying from a src directory to dst directory with docker, to\n    /// copy the contents from `src` into `dst`, `src` must end with `/.`\n    #[track_caller]\n    fn copy_files(\n        &self,\n        src: &Path,\n        reldst: &str,\n        mount_prefix: &str,\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        if let Some((_, rel)) = reldst.rsplit_once('/') {\n            if msg_info.cross_debug\n                && src.is_dir()\n                && !src.to_string_lossy().ends_with(\"/.\")\n                && rel\n                    == src\n                        .file_name()\n                        .expect(\"filename should be defined as we are a directory\")\n            {\n                msg_info.warn(format_args!(\n                    \"source is pointing to a directory instead of its contents: {} -> {}\\nThis might be a bug. {}\",\n                    src.as_posix_relative()?,\n                    reldst,\n                    std::panic::Location::caller()\n                ))?;\n            }\n        }\n        subcommand_or_exit(self.engine, \"cp\")?\n            .arg(\"-a\")\n            .arg(src.to_utf8()?)\n            .arg(format!(\"{}:{mount_prefix}/{reldst}\", self.container))\n            .run_and_get_status(msg_info, false)\n    }\n\n    /// copy files for a docker volume, does not include cache directories\n    ///\n    /// ## Note\n    ///\n    /// if copying from a src directory to dst directory with docker, to\n    /// copy the contents from `src` into `dst`, `src` must end with `/.`\n    #[track_caller]\n    fn copy_files_nocache(\n        &self,\n        src: &Path,\n        reldst: &str,\n        mount_prefix: &str,\n        copy_symlinks: bool,\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        // avoid any cached directories when copying\n        // see https://bford.info/cachedir/\n        // SAFETY: safe, single-threaded execution.\n        let tempdir = unsafe { temp::TempDir::new()? };\n        let temppath = tempdir.path();\n        let had_symlinks = copy_dir(src, temppath, copy_symlinks, 0, |e, _| is_cachedir(e))?;\n        warn_symlinks(had_symlinks, msg_info)?;\n        self.copy_files(&temppath.join(\".\"), reldst, mount_prefix, msg_info)\n    }\n\n    // copy files for a docker volume, for remote host support\n    // provides a list of files relative to src.\n    #[track_caller]\n    fn copy_file_list(\n        &self,\n        src: &Path,\n        reldst: &str,\n        mount_prefix: &str,\n        files: &[&str],\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        // SAFETY: safe, single-threaded execution.\n        let tempdir = unsafe { temp::TempDir::new()? };\n        let temppath = tempdir.path();\n        for file in files {\n            let src_path = src.join(file);\n            let dst_path = temppath.join(file);\n            file::create_dir_all(dst_path.parent().expect(\"must have parent\"))?;\n            fs::copy(src_path, &dst_path)?;\n        }\n\n        self.copy_files(&temppath.join(\".\"), reldst, mount_prefix, msg_info)\n    }\n\n    // removed files from a docker volume, for remote host support\n    // provides a list of files relative to src.\n    #[track_caller]\n    fn remove_file_list(\n        &self,\n        reldst: &str,\n        mount_prefix: &str,\n        files: &[&str],\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        const PATH: &str = \"/tmp/remove_list\";\n        let mut script = vec![];\n        if msg_info.is_verbose() {\n            script.push(\"set -x\".to_owned());\n        }\n        script.push(format!(\n            \"cat \\\"{PATH}\\\" | while read line; do\n        rm -f \\\"${{line}}\\\"\n    done\n\n    rm \\\"{PATH}\\\"\n    \"\n        ));\n\n        // SAFETY: safe, single-threaded execution.\n        let mut tempfile = unsafe { temp::TempFile::new()? };\n        for file in files {\n            writeln!(tempfile.file(), \"{mount_prefix}/{reldst}/{file}\")?;\n        }\n\n        // need to avoid having hundreds of files on the command, so\n        // just provide a single file name.\n        subcommand_or_exit(self.engine, \"cp\")?\n            .arg(tempfile.path())\n            .arg(format!(\"{}:{PATH}\", self.container))\n            .run_and_get_status(msg_info, true)?;\n\n        subcommand_or_exit(self.engine, \"exec\")?\n            .arg(self.container)\n            .args([\"sh\", \"-c\", &script.join(\"\\n\")])\n            .run_and_get_status(msg_info, true)\n    }\n\n    #[track_caller]\n    fn container_path_exists(\n        &self,\n        relpath: &str,\n        mount_prefix: &str,\n        msg_info: &mut MessageInfo,\n    ) -> Result<bool> {\n        Ok(subcommand_or_exit(self.engine, \"exec\")?\n            .arg(self.container)\n            .args([\n                \"bash\",\n                \"-c\",\n                &format!(\"[[ -d '{mount_prefix}/{relpath}' ]]\"),\n            ])\n            .run_and_get_status(msg_info, true)?\n            .success())\n    }\n\n    #[track_caller]\n    pub fn copy_cargo(\n        &self,\n        mount_prefix: &str,\n        copy_registry: bool,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let dirs = &self.toolchain_dirs;\n        let reldst = dirs.cargo_mount_path_relative()?;\n        let copy_registry = env::var(\"CROSS_REMOTE_COPY_REGISTRY\")\n            .map(|s| bool_from_envvar(&s))\n            .unwrap_or(copy_registry);\n\n        self.create_dir(&reldst, mount_prefix, msg_info)?;\n        if copy_registry {\n            self.copy_files(&dirs.cargo().join(\".\"), &reldst, mount_prefix, msg_info)?;\n        } else {\n            // can copy a limit subset of files: the rest is present.\n            for entry in fs::read_dir(dirs.cargo())\n                .wrap_err_with(|| format!(\"when reading directory {:?}\", dirs.cargo()))?\n            {\n                let file = entry?;\n                let basename = file\n                    .file_name()\n                    .to_utf8()\n                    .wrap_err_with(|| format!(\"when reading file {file:?}\"))?\n                    .to_owned();\n                if !basename.starts_with('.') && !matches!(basename.as_ref(), \"git\" | \"registry\") {\n                    self.copy_files(&file.path(), &reldst, mount_prefix, msg_info)?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    // copy over files needed for all targets in the toolchain that should never change\n    #[track_caller]\n    fn copy_rust_base(&self, mount_prefix: &str, msg_info: &mut MessageInfo) -> Result<()> {\n        let dirs = &self.toolchain_dirs;\n\n        // the rust toolchain is quite large, but most of it isn't needed\n        // we need the bin, libexec, and etc directories, and part of the lib directory.\n        let reldst = dirs.sysroot_mount_path_relative()?;\n        let rustlib = \"lib/rustlib\";\n        self.create_dir(&format!(\"{reldst}/{}\", rustlib), mount_prefix, msg_info)?;\n        for basename in [\"bin\", \"libexec\", \"etc\"] {\n            let file = dirs.get_sysroot().join(basename);\n            self.copy_files(&file, &reldst, mount_prefix, msg_info)?;\n        }\n\n        // the lib directories are rather large, so we want only a subset.\n        // now, we use a temp directory for everything else in the libdir\n        // we can pretty safely assume we don't have symlinks here.\n\n        // first, copy the shared libraries inside lib, all except rustlib.\n        // SAFETY: safe, single-threaded execution.\n        let tempdir = unsafe { temp::TempDir::new()? };\n        let temppath = tempdir.path();\n        file::create_dir_all(temppath.join(rustlib))?;\n        let mut had_symlinks = copy_dir(\n            &dirs.get_sysroot().join(\"lib\"),\n            &temppath.join(\"lib\"),\n            true,\n            0,\n            |e, d| d == 0 && e.file_name() == \"rustlib\",\n        )?;\n\n        // next, copy the src/etc directories inside rustlib\n        had_symlinks |= copy_dir(\n            &dirs.get_sysroot().join(rustlib),\n            &temppath.join(rustlib),\n            true,\n            0,\n            |e, d| d == 0 && !(e.file_name() == \"src\" || e.file_name() == \"etc\"),\n        )?;\n        self.copy_files(&temppath.join(\"lib\"), &reldst, mount_prefix, msg_info)?;\n\n        warn_symlinks(had_symlinks, msg_info)\n    }\n\n    #[track_caller]\n    fn copy_rust_manifest(&self, mount_prefix: &str, msg_info: &mut MessageInfo) -> Result<()> {\n        let dirs = &self.toolchain_dirs;\n\n        // copy over all the manifest files in rustlib\n        // these are small text files containing names/paths to toolchains\n        let reldst = dirs.sysroot_mount_path_relative()?;\n        let rustlib = \"lib/rustlib\";\n\n        // SAFETY: safe, single-threaded execution.\n        let tempdir = unsafe { temp::TempDir::new()? };\n        let temppath = tempdir.path();\n        file::create_dir_all(temppath.join(rustlib))?;\n        let had_symlinks = copy_dir(\n            &dirs.get_sysroot().join(rustlib),\n            &temppath.join(rustlib),\n            true,\n            0,\n            |e, d| d != 0 || e.file_type().map(|t| !t.is_file()).unwrap_or(true),\n        )?;\n        self.copy_files(&temppath.join(\"lib\"), &reldst, mount_prefix, msg_info)?;\n\n        warn_symlinks(had_symlinks, msg_info)\n    }\n\n    // copy over the toolchain for a specific triple\n    #[track_caller]\n    fn copy_rust_triple(\n        &self,\n        target_triple: &TargetTriple,\n        mount_prefix: &str,\n        skip_exists: bool,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let dirs = &self.toolchain_dirs;\n\n        // copy over the files for a specific triple\n        let reldst = &dirs.sysroot_mount_path_relative()?;\n        let rustlib = \"lib/rustlib\";\n        let reldst_rustlib = format!(\"{reldst}/{rustlib}\");\n        let src_toolchain = dirs\n            .get_sysroot()\n            .join(Path::new(rustlib))\n            .join(target_triple.triple());\n        let reldst_toolchain = format!(\"{reldst_rustlib}/{}\", target_triple.triple());\n\n        // skip if the toolchain target component already exists. for the host toolchain\n        // or the first run of the target toolchain, we know it doesn't exist.\n        let mut skip = false;\n        if skip_exists {\n            skip = self.container_path_exists(&reldst_toolchain, mount_prefix, msg_info)?;\n        }\n        if !skip {\n            self.copy_files(&src_toolchain, &reldst_rustlib, mount_prefix, msg_info)?;\n        }\n        if !skip && skip_exists {\n            // this means we have a persistent data volume and we have a\n            // new target, meaning we might have new manifests as well.\n            self.copy_rust_manifest(mount_prefix, msg_info)?;\n        }\n\n        Ok(())\n    }\n\n    #[track_caller]\n    pub fn copy_rust(\n        &self,\n        target_triple: Option<&TargetTriple>,\n        mount_prefix: &str,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let dirs = &self.toolchain_dirs;\n\n        self.copy_rust_base(mount_prefix, msg_info)?;\n        self.copy_rust_manifest(mount_prefix, msg_info)?;\n        self.copy_rust_triple(dirs.host_target(), mount_prefix, false, msg_info)?;\n        if let Some(target_triple) = target_triple {\n            if target_triple.triple() != dirs.host_target().triple() {\n                self.copy_rust_triple(target_triple, mount_prefix, false, msg_info)?;\n            }\n        }\n\n        Ok(())\n    }\n\n    #[track_caller]\n    fn copy_mount(\n        &self,\n        src: &Path,\n        reldst: &str,\n        mount_prefix: &str,\n        volume: &VolumeId,\n        copy_cache: bool,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let copy_all = |info: &mut MessageInfo| {\n            if copy_cache {\n                self.copy_files(&src.join(\".\"), reldst, mount_prefix, info)\n            } else {\n                self.copy_files_nocache(&src.join(\".\"), reldst, mount_prefix, true, info)\n            }\n        };\n        match volume {\n            VolumeId::Keep(_) => {\n                let parent = temp::dir()?;\n                file::create_dir_all(&parent)?;\n\n                let toolchain = &self.toolchain_dirs.toolchain();\n                let filename = toolchain.unique_mount_identifier(src)?;\n                let fingerprint = parent.join(filename);\n                let current = Fingerprint::read_dir(src, copy_cache)?;\n                // need to check if the container path exists, otherwise we might\n                // have stale data: the persistent volume was deleted & recreated.\n                if fingerprint.exists()\n                    && self.container_path_exists(reldst, mount_prefix, msg_info)?\n                {\n                    let previous = Fingerprint::read_file(&fingerprint)?;\n                    let (to_copy, to_remove) = previous.difference(&current);\n                    if !to_copy.is_empty() {\n                        self.copy_file_list(src, reldst, mount_prefix, &to_copy, msg_info)?;\n                    }\n                    if !to_remove.is_empty() {\n                        self.remove_file_list(reldst, mount_prefix, &to_remove, msg_info)?;\n                    }\n\n                    // write fingerprint afterwards, in case any failure so we\n                    // ensure any changes will be made on subsequent runs\n                    current.write_file(&fingerprint)?;\n                } else {\n                    current.write_file(&fingerprint)?;\n                    copy_all(msg_info)?;\n                }\n            }\n            VolumeId::Discard => {\n                copy_all(msg_info)?;\n            }\n        }\n\n        Ok(())\n    }\n}\n\nfn is_cachedir_tag(path: &Path) -> Result<bool> {\n    let mut buffer = [b'0'; 43];\n    let mut file = fs::OpenOptions::new().read(true).open(path)?;\n    file.read_exact(&mut buffer)?;\n\n    Ok(&buffer == b\"Signature: 8a477f597d28d172789f06886806bc55\")\n}\n\nfn is_cachedir(entry: &fs::DirEntry) -> bool {\n    // avoid any cached directories when copying\n    // see https://bford.info/cachedir/\n    if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {\n        let path = entry.path().join(\"CACHEDIR.TAG\");\n        path.exists() && is_cachedir_tag(&path).unwrap_or(false)\n    } else {\n        false\n    }\n}\n\n// recursively copy a directory into another\nfn copy_dir<Skip>(\n    src: &Path,\n    dst: &Path,\n    copy_symlinks: bool,\n    depth: u32,\n    skip: Skip,\n) -> Result<bool>\nwhere\n    Skip: Copy + Fn(&fs::DirEntry, u32) -> bool,\n{\n    let mut had_symlinks = false;\n\n    for entry in fs::read_dir(src).wrap_err_with(|| format!(\"when reading directory {src:?}\"))? {\n        let file = entry?;\n        if skip(&file, depth) {\n            continue;\n        }\n\n        let src_path = file.path();\n        let dst_path = dst.join(file.file_name());\n        if file.file_type()?.is_file() {\n            fs::copy(&src_path, &dst_path)\n                .wrap_err_with(|| format!(\"when copying file {src_path:?} -> {dst_path:?}\"))?;\n        } else if file.file_type()?.is_dir() {\n            fs::create_dir(&dst_path).ok();\n            had_symlinks = copy_dir(&src_path, &dst_path, copy_symlinks, depth + 1, skip)?;\n        } else if copy_symlinks {\n            had_symlinks = true;\n            let link_dst = fs::read_link(src_path)?;\n\n            #[cfg(target_family = \"unix\")]\n            {\n                std::os::unix::fs::symlink(link_dst, dst_path)?;\n            }\n\n            #[cfg(target_family = \"windows\")]\n            {\n                let link_dst_absolute = if link_dst.is_absolute() {\n                    link_dst.clone()\n                } else {\n                    // we cannot fail even if the linked to path does not exist.\n                    src.join(&link_dst)\n                };\n                if link_dst_absolute.is_dir() {\n                    std::os::windows::fs::symlink_dir(link_dst, dst_path)?;\n                } else {\n                    // symlink_file handles everything that isn't a directory\n                    std::os::windows::fs::symlink_file(link_dst, dst_path)?;\n                }\n            }\n        } else {\n            had_symlinks = true;\n        }\n    }\n\n    Ok(had_symlinks)\n}\n\nfn warn_symlinks(had_symlinks: bool, msg_info: &mut MessageInfo) -> Result<()> {\n    if had_symlinks {\n        msg_info.warn(\"copied directory contained symlinks. if the volume the link points to was not mounted, the remote build may fail\")\n    } else {\n        Ok(())\n    }\n}\n\n#[derive(Debug)]\nstruct Fingerprint {\n    map: BTreeMap<String, time::SystemTime>,\n}\n\nimpl Fingerprint {\n    fn new() -> Self {\n        Self {\n            map: BTreeMap::new(),\n        }\n    }\n\n    fn read_file(path: &Path) -> Result<Self> {\n        let file = fs::OpenOptions::new().read(true).open(path)?;\n        let reader = io::BufReader::new(file);\n        let mut map = BTreeMap::new();\n        for line in reader.lines() {\n            let line = line?;\n            let (timestamp, relpath) = line\n                .split_once('\\t')\n                .ok_or_else(|| eyre::eyre!(\"unable to parse fingerprint line '{line}'\"))?;\n            let modified = time_from_millis(timestamp.parse::<u64>()?);\n            map.insert(relpath.to_owned(), modified);\n        }\n\n        Ok(Self { map })\n    }\n\n    fn write_file(&self, path: &Path) -> Result<()> {\n        let mut file = fs::OpenOptions::new()\n            .write(true)\n            .truncate(true)\n            .create(true)\n            .open(path)?;\n        for (relpath, modified) in &self.map {\n            let timestamp = time_to_millis(modified)?;\n            writeln!(file, \"{timestamp}\\t{relpath}\")?;\n        }\n\n        Ok(())\n    }\n\n    fn _read_dir(&mut self, home: &Path, path: &Path, copy_cache: bool) -> Result<()> {\n        for entry in fs::read_dir(path)? {\n            let file = entry?;\n            let file_type = file.file_type()?;\n            // only parse known files types: 0 or 1 of these tests can pass.\n            if file_type.is_dir() {\n                if copy_cache || !is_cachedir(&file) {\n                    self._read_dir(home, &path.join(file.file_name()), copy_cache)?;\n                }\n            } else if file_type.is_file() || file_type.is_symlink() {\n                // we're mounting to the same location, so this should fine\n                // we need to round the modified date to millis.\n                let modified = file.metadata()?.modified()?;\n                let rounded = time_from_millis(time_to_millis(&modified)?);\n                let relpath = file.path().strip_prefix(home)?.as_posix_relative()?;\n                self.map.insert(relpath, rounded);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn read_dir(home: &Path, copy_cache: bool) -> Result<Fingerprint> {\n        let mut result = Fingerprint::new();\n        result._read_dir(home, home, copy_cache)?;\n        Ok(result)\n    }\n\n    // returns to_copy (added + modified) and to_remove (removed).\n    fn difference<'a, 'b>(&'a self, current: &'b Fingerprint) -> (Vec<&'b str>, Vec<&'a str>) {\n        let to_copy: Vec<&str> = current\n            .map\n            .iter()\n            .filter(|(k, v1)| self.map.get(*k).is_none_or(|v2| v1 != &v2))\n            .map(|(k, _)| k.as_str())\n            .collect();\n        let to_remove: Vec<&str> = self\n            .map\n            .iter()\n            .filter(|(k, _)| !current.map.contains_key(*k))\n            .map(|(k, _)| k.as_str())\n            .collect();\n        (to_copy, to_remove)\n    }\n}\n\nimpl QualifiedToolchain {\n    pub fn unique_toolchain_identifier(&self) -> Result<String> {\n        // try to get the commit hash for the currently toolchain, if possible\n        // if not, get the default rustc and use the path hash for uniqueness\n        let commit_hash = if let Some(version) = self.rustc_version_string()? {\n            rustc::hash_from_version_string(&version, 1)\n        } else {\n            rustc::version_meta()?.commit_hash()\n        };\n\n        let toolchain_name = self\n            .get_sysroot()\n            .file_name()\n            .expect(\"should be able to get toolchain name\")\n            .to_utf8()?;\n        let toolchain_hash = path_hash(self.get_sysroot(), PATH_HASH_SHORT)?;\n        Ok(format!(\n            \"{VOLUME_PREFIX}{toolchain_name}-{toolchain_hash}-{commit_hash}\"\n        ))\n    }\n\n    // unique identifier for a given container. allows the ID to\n    // be generated outside a rust package and run multiple times.\n    pub fn unique_container_identifier(&self, triple: &TargetTriple) -> Result<String> {\n        let toolchain_id = self.unique_toolchain_identifier()?;\n        let cwd_path = path_hash(&env::current_dir()?, PATH_HASH_SHORT)?;\n        let system_time = now_as_millis()?;\n        Ok(format!(\"{toolchain_id}-{triple}-{cwd_path}-{system_time}\"))\n    }\n\n    // unique identifier for a given mounted volume\n    pub fn unique_mount_identifier(&self, path: &Path) -> Result<String> {\n        let toolchain_id = self.unique_toolchain_identifier()?;\n        let mount_hash = path_hash(path, PATH_HASH_UNIQUE)?;\n        Ok(format!(\"{toolchain_id}-{mount_hash}\"))\n    }\n}\n\npub(crate) fn run(\n    options: DockerOptions,\n    paths: DockerPaths,\n    args: &[String],\n    subcommand: Option<crate::Subcommand>,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<ExitStatus>> {\n    let engine = &options.engine;\n    let target = &options.target;\n    let toolchain_dirs = paths.directories.toolchain_directories();\n    let package_dirs = paths.directories.package_directories();\n\n    let mount_prefix = MOUNT_PREFIX;\n\n    if options.in_docker() {\n        msg_info.warn(\"remote and docker-in-docker are unlikely to work together when using cross. remote cross uses data volumes, so docker-in-docker should not be required.\")?;\n    }\n\n    // the logic is broken into the following steps\n    // 1. get our unique identifiers and cleanup from a previous run.\n    // 2. if not using persistent volumes, create a data volume\n    // 3. start our container with the mounted data volume and all envvars\n    // 4. copy data into the data volume\n    //      with persistent data volumes, copy just copy crate data and\n    //      if not present, the toolchain for the current target.\n    //      otherwise, copy the entire toolchain, cargo, and crate data\n    //      if `CROSS_REMOTE_COPY_CACHE`, copy over the target dir as well\n    // 5. create symlinks for all mounted data\n    //      ensure the paths are the same as local cross\n    // 6. execute our cargo command inside the container\n    // 7. copy data from target dir back to host\n    // 8. stop container and delete data volume\n    //\n    // we use structs that wrap the resources to ensure they're dropped\n    // in the correct order even on error, to ensure safe cleanup\n\n    // 1. get our unique identifiers and cleanup from a previous run.\n    // this can happen if we didn't gracefully exit before\n    // note that since we use `docker run --rm`, it's very\n    // unlikely the container state existed before.\n    let toolchain_id = toolchain_dirs.unique_toolchain_identifier()?;\n    let container_id = toolchain_dirs.unique_container_identifier(target.target())?;\n    let volume = {\n        let existing = DockerVolume::existing(engine, toolchain_dirs.toolchain(), msg_info)?;\n        if existing.iter().any(|v| v == &toolchain_id) {\n            VolumeId::Keep(toolchain_id)\n        } else {\n            let partial = format!(\"{VOLUME_PREFIX}{}\", toolchain_dirs.toolchain());\n            if existing.iter().any(|v| v.starts_with(&partial)) {\n                msg_info.warn(format_args!(\n                    \"a persistent volume does not exists for `{0}`, but there is a volume for a different version.\\n > Create a new volume with `cross-util volumes create --toolchain {0}`\",\n                    toolchain_dirs.toolchain()\n                ))?;\n            }\n            VolumeId::Discard\n        }\n    };\n\n    let container = DockerContainer::new(engine, &container_id);\n    let state = container.state(msg_info)?;\n    if !state.is_stopped() {\n        msg_info.warn(format_args!(\"container {container_id} was running.\"))?;\n        container.stop_default(msg_info)?;\n    }\n    if state.exists() {\n        msg_info.warn(format_args!(\"container {container_id} was exited.\"))?;\n        container.remove(msg_info)?;\n    }\n\n    // 2. create our volume to copy all our data over to\n    // we actually use an anonymous volume, so it's auto-cleaned up,\n    // if we're using a discarded volume.\n\n    // 3. create our start container command here\n    let mut docker = engine.subcommand(\"run\");\n    docker.add_userns();\n    options\n        .image\n        .platform\n        .specify_platform(&options.engine, &mut docker);\n    docker.args([\"--name\", &container_id]);\n    docker.arg(\"--rm\");\n    docker.args([\"-v\", &volume.mount(mount_prefix)]);\n\n    let mut volumes = vec![];\n    docker\n        .add_mounts(\n            &options,\n            &paths,\n            |_, _, _| Ok(()),\n            |(src, dst)| volumes.push((src, dst)),\n            msg_info,\n        )\n        .wrap_err(\"could not determine mount points\")?;\n\n    docker\n        .add_seccomp(engine.kind, target, &paths.metadata)\n        .wrap_err(\"when copying seccomp profile\")?;\n\n    // Prevent `bin` from being mounted inside the Docker container.\n    docker.args([\"-v\", &format!(\"{mount_prefix}/cargo/bin\")]);\n\n    // When running inside NixOS or using Nix packaging we need to add the Nix\n    // Store to the running container so it can load the needed binaries.\n    if let Some(nix_store) = toolchain_dirs.nix_store() {\n        let nix_string = nix_store.to_utf8()?;\n        volumes.push((nix_string.to_owned(), nix_string.to_owned()));\n    }\n\n    docker.arg(\"-d\");\n    let is_tty =\n        io::stdin().is_terminal() && io::stdout().is_terminal() && io::stderr().is_terminal();\n    if is_tty {\n        docker.arg(\"-t\");\n    }\n\n    let mut image_name = options.image.name.clone();\n\n    if options.needs_custom_image() {\n        image_name = options\n            .custom_image_build(&paths, msg_info)\n            .wrap_err(\"when building custom image\")?;\n    }\n\n    docker.arg(&image_name);\n\n    if !is_tty {\n        // ensure the process never exits until we stop it\n        // we only need this infinite loop if we don't allocate\n        // a TTY. this has a few issues though: now, the\n        // container no longer responds to signals, so the\n        // container will need to be sig-killed.\n        docker.args([\"sh\", \"-c\", \"sleep infinity\"]);\n    }\n\n    // store first, since failing to non-existing container is fine\n    ChildContainer::create(engine.clone(), container_id.clone())?;\n    docker.run_and_get_status(msg_info, true)?;\n\n    // 4. copy all mounted volumes over\n    let data_volume = ContainerDataVolume::new(engine, &container_id, toolchain_dirs);\n    let copy_cache = env::var(\"CROSS_REMOTE_COPY_CACHE\")\n        .map(|s| bool_from_envvar(&s))\n        .unwrap_or_default();\n    let copy = |src, reldst: &str, info: &mut MessageInfo| {\n        data_volume.copy_mount(src, reldst, mount_prefix, &volume, copy_cache, info)\n    };\n    if let VolumeId::Discard = volume {\n        data_volume\n            .copy_cargo(mount_prefix, false, msg_info)\n            .wrap_err(\"when copying cargo\")?;\n        data_volume\n            .copy_rust(Some(target.target()), mount_prefix, msg_info)\n            .wrap_err(\"when copying rust\")?;\n    } else {\n        // need to copy over the target triple if it hasn't been previously copied\n        data_volume\n            .copy_rust_triple(target.target(), mount_prefix, true, msg_info)\n            .wrap_err(\"when copying rust target files\")?;\n    }\n    // cannot panic: absolute unix path, must have root\n    let rel_mount_root = package_dirs\n        .mount_root()\n        .strip_prefix('/')\n        .expect(\"mount root should be absolute\");\n    if !rel_mount_root.is_empty() {\n        data_volume\n            .create_dir(\n                posix_parent(rel_mount_root).expect(\"mount root should have a parent directory\"),\n                mount_prefix,\n                msg_info,\n            )\n            .wrap_err(\"when creating mount root\")?;\n    }\n    copy(package_dirs.host_root(), rel_mount_root, msg_info).wrap_err(\"when copying project\")?;\n    let sysroot = toolchain_dirs.get_sysroot().to_owned();\n    let mut copied = vec![\n        (\n            toolchain_dirs.cargo(),\n            toolchain_dirs.cargo_mount_path_relative()?,\n        ),\n        (&sysroot, toolchain_dirs.sysroot_mount_path_relative()?),\n        (package_dirs.host_root(), rel_mount_root.to_owned()),\n    ];\n    let mut to_symlink = vec![];\n    let target_dir = file::canonicalize(package_dirs.target())?;\n    let target_dir = if let Ok(relpath) = target_dir.strip_prefix(package_dirs.host_root()) {\n        relpath.as_posix_relative()?\n    } else {\n        // outside project, need to copy the target data over\n        // only do if we're copying over cached files.\n        let target_dir = \"target\".to_owned();\n        if copy_cache {\n            copy(package_dirs.target(), &target_dir, msg_info)?;\n        } else {\n            data_volume.create_dir(&target_dir, mount_prefix, msg_info)?;\n        }\n\n        copied.push((package_dirs.target(), target_dir.clone()));\n        target_dir\n    };\n    for (src, dst) in &volumes {\n        let src: &Path = src.as_ref();\n        if let Some((psrc, pdst)) = copied.iter().find(|(p, _)| src.starts_with(p)) {\n            // path has already been copied over\n            let relpath = src\n                .strip_prefix(psrc)\n                .expect(\"source should start with prefix\")\n                .as_posix_relative()?;\n            to_symlink.push((format!(\"{pdst}/{relpath}\"), dst));\n        } else {\n            let reldst = dst\n                .strip_prefix('/')\n                .expect(\"destination should be absolute\");\n            if !reldst.is_empty() {\n                data_volume.create_dir(\n                    posix_parent(reldst).expect(\"destination should have a parent directory\"),\n                    mount_prefix,\n                    msg_info,\n                )?;\n            }\n            copy(src, reldst, msg_info)?;\n        }\n    }\n\n    let mut cmd = options.command_variant.safe_command();\n\n    if msg_info.should_fail() {\n        return Ok(None);\n    }\n\n    if !options.command_variant.is_shell() {\n        // `clean` doesn't handle symlinks: it will just unlink the target\n        // directory, so we should just substitute it our target directory\n        // for it. we'll still have the same end behavior\n        let mut final_args = vec![];\n        let mut iter = args.iter().cloned();\n        let mut has_target_dir = false;\n        while let Some(arg) = iter.next() {\n            if arg == \"--target-dir\" {\n                has_target_dir = true;\n                final_args.push(arg);\n                if iter.next().is_some() {\n                    final_args.push(target_dir.clone());\n                }\n            } else if arg.starts_with(\"--target-dir=\") {\n                has_target_dir = true;\n                if arg.split_once('=').is_some() {\n                    final_args.push(format!(\"--target-dir={target_dir}\"));\n                }\n            } else {\n                final_args.push(arg);\n            }\n        }\n        if !has_target_dir && subcommand.is_none_or(|s| s.needs_target_in_command()) {\n            final_args.push(\"--target-dir\".to_owned());\n            final_args.push(target_dir.clone());\n        }\n\n        cmd.args(final_args);\n    } else {\n        cmd.args(args);\n    }\n\n    // 5. create symlinks for copied data\n    let mut symlink = vec![\"set -e pipefail\".to_owned()];\n    if msg_info.is_verbose() {\n        symlink.push(\"set -x\".to_owned());\n    }\n    symlink.push(format!(\n        \"chown -R {uid}:{gid} {mount_prefix}\",\n        uid = user_id(),\n        gid = group_id(),\n    ));\n    // need a simple script to add symlinks, but not override existing files.\n    symlink.push(format!(\n        \"prefix=\\\"{mount_prefix}\\\"\n\nsymlink_recurse() {{\n    for f in \\\"${{1}}\\\"/*; do\n        dst=${{f#\\\"$prefix\\\"}}\n        if [ -f \\\"${{dst}}\\\" ]; then\n            echo \\\"invalid: got unexpected file at ${{dst}}\\\" 1>&2\n            exit 1\n        elif [ -d \\\"${{dst}}\\\" ]; then\n            symlink_recurse \\\"${{f}}\\\"\n        else\n            ln -s \\\"${{f}}\\\" \\\"${{dst}}\\\"\n        fi\n    done\n}}\n\nsymlink_recurse \\\"${{prefix}}\\\"\n\"\n    ));\n    for (src, dst) in to_symlink {\n        symlink.push(format!(\"ln -s \\\"{src}\\\" \\\"{dst}\\\"\",));\n    }\n    subcommand_or_exit(engine, \"exec\")?\n        .arg(&container_id)\n        .args([\"sh\", \"-c\", &symlink.join(\"\\n\")])\n        .run_and_get_status(msg_info, false)\n        .wrap_err(\"when creating symlinks to provide consistent host/mount paths\")?;\n\n    // 6. execute our cargo command inside the container\n    let mut docker = engine.subcommand(\"exec\");\n    docker.add_user_id(engine.is_rootless);\n    docker.add_envvars(&options, toolchain_dirs, msg_info)?;\n    docker.add_cwd(&paths)?;\n    docker.arg(&container_id);\n    docker.add_build_command(toolchain_dirs, &cmd);\n\n    if options.interactive {\n        docker.arg(\"-i\");\n    }\n\n    bail_container_exited!();\n    let status = docker.run_and_get_status(msg_info, false);\n\n    // 7. copy data from our target dir back to host\n    // this might not exist if we ran `clean`.\n    let skip_artifacts = env::var(\"CROSS_REMOTE_SKIP_BUILD_ARTIFACTS\")\n        .map(|s| bool_from_envvar(&s))\n        .unwrap_or_default();\n    bail_container_exited!();\n    let mount_target_dir = format!(\"{}/{}\", package_dirs.mount_root(), target_dir);\n    if !skip_artifacts\n        && data_volume.container_path_exists(&mount_target_dir, mount_prefix, msg_info)?\n    {\n        subcommand_or_exit(engine, \"cp\")?\n            .arg(\"-a\")\n            .arg(format!(\"{container_id}:{mount_target_dir}\",))\n            .arg(\n                package_dirs\n                    .target()\n                    .parent()\n                    .expect(\"target directory should have a parent\"),\n            )\n            .run_and_get_status(msg_info, false)?;\n    }\n\n    ChildContainer::finish_static(is_tty, msg_info);\n\n    status.map(Some)\n}\n"
  },
  {
    "path": "src/docker/seccomp.json",
    "content": "{\n    \"defaultAction\": \"SCMP_ACT_ALLOW\",\n    \"syscalls\": [\n        {\n            \"names\": [\n                \"add_key\",\n                \"get_kernel_syms\",\n                \"keyctl\",\n                \"move_pages\",\n                \"nfsservctl\",\n                \"perf_event_open\",\n                \"pivot_root\",\n                \"query_module\",\n                \"request_key\",\n                \"sysfs\",\n                \"_sysctl\",\n                \"uselib\",\n                \"userfaultfd\",\n                \"ustat\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1\n        },\n        {\n            \"names\": [\n                \"acct\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_PACCT\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"bpf\",\n                \"lookup_dcookie\",\n                \"mount\",\n                \"quotactl\",\n                \"quotactl_fd\",\n                \"setns\",\n                \"swapon\",\n                \"swapoff\",\n                \"umount\",\n                \"umount2\",\n                \"unshare\",\n                \"vm86\",\n                \"vm86old\",\n                \"pciconfig_read\",\n                \"pciconfig_write\",\n                \"salinfo_log_open\",\n                \"salinfo_event_open\",\n                \"sys_cacheflush\",\n                \"rtas\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_ADMIN\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"clock_adjtime\",\n                \"clock_settime\",\n                \"settimeofday\",\n                \"stime\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_TIME\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"create_module\",\n                \"delete_module\",\n                \"finit_module\",\n                \"init_module\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_MODULE\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"get_mempolicy\",\n                \"mbind\",\n                \"set_mempolicy\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_NICE\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"ioperm\",\n                \"iopl\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_RAWIO\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"kcmp\",\n                \"process_vm_readv\",\n                \"process_vm_writev\",\n                \"ptrace\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_PTRACE\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"kexec_file_load\",\n                \"kexec_load\",\n                \"reboot\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_SYS_BOOT\"\n                ]\n            }\n        },\n        {\n            \"names\": [\n                \"name_to_handle_at\",\n                \"open_by_handle_at\"\n            ],\n            \"action\": \"SCMP_ACT_ERRNO\",\n            \"errnoRet\": 1,\n            \"excludes\": {\n                \"caps\": [\n                    \"CAP_DAC_READ_SEARCH\"\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "src/docker/shared.rs",
    "content": "#![allow(static_mut_refs)] // FIXME: Use correct types for CHILD_CONTAINER\n\nuse super::custom::{Dockerfile, PreBuild};\nuse super::image::PossibleImage;\nuse super::Image;\nuse super::PROVIDED_IMAGES;\nuse super::{engine::*, ProvidedImage};\nuse crate::cargo::CargoMetadata;\nuse crate::config::Config;\nuse crate::errors::*;\nuse crate::extensions::{CommandExt, SafeCommand};\nuse crate::file::{self, write_file, PathExt, ToUtf8};\nuse crate::id;\nuse crate::rustc::QualifiedToolchain;\nuse crate::shell::{ColorChoice, MessageInfo, Verbosity};\nuse crate::{CommandVariant, OutputExt, Target, TargetTriple};\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, ExitStatus, Output};\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::{env, fs, time};\n\nuse rustc_version::Version as RustcVersion;\n\npub use super::custom::CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX;\npub const CROSS_IMAGE: &str = {\n    if let Some(opt) = option_env!(\"CROSS_IMAGE\") {\n        opt\n    } else {\n        \"ghcr.io/cross-rs\"\n    }\n};\n\n// note: this is the most common base image for our images\npub const UBUNTU_BASE: &str = \"ubuntu:20.04\";\npub const DEFAULT_IMAGE_VERSION: &str = if crate::commit_info().is_empty() {\n    env!(\"CARGO_PKG_VERSION\")\n} else {\n    \"main\"\n};\n\n#[derive(Debug)]\npub struct DockerOptions {\n    pub engine: Engine,\n    pub target: Target,\n    pub config: Config,\n    pub image: Image,\n    pub command_variant: CommandVariant,\n    // not all toolchains will provide this\n    pub rustc_version: Option<RustcVersion>,\n    pub interactive: bool,\n}\n\nimpl DockerOptions {\n    pub fn new(\n        engine: Engine,\n        target: Target,\n        config: Config,\n        image: Image,\n        cargo_variant: CommandVariant,\n        rustc_version: Option<RustcVersion>,\n        interactive: bool,\n    ) -> DockerOptions {\n        DockerOptions {\n            engine,\n            target,\n            config,\n            image,\n            command_variant: cargo_variant,\n            rustc_version,\n            interactive,\n        }\n    }\n\n    #[must_use]\n    pub fn in_docker(&self) -> bool {\n        self.engine.in_docker\n    }\n\n    #[must_use]\n    pub fn is_remote(&self) -> bool {\n        self.engine.is_remote\n    }\n\n    #[must_use]\n    pub fn needs_custom_image(&self) -> bool {\n        self.config.dockerfile(&self.target).is_some()\n            || self.config.pre_build(&self.target).is_some()\n    }\n\n    pub(crate) fn custom_image_build(\n        &self,\n        paths: &DockerPaths,\n        msg_info: &mut MessageInfo,\n    ) -> Result<String> {\n        let mut image = self.image.clone();\n        if self.target.triple() == \"arm-unknown-linux-gnueabihf\" {\n            msg_info.note(\"cannot install armhf system packages via apt for `arm-unknown-linux-gnueabihf`, since they are for ARMv7a targets but this target is ARMv6. installation of all packages for the armhf architecture has been blocked.\")?;\n        }\n\n        if let Some(path) = self.config.dockerfile(&self.target) {\n            let context = self.config.dockerfile_context(&self.target);\n\n            let is_custom_image = self.config.image(&self.target)?.is_some();\n\n            let build = Dockerfile::File {\n                path: &path,\n                context: context.as_deref(),\n                name: if is_custom_image {\n                    Some(&image.name)\n                } else {\n                    None\n                },\n                runs_with: &image.platform,\n            };\n\n            image.name = build\n                .build(\n                    self,\n                    paths,\n                    self.config\n                        .dockerfile_build_args(&self.target)\n                        .unwrap_or_default(),\n                    msg_info,\n                )\n                .wrap_err(\"when building dockerfile\")?;\n        }\n        let pre_build = self.config.pre_build(&self.target);\n\n        if let Some(pre_build) = pre_build {\n            match pre_build {\n                super::custom::PreBuild::Single {\n                    line: pre_build_script,\n                    env,\n                } if !env\n                    && !pre_build_script.contains('\\n')\n                    && paths.host_root().join(&pre_build_script).is_file() =>\n                {\n                    let custom = Dockerfile::Custom {\n                        content: format!(\n                            r#\"\n                FROM {image}\n                ARG CROSS_DEB_ARCH=\n                ARG CROSS_SCRIPT\n                ARG CROSS_TARGET\n                COPY $CROSS_SCRIPT /pre-build-script\n                RUN chmod +x /pre-build-script\n                RUN ./pre-build-script $CROSS_TARGET\"#\n                        ),\n                        runs_with: &image.platform,\n                    };\n\n                    image.name = custom\n                        .build(\n                            self,\n                            paths,\n                            vec![\n                                (\"CROSS_SCRIPT\", &*pre_build_script),\n                                (\"CROSS_TARGET\", self.target.triple()),\n                            ],\n                            msg_info,\n                        )\n                        .wrap_err(\"when pre-building\")\n                        .with_note(|| format!(\"CROSS_SCRIPT={pre_build_script}\"))\n                        .with_note(|| format!(\"CROSS_TARGET={}\", self.target))?;\n                }\n                this => {\n                    let pre_build = match this {\n                        PreBuild::Single { line, .. } => vec![line],\n                        PreBuild::Lines(lines) => lines,\n                    };\n                    if !pre_build.is_empty() {\n                        let custom = Dockerfile::Custom {\n                            content: format!(\n                                r#\"\n                FROM {image}\n                ARG CROSS_DEB_ARCH=\n                ARG CROSS_CMD\n                RUN eval \"${{CROSS_CMD}}\"\"#\n                            ),\n                            runs_with: &image.platform,\n                        };\n                        image.name = custom\n                            .build(\n                                self,\n                                paths,\n                                Some((\"CROSS_CMD\", pre_build.join(\"\\n\"))),\n                                msg_info,\n                            )\n                            .wrap_err(\"when pre-building\")\n                            .with_note(|| format!(\"CROSS_CMD={}\", pre_build.join(\"\\n\")))?;\n                    }\n                }\n            }\n        }\n        Ok(image.name.clone())\n    }\n}\n\n#[derive(Debug)]\npub struct DockerPaths {\n    pub mount_finder: MountFinder,\n    pub metadata: CargoMetadata,\n    pub cwd: PathBuf,\n    pub directories: Directories,\n}\n\nimpl DockerPaths {\n    pub fn create(\n        engine: &Engine,\n        metadata: CargoMetadata,\n        cwd: PathBuf,\n        toolchain: QualifiedToolchain,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Self> {\n        let mount_finder = MountFinder::create(engine, msg_info)?;\n        let (directories, metadata) =\n            Directories::assemble(&mount_finder, metadata, &cwd, toolchain)?;\n        Ok(Self {\n            mount_finder,\n            metadata,\n            cwd,\n            directories,\n        })\n    }\n\n    pub fn get_sysroot(&self) -> &Path {\n        self.directories.toolchain_directories().get_sysroot()\n    }\n\n    pub fn workspace_root(&self) -> &Path {\n        &self.metadata.workspace_root\n    }\n\n    pub fn workspace_dependencies(&self) -> impl Iterator<Item = &Path> {\n        self.metadata.path_dependencies()\n    }\n\n    pub fn workspace_from_cwd(&self) -> Result<&Path> {\n        self.cwd\n            .strip_prefix(self.workspace_root())\n            .map_err(Into::into)\n    }\n\n    #[must_use]\n    pub fn in_workspace(&self) -> bool {\n        self.workspace_from_cwd().is_ok()\n    }\n\n    pub fn mount_cwd(&self) -> &str {\n        self.directories.package_directories().mount_cwd()\n    }\n\n    pub fn host_root(&self) -> &Path {\n        self.directories.package_directories().host_root()\n    }\n}\n\n#[derive(Debug)]\npub struct ToolchainDirectories {\n    cargo: PathBuf,\n    nix_store: Option<PathBuf>,\n    toolchain: QualifiedToolchain,\n    cargo_mount_path: String,\n    sysroot_mount_path: String,\n}\n\nimpl ToolchainDirectories {\n    pub fn assemble(mount_finder: &MountFinder, toolchain: QualifiedToolchain) -> Result<Self> {\n        let cargo = home::cargo_home()?;\n        // NIX_STORE_DIR is an override of NIX_STORE, which is the path in derivations.\n        let nix_store = env::var_os(\"NIX_STORE_DIR\")\n            .or_else(|| env::var_os(\"NIX_STORE\"))\n            .map(PathBuf::from);\n\n        // create the directories we are going to mount before we mount them,\n        // otherwise `docker` will create them but they will be owned by `root`\n        // cargo builds all intermediate directories, but fails\n        // if it has other issues (such as permission errors).\n        file::create_dir_all(&cargo)?;\n        if let Some(ref nix_store) = nix_store {\n            file::create_dir_all(nix_store)?;\n        }\n\n        // get our mount paths prior to canonicalizing them\n        let cargo_mount_path = cargo.as_posix_absolute()?;\n\n        // now that we know the paths exist, canonicalize them. this avoids creating\n        // directories after failed canonicalization into a shared directory.\n        let cargo = file::canonicalize(&cargo)?;\n\n        let default_nix_store = PathBuf::from(\"/nix/store\");\n        let nix_store = match nix_store {\n            Some(store) if store.exists() => {\n                let path = file::canonicalize(store)?;\n                Some(path)\n            }\n            Some(store) => {\n                eyre::bail!(\"unable to find provided nix-store directory {store:?}\");\n            }\n            None if cfg!(target_os = \"linux\") && default_nix_store.exists() => {\n                Some(default_nix_store)\n            }\n            None => None,\n        };\n\n        let cargo = mount_finder.find_mount_path(cargo);\n\n        // canonicalize these once to avoid syscalls\n        let sysroot_mount_path = toolchain.get_sysroot().as_posix_absolute()?;\n\n        Ok(ToolchainDirectories {\n            cargo,\n            nix_store,\n            toolchain,\n            cargo_mount_path,\n            sysroot_mount_path,\n        })\n    }\n\n    pub fn unique_toolchain_identifier(&self) -> Result<String> {\n        self.toolchain.unique_toolchain_identifier()\n    }\n\n    pub fn unique_container_identifier(&self, triple: &TargetTriple) -> Result<String> {\n        self.toolchain.unique_container_identifier(triple)\n    }\n\n    pub fn toolchain(&self) -> &QualifiedToolchain {\n        &self.toolchain\n    }\n\n    pub fn get_sysroot(&self) -> &Path {\n        self.toolchain.get_sysroot()\n    }\n\n    pub fn host_target(&self) -> &TargetTriple {\n        &self.toolchain.host().target\n    }\n\n    pub fn cargo(&self) -> &Path {\n        &self.cargo\n    }\n\n    pub fn cargo_host_path(&self) -> Result<&str> {\n        self.cargo.to_utf8()\n    }\n\n    pub fn cargo_mount_path(&self) -> &str {\n        &self.cargo_mount_path\n    }\n\n    pub fn sysroot_mount_path(&self) -> &str {\n        &self.sysroot_mount_path\n    }\n\n    pub fn nix_store(&self) -> Option<&Path> {\n        self.nix_store.as_deref()\n    }\n\n    pub fn cargo_mount_path_relative(&self) -> Result<String> {\n        self.cargo_mount_path()\n            .strip_prefix('/')\n            .map(ToOwned::to_owned)\n            .ok_or_else(|| eyre::eyre!(\"cargo directory must be relative to root\"))\n    }\n\n    pub fn sysroot_mount_path_relative(&self) -> Result<String> {\n        self.sysroot_mount_path()\n            .strip_prefix('/')\n            .map(ToOwned::to_owned)\n            .ok_or_else(|| eyre::eyre!(\"sysroot directory must be relative to root\"))\n    }\n}\n\n#[derive(Debug)]\npub struct PackageDirectories {\n    target: PathBuf,\n    host_root: PathBuf,\n    // both mount fields are WSL paths on windows: they already are POSIX paths\n    mount_root: String,\n    mount_cwd: String,\n}\n\nimpl PackageDirectories {\n    pub fn assemble(\n        mount_finder: &MountFinder,\n        metadata: CargoMetadata,\n        cwd: &Path,\n    ) -> Result<(Self, CargoMetadata)> {\n        let target = &metadata.target_directory;\n        // see ToolchainDirectories::assemble for creating directories\n        create_target_dir(target)?;\n\n        // root is either workspace_root, or, if we're outside the workspace root, the current directory\n        let host_root = if metadata.workspace_root.starts_with(cwd) {\n            cwd\n        } else {\n            &metadata.workspace_root\n        }\n        .to_path_buf();\n\n        // on Windows, we can not mount the directory name directly. Instead, we use wslpath to convert the path to a linux compatible path.\n        // NOTE: on unix, host root has already found the mount path\n        let mount_root = host_root.as_posix_absolute()?;\n        let mount_cwd = cwd.as_posix_absolute()?;\n\n        Ok((\n            PackageDirectories {\n                target: mount_finder.find_mount_path(target),\n                host_root,\n                mount_root,\n                mount_cwd,\n            },\n            metadata,\n        ))\n    }\n\n    pub fn target(&self) -> &Path {\n        &self.target\n    }\n\n    pub fn host_root(&self) -> &Path {\n        &self.host_root\n    }\n\n    pub fn mount_root(&self) -> &str {\n        &self.mount_root\n    }\n\n    pub fn mount_cwd(&self) -> &str {\n        &self.mount_cwd\n    }\n}\n\n#[derive(Debug)]\npub struct Directories {\n    toolchain: ToolchainDirectories,\n    package: PackageDirectories,\n}\n\nimpl Directories {\n    pub fn assemble(\n        mount_finder: &MountFinder,\n        metadata: CargoMetadata,\n        cwd: &Path,\n        toolchain: QualifiedToolchain,\n    ) -> Result<(Self, CargoMetadata)> {\n        let (package, metadata) = PackageDirectories::assemble(mount_finder, metadata, cwd)?;\n        let toolchain = ToolchainDirectories::assemble(mount_finder, toolchain)?;\n\n        Ok((Directories { toolchain, package }, metadata))\n    }\n\n    pub fn toolchain_directories(&self) -> &ToolchainDirectories {\n        &self.toolchain\n    }\n\n    pub fn package_directories(&self) -> &PackageDirectories {\n        &self.package\n    }\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum ContainerState {\n    Created,\n    Running,\n    Paused,\n    Restarting,\n    Dead,\n    Exited,\n    DoesNotExist,\n}\n\nimpl ContainerState {\n    pub fn new(state: &str) -> Result<Self> {\n        match state {\n            \"created\" => Ok(ContainerState::Created),\n            \"running\" => Ok(ContainerState::Running),\n            \"paused\" => Ok(ContainerState::Paused),\n            \"restarting\" => Ok(ContainerState::Restarting),\n            \"dead\" => Ok(ContainerState::Dead),\n            \"exited\" => Ok(ContainerState::Exited),\n            \"\" => Ok(ContainerState::DoesNotExist),\n            _ => eyre::bail!(\"unknown container state: got {state}\"),\n        }\n    }\n\n    #[must_use]\n    pub fn is_stopped(&self) -> bool {\n        matches!(self, Self::Exited | Self::DoesNotExist)\n    }\n\n    #[must_use]\n    pub fn exists(&self) -> bool {\n        !matches!(self, Self::DoesNotExist)\n    }\n}\n\n// the mount directory for the data volume.\npub const MOUNT_PREFIX: &str = \"/cross\";\n// the prefix used when naming volumes\npub const VOLUME_PREFIX: &str = \"cross-\";\n// default timeout to stop a container (in seconds)\npub const DEFAULT_TIMEOUT: u32 = 2;\n// instant kill in case of a non-graceful exit\npub const NO_TIMEOUT: u32 = 0;\n\npub(crate) static mut CHILD_CONTAINER: ChildContainer = ChildContainer::new();\n\n// the lack of [MessageInfo] is because it'd require a mutable reference,\n// since we don't need the functionality behind the [MessageInfo], we can just store the basic\n// MessageInfo configurations.\npub(crate) struct ChildContainerInfo {\n    engine: Engine,\n    name: String,\n    timeout: u32,\n    color_choice: ColorChoice,\n    verbosity: Verbosity,\n}\n\n// we need to specify drops for the containers, but we\n// also need to ensure the drops are called on a\n// termination handler. we use an atomic bool to ensure\n// that the drop only gets called once, even if we have\n// the signal handle invoked multiple times or it fails.\n#[allow(missing_debug_implementations)]\npub struct ChildContainer {\n    info: Option<ChildContainerInfo>,\n    exists: AtomicBool,\n}\n\nimpl Default for ChildContainer {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl ChildContainer {\n    pub const fn new() -> ChildContainer {\n        ChildContainer {\n            info: None,\n            exists: AtomicBool::new(false),\n        }\n    }\n\n    pub fn create(engine: Engine, name: String) -> Result<()> {\n        // SAFETY: guarded by an atomic swap\n        unsafe {\n            if !CHILD_CONTAINER.exists.swap(true, Ordering::SeqCst) {\n                CHILD_CONTAINER.info = Some(ChildContainerInfo {\n                    engine,\n                    name,\n                    timeout: NO_TIMEOUT,\n                    color_choice: ColorChoice::Never,\n                    verbosity: Verbosity::Quiet,\n                });\n                Ok(())\n            } else {\n                eyre::bail!(\"attempted to create already existing container.\");\n            }\n        }\n    }\n\n    // the static functions have been placed by the internal functions to\n    // verify the internal functions are wrapped in atomic load/stores.\n\n    pub fn exists(&self) -> bool {\n        self.exists.load(Ordering::SeqCst)\n    }\n\n    pub fn exists_static() -> bool {\n        // SAFETY: an atomic load.\n        unsafe { CHILD_CONTAINER.exists() }\n    }\n\n    // when the `docker run` command finished.\n    // the container has already exited, so no cleanup required.\n    pub fn exit(&mut self) {\n        self.exists.store(false, Ordering::SeqCst);\n    }\n\n    pub fn exit_static() {\n        // SAFETY: an atomic store.\n        unsafe {\n            CHILD_CONTAINER.exit();\n        }\n    }\n\n    // when the `docker exec` command finished.\n    pub fn finish(&mut self, is_tty: bool, msg_info: &mut MessageInfo) {\n        // relax the no-timeout and lack of output\n        // ensure we have atomic ordering\n        if self.exists() {\n            let info = self\n                .info\n                .as_mut()\n                .expect(\"since we're loaded and exist, child should not be terminated\");\n            if is_tty {\n                info.timeout = DEFAULT_TIMEOUT;\n            }\n            info.color_choice = msg_info.color_choice;\n            info.verbosity = msg_info.verbosity;\n        }\n\n        self.terminate();\n    }\n\n    pub fn finish_static(is_tty: bool, msg_info: &mut MessageInfo) {\n        // SAFETY: internally guarded by an atomic load.\n        unsafe {\n            CHILD_CONTAINER.finish(is_tty, msg_info);\n        }\n    }\n\n    // terminate the container early. leaves the struct in a valid\n    // state, so it's async safe, but so the container will not\n    // be stopped again.\n    pub fn terminate(&mut self) {\n        if self.exists.swap(false, Ordering::SeqCst) {\n            let info = self.info.as_mut().expect(\n                \"since we're loaded and exist, child should not have been terminated already\",\n            );\n            let mut msg_info = MessageInfo::new(info.color_choice, info.verbosity);\n            let container = DockerContainer::new(&info.engine, &info.name);\n            container.stop(info.timeout, &mut msg_info).ok();\n            container.remove(&mut msg_info).ok();\n\n            self.info = None;\n        }\n    }\n}\n\nimpl Drop for ChildContainer {\n    fn drop(&mut self) {\n        self.terminate();\n    }\n}\n\n#[derive(Debug)]\npub struct ContainerDataVolume<'a, 'b, 'c> {\n    pub(crate) engine: &'a Engine,\n    pub(crate) container: &'b str,\n    pub(crate) toolchain_dirs: &'c ToolchainDirectories,\n}\n\nimpl<'a, 'b, 'c> ContainerDataVolume<'a, 'b, 'c> {\n    pub const fn new(\n        engine: &'a Engine,\n        container: &'b str,\n        toolchain_dirs: &'c ToolchainDirectories,\n    ) -> Self {\n        Self {\n            engine,\n            container,\n            toolchain_dirs,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub enum VolumeId {\n    Keep(String),\n    Discard,\n}\n\nimpl VolumeId {\n    pub fn mount(&self, mount_prefix: &str) -> String {\n        match self {\n            VolumeId::Keep(ref id) => format!(\"{id}:{mount_prefix}\"),\n            VolumeId::Discard => mount_prefix.to_owned(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct DockerVolume<'a, 'b> {\n    pub(crate) engine: &'a Engine,\n    pub(crate) name: &'b str,\n}\n\nimpl<'a, 'b> DockerVolume<'a, 'b> {\n    pub const fn new(engine: &'a Engine, name: &'b str) -> Self {\n        Self { engine, name }\n    }\n\n    #[track_caller]\n    pub fn create(&self, msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n        self.engine\n            .run_and_get_status(&[\"volume\", \"create\", self.name], msg_info)\n    }\n\n    #[track_caller]\n    pub fn remove(&self, msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n        self.engine\n            .run_and_get_status(&[\"volume\", \"rm\", self.name], msg_info)\n    }\n\n    #[track_caller]\n    pub fn exists(&self, msg_info: &mut MessageInfo) -> Result<bool> {\n        self.engine\n            .run_and_get_output(&[\"volume\", \"inspect\", self.name], msg_info)\n            .map(|output| output.status.success())\n    }\n\n    #[track_caller]\n    pub fn existing(\n        engine: &Engine,\n        toolchain: &QualifiedToolchain,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Vec<String>> {\n        let list = engine\n            .run_and_get_output(\n                &[\n                    \"volume\",\n                    \"list\",\n                    \"--format\",\n                    \"{{.Name}}\",\n                    \"--filter\",\n                    &format!(\"name=^{VOLUME_PREFIX}{}\", toolchain),\n                ],\n                msg_info,\n            )?\n            .stdout()?;\n\n        if list.is_empty() {\n            Ok(vec![])\n        } else {\n            Ok(list.split('\\n').map(ToOwned::to_owned).collect())\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct DockerContainer<'a, 'b> {\n    pub(crate) engine: &'a Engine,\n    pub(crate) name: &'b str,\n}\n\nimpl<'a, 'b> DockerContainer<'a, 'b> {\n    pub const fn new(engine: &'a Engine, name: &'b str) -> Self {\n        Self { engine, name }\n    }\n\n    pub fn stop(&self, timeout: u32, msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n        self.engine.run_and_get_status(\n            &[\"stop\", self.name, \"--time\", &timeout.to_string()],\n            msg_info,\n        )\n    }\n\n    pub fn stop_default(&self, msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n        // we want a faster timeout, since this might happen in signal\n        // handler. our containers normally clean up pretty fast, it's\n        // only without a pseudo-tty that they don't.\n        self.stop(DEFAULT_TIMEOUT, msg_info)\n    }\n\n    /// if stopping a container succeeds without a timeout, this command\n    /// can fail because the container no longer exists. however, if\n    /// the container was killed, we need to cleanup the exited container.\n    /// just silence any warnings.\n    pub fn remove(&self, msg_info: &mut MessageInfo) -> Result<ExitStatus> {\n        self.engine\n            .run_and_get_output(&[\"rm\", self.name], msg_info)\n            .map(|output| output.status)\n    }\n\n    pub fn state(&self, msg_info: &mut MessageInfo) -> Result<ContainerState> {\n        let stdout = self\n            .engine\n            .command()\n            .args([\"ps\", \"-a\"])\n            .args([\"--filter\", &format!(\"name={}\", self.name)])\n            .args([\"--format\", \"{{.State}}\"])\n            .run_and_get_stdout(msg_info)?;\n        ContainerState::new(stdout.trim())\n    }\n}\n\npub(crate) fn time_to_millis(timestamp: &time::SystemTime) -> Result<u64> {\n    Ok(timestamp\n        .duration_since(time::SystemTime::UNIX_EPOCH)?\n        .as_millis() as u64)\n}\n\npub(crate) fn time_from_millis(millis: u64) -> time::SystemTime {\n    time::SystemTime::UNIX_EPOCH + time::Duration::from_millis(millis)\n}\n\npub(crate) fn now_as_millis() -> Result<u64> {\n    time_to_millis(&time::SystemTime::now())\n}\n\nconst CACHEDIR_TAG: &str = \"Signature: 8a477f597d28d172789f06886806bc55\n# This file is a cache directory tag created by cross.\n# For information about cache directory tags see https://bford.info/cachedir/\";\n\npub fn create_target_dir(path: &Path) -> Result<()> {\n    // cargo creates all paths to the target directory, and writes\n    // a cache dir tag only if the path doesn't previously exist.\n    if !path.exists() {\n        file::create_dir_all(path)?;\n        fs::OpenOptions::new()\n            .write(true)\n            .create_new(true)\n            .open(path.join(\"CACHEDIR.TAG\"))?\n            .write_all(CACHEDIR_TAG.as_bytes())?;\n    }\n    Ok(())\n}\n\nimpl Engine {\n    pub fn command(&self) -> Command {\n        let mut command = Command::new(&self.path);\n        if self.needs_remote() {\n            // if we're using podman and not podman-remote, need `--remote`.\n            command.arg(\"--remote\");\n        }\n        command\n    }\n\n    pub fn subcommand(&self, cmd: &str) -> Command {\n        let mut command = self.command();\n        command.arg(cmd);\n        command\n    }\n\n    #[track_caller]\n    pub(crate) fn run_and_get_status(\n        &self,\n        args: &[&str],\n        msg_info: &mut MessageInfo,\n    ) -> Result<ExitStatus> {\n        self.command().args(args).run_and_get_status(msg_info, true)\n    }\n\n    #[track_caller]\n    pub(crate) fn run_and_get_output(\n        &self,\n        args: &[&str],\n        msg_info: &mut MessageInfo,\n    ) -> Result<Output> {\n        self.command().args(args).run_and_get_output(msg_info)\n    }\n\n    pub fn parse_opts(value: &str) -> Result<Vec<String>> {\n        shell_words::split(value)\n            .wrap_err_with(|| format!(\"could not parse docker opts of {}\", value))\n    }\n\n    /// Register binfmt interpreters\n    pub(crate) fn register_binfmt(\n        &self,\n        target: &Target,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let cmd = if target.is_windows() {\n            // https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html\n            \"mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc && \\\n                echo ':wine:M::MZ::/usr/bin/run-detectors:' > /proc/sys/fs/binfmt_misc/register\"\n        } else {\n            \"apt-get update && apt-get install --no-install-recommends --assume-yes \\\n                binfmt-support qemu-user-static\"\n        };\n\n        let mut docker = self.subcommand(\"run\");\n        docker.add_userns();\n        docker.arg(\"--privileged\");\n        docker.arg(\"--rm\");\n        docker.arg(UBUNTU_BASE);\n        docker.args([\"sh\", \"-c\", cmd]);\n\n        docker.run(msg_info, false)\n    }\n}\n\nfn validate_env_var<'a>(\n    var: &'a str,\n    warned: &mut bool,\n    var_type: &'static str,\n    var_syntax: &'static str,\n    msg_info: &mut MessageInfo,\n) -> Result<(&'a str, Option<&'a str>)> {\n    let (key, value) = match var.split_once('=') {\n        Some((key, value)) => (key, Some(value)),\n        _ => (var, None),\n    };\n\n    if value.is_none()\n        && !*warned\n        && !var\n            .chars()\n            .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9'))\n    {\n        msg_info.warn(format_args!(\n            \"got {var_type} of \\\"{var}\\\" which is not a valid environment variable name. the proper syntax is {var_syntax}\"\n        ))?;\n        *warned = true;\n    }\n\n    if key == \"CROSS_RUNNER\" {\n        eyre::bail!(\n            \"CROSS_RUNNER environment variable name is reserved and cannot be pass through\"\n        );\n    }\n\n    Ok((key, value))\n}\n\nimpl CommandVariant {\n    pub(crate) fn safe_command(&self) -> SafeCommand {\n        SafeCommand::new(self.to_str())\n    }\n}\n\npub(crate) trait DockerCommandExt {\n    fn add_configuration_envvars(&mut self);\n    fn add_envvars(\n        &mut self,\n        options: &DockerOptions,\n        dirs: &ToolchainDirectories,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()>;\n    fn add_cwd(&mut self, paths: &DockerPaths) -> Result<()>;\n    fn add_build_command(&mut self, dirs: &ToolchainDirectories, cmd: &SafeCommand) -> &mut Self;\n    fn add_user_id(&mut self, is_rootless: bool);\n    fn add_userns(&mut self);\n    fn add_seccomp(\n        &mut self,\n        engine_type: EngineType,\n        target: &Target,\n        metadata: &CargoMetadata,\n    ) -> Result<()>;\n    fn add_mounts(\n        &mut self,\n        options: &DockerOptions,\n        paths: &DockerPaths,\n        mount_cb: impl Fn(&mut Command, &Path, &Path) -> Result<()>,\n        store_cb: impl FnMut((String, String)),\n        msg_info: &mut MessageInfo,\n    ) -> Result<()>;\n}\n\nimpl DockerCommandExt for Command {\n    fn add_configuration_envvars(&mut self) {\n        let other = &[\n            \"http_proxy\",\n            \"TERM\",\n            \"RUSTDOCFLAGS\",\n            \"RUSTFLAGS\",\n            \"BROWSER\",\n            \"HTTPS_PROXY\",\n            \"HTTP_TIMEOUT\",\n            \"https_proxy\",\n            \"QEMU_STRACE\",\n        ];\n        let cargo_prefix_skip = &[\n            \"CARGO_HOME\",\n            \"CARGO_TARGET_DIR\",\n            \"CARGO_BUILD_TARGET_DIR\",\n            \"CARGO_BUILD_RUSTC\",\n            \"CARGO_BUILD_RUSTC_WRAPPER\",\n            \"CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER\",\n            \"CARGO_BUILD_RUSTDOC\",\n        ];\n        let cross_prefix_skip = &[\n            \"CROSS_RUNNER\",\n            \"CROSS_RUSTC_MAJOR_VERSION\",\n            \"CROSS_RUSTC_MINOR_VERSION\",\n            \"CROSS_RUSTC_PATCH_VERSION\",\n        ];\n        let is_passthrough = |key: &str| -> bool {\n            other.contains(&key)\n                || key.starts_with(\"CARGO_\") && !cargo_prefix_skip.contains(&key)\n                || key.starts_with(\"CROSS_\") && !cross_prefix_skip.contains(&key)\n        };\n\n        // also need to accept any additional flags used to configure\n        // cargo or cross, but only pass what's actually present.\n        for (key, _) in env::vars() {\n            if is_passthrough(&key) {\n                self.args([\"-e\", &key]);\n            }\n        }\n    }\n\n    fn add_envvars(\n        &mut self,\n        options: &DockerOptions,\n        dirs: &ToolchainDirectories,\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let mut warned = false;\n        for ref var in options\n            .config\n            .env_passthrough(&options.target)\n            .unwrap_or_default()\n        {\n            validate_env_var(\n                var,\n                &mut warned,\n                \"environment variable\",\n                \"`passthrough = [\\\"ENVVAR=value\\\"]`\",\n                msg_info,\n            )?;\n\n            // Only specifying the environment variable name in the \"-e\"\n            // flag forwards the value from the parent shell\n            self.args([\"-e\", var]);\n        }\n\n        let runner = options.config.runner(&options.target);\n        let cross_runner = format!(\"CROSS_RUNNER={}\", runner.unwrap_or_default());\n        self.args([\"-e\", &format!(\"CARGO_HOME={}\", dirs.cargo_mount_path())])\n            .args([\n                \"-e\",\n                &format!(\"CROSS_RUST_SYSROOT={}\", dirs.sysroot_mount_path()),\n            ])\n            .args([\"-e\", \"CARGO_TARGET_DIR=/target\"])\n            .args([\"-e\", &cross_runner]);\n        if options.command_variant.uses_zig() {\n            // otherwise, zig has a permission error trying to create the cache\n            self.args([\"-e\", \"XDG_CACHE_HOME=/target/.zig-cache\"]);\n        }\n        self.add_configuration_envvars();\n\n        if let Some(username) = id::username().wrap_err(\"could not get username\")? {\n            self.args([\"-e\", &format!(\"USER={username}\")]);\n        }\n\n        if let Ok(value) = env::var(\"CROSS_CONTAINER_OPTS\") {\n            if env::var(\"DOCKER_OPTS\").is_ok() {\n                msg_info.warn(\"using both `CROSS_CONTAINER_OPTS` and `DOCKER_OPTS`.\")?;\n            }\n            self.args(&Engine::parse_opts(&value)?);\n        } else if let Ok(value) = env::var(\"DOCKER_OPTS\") {\n            // FIXME: remove this when we deprecate DOCKER_OPTS.\n            self.args(&Engine::parse_opts(&value)?);\n        };\n\n        let (major, minor, patch) = match options.rustc_version.as_ref() {\n            Some(version) => (version.major, version.minor, version.patch),\n            // no toolchain version available, always provide the oldest\n            // compiler available. this isn't a major issue because\n            // linking with libgcc will not include symbols found in\n            // the builtins.\n            None => (1, 0, 0),\n        };\n        self.args([\"-e\", &format!(\"CROSS_RUSTC_MAJOR_VERSION={}\", major)]);\n        self.args([\"-e\", &format!(\"CROSS_RUSTC_MINOR_VERSION={}\", minor)]);\n        self.args([\"-e\", &format!(\"CROSS_RUSTC_PATCH_VERSION={}\", patch)]);\n\n        Ok(())\n    }\n\n    fn add_cwd(&mut self, paths: &DockerPaths) -> Result<()> {\n        self.args([\"-w\", paths.mount_cwd()]);\n\n        Ok(())\n    }\n\n    fn add_build_command(&mut self, dirs: &ToolchainDirectories, cmd: &SafeCommand) -> &mut Self {\n        let build_command = format!(\n            \"PATH=\\\"$PATH\\\":\\\"{}/bin\\\" {:?}\",\n            dirs.sysroot_mount_path(),\n            cmd\n        );\n        self.args([\"sh\", \"-c\", &build_command])\n    }\n\n    fn add_user_id(&mut self, is_rootless: bool) {\n        // by default, docker runs as root so we need to specify the user\n        // so the resulting file permissions are for the current user.\n        // since we can have rootless docker, we provide an override.\n        if !is_rootless {\n            self.args([\"--user\", &format!(\"{}:{}\", user_id(), group_id(),)]);\n        }\n    }\n\n    fn add_userns(&mut self) {\n        let userns = match env::var(\"CROSS_CONTAINER_USER_NAMESPACE\").ok().as_deref() {\n            Some(\"none\") => None,\n            None | Some(\"auto\") => Some(\"host\".to_owned()),\n            Some(ns) => Some(ns.to_owned()),\n        };\n        if let Some(ns) = userns {\n            self.args([\"--userns\", &ns]);\n        }\n    }\n\n    #[allow(unused_mut, clippy::let_and_return)]\n    fn add_seccomp(\n        &mut self,\n        engine_type: EngineType,\n        target: &Target,\n        metadata: &CargoMetadata,\n    ) -> Result<()> {\n        // secured profile based off the docker documentation for denied syscalls:\n        // https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile\n        // note that we've allow listed `clone` and `clone3`, which is necessary\n        // to fork the process, and which podman allows by default.\n        const SECCOMP: &str = include_str!(\"seccomp.json\");\n\n        // docker uses seccomp now on all installations\n        if target.needs_docker_seccomp() {\n            let seccomp = if engine_type.is_docker() && cfg!(target_os = \"windows\") {\n                // docker on windows fails due to a bug in reading the profile\n                // https://github.com/docker/for-win/issues/12760\n                \"unconfined\".to_owned()\n            } else {\n                #[allow(unused_mut)] // target_os = \"windows\"\n                let mut path = metadata\n                    .target_directory\n                    .join(target.triple())\n                    .join(\"seccomp.json\");\n                if !path.exists() {\n                    write_file(&path, false)?.write_all(SECCOMP.as_bytes())?;\n                }\n                let mut path_string = path.to_utf8()?.to_owned();\n                #[cfg(target_os = \"windows\")]\n                if matches!(engine_type, EngineType::Podman | EngineType::PodmanRemote) {\n                    // podman weirdly expects a WSL path here, and fails otherwise\n                    path_string = path.as_posix_absolute()?;\n                }\n                path_string\n            };\n\n            self.args([\"--security-opt\", &format!(\"seccomp={}\", seccomp)]);\n        }\n\n        Ok(())\n    }\n\n    fn add_mounts(\n        &mut self,\n        options: &DockerOptions,\n        paths: &DockerPaths,\n        mount_cb: impl Fn(&mut Command, &Path, &Path) -> Result<()>,\n        mut store_cb: impl FnMut((String, String)),\n        msg_info: &mut MessageInfo,\n    ) -> Result<()> {\n        let mut warned = false;\n        for ref var in options\n            .config\n            .env_volumes(&options.target)\n            .unwrap_or_default()\n        {\n            let (var, value) = validate_env_var(\n                var,\n                &mut warned,\n                \"volume\",\n                \"`volumes = [\\\"ENVVAR=/path/to/directory\\\"]`\",\n                msg_info,\n            )?;\n            let value = match value {\n                Some(v) => Ok(v.to_owned()),\n                None => env::var(var),\n            };\n\n            // NOTE: we use canonical paths on the host, since it's unambiguous.\n            // however, for the mounted paths, we use the same path as was\n            // provided. this avoids canonicalizing symlinks which then causes\n            // the mounted path to differ from the path expected on the host.\n            // for example, if `/tmp` is a symlink to `/private/tmp`, canonicalizing\n            // it would lead to us mounting `/tmp/process` to `/private/tmp/process`,\n            // which would cause any code relying on `/tmp/process` to break.\n\n            if let Ok(val) = value {\n                let canonical_path = file::canonicalize(&val)?;\n                let host_path = paths.mount_finder.find_path(&canonical_path, true)?;\n                let mount_path = Path::new(&val).as_posix_absolute()?;\n                mount_cb(self, host_path.as_ref(), mount_path.as_ref())?;\n                self.args([\"-e\", &format!(\"{}={}\", var, mount_path)]);\n                store_cb((val, mount_path));\n            }\n        }\n\n        for path in paths.workspace_dependencies() {\n            // NOTE: we use canonical paths here since cargo metadata\n            // always canonicalizes paths, so these should be relative\n            // to the mounted project directory.\n            let canonical_path = file::canonicalize(path)?;\n            let host_path = paths.mount_finder.find_path(&canonical_path, true)?;\n            let mount_path = path.as_posix_absolute()?;\n            mount_cb(self, host_path.as_ref(), mount_path.as_ref())?;\n            store_cb((path.to_utf8()?.to_owned(), mount_path));\n        }\n\n        Ok(())\n    }\n}\n\npub(crate) fn user_id() -> String {\n    env::var(\"CROSS_CONTAINER_UID\").unwrap_or_else(|_| id::user().to_string())\n}\n\npub(crate) fn group_id() -> String {\n    env::var(\"CROSS_CONTAINER_GID\").unwrap_or_else(|_| id::group().to_string())\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum GetImageError {\n    #[error(\n        \"`cross` does not provide a Docker image for target {0}, \\\n    specify a custom image in `Cross.toml`.\"\n    )]\n    NoCompatibleImages(String),\n    #[error(\"platforms for provided image `{0}` are not specified, this is a bug in cross\")]\n    SpecifiedImageNoPlatform(String),\n    #[error(transparent)]\n    MultipleImages(eyre::Report),\n    #[error(transparent)]\n    Other(eyre::Report),\n}\n\nfn get_target_name(target: &Target, uses_zig: bool) -> &str {\n    if uses_zig {\n        \"zig\"\n    } else {\n        target.triple()\n    }\n}\n\nfn get_user_image(\n    config: &Config,\n    target: &Target,\n    uses_zig: bool,\n) -> Result<Option<PossibleImage>, GetImageError> {\n    let mut image = if uses_zig {\n        config.zig_image(target)\n    } else {\n        config.image(target)\n    }\n    .map_err(GetImageError::Other)?;\n\n    if let Some(image) = &mut image {\n        let target_name = get_target_name(target, uses_zig);\n        image.reference.ensure_qualified(target_name);\n    }\n\n    Ok(image)\n}\n\nfn get_provided_images_for_target(\n    target_name: &str,\n) -> Result<Vec<&'static ProvidedImage>, GetImageError> {\n    let compatible = PROVIDED_IMAGES\n        .iter()\n        .filter(|p| p.name == target_name)\n        .collect::<Vec<_>>();\n\n    if compatible.is_empty() {\n        return Err(GetImageError::NoCompatibleImages(target_name.to_owned()));\n    }\n\n    Ok(compatible)\n}\n\n/// Simpler version of [get_image]\npub fn get_image_name(\n    config: &Config,\n    target: &Target,\n    uses_zig: bool,\n) -> Result<String, GetImageError> {\n    if let Some(image) = get_user_image(config, target, uses_zig)? {\n        return Ok(image.reference.get().to_owned());\n    }\n\n    let target_name = get_target_name(target, uses_zig);\n    let compatible = get_provided_images_for_target(target_name)?;\n    Ok(compatible\n        .first()\n        .expect(\"should not be empty\")\n        .default_image_name())\n}\n\npub fn get_image(\n    config: &Config,\n    target: &Target,\n    uses_zig: bool,\n) -> Result<PossibleImage, GetImageError> {\n    if let Some(image) = get_user_image(config, target, uses_zig)? {\n        return Ok(image);\n    }\n\n    let target_name = get_target_name(target, uses_zig);\n    let compatible = get_provided_images_for_target(target_name)?;\n    let pick = if let [first] = compatible[..] {\n        // If only one match, use that\n        first\n    } else if compatible\n        .iter()\n        .filter(|provided| provided.sub.is_none())\n        .count()\n        == 1\n    {\n        // if multiple matches, but only one is not a sub-target, pick that one\n        compatible\n            .iter()\n            .find(|provided| provided.sub.is_none())\n            .expect(\"should exists at least one non-sub image in list\")\n    } else {\n        // if there's multiple targets and no option can be chosen, bail\n        return Err(GetImageError::MultipleImages(\n            eyre::eyre!(\n                \"`cross` provides multiple images for target {target_name}, \\\n               specify toolchain in `Cross.toml`.\"\n            )\n            .with_note(|| {\n                format!(\n                    \"candidates: {}\",\n                    compatible\n                        .iter()\n                        .map(|provided| format!(\"\\\"{}\\\"\", provided.default_image_name()))\n                        .collect::<Vec<_>>()\n                        .join(\", \")\n                )\n            }),\n        ));\n    };\n\n    let image_name = pick.default_image_name();\n    if pick.platforms.is_empty() {\n        return Err(GetImageError::SpecifiedImageNoPlatform(image_name));\n    }\n\n    let mut image: PossibleImage = image_name.into();\n    image.toolchain = pick.platforms.to_vec();\n    Ok(image)\n}\n\nfn docker_inspect_self_mountinfo(engine: &Engine, msg_info: &mut MessageInfo) -> Result<String> {\n    if cfg!(not(target_os = \"linux\")) {\n        eyre::bail!(\"/proc/self/mountinfo is unavailable when target_os != linux\");\n    }\n\n    // The ID for the current Docker container might be in mountinfo,\n    // somewhere in a mount root. Full IDs are 64-char hexadecimal\n    // strings, so the first matching path segment in a mount root\n    // containing /docker/ is likely to be what we're looking for. See:\n    // https://www.kernel.org/doc/Documentation/filesystems/proc.txt\n    // https://community.toradex.com/t/15240/4\n    let mountinfo = file::read(\"/proc/self/mountinfo\")?;\n    let container_id = mountinfo\n        .lines()\n        .filter_map(|s| s.split(' ').nth(3))\n        .filter(|s| s.contains(\"/docker/\"))\n        .flat_map(|s| s.split('/'))\n        .find(|s| s.len() == 64 && s.as_bytes().iter().all(u8::is_ascii_hexdigit))\n        .ok_or_else(|| eyre::eyre!(\"couldn't find container id in mountinfo\"))?;\n\n    engine\n        .subcommand(\"inspect\")\n        .arg(container_id)\n        .run_and_get_stdout(msg_info)\n}\n\nfn docker_inspect_self(engine: &Engine, msg_info: &mut MessageInfo) -> Result<String> {\n    // Try to find the container ID by looking at HOSTNAME, and fallback to\n    // parsing `/proc/self/mountinfo` if HOSTNAME is unset or if there's no\n    // container that matches it (necessary e.g. when the container uses\n    // `--network=host`, which is act's default, see issue #1321).\n    // If `docker inspect` fails with unexpected output, skip the fallback\n    // and fail instantly.\n    if let Ok(hostname) = env::var(\"HOSTNAME\") {\n        let mut command = engine.subcommand(\"inspect\");\n        command.arg(hostname);\n        let out = command.run_and_get_output(msg_info)?;\n\n        if out.status.success() {\n            Ok(out.stdout()?)\n        } else {\n            let val = serde_json::from_slice::<serde_json::Value>(&out.stdout);\n            if let Ok(val) = val {\n                if let Some(array) = val.as_array() {\n                    // `docker inspect` completed but returned an empty array, most\n                    // likely indicating that the hostname isn't a valid container ID.\n                    if array.is_empty() {\n                        msg_info.debug(\"docker inspect found no containers matching HOSTNAME, retrying using mountinfo\")?;\n                        return docker_inspect_self_mountinfo(engine, msg_info);\n                    }\n                }\n            }\n\n            let report = command\n                .status_result(msg_info, out.status, Some(&out))\n                .expect_err(\"we know the command failed\")\n                .to_section_report();\n            Err(report)\n        }\n    } else {\n        msg_info.debug(\"HOSTNAME environment variable is unset\")?;\n        docker_inspect_self_mountinfo(engine, msg_info)\n    }\n}\n\nfn docker_read_mount_paths(\n    engine: &Engine,\n    msg_info: &mut MessageInfo,\n) -> Result<Vec<MountDetail>> {\n    let output = docker_inspect_self(engine, msg_info)?;\n    let info = serde_json::from_str(&output).wrap_err(\"failed to parse docker inspect output\")?;\n    dockerinfo_parse_mounts(&info)\n}\n\nfn dockerinfo_parse_mounts(info: &serde_json::Value) -> Result<Vec<MountDetail>> {\n    let mut mounts = dockerinfo_parse_user_mounts(info);\n    let root_info = dockerinfo_parse_root_mount_path(info)?;\n    mounts.push(root_info);\n    Ok(mounts)\n}\n\nfn dockerinfo_parse_root_mount_path(info: &serde_json::Value) -> Result<MountDetail> {\n    let driver_name = info\n        .pointer(\"/0/GraphDriver/Name\")\n        .and_then(|v| v.as_str())\n        .ok_or_else(|| eyre::eyre!(\"no driver name found\"))?;\n\n    if driver_name.to_lowercase().contains(\"overlay\") {\n        let path = info\n            .pointer(\"/0/GraphDriver/Data/MergedDir\")\n            .and_then(|v| v.as_str())\n            .ok_or_else(|| eyre::eyre!(\"No merge directory found\"))?;\n\n        Ok(MountDetail {\n            source: PathBuf::from(&path),\n            destination: PathBuf::from(\"/\"),\n        })\n    } else {\n        eyre::bail!(\"want driver overlay2, got {driver_name}\")\n    }\n}\n\nfn dockerinfo_parse_user_mounts(info: &serde_json::Value) -> Vec<MountDetail> {\n    info.pointer(\"/0/Mounts\")\n        .and_then(|v| v.as_array())\n        .map_or_else(Vec::new, |v| {\n            let make_path = |v: &serde_json::Value| {\n                PathBuf::from(&v.as_str().expect(\"docker mount should be defined\"))\n            };\n            let mut mounts = vec![];\n            for details in v {\n                let source = make_path(&details[\"Source\"]);\n                let destination = make_path(&details[\"Destination\"]);\n                mounts.push(MountDetail {\n                    source,\n                    destination,\n                });\n            }\n            mounts\n        })\n}\n\n#[derive(Debug, Default)]\npub struct MountFinder {\n    mounts: Vec<MountDetail>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\nstruct MountDetail {\n    source: PathBuf,\n    destination: PathBuf,\n}\n\nimpl MountFinder {\n    fn new(mounts: Vec<MountDetail>) -> MountFinder {\n        // sort by length (reverse), to give mounts with more path components a higher priority;\n        let mut mounts = mounts;\n        mounts.sort_by(|a, b| {\n            let la = a.destination.as_os_str().len();\n            let lb = b.destination.as_os_str().len();\n            la.cmp(&lb).reverse()\n        });\n        MountFinder { mounts }\n    }\n\n    pub fn create(engine: &Engine, msg_info: &mut MessageInfo) -> Result<MountFinder> {\n        Ok(if engine.in_docker {\n            MountFinder::new(docker_read_mount_paths(engine, msg_info)?)\n        } else {\n            MountFinder::default()\n        })\n    }\n\n    pub fn find_mount_path(&self, path: impl AsRef<Path>) -> PathBuf {\n        let path = path.as_ref();\n\n        for info in &self.mounts {\n            if let Ok(stripped) = path.strip_prefix(&info.destination) {\n                return info.source.join(stripped);\n            }\n        }\n\n        path.to_path_buf()\n    }\n\n    fn find_path(&self, path: &Path, host: bool) -> Result<String> {\n        if cfg!(target_os = \"windows\") && host {\n            // On Windows, we can not mount the directory name directly.\n            // Instead, we convert the path to a linux compatible path.\n            path.to_utf8().map(ToOwned::to_owned)\n        } else if cfg!(target_os = \"windows\") {\n            path.as_posix_absolute()\n        } else {\n            self.find_mount_path(path).as_posix_absolute()\n        }\n    }\n}\n\n/// Short hash for identifiers with minimal risk of collision.\npub const PATH_HASH_SHORT: usize = 5;\n\n/// Longer hash to minimize risk of random collisions\n/// Collision chance is ~10^-6\npub const PATH_HASH_UNIQUE: usize = 10;\n\nfn path_digest(path: &Path) -> Result<const_sha1::Digest> {\n    Ok(const_sha1::sha1(path.to_utf8()?.as_bytes()))\n}\n\npub fn path_hash(path: &Path, count: usize) -> Result<String> {\n    Ok(path_digest(path)?\n        .to_string()\n        .get(..count)\n        .unwrap_or_else(|| panic!(\"sha1 is expected to be at least {count} characters long\"))\n        .to_owned())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::collections::HashMap;\n\n    use super::*;\n    use crate::{config::Environment, id};\n\n    #[cfg(not(target_os = \"windows\"))]\n    use crate::file::PathExt;\n\n    #[test]\n    fn test_docker_user_id() {\n        let rootful = format!(\"\\\"engine\\\" \\\"--user\\\" \\\"{}:{}\\\"\", id::user(), id::group());\n        let rootless = \"\\\"engine\\\"\".to_owned();\n\n        let test = |noroot, expected| {\n            let mut cmd = Command::new(\"engine\");\n            cmd.add_user_id(noroot);\n            assert_eq!(expected, &format!(\"{cmd:?}\"));\n        };\n\n        test(false, &rootful);\n        test(true, &rootless);\n    }\n\n    #[test]\n    fn test_docker_userns() {\n        let var = \"CROSS_CONTAINER_USER_NAMESPACE\";\n        let old = env::var(var);\n        env::remove_var(var);\n\n        let host = \"\\\"engine\\\" \\\"--userns\\\" \\\"host\\\"\".to_owned();\n        let custom = \"\\\"engine\\\" \\\"--userns\\\" \\\"custom\\\"\".to_owned();\n        let none = \"\\\"engine\\\"\".to_owned();\n\n        let test = |expected| {\n            let mut cmd = Command::new(\"engine\");\n            cmd.add_userns();\n            assert_eq!(expected, &format!(\"{cmd:?}\"));\n        };\n        test(&host);\n\n        env::set_var(var, \"auto\");\n        test(&host);\n\n        env::set_var(var, \"none\");\n        test(&none);\n\n        env::set_var(var, \"host\");\n        test(&host);\n\n        env::set_var(var, \"custom\");\n        test(&custom);\n\n        match old {\n            Ok(v) => env::set_var(var, v),\n            Err(_) => env::remove_var(var),\n        }\n    }\n\n    #[test]\n    fn test_tag_only_image() -> Result<()> {\n        let target: Target = TargetTriple::X86_64UnknownLinuxGnu.into();\n        let test = |map, expected_ver: &str, expected_ver_zig: &str| -> Result<()> {\n            let env = Environment::new(Some(map));\n            let config = Config::new_with(None, env);\n            for (uses_zig, expected_ver) in [(false, expected_ver), (true, expected_ver_zig)] {\n                let expected_image_target = if uses_zig {\n                    \"zig\"\n                } else {\n                    \"x86_64-unknown-linux-gnu\"\n                };\n                let expected = format!(\"{CROSS_IMAGE}/{expected_image_target}{expected_ver}\");\n\n                let image = get_image(&config, &target, uses_zig)?;\n                assert_eq!(image.reference.get(), expected);\n                let image_name = get_image_name(&config, &target, uses_zig)?;\n                assert_eq!(image_name, expected);\n            }\n            Ok(())\n        };\n\n        let default_ver = format!(\":{DEFAULT_IMAGE_VERSION}\");\n        let mut map = HashMap::new();\n        test(map.clone(), &default_ver, &default_ver)?;\n\n        map.insert(\"CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE\", \"-centos\");\n        test(map.clone(), &format!(\"{default_ver}-centos\"), &default_ver)?;\n\n        map.insert(\"CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE\", \":edge\");\n        test(map.clone(), \":edge\", &default_ver)?;\n\n        map.insert(\n            \"CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_ZIG_IMAGE\",\n            \"@sha256:foobar\",\n        );\n        test(map.clone(), \":edge\", \"@sha256:foobar\")?;\n\n        map.remove(\"CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE\");\n        test(map.clone(), &default_ver, \"@sha256:foobar\")?;\n\n        Ok(())\n    }\n\n    mod directories {\n        use super::*;\n        use crate::cargo::cargo_metadata_with_args;\n        use crate::rustc::{self, VersionMetaExt};\n        use crate::temp;\n\n        fn unset_env() -> Vec<(&'static str, Option<String>)> {\n            let mut result = vec![];\n            let envvars = [\"CARGO_HOME\", \"NIX_STORE\"];\n            for var in envvars {\n                result.push((var, env::var(var).ok()));\n                env::remove_var(var);\n            }\n\n            result\n        }\n\n        fn reset_env(vars: Vec<(&'static str, Option<String>)>) {\n            for (var, value) in vars {\n                if let Some(value) = value {\n                    env::set_var(var, value);\n                }\n            }\n        }\n\n        fn create_engine(msg_info: &mut MessageInfo) -> Result<Engine> {\n            Engine::from_path(get_container_engine()?, None, Some(false), msg_info)\n        }\n\n        fn cargo_metadata(subdir: bool, msg_info: &mut MessageInfo) -> Result<CargoMetadata> {\n            let mut metadata = cargo_metadata_with_args(\n                Some(Path::new(env!(\"CARGO_MANIFEST_DIR\"))),\n                None,\n                msg_info,\n            )?\n            .ok_or_else(|| eyre::eyre!(\"could not find cross workspace\"))?;\n\n            let root = match subdir {\n                true => get_cwd()?.join(\"member\"),\n                false => get_cwd()?\n                    .parent()\n                    .expect(\"current directory should have a parent\")\n                    .to_path_buf(),\n            };\n            fs::create_dir_all(&root)?;\n            metadata.workspace_root = root;\n            metadata.target_directory = metadata.workspace_root.join(\"target\");\n\n            Ok(metadata)\n        }\n\n        fn home() -> Result<PathBuf> {\n            home::home_dir().ok_or_else(|| eyre::eyre!(\"could not find home directory\"))\n        }\n\n        fn get_cwd() -> Result<PathBuf> {\n            // we need this directory to exist for Windows\n            let path = temp::dir()?.join(\"Documents\").join(\"package\");\n            fs::create_dir_all(&path)?;\n            Ok(path)\n        }\n\n        fn get_toolchain() -> Result<QualifiedToolchain> {\n            let host_version_meta = rustc::version_meta()?;\n            let host = host_version_meta.host();\n            let image_platform =\n                crate::docker::ImagePlatform::from_const_target(host.triple().into());\n            let sysroot = home()?\n                .join(\".rustup\")\n                .join(\"toolchains\")\n                .join(host.triple());\n            Ok(QualifiedToolchain::new(\n                \"stable\",\n                &None,\n                &image_platform,\n                &sysroot,\n                false,\n            ))\n        }\n\n        fn get_directories(\n            metadata: CargoMetadata,\n            mount_finder: &MountFinder,\n        ) -> Result<(Directories, CargoMetadata)> {\n            let cwd = get_cwd()?;\n            let toolchain = get_toolchain()?;\n            Directories::assemble(mount_finder, metadata, &cwd, toolchain)\n        }\n\n        #[track_caller]\n        fn paths_equal(x: &Path, y: &Path) -> Result<()> {\n            assert_eq!(x.as_posix_absolute()?, y.as_posix_absolute()?);\n            Ok(())\n        }\n\n        #[test]\n        #[cfg_attr(cross_sandboxed, ignore)]\n        fn test_host() -> Result<()> {\n            let vars = unset_env();\n            let mount_finder = MountFinder::new(vec![]);\n            let metadata = cargo_metadata(false, &mut MessageInfo::default())?;\n            let (directories, metadata) = get_directories(metadata, &mount_finder)?;\n            let toolchain_dirs = directories.toolchain_directories();\n            let package_dirs = directories.package_directories();\n            paths_equal(toolchain_dirs.cargo(), &home()?.join(\".cargo\"))?;\n            paths_equal(package_dirs.host_root(), &metadata.workspace_root)?;\n            assert_eq!(\n                package_dirs.mount_root(),\n                &metadata.workspace_root.as_posix_absolute()?\n            );\n            assert_eq!(package_dirs.mount_cwd(), &get_cwd()?.as_posix_absolute()?);\n\n            reset_env(vars);\n            Ok(())\n        }\n\n        #[test]\n        #[cfg_attr(not(target_os = \"linux\"), ignore)]\n        fn test_docker_in_docker() -> Result<()> {\n            let vars = unset_env();\n\n            let mut msg_info = MessageInfo::default();\n            let engine = create_engine(&mut msg_info);\n            if engine.is_err() {\n                eprintln!(\"could not get container engine\");\n                reset_env(vars);\n                return Ok(());\n            }\n            let engine = engine.unwrap();\n            if !engine.in_docker {\n                eprintln!(\"not in docker\");\n                reset_env(vars);\n                return Ok(());\n            }\n            let output = docker_inspect_self(&engine, &mut msg_info);\n            if output.is_err() {\n                eprintln!(\"inspect failed\");\n                reset_env(vars);\n                return Ok(());\n            }\n\n            let mount_finder = MountFinder::create(&engine, &mut msg_info)?;\n            let metadata = cargo_metadata(true, &mut msg_info)?;\n            let (directories, _) = get_directories(metadata, &mount_finder)?;\n            let toolchain_dirs = directories.toolchain_directories();\n            let package_dirs = directories.package_directories();\n            let mount_finder = MountFinder::new(docker_read_mount_paths(&engine, &mut msg_info)?);\n            let mount_path = |p| mount_finder.find_mount_path(p);\n\n            paths_equal(toolchain_dirs.cargo(), &mount_path(home()?.join(\".cargo\")))?;\n            paths_equal(package_dirs.host_root(), &get_cwd()?)?;\n            assert_eq!(package_dirs.mount_root(), &get_cwd()?.as_posix_absolute()?);\n            assert_eq!(package_dirs.mount_cwd(), &get_cwd()?.as_posix_absolute()?);\n\n            reset_env(vars);\n            Ok(())\n        }\n    }\n\n    mod mount_finder {\n        use super::*;\n\n        #[test]\n        fn test_default_finder_returns_original() {\n            let finder = MountFinder::default();\n            assert_eq!(\n                PathBuf::from(\"/test/path\"),\n                finder.find_mount_path(\"/test/path\"),\n            );\n        }\n\n        #[test]\n        fn test_longest_destination_path_wins() {\n            let finder = MountFinder::new(vec![\n                MountDetail {\n                    source: PathBuf::from(\"/project/path\"),\n                    destination: PathBuf::from(\"/project\"),\n                },\n                MountDetail {\n                    source: PathBuf::from(\"/target/path\"),\n                    destination: PathBuf::from(\"/project/target\"),\n                },\n            ]);\n            assert_eq!(\n                PathBuf::from(\"/target/path/test\"),\n                finder.find_mount_path(\"/project/target/test\")\n            );\n        }\n\n        #[test]\n        fn test_adjust_multiple_paths() {\n            let finder = MountFinder::new(vec![\n                MountDetail {\n                    source: PathBuf::from(\"/var/lib/docker/overlay2/container-id/merged\"),\n                    destination: PathBuf::from(\"/\"),\n                },\n                MountDetail {\n                    source: PathBuf::from(\"/home/project/path\"),\n                    destination: PathBuf::from(\"/project\"),\n                },\n            ]);\n            assert_eq!(\n                PathBuf::from(\"/var/lib/docker/overlay2/container-id/merged/container/path\"),\n                finder.find_mount_path(\"/container/path\")\n            );\n            assert_eq!(\n                PathBuf::from(\"/home/project/path\"),\n                finder.find_mount_path(\"/project\")\n            );\n            assert_eq!(\n                PathBuf::from(\"/home/project/path/target\"),\n                finder.find_mount_path(\"/project/target\")\n            );\n        }\n    }\n\n    mod parse_docker_inspect {\n        use super::*;\n        use serde_json::json;\n\n        #[test]\n        fn test_parse_container_root() {\n            let actual = dockerinfo_parse_root_mount_path(&json!([{\n                \"GraphDriver\": {\n                    \"Data\": {\n                        \"LowerDir\": \"/var/lib/docker/overlay2/f107af83b37bc0a182d3d2661f3d84684f0fffa1a243566b338a388d5e54bef4-init/diff:/var/lib/docker/overlay2/dfe81d459bbefada7aa897a9d05107a77145b0d4f918855f171ee85789ab04a0/diff:/var/lib/docker/overlay2/1f704696915c75cd081a33797ecc66513f9a7a3ffab42d01a3f17c12c8e2dc4c/diff:/var/lib/docker/overlay2/0a4f6cb88f4ace1471442f9053487a6392c90d2c6e206283d20976ba79b38a46/diff:/var/lib/docker/overlay2/1ee3464056f9cdc968fac8427b04e37ec96b108c5050812997fa83498f2499d1/diff:/var/lib/docker/overlay2/0ec5a47f1854c0f5cfe0e3f395b355b5a8bb10f6e622710ce95b96752625f874/diff:/var/lib/docker/overlay2/f24c8ad76303838b49043d17bf2423fe640836fd9562d387143e68004f8afba0/diff:/var/lib/docker/overlay2/462f89d5a0906805a6f2eec48880ed1e48256193ed506da95414448d435db2b7/diff\",\n                        \"MergedDir\": \"/var/lib/docker/overlay2/f107af83b37bc0a182d3d2661f3d84684f0fffa1a243566b338a388d5e54bef4/merged\",\n                        \"UpperDir\": \"/var/lib/docker/overlay2/f107af83b37bc0a182d3d2661f3d84684f0fffa1a243566b338a388d5e54bef4/diff\",\n                        \"WorkDir\": \"/var/lib/docker/overlay2/f107af83b37bc0a182d3d2661f3d84684f0fffa1a243566b338a388d5e54bef4/work\"\n                    },\n                    \"Name\": \"overlay2\"\n                },\n            }])).unwrap();\n            let want = MountDetail {\n                source: PathBuf::from(\"/var/lib/docker/overlay2/f107af83b37bc0a182d3d2661f3d84684f0fffa1a243566b338a388d5e54bef4/merged\"),\n                destination: PathBuf::from(\"/\"),\n            };\n            assert_eq!(want, actual);\n        }\n\n        #[test]\n        fn test_parse_empty_user_mounts() {\n            let actual = dockerinfo_parse_user_mounts(&json!([{\n                \"Mounts\": [],\n            }]));\n            assert_eq!(Vec::<MountDetail>::new(), actual);\n        }\n\n        #[test]\n        fn test_parse_missing_user_moutns() {\n            let actual = dockerinfo_parse_user_mounts(&json!([{\n                \"Id\": \"test\",\n            }]));\n            assert_eq!(Vec::<MountDetail>::new(), actual);\n        }\n    }\n}\n"
  },
  {
    "path": "src/errors.rs",
    "content": "use crate::docker;\nuse crate::temp;\n\nuse std::sync::atomic::{AtomicBool, Ordering};\n\npub use color_eyre::Section;\npub use eyre::Context;\npub use eyre::Result;\n\npub static TERMINATED: AtomicBool = AtomicBool::new(false);\n\npub fn install_panic_hook() -> Result<()> {\n    let is_dev = !crate::commit_info().is_empty() || std::env::var(\"CROSS_DEBUG\").is_ok();\n    color_eyre::config::HookBuilder::new()\n        .display_env_section(false)\n        .display_location_section(is_dev)\n        .install()\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\nunsafe fn termination_handler() {\n    // we can't warn the user here, since locks aren't signal-safe.\n    // we can delete files, since fdopendir is thread-safe, and\n    // `openat`, `unlinkat`, and `lstat` are signal-safe.\n    //  https://man7.org/linux/man-pages/man7/signal-safety.7.html\n    if !TERMINATED.swap(true, Ordering::SeqCst) && temp::has_tempfiles() {\n        temp::clean();\n    }\n\n    // tl;dr this is a long explanation to say this is thread-safe.\n    // due to the risk for UB with this code, it's important we get\n    // this right.\n    //\n    // this code is wrapped in a sequentially-consistent ordering\n    // for strong guarantees about signal safety. since all atomics\n    // are guaranteed to be lock free, and must ensure consistent swaps\n    // among all threads, this means that we should only ever have one\n    // entry into the drop, which makes the external commands safe.\n    //\n    // to quote the reference on signal safety for linux:\n    // > In general, a function is async-signal-safe either because it\n    // > is reentrant or because it is atomic with respect to signals\n    // > (i.e., its execution can't be interrupted by a signal handler).\n    //\n    // https://man7.org/linux/man-pages/man7/signal-safety.7.html\n    //\n    // our operations are atomic, and this is the generally-recommended\n    // approach for signal-safe functions that modify global state (note\n    // that this is C, but the same rules apply except volatile vars):\n    // https://wiki.sei.cmu.edu/confluence/display/c/SIG31-C.+Do+not+access+shared+objects+in+signal+handlers\n    //\n    // even if the execution of the child process was interrupted by a\n    // signal handler, it's an external process that doesn't modify the\n    // environment variables of the parent, nor is it likely to lock\n    // (except on windows). therefore, for most cases, this code inside\n    // the atomic lock guard will still be lock-free, and therefore async\n    // signal-safe. in general, the implementations for spawning a child\n    // process in rust have the following logic:\n    // 1. get a rw-lock for the environment variables, and read them.\n    // 2. exec the child process\n    //\n    // the rw-lock allows any number of readers, which since we're not\n    // writing any environment variables, should be async-signal safe:\n    // it won't deadlock. it could technically lock if we had something\n    // writing environment variables to a child process, and the execution\n    // was multi-threaded, but we simply don't do that.\n    //\n    // for spawning the child process, on unix, the spawn is done via\n    // `posix_spawnp`, which is async-signal safe on linux, although\n    // it is not guaranteed to be async-signal safe on POSIX in general:\n    //      https://bugzilla.kernel.org/show_bug.cgi?id=25292\n    //\n    // even for POSIX, it's basically thread-safe for our invocations,\n    // since we do not modify the environment for our invocations:\n    // >  It is also complicated to modify the environment of a multi-\n    // > threaded process temporarily, since all threads must agree\n    // > when it is safe for the environment to be changed. However,\n    // > this cost is only borne by those invocations of posix_spawn()\n    // > and posix_spawnp() that use the additional functionality.\n    // > Since extensive modifications are not the usual case,\n    // > and are particularly unlikely in time-critical code,\n    // > keeping much of the environment control out of posix_spawn()\n    // > and posix_spawnp() is appropriate design.\n    //\n    // https://pubs.opengroup.org/onlinepubs/009695399/functions/posix_spawn.html\n    //\n    // on windows, a non-reentrant static mutex is used, so it is\n    // definitely not thread safe, but this should not matter.\n    //\n    // NOTE: there is one major function that is not async-signal safe here:\n    // memory allocation and deallocation, which is not async-signal safe.\n    // this should only be run once without deadlocking since any\n    // atomics are guaranteed to be lock-free. we cannot easily avoid\n    // allocation/deallocation, since we would need static global muts\n    // for basically everything. `Command::arg` and `Command::new` will\n    // internally allocate, and freeing it will deallocate any arguments\n    // it was provided. even if we used a static global `Command`, the\n    // `io::Result` requires a `Box` or `io::Error`, which would allocate.\n    // the alternative would be to use `posix_spawnp` or `CreateProcess`\n    // directly, which are async-signal safe and thread-safe, respectively,\n    // however, we'd need to store the engine path and the argument list as\n    // a global CString and `Vec<CString>`, respectively. this atomic guard\n    // makes this safe regardless.\n    #[allow(static_mut_refs)] // FIXME: Use correct types for CHILD_CONTAINER\n    docker::CHILD_CONTAINER.terminate();\n\n    // all termination exit codes are 128 + signal code. the exit code is\n    // 130 for Ctrl+C or SIGINT (signal code 2) for linux, macos, and windows.\n    std::process::exit(130);\n}\n\npub fn install_termination_hook() -> Result<()> {\n    // SAFETY: safe since single-threaded execution.\n    unsafe {\n        signal_hook::low_level::register(signal_hook::consts::SIGINT, || termination_handler())\n    }\n    .map_err(Into::into)\n    .map(|_| ())\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum CommandError {\n    #[error(\"`{command}` failed with {status}\")]\n    NonZeroExitCode {\n        status: std::process::ExitStatus,\n        command: String,\n        stderr: Vec<u8>,\n        stdout: Vec<u8>,\n    },\n    #[error(\"could not execute `{command}`\")]\n    CouldNotExecute {\n        #[source]\n        source: Box<dyn std::error::Error + Send + Sync>,\n        command: String,\n    },\n    #[error(\"`{0:?}` output was not UTF-8\")]\n    Utf8Error(#[source] std::string::FromUtf8Error, std::process::Output),\n}\n\nimpl CommandError {\n    /// Attach valuable information to this [`CommandError`](Self)\n    pub fn to_section_report(self) -> eyre::Report {\n        match &self {\n            CommandError::NonZeroExitCode { stderr, stdout, .. } => {\n                let stderr = String::from_utf8_lossy(stderr).trim().to_owned();\n                let stdout = String::from_utf8_lossy(stdout).trim().to_owned();\n                eyre::eyre!(self)\n                    .section(color_eyre::SectionExt::header(stderr, \"Stderr:\"))\n                    .section(color_eyre::SectionExt::header(stdout, \"Stdout:\"))\n            }\n            _ => eyre::eyre!(self),\n        }\n    }\n}\n"
  },
  {
    "path": "src/extensions.rs",
    "content": "use std::borrow::Cow;\nuse std::fmt;\nuse std::process::{Command, ExitStatus, Output};\n\nuse crate::errors::*;\nuse crate::shell::MessageInfo;\n\npub const STRIPPED_BINS: &[&str] = &[crate::docker::DOCKER, crate::docker::PODMAN, \"cargo\"];\n\npub trait CommandExt {\n    fn fmt_message(&self, msg_info: &mut MessageInfo) -> String;\n\n    #[track_caller]\n    fn print(&self, msg_info: &mut MessageInfo) -> Result<()> {\n        let msg = self.fmt_message(msg_info);\n        msg_info.print(&msg)\n    }\n\n    #[track_caller]\n    fn info(&self, msg_info: &mut MessageInfo) -> Result<()> {\n        let msg = self.fmt_message(msg_info);\n        msg_info.info(&msg)\n    }\n\n    #[track_caller]\n    fn debug(&self, msg_info: &mut MessageInfo) -> Result<()> {\n        let msg = self.fmt_message(msg_info);\n        msg_info.debug(&msg)\n    }\n\n    fn status_result(\n        &self,\n        msg_info: &mut MessageInfo,\n        status: ExitStatus,\n        output: Option<&Output>,\n    ) -> Result<(), CommandError>;\n    #[track_caller]\n    fn run(&mut self, msg_info: &mut MessageInfo, silence_stdout: bool) -> Result<()>;\n    #[track_caller]\n    fn run_and_get_status(\n        &mut self,\n        msg_info: &mut MessageInfo,\n        silence_stdout: bool,\n    ) -> Result<ExitStatus>;\n    #[track_caller]\n    fn run_and_get_stdout(&mut self, msg_info: &mut MessageInfo) -> Result<String>;\n    #[track_caller]\n    fn run_and_get_output(&mut self, msg_info: &mut MessageInfo) -> Result<std::process::Output>;\n    fn command_pretty(\n        &self,\n        msg_info: &mut MessageInfo,\n        strip: impl for<'a> Fn(&'a str) -> bool,\n    ) -> String;\n}\n\nimpl CommandExt for Command {\n    fn command_pretty(\n        &self,\n        msg_info: &mut MessageInfo,\n        strip: impl for<'a> Fn(&'a str) -> bool,\n    ) -> String {\n        // a dummy implementor of display to avoid using unwraps\n        struct C<'c, 'd, F>(&'c Command, &'d mut MessageInfo, F);\n        impl<'e, 'f, F> std::fmt::Display for C<'e, 'f, F>\n        where\n            F: for<'a> Fn(&'a str) -> bool,\n        {\n            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n                let cmd = self.0;\n                write!(\n                    f,\n                    \"{}\",\n                    // if verbose, never strip, if not, let user choose\n                    crate::file::pretty_path(cmd.get_program(), move |c| if self.1.is_verbose() {\n                        false\n                    } else {\n                        self.2(c)\n                    })\n                )?;\n                let args = cmd.get_args();\n                if args.len() > 1 {\n                    write!(\n                        f,\n                        \" {}\",\n                        shell_words::join(args.map(|o| o.to_string_lossy()))\n                    )?;\n                }\n                Ok(())\n            }\n        }\n        format!(\"{}\", C(self, msg_info, strip))\n    }\n\n    #[track_caller]\n    fn fmt_message(&self, mut msg_info: &mut MessageInfo) -> String {\n        use std::fmt::Write;\n        let msg_info = &mut msg_info;\n        let mut string = String::new();\n        if let Some(caller) = msg_info.caller() {\n            write!(string, \"[{}] ->\\n+ \", caller).expect(\"writing to string should not fail\");\n        } else {\n            write!(string, \"+ \").expect(\"writing to string should not fail\");\n        };\n        if let Some(cwd) = self.get_current_dir() {\n            write!(\n                string,\n                \"{:?} {}\",\n                cwd,\n                msg_info.as_verbose(|info| self.command_pretty(info, |_| false))\n            )\n            .expect(\"writing to string should not fail\");\n        } else {\n            write!(\n                string,\n                \"{}\",\n                msg_info.as_verbose(|info| self.command_pretty(info, |_| false))\n            )\n            .expect(\"writing to string should not fail\");\n        }\n        string\n    }\n\n    #[track_caller]\n    fn status_result(\n        &self,\n        msg_info: &mut MessageInfo,\n        status: ExitStatus,\n        output: Option<&Output>,\n    ) -> Result<(), CommandError> {\n        if status.success() {\n            Ok(())\n        } else {\n            Err(CommandError::NonZeroExitCode {\n                status,\n                command: self\n                    .command_pretty(msg_info, |cmd| STRIPPED_BINS.iter().any(|f| f == &cmd)),\n                stderr: output.map(|out| out.stderr.clone()).unwrap_or_default(),\n                stdout: output.map(|out| out.stdout.clone()).unwrap_or_default(),\n            })\n        }\n    }\n\n    /// Runs the command to completion\n    #[track_caller]\n    fn run(&mut self, msg_info: &mut MessageInfo, silence_stdout: bool) -> Result<()> {\n        let status = self.run_and_get_status(msg_info, silence_stdout)?;\n        #[warn(clippy::nursery)]\n        Ok(self.status_result(msg_info, status, None)?)\n    }\n\n    /// Runs the command to completion\n    #[track_caller]\n    fn run_and_get_status(\n        &mut self,\n        msg_info: &mut MessageInfo,\n        silence_stdout: bool,\n    ) -> Result<ExitStatus> {\n        self.debug(msg_info)?;\n        if silence_stdout && !msg_info.is_verbose() {\n            self.stdout(std::process::Stdio::null());\n        }\n        Ok(self.status().map_err(|e| CommandError::CouldNotExecute {\n            source: Box::new(e),\n            command: self.command_pretty(msg_info, |cmd| STRIPPED_BINS.iter().any(|f| f == &cmd)),\n        })?)\n    }\n\n    /// Runs the command to completion and returns its stdout\n    #[track_caller]\n    fn run_and_get_stdout(&mut self, msg_info: &mut MessageInfo) -> Result<String> {\n        let out = self.run_and_get_output(msg_info)?;\n        self.status_result(msg_info, out.status, Some(&out))\n            .map_err(CommandError::to_section_report)?;\n        Ok(out.stdout()?)\n    }\n\n    /// Runs the command to completion and returns the status and its [output](std::process::Output).\n    ///\n    /// # Notes\n    ///\n    /// This command does not check the status.\n    #[track_caller]\n    fn run_and_get_output(&mut self, msg_info: &mut MessageInfo) -> Result<std::process::Output> {\n        self.debug(msg_info)?;\n        self.output().map_err(|e| {\n            CommandError::CouldNotExecute {\n                source: Box::new(e),\n                command: self\n                    .command_pretty(msg_info, |cmd| STRIPPED_BINS.iter().any(|f| f == &cmd)),\n            }\n            .to_section_report()\n        })\n    }\n}\n\npub trait OutputExt {\n    fn stdout(&self) -> Result<String, CommandError>;\n    fn stderr(&self) -> Result<String, CommandError>;\n}\n\nimpl OutputExt for std::process::Output {\n    fn stdout(&self) -> Result<String, CommandError> {\n        String::from_utf8(self.stdout.clone()).map_err(|e| CommandError::Utf8Error(e, self.clone()))\n    }\n\n    fn stderr(&self) -> Result<String, CommandError> {\n        String::from_utf8(self.stderr.clone()).map_err(|e| CommandError::Utf8Error(e, self.clone()))\n    }\n}\n\npub struct SafeCommand {\n    program: String,\n    args: Vec<String>,\n}\n\nimpl SafeCommand {\n    pub fn new<S: ToString>(program: S) -> Self {\n        let program = program.to_string();\n        SafeCommand {\n            program,\n            args: Vec::new(),\n        }\n    }\n\n    pub fn arg<S>(&mut self, arg: &S) -> &mut Self\n    where\n        S: ToString,\n    {\n        self.args.push(arg.to_string());\n        self\n    }\n\n    pub fn args<I, S>(&mut self, args: I) -> &mut Self\n    where\n        I: IntoIterator<Item = S>,\n        S: ToString,\n    {\n        for arg in args {\n            self.arg(&arg);\n        }\n        self\n    }\n}\n\nimpl fmt::Debug for SafeCommand {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(&shell_escape::escape(Cow::from(&self.program)))?;\n        for arg in &self.args {\n            f.write_str(\" \")?;\n            f.write_str(&shell_escape::escape(Cow::from(arg)))?;\n        }\n        Ok(())\n    }\n}\n\nimpl From<SafeCommand> for Command {\n    fn from(s: SafeCommand) -> Self {\n        let mut cmd = Command::new(&s.program);\n        cmd.args(&s.args);\n        cmd\n    }\n}\n\npub(crate) fn env_program(envvar: &str, program: &str) -> String {\n    std::env::var(envvar)\n        .ok()\n        .unwrap_or_else(|| program.to_owned())\n}\n"
  },
  {
    "path": "src/file.rs",
    "content": "use std::borrow::Cow;\nuse std::env;\nuse std::ffi::OsStr;\nuse std::fs::{self, File};\nuse std::io::Read;\n#[cfg(target_family = \"windows\")]\nuse std::path::Prefix;\nuse std::path::{Component, Path, PathBuf};\n\nuse crate::errors::*;\n\npub trait ToUtf8 {\n    fn to_utf8(&self) -> Result<&str>;\n}\n\nimpl ToUtf8 for OsStr {\n    fn to_utf8(&self) -> Result<&str> {\n        self.to_str()\n            .ok_or_else(|| eyre::eyre!(\"unable to convert `{self:?}` to UTF-8 string\"))\n    }\n}\n\nimpl ToUtf8 for Path {\n    fn to_utf8(&self) -> Result<&str> {\n        self.as_os_str().to_utf8()\n    }\n}\n\npub trait PathExt {\n    fn as_posix_relative(&self) -> Result<String>;\n    fn as_posix_absolute(&self) -> Result<String>;\n}\n\n#[cfg(target_family = \"windows\")]\nfn format_prefix(prefix: &str) -> Result<String> {\n    match prefix {\n        \"\" => Ok(\"\".to_owned()),\n        _ => Ok(format!(\"/mnt/{}\", prefix.to_lowercase())),\n    }\n}\n\n#[cfg(target_family = \"windows\")]\nfn fmt_disk(disk: u8) -> String {\n    (disk as char).to_string()\n}\n\n#[cfg(target_family = \"windows\")]\nfn fmt_ns_disk(disk: &std::ffi::OsStr) -> Result<String> {\n    let disk = disk.to_utf8()?;\n    Ok(match disk.len() {\n        // ns can be similar to `\\\\.\\COM42`, or also `\\\\.\\C:\\`\n        2 => {\n            let c = disk.chars().next().expect(\"cannot be empty\");\n            if c.is_ascii_alphabetic() && disk.ends_with(':') {\n                fmt_disk(c as u8)\n            } else {\n                disk.to_owned()\n            }\n        }\n        _ => disk.to_owned(),\n    })\n}\n\n#[cfg(target_family = \"windows\")]\nfn fmt_unc(server: &std::ffi::OsStr, volume: &std::ffi::OsStr) -> Result<String> {\n    let server = server.to_utf8()?;\n    let volume = volume.to_utf8()?;\n    let bytes = volume.as_bytes();\n    if server == \"localhost\"\n        && bytes.len() == 2\n        && bytes[1] == b'$'\n        && bytes[0].is_ascii_alphabetic()\n    {\n        Ok(fmt_disk(bytes[0]))\n    } else {\n        Ok(format!(\"{}/{}\", server, volume))\n    }\n}\n\nimpl PathExt for Path {\n    fn as_posix_relative(&self) -> Result<String> {\n        if cfg!(target_os = \"windows\") {\n            let push = |p: &mut String, c: &str| {\n                if !p.is_empty() && p != \"/\" {\n                    p.push('/');\n                }\n                p.push_str(c);\n            };\n\n            // iterate over components to join them\n            let mut output = String::new();\n            for component in self.components() {\n                match component {\n                    Component::Prefix(prefix) => {\n                        eyre::bail!(\"unix paths cannot handle windows prefix {prefix:?}.\")\n                    }\n                    Component::RootDir => output = \"/\".to_owned(),\n                    Component::CurDir => push(&mut output, \".\"),\n                    Component::ParentDir => push(&mut output, \"..\"),\n                    Component::Normal(path) => push(&mut output, path.to_utf8()?),\n                }\n            }\n            Ok(output)\n        } else {\n            self.to_utf8().map(|x| x.to_owned())\n        }\n    }\n\n    #[cfg(not(target_family = \"windows\"))]\n    fn as_posix_absolute(&self) -> Result<String> {\n        absolute_path(self)?.to_utf8().map(ToOwned::to_owned)\n    }\n\n    // this is similar to as_posix_relative, but it handles drive\n    // separators and will only work with absolute paths.\n    #[cfg(target_family = \"windows\")]\n    fn as_posix_absolute(&self) -> Result<String> {\n        let path = absolute_path(self)?;\n\n        let push = |p: &mut String, c: &str, r: bool| {\n            if !r {\n                p.push('/');\n            }\n            p.push_str(c);\n        };\n        // iterate over components to join them\n        let mut output = String::new();\n        let mut root_prefix = String::new();\n        let mut was_root = false;\n        for component in path.components() {\n            match component {\n                Component::Prefix(prefix) => {\n                    root_prefix = match prefix.kind() {\n                        Prefix::Verbatim(verbatim) => verbatim.to_utf8()?.to_owned(),\n                        Prefix::VerbatimUNC(server, volume) => fmt_unc(server, volume)?,\n                        // we should never get this, but it's effectively just\n                        // a root_prefix since we force absolute paths.\n                        Prefix::VerbatimDisk(disk) => fmt_disk(disk),\n                        Prefix::UNC(server, volume) => fmt_unc(server, volume)?,\n                        Prefix::DeviceNS(ns) => fmt_ns_disk(ns)?,\n                        Prefix::Disk(disk) => fmt_disk(disk),\n                    }\n                }\n                Component::RootDir => output = format!(\"{}/\", format_prefix(&root_prefix)?),\n                Component::CurDir => push(&mut output, \".\", was_root),\n                Component::ParentDir => push(&mut output, \"..\", was_root),\n                Component::Normal(path) => push(&mut output, path.to_utf8()?, was_root),\n            }\n            was_root = component == Component::RootDir;\n        }\n\n        // remove trailing '/'\n        if was_root {\n            output.truncate(output.len() - 1);\n        }\n\n        Ok(output)\n    }\n}\n\npub fn read<P>(path: P) -> Result<String>\nwhere\n    P: AsRef<Path>,\n{\n    read_(path.as_ref())\n}\n\npub fn create_dir_all(path: impl AsRef<Path>) -> Result<()> {\n    fs::create_dir_all(path.as_ref())\n        .wrap_err_with(|| format!(\"couldn't create directory {:?}\", path.as_ref()))\n}\n\nfn read_(path: &Path) -> Result<String> {\n    let mut s = String::new();\n    File::open(path)\n        .wrap_err_with(|| format!(\"couldn't open {path:?}\"))?\n        .read_to_string(&mut s)\n        .wrap_err_with(|| format!(\"couldn't read {path:?}\"))?;\n    Ok(s)\n}\n\npub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {\n    _canonicalize(path.as_ref())\n        .wrap_err_with(|| format!(\"when canonicalizing path `{:?}`\", path.as_ref()))\n}\n\nfn _canonicalize(path: &Path) -> Result<PathBuf> {\n    #[cfg(target_os = \"windows\")]\n    {\n        // Docker does not support UNC paths, this will try to not use UNC paths\n        dunce::canonicalize(path).map_err(Into::into)\n    }\n    #[cfg(not(target_os = \"windows\"))]\n    {\n        Path::new(&path).canonicalize().map_err(Into::into)\n    }\n}\n\nfn is_wsl_absolute(path: &str) -> bool {\n    if !cfg!(target_os = \"windows\") {\n        return false;\n    }\n    let path = path.strip_prefix(\"/mnt/\").unwrap_or(path);\n    let maybe_drive = path.split_once('/').map_or(path, |x| x.0);\n\n    maybe_drive.len() == 1 && matches!(maybe_drive.chars().next(), Some('a'..='z'))\n}\n\n// Fix for issue #581. target_dir must be absolute.\npub fn absolute_path(path: impl AsRef<Path>) -> Result<PathBuf> {\n    let as_ref = path.as_ref();\n    Ok(\n        if as_ref.is_absolute()\n            || cfg!(target_family = \"windows\") && is_wsl_absolute(as_ref.to_utf8()?)\n        {\n            as_ref.to_path_buf()\n        } else {\n            env::current_dir()?.join(path)\n        },\n    )\n}\n\n/// Pretty format a file path. Also removes the path prefix from a command if wanted\npub fn pretty_path(path: impl AsRef<Path>, strip: impl for<'a> Fn(&'a str) -> bool) -> String {\n    let path = path.as_ref();\n    // TODO: Use Path::file_prefix\n    let file_stem = path.file_stem();\n    let file_name = path.file_name();\n    let path = if let (Some(file_stem), Some(file_name)) = (file_stem, file_name) {\n        if let Some(file_name) = file_name.to_str() {\n            if strip(file_name) {\n                Cow::Borrowed(file_stem)\n            } else {\n                Cow::Borrowed(path.as_os_str())\n            }\n        } else {\n            Cow::Borrowed(path.as_os_str())\n        }\n    } else {\n        maybe_canonicalize(path)\n    };\n\n    if let Some(path) = path.to_str() {\n        shell_escape(path).to_string()\n    } else {\n        format!(\"{path:?}\")\n    }\n}\n\npub fn shell_escape(string: &str) -> Cow<'_, str> {\n    let escape: &[char] = if cfg!(target_os = \"windows\") {\n        &['%', '$', '`', '!', '\"']\n    } else {\n        &['$', '\\'', '\\\\', '!', '\"']\n    };\n\n    if string.contains(escape) {\n        Cow::Owned(format!(\"{string:?}\"))\n    } else if string.contains(' ') {\n        Cow::Owned(format!(\"\\\"{string}\\\"\"))\n    } else {\n        Cow::Borrowed(string)\n    }\n}\n\n#[must_use]\npub fn maybe_canonicalize(path: &Path) -> Cow<'_, OsStr> {\n    canonicalize(path).map_or_else(\n        |_| path.as_os_str().into(),\n        |p| Cow::Owned(p.as_os_str().to_owned()),\n    )\n}\n\npub fn write_file(path: impl AsRef<Path>, overwrite: bool) -> Result<File> {\n    let path = path.as_ref();\n    create_dir_all(\n        path.parent()\n            .ok_or_else(|| eyre::eyre!(\"could not find parent directory for `{path:?}`\"))?,\n    )?;\n\n    let mut open = fs::OpenOptions::new();\n    open.write(true);\n\n    if overwrite {\n        open.truncate(true).create(true);\n    } else {\n        open.create_new(true);\n    }\n\n    open.open(path)\n        .wrap_err(format!(\"couldn't write to file `{path:?}`\"))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::fmt::Debug;\n\n    macro_rules! p {\n        ($path:expr) => {\n            Path::new($path)\n        };\n    }\n\n    #[track_caller]\n    fn result_eq<T: PartialEq + Eq + Debug>(x: Result<T>, y: Result<T>) {\n        match (x, y) {\n            (Ok(x), Ok(y)) => assert_eq!(x, y),\n            (x, y) => panic!(\"assertion failed: `(left == right)`\\nleft: {x:?}\\nright: {y:?}\"),\n        }\n    }\n\n    #[test]\n    fn as_posix_relative() {\n        result_eq(\n            p!(\".\").join(\"..\").as_posix_relative(),\n            Ok(\"./..\".to_owned()),\n        );\n        result_eq(p!(\".\").join(\"/\").as_posix_relative(), Ok(\"/\".to_owned()));\n        result_eq(\n            p!(\"foo\").join(\"bar\").as_posix_relative(),\n            Ok(\"foo/bar\".to_owned()),\n        );\n        result_eq(\n            p!(\"/foo\").join(\"bar\").as_posix_relative(),\n            Ok(\"/foo/bar\".to_owned()),\n        );\n    }\n\n    #[test]\n    #[cfg(target_family = \"windows\")]\n    fn as_posix_prefix() {\n        assert_eq!(p!(\"C:\").join(\"..\"), p!(\"C:..\"));\n        assert!(p!(\"C:\").join(\"..\").as_posix_relative().is_err());\n    }\n\n    #[test]\n    #[cfg(target_family = \"windows\")]\n    fn is_absolute_wslpath() {\n        assert!(is_wsl_absolute(\"/mnt/c/Users\"));\n        assert!(is_wsl_absolute(\"/mnt/c\"));\n        assert!(is_wsl_absolute(\"/mnt/z/Users\"));\n        assert!(!is_wsl_absolute(\"/mnt\"));\n        assert!(!is_wsl_absolute(\"/mnt/cc\"));\n        assert!(!is_wsl_absolute(\"/mnt/zc\"));\n    }\n\n    #[test]\n    #[cfg(target_family = \"windows\")]\n    fn as_posix_with_drive() {\n        use regex::Regex;\n\n        result_eq(p!(r\"C:\\\").as_posix_absolute(), Ok(\"/mnt/c\".to_owned()));\n        result_eq(\n            p!(r\"C:\\Users\").as_posix_absolute(),\n            Ok(\"/mnt/c/Users\".to_owned()),\n        );\n        result_eq(\n            p!(r\"\\\\localhost\\c$\\Users\").as_posix_absolute(),\n            Ok(\"/mnt/c/Users\".to_owned()),\n        );\n        result_eq(p!(r\"\\\\.\\C:\\\").as_posix_absolute(), Ok(\"/mnt/c\".to_owned()));\n        result_eq(\n            p!(r\"\\\\.\\C:\\Users\").as_posix_absolute(),\n            Ok(\"/mnt/c/Users\".to_owned()),\n        );\n\n        result_eq(\n            p!(r\"/mnt/c/Users\").as_posix_absolute(),\n            Ok(\"/mnt/c/Users\".to_owned()),\n        );\n        result_eq(p!(r\"/mnt/c\").as_posix_absolute(), Ok(\"/mnt/c\".to_owned()));\n\n        let regex = Regex::new(\"/mnt/[A-Za-z]/mnt\").unwrap();\n        let result = p!(r\"/mnt\").as_posix_absolute();\n        assert!(result.is_ok());\n        assert!(regex.is_match(&result.unwrap()));\n    }\n\n    #[test]\n    #[cfg(target_family = \"windows\")]\n    fn pretty_path_windows() {\n        assert_eq!(\n            pretty_path(\"C:\\\\path\\\\bin\\\\cargo.exe\", |f| f.contains(\"cargo\")),\n            \"cargo\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"C:\\\\Program Files\\\\Docker\\\\bin\\\\docker.exe\", |_| false),\n            \"\\\"C:\\\\Program Files\\\\Docker\\\\bin\\\\docker.exe\\\"\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"C:\\\\Program Files\\\\single'quote\\\\cargo.exe\", |c| c\n                .contains(\"cargo\")),\n            \"cargo\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"C:\\\\Program Files\\\\single'quote\\\\cargo.exe\", |_| false),\n            \"\\\"C:\\\\Program Files\\\\single'quote\\\\cargo.exe\\\"\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"C:\\\\Program Files\\\\%not_var%\\\\cargo.exe\", |_| false),\n            \"\\\"C:\\\\\\\\Program Files\\\\\\\\%not_var%\\\\\\\\cargo.exe\\\"\".to_owned()\n        );\n    }\n\n    #[test]\n    #[cfg(target_family = \"unix\")]\n    fn pretty_path_linux() {\n        assert_eq!(\n            pretty_path(\"/usr/bin/cargo\", |f| f.contains(\"cargo\")),\n            \"cargo\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"/home/user/my rust/bin/cargo\", |_| false),\n            \"\\\"/home/user/my rust/bin/cargo\\\"\".to_owned(),\n        );\n        assert_eq!(\n            pretty_path(\"/home/user/single'quote/cargo\", |c| c.contains(\"cargo\")),\n            \"cargo\".to_owned()\n        );\n        assert_eq!(\n            pretty_path(\"/home/user/single'quote/cargo\", |_| false),\n            \"\\\"/home/user/single'quote/cargo\\\"\".to_owned()\n        );\n    }\n}\n"
  },
  {
    "path": "src/id.rs",
    "content": "#[cfg(not(target_os = \"windows\"))]\nuse nix::{\n    errno::Errno,\n    unistd::{Gid, Uid},\n};\n#[cfg(not(target_os = \"windows\"))]\nuse std::ffi::CStr;\n\nuse crate::errors::*;\n\n#[cfg(target_os = \"windows\")]\npub fn group() -> u32 {\n    1000\n}\n\n#[cfg(not(target_os = \"windows\"))]\npub fn group() -> u32 {\n    Gid::current().as_raw()\n}\n\n#[cfg(target_os = \"windows\")]\npub fn user() -> u32 {\n    1000\n}\n\n#[cfg(not(target_os = \"windows\"))]\npub fn user() -> u32 {\n    Uid::current().as_raw()\n}\n\n#[cfg(target_os = \"windows\")]\npub fn username() -> Result<Option<String>> {\n    use std::ptr;\n\n    use winapi::um::winbase::GetUserNameW;\n\n    unsafe {\n        let mut size = 0;\n        GetUserNameW(ptr::null_mut(), &mut size);\n\n        if size == 0 {\n            return Ok(None);\n        }\n\n        let mut username = Vec::with_capacity(size as usize);\n\n        if GetUserNameW(username.as_mut_ptr(), &mut size) == 0 {\n            eyre::bail!(\"Could not get UserName.\");\n        }\n\n        // Remove null terminator.\n        username.set_len((size - 1) as usize);\n\n        Ok(Some(String::from_utf16_lossy(&username)))\n    }\n}\n\n#[cfg(not(target_os = \"windows\"))]\npub fn username() -> Result<Option<String>> {\n    let name = unsafe {\n        Errno::clear();\n\n        let passwd = libc::getpwuid(Uid::current().as_raw());\n\n        if passwd.is_null() {\n            let errno = Errno::last_raw();\n\n            if errno == 0 {\n                return Ok(None);\n            }\n\n            return Err(Errno::from_raw(errno)).wrap_err(\"could not get username\");\n        }\n\n        CStr::from_ptr((*passwd).pw_name)\n    };\n\n    Ok(Some(name.to_string_lossy().into_owned()))\n}\n"
  },
  {
    "path": "src/interpreter.rs",
    "content": "use std::path::Path;\n\nuse crate::errors::*;\nuse crate::file;\nuse crate::Target;\n\n/// Checks if the interpreters have been registered in the host system\npub fn is_registered(target: &Target) -> Result<bool> {\n    if file::read(\"/proc/sys/fs/binfmt_misc/status\")?.trim() != \"enabled\" {\n        eyre::bail!(\"host system doesn't have binfmt_misc support\")\n    }\n\n    let ok = if target.is_windows() {\n        let wine = Path::new(\"/proc/sys/fs/binfmt_misc/wine\");\n        wine.exists() && {\n            let f = file::read(wine)?;\n            f.contains(\"/usr/bin/run-detectors\")\n                || f.contains(\"/usr/lib/binfmt-support/run-detectors\")\n        }\n    } else {\n        // NOTE checking any architecture will do, here we pick arm\n        let qemu = Path::new(\"/proc/sys/fs/binfmt_misc/qemu-arm\");\n        qemu.exists() && file::read(qemu)?.contains(\"/usr/bin/qemu-arm-static\")\n    };\n\n    Ok(ok)\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! [![crates.io](https://img.shields.io/crates/v/cross.svg)](https://crates.io/crates/cross)\n//! [![crates.io](https://img.shields.io/crates/d/cross.svg)](https://crates.io/crates/cross)\n//! [![Matrix](https://img.shields.io/matrix/cross-rs:matrix.org)](https://matrix.to/#/#cross-rs:matrix.org)\n//!\n//! # `cross`\n//!\n//! <p style=\"background:rgba(148,192,255,0.1);padding:0.5em;border-radius:0.2em\">\n//! <strong>⚠️ Warning:</strong> The cross library is for internal\n//! use only: only the command-line interface is stable. The library\n//! may change at any point for any reason. For documentation on the\n//! CLI, please see the repository <a href=\"https://github.com/cross-rs/cross\">README</a>,\n//! <a href=\"https://github.com/cross-rs/cross/tree/main/docs\">docs folder</a>\n//! or the <a href=\"https://github.com/cross-rs/cross/wiki\">wiki</a>.\n//! </p>\n\n#![deny(missing_debug_implementations, rust_2018_idioms)]\n#![warn(\n    clippy::explicit_into_iter_loop,\n    clippy::explicit_iter_loop,\n    clippy::implicit_clone,\n    clippy::inefficient_to_string,\n    clippy::map_err_ignore,\n    clippy::map_unwrap_or,\n    clippy::ref_binding_to_reference,\n    clippy::semicolon_if_nothing_returned,\n    clippy::str_to_string,\n    clippy::unwrap_used\n)]\n\n#[cfg(test)]\nmod tests;\n\npub mod cargo;\npub mod cli;\npub mod config;\npub mod cross_toml;\npub mod docker;\npub mod errors;\nmod extensions;\npub mod file;\nmod id;\nmod interpreter;\npub mod rustc;\npub mod rustup;\npub mod shell;\npub mod temp;\n\nuse std::env;\nuse std::path::PathBuf;\nuse std::process::ExitStatus;\n\nuse cli::Args;\nuse color_eyre::owo_colors::OwoColorize;\nuse color_eyre::{Help, SectionExt};\nuse config::Config;\nuse cross_toml::BuildStd;\nuse rustc::{QualifiedToolchain, Toolchain};\nuse rustc_version::Channel;\nuse serde::{Deserialize, Serialize, Serializer};\n\npub use self::cargo::{cargo_command, cargo_metadata_with_args, CargoMetadata, Subcommand};\nuse self::cross_toml::CrossToml;\nuse self::errors::Context;\nuse self::shell::{MessageInfo, Verbosity};\n\npub use self::errors::{install_panic_hook, install_termination_hook, Result};\npub use self::extensions::{CommandExt, OutputExt, SafeCommand};\npub use self::file::{pretty_path, ToUtf8};\npub use self::rustc::{TargetList, VersionMetaExt};\n\npub const CROSS_LABEL_DOMAIN: &str = \"org.cross-rs\";\n\n#[allow(non_camel_case_types)]\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]\n#[serde(from = \"&str\", into = \"String\")]\n#[serde(rename_all = \"snake_case\")]\npub enum TargetTriple {\n    Other(String),\n\n    // OSX\n    X86_64AppleDarwin,\n    // Support Apple Silicon, as developers are starting to use it as development workstation.\n    Aarch64AppleDarwin,\n\n    // Linux\n    X86_64UnknownLinuxGnu,\n    // Linux Aarch64 is become more popular in CI pipelines (e.g. to AWS Graviton based systems)\n    Aarch64UnknownLinuxGnu,\n    // (Alpine) Linux (musl) often use in CI pipelines to cross compile rust projects to different\n    // targets (e.g. in GitLab CI pipelines).\n    X86_64UnknownLinuxMusl,\n    // (Alpine) Linux (musl) often use in CI pipelines to cross compile rust projects to different\n    // targets (e.g. in GitLab CI pipelines). Now, that AWS Graviton based systems are gaining\n    // attraction CI pipelines might run on (Alpine) Linux Aarch64.\n    Aarch64UnknownLinuxMusl,\n\n    // Windows MSVC\n    X86_64PcWindowsMsvc,\n}\n\nimpl TargetTriple {\n    pub const DEFAULT: Self = Self::X86_64UnknownLinuxGnu;\n    /// Returns the architecture name according to `dpkg` naming convention\n    ///\n    /// # Notes\n    ///\n    /// Some of these make no sense to use in our standard images\n    pub fn deb_arch(&self) -> Option<&'static str> {\n        match self.triple() {\n            \"aarch64-unknown-linux-gnu\" => Some(\"arm64\"),\n            \"aarch64-unknown-linux-musl\" => Some(\"musl-linux-arm64\"),\n            \"aarch64-linux-android\" => None,\n            \"x86_64-unknown-linux-gnu\" => Some(\"amd64\"),\n            \"x86_64-apple-darwin\" => Some(\"darwin-amd64\"),\n            \"x86_64-unknown-linux-musl\" => Some(\"musl-linux-amd64\"),\n\n            \"x86_64-pc-windows-msvc\" => None,\n            \"arm-unknown-linux-gnueabi\" => Some(\"armel\"),\n            \"arm-unknown-linux-gnueabihf\" => Some(\"armhf\"),\n            \"armv7-unknown-linux-gnueabi\" => Some(\"armel\"),\n            \"armv7-unknown-linux-gnueabihf\" => Some(\"armhf\"),\n            \"thumbv7neon-unknown-linux-gnueabihf\" => Some(\"armhf\"),\n            \"i586-unknown-linux-gnu\" => Some(\"i386\"),\n            \"i686-unknown-linux-gnu\" => Some(\"i386\"),\n            \"mips-unknown-linux-gnu\" => Some(\"mips\"),\n            \"mipsel-unknown-linux-gnu\" => Some(\"mipsel\"),\n            \"mips64-unknown-linux-gnuabi64\" => Some(\"mips64\"),\n            \"mips64el-unknown-linux-gnuabi64\" => Some(\"mips64el\"),\n            \"mips64-unknown-linux-muslabi64\" => Some(\"musl-linux-mips64\"),\n            \"mips64el-unknown-linux-muslabi64\" => Some(\"musl-linux-mips64el\"),\n            \"powerpc-unknown-linux-gnu\" => Some(\"powerpc\"),\n            \"powerpc64-unknown-linux-gnu\" => Some(\"ppc64\"),\n            \"powerpc64le-unknown-linux-gnu\" => Some(\"ppc64el\"),\n            \"riscv64gc-unknown-linux-gnu\" => Some(\"riscv64\"),\n            \"s390x-unknown-linux-gnu\" => Some(\"s390x\"),\n            \"sparc64-unknown-linux-gnu\" => Some(\"sparc64\"),\n            \"arm-unknown-linux-musleabihf\" => Some(\"musl-linux-armhf\"),\n            \"arm-unknown-linux-musleabi\" => Some(\"musl-linux-arm\"),\n            \"armv5te-unknown-linux-gnueabi\" => None,\n            \"armv5te-unknown-linux-musleabi\" => None,\n            \"armv7-unknown-linux-musleabi\" => Some(\"musl-linux-arm\"),\n            \"armv7-unknown-linux-musleabihf\" => Some(\"musl-linux-armhf\"),\n            \"i586-unknown-linux-musl\" => Some(\"musl-linux-i386\"),\n            \"i686-unknown-linux-musl\" => Some(\"musl-linux-i386\"),\n            \"mips-unknown-linux-musl\" => Some(\"musl-linux-mips\"),\n            \"mipsel-unknown-linux-musl\" => Some(\"musl-linux-mipsel\"),\n            \"arm-linux-androideabi\" => None,\n            \"armv7-linux-androideabi\" => None,\n            \"thumbv7neon-linux-androideabi\" => None,\n            \"i686-linux-android\" => None,\n            \"x86_64-linux-android\" => None,\n            \"x86_64-pc-windows-gnu\" => None,\n            \"i686-pc-windows-gnu\" => None,\n            \"asmjs-unknown-emscripten\" => None,\n            \"wasm32-unknown-emscripten\" => None,\n            \"x86_64-unknown-dragonfly\" => Some(\"dragonflybsd-amd64\"),\n            \"i686-unknown-freebsd\" => Some(\"freebsd-i386\"),\n            \"x86_64-unknown-freebsd\" => Some(\"freebsd-amd64\"),\n            \"aarch64-unknown-freebsd\" => Some(\"freebsd-arm64\"),\n            \"x86_64-unknown-netbsd\" => Some(\"netbsd-amd64\"),\n            \"sparcv9-sun-solaris\" => Some(\"solaris-sparc\"),\n            \"x86_64-pc-solaris\" => Some(\"solaris-amd64\"),\n            \"thumbv6m-none-eabi\" => Some(\"arm\"),\n            \"thumbv7em-none-eabi\" => Some(\"arm\"),\n            \"thumbv7em-none-eabihf\" => Some(\"armhf\"),\n            \"thumbv7m-none-eabi\" => Some(\"arm\"),\n            _ => None,\n        }\n    }\n\n    /// Checks if this `(host, target)` pair is supported by `cross`\n    ///\n    /// `target == None` means `target == host`\n    fn is_supported(&self, target: Option<&Target>) -> bool {\n        match std::env::var(\"CROSS_COMPATIBILITY_VERSION\")\n            .as_ref()\n            .map(|v| v.as_str())\n        {\n            // Old behavior (up to cross version 0.2.1) can be activated on demand using environment\n            // variable `CROSS_COMPATIBILITY_VERSION`.\n            Ok(\"0.2.1\") => match self {\n                TargetTriple::X86_64AppleDarwin | TargetTriple::Aarch64AppleDarwin => {\n                    target.is_some_and(|t| t.needs_docker())\n                }\n                TargetTriple::X86_64UnknownLinuxGnu\n                | TargetTriple::Aarch64UnknownLinuxGnu\n                | TargetTriple::X86_64UnknownLinuxMusl\n                | TargetTriple::Aarch64UnknownLinuxMusl => target.is_none_or(|t| t.needs_docker()),\n                TargetTriple::X86_64PcWindowsMsvc => target.is_some_and(|t| {\n                    t.triple() != TargetTriple::X86_64PcWindowsMsvc.triple() && t.needs_docker()\n                }),\n                TargetTriple::Other(_) => false,\n            },\n            // New behaviour, if a target is provided (--target ...) then always run with docker\n            // image unless the target explicitly opts-out (i.e. unless needs_docker() returns false).\n            // If no target is provided run natively (on host) using cargo.\n            //\n            // This not only simplifies the logic, it also enables forward-compatibility without\n            // having to change cross every time someone comes up with the need for a new host/target\n            // combination. It's totally fine to call cross with `--target=$host_triple`, for\n            // example to test custom docker images. Cross should not try to recognize if host and\n            // target are equal, it's a user decision and if user wants to bypass cross he can call\n            // cargo directly or omit the `--target` option.\n            _ => target.is_some_and(|t| t.needs_docker()),\n        }\n    }\n\n    /// Returns the [`Target`] as target triple string\n    pub fn triple(&self) -> &str {\n        match self {\n            TargetTriple::X86_64AppleDarwin => \"x86_64-apple-darwin\",\n            TargetTriple::Aarch64AppleDarwin => \"aarch64-apple-darwin\",\n            TargetTriple::X86_64UnknownLinuxGnu => \"x86_64-unknown-linux-gnu\",\n            TargetTriple::Aarch64UnknownLinuxGnu => \"aarch64-unknown-linux-gnu\",\n            TargetTriple::X86_64UnknownLinuxMusl => \"x86_64-unknown-linux-musl\",\n            TargetTriple::Aarch64UnknownLinuxMusl => \"aarch64-unknown-linux-musl\",\n            TargetTriple::X86_64PcWindowsMsvc => \"x86_64-pc-windows-msvc\",\n            TargetTriple::Other(s) => s.as_str(),\n        }\n    }\n}\n\nimpl From<&str> for TargetTriple {\n    fn from(s: &str) -> TargetTriple {\n        match s {\n            \"x86_64-apple-darwin\" => TargetTriple::X86_64AppleDarwin,\n            \"x86_64-unknown-linux-gnu\" => TargetTriple::X86_64UnknownLinuxGnu,\n            \"x86_64-unknown-linux-musl\" => TargetTriple::X86_64UnknownLinuxMusl,\n            \"x86_64-pc-windows-msvc\" => TargetTriple::X86_64PcWindowsMsvc,\n            \"aarch64-apple-darwin\" => TargetTriple::Aarch64AppleDarwin,\n            \"aarch64-unknown-linux-gnu\" => TargetTriple::Aarch64UnknownLinuxGnu,\n            \"aarch64-unknown-linux-musl\" => TargetTriple::Aarch64UnknownLinuxMusl,\n            s => TargetTriple::Other(s.to_owned()),\n        }\n    }\n}\n\nimpl Default for TargetTriple {\n    fn default() -> TargetTriple {\n        TargetTriple::DEFAULT\n    }\n}\n\nimpl std::str::FromStr for TargetTriple {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(s.into())\n    }\n}\n\nimpl std::fmt::Display for TargetTriple {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(self.triple())\n    }\n}\n\nimpl From<String> for TargetTriple {\n    fn from(s: String) -> TargetTriple {\n        s.as_str().into()\n    }\n}\n\nimpl Serialize for TargetTriple {\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serializer.serialize_str(self.triple())\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]\n#[serde(from = \"String\")]\npub enum Target {\n    BuiltIn { triple: TargetTriple },\n    Custom { triple: TargetTriple },\n}\n\nimpl Target {\n    pub const DEFAULT: Self = Self::BuiltIn {\n        triple: TargetTriple::DEFAULT,\n    };\n\n    fn new_built_in(triple: &str) -> Self {\n        Target::BuiltIn {\n            triple: triple.into(),\n        }\n    }\n\n    fn new_custom(triple: &str) -> Self {\n        Target::Custom {\n            triple: triple.into(),\n        }\n    }\n\n    pub fn triple(&self) -> &str {\n        match *self {\n            Target::BuiltIn { ref triple } => triple.triple(),\n            Target::Custom { ref triple } => triple.triple(),\n        }\n    }\n\n    pub fn target(&self) -> &TargetTriple {\n        match *self {\n            Target::BuiltIn { ref triple } => triple,\n            Target::Custom { ref triple } => triple,\n        }\n    }\n\n    fn is_apple(&self) -> bool {\n        self.triple().contains(\"apple\")\n    }\n\n    fn is_bare_metal(&self) -> bool {\n        self.triple().ends_with(\"-none\")\n            || self.triple().ends_with(\"-none-elf\")\n            || self.triple().ends_with(\"-none-eabi\")\n            || self.triple().ends_with(\"-none-eabihf\")\n    }\n\n    fn is_builtin(&self) -> bool {\n        match *self {\n            Target::BuiltIn { .. } => true,\n            Target::Custom { .. } => false,\n        }\n    }\n\n    fn is_bsd(&self) -> bool {\n        self.triple().contains(\"bsd\") || self.triple().contains(\"dragonfly\")\n    }\n\n    fn is_solaris(&self) -> bool {\n        self.triple().contains(\"solaris\")\n    }\n\n    fn is_illumos(&self) -> bool {\n        self.triple().contains(\"illumos\")\n    }\n\n    fn is_android(&self) -> bool {\n        self.triple().contains(\"android\")\n    }\n\n    fn is_emscripten(&self) -> bool {\n        self.triple().contains(\"emscripten\")\n    }\n\n    fn is_linux(&self) -> bool {\n        self.triple().contains(\"linux\") && !self.is_android()\n    }\n\n    fn is_windows(&self) -> bool {\n        self.triple().contains(\"windows\")\n    }\n\n    fn needs_docker(&self) -> bool {\n        self.is_linux()\n            || self.is_android()\n            || self.is_bare_metal()\n            || self.is_bsd()\n            || self.is_solaris()\n            || self.is_illumos()\n            || !self.is_builtin()\n            || self.is_windows()\n            || self.is_emscripten()\n            || self.is_apple()\n    }\n\n    fn needs_interpreter(&self) -> bool {\n        let native = self.triple().starts_with(\"x86_64\")\n            || self.triple().starts_with(\"i586\")\n            || self.triple().starts_with(\"i686\");\n\n        !native && (self.is_linux() || self.is_windows() || self.is_bare_metal())\n    }\n\n    fn needs_docker_seccomp(&self) -> bool {\n        let arch_32bit = self.triple().starts_with(\"arm\")\n            || self.triple().starts_with(\"thumb\")\n            || self.triple().starts_with(\"i586\")\n            || self.triple().starts_with(\"i686\");\n\n        arch_32bit && self.is_android()\n    }\n}\n\nimpl Default for Target {\n    fn default() -> Target {\n        Target::DEFAULT\n    }\n}\n\nimpl std::fmt::Display for Target {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(self.triple())\n    }\n}\n\nimpl Target {\n    pub fn from(triple: &str, target_list: &TargetList) -> Target {\n        if target_list.contains(triple) {\n            Target::new_built_in(triple)\n        } else {\n            Target::new_custom(triple)\n        }\n    }\n}\n\nimpl From<TargetTriple> for Target {\n    fn from(host: TargetTriple) -> Target {\n        match host {\n            TargetTriple::X86_64UnknownLinuxGnu => Target::new_built_in(\"x86_64-unknown-linux-gnu\"),\n            TargetTriple::X86_64UnknownLinuxMusl => {\n                Target::new_built_in(\"x86_64-unknown-linux-musl\")\n            }\n            TargetTriple::X86_64AppleDarwin => Target::new_built_in(\"x86_64-apple-darwin\"),\n            TargetTriple::X86_64PcWindowsMsvc => Target::new_built_in(\"x86_64-pc-windows-msvc\"),\n            TargetTriple::Aarch64AppleDarwin => Target::new_built_in(\"aarch64-apple-darwin\"),\n            TargetTriple::Aarch64UnknownLinuxGnu => {\n                Target::new_built_in(\"aarch64-unknown-linux-gnu\")\n            }\n            TargetTriple::Aarch64UnknownLinuxMusl => {\n                Target::new_built_in(\"aarch64-unknown-linux-musl\")\n            }\n            TargetTriple::Other(s) => Target::from(\n                s.as_str(),\n                &rustc::target_list(&mut Verbosity::Quiet.into())\n                    .expect(\"should be able to query rustc\"),\n            ),\n        }\n    }\n}\n\nimpl From<String> for Target {\n    fn from(target_str: String) -> Target {\n        let target_host: TargetTriple = target_str.as_str().into();\n        target_host.into()\n    }\n}\n\nimpl Serialize for Target {\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serializer.serialize_str(self.triple())\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum CommandVariant {\n    Cargo,\n    Zig,\n    Shell,\n}\n\nimpl CommandVariant {\n    pub fn create(uses_zig: bool) -> Result<CommandVariant> {\n        match uses_zig {\n            true => Ok(CommandVariant::Zig),\n            false => Ok(CommandVariant::Cargo),\n        }\n    }\n\n    pub fn to_str(self) -> &'static str {\n        match self {\n            CommandVariant::Cargo => \"cargo\",\n            CommandVariant::Zig => \"cargo-zigbuild\",\n            CommandVariant::Shell => \"sh\",\n        }\n    }\n\n    pub fn uses_zig(self) -> bool {\n        self == CommandVariant::Zig\n    }\n\n    pub(crate) fn is_shell(self) -> bool {\n        self == CommandVariant::Shell\n    }\n}\n\nfn warn_on_failure(\n    target: &Target,\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<()> {\n    let rust_std = format!(\"rust-std-{target}\");\n    if target.is_builtin() {\n        let component = rustup::check_component(&rust_std, toolchain, msg_info)?;\n        if component.is_not_available() {\n            msg_info.warn(format_args!(\"rust-std is not available for {target}\"))?;\n            msg_info.note(\n                format_args!(\n                    r#\"you may need to build components for the target via `-Z build-std=<components>` or in your cross configuration specify `target.{target}.build-std`\n              the available components are core, std, alloc, and proc_macro\"#\n                ),\n            )?;\n        }\n    }\n    Ok(())\n}\n\nfn add_libc_version(triple: &str, zig_version: Option<&str>) -> String {\n    match zig_version {\n        Some(libc) => format!(\"{triple}.{libc}\"),\n        None => triple.to_owned(),\n    }\n}\n\npub fn run(\n    args: Args,\n    target_list: TargetList,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<ExitStatus>> {\n    if args.version && args.subcommand.is_none() {\n        msg_info.print(concat!(\n            \"cross \",\n            env!(\"CARGO_PKG_VERSION\"),\n            crate::commit_info!()\n        ))?;\n    }\n\n    if let Some(Subcommand::Other(command)) = &args.subcommand {\n        msg_info.warn(format_args!(\n            \"specified cargo subcommand `{command}` is not supported by `cross`.\"\n        ))?;\n        return Ok(None);\n    }\n\n    let host_version_meta = rustc::version_meta()?;\n\n    let cwd = std::env::current_dir()?;\n    if let Some(metadata) = cargo_metadata_with_args(None, Some(&args), msg_info)? {\n        let CrossSetup {\n            config,\n            target,\n            uses_zig,\n            build_std,\n            zig_version,\n            toolchain,\n            is_remote,\n            engine,\n            image,\n        } = match setup(&host_version_meta, &metadata, &args, target_list, msg_info)? {\n            Some(setup) => setup,\n            _ => {\n                return Ok(None);\n            }\n        };\n\n        config.confusable_target(&target, msg_info)?;\n\n        let picked_generic_channel =\n            matches!(toolchain.channel.as_str(), \"stable\" | \"beta\" | \"nightly\");\n\n        if image.platform.target.is_supported(Some(&target)) {\n            if image.platform.architecture != toolchain.host().architecture {\n                msg_info.warn(format_args!(\n                    \"toolchain `{toolchain}` may not run on image `{image}`\"\n                ))?;\n            }\n            let mut is_nightly = toolchain.channel.contains(\"nightly\");\n            let mut rustc_version = None;\n            if let Some((version, channel, commit)) = toolchain.rustc_version()? {\n                if picked_generic_channel && toolchain.date.is_none() {\n                    warn_host_version_mismatch(\n                        &host_version_meta,\n                        &toolchain,\n                        &version,\n                        &commit,\n                        msg_info,\n                    )?;\n                }\n                is_nightly = channel == Channel::Nightly;\n                rustc_version = Some(version);\n            }\n\n            let available_targets = rustup::setup_rustup(&toolchain, msg_info)?;\n\n            rustup::setup_components(\n                &target,\n                build_std.enabled(),\n                &toolchain,\n                is_nightly,\n                available_targets,\n                &args,\n                msg_info,\n            )?;\n\n            let filtered_args =\n                get_filtered_args(zig_version, &args, &target, &config, is_nightly, &build_std);\n\n            let needs_docker = args\n                .subcommand\n                .clone()\n                .is_some_and(|sc| sc.needs_docker(is_remote));\n            if target.needs_docker() && needs_docker {\n                let paths = docker::DockerPaths::create(\n                    &engine,\n                    metadata,\n                    cwd,\n                    toolchain.clone(),\n                    msg_info,\n                )?;\n                let options = docker::DockerOptions::new(\n                    engine,\n                    target.clone(),\n                    config,\n                    image,\n                    crate::CommandVariant::create(uses_zig)?,\n                    rustc_version,\n                    false,\n                );\n\n                if msg_info.should_fail() {\n                    return Ok(None);\n                }\n\n                install_interpreter_if_needed(\n                    &args,\n                    host_version_meta,\n                    &target,\n                    &options,\n                    msg_info,\n                )?;\n                let status = if let Some(status) = docker::run(\n                    options,\n                    paths,\n                    &filtered_args,\n                    args.subcommand.clone(),\n                    msg_info,\n                )\n                .wrap_err(\"could not run container\")?\n                {\n                    status\n                } else {\n                    return Ok(None);\n                };\n\n                let needs_host = args.subcommand.is_some_and(|sc| sc.needs_host(is_remote));\n                if !status.success() {\n                    warn_on_failure(&target, &toolchain, msg_info)?;\n                }\n                if !(status.success() && needs_host) {\n                    return Ok(Some(status));\n                }\n            }\n        }\n    }\n    Ok(None)\n}\n\n/// Check if an interpreter is needed and then install it.\npub fn install_interpreter_if_needed(\n    args: &Args,\n    host_version_meta: rustc_version::VersionMeta,\n    target: &Target,\n    options: &docker::DockerOptions,\n    msg_info: &mut MessageInfo,\n) -> Result<(), color_eyre::Report> {\n    let needs_interpreter = args\n        .subcommand\n        .clone()\n        .is_some_and(|sc| sc.needs_interpreter());\n\n    if host_version_meta.needs_interpreter()\n        && needs_interpreter\n        && target.needs_interpreter()\n        && !interpreter::is_registered(target)?\n    {\n        options.engine.register_binfmt(target, msg_info)?;\n    }\n    Ok(())\n}\n\n/// Get filtered args to pass to cargo\npub fn get_filtered_args(\n    zig_version: Option<String>,\n    args: &Args,\n    target: &Target,\n    config: &Config,\n    is_nightly: bool,\n    build_std: &BuildStd,\n) -> Vec<String> {\n    let add_libc = |triple: &str| add_libc_version(triple, zig_version.as_deref());\n    let mut filtered_args = if args\n        .subcommand\n        .clone()\n        .is_some_and(|s| !s.needs_target_in_command())\n    {\n        let mut filtered_args = Vec::new();\n        let mut args_iter = args.cargo_args.clone().into_iter();\n        while let Some(arg) = args_iter.next() {\n            if arg == \"--target\" {\n                args_iter.next();\n            } else if arg.starts_with(\"--target=\") {\n                // NOOP\n            } else {\n                filtered_args.push(arg);\n            }\n        }\n        filtered_args\n    // Make sure --target is present\n    } else if !args.cargo_args.iter().any(|a| a.starts_with(\"--target\")) {\n        let mut args_with_target = args.cargo_args.clone();\n        args_with_target.push(\"--target\".to_owned());\n        args_with_target.push(add_libc(target.triple()));\n        args_with_target\n    } else if zig_version.is_some() {\n        let mut filtered_args = Vec::new();\n        let mut args_iter = args.cargo_args.clone().into_iter();\n        while let Some(arg) = args_iter.next() {\n            if arg == \"--target\" {\n                filtered_args.push(\"--target\".to_owned());\n                if let Some(triple) = args_iter.next() {\n                    filtered_args.push(add_libc(&triple));\n                }\n            } else if let Some(stripped) = arg.strip_prefix(\"--target=\") {\n                filtered_args.push(format!(\"--target={}\", add_libc(stripped)));\n            } else {\n                filtered_args.push(arg);\n            }\n        }\n        filtered_args\n    } else {\n        args.cargo_args.clone()\n    };\n\n    let is_test = args\n        .subcommand\n        .clone()\n        .is_some_and(|sc| sc == Subcommand::Test);\n    if is_test && config.doctests().unwrap_or_default() && is_nightly {\n        filtered_args.push(\"-Zdoctest-xcompile\".to_owned());\n    }\n\n    if build_std.enabled() {\n        let mut arg = \"-Zbuild-std\".to_owned();\n        if let BuildStd::Crates(crates) = build_std {\n            arg.push('=');\n            arg.push_str(&crates.join(\",\"));\n        }\n        filtered_args.push(arg);\n    }\n\n    filtered_args.extend(args.rest_args.iter().cloned());\n    filtered_args\n}\n\n/// Setup cross configuration\npub fn setup(\n    host_version_meta: &rustc_version::VersionMeta,\n    metadata: &CargoMetadata,\n    args: &Args,\n    target_list: TargetList,\n    msg_info: &mut MessageInfo,\n) -> Result<Option<CrossSetup>, color_eyre::Report> {\n    let host = host_version_meta.host();\n    let toml = toml(metadata, msg_info)?;\n    let config = Config::new(Some(toml));\n    let target = args\n        .target\n        .clone()\n        .or_else(|| config.target(&target_list))\n        .unwrap_or_else(|| Target::from(host.triple(), &target_list));\n    let build_std = config.build_std(&target).unwrap_or_default();\n    let uses_zig = config.zig(&target).unwrap_or(false);\n    let zig_version = config.zig_version(&target);\n    let image = match docker::get_image(&config, &target, uses_zig) {\n        Ok(i) => i,\n        Err(docker::GetImageError::NoCompatibleImages(..))\n            if config.dockerfile(&target).is_some() =>\n        {\n            \"scratch\".into()\n        }\n        Err(err) => {\n            msg_info.warn(err)?;\n\n            return Ok(None);\n        }\n    };\n    let default_toolchain = QualifiedToolchain::default(&config, msg_info)?;\n    let mut toolchain = if let Some(channel) = &args.channel {\n        let picked_toolchain: Toolchain = channel.parse()?;\n\n        if let Some(picked_host) = &picked_toolchain.host {\n            return Err(eyre::eyre!(\"the specified toolchain `{picked_toolchain}` can't be used\"))\n                .with_suggestion(|| {\n                    format!(\n                        \"try `cross +{}` instead\",\n                        picked_toolchain.remove_host()\n                    )\n                }).with_section(|| format!(\n    r#\"Overriding the toolchain in cross is only possible in CLI by specifying a channel and optional date: `+channel[-YYYY-MM-DD]`.\nTo override the toolchain mounted in the image, set `target.{target}.image.toolchain = \"{picked_host}\"`\"#).header(\"Note:\".bright_cyan()));\n        }\n\n        default_toolchain.with_picked(picked_toolchain)?\n    } else {\n        default_toolchain\n    };\n    let is_remote = docker::Engine::is_remote();\n    let engine = docker::Engine::new(None, Some(is_remote), msg_info)?;\n    let image = image.to_definite_with(&engine, msg_info)?;\n    toolchain.replace_host(&image.platform);\n    Ok(Some(CrossSetup {\n        config,\n        target,\n        uses_zig,\n        build_std,\n        zig_version,\n        toolchain,\n        is_remote,\n        engine,\n        image,\n    }))\n}\n\n#[derive(Debug)]\npub struct CrossSetup {\n    pub config: Config,\n    pub target: Target,\n    pub uses_zig: bool,\n    pub build_std: BuildStd,\n    pub zig_version: Option<String>,\n    pub toolchain: QualifiedToolchain,\n    pub is_remote: bool,\n    pub engine: docker::Engine,\n    pub image: docker::Image,\n}\n\n#[derive(PartialEq, Eq, Debug)]\npub(crate) enum VersionMatch {\n    Same,\n    OlderTarget,\n    NewerTarget,\n    Different,\n}\n\npub(crate) fn warn_host_version_mismatch(\n    host_version_meta: &rustc_version::VersionMeta,\n    toolchain: &QualifiedToolchain,\n    rustc_version: &rustc_version::Version,\n    rustc_commit: &str,\n    msg_info: &mut MessageInfo,\n) -> Result<VersionMatch> {\n    let host_commit = host_version_meta.short_version_string.splitn(3, ' ').nth(2);\n    let rustc_commit_date = rustc_commit\n        .split_once(' ')\n        .and_then(|x| x.1.strip_suffix(')'));\n\n    if rustc_version != &host_version_meta.semver || (Some(rustc_commit) != host_commit) {\n        let versions = rustc_version.cmp(&host_version_meta.semver);\n        let dates = rustc_commit_date.cmp(&host_version_meta.commit_date.as_deref());\n\n        let rustc_warning = format!(\n            \"rustc `{rustc_version} {rustc_commit}` for the target. Current active rustc on the host is `{}`\",\n            host_version_meta.short_version_string\n        );\n        if versions.is_lt() || (versions.is_eq() && dates.is_lt()) {\n            if cfg!(not(test)) {\n                msg_info.info(format_args!(\"using older {rustc_warning}.\\n > Update with `rustup update --force-non-host {toolchain}`\"))?;\n            }\n            return Ok(VersionMatch::OlderTarget);\n        } else if versions.is_gt() || (versions.is_eq() && dates.is_gt()) {\n            if cfg!(not(test)) {\n                msg_info.info(format_args!(\n                    \"using newer {rustc_warning}.\\n > Update with `rustup update`\"\n                ))?;\n            }\n            return Ok(VersionMatch::NewerTarget);\n        } else {\n            if cfg!(not(test)) {\n                msg_info.info(format_args!(\"using {rustc_warning}.\"))?;\n            }\n            return Ok(VersionMatch::Different);\n        }\n    }\n    Ok(VersionMatch::Same)\n}\n\npub const fn commit_info() -> &'static str {\n    commit_info!()\n}\n\n#[macro_export]\nmacro_rules! commit_info {\n    () => {\n        include_str!(concat!(env!(\"OUT_DIR\"), \"/commit-info.txt\"))\n    };\n}\n\n/// Obtains the [`CrossToml`] from one of the possible locations\n///\n/// These locations are checked in the following order:\n/// 1. If the `CROSS_CONFIG` variable is set, it tries to read the config from its value\n/// 2. Otherwise, the `Cross.toml` in the project root is used\n/// 3. Package and workspace metadata in the Cargo.toml\n///\n/// The values from `CROSS_CONFIG` or `Cross.toml` are concatenated with the\n/// metadata in `Cargo.toml`, with `Cross.toml` having the highest priority.\npub fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<CrossToml> {\n    let root = &metadata.workspace_root;\n    let cross_config_path = match env::var(\"CROSS_CONFIG\") {\n        Ok(var) => PathBuf::from(var),\n        Err(_) => root.join(\"Cross.toml\"),\n    };\n\n    let mut config = if cross_config_path.exists() {\n        let cross_toml_str = file::read(&cross_config_path)\n            .wrap_err_with(|| format!(\"could not read file `{cross_config_path:?}`\"))?;\n\n        let (config, _) = CrossToml::parse_from_cross_str(\n            &cross_toml_str,\n            Some(cross_config_path.to_utf8()?),\n            msg_info,\n        )\n        .wrap_err_with(|| format!(\"failed to parse file `{cross_config_path:?}` as TOML\",))?;\n\n        config\n    } else {\n        // Checks if there is a lowercase version of this file\n        if root.join(\"cross.toml\").exists() {\n            msg_info.warn(\"There's a file named cross.toml, instead of Cross.toml. You may want to rename it, or it won't be considered.\")?;\n        }\n        CrossToml::default()\n    };\n    let mut found: Option<std::borrow::Cow<'_, str>> = None;\n\n    if let Some(workspace_metadata) = &metadata.metadata {\n        let workspace_metadata =\n            serde_json::de::from_str::<serde_json::Value>(workspace_metadata.get())?;\n        if let Some(cross) = workspace_metadata.get(\"cross\") {\n            found = Some(\n                metadata\n                    .workspace_root\n                    .join(\"Cargo.toml\")\n                    .to_utf8()?\n                    .to_owned()\n                    .into(),\n            );\n            let (workspace_config, _) =\n                CrossToml::parse_from_deserializer(cross, found.as_deref(), msg_info)?;\n            config = config.merge(workspace_config)?;\n        }\n    }\n\n    for (package, package_metadata) in metadata\n        .packages\n        .iter()\n        .filter(|p| metadata.workspace_members.contains(&p.id))\n        .filter_map(|p| Some((p.manifest_path.as_path(), p.metadata.as_deref()?)))\n    {\n        let package_metadata =\n            serde_json::de::from_str::<serde_json::Value>(package_metadata.get())?;\n\n        if let Some(cross) = package_metadata.get(\"cross\") {\n            if let Some(found) = &found {\n                msg_info.warn(format_args!(\"Found conflicting cross configuration in `{}`, use `[workspace.metadata.cross]` in the workspace manifest instead.\\nCurrently only using configuration from `{}`\", package.to_utf8()?, found))?;\n                continue;\n            }\n            let (workspace_config, _) = CrossToml::parse_from_deserializer(\n                cross,\n                Some(metadata.workspace_root.join(\"Cargo.toml\").to_utf8()?),\n                msg_info,\n            )?;\n            config = config.merge(workspace_config)?;\n            found = Some(package.to_utf8()?.into());\n        }\n    }\n\n    Ok(config)\n}\n"
  },
  {
    "path": "src/rustc.rs",
    "content": "use std::path::{Path, PathBuf};\nuse std::process::Command;\n\nuse rustc_version::{Version, VersionMeta};\nuse serde::Deserialize;\n\nuse crate::docker::ImagePlatform;\nuse crate::errors::*;\nuse crate::extensions::{env_program, CommandExt};\nuse crate::shell::MessageInfo;\nuse crate::TargetTriple;\n\n#[derive(Debug)]\npub struct TargetList {\n    pub triples: Vec<String>,\n}\n\nimpl TargetList {\n    #[must_use]\n    pub fn contains(&self, triple: &str) -> bool {\n        self.triples.iter().any(|t| t == triple)\n    }\n}\n\npub trait VersionMetaExt {\n    fn host(&self) -> TargetTriple;\n    fn needs_interpreter(&self) -> bool;\n    fn commit_hash(&self) -> String;\n}\n\nimpl VersionMetaExt for VersionMeta {\n    fn host(&self) -> TargetTriple {\n        TargetTriple::from(&*self.host)\n    }\n\n    fn needs_interpreter(&self) -> bool {\n        self.semver < Version::new(1, 19, 0)\n    }\n\n    fn commit_hash(&self) -> String {\n        self.commit_hash.as_ref().map_or_else(\n            || hash_from_version_string(&self.short_version_string, 2),\n            |x| short_commit_hash(x),\n        )\n    }\n}\n\nfn short_commit_hash(hash: &str) -> String {\n    // short version hashes are always 9 digits\n    //  https://github.com/rust-lang/cargo/pull/10579\n    const LENGTH: usize = 9;\n\n    hash.get(..LENGTH)\n        .unwrap_or_else(|| panic!(\"commit hash must be at least {LENGTH} characters long\"))\n        .to_owned()\n}\n\n#[must_use]\npub fn hash_from_version_string(version: &str, index: usize) -> String {\n    let is_hash = |x: &str| x.chars().all(|c| c.is_ascii_hexdigit());\n    let is_date = |x: &str| x.chars().all(|c| matches!(c, '-' | '0'..='9'));\n\n    // the version can be one of two forms:\n    //   multirust channel string: `\"1.61.0 (fe5b13d68 2022-05-18)\"`\n    //   short version string: `\"rustc 1.61.0 (fe5b13d68 2022-05-18)\"`\n    // want to extract the commit hash if we can, if not, just hash the string.\n    if let Some((commit, date)) = version\n        .splitn(index + 1, ' ')\n        .nth(index)\n        .and_then(|meta| meta.strip_prefix('('))\n        .and_then(|meta| meta.strip_suffix(')'))\n        .and_then(|meta| meta.split_once(' '))\n    {\n        if is_hash(commit) && is_date(date) {\n            return short_commit_hash(commit);\n        }\n    }\n\n    // fallback: can't extract the hash. just create a hash of the version string.\n    short_commit_hash(&const_sha1::sha1(version.as_bytes()).to_string())\n}\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]\npub struct QualifiedToolchain {\n    pub channel: String,\n    pub date: Option<String>,\n    pub(self) host: ImagePlatform,\n    pub is_custom: bool,\n    pub full: String,\n    pub(self) sysroot: PathBuf,\n}\n\nimpl QualifiedToolchain {\n    pub fn new(\n        channel: &str,\n        date: &Option<String>,\n        host: &ImagePlatform,\n        sysroot: &Path,\n        is_custom: bool,\n    ) -> Self {\n        let mut this = Self {\n            channel: channel.to_owned(),\n            date: date.clone(),\n            host: host.clone(),\n            is_custom,\n            full: if let Some(date) = date {\n                format!(\"{}-{}-{}\", channel, date, host.target)\n            } else {\n                format!(\"{}-{}\", channel, host.target)\n            },\n            sysroot: sysroot.to_owned(),\n        };\n        if !is_custom {\n            this.sysroot.set_file_name(&this.full);\n        }\n        this\n    }\n\n    /// Replace the host, does nothing if ran on a custom toolchain\n    pub fn replace_host(&mut self, host: &ImagePlatform) -> &mut Self {\n        if !self.is_custom {\n            *self = Self::new(&self.channel, &self.date, host, &self.sysroot, false);\n            self.sysroot.set_file_name(&self.full);\n        }\n        self\n    }\n\n    /// Makes a good guess as to what the toolchain is compiled to run on.\n    pub(crate) fn custom(\n        name: &str,\n        sysroot: &Path,\n        config: &crate::config::Config,\n        msg_info: &mut MessageInfo,\n    ) -> Result<QualifiedToolchain> {\n        if let Some(compat) = config.custom_toolchain_compat() {\n            let mut toolchain: QualifiedToolchain = QualifiedToolchain::parse(\n                sysroot.to_owned(),\n                &compat,\n                config,\n                msg_info,\n            )\n            .wrap_err(\n                \"could not parse CROSS_CUSTOM_TOOLCHAIN_COMPAT as a fully qualified toolchain name\",\n            )?;\n            toolchain.is_custom = true;\n            toolchain.full = name.to_owned();\n            return Ok(toolchain);\n        }\n        // a toolchain installed by https://github.com/rust-lang/cargo-bisect-rustc\n        if name.starts_with(\"bisector-nightly\") {\n            let (_, toolchain) = name.split_once('-').expect(\"should include -\");\n            let mut toolchain =\n                QualifiedToolchain::parse(sysroot.to_owned(), toolchain, config, msg_info)\n                    .wrap_err(\"could not parse bisector toolchain\")?;\n            toolchain.is_custom = true;\n            toolchain.full = name.to_owned();\n            return Ok(toolchain);\n        } else if let Ok(stdout) = Command::new(sysroot.join(\"bin/rustc\"))\n            .arg(\"-Vv\")\n            .run_and_get_stdout(msg_info)\n        {\n            let rustc_version::VersionMeta {\n                build_date,\n                channel,\n                host,\n                ..\n            } = rustc_version::version_meta_for(&stdout)?;\n            let mut toolchain = QualifiedToolchain::new(\n                match channel {\n                    rustc_version::Channel::Dev => \"dev\",\n                    rustc_version::Channel::Nightly => \"nightly\",\n                    rustc_version::Channel::Beta => \"beta\",\n                    rustc_version::Channel::Stable => \"stable\",\n                },\n                &build_date,\n                &ImagePlatform::from_target(host.into())?,\n                sysroot,\n                true,\n            );\n            toolchain.full = name.to_owned();\n            return Ok(toolchain);\n        }\n        Err(eyre::eyre!(\n            \"cross can not figure out what your custom toolchain is\"\n        ))\n        .suggestion(\"set `CROSS_CUSTOM_TOOLCHAIN_COMPAT` to a fully qualified toolchain name: i.e `nightly-aarch64-unknown-linux-musl`\")\n    }\n\n    pub fn host(&self) -> &ImagePlatform {\n        &self.host\n    }\n\n    pub fn get_sysroot(&self) -> &Path {\n        &self.sysroot\n    }\n\n    /// Grab the current default toolchain\n    pub fn default(config: &crate::config::Config, msg_info: &mut MessageInfo) -> Result<Self> {\n        let sysroot = sysroot(msg_info)?;\n\n        let default_toolchain_name = sysroot\n            .file_name()\n            .ok_or_else(|| eyre::eyre!(\"couldn't get name of active toolchain\"))?\n            .to_str()\n            .ok_or_else(|| eyre::eyre!(\"toolchain was not utf-8\"))?;\n\n        if !config.custom_toolchain() {\n            QualifiedToolchain::parse(sysroot.clone(), default_toolchain_name, config, msg_info)\n        } else {\n            QualifiedToolchain::custom(default_toolchain_name, &sysroot, config, msg_info)\n        }\n    }\n\n    /// Merge a \"picked\" toolchain, overriding set fields.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the host platform cannot be determined.\n    pub fn with_picked(self, picked: Toolchain) -> Result<Self> {\n        // Only nightly channel supports dated releases\n        let date = if picked.channel.starts_with(\"nightly\") {\n            picked.date.or(self.date)\n        } else {\n            picked.date\n        };\n        let host = picked\n            .host\n            .map_or(Ok(self.host), ImagePlatform::from_target)?;\n        let channel = picked.channel;\n\n        Ok(Self::new(&channel, &date, &host, &self.sysroot, false))\n    }\n\n    pub fn set_sysroot(&mut self, convert: impl Fn(&Path) -> PathBuf) {\n        self.sysroot = convert(&self.sysroot);\n    }\n}\n\nimpl std::fmt::Display for QualifiedToolchain {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(&self.full)\n    }\n}\n\nimpl QualifiedToolchain {\n    fn parse(\n        sysroot: PathBuf,\n        toolchain: &str,\n        config: &crate::config::Config,\n        msg_info: &mut MessageInfo,\n    ) -> Result<Self> {\n        match toolchain.parse::<Toolchain>() {\n            Ok(Toolchain {\n                channel,\n                date,\n                host: Some(host),\n                is_custom,\n                full,\n            }) => Ok(QualifiedToolchain {\n                channel,\n                date,\n                host: ImagePlatform::from_target(host)?,\n                is_custom,\n                full,\n                sysroot,\n            }),\n            Ok(_) | Err(_) if config.custom_toolchain() => {\n                QualifiedToolchain::custom(toolchain, &sysroot, config, msg_info)\n            }\n            Ok(_) => Err(eyre::eyre!(\"toolchain is not fully qualified\")\n                .with_note(|| \"cross expects the toolchain to be a rustup installed toolchain\")\n                .with_suggestion(|| {\n                    \"if you're using a custom toolchain try setting `CROSS_CUSTOM_TOOLCHAIN=1` or install rust via rustup\"\n            })),\n            Err(e) => Err(e),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]\npub struct Toolchain {\n    pub channel: String,\n    pub date: Option<String>,\n    pub host: Option<TargetTriple>,\n    pub is_custom: bool,\n    pub full: String,\n}\n\nimpl Toolchain {\n    pub fn remove_host(&self) -> Self {\n        let mut new = Self {\n            host: None,\n            ..self.clone()\n        };\n        if let Some(host) = &self.host {\n            new.full = new.full.replace(&format!(\"-{host}\"), \"\");\n        }\n        new\n    }\n}\n\nimpl std::fmt::Display for Toolchain {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(&self.full)\n    }\n}\n\nimpl std::str::FromStr for Toolchain {\n    type Err = eyre::Report;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        fn dig(s: &str) -> bool {\n            s.chars().all(|c: char| c.is_ascii_digit())\n        }\n        if let Some((channel, parts)) = s.split_once('-') {\n            if parts.starts_with(|c: char| c.is_ascii_digit()) {\n                // a date, YYYY-MM-DD\n                let mut split = parts.splitn(4, '-');\n                let ymd = [split.next(), split.next(), split.next()];\n                let ymd = match ymd {\n                    [Some(y), Some(m), Some(d)] if dig(y) && dig(m) && dig(d) => {\n                        format!(\"{y}-{m}-{d}\")\n                    }\n                    _ => eyre::bail!(\"invalid toolchain `{s}`\"),\n                };\n                Ok(Toolchain {\n                    channel: channel.to_owned(),\n                    date: Some(ymd),\n                    host: split.next().map(|s| s.into()),\n                    is_custom: false,\n                    full: s.to_owned(),\n                })\n            } else {\n                // channel-host\n                Ok(Toolchain {\n                    channel: channel.to_owned(),\n                    date: None,\n                    host: Some(parts.into()),\n                    is_custom: false,\n                    full: s.to_owned(),\n                })\n            }\n        } else {\n            Ok(Toolchain {\n                channel: s.to_owned(),\n                date: None,\n                host: None,\n                is_custom: false,\n                full: s.to_owned(),\n            })\n        }\n    }\n}\n\n#[must_use]\npub fn rustc_command() -> Command {\n    Command::new(env_program(\"RUSTC\", \"rustc\"))\n}\n\npub fn target_list(msg_info: &mut MessageInfo) -> Result<TargetList> {\n    rustc_command()\n        .args([\"--print\", \"target-list\"])\n        .run_and_get_stdout(msg_info)\n        .map(|s| TargetList {\n            triples: s.lines().map(|l| l.to_owned()).collect(),\n        })\n}\n\npub fn sysroot(msg_info: &mut MessageInfo) -> Result<PathBuf> {\n    let stdout = rustc_command()\n        .args([\"--print\", \"sysroot\"])\n        .run_and_get_stdout(msg_info)?\n        .trim()\n        .to_owned();\n    Ok(PathBuf::from(stdout))\n}\n\npub fn version_meta() -> Result<rustc_version::VersionMeta> {\n    rustc_version::version_meta().wrap_err(\"couldn't fetch the `rustc` version\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn bisect() {\n        QualifiedToolchain::custom(\n            \"bisector-nightly-2022-04-26-x86_64-unknown-linux-gnu\",\n            \"/tmp/cross/sysroot\".as_ref(),\n            &crate::config::Config::new(None),\n            &mut MessageInfo::create(2, false, None).unwrap(),\n        )\n        .unwrap();\n    }\n\n    #[test]\n    fn hash_from_rustc() {\n        assert_eq!(\n            hash_from_version_string(\"1.61.0 (fe5b13d68 2022-05-18)\", 1),\n            \"fe5b13d68\"\n        );\n        assert_eq!(\n            hash_from_version_string(\"rustc 1.61.0 (fe5b13d68 2022-05-18)\", 2),\n            \"fe5b13d68\"\n        );\n    }\n\n    #[test]\n    fn with_picked_stable_removes_date() {\n        let base = QualifiedToolchain::new(\n            \"nightly\",\n            &Some(\"2024-08-02\".to_owned()),\n            &ImagePlatform::from_const_target(TargetTriple::X86_64UnknownLinuxGnu),\n            Path::new(\"/tmp/toolchain\"),\n            false,\n        );\n\n        let picked: Toolchain = \"stable\".parse().unwrap();\n        let result = base.with_picked(picked).unwrap();\n\n        assert_eq!(result.channel, \"stable\");\n        assert_eq!(result.date, None);\n    }\n\n    #[test]\n    fn with_picked_beta_removes_date() {\n        let base = QualifiedToolchain::new(\n            \"nightly\",\n            &Some(\"2024-08-02\".to_owned()),\n            &ImagePlatform::from_const_target(TargetTriple::X86_64UnknownLinuxGnu),\n            Path::new(\"/tmp/toolchain\"),\n            false,\n        );\n\n        let picked: Toolchain = \"beta\".parse().unwrap();\n        let result = base.with_picked(picked).unwrap();\n\n        assert_eq!(result.channel, \"beta\");\n        assert_eq!(result.date, None);\n    }\n\n    #[test]\n    fn with_picked_nightly_preserves_date() {\n        let base = QualifiedToolchain::new(\n            \"stable\",\n            &None,\n            &ImagePlatform::from_const_target(TargetTriple::X86_64UnknownLinuxGnu),\n            Path::new(\"/tmp/toolchain\"),\n            false,\n        );\n\n        let picked: Toolchain = \"nightly-2024-08-02\".parse().unwrap();\n        let result = base.with_picked(picked).unwrap();\n\n        assert_eq!(result.channel, \"nightly\");\n        assert_eq!(result.date, Some(\"2024-08-02\".to_owned()));\n    }\n}\n"
  },
  {
    "path": "src/rustup.rs",
    "content": "use std::path::PathBuf;\nuse std::process::Command;\n\nuse rustc_version::{Channel, Version};\n\nuse crate::errors::*;\npub use crate::extensions::{CommandExt, OutputExt};\nuse crate::rustc::QualifiedToolchain;\nuse crate::shell::{MessageInfo, Verbosity};\nuse crate::Target;\n\n#[derive(Debug)]\npub struct AvailableTargets {\n    pub default: String,\n    pub installed: Vec<String>,\n    pub not_installed: Vec<String>,\n}\n\nimpl AvailableTargets {\n    pub fn contains(&self, target: &Target) -> bool {\n        let triple = target.triple();\n        self.is_installed(target) || self.not_installed.iter().any(|x| x == triple)\n    }\n\n    pub fn is_installed(&self, target: &Target) -> bool {\n        let target = target.triple();\n        target == self.default || self.installed.iter().any(|x| x == target)\n    }\n}\n\npub fn setup_rustup(\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<AvailableTargets, color_eyre::Report> {\n    if !toolchain.is_custom\n        && !installed_toolchains(msg_info)?\n            .into_iter()\n            .any(|t| t == toolchain.to_string())\n    {\n        install_toolchain(toolchain, msg_info)?;\n    }\n    let available_targets = if !toolchain.is_custom {\n        available_targets(&toolchain.full, msg_info).with_note(|| {\n            format!(\"cross would use the toolchain '{toolchain}' for mounting rust\")\n        })?\n    } else {\n        AvailableTargets {\n            default: String::new(),\n            installed: vec![],\n            not_installed: vec![],\n        }\n    };\n    Ok(available_targets)\n}\n\nfn rustup_command(msg_info: &mut MessageInfo, no_flags: bool) -> Command {\n    let mut cmd = Command::new(\"rustup\");\n    if no_flags {\n        return cmd;\n    }\n    match msg_info.verbosity {\n        Verbosity::Quiet => {\n            cmd.arg(\"--quiet\");\n        }\n        Verbosity::Verbose(2..) => {\n            cmd.arg(\"--verbose\");\n        }\n        _ => (),\n    }\n    cmd\n}\n\npub fn active_toolchain(msg_info: &mut MessageInfo) -> Result<String> {\n    let out = rustup_command(msg_info, true)\n        .args([\"show\", \"active-toolchain\"])\n        .run_and_get_output(msg_info)?;\n\n    Ok(out\n        .stdout()?\n        .split_once(' ')\n        .ok_or_else(|| eyre::eyre!(\"rustup returned invalid data\"))?\n        .0\n        .to_owned())\n}\n\npub fn installed_toolchains(msg_info: &mut MessageInfo) -> Result<Vec<String>> {\n    Ok(remove_toolchain_suffixes(\n        rustup_command(msg_info, true)\n            .args([\"toolchain\", \"list\"]) // --quiet would be available from 1.28.0\n            .run_and_get_stdout(msg_info)?,\n    ))\n}\n\npub(crate) fn remove_toolchain_suffixes<S: AsRef<str>>(out: S) -> Vec<String> {\n    // Emulate --quiet by removing suffixes like \" (active, default)\" manually\n    out.as_ref()\n        .lines()\n        .map(|l| l.split_once(\" (\").map_or(l.trim(), |(a, _)| a).to_owned())\n        .collect()\n}\n\npub fn available_targets(\n    // this is explicitly a string and not `QualifiedToolchain`,\n    // this is because we use this as a way to ensure that\n    // the toolchain is an official toolchain, if this errors on\n    // `is a custom toolchain`, we tell the user to set CROSS_CUSTOM_TOOLCHAIN\n    // to handle the logic needed.\n    toolchain: &str,\n    msg_info: &mut MessageInfo,\n) -> Result<AvailableTargets> {\n    let mut cmd = rustup_command(msg_info, true);\n\n    cmd.args([\"target\", \"list\", \"--toolchain\", toolchain]);\n    let output = cmd\n        .run_and_get_output(msg_info)\n        .suggestion(\"is rustup installed?\")?;\n\n    if !output.status.success() {\n        let mut err = cmd\n            .status_result(msg_info, output.status, Some(&output))\n            .expect_err(\"we know the command failed\")\n            .to_section_report();\n        if String::from_utf8_lossy(&output.stderr).contains(\"is a custom toolchain\") {\n            err = err.wrap_err(\"'{toolchain}' is a custom toolchain.\")\n            .suggestion(r#\"To use this toolchain with cross, you'll need to set the environment variable `CROSS_CUSTOM_TOOLCHAIN=1`\ncross will not attempt to configure the toolchain further so that it can run your binary.\"#);\n        } else if String::from_utf8_lossy(&output.stderr).contains(\"does not support components\") {\n            err = err.suggestion(format!(\n                \"try reinstalling the '{toolchain}' toolchain\n$ rustup toolchain uninstall {toolchain}\n$ rustup toolchain install {toolchain} --force-non-host\"\n            ));\n        }\n        return Err(err);\n    }\n    let out = output.stdout()?;\n    let mut default = String::new();\n    let mut installed = vec![];\n    let mut not_installed = vec![];\n\n    for line in out.lines() {\n        let target = line\n            .split(' ')\n            .next()\n            .expect(\"rustup output should be consistent\")\n            .to_owned();\n        if line.contains(\"(default)\") {\n            assert!(default.is_empty());\n            default = target;\n        } else if line.contains(\"(installed)\") {\n            installed.push(target);\n        } else {\n            not_installed.push(target);\n        }\n    }\n\n    Ok(AvailableTargets {\n        default,\n        installed,\n        not_installed,\n    })\n}\n\nfn version(msg_info: &mut MessageInfo) -> Result<Version> {\n    let out = rustup_command(msg_info, false)\n        .arg(\"--version\")\n        .run_and_get_stdout(msg_info)?;\n\n    match out\n        .lines()\n        .next()\n        .and_then(|line| line.split_whitespace().nth(1))\n    {\n        Some(version) => {\n            semver::Version::parse(version).wrap_err_with(|| \"failed to parse rustup version\")\n        }\n        None => eyre::bail!(\"failed to get rustup version\"),\n    }\n}\n\npub fn install_toolchain(toolchain: &QualifiedToolchain, msg_info: &mut MessageInfo) -> Result<()> {\n    let mut command = rustup_command(msg_info, false);\n    let toolchain = toolchain.to_string();\n    command.args([\"toolchain\", \"add\", &toolchain, \"--profile\", \"minimal\"]);\n    if version(msg_info)? >= semver::Version::new(1, 25, 0) {\n        command.arg(\"--force-non-host\");\n    }\n    command\n        .run(msg_info, false)\n        .wrap_err_with(|| format!(\"couldn't install toolchain `{toolchain}`\"))\n}\n\npub fn install(\n    target: &Target,\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<()> {\n    let target = target.triple();\n    let toolchain = toolchain.to_string();\n    rustup_command(msg_info, false)\n        .args([\"target\", \"add\", target, \"--toolchain\", &toolchain])\n        .run(msg_info, false)\n        .wrap_err_with(|| format!(\"couldn't install `std` for {target}\"))\n}\n\npub fn install_component(\n    component: &str,\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<()> {\n    let toolchain = toolchain.to_string();\n    rustup_command(msg_info, false)\n        .args([\"component\", \"add\", component, \"--toolchain\", &toolchain])\n        .run(msg_info, false)\n        .wrap_err_with(|| format!(\"couldn't install the `{component}` component\"))\n}\n\n#[derive(Debug)]\npub enum Component<'a> {\n    Installed(&'a str),\n    Available(&'a str),\n    NotAvailable(&'a str),\n}\n\nimpl<'a> Component<'a> {\n    pub fn is_installed(&'a self) -> bool {\n        matches!(self, Component::Installed(_))\n    }\n\n    pub fn is_not_available(&'a self) -> bool {\n        matches!(self, Component::NotAvailable(_))\n    }\n}\n\npub fn check_component<'a>(\n    component: &'a str,\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<Component<'a>> {\n    Ok(Command::new(\"rustup\")\n        .args([\"component\", \"list\", \"--toolchain\", &toolchain.to_string()])\n        .run_and_get_stdout(msg_info)?\n        .lines()\n        .find_map(|line| {\n            let available = line.starts_with(component);\n            let installed = line.contains(\"installed\");\n            match available {\n                true => Some(installed),\n                false => None,\n            }\n        })\n        .map_or_else(\n            || Component::NotAvailable(component),\n            |installed| match installed {\n                true => Component::Installed(component),\n                false => Component::Available(component),\n            },\n        ))\n}\n\npub fn component_is_installed(\n    component: &str,\n    toolchain: &QualifiedToolchain,\n    msg_info: &mut MessageInfo,\n) -> Result<bool> {\n    Ok(check_component(component, toolchain, msg_info)?.is_installed())\n}\n\n#[allow(clippy::too_many_arguments)]\npub fn setup_components(\n    target: &Target,\n    uses_build_std: bool,\n    toolchain: &QualifiedToolchain,\n    is_nightly: bool,\n    available_targets: AvailableTargets,\n    args: &crate::cli::Args,\n    msg_info: &mut MessageInfo,\n) -> Result<(), color_eyre::Report> {\n    if !toolchain.is_custom {\n        if !is_nightly && uses_build_std {\n            eyre::bail!(\n                \"no rust-std component available for {}: must use nightly\",\n                target.triple()\n            );\n        }\n\n        if !uses_build_std\n            && !available_targets.is_installed(target)\n            && available_targets.contains(target)\n        {\n            install(target, toolchain, msg_info)?;\n        } else if !component_is_installed(\"rust-src\", toolchain, msg_info)? {\n            install_component(\"rust-src\", toolchain, msg_info)?;\n        }\n        if args\n            .subcommand\n            .clone()\n            .is_some_and(|sc| sc == crate::Subcommand::Clippy)\n            && !component_is_installed(\"clippy\", toolchain, msg_info)?\n        {\n            install_component(\"clippy\", toolchain, msg_info)?;\n        }\n    }\n    Ok(())\n}\n\nfn rustc_channel(version: &Version) -> Result<Channel> {\n    match version\n        .pre\n        .split('.')\n        .next()\n        .expect(\"rust prerelease version should contain `.`\")\n    {\n        \"\" => Ok(Channel::Stable),\n        \"dev\" => Ok(Channel::Dev),\n        \"beta\" => Ok(Channel::Beta),\n        \"nightly\" => Ok(Channel::Nightly),\n        x => eyre::bail!(\"unknown prerelease tag {x}\"),\n    }\n}\n\nimpl QualifiedToolchain {\n    fn multirust_channel_manifest_path(&self) -> PathBuf {\n        self.get_sysroot()\n            .join(\"lib/rustlib/multirust-channel-manifest.toml\")\n    }\n\n    pub fn rustc_version_string(&self) -> Result<Option<String>> {\n        let path = self.multirust_channel_manifest_path();\n        if path.exists() {\n            let contents =\n                std::fs::read(&path).wrap_err_with(|| format!(\"couldn't open file `{path:?}`\"))?;\n            let manifest: toml::value::Table = toml::from_str(std::str::from_utf8(&contents)?)?;\n            return Ok(manifest\n                .get(\"pkg\")\n                .and_then(|pkg| pkg.get(\"rust\"))\n                .and_then(|rust| rust.get(\"version\"))\n                .and_then(|version| version.as_str())\n                .map(|version| version.to_owned()));\n        }\n        Ok(None)\n    }\n\n    pub fn rustc_version(&self) -> Result<Option<(Version, Channel, String)>> {\n        let path = self.multirust_channel_manifest_path();\n        if let Some(rust_version) = self.rustc_version_string()? {\n            // Field is `\"{version} ({commit} {date})\"`\n            if let Some((version, meta)) = rust_version.split_once(' ') {\n                let version = Version::parse(version)\n                    .wrap_err_with(|| format!(\"invalid rust version found in {path:?}\"))?;\n                let channel = rustc_channel(&version)?;\n                return Ok(Some((version, channel, meta.to_owned())));\n            }\n        }\n        Ok(None)\n    }\n}\n"
  },
  {
    "path": "src/shell.rs",
    "content": "// This file was adapted from:\n//   https://github.com/rust-lang/cargo/blob/ca4edabb28fc96fdf2a1d56fe3851831ac166f8a/src/cargo/core/shell.rs\n\nuse std::env;\nuse std::fmt;\nuse std::io::{self, Write};\nuse std::str::FromStr;\n\nuse crate::config::bool_from_envvar;\nuse crate::errors::Result;\nuse owo_colors::{self, OwoColorize};\n\n// get the prefix for stderr messages\nmacro_rules! cross_prefix {\n    ($s:literal) => {\n        concat!(\"[cross]\", \" \", $s)\n    };\n}\n\n// generate the color style\nmacro_rules! write_style {\n    ($stream:ident, $msg_info:expr, $message:expr $(, $style:ident)* $(,)?) => {{\n        match $msg_info.color_choice {\n            ColorChoice::Always => write!($stream, \"{}\", $message $(.$style())*),\n            ColorChoice::Never => write!($stream, \"{}\", $message),\n            ColorChoice::Auto => write!(\n                $stream,\n                \"{}\",\n                $message $(.if_supports_color($stream.owo(), |text| text.$style()))*\n            ),\n        }?;\n    }};\n}\n\n// low-level interface for printing colorized messages\nmacro_rules! message {\n    // write a status message, which has the following format:\n    //  \"{status}: {message}\"\n    // both status and ':' are bold.\n    (@status $stream:ident, $status:expr, $message:expr, $color:ident, $msg_info:expr $(,)?) => {{\n        write_style!($stream, $msg_info, $status, bold, $color);\n        write_style!($stream, $msg_info, \":\", bold);\n        if let Some(caller) = $msg_info.caller() {\n            write!($stream, \" [{}]\", caller)?;\n        }\n        match $message {\n            Some(message) => writeln!($stream, \" {}\", message, )?,\n            None => write!($stream, \" \")?,\n        }\n\n        Ok(())\n    }};\n\n    (@status @name $name:ident, $status:expr, $message:expr, $color:ident, $msg_info:expr $(,)?) => {{\n        let mut stream = io::$name();\n        message!(@status stream, $status, $message, $color, $msg_info)\n    }};\n}\n\n// high-level interface to message\nmacro_rules! status {\n    (@stderr $status:expr, $message:expr, $color:ident, $msg_info:expr $(,)?) => {{\n        message!(@status @name stderr, $status, $message, $color, $msg_info)\n    }};\n\n    (@stdout $status:expr, $message:expr, $color:ident, $msg_info:expr  $(,)?) => {{\n        message!(@status @name stdout, $status, $message, $color, $msg_info)\n    }};\n}\n\n/// the requested verbosity of output.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\npub enum Verbosity {\n    Quiet,\n    #[default]\n    Normal,\n    Verbose(u8),\n}\n\nimpl Verbosity {\n    pub fn verbose(self) -> bool {\n        match self {\n            Self::Verbose(..) => true,\n            Self::Normal | Self::Quiet => false,\n        }\n    }\n\n    #[must_use]\n    pub fn level(&self) -> u8 {\n        match &self {\n            Verbosity::Verbose(v) => *v,\n            _ => 0,\n        }\n    }\n\n    fn create(color_choice: ColorChoice, verbose: impl Into<u8>, quiet: bool) -> Option<Self> {\n        match (verbose.into(), quiet) {\n            (1.., true) => {\n                MessageInfo::from(color_choice).fatal(\"cannot set both --verbose and --quiet\", 101)\n            }\n            (v @ 1.., false) => Some(Verbosity::Verbose(v)),\n            (0, true) => Some(Verbosity::Quiet),\n            (0, false) => None,\n        }\n    }\n}\n\n/// Whether messages should use color output\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ColorChoice {\n    /// force color output\n    Always,\n    /// force disable color output\n    Never,\n    /// intelligently guess whether to use color output\n    Auto,\n}\n\nimpl FromStr for ColorChoice {\n    type Err = eyre::ErrReport;\n\n    fn from_str(s: &str) -> Result<ColorChoice> {\n        match s {\n            \"always\" => Ok(ColorChoice::Always),\n            \"never\" => Ok(ColorChoice::Never),\n            \"auto\" => Ok(ColorChoice::Auto),\n            arg => eyre::bail!(\n                \"argument for --color must be auto, always, or never, but found `{arg}`\"\n            ),\n        }\n    }\n}\n\n// Should simplify the APIs a lot.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct MessageInfo {\n    pub color_choice: ColorChoice,\n    pub verbosity: Verbosity,\n    pub stdout_needs_erase: bool,\n    pub stderr_needs_erase: bool,\n    pub cross_debug: bool,\n    pub has_warned: bool,\n}\n\nimpl MessageInfo {\n    pub fn new(color_choice: ColorChoice, verbosity: Verbosity) -> MessageInfo {\n        MessageInfo {\n            color_choice,\n            verbosity,\n            stdout_needs_erase: false,\n            stderr_needs_erase: false,\n            cross_debug: std::env::var(\"CROSS_DEBUG\")\n                .as_deref()\n                .map(bool_from_envvar)\n                .unwrap_or_default(),\n            has_warned: false,\n        }\n    }\n\n    pub fn create(verbose: impl Into<u8>, quiet: bool, color: Option<&str>) -> Result<MessageInfo> {\n        let color_choice = get_color_choice(color)?;\n        let verbosity = get_verbosity(color_choice, verbose, quiet)?;\n\n        Ok(Self::new(color_choice, verbosity))\n    }\n\n    #[track_caller]\n    pub fn caller(&mut self) -> Option<impl fmt::Display> {\n        if self.cross_debug {\n            let loc = std::panic::Location::caller();\n            Some(format!(\"{}:{}\", loc.file(), loc.line()))\n        } else {\n            None\n        }\n    }\n\n    #[must_use]\n    pub fn is_verbose(&self) -> bool {\n        self.verbosity.verbose()\n    }\n\n    fn as_verbosity<T, C: Fn(&mut MessageInfo) -> T>(&mut self, call: C, new: Verbosity) -> T {\n        let old = self.verbosity;\n        self.verbosity = new;\n        let result = call(self);\n        self.verbosity = old;\n\n        result\n    }\n\n    pub fn as_quiet<T, C: Fn(&mut MessageInfo) -> T>(&mut self, call: C) -> T {\n        self.as_verbosity(call, Verbosity::Quiet)\n    }\n\n    pub fn as_normal<T, C: Fn(&mut MessageInfo) -> T>(&mut self, call: C) -> T {\n        self.as_verbosity(call, Verbosity::Normal)\n    }\n\n    pub fn as_verbose<T, C: Fn(&mut MessageInfo) -> T>(&mut self, call: C) -> T {\n        self.as_verbosity(call, Verbosity::Verbose(2))\n    }\n\n    fn erase_line<S: OwoStream + Write>(&mut self, stream: &mut S) -> Result<()> {\n        // this is the Erase in Line sequence\n        stream.write_all(b\"\\x1B[K\").map_err(Into::into)\n    }\n\n    fn stdout_check_erase(&mut self) -> Result<()> {\n        if self.stdout_needs_erase {\n            self.erase_line(&mut io::stdout())?;\n            self.stdout_needs_erase = false;\n        }\n        Ok(())\n    }\n\n    fn stderr_check_erase(&mut self) -> Result<()> {\n        if self.stderr_needs_erase {\n            self.erase_line(&mut io::stderr())?;\n            self.stderr_needs_erase = false;\n        }\n        Ok(())\n    }\n\n    /// prints a red 'error' message and terminates.\n    #[track_caller]\n    pub fn fatal<T: fmt::Display>(&mut self, message: T, code: i32) -> ! {\n        self.error(message)\n            .expect(\"could not display fatal message\");\n        std::process::exit(code);\n    }\n\n    /// prints a red 'error' message.\n    #[track_caller]\n    pub fn error<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        self.has_warned = true;\n        self.stderr_check_erase()?;\n        status!(@stderr cross_prefix!(\"error\"), Some(&message), red, self)\n    }\n\n    /// prints an amber 'warning' message.\n    #[track_caller]\n    pub fn warn<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        self.has_warned = true;\n        match self.verbosity {\n            Verbosity::Quiet => Ok(()),\n            _ => status!(@stderr\n                cross_prefix!(\"warning\"),\n                Some(&message),\n                yellow,\n                self,\n            ),\n        }\n    }\n\n    /// prints a cyan 'note' message.\n    #[track_caller]\n    pub fn note<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        match self.verbosity {\n            Verbosity::Quiet => Ok(()),\n            _ => status!(@stderr cross_prefix!(\"note\"), Some(&message), cyan, self),\n        }\n    }\n\n    pub fn status<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        match self.verbosity {\n            Verbosity::Quiet => Ok(()),\n            _ => {\n                eprintln!(\"{}\", message);\n                Ok(())\n            }\n        }\n    }\n\n    /// prints a high-priority message to stdout.\n    #[track_caller]\n    pub fn print<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        self.stdout_check_erase()?;\n        println!(\"{}\", message);\n        Ok(())\n    }\n\n    /// prints a normal message to stdout.\n    #[track_caller]\n    pub fn info<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        match self.verbosity {\n            Verbosity::Quiet => Ok(()),\n            _ => {\n                println!(\"{}\", message);\n                Ok(())\n            }\n        }\n    }\n\n    /// prints a debugging message to stdout.\n    #[track_caller]\n    pub fn debug<T: fmt::Display>(&mut self, message: T) -> Result<()> {\n        match self.verbosity {\n            Verbosity::Quiet | Verbosity::Normal => Ok(()),\n            _ => {\n                println!(\"{}\", message);\n                Ok(())\n            }\n        }\n    }\n\n    pub fn fatal_usage<T: fmt::Display>(\n        &mut self,\n        arg: T,\n        provided: Option<&str>,\n        possible: Option<&[&str]>,\n        code: i32,\n    ) -> ! {\n        self.error_usage(arg, provided, possible)\n            .expect(\"could not display usage message\");\n        std::process::exit(code);\n    }\n\n    fn error_usage<T: fmt::Display>(\n        &mut self,\n        arg: T,\n        provided: Option<&str>,\n        possible: Option<&[&str]>,\n    ) -> Result<()> {\n        let mut stream = io::stderr();\n        write_style!(stream, self, cross_prefix!(\"error\"), bold, red);\n        write_style!(stream, self, \":\", bold);\n        match provided {\n            Some(value) => {\n                write_style!(\n                    stream,\n                    self,\n                    format_args!(\" \\\"{value}\\\" isn't a valid value for '\")\n                );\n                write_style!(stream, self, arg, yellow);\n                write_style!(stream, self, \"'\\n\");\n            }\n            None => {\n                write_style!(stream, self, \" The argument '\");\n                write_style!(stream, self, arg, yellow);\n                write_style!(stream, self, \"' requires a value but none was supplied\\n\");\n            }\n        }\n        match possible {\n            Some(values) if !values.is_empty() => {\n                let error_indent = cross_prefix!(\"error: \").len();\n                write_style!(\n                    stream,\n                    self,\n                    format_args!(\"{:error_indent$}[possible values: \", \"\")\n                );\n                let max_index = values.len() - 1;\n                for (index, value) in values.iter().enumerate() {\n                    write_style!(stream, self, value, green);\n                    if index < max_index {\n                        write_style!(stream, self, \", \");\n                    }\n                }\n                write_style!(stream, self, \"]\\n\");\n            }\n            _ => (),\n        }\n        write_style!(stream, self, \"Usage:\\n\");\n        write_style!(\n            stream,\n            self,\n            \"    cross [+toolchain] [OPTIONS] [SUBCOMMAND]\\n\"\n        );\n        write_style!(stream, self, \"\\n\");\n        write_style!(stream, self, \"For more information try \");\n        write_style!(stream, self, \"--help\", green);\n        write_style!(stream, self, \"\\n\");\n\n        stream.flush()?;\n\n        Ok(())\n    }\n\n    /// Returns true if we've previously warned or errored, and we're in CI or `CROSS_NO_WARNINGS` has been set.\n    ///\n    /// This is used so that unexpected warnings and errors cause ci to fail.\n    pub fn should_fail(&self) -> bool {\n        // FIXME: store env var\n        env::var(\"CROSS_NO_WARNINGS\").map_or_else(|_| is_ci::cached(), |env| bool_from_envvar(&env))\n            && self.has_warned\n    }\n}\n\nimpl Default for MessageInfo {\n    fn default() -> MessageInfo {\n        MessageInfo::new(ColorChoice::Auto, Verbosity::Normal)\n    }\n}\n\nimpl From<ColorChoice> for MessageInfo {\n    fn from(color_choice: ColorChoice) -> MessageInfo {\n        MessageInfo::new(color_choice, Verbosity::Normal)\n    }\n}\n\nimpl From<Verbosity> for MessageInfo {\n    fn from(verbosity: Verbosity) -> MessageInfo {\n        MessageInfo::new(ColorChoice::Auto, verbosity)\n    }\n}\n\nimpl From<(ColorChoice, Verbosity)> for MessageInfo {\n    fn from(info: (ColorChoice, Verbosity)) -> MessageInfo {\n        MessageInfo::new(info.0, info.1)\n    }\n}\n\n// cargo only accepts literal booleans for some values.\npub fn cargo_envvar_bool(var: &str) -> Result<bool> {\n    match env::var(var).ok() {\n        Some(value) => value.parse::<bool>().map_err(|_ignore| {\n            eyre::eyre!(\"environment variable for `{var}` was not `true` or `false`.\")\n        }),\n        None => Ok(false),\n    }\n}\n\npub fn invalid_color(provided: Option<&str>) -> ! {\n    let possible = [\"auto\", \"always\", \"never\"];\n    MessageInfo::default().fatal_usage(\"--color <WHEN>\", provided, Some(&possible), 1);\n}\n\nfn get_color_choice(color: Option<&str>) -> Result<ColorChoice> {\n    Ok(match color {\n        Some(arg) => arg.parse().unwrap_or_else(|_| invalid_color(color)),\n        None => match env::var(\"CARGO_TERM_COLOR\").ok().as_deref() {\n            Some(arg) => arg.parse().unwrap_or_else(|_| invalid_color(color)),\n            None => ColorChoice::Auto,\n        },\n    })\n}\n\nfn get_verbosity(\n    color_choice: ColorChoice,\n    verbose: impl Into<u8>,\n    quiet: bool,\n) -> Result<Verbosity> {\n    // cargo always checks the value of these variables.\n    let env_verbose = cargo_envvar_bool(\"CARGO_TERM_VERBOSE\")?;\n    let env_quiet = cargo_envvar_bool(\"CARGO_TERM_QUIET\")?;\n    Ok(match Verbosity::create(color_choice, verbose, quiet) {\n        Some(v) => v,\n        None => Verbosity::create(color_choice, env_verbose, env_quiet).unwrap_or_default(),\n    })\n}\n\ntrait OwoStream {\n    fn owo(&self) -> owo_colors::Stream;\n}\n\nimpl OwoStream for io::Stdout {\n    fn owo(&self) -> owo_colors::Stream {\n        owo_colors::Stream::Stdout\n    }\n}\n\nimpl OwoStream for io::Stderr {\n    fn owo(&self) -> owo_colors::Stream {\n        owo_colors::Stream::Stderr\n    }\n}\n\npub fn default_ident() -> usize {\n    cross_prefix!(\"\").len()\n}\n\n#[must_use]\npub fn indent(message: &str, spaces: usize) -> String {\n    use std::fmt::Write as _;\n    message.lines().fold(String::new(), |mut string, line| {\n        let _ = write!(string, \"{:spaces$}{line}\", \"\");\n        string\n    })\n}\n"
  },
  {
    "path": "src/temp.rs",
    "content": "#![allow(static_mut_refs)] // FIXME: use appropriate std types for `FILES` and `DIRS`.\n\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\nuse crate::errors::Result;\n\n// open temporary directories and files so we ensure we cleanup on exit.\nstatic mut FILES: Vec<tempfile::NamedTempFile> = vec![];\nstatic mut DIRS: Vec<tempfile::TempDir> = vec![];\n\nfn data_dir() -> Option<PathBuf> {\n    directories::BaseDirs::new().map(|d| d.data_dir().to_path_buf())\n}\n\npub fn dir() -> Result<PathBuf> {\n    data_dir()\n        .map(|p| p.join(\"cross-rs\").join(\"tmp\"))\n        .ok_or(eyre::eyre!(\"unable to get data directory\"))\n}\n\npub(crate) fn has_tempfiles() -> bool {\n    // SAFETY: safe, since we only check if the stack is empty.\n    unsafe { !FILES.is_empty() || !DIRS.is_empty() }\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\npub(crate) unsafe fn clean() {\n    // don't expose FILES or DIRS outside this module,\n    // so we can only add or remove from the stack using\n    // our wrappers, guaranteeing add/remove in order.\n    FILES.clear();\n    DIRS.clear();\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\nunsafe fn push_tempfile() -> Result<&'static mut tempfile::NamedTempFile> {\n    let parent = dir()?;\n    fs::create_dir_all(&parent).ok();\n    let file = tempfile::NamedTempFile::new_in(&parent)?;\n    FILES.push(file);\n    Ok(FILES.last_mut().expect(\"file list should not be empty\"))\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\nunsafe fn pop_tempfile() -> Option<tempfile::NamedTempFile> {\n    FILES.pop()\n}\n\n#[derive(Debug)]\npub struct TempFile {\n    file: &'static mut tempfile::NamedTempFile,\n}\n\nimpl TempFile {\n    /// # Safety\n    /// Safe as long as we have single-threaded execution.\n    pub unsafe fn new() -> Result<Self> {\n        Ok(Self {\n            file: push_tempfile()?,\n        })\n    }\n\n    pub fn file(&mut self) -> &mut tempfile::NamedTempFile {\n        self.file\n    }\n\n    #[must_use]\n    pub fn path(&self) -> &Path {\n        self.file.path()\n    }\n}\n\nimpl Drop for TempFile {\n    fn drop(&mut self) {\n        // SAFETY: safe if we only modify the stack via `TempFile`.\n        unsafe {\n            pop_tempfile();\n        }\n    }\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\nunsafe fn push_tempdir() -> Result<&'static Path> {\n    let parent = dir()?;\n    fs::create_dir_all(&parent).ok();\n    let dir = tempfile::TempDir::new_in(&parent)?;\n    DIRS.push(dir);\n    Ok(DIRS.last().expect(\"should not be empty\").path())\n}\n\n/// # Safety\n/// Safe as long as we have single-threaded execution.\nunsafe fn pop_tempdir() -> Option<tempfile::TempDir> {\n    DIRS.pop()\n}\n\n#[derive(Debug)]\npub struct TempDir {\n    path: &'static Path,\n}\n\nimpl TempDir {\n    /// # Safety\n    /// Safe as long as we have single-threaded execution.\n    pub unsafe fn new() -> Result<Self> {\n        Ok(Self {\n            path: push_tempdir()?,\n        })\n    }\n\n    #[must_use]\n    pub fn path(&self) -> &'static Path {\n        self.path\n    }\n}\n\nimpl Drop for TempDir {\n    fn drop(&mut self) {\n        // SAFETY: safe if we only modify the stack via `TempDir`.\n        unsafe {\n            pop_tempdir();\n        }\n    }\n}\n"
  },
  {
    "path": "src/tests/rustup.rs",
    "content": "use crate::rustup;\n\n#[test]\nfn remove_toolchain_suffixes() {\n    // no overrides (default is active)\n    assert_eq!(\n        rustup::remove_toolchain_suffixes(\n            \"stable-aarch64-apple-darwin (active, default)\\n\\\n             stable-x86_64-unknown-linux-gnu\"\n        ),\n        vec![\n            \"stable-aarch64-apple-darwin\",\n            \"stable-x86_64-unknown-linux-gnu\",\n        ]\n    );\n    // with overrides (default is not active)\n    assert_eq!(\n        rustup::remove_toolchain_suffixes(\n            \"stable-aarch64-apple-darwin (default)\\n\\\n             stable-x86_64-unknown-linux-gnu\\n\\\n             nightly-aarch64-apple-darwin (active)\"\n        ),\n        vec![\n            \"stable-aarch64-apple-darwin\",\n            \"stable-x86_64-unknown-linux-gnu\",\n            \"nightly-aarch64-apple-darwin\",\n        ]\n    );\n}\n"
  },
  {
    "path": "src/tests/toml.rs",
    "content": "use std::io::Read;\n\nuse once_cell::sync::Lazy;\nuse regex::{Regex, RegexBuilder};\n\nuse crate::ToUtf8;\n\nstatic TOML_REGEX: Lazy<Regex> = Lazy::new(|| {\n    RegexBuilder::new(r#\"```toml(.*?)\\n(.*?)```\"#)\n        .multi_line(true)\n        .dot_matches_new_line(true)\n        .build()\n        .expect(\"regex should be valid\")\n});\n\n#[test]\nfn toml_check() -> Result<(), Box<dyn std::error::Error>> {\n    let workspace_root = super::get_cargo_workspace();\n    let walk = super::walk_dir(\n        workspace_root,\n        &[\n            \"target\",\n            \".git\",\n            \"src\",\n            \"CODE_OF_CONDUCT.md\",\n            \"CHANGELOG.md\",\n        ],\n        |p| p == Some(\"md\".as_ref()),\n    );\n\n    for dir_entry in walk {\n        let dir_entry = dir_entry?;\n        if dir_entry.file_type().is_none_or(|f| f.is_dir()) {\n            continue;\n        }\n        eprintln!(\"File: {:?}\", dir_entry.path());\n        let mut file = std::fs::File::open(dir_entry.path()).unwrap();\n        let mut contents = String::new();\n        file.read_to_string(&mut contents).unwrap();\n        for matches in TOML_REGEX.captures_iter(&contents) {\n            let cargo = {\n                let t = matches.get(1).unwrap().as_str();\n                if t.is_empty() {\n                    false\n                } else if t == \",cargo\" {\n                    true\n                } else {\n                    println!(\"skipping {t}\");\n                    continue;\n                }\n            };\n            let fence = matches.get(2).unwrap();\n            let fence_content = fence\n                .as_str()\n                .replace(\"$TARGET\", \"x86_64-unknown-linux-gnu\")\n                .replace(\"${target}\", \"x86_64-unknown-linux-gnu\");\n\n            eprintln!(\n                \"testing snippet at: {}:{:?}\",\n                dir_entry.path().to_utf8()?,\n                text_line_no(&contents, fence.range().start),\n            );\n            let mut msg_info = crate::shell::MessageInfo::default();\n            let toml = if !cargo {\n                crate::cross_toml::CrossToml::parse_from_cross_str(\n                    &fence_content,\n                    None,\n                    &mut msg_info,\n                )?\n            } else {\n                crate::cross_toml::CrossToml::parse_from_cargo_package_str(\n                    &fence_content,\n                    &mut msg_info,\n                )?\n                .unwrap_or_default()\n            };\n            assert!(toml.1.is_empty());\n\n            // TODO: Add serde_path_to_error\n            // Check if roundtrip works, needed for merging Cross.toml and Cargo.toml\n            serde_json::from_value::<crate::cross_toml::CrossToml>(serde_json::to_value(toml.0)?)?;\n        }\n    }\n    Ok(())\n}\n\npub fn text_line_no(text: &str, index: usize) -> usize {\n    let mut line_no = 0;\n    let mut count = 0;\n    for line in text.split('\\n') {\n        line_no += 1;\n        count += line.len() + 1;\n        if count >= index {\n            break;\n        }\n    }\n    line_no\n}\n"
  },
  {
    "path": "src/tests.rs",
    "content": "mod rustup;\nmod toml;\n\nuse std::{\n    ffi::OsStr,\n    path::{Path, PathBuf},\n};\n\nuse once_cell::sync::OnceCell;\nuse rustc_version::VersionMeta;\n\nuse crate::{docker::ImagePlatform, rustc::QualifiedToolchain, TargetTriple, ToUtf8};\n\nstatic WORKSPACE: OnceCell<PathBuf> = OnceCell::new();\n\n/// Returns the cargo workspace for the manifest\npub fn get_cargo_workspace() -> &'static Path {\n    let manifest_dir = env!(\"CARGO_MANIFEST_DIR\");\n    let mut msg_info = crate::shell::Verbosity::Verbose(2).into();\n    #[allow(clippy::unwrap_used)]\n    WORKSPACE.get_or_init(|| {\n        crate::cargo_metadata_with_args(Some(manifest_dir.as_ref()), None, &mut msg_info)\n            .unwrap()\n            .unwrap()\n            .workspace_root\n    })\n}\n\npub fn walk_dir<'a>(\n    root: &'_ Path,\n    skip: &'static [impl AsRef<OsStr> + Send + Sync + 'a],\n    ext: impl for<'s> Fn(Option<&'s std::ffi::OsStr>) -> bool + Sync + Send + 'static,\n) -> impl Iterator<Item = Result<ignore::DirEntry, ignore::Error>> {\n    ignore::WalkBuilder::new(root)\n        .filter_entry(move |e| {\n            if skip\n                .iter()\n                .map(|s| -> &std::ffi::OsStr { s.as_ref() })\n                .any(|dir| e.file_name() == dir)\n            {\n                return false;\n            } else if e.file_type().is_some_and(|f| f.is_dir()) {\n                return true;\n            }\n            ext(e.path().extension())\n        })\n        .build()\n}\n\n#[test]\npub fn target_mismatch() {\n    use crate::{warn_host_version_mismatch, VersionMatch};\n\n    fn make_meta(input: &str) -> VersionMeta {\n        let mut split = input.split(' ');\n        let version = split.next().unwrap();\n        let hash_short = &split.next().unwrap().strip_prefix('(').unwrap();\n        let date = split.next().unwrap().strip_suffix(')').unwrap();\n        rustc_version::version_meta_for(&format!(\n            r#\"rustc {version} ({hash_short} {date})\nbinary: rustc\ncommit-hash: {hash_short:f<40}\ncommit-date: {date}\nhost: xxxx\nrelease: {version}\n\"#\n        ))\n        .unwrap()\n    }\n\n    fn make_rustc_version(input: &str) -> (rustc_version::Version, String) {\n        let (version, meta) = input.split_once(' ').unwrap();\n        (version.parse().unwrap(), meta.to_owned())\n    }\n\n    #[track_caller]\n    fn compare(expected: VersionMatch, host: &str, targ: &str) {\n        let host_meta = dbg!(make_meta(host));\n        let target_meta = dbg!(make_rustc_version(targ));\n        let mut msg_info = crate::shell::MessageInfo::default();\n        assert_eq!(\n            expected,\n            warn_host_version_mismatch(\n                &host_meta,\n                &QualifiedToolchain::new(\n                    \"xxxx\",\n                    &None,\n                    &ImagePlatform::from_const_target(TargetTriple::X86_64UnknownLinuxGnu),\n                    Path::new(\"/toolchains/xxxx-x86_64-unknown-linux-gnu\"),\n                    false,\n                ),\n                &target_meta.0,\n                &target_meta.1,\n                &mut msg_info,\n            )\n            .unwrap(),\n            \"\\nhost = {}\\ntarg = {}\",\n            host,\n            targ\n        );\n    }\n\n    compare(\n        VersionMatch::Same,\n        \"1.0.0 (11111111 2022-01-01)\",\n        \"1.0.0 (11111111 2022-01-01)\",\n    );\n    compare(\n        VersionMatch::Same,\n        \"1.0.0-nightly (11111111 2022-01-01)\",\n        \"1.0.0-nightly (11111111 2022-01-01)\",\n    );\n    compare(\n        VersionMatch::OlderTarget,\n        \"1.2.0 (22222222 2022-02-02)\",\n        \"1.0.0 (11111111 2022-01-01)\",\n    );\n    compare(\n        VersionMatch::NewerTarget,\n        \"1.0.0 (11111111 2022-01-01)\",\n        \"1.2.0 (22222222 2022-02-02)\",\n    );\n    compare(\n        VersionMatch::Different,\n        \"1.0.0-nightly (11111111 2022-01-01)\",\n        \"1.0.0-nightly (22222222 2022-01-01)\",\n    );\n    compare(\n        VersionMatch::OlderTarget,\n        \"1.0.0-nightly (22222222 2022-02-02)\",\n        \"1.0.0-nightly (11111111 2022-01-01)\",\n    );\n    compare(\n        VersionMatch::NewerTarget,\n        \"1.0.0-nightly (11111111 2022-01-01)\",\n        \"1.0.0-nightly (22222222 2022-02-02)\",\n    );\n}\n\n#[test]\nfn check_newlines() -> crate::Result<()> {\n    for file in walk_dir(get_cargo_workspace(), &[\".git\", \"target\"], |_| true) {\n        let file = file?;\n        if !file.file_type().is_none_or(|f| f.is_file()) {\n            continue;\n        }\n        eprintln!(\"File: {:?}\", file.path());\n        assert!(\n            crate::file::read(file.path())\n                .unwrap_or_else(|_| String::from(\"\\n\"))\n                .ends_with('\\n'),\n            \"file {:?} does not end with a newline\",\n            file.path().to_utf8()?\n        );\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "targets.toml",
    "content": "# This file contains all the \"targets\" cross can be used with by default and is used for generating `src/docker/provided_images.rs` and our images\n# the only required value for a `target` entry is its name in `target.target` and the `os` to use in CI\n#\n# spec is available in `xtask/src/util.rs` on `CiTarget`\n\n[[target]]\ntarget = \"x86_64-apple-darwin\"\nos = \"macos-13\"\nspecial = true\ndeploy = true\n\n[[target]]\ntarget = \"x86_64-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"native qemu-user qemu-system\"\ndeploy = true\n\n[[target]]\ntarget = \"x86_64-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"native qemu-user\"\ndeploy = true\n\n[[target]]\ntarget = \"x86_64-pc-windows-msvc\"\nos = \"windows-2019\"\nspecial = true\ndeploy = true\n\n[[target]]\ntarget = \"x86_64-unknown-linux-gnu\"\nsub = \"centos\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"native qemu-user qemu-system\"\n\n[[target]]\ntarget = \"aarch64-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"aarch64_be-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\nbuild-std = true\n\n[[target]]\ntarget = \"arm-unknown-linux-gnueabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"arm-unknown-linux-gnueabihf\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv7-unknown-linux-gnueabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\n\n[[target]]\ntarget = \"armv7-unknown-linux-gnueabihf\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"thumbv7neon-unknown-linux-gnueabihf\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"i586-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"i686-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"native qemu-user qemu-system\"\n\n[[target]]\ntarget = \"loongarch64-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\n\n[[target]]\ntarget = \"loongarch64-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\n\n[[target]]\ntarget = \"mips-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\nbuild-std = true\n\n[[target]]\ntarget = \"mipsel-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\nbuild-std = true\n\n[[target]]\ntarget = \"mips64-unknown-linux-gnuabi64\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nbuild-std = true\n\n[[target]]\ntarget = \"mips64el-unknown-linux-gnuabi64\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\nbuild-std = true\n\n[[target]]\ndisabled = true # https://github.com/cross-rs/cross/issues/1422\ntarget = \"mips64-unknown-linux-muslabi64\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nbuild-std = true\n\n[[target]]\ndisabled = true # https://github.com/cross-rs/cross/issues/1422\ntarget = \"mips64el-unknown-linux-muslabi64\"\nos = \"ubuntu-latest\"\n# FIXME: Lacking partial C++ support due to missing compiler builtins.\ncpp = true\nstd = true\nrun = true\nbuild-std = true\n\n[[target]]\ntarget = \"powerpc-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"powerpc64-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"powerpc64le-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"riscv64gc-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"riscv64gc-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user\"\n\n[[target]]\ntarget = \"s390x-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"sparc64-unknown-linux-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n\n[[target]]\ntarget = \"aarch64-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"arm-unknown-linux-musleabihf\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"arm-unknown-linux-musleabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv5te-unknown-linux-gnueabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv5te-unknown-linux-musleabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv7-unknown-linux-musleabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv7-unknown-linux-musleabihf\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"i586-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n# FIXME: These could run without qemu in our tests (`native`), but it fails today\nrunners = \"qemu-user\"\n\n[[target]]\ntarget = \"i686-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n# FIXME: These could run without qemu in our tests (`native`), but it fails today\nrunners = \"qemu-user\"\n\n[[target]]\ndisabled = true # https://github.com/cross-rs/cross/issues/1422\ntarget = \"mips-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nbuild-std = true\n\n[[target]]\ndisabled = true # https://github.com/cross-rs/cross/issues/1422\ntarget = \"mipsel-unknown-linux-musl\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nbuild-std = true\n\n[[target]]\ntarget = \"aarch64-linux-android\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"arm-linux-androideabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"armv7-linux-androideabi\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"thumbv7neon-linux-androideabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"i686-linux-android\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"x86_64-linux-android\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"x86_64-pc-windows-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"i686-pc-windows-gnu\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = true\nrun = true\n\n[[target]]\n# Disabled for now, see https://github.com/rust-lang/rust/issues/98216 & https://github.com/cross-rs/cross/issues/634\ndisabled = true\ntarget = \"asmjs-unknown-emscripten\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"wasm32-unknown-emscripten\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = true\nrun = true\n\n[[target]]\ntarget = \"x86_64-unknown-dragonfly\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nbuild-std = true\n\n[[target]]\ntarget = \"i686-unknown-freebsd\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"x86_64-unknown-freebsd\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"aarch64-unknown-freebsd\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nbuild-std = true\n\n[[target]]\ntarget = \"x86_64-unknown-netbsd\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"sparcv9-sun-solaris\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"x86_64-pc-solaris\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"x86_64-unknown-illumos\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\n\n[[target]]\ntarget = \"thumbv6m-none-eabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv7em-none-eabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv7em-none-eabihf\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv7m-none-eabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv8m.base-none-eabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv8m.main-none-eabi\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"thumbv8m.main-none-eabihf\"\nos = \"ubuntu-latest\"\ncpp = true\nstd = false\n\n[[target]]\ntarget = \"cross\"\nos = \"ubuntu-latest\"\nspecial = true\n\n[[target]]\ntarget = \"zig\"\nos = \"ubuntu-latest\"\nspecial = true\n\n[[target]]\ntarget = \"aarch64-unknown-linux-gnu\"\nsub = \"centos\"\nos = \"ubuntu-latest\"\ncpp = true\ndylib = true\nstd = true\nrun = true\nrunners = \"qemu-user qemu-system\"\n"
  },
  {
    "path": "xtask/Cargo.toml",
    "content": "[package]\ndocumentation = \"https://github.com/cross-rs/cross\"\nlicense = \"MIT OR Apache-2.0\"\nname = \"xtask\"\nrepository = \"https://github.com/cross-rs/cross\"\nversion = \"0.0.0-dev.0\"\nedition = \"2021\"\npublish = false\n\n\n[dependencies]\ncross = { path = \"..\", features = [\"dev\"] }\nwalkdir = \"2.3.3\"\ncolor-eyre = \"0.6.2\"\neyre = \"0.6.8\"\nclap = { version = \"4.1\", features = [\"derive\", \"env\"] }\nwhich = { version = \"4.2\", default-features = false }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1.0\"\nshell-words = \"1.1.0\"\ntoml = \"0.7\"\nonce_cell = \"1.17\"\nsemver = \"1\"\nchrono = \"0.4\"\nwildmatch = \"2.1.1\"\n"
  },
  {
    "path": "xtask/src/build_docker_image.rs",
    "content": "use std::fmt::Write;\nuse std::path::Path;\n\nuse crate::util::{\n    cargo_metadata, get_matrix, gha_error, gha_output, gha_print, DEFAULT_PLATFORMS,\n};\nuse crate::ImageTarget;\nuse clap::Args;\nuse cross::docker::{self, BuildCommandExt, BuildResultExt, ImagePlatform, Progress};\nuse cross::shell::MessageInfo;\nuse cross::{CommandExt, ToUtf8};\n\n#[derive(Args, Debug)]\npub struct BuildDockerImage {\n    #[clap(long, hide = true, env = \"GITHUB_REF_TYPE\")]\n    pub ref_type: Option<String>,\n    #[clap(long, hide = true, env = \"GITHUB_REF_NAME\")]\n    ref_name: Option<String>,\n    /// Pass extra flags to the build\n    #[clap(long, env = \"CROSS_BUILD_OPTS\")]\n    build_opts: Option<String>,\n    #[clap(action, long = \"latest\", hide = true, env = \"LATEST\")]\n    is_latest: bool,\n    /// Specify a tag to use instead of the derived one, eg `local`\n    #[clap(long)]\n    pub tag: Option<String>,\n    /// Repository name for image.\n    #[clap(long, default_value = docker::CROSS_IMAGE)]\n    pub repository: String,\n    /// Newline separated labels\n    #[clap(long, env = \"LABELS\")]\n    /// Print but do not execute the build commands.\n    pub labels: Option<String>,\n    #[clap(long)]\n    pub dry_run: bool,\n    /// Force a push when `--push` is set, but not `--tag`\n    #[clap(long, hide = true)]\n    pub force: bool,\n    /// Push build to registry.\n    #[clap(short, long)]\n    pub push: bool,\n    /// Set output to /dev/null\n    #[clap(short, long)]\n    pub no_output: bool,\n    /// Docker build progress output type.\n    #[clap(\n        long,\n        value_parser = clap::builder::PossibleValuesParser::new([\"auto\", \"plain\", \"tty\"]),\n    )]\n    pub progress: Option<String>,\n    /// Do not load from cache when building the image.\n    #[clap(long)]\n    pub no_cache: bool,\n    /// Option `--cache-to` for docker, only would work if push is not set to true\n    ///\n    /// Additionally you can use {base_name} to replace with base name of the image\n    /// If not specified, would not be passed to docker unless `--push` is used\n    #[clap(long)]\n    pub cache_to: Option<String>,\n    /// Option `--cache-from` for docker, would only work if engine supports cache from type and no_cache is not set to true\n    ///\n    /// Additionally you can use {base_name} to replace with base name of the image\n    #[clap(long, default_value = \"type=registry,ref={base_name}:main\")]\n    pub cache_from: String,\n    /// Continue building images even if an image fails to build.\n    #[clap(long)]\n    pub no_fastfail: bool,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n    /// If no target list is provided, parse list from CI.\n    #[clap(long)]\n    pub from_ci: bool,\n    /// Additional build arguments to pass to Docker.\n    #[clap(long)]\n    pub build_arg: Vec<String>,\n    // [os/arch[/variant]=]toolchain\n    #[clap(long, short = 'a', action = clap::builder::ArgAction::Append)]\n    pub platform: Vec<ImagePlatform>,\n    /// Targets to build for\n    #[clap()]\n    pub targets: Vec<ImageTarget>,\n}\n\nfn locate_dockerfile(\n    target: ImageTarget,\n    docker_root: &Path,\n    cross_toolchain_root: &Path,\n) -> cross::Result<(ImageTarget, String)> {\n    let dockerfile_name = format!(\"Dockerfile.{target}\");\n    let dockerfile_root = if cross_toolchain_root.join(&dockerfile_name).exists() {\n        &cross_toolchain_root\n    } else if docker_root.join(&dockerfile_name).exists() {\n        &docker_root\n    } else {\n        eyre::bail!(\"unable to find dockerfile for target \\\"{target}\\\"\");\n    };\n    let dockerfile = dockerfile_root.join(dockerfile_name).to_utf8()?.to_string();\n    Ok((target, dockerfile))\n}\n\npub fn build_docker_image(\n    BuildDockerImage {\n        ref_type,\n        ref_name,\n        build_opts,\n        is_latest,\n        tag: tag_override,\n        repository,\n        labels,\n        dry_run,\n        force,\n        push,\n        no_output,\n        progress,\n        no_cache,\n        no_fastfail,\n        from_ci,\n        build_arg,\n        platform,\n        cache_from,\n        cache_to,\n        mut targets,\n        ..\n    }: BuildDockerImage,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let metadata = cargo_metadata(msg_info)?;\n    let version = metadata\n        .get_package(\"cross\")\n        .expect(\"cross expected in workspace\")\n        .version\n        .clone();\n    if targets.is_empty() {\n        if from_ci {\n            targets = get_matrix()\n                .iter()\n                .filter(|m| m.os.starts_with(\"ubuntu\"))\n                .filter(|m| !m.disabled)\n                .map(|m| m.to_image_target())\n                .collect();\n        } else {\n            targets = walkdir::WalkDir::new(metadata.workspace_root.join(\"docker\"))\n                .max_depth(1)\n                .contents_first(true)\n                .into_iter()\n                .filter_map(|e| e.ok().filter(|f| f.file_type().is_file()))\n                .filter_map(|f| {\n                    f.file_name()\n                        .to_string_lossy()\n                        .strip_prefix(\"Dockerfile.\")\n                        .map(ToOwned::to_owned)\n                        .map(|s| s.parse().unwrap())\n                })\n                .collect();\n        }\n    }\n    let gha = std::env::var(\"GITHUB_ACTIONS\").is_ok();\n    let mut progress = progress.map(|x| x.parse().unwrap());\n    if gha {\n        progress = Some(Progress::Plain);\n    }\n    let root = metadata.workspace_root;\n    let docker_root = root.join(\"docker\");\n    let cross_toolchains_root = docker_root.join(\"cross-toolchains\").join(\"docker\");\n    let targets = targets\n        .into_iter()\n        .map(|t| locate_dockerfile(t, &docker_root, &cross_toolchains_root))\n        .collect::<cross::Result<Vec<_>>>()?;\n\n    let platforms = if platform.is_empty() {\n        DEFAULT_PLATFORMS.to_vec()\n    } else {\n        platform\n    };\n\n    let mut results = vec![];\n    for (platform, (target, dockerfile)) in targets\n        .iter()\n        .flat_map(|t| platforms.iter().map(move |p| (p, t)))\n    {\n        if gha && targets.len() > 1 {\n            gha_print(\"::group::Build {target}\");\n        } else {\n            msg_info.note(format_args!(\"Build {target} for {}\", platform.target))?;\n        }\n        let mut docker_build = engine.command();\n        docker_build.invoke_build_command();\n        let has_buildkit = docker::Engine::has_buildkit();\n        docker_build.current_dir(&docker_root);\n\n        let docker_platform = platform.docker_platform();\n        let mut dockerfile = dockerfile.clone();\n        docker_build.args([\"--platform\", &docker_platform]);\n        let uppercase_triple = target.name.to_ascii_uppercase().replace('-', \"_\");\n        docker_build.args([\n            \"--build-arg\",\n            &format!(\"CROSS_TARGET_TRIPLE={}\", uppercase_triple),\n        ]);\n        // add our platform, and determine if we need to use a native docker image\n        if has_native_image(docker_platform.as_str(), target, msg_info)? {\n            let dockerfile_name = match target.sub.as_deref() {\n                Some(sub) => format!(\"Dockerfile.native.{sub}\"),\n                None => \"Dockerfile.native\".to_owned(),\n            };\n            let dockerfile_path = docker_root.join(&dockerfile_name);\n            if !dockerfile_path.exists() {\n                eyre::bail!(\n                    \"unable to find native dockerfile named {dockerfile_name} for target {target}.\"\n                );\n            }\n            dockerfile = dockerfile_path.to_utf8()?.to_string();\n        }\n\n        if push {\n            docker_build.arg(\"--push\");\n        } else if engine.kind.supports_output_flag() && no_output {\n            docker_build.args([\"--output\", \"type=tar,dest=/dev/null\"]);\n        } else if no_output {\n            msg_info.fatal(\"cannot specify `--no-output` with engine that does not support the `--output` flag\", 1);\n        } else if has_buildkit {\n            docker_build.arg(\"--load\");\n        }\n\n        let mut tags = vec![];\n\n        match (ref_type.as_deref(), ref_name.as_deref()) {\n            (Some(ref_type), Some(ref_name)) => tags.extend(determine_image_name(\n                target,\n                &repository,\n                ref_type,\n                ref_name,\n                is_latest,\n                &version,\n            )?),\n            _ => {\n                if push && tag_override.is_none() {\n                    panic!(\"Refusing to push without tag or branch. Specify a repository and tag with `--repository <repository> --tag <tag>`\")\n                }\n                tags.push(target.image_name(&repository, \"local\"));\n            }\n        }\n\n        if let Some(ref tag) = tag_override {\n            tags = vec![target.image_name(&repository, tag)];\n        }\n\n        if engine.kind.supports_pull_flag() {\n            docker_build.arg(\"--pull\");\n        }\n        let base_name = format!(\"{repository}/{}\", target.name);\n        if no_cache {\n            docker_build.arg(\"--no-cache\");\n        } else if engine.kind.supports_cache_from_type() {\n            docker_build.args([\n                \"--cache-from\",\n                &cache_from.replace(\"{base_name}\", &base_name),\n            ]);\n        } else {\n            // we can't use `image_name` since podman doesn't support tags\n            // with `--cache-from`. podman only supports an image format\n            // of registry/repo although it does when pulling images. this\n            // affects building from cache with target+subs images since we\n            // can't use caches from registry. this is only an issue if\n            // building with podman without a local cache, which never\n            // happens in practice.\n            docker_build.args([\"--cache-from\", &base_name]);\n        }\n\n        if push {\n            docker_build.args([\"--cache-to\", \"type=inline\"]);\n        } else if let Some(ref cache_to) = cache_to {\n            docker_build.args([\"--cache-to\", &cache_to.replace(\"{base_name}\", &base_name)]);\n        }\n\n        for tag in &tags {\n            docker_build.args([\"--tag\", tag]);\n        }\n\n        for label in labels\n            .as_deref()\n            .unwrap_or(\"\")\n            .split('\\n')\n            .filter(|s| !s.is_empty())\n        {\n            docker_build.args([\"--label\", label]);\n        }\n\n        docker_build.cross_labels(&target.name, platform.target.triple());\n        docker_build.args([\"--file\", &dockerfile]);\n\n        docker_build.progress(progress)?;\n        docker_build.verbose(msg_info.verbosity);\n        for arg in &build_arg {\n            docker_build.args([\"--build-arg\", arg]);\n        }\n\n        if let Some(opts) = &build_opts {\n            docker_build.args(docker::Engine::parse_opts(opts)?);\n        }\n\n        docker_build.arg(match target.needs_workspace_root_context() {\n            true => root.as_path(),\n            false => Path::new(\".\"),\n        });\n\n        if !dry_run && (force || !push || gha) {\n            let result = docker_build\n                .run(msg_info, false)\n                .engine_warning(engine)\n                .buildkit_warning();\n            if gha && targets.len() > 1 {\n                if let Err(e) = &result {\n                    // TODO: Determine what instruction errorred, and place warning on that line with appropriate warning\n                    gha_error(&format!(\"file=docker/{dockerfile},title=Build failed::{e}\"));\n                }\n            }\n            results.push(\n                result\n                    .map(|_| target.clone())\n                    .map_err(|e| (target.clone(), e)),\n            );\n            if !no_fastfail && results.last().unwrap().is_err() {\n                break;\n            }\n        } else {\n            docker_build.print(msg_info)?;\n            if !dry_run {\n                msg_info.fatal(\"refusing to push, use --force to override\", 1);\n            }\n        }\n        if gha {\n            gha_output(\"image\", &tags[0])?;\n            gha_output(\"images\", &format!(\"'{}'\", serde_json::to_string(&tags)?))?;\n            if targets.len() > 1 {\n                gha_print(\"::endgroup::\");\n            }\n        }\n    }\n    if gha {\n        std::env::set_var(\"GITHUB_STEP_SUMMARY\", job_summary(&results)?);\n    }\n    if results.iter().any(|r| r.is_err()) {\n        #[allow(unknown_lints, clippy::manual_try_fold)]\n        return Err(crate::util::with_section_reports(\n            eyre::eyre!(\"some error(s) encountered\"),\n            results.into_iter().filter_map(Result::err).map(|e| e.1),\n        ));\n    }\n    Ok(())\n}\n\nfn has_native_image(\n    platform: &str,\n    target: &ImageTarget,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<bool> {\n    let note_host_target_detection = |msg_info: &mut MessageInfo| -> cross::Result<()> {\n        msg_info.note(\"using the rust target triple to determine the host triple to determine if the docker platform is native. this may fail if cross-compiling xtask.\")\n    };\n\n    Ok(match target.sub.as_deref() {\n        // FIXME: add additional subs for new Linux distros, such as alpine.\n        None | Some(\"centos\") => match (platform, target.name.as_str()) {\n            (\"linux/386\", \"i686-unknown-linux-gnu\")\n            | (\"linux/amd64\", \"x86_64-unknown-linux-gnu\")\n            | (\"linux/arm64\" | \"linux/arm64/v8\", \"aarch64-unknown-linux-gnu\")\n            | (\"linux/ppc64le\", \"powerpc64le-unknown-linux-gnu\")\n            | (\"linux/riscv64\", \"riscv64gc-unknown-linux-gnu\")\n            | (\"linux/s390x\", \"s390x-unknown-linux-gnu\") => true,\n            (\"linux/arm/v6\", \"arm-unknown-linux-gnueabi\") if target.is_armv6() => {\n                note_host_target_detection(msg_info)?;\n                true\n            }\n            (\"linux/arm\" | \"linux/arm/v7\", \"armv7-unknown-linux-gnueabihf\")\n                if target.is_armv7() =>\n            {\n                note_host_target_detection(msg_info)?;\n                true\n            }\n            _ => false,\n        },\n        Some(_) => false,\n    })\n}\n\npub fn determine_image_name(\n    target: &ImageTarget,\n    repository: &str,\n    ref_type: &str,\n    ref_name: &str,\n    is_latest: bool,\n    version: &str,\n) -> cross::Result<Vec<String>> {\n    let mut tags = vec![];\n    match (ref_type, ref_name) {\n        (\"tag\", ref_name) if ref_name.starts_with('v') => {\n            let tag_version = ref_name\n                .strip_prefix('v')\n                .expect(\"tag name should start with v\");\n            if version != tag_version {\n                eyre::bail!(\"git tag does not match package version.\")\n            }\n            tags.push(target.image_name(repository, version));\n            // Check for unstable releases, tag stable releases as `latest`\n            if is_latest {\n                tags.push(target.image_name(repository, \"latest\"))\n            }\n        }\n        (\"branch\", ref_name) => {\n            if let Some(gh_queue) = ref_name.strip_prefix(\"gh-readonly-queue/\") {\n                let (_, source) = gh_queue\n                    .split_once('/')\n                    .ok_or_else(|| eyre::eyre!(\"invalid gh-readonly-queue branch name\"))?;\n                tags.push(target.image_name(repository, source));\n            } else {\n                tags.push(target.image_name(repository, ref_name));\n            }\n\n            if [\"staging\", \"trying\"]\n                .iter()\n                .any(|branch| branch != &ref_name)\n            {\n                tags.push(target.image_name(repository, \"edge\"));\n            }\n        }\n        _ => eyre::bail!(\"no valid choice to pick for image name\"),\n    }\n    Ok(tags)\n}\n\npub fn job_summary(\n    results: &[Result<ImageTarget, (ImageTarget, eyre::ErrReport)>],\n) -> cross::Result<String> {\n    let mut summary = \"# SUMMARY\\n\\n\".to_string();\n    let success: Vec<_> = results.iter().filter_map(|r| r.as_ref().ok()).collect();\n    let errors: Vec<_> = results.iter().filter_map(|r| r.as_ref().err()).collect();\n\n    if !success.is_empty() {\n        summary.push_str(\"## Success\\n\\n| Target |\\n| ------ |\\n\");\n    }\n\n    for target in success {\n        writeln!(summary, \"| {} |\", target.alt())?;\n    }\n\n    if !errors.is_empty() {\n        // TODO: Tee error output and show in summary\n        summary.push_str(\"\\n## Errors\\n\\n| Target |\\n| ------ |\\n\");\n    }\n\n    for (target, _) in errors {\n        writeln!(summary, \"| {target} |\")?;\n    }\n    Ok(summary)\n}\n"
  },
  {
    "path": "xtask/src/changelog.rs",
    "content": "use std::cmp;\nuse std::collections::BTreeSet;\nuse std::fmt;\nuse std::fs;\nuse std::path::Path;\n\nuse crate::util::{project_dir, write_to_string};\nuse cross::shell::MessageInfo;\nuse cross::ToUtf8;\n\nuse chrono::{Datelike, Utc};\nuse clap::{Args, Subcommand};\nuse eyre::Context;\nuse serde::Deserialize;\n\npub fn changelog(args: Changelog, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    match args {\n        Changelog::Build(args) => build_changelog(args, msg_info),\n        Changelog::Validate(args) => validate_changelog(args, msg_info),\n    }\n}\n\n#[derive(Subcommand, Debug)]\npub enum Changelog {\n    /// Build the changelog.\n    Build(BuildChangelog),\n    /// Validate changelog entries.\n    Validate(ValidateChangelog),\n}\n\n#[derive(Args, Debug)]\npub struct BuildChangelog {\n    /// Build a release changelog.\n    #[clap(long, env = \"NEW_VERSION\", required = true)]\n    release: Option<String>,\n    /// Whether we're doing a dry run or not.\n    #[clap(long, env = \"DRY_RUN\")]\n    dry_run: bool,\n}\n\n#[derive(Args, Debug)]\npub struct ValidateChangelog {\n    /// List of changelog entries to validate.\n    files: Vec<String>,\n}\n\n// the type for the identifier: if it's a PR, sort\n// by the number, otherwise, sort as 0. the numbers\n// should be sorted, and the `max(values) || 0` should\n// be used\n#[derive(Debug, Clone, PartialEq, Eq)]\nenum IdType {\n    PullRequest(Vec<u64>),\n    Issue(Vec<u64>),\n}\n\nimpl IdType {\n    fn numbers(&self) -> &[u64] {\n        match self {\n            IdType::PullRequest(v) => v,\n            IdType::Issue(v) => v,\n        }\n    }\n\n    fn max_number(&self) -> u64 {\n        self.numbers().iter().max().map_or_else(|| 0, |v| *v)\n    }\n\n    fn parse_stem(file_stem: &str) -> cross::Result<IdType> {\n        let (is_issue, rest) = match file_stem.strip_prefix(\"issue\") {\n            Some(n) => (true, n),\n            None => (false, file_stem),\n        };\n        let mut numbers = rest\n            .split('-')\n            .map(|x| x.parse::<u64>())\n            .collect::<Result<Vec<u64>, _>>()?;\n        numbers.sort_unstable();\n\n        Ok(match is_issue {\n            false => IdType::PullRequest(numbers),\n            true => IdType::Issue(numbers),\n        })\n    }\n\n    fn parse_changelog(prs: &str) -> cross::Result<IdType> {\n        let mut numbers = prs\n            .split(',')\n            .map(|x| x.trim().parse::<u64>())\n            .collect::<Result<Vec<u64>, _>>()?;\n        numbers.sort_unstable();\n\n        Ok(IdType::PullRequest(numbers))\n    }\n}\n\nimpl cmp::PartialOrd for IdType {\n    fn partial_cmp(&self, other: &IdType) -> Option<cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl cmp::Ord for IdType {\n    fn cmp(&self, other: &IdType) -> cmp::Ordering {\n        self.max_number().cmp(&other.max_number())\n    }\n}\n\n#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"lowercase\")]\nenum ChangelogType {\n    Added,\n    Changed,\n    Fixed,\n    Removed,\n    Internal,\n}\n\nimpl ChangelogType {\n    fn from_header(s: &str) -> cross::Result<Self> {\n        Ok(match s {\n            \"Added\" => Self::Added,\n            \"Changed\" => Self::Changed,\n            \"Fixed\" => Self::Fixed,\n            \"Removed\" => Self::Removed,\n            \"Internal\" => Self::Internal,\n            _ => eyre::bail!(\"invalid header section, got {s}\"),\n        })\n    }\n\n    fn sort_by(&self) -> u32 {\n        match self {\n            ChangelogType::Added => 4,\n            ChangelogType::Changed => 3,\n            ChangelogType::Fixed => 2,\n            ChangelogType::Removed => 1,\n            ChangelogType::Internal => 0,\n        }\n    }\n}\n\nimpl cmp::PartialOrd for ChangelogType {\n    fn partial_cmp(&self, other: &ChangelogType) -> Option<cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl cmp::Ord for ChangelogType {\n    fn cmp(&self, other: &ChangelogType) -> cmp::Ordering {\n        self.sort_by().cmp(&other.sort_by())\n    }\n}\n\n// internal type for a changelog, just containing the contents\n#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]\nstruct ChangelogContents {\n    description: String,\n    #[serde(default)]\n    issues: Vec<u64>,\n    #[serde(default)]\n    breaking: bool,\n    #[serde(rename = \"type\")]\n    kind: ChangelogType,\n}\n\nimpl ChangelogContents {\n    fn sort_by(&self) -> (&ChangelogType, &str, &bool) {\n        (&self.kind, &self.description, &self.breaking)\n    }\n}\n\nimpl cmp::PartialOrd for ChangelogContents {\n    fn partial_cmp(&self, other: &ChangelogContents) -> Option<cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl cmp::Ord for ChangelogContents {\n    fn cmp(&self, other: &ChangelogContents) -> cmp::Ordering {\n        self.sort_by().cmp(&other.sort_by())\n    }\n}\n\nimpl fmt::Display for ChangelogContents {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if self.breaking {\n            f.write_str(\"BREAKING: \")?;\n        }\n        f.write_str(&self.description)\n    }\n}\n\n#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]\nstruct ChangelogEntry {\n    id: IdType,\n    contents: ChangelogContents,\n}\n\nimpl ChangelogEntry {\n    fn new(id: IdType, contents: ChangelogContents) -> Self {\n        Self { id, contents }\n    }\n\n    fn parse(s: &str, kind: ChangelogType) -> cross::Result<Self> {\n        let (id, rest) = match s.split_once('-') {\n            Some((prefix, rest)) => match prefix.trim().strip_prefix('#') {\n                Some(prs) => (IdType::parse_changelog(prs)?, rest),\n                None => (IdType::Issue(vec![]), s),\n            },\n            None => (IdType::Issue(vec![]), s),\n        };\n\n        let trimmed = rest.trim();\n        let (breaking, description) = match trimmed.strip_prefix(\"BREAKING: \") {\n            Some(d) => (true, d.trim().to_owned()),\n            None => (false, trimmed.to_owned()),\n        };\n\n        Ok(ChangelogEntry {\n            id,\n            contents: ChangelogContents {\n                kind,\n                breaking,\n                description,\n                issues: vec![],\n            },\n        })\n    }\n\n    fn from_object(id: IdType, value: serde_json::Value) -> cross::Result<Self> {\n        Ok(Self::new(id, serde_json::value::from_value(value)?))\n    }\n\n    fn from_value(id: IdType, mut value: serde_json::Value) -> cross::Result<Vec<Self>> {\n        let mut result = vec![];\n        if value.is_array() {\n            for item in value.as_array_mut().expect(\"must be array\") {\n                result.push(Self::from_object(id.clone(), item.take())?);\n            }\n        } else {\n            result.push(Self::from_object(id, value)?);\n        }\n\n        Ok(result)\n    }\n}\n\nimpl fmt::Display for ChangelogEntry {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"-\")?;\n        match &self.id {\n            IdType::PullRequest(prs) => f.write_fmt(format_args!(\n                \" #{} -\",\n                prs.iter()\n                    .map(|x| x.to_string())\n                    .collect::<Vec<String>>()\n                    .join(\",#\")\n            ))?,\n            IdType::Issue(_) => (),\n        }\n        f.write_fmt(format_args!(\" {}\", self.contents))?;\n        f.write_str(\"\\n\")\n    }\n}\n\n// de-duplicate in place\nfn deduplicate_entries(original: &mut Vec<ChangelogEntry>) {\n    let mut result = Vec::with_capacity(original.len());\n    let mut memo = BTreeSet::new();\n    for item in original.iter() {\n        if memo.insert(item.to_string()) {\n            result.push(item.clone());\n        }\n    }\n\n    *original = result;\n}\n\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\nstruct Changes {\n    added: Vec<ChangelogEntry>,\n    changed: Vec<ChangelogEntry>,\n    fixed: Vec<ChangelogEntry>,\n    removed: Vec<ChangelogEntry>,\n    internal: Vec<ChangelogEntry>,\n}\n\nimpl Changes {\n    fn sort_descending(&mut self) {\n        self.added.sort_by(|x, y| y.cmp(x));\n        self.changed.sort_by(|x, y| y.cmp(x));\n        self.fixed.sort_by(|x, y| y.cmp(x));\n        self.removed.sort_by(|x, y| y.cmp(x));\n        self.internal.sort_by(|x, y| y.cmp(x));\n    }\n\n    fn deduplicate(&mut self) {\n        deduplicate_entries(&mut self.added);\n        deduplicate_entries(&mut self.changed);\n        deduplicate_entries(&mut self.fixed);\n        deduplicate_entries(&mut self.removed);\n        deduplicate_entries(&mut self.internal);\n    }\n\n    fn merge(&mut self, other: &mut Self) {\n        self.added.append(&mut other.added);\n        self.changed.append(&mut other.changed);\n        self.fixed.append(&mut other.fixed);\n        self.removed.append(&mut other.removed);\n        self.internal.append(&mut other.internal);\n    }\n\n    fn push(&mut self, entry: ChangelogEntry) {\n        match entry.contents.kind {\n            ChangelogType::Added => self.added.push(entry),\n            ChangelogType::Changed => self.changed.push(entry),\n            ChangelogType::Fixed => self.fixed.push(entry),\n            ChangelogType::Removed => self.removed.push(entry),\n            ChangelogType::Internal => self.internal.push(entry),\n        }\n    }\n}\n\nmacro_rules! fmt_changelog_vec {\n    ($self:ident, $fmt:ident, $field:ident, $header:literal) => {{\n        if !$self.$field.is_empty() {\n            $fmt.write_str(concat!(\"\\n### \", $header, \"\\n\\n\"))?;\n            for entry in &$self.$field {\n                $fmt.write_fmt(format_args!(\"{}\", entry))?;\n            }\n        }\n    }};\n}\n\nimpl fmt::Display for Changes {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt_changelog_vec!(self, f, added, \"Added\");\n        fmt_changelog_vec!(self, f, changed, \"Changed\");\n        fmt_changelog_vec!(self, f, fixed, \"Fixed\");\n        fmt_changelog_vec!(self, f, removed, \"Removed\");\n        fmt_changelog_vec!(self, f, internal, \"Internal\");\n\n        Ok(())\n    }\n}\n\nfn file_stem(path: &Path) -> cross::Result<&str> {\n    path.file_stem()\n        .ok_or(eyre::eyre!(\"unable to get file stem {path:?}\"))?\n        .to_utf8()\n}\n\nfn read_changes(changes_dir: &Path) -> cross::Result<Changes> {\n    let mut changes = Changes::default();\n    for entry in fs::read_dir(changes_dir)? {\n        let entry = entry?;\n        let file_type = entry.file_type()?;\n        let file_name = entry.file_name();\n        let path = entry.path();\n        let ext = path.extension();\n        if file_type.is_file() && ext.is_some_and(|v| v == \"json\") {\n            let stem = file_stem(&path)?;\n            let id = IdType::parse_stem(stem)?;\n            let contents = fs::read_to_string(path)?;\n            let value = serde_json::from_str(&contents)\n                .wrap_err_with(|| format!(\"unable to parse JSON for {file_name:?}\"))?;\n            let new_entries = ChangelogEntry::from_value(id, value)\n                .wrap_err_with(|| format!(\"unable to extract changelog from {file_name:?}\"))?;\n            for change in new_entries {\n                match change.contents.kind {\n                    ChangelogType::Added => changes.added.push(change),\n                    ChangelogType::Changed => changes.changed.push(change),\n                    ChangelogType::Fixed => changes.fixed.push(change),\n                    ChangelogType::Removed => changes.removed.push(change),\n                    ChangelogType::Internal => changes.internal.push(change),\n                }\n            }\n        }\n    }\n\n    Ok(changes)\n}\n\nfn read_changelog(root: &Path) -> cross::Result<(String, Changes, String)> {\n    let lines: Vec<String> = fs::read_to_string(root.join(\"CHANGELOG.md\"))?\n        .lines()\n        .map(ToOwned::to_owned)\n        .collect();\n\n    let next_index = lines\n        .iter()\n        .position(|x| x.trim().starts_with(\"## [Unreleased]\"))\n        .ok_or(eyre::eyre!(\"could not find unreleased section\"))?;\n    let (header, rest) = lines.split_at(next_index);\n\n    // need to skip the first index since it's previously\n    // matched, and then just increment our split by 1.\n    let last_index = 1 + rest[1..]\n        .iter()\n        .position(|x| x.trim().starts_with(\"## \"))\n        .ok_or(eyre::eyre!(\"could not find the next release section\"))?;\n    let (section, footer) = rest.split_at(last_index);\n\n    // the unreleased should have the format:\n    //  ## [Unreleased] - ReleaseDate\n    //\n    //  ### Added\n    //\n    //  - #905 - ...\n    let mut kind = None;\n    let mut changes = Changes::default();\n    for line in section {\n        let line = line.trim();\n        if let Some(header) = line.strip_prefix(\"### \") {\n            kind = Some(ChangelogType::from_header(header)?);\n        } else if let Some(entry) = line.strip_prefix(\"- \") {\n            match kind {\n                Some(kind) => changes.push(ChangelogEntry::parse(entry, kind)?),\n                None => eyre::bail!(\"changelog entry \\\"{line}\\\" without header\"),\n            }\n        } else if !(line.is_empty() || line == \"## [Unreleased] - ReleaseDate\") {\n            eyre::bail!(\"invalid changelog entry, got \\\"{line}\\\"\");\n        }\n    }\n\n    Ok((header.join(\"\\n\"), changes, footer.join(\"\\n\")))\n}\n\nfn delete_changes(root: &Path) -> cross::Result<()> {\n    // move all files to the denoted version release\n    for entry in fs::read_dir(root.join(\".changes\"))? {\n        let entry = entry?;\n        let file_type = entry.file_type()?;\n        let srcpath = entry.path();\n        let ext = srcpath.extension();\n        if file_type.is_file() && ext.is_some_and(|v| v == \"json\") {\n            fs::remove_file(srcpath)?;\n        }\n    }\n\n    Ok(())\n}\n\n/// Get the date as a year/month/day tuple.\npub fn get_current_date() -> String {\n    let utc = Utc::now();\n    let date = utc.date_naive();\n\n    format!(\"{}-{:0>2}-{}\", date.year(), date.month(), date.day())\n}\n\n// used for internal testing\nfn build_changelog_from_dir(\n    root: &Path,\n    changes_dir: &Path,\n    release: Option<&str>,\n) -> cross::Result<String> {\n    use std::fmt::Write;\n\n    let mut new = read_changes(changes_dir)?;\n    let (header, mut existing, footer) = read_changelog(root)?;\n    new.merge(&mut existing);\n    new.deduplicate();\n    new.sort_descending();\n\n    let mut output = header;\n    output.push_str(\"\\n## [Unreleased] - ReleaseDate\\n\");\n    if let Some(release) = release {\n        let version = semver::Version::parse(release)?;\n        if version.pre.is_empty() {\n            let date = get_current_date();\n            writeln!(&mut output, \"\\n## [v{release}] - {date}\")?;\n        }\n    }\n    output.push_str(&new.to_string());\n    output.push('\\n');\n    output.push_str(&footer);\n\n    Ok(output)\n}\n\npub fn build_changelog(\n    BuildChangelog {\n        dry_run, release, ..\n    }: BuildChangelog,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    msg_info.info(\"Building the changelog.\")?;\n    msg_info.debug(format_args!(\n        \"Running with dry-run set the {dry_run} and with release {release:?}\"\n    ))?;\n\n    let root = project_dir(msg_info)?;\n    let changes_dir = root.join(\".changes\");\n    let output = build_changelog_from_dir(&root, &changes_dir, release.as_deref())?;\n\n    let filename = match !dry_run && release.is_some() {\n        true => {\n            delete_changes(&root)?;\n            \"CHANGELOG.md\"\n        }\n        false => \"CHANGELOG.md.draft\",\n    };\n    let path = root.join(filename);\n    write_to_string(&path, &output)?;\n    #[allow(clippy::disallowed_methods)]\n    msg_info.info(format_args!(\"Changelog written to `{}`\", path.display()))?;\n\n    Ok(())\n}\n\n#[allow(clippy::disallowed_methods)]\npub fn validate_changelog(\n    ValidateChangelog { mut files, .. }: ValidateChangelog,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let root = project_dir(msg_info)?;\n    let changes_dir = root.join(\".changes\");\n    if files.is_empty() {\n        files = fs::read_dir(&changes_dir)?\n            .filter_map(|x| x.ok())\n            .filter(|x| x.file_type().is_ok_and(|v| v.is_file()))\n            .filter_map(|x| {\n                if x.path()\n                    .extension()\n                    .and_then(|s: &std::ffi::OsStr| s.to_str())\n                    .unwrap_or_default()\n                    == \"json\"\n                {\n                    Some(x.file_name().to_utf8().unwrap().to_owned())\n                } else {\n                    None\n                }\n            })\n            .collect();\n    }\n    let mut errors = vec![];\n    for file in files {\n        let file_name = Path::new(&file);\n        let path = changes_dir.join(file_name);\n        let stem = file_stem(&path)?;\n        let contents = fs::read_to_string(&path)\n            .wrap_err_with(|| eyre::eyre!(\"cannot find file {}\", path.display()))?;\n\n        let id = match IdType::parse_stem(stem)\n            .wrap_err_with(|| format!(\"unable to parse file stem for \\\"{}\\\"\", path.display()))\n        {\n            Ok(id) => id,\n            Err(e) => {\n                errors.push(e);\n                continue;\n            }\n        };\n\n        let value = match serde_json::from_str(&contents)\n            .wrap_err_with(|| format!(\"unable to parse JSON for \\\"{}\\\"\", path.display()))\n        {\n            Ok(value) => value,\n            Err(e) => {\n                errors.push(e);\n                continue;\n            }\n        };\n\n        let res = ChangelogEntry::from_value(id, value)\n            .wrap_err_with(|| format!(\"unable to extract changelog from \\\"{}\\\"\", path.display()))\n            .map(|_| ());\n        errors.extend(res.err());\n    }\n\n    if !errors.is_empty() {\n        return Err(crate::util::with_section_reports(\n            eyre::eyre!(\"some files were not validated\"),\n            errors,\n        ));\n    }\n    // also need to validate the existing changelog\n    let _ = read_changelog(&root)?;\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    macro_rules! s {\n        ($x:literal) => {\n            $x.to_owned()\n        };\n    }\n\n    #[test]\n    fn test_id_type_parse_stem() -> cross::Result<()> {\n        assert_eq!(IdType::parse_stem(\"645\")?, IdType::PullRequest(vec![645]));\n        assert_eq!(\n            IdType::parse_stem(\"640-645\")?,\n            IdType::PullRequest(vec![640, 645])\n        );\n        assert_eq!(\n            IdType::parse_stem(\"issue640-645\")?,\n            IdType::Issue(vec![640, 645])\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_id_type_parse_changelog() -> cross::Result<()> {\n        assert_eq!(\n            IdType::parse_changelog(\"645\")?,\n            IdType::PullRequest(vec![645])\n        );\n        assert_eq!(\n            IdType::parse_changelog(\"640,645\")?,\n            IdType::PullRequest(vec![640, 645])\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn changelog_type_sort() {\n        assert!(ChangelogType::Added > ChangelogType::Changed);\n        assert!(ChangelogType::Changed > ChangelogType::Fixed);\n    }\n\n    #[test]\n    fn change_log_type_from_header() -> cross::Result<()> {\n        assert_eq!(ChangelogType::from_header(\"Added\")?, ChangelogType::Added);\n\n        Ok(())\n    }\n\n    #[test]\n    fn changelog_contents_deserialize() -> cross::Result<()> {\n        let actual: ChangelogContents = serde_json::from_str(CHANGES_OBJECT)?;\n        let expected = ChangelogContents {\n            description: s!(\"sample description for a PR adding one CHANGELOG entry.\"),\n            issues: vec![437],\n            breaking: false,\n            kind: ChangelogType::Fixed,\n        };\n        assert_eq!(actual, expected);\n\n        let actual: Vec<ChangelogContents> = serde_json::from_str(CHANGES_ARRAY)?;\n        let expected = vec![\n            ChangelogContents {\n                description: s!(\"this is one added entry.\"),\n                issues: vec![630],\n                breaking: false,\n                kind: ChangelogType::Added,\n            },\n            ChangelogContents {\n                description: s!(\"this is another added entry.\"),\n                issues: vec![642],\n                breaking: false,\n                kind: ChangelogType::Added,\n            },\n            ChangelogContents {\n                description: s!(\"this is a fixed entry that has no attached issue.\"),\n                issues: vec![],\n                breaking: false,\n                kind: ChangelogType::Fixed,\n            },\n            ChangelogContents {\n                description: s!(\"this is a breaking change.\"),\n                issues: vec![679],\n                breaking: true,\n                kind: ChangelogType::Changed,\n            },\n        ];\n        assert_eq!(actual, expected);\n\n        Ok(())\n    }\n\n    #[test]\n    fn changelog_entry_display() {\n        let mut entry = ChangelogEntry::new(\n            IdType::PullRequest(vec![637]),\n            ChangelogContents {\n                description: s!(\"this is one added entry.\"),\n                issues: vec![630],\n                breaking: false,\n                kind: ChangelogType::Added,\n            },\n        );\n        assert_eq!(entry.to_string(), s!(\"- #637 - this is one added entry.\\n\"));\n\n        entry.contents.breaking = true;\n        assert_eq!(\n            entry.to_string(),\n            s!(\"- #637 - BREAKING: this is one added entry.\\n\")\n        );\n\n        entry.id = IdType::Issue(vec![640]);\n        assert_eq!(\n            entry.to_string(),\n            s!(\"- BREAKING: this is one added entry.\\n\")\n        );\n\n        entry.contents.breaking = false;\n        assert_eq!(entry.to_string(), s!(\"- this is one added entry.\\n\"));\n    }\n\n    #[test]\n    fn read_template_changes() -> cross::Result<()> {\n        let mut msg_info = MessageInfo::default();\n        let root = project_dir(&mut msg_info)?;\n\n        let mut actual = read_changes(&root.join(\".changes\").join(\"template\"))?;\n        actual.sort_descending();\n        let expected = Changes {\n            added: vec![\n                ChangelogEntry::new(\n                    IdType::PullRequest(vec![979, 981]),\n                    ChangelogContents {\n                        description: s!(\"this has 2 PRs associated.\"),\n                        issues: vec![441],\n                        breaking: false,\n                        kind: ChangelogType::Added,\n                    },\n                ),\n                ChangelogEntry::new(\n                    IdType::PullRequest(vec![940]),\n                    ChangelogContents {\n                        description: s!(\"this is one added entry.\"),\n                        issues: vec![630],\n                        breaking: false,\n                        kind: ChangelogType::Added,\n                    },\n                ),\n                ChangelogEntry::new(\n                    IdType::PullRequest(vec![940]),\n                    ChangelogContents {\n                        description: s!(\"this is another added entry.\"),\n                        issues: vec![642],\n                        breaking: false,\n                        kind: ChangelogType::Added,\n                    },\n                ),\n            ],\n            changed: vec![ChangelogEntry::new(\n                IdType::PullRequest(vec![940]),\n                ChangelogContents {\n                    description: s!(\"this is a breaking change.\"),\n                    issues: vec![679],\n                    breaking: true,\n                    kind: ChangelogType::Changed,\n                },\n            )],\n            fixed: vec![\n                ChangelogEntry::new(\n                    IdType::PullRequest(vec![978]),\n                    ChangelogContents {\n                        description: s!(\"sample description for a PR adding one CHANGELOG entry.\"),\n                        issues: vec![437],\n                        breaking: false,\n                        kind: ChangelogType::Fixed,\n                    },\n                ),\n                ChangelogEntry::new(\n                    IdType::PullRequest(vec![940]),\n                    ChangelogContents {\n                        description: s!(\"this is a fixed entry that has no attached issue.\"),\n                        issues: vec![],\n                        breaking: false,\n                        kind: ChangelogType::Fixed,\n                    },\n                ),\n                ChangelogEntry::new(\n                    IdType::Issue(vec![440]),\n                    ChangelogContents {\n                        description: s!(\"no associated PR.\"),\n                        issues: vec![440],\n                        breaking: false,\n                        kind: ChangelogType::Fixed,\n                    },\n                ),\n            ],\n            removed: vec![],\n            internal: vec![],\n        };\n        assert_eq!(actual, expected);\n\n        Ok(())\n    }\n\n    #[test]\n    fn read_template_changelog() -> cross::Result<()> {\n        let mut msg_info = MessageInfo::default();\n        let root = project_dir(&mut msg_info)?;\n\n        let (_, mut actual, _) = read_changelog(&root.join(\".changes\").join(\"template\"))?;\n        actual.sort_descending();\n        let expected = ChangelogEntry::new(\n            IdType::PullRequest(vec![905]),\n            ChangelogContents {\n                description: s!(\"added qemu emulation to `i586-unknown-linux-gnu`, `i686-unknown-linux-musl`, and `i586-unknown-linux-gnu`, so they can run on an `x86` CPU, rather than an `x86_64` CPU.\"),\n                issues: vec![],\n                breaking: false,\n                kind: ChangelogType::Added,\n            },\n        );\n        assert_eq!(actual.added[0], expected);\n\n        let expected = ChangelogEntry::new(\n            IdType::PullRequest(vec![869]),\n            ChangelogContents {\n                description: s!(\"ensure cargo configuration environment variable flags are passed to the docker container.\"),\n                issues: vec![],\n                breaking: false,\n                kind: ChangelogType::Changed,\n            },\n        );\n        assert_eq!(actual.changed[0], expected);\n\n        let expected = ChangelogEntry::new(\n            IdType::PullRequest(vec![905]),\n            ChangelogContents {\n                description: s!(\"fixed running dynamically-linked libraries for all musl targets except `x86_64-unknown-linux-musl`.\"),\n                issues: vec![],\n                breaking: false,\n                kind: ChangelogType::Fixed,\n            },\n        );\n        assert_eq!(actual.fixed[0], expected);\n        assert_eq!(actual.removed.len(), 0);\n        assert_eq!(actual.internal.len(), 0);\n\n        Ok(())\n    }\n\n    fn build_changelog_test(release: Option<&str>) -> cross::Result<String> {\n        let mut msg_info = MessageInfo::default();\n        let root = project_dir(&mut msg_info)?;\n        let changes_dir = root.join(\".changes\").join(\"template\");\n\n        build_changelog_from_dir(&changes_dir, &changes_dir, release)\n    }\n\n    #[test]\n    fn test_build_changelog_no_release() -> cross::Result<()> {\n        let output = build_changelog_test(None)?;\n        let lines: Vec<&str> = output.lines().collect();\n\n        assert_eq!(lines[10], \"- #979,#981 - this has 2 PRs associated.\");\n        assert_eq!(lines[11], \"- #940 - this is one added entry.\");\n        assert_eq!(\n            lines[36],\n            \"- #885 - handle symlinks when using remote docker.\"\n        );\n        assert_eq!(lines[39], \"- no associated PR.\");\n        assert_eq!(\n            &lines[6..12],\n            &[\n                \"## [Unreleased] - ReleaseDate\",\n                \"\",\n                \"### Added\",\n                \"\",\n                \"- #979,#981 - this has 2 PRs associated.\",\n                \"- #940 - this is one added entry.\",\n            ]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_build_changelog_dev_release() -> cross::Result<()> {\n        let output = build_changelog_test(Some(\"0.2.4-alpha\"))?;\n        let lines: Vec<&str> = output.lines().collect();\n\n        assert_eq!(\n            &lines[6..12],\n            &[\n                \"## [Unreleased] - ReleaseDate\",\n                \"\",\n                \"### Added\",\n                \"\",\n                \"- #979,#981 - this has 2 PRs associated.\",\n                \"- #940 - this is one added entry.\",\n            ]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_build_changelog_release() -> cross::Result<()> {\n        let output = build_changelog_test(Some(\"0.2.4\"))?;\n        let lines: Vec<&str> = output.lines().collect();\n        let date = get_current_date();\n\n        assert_eq!(\n            &lines[6..14],\n            &[\n                \"## [Unreleased] - ReleaseDate\",\n                \"\",\n                &format!(\"## [v0.2.4] - {date}\"),\n                \"\",\n                \"### Added\",\n                \"\",\n                \"- #979,#981 - this has 2 PRs associated.\",\n                \"- #940 - this is one added entry.\",\n            ]\n        );\n\n        Ok(())\n    }\n\n    static CHANGES_OBJECT: &str = r#\"\n    {\n        \"description\": \"sample description for a PR adding one CHANGELOG entry.\",\n        \"issues\": [437],\n        \"type\": \"fixed\"\n    }\n    \"#;\n\n    static CHANGES_ARRAY: &str = r#\"\n    [\n        {\n            \"description\": \"this is one added entry.\",\n            \"issues\": [630],\n            \"type\": \"added\"\n        },\n        {\n            \"description\": \"this is another added entry.\",\n            \"issues\": [642],\n            \"type\": \"added\"\n        },\n        {\n            \"description\": \"this is a fixed entry that has no attached issue.\",\n            \"type\": \"fixed\"\n        },\n        {\n            \"description\": \"this is a breaking change.\",\n            \"issues\": [679],\n            \"breaking\": true,\n            \"type\": \"changed\"\n        }\n    ]\n    \"#;\n}\n"
  },
  {
    "path": "xtask/src/ci/target_matrix.rs",
    "content": "use std::process::Command;\n\nuse clap::builder::{BoolishValueParser, PossibleValuesParser};\nuse clap::Parser;\nuse cross::{shell::Verbosity, CommandExt};\nuse serde::{Deserialize, Serialize};\n\nuse crate::util::{get_matrix, gha_output, gha_print, CiTarget, ImageTarget};\n\n#[derive(Parser, Debug)]\npub struct TargetMatrix {\n    /// check is being run as part of a weekly check\n    #[clap(long)]\n    pub weekly: bool,\n    /// merge group that is being checked.\n    #[clap(long)]\n    pub merge_group: Option<String>,\n    #[clap(subcommand)]\n    pub subcommand: Option<TargetMatrixSub>,\n}\n\n#[derive(Parser, Debug)]\npub enum TargetMatrixSub {\n    Try {\n        /// pr to check\n        #[clap(long)]\n        pr: String,\n        /// comment to check\n        #[clap(long)]\n        comment: String,\n    },\n}\n\nimpl TargetMatrix {\n    pub(crate) fn run(&self) -> Result<(), color_eyre::Report> {\n        let mut matrix: Vec<CiTarget> = get_matrix().clone();\n        matrix.retain(|t| !t.disabled);\n        let mut is_default_try = false;\n        let pr: Option<String>;\n        let (prs, mut app) = match self {\n            TargetMatrix {\n                merge_group: Some(ref_),\n                ..\n            } => (\n                vec![process_merge_group(ref_)?],\n                TargetMatrixArgs::default(),\n            ),\n            TargetMatrix { weekly: true, .. } => (\n                vec![],\n                TargetMatrixArgs {\n                    target: std::env::var(\"TARGETS\")\n                        .unwrap_or_default()\n                        .split(' ')\n                        .flat_map(|s| s.split(','))\n                        .filter(|s| !s.is_empty())\n                        .map(|s| s.to_owned())\n                        .collect(),\n                    std: None,\n                    cpp: None,\n                    dylib: None,\n                    run: None,\n                    runners: vec![],\n                    none: false,\n                    has_image: true,\n                    verbose: false,\n                    cross_debug: false,\n                    tests: vec![\"all\".to_owned()],\n                },\n            ),\n            TargetMatrix {\n                subcommand: Some(TargetMatrixSub::Try { pr, comment }),\n                ..\n            } => {\n                let process_try_comment = process_try_comment(comment)?;\n                is_default_try = process_try_comment.0;\n                (vec![pr.as_ref()], process_try_comment.1)\n            }\n            _ => {\n                pr = current_pr();\n                (\n                    pr.iter().map(|s| s.as_str()).collect(),\n                    TargetMatrixArgs::default(),\n                )\n            }\n        };\n\n        // only apply ci labels on prs and `/ci try`,\n        // if the try command is not the default, we don't want to apply ci labels\n        if matches!(\n            self,\n            Self {\n                weekly: false,\n                merge_group: Some(_) | None,\n                subcommand: None,\n            }\n        ) || is_default_try\n        {\n            apply_ci_labels(&prs, &mut app)?\n        }\n\n        app.filter(&mut matrix);\n\n        let matrix = matrix\n            .iter()\n            .map(|target| TargetMatrixElement {\n                pretty: target.to_image_target().alt(),\n                platforms: target.platforms(),\n                target: &target.target,\n                sub: target.sub.as_deref(),\n                os: &target.os,\n                run: target.run.map(|b| b as u8),\n                deploy: target.deploy.map(|b| b as u8),\n                build_std: target.build_std.map(|b| b as u8),\n                cpp: target.cpp.map(|b| b as u8),\n                dylib: target.dylib.map(|b| b as u8),\n                runners: target.runners.as_deref(),\n                std: target.std.map(|b| b as u8),\n                verbose: app.verbose,\n                cross_debug: app.cross_debug,\n            })\n            .collect::<Vec<_>>();\n\n        let json = serde_json::to_string(&matrix)?;\n        gha_output(\"matrix\", &json)?;\n        let tests = serde_json::to_string(&app.tests()?)?;\n        gha_output(\"tests\", &tests)?;\n        Ok(())\n    }\n}\n\nfn apply_ci_labels(prs: &[&str], app: &mut TargetMatrixArgs) -> Result<(), eyre::Error> {\n    apply_has_no_ci_tests(prs, app)?;\n    apply_has_no_ci_target(prs, app)?;\n\n    let mut to_add = vec![];\n    'pr_loop: for pr in prs {\n        let labels = parse_gh_labels(pr)?;\n        let targets = labels\n            .iter()\n            .filter_map(|label| label.strip_prefix(\"CI-\"))\n            .collect::<Vec<_>>();\n        if targets.is_empty() {\n            // if a pr doesn't specify a target, assume it affects all targets\n            to_add.clear();\n            break 'pr_loop;\n        }\n        to_add.extend(targets.iter().map(|label| label.to_string()));\n    }\n    app.target.extend(to_add);\n    Ok(())\n}\n\nfn apply_has_no_ci_tests(prs: &[&str], app: &mut TargetMatrixArgs) -> Result<(), eyre::Error> {\n    if !prs.is_empty()\n        && prs.iter().try_fold(true, |b, pr| {\n            Ok::<_, eyre::Report>(b && has_no_ci_tests_label(pr)?)\n        })?\n    {\n        app.none = true;\n        app.tests = vec![\"none\".to_owned()];\n    }\n    Ok(())\n}\n\nfn apply_has_no_ci_target(prs: &[&str], app: &mut TargetMatrixArgs) -> Result<(), eyre::Error> {\n    if !prs.is_empty()\n        && prs.iter().try_fold(true, |b, pr| {\n            Ok::<_, eyre::Report>(b && has_no_ci_target_label(pr)?)\n        })?\n    {\n        app.none = true;\n    }\n    Ok(())\n}\n\nfn parse_gh_labels(pr: &str) -> cross::Result<Vec<String>> {\n    #[derive(Deserialize)]\n    struct PullRequest {\n        labels: Vec<PullRequestLabels>,\n    }\n\n    #[derive(Deserialize)]\n    struct PullRequestLabels {\n        name: String,\n    }\n    eyre::ensure!(\n        pr.chars().all(|c| c.is_ascii_digit()),\n        \"pr should be a number, got {:?}\",\n        pr\n    );\n    let stdout = Command::new(\"gh\")\n        .args([\"pr\", \"view\", pr, \"--json\", \"labels\"])\n        .run_and_get_stdout(&mut Verbosity::Quiet.into())?;\n    let pr_info: PullRequest = serde_json::from_str(&stdout)?;\n    Ok(pr_info.labels.into_iter().map(|l| l.name).collect())\n}\n\nfn has_no_ci_target_label(pr: &str) -> cross::Result<bool> {\n    Ok(parse_gh_labels(pr)?.contains(&\"no-ci-targets\".to_owned()))\n}\n\nfn has_no_ci_tests_label(pr: &str) -> cross::Result<bool> {\n    Ok(parse_gh_labels(pr)?.contains(&\"no-ci-tests\".to_owned()))\n}\n\n/// Convert a `GITHUB_REF` into it's merge group pr\nfn process_merge_group(ref_: &str) -> cross::Result<&str> {\n    ref_.split('/')\n        .next_back()\n        .unwrap_or_default()\n        .strip_prefix(\"pr-\")\n        .ok_or_else(|| eyre::eyre!(\"merge group ref must start last / segment with \\\"pr-\\\"\"))?\n        .split('-')\n        .next()\n        .ok_or_else(|| eyre::eyre!(\"merge group ref must include \\\"pr-<num>-<sha>\\\"\"))\n}\n\nfn current_pr() -> Option<String> {\n    // gh pr view --json number --template \"{{.number}}\"\n    let stdout = Command::new(\"gh\")\n        .args([\"pr\", \"view\", \"--json\", \"number\"])\n        .run_and_get_stdout(&mut Verbosity::Quiet.into())\n        .ok()?;\n    let pr_info: serde_json::Value = serde_json::from_str(&stdout).ok()?;\n    pr_info.get(\"number\").map(|n| n.to_string())\n}\n\n/// Returns app to use for matrix on try comment, boolean is used to determine if its a try without arguments\nfn process_try_comment(message: &str) -> cross::Result<(bool, TargetMatrixArgs)> {\n    for line in message.lines() {\n        let command = if let Some(command) = line.strip_prefix(\"/ci try\") {\n            command.trim()\n        } else {\n            continue;\n        };\n        if command.is_empty() {\n            return Ok((true, TargetMatrixArgs::default()));\n        } else {\n            return Ok((false, TargetMatrixArgs::parse_from(command.split(' '))));\n        }\n    }\n    eyre::bail!(\"no /ci try command found in comment\")\n}\n\n#[derive(Serialize)]\n#[serde(rename_all = \"kebab-case\")]\nstruct TargetMatrixElement<'a> {\n    pretty: String,\n    platforms: &'a [String],\n    target: &'a str,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    sub: Option<&'a str>,\n    os: &'a str,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    run: Option<u8>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    deploy: Option<u8>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    build_std: Option<u8>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    cpp: Option<u8>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    dylib: Option<u8>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    runners: Option<&'a str>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    std: Option<u8>,\n    verbose: bool,\n    #[serde(skip_serializing_if = \"bool_negate\")]\n    cross_debug: bool,\n}\n\nfn bool_negate(b: &bool) -> bool {\n    !b\n}\n\n#[derive(Parser, Debug, PartialEq, Eq)]\n#[clap(no_binary_name = true)]\nstruct TargetMatrixArgs {\n    #[clap(long, short, num_args = 0..)]\n    target: Vec<String>,\n    #[clap(long, value_parser = BoolishValueParser::new())]\n    std: Option<bool>,\n    #[clap(long, value_parser = BoolishValueParser::new())]\n    cpp: Option<bool>,\n    #[clap(long, value_parser = BoolishValueParser::new())]\n    dylib: Option<bool>,\n    #[clap(long, value_parser = BoolishValueParser::new())]\n    run: Option<bool>,\n    #[clap(long, short, num_args = 0..)]\n    runners: Vec<String>,\n    #[clap(long)]\n    none: bool,\n    #[clap(long)]\n    has_image: bool,\n    #[clap(long, short)]\n    verbose: bool,\n    #[clap(long, short)]\n    cross_debug: bool,\n    #[clap(long, value_parser = PossibleValuesParser::new(&[\n            \"remote\",\n            \"bisect\",\n            \"foreign\",\n            \"docker-in-docker\",\n            \"podman\",\n            \"none\",\n            \"all\"\n        ]),\n        num_args = 0..,\n        value_delimiter = ',',\n        default_value = \"all\"\n    )]\n    tests: Vec<String>,\n}\n\nimpl Default for TargetMatrixArgs {\n    fn default() -> Self {\n        Self {\n            target: Vec::new(),\n            std: None,\n            cpp: None,\n            dylib: None,\n            run: None,\n            runners: Vec::new(),\n            none: false,\n            has_image: false,\n            verbose: false,\n            cross_debug: false,\n            tests: vec![\"all\".to_owned()],\n        }\n    }\n}\n\nimpl TargetMatrixArgs {\n    pub fn filter(&self, matrix: &mut Vec<CiTarget>) {\n        if self == &TargetMatrixArgs::default() {\n            gha_print(\"Running all targets.\");\n        }\n        if self.none {\n            gha_print(\"Running no targets.\");\n            std::mem::take(matrix);\n            return;\n        }\n        if self.has_image {\n            matrix.retain(|t| t.to_image_target().has_ci_image());\n        }\n        if !self.target.is_empty() {\n            matrix.retain(|m| {\n                let matrix_target = m.to_image_target();\n                let matrix_string = matrix_target.to_string();\n\n                self.target\n                    .iter()\n                    .any(|t| t.parse::<ImageTarget>().unwrap() == matrix_target)\n                    || self\n                        .target\n                        .iter()\n                        .any(|t| wildmatch::WildMatch::new(t).matches(&matrix_string))\n            })\n        };\n        if let Some(std) = self.std {\n            matrix.retain(|m| m.std.unwrap_or_default() == std)\n        }\n        if let Some(cpp) = self.cpp {\n            matrix.retain(|m| m.cpp.unwrap_or_default() == cpp)\n        }\n        if let Some(dylib) = self.dylib {\n            matrix.retain(|m| m.dylib.unwrap_or_default() == dylib)\n        }\n        if let Some(run) = self.run {\n            matrix.retain(|m| m.run.unwrap_or_default() == run)\n        }\n        if !self.runners.is_empty() {\n            matrix.retain(|m| {\n                self.runners\n                    .iter()\n                    .any(|runner| m.runners.as_deref().unwrap_or_default().contains(runner))\n            });\n        }\n    }\n\n    fn tests(&self) -> Result<serde_json::Value, serde_json::Error> {\n        use clap::CommandFactory;\n        use serde::ser::SerializeMap;\n        struct Ser(Vec<String>);\n        impl serde::Serialize for Ser {\n            fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n                let mut map = serializer.serialize_map(Some(self.0.len()))?;\n                for e in &self.0 {\n                    map.serialize_entry(&e, &true)?;\n                }\n                map.end()\n            }\n        }\n        let mut tests = match (\n            self.tests.iter().any(|t| t == \"all\"),\n            self.tests.iter().any(|t| t == \"none\"),\n        ) {\n            (_, true) => vec![],\n            (true, false) => {\n                let possible: Vec<String> = Self::command()\n                    .get_arguments()\n                    .find(|arg| arg.get_id() == \"tests\")\n                    .expect(\"a `tests` argument should exist\")\n                    .get_possible_values()\n                    .into_iter()\n                    .map(|p| p.get_name().to_owned())\n                    .collect();\n\n                possible\n            }\n            _ => self.tests.clone(),\n        };\n        tests.retain(|p| p != \"all\");\n        serde_json::to_value(Ser(tests))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[track_caller]\n    fn run<'a>(args: impl IntoIterator<Item = &'a str>) -> Vec<CiTarget> {\n        let mut matrix = get_matrix().clone();\n        matrix.retain_mut(|t| !t.disabled);\n        TargetMatrixArgs::try_parse_from(args)\n            .unwrap()\n            .filter(&mut matrix);\n        matrix\n    }\n\n    #[test]\n    fn it_works() {\n        run([\n            \"--target\",\n            \"*\",\n            \"--std\",\n            \"1\",\n            \"--cpp\",\n            \"1\",\n            \"--dylib\",\n            \"1\",\n            \"--run\",\n            \"1\",\n            \"--runners\",\n            \"native\",\n        ]);\n    }\n\n    #[test]\n    fn exact() {\n        let matrix = run([\"--target\", \"arm-unknown-linux-gnueabi\"]);\n        assert_eq!(matrix.len(), 1);\n        assert_eq!(matrix[0].target, \"arm-unknown-linux-gnueabi\");\n    }\n\n    #[test]\n    fn glob() {\n        let matrix = run([\"--target\", \"arm-unknown-linux-gnueabi*\"]);\n        assert_eq!(matrix.len(), 2);\n        assert_eq!(matrix[0].target, \"arm-unknown-linux-gnueabi\");\n        assert_eq!(matrix[1].target, \"arm-unknown-linux-gnueabihf\");\n    }\n\n    #[test]\n    fn ensure_filter_works() {\n        let matrix = run([\"--dylib\", \"1\"]);\n        assert!(matrix\n            .iter()\n            .any(|t| t.target == \"aarch64-unknown-linux-gnu\"));\n        assert!(matrix.iter().all(|t| t.target != \"thumbv6m-none-eabi\"));\n\n        let matrix = run([\"--dylib\", \"0\"]);\n        assert!(matrix\n            .iter()\n            .all(|t| t.target != \"aarch64-unknown-linux-gnu\"));\n        assert!(matrix.iter().any(|t| t.target == \"thumbv6m-none-eabi\"));\n    }\n\n    #[test]\n    fn all() {\n        let matrix = run([]);\n        let mut all = get_matrix().clone();\n        all.retain(|t| !t.disabled);\n        assert_eq!(&all, &matrix);\n    }\n\n    #[test]\n    fn none() {\n        let matrix = run([\"--none\"]);\n        assert_eq!(&Vec::<CiTarget>::new(), &matrix);\n    }\n\n    #[test]\n    fn merge_group() {\n        assert_eq!(\n            process_merge_group(\"refs/heads/gh-readonly-queue/main/pr-1375-44011c8854cb2eaac83b173cc323220ccdff18ea\").unwrap(),\n            \"1375\"\n        );\n    }\n}\n"
  },
  {
    "path": "xtask/src/ci.rs",
    "content": "mod target_matrix;\n\nuse crate::util::gha_output;\nuse clap::Subcommand;\nuse cross::shell::Verbosity;\nuse cross::{cargo_command, CargoMetadata, CommandExt};\n\n#[derive(Subcommand, Debug)]\npub enum CiJob {\n    /// Return needed metadata for building images\n    PrepareMeta {\n        // tag, branch\n        #[clap(long, env = \"GITHUB_REF_TYPE\")]\n        ref_type: String,\n        // main, v0.1.0\n        #[clap(long, env = \"GITHUB_REF_NAME\")]\n        ref_name: String,\n        target: crate::ImageTarget,\n    },\n    /// Check workspace metadata.\n    Check {\n        /// tag, branch\n        #[clap(long, env = \"GITHUB_REF_TYPE\")]\n        ref_type: String,\n        /// main, v0.1.0\n        #[clap(long, env = \"GITHUB_REF_NAME\")]\n        ref_name: String,\n    },\n    TargetMatrix(target_matrix::TargetMatrix),\n}\n\npub fn ci(args: CiJob, metadata: CargoMetadata) -> cross::Result<()> {\n    let cross_meta = metadata\n        .get_package(\"cross\")\n        .expect(\"cross expected in workspace\");\n\n    match args {\n        CiJob::PrepareMeta {\n            ref_type,\n            ref_name,\n            target,\n        } => {\n            // Set labels\n            let mut labels = vec![];\n\n            let image_title = match target.name.as_ref() {\n                \"cross\" => target.name.to_string(),\n                // TODO: Mention platform?\n                _ => format!(\"cross (for {})\", target.name),\n            };\n            labels.push(format!(\"org.opencontainers.image.title={image_title}\"));\n            labels.push(format!(\n                \"org.opencontainers.image.licenses={}\",\n                cross_meta.license.as_deref().unwrap_or_default()\n            ));\n            labels.push(format!(\n                \"org.opencontainers.image.created={}\",\n                chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true)\n            ));\n\n            gha_output(\"labels\", &serde_json::to_string(&labels.join(\"\\n\"))?)?;\n\n            let version = cross_meta.version.clone();\n\n            // Set image name\n            gha_output(\n                \"image\",\n                &crate::build_docker_image::determine_image_name(\n                    &target,\n                    cross::docker::CROSS_IMAGE,\n                    &ref_type,\n                    &ref_name,\n                    false,\n                    &version,\n                )?[0],\n            )?;\n\n            if target.has_ci_image() {\n                gha_output(\"has-image\", \"true\")?\n            }\n            if target.is_standard_target_image() {\n                gha_output(\"test-variant\", \"default\")?\n            } else {\n                gha_output(\"test-variant\", &target.name)?\n            }\n        }\n        CiJob::Check { ref_type, ref_name } => {\n            let version = semver::Version::parse(&cross_meta.version)?;\n            if ref_type == \"tag\" {\n                if ref_name.starts_with('v') && ref_name != format!(\"v{version}\") {\n                    eyre::bail!(\"a version tag was published, but the tag does not match the current version in Cargo.toml\");\n                }\n                let search = cargo_command()\n                    .args([\"search\", \"--limit\", \"1\"])\n                    .arg(\"cross\")\n                    .run_and_get_stdout(&mut Verbosity::Verbose(2).into())?;\n                let (cross, rest) = search\n                    .split_once(\" = \")\n                    .ok_or_else(|| eyre::eyre!(\"cargo search failed\"))?;\n                assert_eq!(cross, \"cross\");\n                // Note: this version includes pre-releases.\n                let latest_version = semver::Version::parse(\n                    rest.split('\"')\n                        .nth(1)\n                        .ok_or_else(|| eyre::eyre!(\"cargo search returned unexpected data\"))?,\n                )?;\n                if version >= latest_version && version.pre.is_empty() {\n                    gha_output(\"is-latest\", \"true\")?\n                }\n            }\n        }\n        CiJob::TargetMatrix(target_matrix) => {\n            target_matrix.run()?;\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/codegen.rs",
    "content": "use clap::Args;\nuse cross::docker::ImagePlatform;\nuse eyre::Context;\nuse std::fmt::Write;\n\nuse crate::util::{get_cargo_workspace, get_matrix};\n\n#[derive(Args, Debug)]\npub struct Codegen {}\n\npub fn codegen(Codegen { .. }: Codegen) -> cross::Result<()> {\n    let path = get_cargo_workspace().join(\"src/docker/provided_images.rs\");\n    std::fs::write(path, docker_images()).wrap_err(\"when writing src/docker/provided_images.rs\")?;\n    Ok(())\n}\n\npub fn docker_images() -> String {\n    let mut images = String::from(\n        r#\"#![doc = \"*** AUTO-GENERATED, do not touch. Run `cargo xtask codegen` to update ***\"]\nuse super::{ImagePlatform, ProvidedImage};\n\n#[rustfmt::skip]\npub static PROVIDED_IMAGES: &[ProvidedImage] = &[\"#,\n    );\n\n    for image_target in get_matrix()\n        .iter()\n        .filter(|i| i.builds_image() && i.to_image_target().is_toolchain_image() && !i.disabled)\n    {\n        write!(\n            &mut images,\n            r#\"\n        ProvidedImage {{\n            name: \"{name}\",\n            platforms: &[{platform}],\n            sub: {sub}\n        }},\"#,\n            name = image_target.target.clone(),\n            platform = &image_target\n                .platforms()\n                .iter()\n                .map(|p| {\n                    let image_platform: ImagePlatform =\n                        p.parse().expect(\"should be a valid platform\");\n\n                    image_platform\n                        .to_codegen_string()\n                        .expect(\"should be a valid platform\")\n                })\n                .collect::<Vec<_>>()\n                .join(\", \"),\n            sub = if let Some(sub) = &image_target.sub {\n                format!(r#\"Some(\"{}\")\"#, sub)\n            } else {\n                \"None\".to_string()\n            }\n        )\n        .expect(\"writing to string should not fail\")\n    }\n\n    images.push_str(\"\\n];\\n\");\n    images\n}\n\n#[cfg(test)]\n#[test]\npub fn ensure_correct_codegen() -> cross::Result<()> {\n    let provided_images = crate::util::get_cargo_workspace().join(\"src/docker/provided_images.rs\");\n    let content = cross::file::read(provided_images)?;\n    assert_eq!(content.replace(\"\\r\\n\", \"\\n\"), docker_images());\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/crosstool.rs",
    "content": "use std::cmp::Ordering;\nuse std::fmt::Write;\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\nuse crate::util::{project_dir, write_to_string};\nuse clap::Args;\nuse cross::shell::MessageInfo;\nuse cross::ToUtf8;\n\nconst DEFAULT_GCC_VERSION: &str = \"8.3.0\";\nconst DEFAULT_GLIBC_VERSION: &str = \"2.17.0\";\nconst DEFAULT_UCLIBC_VERSION: &str = \"1.0.31\";\nconst DEFAULT_MUSL_VERSION: &str = \"1.1.24\";\nconst DEFAULT_NEWLIB_VERSION: &str = \"3.1.0.20181231\";\nconst DEFAULT_LINUX_VERSION: &str = \"4.19.21\";\nconst DOCKER: &str = \"docker\";\nconst CT_NG: &str = \"crosstool-ng\";\nconst TOOLCHAINS: &str = \"cross-toolchains\";\nconst CT_CONFIG: &str = \"crosstool-config\";\n\n#[derive(Args, Debug)]\npub struct ConfigureCrosstool {\n    /// The gcc version to configure for.\n    #[clap(long, env = \"GCC_VERSION\")]\n    pub gcc_version: Option<String>,\n    /// The glibc version to configure for.\n    #[clap(long, env = \"GLIBC_VERSION\")]\n    pub glibc_version: Option<String>,\n    /// The uclibc version to configure for.\n    #[clap(long, env = \"UCLIBC_VERSION\")]\n    pub uclibc_version: Option<String>,\n    /// The musl version to configure for.\n    #[clap(long, env = \"MUSL_VERSION\")]\n    pub musl_version: Option<String>,\n    /// The newlib version to configure for.\n    #[clap(long, env = \"NEWLIB_VERSION\")]\n    pub newlib_version: Option<String>,\n    /// The linux version to configure for.\n    #[clap(long, env = \"LINUX_VERSION\")]\n    pub linux_version: Option<String>,\n    /// Targets to build for\n    #[clap()]\n    targets: Vec<String>,\n}\n\nfn locate_ctng_config(\n    target: &str,\n    root: &Path,\n    cross_toolchains_root: &Path,\n) -> cross::Result<(PathBuf, PathBuf)> {\n    let config_name = format!(\"{target}.config\");\n    let template_name = format!(\"{config_name}.in\");\n    let ct_ng_root = root.join(CT_NG);\n    let cross_toolchains_ctng_root = cross_toolchains_root.join(CT_NG);\n    let (src_root, dst_root) = if cross_toolchains_ctng_root.join(&template_name).exists() {\n        (\n            &cross_toolchains_ctng_root,\n            cross_toolchains_root.join(DOCKER).join(CT_CONFIG),\n        )\n    } else if ct_ng_root.join(&template_name).exists() {\n        (&ct_ng_root, root.join(DOCKER).join(CT_CONFIG))\n    } else {\n        eyre::bail!(\"unable to find config for target \\\"{target}\\\"\");\n    };\n    Ok((src_root.join(template_name), dst_root.join(config_name)))\n}\n\nfn read_config_dir(dir: &Path) -> cross::Result<Vec<String>> {\n    let mut targets = vec![];\n    for entry in fs::read_dir(dir)? {\n        let file = entry?;\n        let basename = file.file_name().to_utf8()?.to_string();\n        if let Some(target) = basename.strip_suffix(\".config.in\") {\n            targets.push(target.to_string());\n        }\n    }\n\n    Ok(targets)\n}\n\nfn configure_glibc(\n    glibc_version: &str,\n) -> cross::Result<(&'static str, String, String, Option<String>)> {\n    // configure the `CT_GLIBC` values\n    let glibc_versions: Vec<&str> = glibc_version.split('.').collect();\n    if !matches!(glibc_versions.len(), 2 | 3) {\n        eyre::bail!(\"invalid glibc version, got {glibc_version}\");\n    }\n\n    let glibc_major = glibc_versions[0].parse::<u32>()?;\n    let glibc_minor = glibc_versions[1].parse::<u32>()?;\n    let _glibc_patch = glibc_versions.get(2).unwrap_or(&\"0\").parse::<u32>()?;\n    if glibc_major != 2 {\n        eyre::bail!(\"glibc major versions other than 2 currently unsupported, got {glibc_major}\");\n    }\n    let ct_glibc_v = format!(\n        r#\"CT_GLIBC_V_{glibc_major}_{glibc_minor}=y\n# CT_GLIBC_NO_VERSIONS is not set\nCT_GLIBC_VERSION=\"{glibc_major}.{glibc_minor}\"\"#\n    );\n    let mut ct_glibc = String::new();\n    let glibc_older = [29, 27, 26, 25, 24, 23, 20];\n    for minor in glibc_older {\n        if glibc_minor <= minor {\n            write!(ct_glibc, \"\\nCT_GLIBC_2_{minor}_or_older=y\")?;\n        }\n        if glibc_minor < minor {\n            write!(ct_glibc, \"\\nCT_GLIBC_older_than_2_{minor}=y\")?;\n        }\n    }\n    if glibc_minor >= 17 {\n        ct_glibc.push_str(\"\\nCT_GLIBC_2_17_or_later=y\");\n    }\n    if glibc_minor <= 17 {\n        ct_glibc.push_str(\"\\nCT_GLIBC_2_17_or_older=y\");\n    }\n    if glibc_minor > 14 {\n        ct_glibc.push_str(\"\\nCT_GLIBC_later_than_2_14=y\");\n    }\n    if glibc_minor >= 14 {\n        ct_glibc.push_str(\"\\nCT_GLIBC_2_14_or_later=y\");\n    }\n\n    Ok((\"GLIBC\", ct_glibc_v, ct_glibc, None))\n}\n\nfn configure_uclibc(\n    uclibc_version: &str,\n) -> cross::Result<(&'static str, String, String, Option<String>)> {\n    // configure the `CT_UCLIBC` values\n    let uclibc_versions: Vec<&str> = uclibc_version.split('.').collect();\n    if !matches!(uclibc_versions.len(), 3 | 4) {\n        eyre::bail!(\"invalid uclibc version, got {uclibc_version}\");\n    }\n\n    let uclibc_major = uclibc_versions[0].parse::<u32>()?;\n    let uclibc_minor = uclibc_versions[1].parse::<u32>()?;\n    let uclibc_patch = uclibc_versions[2].parse::<u32>()?;\n    let uclibc_dev = uclibc_versions.get(3).unwrap_or(&\"0\").parse::<u32>()?;\n    let (key, version) = match uclibc_dev {\n        0 => (\n            format!(\"{uclibc_major}_{uclibc_minor}_{uclibc_patch}\"),\n            format!(\"{uclibc_major}.{uclibc_minor}.{uclibc_patch}\"),\n        ),\n        _ => (\n            format!(\"{uclibc_major}_{uclibc_minor}_{uclibc_patch}_{uclibc_dev}\"),\n            format!(\"{uclibc_major}.{uclibc_minor}.{uclibc_patch}.{uclibc_dev}\"),\n        ),\n    };\n    let (ct_uclibc_v, ct_extras) = match uclibc_major {\n        0 => (\n            format!(\n                r#\"CT_UCLIBC_V_{key}=y\n# CT_UCLIBC_NO_VERSIONS is not set\nCT_UCLIBC_VERSION=\"{version}\"\"#\n            ),\n            r#\"CT_UCLIBC_USE_UCLIBC_ORG=y\nCT_UCLIBC_USE=\"UCLIBC\"\nCT_UCLIBC_PKG_NAME=\"uClibc\"\nCT_UCLIBC_SRC_RELEASE=y\nCT_UCLIBC_PATCH_ORDER=\"global\"\nCT_UCLIBC_MIRRORS=\"https://uclibc.org/downloads/\"\nCT_UCLIBC_ARCHIVE_FILENAME=\"@{pkg_name}-@{version}\"\nCT_UCLIBC_ARCHIVE_DIRNAME=\"@{pkg_name}-@{version}\"\nCT_UCLIBC_ARCHIVE_FORMATS=\".tar.xz .tar.bz2\"\nCT_UCLIBC_SIGNATURE_FORMAT=\"packed/.asc\"\"#,\n        ),\n        _ => (\n            format!(\n                r#\"CT_UCLIBC_NG_V_{key}=y\n# CT_UCLIBC_NG_NO_VERSIONS is not set\nCT_UCLIBC_NG_VERSION=\"{version}\"\"#\n            ),\n            r#\"CT_UCLIBC_USE_UCLIBC_NG_ORG=y\nCT_UCLIBC_USE=\"UCLIBC_NG\"\nCT_UCLIBC_NG_PKG_NAME=\"uClibc-ng\"\nCT_UCLIBC_NG_SRC_RELEASE=y\nCT_UCLIBC_NG_PATCH_ORDER=\"global\"\nCT_UCLIBC_NG_MIRRORS=\"http://downloads.uclibc-ng.org/releases/${CT_UCLIBC_NG_VERSION}\"\nCT_UCLIBC_NG_ARCHIVE_FILENAME=\"@{pkg_name}-@{version}\"\nCT_UCLIBC_NG_ARCHIVE_DIRNAME=\"@{pkg_name}-@{version}\"\nCT_UCLIBC_NG_ARCHIVE_FORMATS=\".tar.xz .tar.lz .tar.bz2 .tar.gz\"\nCT_UCLIBC_NG_SIGNATURE_FORMAT=\"packed/.asc\"\"#,\n        ),\n    };\n\n    let mut ct_uclibc = String::new();\n    let version = (uclibc_major, uclibc_minor, uclibc_patch, uclibc_dev);\n    let uclibc_older = [\n        (1, 0, 23, 0),\n        (1, 0, 21, 0),\n        (1, 0, 15, 0),\n        (1, 0, 0, 0),\n        (0, 9, 33, 2),\n    ];\n    for older in uclibc_older {\n        let cmp = older.cmp(&version);\n        let (major, minor, patch, dev) = older;\n        let key = match dev {\n            0 => format!(\"{major}_{minor}_{patch}\"),\n            _ => format!(\"{major}_{minor}_{patch}_{dev}\"),\n        };\n        match cmp {\n            Ordering::Greater => {\n                write!(ct_uclibc, \"\\nCT_UCLIBC_{key}_or_older=y\")?;\n                write!(ct_uclibc, \"\\nCT_UCLIBC_older_than_{key}=y\")?;\n            }\n            Ordering::Equal => {\n                write!(ct_uclibc, \"\\nCT_UCLIBC_{key}_or_later=y\")?;\n                write!(ct_uclibc, \"\\nCT_UCLIBC_{key}_or_older=y\")?;\n            }\n            Ordering::Less => {\n                write!(ct_uclibc, \"\\nCT_UCLIBC_{key}_or_later=y\")?;\n                write!(ct_uclibc, \"\\nCT_UCLIBC_later_than_{key}=y\")?;\n            }\n        }\n    }\n\n    Ok((\"UCLIBC\", ct_uclibc_v, ct_uclibc, Some(ct_extras.to_owned())))\n}\n\nfn configure_musl(\n    musl_version: &str,\n) -> cross::Result<(&'static str, String, String, Option<String>)> {\n    let musl_versions: Vec<&str> = musl_version.split('.').collect();\n    if !matches!(musl_versions.len(), 3) {\n        eyre::bail!(\"invalid musl version, got {musl_version}\");\n    }\n\n    let musl_major = musl_versions[0].parse::<u32>()?;\n    let musl_minor = musl_versions[1].parse::<u32>()?;\n    let musl_patch = musl_versions[2].parse::<u32>()?;\n    let ct_musl_v = format!(\n        r#\"CT_MUSL_V_{musl_major}_{musl_minor}_{musl_patch}=y\n# CT_MUSL_NO_VERSIONS is not set\nCT_MUSL_VERSION=\"{musl_major}.{musl_minor}.{musl_patch}\"\"#\n    );\n\n    Ok((\"MUSL\", ct_musl_v, \"\".to_owned(), None))\n}\n\nfn configure_newlib(\n    newlib_version: &str,\n) -> cross::Result<(&'static str, String, String, Option<String>)> {\n    let newlib_versions: Vec<&str> = newlib_version.split('.').collect();\n    if !matches!(newlib_versions.len(), 3 | 4) {\n        eyre::bail!(\"invalid newlib version, got {newlib_version}\");\n    }\n\n    let newlib_major = newlib_versions[0].parse::<u32>()?;\n    let newlib_minor = newlib_versions[1].parse::<u32>()?;\n    let newlib_patch = newlib_versions[2].parse::<u32>()?;\n    let newlib_dev = newlib_versions.get(3).unwrap_or(&\"0\").parse::<u32>()?;\n    let ct_newlib_v = format!(\n        r#\"CT_NEWLIB_V_{newlib_major}_{newlib_minor}=y\n# CT_NEWLIB_NO_VERSIONS is not set\nCT_NEWLIB_VERSION=\"{newlib_major}.{newlib_minor}.{newlib_patch}.{newlib_dev}\"\"#\n    );\n\n    let mut ct_newlib = String::new();\n    let version = (newlib_major, newlib_minor);\n    let newlib_older = [(2, 2), (2, 1), (2, 0)];\n    for older in newlib_older {\n        let cmp = older.cmp(&version);\n        let (major, minor) = older;\n        let key = format!(\"{major}_{minor}\");\n        match cmp {\n            Ordering::Greater => {\n                write!(ct_newlib, \"\\nCT_NEWLIB_{key}_or_older=y\")?;\n                write!(ct_newlib, \"\\nCT_NEWLIB_older_than_{key}=y\")?;\n            }\n            Ordering::Equal => {\n                write!(ct_newlib, \"\\nCT_NEWLIB_{key}_or_later=y\")?;\n                write!(ct_newlib, \"\\nCT_NEWLIB_{key}_or_older=y\")?;\n            }\n            Ordering::Less => {\n                write!(ct_newlib, \"\\nCT_NEWLIB_{key}_or_later=y\")?;\n                write!(ct_newlib, \"\\nCT_NEWLIB_later_than_{key}=y\")?;\n            }\n        }\n    }\n\n    Ok((\"NEWLIB\", ct_newlib_v, ct_newlib, None))\n}\n\nfn configure_target(\n    src_file: &Path,\n    gcc_version: &str,\n    glibc_version: &str,\n    uclibc_version: &str,\n    musl_version: &str,\n    newlib_version: &str,\n    linux_version: &str,\n) -> cross::Result<String> {\n    let file_name = src_file\n        .file_name()\n        .ok_or(eyre::eyre!(\"unable to get filename for {src_file:?}\"))?\n        .to_utf8()?;\n    let mut contents = fs::read_to_string(src_file)?;\n\n    // configure the `CT_GCC` values\n    let gcc_versions: Vec<&str> = gcc_version.split('.').collect();\n    if !matches!(gcc_versions.len(), 2 | 3) {\n        eyre::bail!(\"invalid GCC version, got {gcc_version}\");\n    }\n    let gcc_major = gcc_versions[0].parse::<u32>()?;\n    let gcc_minor = gcc_versions[1].parse::<u32>()?;\n    let gcc_patch = gcc_versions.get(2).unwrap_or(&\"0\").parse::<u32>()?;\n    let ct_gcc_v = format!(\n        r#\"CT_GCC_V_{gcc_major}=y\n# CT_GCC_NO_VERSIONS is not set\nCT_GCC_VERSION=\"{gcc_major}.{gcc_minor}.{gcc_patch}\"\"#\n    );\n    let mut ct_gcc = String::new();\n    for major in (5..=7).rev() {\n        if gcc_major > major {\n            write!(ct_gcc, \"\\nCT_GCC_later_than_{major}=y\")?;\n        }\n        if gcc_major >= major {\n            write!(ct_gcc, \"\\nCT_GCC_{major}_or_later=y\")?;\n        }\n    }\n    if gcc_major > 4 || (gcc_major == 4 && gcc_major > 9) {\n        ct_gcc.push_str(\"\\nCT_GCC_later_than_4_9=y\");\n    }\n    if gcc_major > 4 || (gcc_major == 4 && gcc_major >= 9) {\n        ct_gcc.push_str(\"\\nCT_GCC_4_9_or_later=y\");\n    }\n    if gcc_major > 4 || (gcc_major == 4 && gcc_major > 8) {\n        ct_gcc.push_str(\"\\nCT_GCC_later_than_4_8=y\");\n    }\n    if gcc_major > 4 || (gcc_major == 4 && gcc_major >= 8) {\n        ct_gcc.push_str(\"\\nCT_GCC_4_8_or_later=y\");\n    }\n    if ct_gcc.starts_with('\\n') {\n        ct_gcc.remove(0);\n    }\n    contents = contents\n        .replacen(\"%CT_GCC_V%\", &ct_gcc_v, 1)\n        .replacen(\"%CT_GCC%\", &ct_gcc, 1);\n\n    // configure the libc versions\n    let (key, libc_v, mut libc, extras) = if file_name.contains(\"gnu\") {\n        configure_glibc(glibc_version)?\n    } else if file_name.contains(\"uclibc\") {\n        configure_uclibc(uclibc_version)?\n    } else if file_name.contains(\"musl\") {\n        configure_musl(musl_version)?\n    } else if file_name.contains(\"none\") {\n        configure_newlib(newlib_version)?\n    } else {\n        eyre::bail!(\"unsupported rust target for file {file_name}: unknown libc version\");\n    };\n    if libc.starts_with('\\n') {\n        libc.remove(0);\n    }\n    contents = contents\n        .replacen(&format!(\"%CT_{key}_V%\"), &libc_v, 1)\n        .replacen(&format!(\"%CT_{key}%\"), &libc, 1);\n    if let Some(extras) = extras {\n        contents = contents.replacen(&format!(\"%CT_{key}_EXTRAS%\"), &extras, 1);\n    }\n\n    // configure the `CT_LINUX` values\n    if file_name.contains(\"linux\") {\n        let linux_versions: Vec<&str> = linux_version.split('.').collect();\n        if !matches!(linux_versions.len(), 2 | 3) {\n            eyre::bail!(\"invalid linux version, got {linux_version}\");\n        }\n        let linux_major = linux_versions[0].parse::<u32>()?;\n        let linux_minor = linux_versions[1].parse::<u32>()?;\n        let linux_patch = linux_versions.get(2).unwrap_or(&\"0\").parse::<u32>()?;\n        let ct_linux_v = format!(\n            r#\"CT_LINUX_V_{linux_major}_{linux_minor}=y\n# CT_LINUX_NO_VERSIONS is not set\nCT_LINUX_VERSION=\"{linux_major}.{linux_minor}.{linux_patch}\"\"#\n        );\n        let mut ct_linux = String::new();\n        if linux_major < 4 || (linux_major == 4 && linux_minor < 8) {\n            ct_linux.push_str(\"\\nCT_LINUX_older_than_4_8=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_4_8_or_older=y\");\n        } else {\n            ct_linux.push_str(\"\\nCT_LINUX_later_than_4_8=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_4_8_or_later=y\");\n        }\n        if linux_major < 3 || (linux_major == 3 && linux_minor < 7) {\n            ct_linux.push_str(\"\\nCT_LINUX_older_than_3_7=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_3_7_or_older=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_older_than_3_2=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_3_2_or_older=y\");\n        } else {\n            ct_linux.push_str(\"\\nCT_LINUX_later_than_3_7=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_3_7_or_later=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_later_than_3_2=y\");\n            ct_linux.push_str(\"\\nCT_LINUX_3_2_or_later=y\");\n        }\n        if ct_linux.starts_with('\\n') {\n            ct_linux.remove(0);\n        }\n\n        contents =\n            contents\n                .replacen(\"%CT_LINUX_V%\", &ct_linux_v, 1)\n                .replacen(\"%CT_LINUX%\", &ct_linux, 1);\n    }\n\n    Ok(contents)\n}\n\npub fn configure_crosstool(\n    ConfigureCrosstool {\n        gcc_version,\n        glibc_version,\n        uclibc_version,\n        musl_version,\n        newlib_version,\n        linux_version,\n        mut targets,\n        ..\n    }: ConfigureCrosstool,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let gcc_version = gcc_version.as_deref().unwrap_or(DEFAULT_GCC_VERSION);\n    let glibc_version = glibc_version.as_deref().unwrap_or(DEFAULT_GLIBC_VERSION);\n    let uclibc_version = uclibc_version.as_deref().unwrap_or(DEFAULT_UCLIBC_VERSION);\n    let musl_version = musl_version.as_deref().unwrap_or(DEFAULT_MUSL_VERSION);\n    let newlib_version = newlib_version.as_deref().unwrap_or(DEFAULT_NEWLIB_VERSION);\n    let linux_version = linux_version.as_deref().unwrap_or(DEFAULT_LINUX_VERSION);\n\n    let root = project_dir(msg_info)?;\n    let cross_toolchains_root = root.join(DOCKER).join(TOOLCHAINS);\n    if targets.is_empty() {\n        targets = read_config_dir(&root.join(CT_NG))?;\n        let cross_toolchains_ctng_root = cross_toolchains_root.join(CT_NG);\n        if cross_toolchains_ctng_root.exists() {\n            targets.append(&mut read_config_dir(&cross_toolchains_ctng_root)?);\n        }\n    }\n    let config_files = targets\n        .into_iter()\n        .map(|t| locate_ctng_config(&t, &root, &cross_toolchains_root))\n        .collect::<cross::Result<Vec<_>>>()?;\n    for (src_file, dst_file) in config_files {\n        let configured = configure_target(\n            &src_file,\n            gcc_version,\n            glibc_version,\n            uclibc_version,\n            musl_version,\n            newlib_version,\n            linux_version,\n        )?;\n        write_to_string(&dst_file, &configured)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/hooks.rs",
    "content": "use std::fs::File;\nuse std::io::{BufRead, BufReader, ErrorKind};\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\n\nuse crate::util::{cargo, cargo_metadata, get_channel_prefer_nightly};\nuse clap::builder::BoolishValueParser;\nuse clap::Args;\nuse cross::shell::MessageInfo;\nuse cross::CommandExt;\nuse eyre::Context;\n\nconst CARGO_FLAGS: &[&str] = &[\"--all-features\", \"--all-targets\", \"--workspace\"];\n\n#[derive(Args, Debug)]\npub struct Check {\n    /// Run shellcheck on all files, not just staged files.\n    #[clap(short, long)]\n    all: bool,\n    /// Run Python linter checks.\n    #[clap(short, long, env = \"PYTHON\", value_parser = BoolishValueParser::new())]\n    python: bool,\n    /// Flake8 command (either an executable or list of arguments)\n    #[clap(short, long, env = \"FLAKE8\")]\n    flake8: Option<String>,\n}\n\n#[derive(Args, Debug)]\npub struct Test {\n    /// Run Python test suite.\n    #[clap(short, long, env = \"PYTHON\", value_parser = BoolishValueParser::new())]\n    python: bool,\n    /// Tox command (either an executable or list of arguments)\n    #[clap(short, long, env = \"TOX\")]\n    tox: Option<String>,\n}\n\n#[track_caller]\nfn cargo_fmt(msg_info: &mut MessageInfo, channel: Option<&str>) -> cross::Result<()> {\n    cargo(channel)\n        .args([\"fmt\", \"--\", \"--check\"])\n        .run(msg_info, false)\n}\n\n#[track_caller]\nfn cargo_clippy(msg_info: &mut MessageInfo, channel: Option<&str>) -> cross::Result<()> {\n    cargo(channel)\n        .arg(\"clippy\")\n        .args(CARGO_FLAGS)\n        .args([\"--\", \"--deny\", \"warnings\"])\n        .run(msg_info, false)\n}\n\n#[track_caller]\nfn cargo_test(msg_info: &mut MessageInfo, channel: Option<&str>) -> cross::Result<()> {\n    cargo(channel)\n        .arg(\"test\")\n        .args(CARGO_FLAGS)\n        .run(msg_info, false)\n}\n\nfn splitlines(string: String) -> Vec<String> {\n    string.lines().map(|l| l.to_string()).collect()\n}\n\nfn staged_files(msg_info: &mut MessageInfo) -> cross::Result<Vec<String>> {\n    Command::new(\"git\")\n        .args([\"diff\", \"--cached\", \"--name-only\", \"--diff-filter=ACM\"])\n        .run_and_get_stdout(msg_info)\n        .map(splitlines)\n}\n\nfn all_files(msg_info: &mut MessageInfo) -> cross::Result<Vec<String>> {\n    Command::new(\"git\")\n        .arg(\"ls-files\")\n        .run_and_get_stdout(msg_info)\n        .map(splitlines)\n}\n\nfn is_shell_script(path: impl AsRef<Path>) -> cross::Result<bool> {\n    if path.as_ref().is_dir() {\n        // is a directory if a git submodule\n        return Ok(false);\n    }\n    let file = File::open(path.as_ref())?;\n    let reader = BufReader::new(file);\n\n    match reader.lines().next() {\n        Some(Ok(line)) => Ok(line.starts_with(\"#!\") && line.trim().ends_with(\"sh\")),\n        Some(Err(e)) => match e.kind() {\n            // not a UTF-8 file: can't be a shell script\n            ErrorKind::InvalidData => Ok(false),\n            _ => Err(e.into()),\n        },\n        None => Ok(false),\n    }\n}\n\nfn shellcheck(all: bool, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    if which::which(\"shellcheck\").is_ok() {\n        let files = match all {\n            true => all_files(msg_info),\n            false => staged_files(msg_info),\n        }?;\n        let mut scripts = vec![];\n        for file in files {\n            if is_shell_script(&file)? {\n                scripts.push(file);\n            }\n        }\n        if !scripts.is_empty() {\n            Command::new(\"shellcheck\")\n                .args(&scripts)\n                .run(msg_info, false)?;\n        }\n    }\n\n    Ok(())\n}\n\nfn parse_command(value: &str) -> cross::Result<Vec<String>> {\n    shell_words::split(value).wrap_err_with(|| format!(\"could not parse command of {}\", value))\n}\n\nfn python_dir(metadata: &cross::CargoMetadata) -> PathBuf {\n    metadata.workspace_root.join(\"docker\").join(\"android\")\n}\n\nfn python_env(cmd: &mut Command, metadata: &cross::CargoMetadata) {\n    cmd.env(\"PYTHONDONTWRITEBYTECODE\", \"1\");\n    cmd.env(\n        \"PYTHONPYCACHEPREFIX\",\n        metadata.target_directory.join(\"__pycache__\"),\n    );\n}\n\nfn python_lint(flake8: Option<&str>, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    let metadata = cargo_metadata(msg_info)?;\n    let args = flake8\n        .map(parse_command)\n        .unwrap_or_else(|| Ok(vec![\"flake8\".to_owned()]))?;\n    let mut cmd = Command::new(\n        args.first()\n            .ok_or_else(|| eyre::eyre!(\"empty string provided for flake8 command\"))?,\n    );\n    cmd.args(&args[1..]);\n    python_env(&mut cmd, &metadata);\n    if msg_info.is_verbose() {\n        cmd.arg(\"--verbose\");\n    }\n    cmd.current_dir(python_dir(&metadata));\n    cmd.run(msg_info, false)?;\n\n    Ok(())\n}\n\nfn python_test(tox: Option<&str>, msg_info: &mut MessageInfo) -> cross::Result<()> {\n    let metadata = cargo_metadata(msg_info)?;\n    let args = tox\n        .map(parse_command)\n        .unwrap_or_else(|| Ok(vec![\"tox\".to_owned()]))?;\n    let mut cmd = Command::new(\n        args.first()\n            .ok_or_else(|| eyre::eyre!(\"empty string provided for tox command\"))?,\n    );\n    cmd.args(&args[1..]);\n    cmd.args([\"-e\", \"py3\"]);\n    python_env(&mut cmd, &metadata);\n    cmd.arg(\"--workdir\");\n    cmd.arg(&metadata.target_directory);\n    if msg_info.is_verbose() {\n        cmd.arg(\"--verbose\");\n    }\n    cmd.current_dir(python_dir(&metadata));\n    cmd.run(msg_info, false)?;\n\n    Ok(())\n}\n\npub fn check(\n    Check {\n        all,\n        python,\n        flake8,\n        ..\n    }: Check,\n    toolchain: Option<&str>,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut checks = vec![\"rustfmt\", \"clippy\", \"shellcheck\"];\n    if python {\n        checks.push(\"python\");\n    }\n    msg_info.info(format_args!(\"Running {} checks.\", checks.join(\", \")))?;\n\n    let channel = get_channel_prefer_nightly(msg_info, toolchain)?;\n    cargo_fmt(msg_info, channel).wrap_err(\"fmt failed\")?;\n    cargo_clippy(msg_info, channel).wrap_err(\"clippy failed\")?;\n    shellcheck(all, msg_info).wrap_err(\"shellcheck failed\")?;\n    if python {\n        python_lint(flake8.as_deref(), msg_info)?;\n    }\n\n    Ok(())\n}\n\npub fn test(\n    Test { python, tox, .. }: Test,\n    toolchain: Option<&str>,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut tests = vec![\"rustfmt\", \"unit\"];\n    if python {\n        tests.push(\"python\");\n    }\n    msg_info.info(format_args!(\"Running {} tests.\", tests.join(\", \")))?;\n\n    let channel = get_channel_prefer_nightly(msg_info, toolchain)?;\n    cargo_fmt(msg_info, channel)?;\n    cargo_test(msg_info, channel)?;\n    if python {\n        python_test(tox.as_deref(), msg_info)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/install_git_hooks.rs",
    "content": "use crate::util::project_dir;\nuse clap::Args;\nuse cross::shell::MessageInfo;\n\n#[derive(Args, Debug)]\npub struct InstallGitHooks {}\n\npub fn install_git_hooks(msg_info: &mut MessageInfo) -> cross::Result<()> {\n    let root = project_dir(msg_info)?;\n    let git_hooks = root.join(\".git\").join(\"hooks\");\n    let cross_dev = root.join(\"xtask\").join(\"src\");\n\n    std::fs::copy(\n        cross_dev.join(\"pre-commit.sh\"),\n        git_hooks.join(\"pre-commit\"),\n    )?;\n    std::fs::copy(cross_dev.join(\"pre-push.sh\"), git_hooks.join(\"pre-push\"))?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/main.rs",
    "content": "#![deny(missing_debug_implementations, rust_2018_idioms)]\n\npub mod build_docker_image;\npub mod changelog;\npub mod ci;\npub mod codegen;\npub mod crosstool;\npub mod hooks;\npub mod install_git_hooks;\npub mod target_info;\npub mod util;\n\nuse ci::CiJob;\nuse clap::{CommandFactory, Parser, Subcommand};\nuse codegen::Codegen;\nuse cross::docker;\nuse cross::shell::{MessageInfo, Verbosity};\nuse util::{cargo_metadata, ImageTarget};\n\nuse self::build_docker_image::BuildDockerImage;\nuse self::changelog::Changelog;\nuse self::crosstool::ConfigureCrosstool;\nuse self::hooks::{Check, Test};\nuse self::install_git_hooks::InstallGitHooks;\nuse self::target_info::TargetInfo;\n\n#[derive(Parser, Debug)]\n#[clap(version, about, long_about = None)]\nstruct Cli {\n    /// Toolchain name/version to use (such as stable or 1.59.0).\n    #[clap(value_parser = is_toolchain)]\n    toolchain: Option<String>,\n    #[clap(subcommand)]\n    command: Commands,\n    /// Provide verbose diagnostic output.\n    #[clap(short, long, global = true, action = clap::ArgAction::Count)]\n    pub verbose: u8,\n    /// Do not print cross log messages.\n    #[clap(short, long, global = true)]\n    pub quiet: bool,\n    /// Coloring: auto, always, never\n    #[clap(long, global = true)]\n    pub color: Option<String>,\n}\n\n// hidden implied parser so we can get matches without recursion.\n#[derive(Parser, Debug)]\nstruct CliHidden {\n    #[clap(subcommand)]\n    command: Commands,\n}\n\n#[derive(Subcommand, Debug)]\nenum Commands {\n    /// Extract and print info for targets.\n    TargetInfo(TargetInfo),\n    /// Build a docker image from file.\n    BuildDockerImage(BuildDockerImage),\n    /// Install git development hooks.\n    InstallGitHooks(InstallGitHooks),\n    /// Run code formatting checks and lints.\n    Check(Check),\n    /// Run unittest suite.\n    Test(Test),\n    /// CI tasks\n    #[clap(subcommand, hide = true)]\n    CiJob(CiJob),\n    /// Configure crosstool config files.\n    ConfigureCrosstool(ConfigureCrosstool),\n    /// Changelog related commands\n    #[clap(subcommand)]\n    Changelog(Changelog),\n    /// Code generation\n    Codegen(Codegen),\n}\n\nfn is_toolchain(toolchain: &str) -> cross::Result<String> {\n    if toolchain.starts_with('+') {\n        Ok(toolchain.chars().skip(1).collect())\n    } else {\n        let _ = <CliHidden as CommandFactory>::command().get_matches();\n        unreachable!();\n    }\n}\n\nmacro_rules! get_engine {\n    ($args:ident, $msg_info:ident) => {{\n        get_container_engine($args.engine.as_deref(), &mut $msg_info)\n    }};\n}\n\npub fn main() -> cross::Result<()> {\n    cross::install_panic_hook()?;\n    let cli = Cli::parse();\n    let mut msg_info = MessageInfo::create(cli.verbose, cli.quiet, cli.color.as_deref())?;\n    match cli.command {\n        Commands::TargetInfo(args) => {\n            let engine = get_engine!(args, msg_info)?;\n            target_info::target_info(args, &engine, &mut msg_info)?;\n        }\n        Commands::BuildDockerImage(args) => {\n            let engine = get_engine!(args, msg_info)?;\n            build_docker_image::build_docker_image(args, &engine, &mut msg_info)?;\n        }\n        Commands::InstallGitHooks(_) => {\n            install_git_hooks::install_git_hooks(&mut msg_info)?;\n        }\n        Commands::Check(args) => {\n            hooks::check(args, cli.toolchain.as_deref(), &mut msg_info)?;\n        }\n        Commands::Test(args) => {\n            hooks::test(args, cli.toolchain.as_deref(), &mut msg_info)?;\n        }\n        Commands::CiJob(args) => {\n            let metadata = cargo_metadata(&mut Verbosity::Verbose(2).into())?;\n            ci::ci(args, metadata)?;\n        }\n        Commands::ConfigureCrosstool(args) => {\n            crosstool::configure_crosstool(args, &mut msg_info)?;\n        }\n        Commands::Changelog(args) => {\n            changelog::changelog(args, &mut msg_info)?;\n        }\n        Commands::Codegen(args) => codegen::codegen(args)?,\n    }\n\n    Ok(())\n}\n\nfn get_container_engine(\n    engine: Option<&str>,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<docker::Engine> {\n    let engine = if let Some(ce) = engine {\n        which::which(ce)?\n    } else {\n        docker::get_container_engine()?\n    };\n    docker::Engine::from_path(engine, None, None, msg_info)\n}\n"
  },
  {
    "path": "xtask/src/pre-commit.sh",
    "content": "#!/usr/bin/env bash\ncargo xtask check --verbose\n"
  },
  {
    "path": "xtask/src/pre-push.sh",
    "content": "#!/usr/bin/env bash\ncargo xtask test --verbose\n"
  },
  {
    "path": "xtask/src/target_info.rs",
    "content": "use std::collections::BTreeMap;\n\nuse crate::util::{format_repo, pull_image};\nuse clap::Args;\nuse cross::shell::MessageInfo;\nuse cross::{docker, CommandExt};\n\n// Store raw text data in the binary so we don't need a data directory\n// when extracting all targets, or running our target info script.\nconst TARGET_INFO_SCRIPT: &str = include_str!(\"target_info.sh\");\n\n#[derive(Args, Debug)]\npub struct TargetInfo {\n    /// If not provided, get info for all targets.\n    pub targets: Vec<crate::ImageTarget>,\n    /// Image registry.\n    #[clap(long, default_value_t = String::from(\"ghcr.io\"))]\n    pub registry: String,\n    /// Image repository.\n    #[clap(long, default_value_t = String::from(\"cross-rs\"))]\n    pub repository: String,\n    /// Image tag.\n    #[clap(long, default_value_t = String::from(\"main\"))]\n    pub tag: String,\n    /// Container engine (such as docker or podman).\n    #[clap(long)]\n    pub engine: Option<String>,\n}\n\nfn image_info(\n    engine: &docker::Engine,\n    target: &crate::ImageTarget,\n    image: &str,\n    tag: &str,\n    msg_info: &mut MessageInfo,\n    has_test: bool,\n) -> cross::Result<()> {\n    if !tag.starts_with(\"local\") {\n        pull_image(engine, image, msg_info)?;\n    }\n\n    let mut command = engine.command();\n    command.arg(\"run\");\n    command.arg(\"--rm\");\n    command.args([\"-e\", &format!(\"TARGET={}\", target.name)]);\n    if msg_info.is_verbose() {\n        command.args([\"-e\", \"VERBOSE=1\"]);\n    }\n    if has_test {\n        command.args([\"-e\", \"HAS_TEST=1\"]);\n    } else {\n        command.args([\"-e\", \"HAS_TEST=\"]);\n    }\n    command.arg(image);\n    command.args([\"bash\", \"-c\", TARGET_INFO_SCRIPT]);\n    command.run(msg_info, msg_info.is_verbose())\n}\n\npub fn target_info(\n    TargetInfo {\n        mut targets,\n        registry,\n        repository,\n        tag,\n        ..\n    }: TargetInfo,\n    engine: &docker::Engine,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let matrix = crate::util::get_matrix();\n    let test_map: BTreeMap<crate::ImageTarget, bool> = matrix\n        .iter()\n        .map(|i| (i.to_image_target(), i.has_test(&i.target)))\n        .collect();\n\n    if targets.is_empty() {\n        targets = matrix\n            .iter()\n            .map(|t| t.to_image_target())\n            .filter(|t| t.has_ci_image())\n            .collect();\n    }\n\n    for target in targets {\n        let image = target.image_name(&format_repo(&registry, &repository), &tag);\n        let has_test = test_map\n            .get(&target)\n            .cloned()\n            .ok_or_else(|| eyre::eyre!(\"invalid target name {}\", target))?;\n        image_info(engine, &target, &image, &tag, msg_info, has_test)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "xtask/src/target_info.sh",
    "content": "#!/usr/bin/env bash\n#\n# this script can be invoked as follows:\n#       export TARGET=aarch64-unknown-linux-musl\n#       docker run -e TARGET ghcr.io/cross-rs/\"$TARGET\":main bash -c \"`cat extract_target_info.sh`\"\n#\n# the output will be similar to the following:\n#       | `aarch64-unknown-linux-musl`         | 1.2.0  | 9.2.0   | ✓   | 5.1.0 |\n#\n# in short, it recreates the table except for the test section in README.md.\n\nset -eo pipefail\n\nif [[ \"${VERBOSE}\" == 1 ]]; then\n    set -x\nfi\n\n# shellcheck disable=SC2153\ntarget=\"${TARGET}\"\narch=\"${target//-*/}\"\n\nextract_regex_version() {\n    # executing shared libraries outputs to stderr, rest to stdout\n    version=\"$($1 --version 2>&1)\"\n    if [[ \"${version}\" =~ $2 ]]; then\n        echo \"${BASH_REMATCH[1]}\"\n    else\n        echo \"Unable to match $3 version info for ${target}.\" 1>&2\n        exit 1\n    fi\n}\n\nmax_glibc_version() {\n    # glibc versions have the following format:\n    #   `libc-$major-$minor.so.$abi`, where the `.$abi` may be optional.\n    # shellcheck disable=SC2207\n    local -a paths=( $(ls \"${1}\"/libc-[0-9]*.[0-9]*.so* 2>/dev/null)  )\n    # shellcheck disable=SC2128\n    [ -z \"$paths\" ] && return 0\n    local major=0\n    local minor=0\n    local version\n    local x\n    local y\n    local is_larger\n\n    for i in \"${!paths[@]}\"; do\n        file=$(basename \"${paths[$i]}\")\n        version=\"${file//libc-/}\"\n        x=$(echo \"${version}\" | cut -d '.' -f 1)\n        y=$(echo \"${version}\" | cut -d '.' -f 2)\n        is_larger=\n\n        if [ \"${x}\" -gt \"${major}\" ]; then\n            is_larger=1\n        elif [ \"${x}\" -eq \"${major}\" ] && [ \"${y}\" -gt \"${minor}\" ]; then\n            is_larger=1\n        fi\n\n        if [ -n \"${is_larger}\" ]; then\n            major=\"${x}\"\n            minor=\"${y}\"\n        fi\n    done\n\n    echo \"${major}.${minor}\"\n}\n\nmax_solaris_libc_version() {\n    # solaris libc versions have the following format:\n    #  67: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS SUNW_1.21.1\n    local major=0\n    local minor=0\n    local patch=0\n    local version\n    local x\n    local y\n    local z\n    local is_larger\n\n    for version in \"${@}\"; do\n        x=$(echo \"${version}\" | cut -d '.' -f 1)\n        y=$(echo \"${version}\" | cut -d '.' -f 2)\n        z=$(echo \"${version}\" | cut -d '.' -f 3)\n        is_larger=\n\n        if [ \"${x}\" -gt \"${major}\" ]; then\n            is_larger=1\n        elif [ \"${x}\" -eq \"${major}\" ] && [ \"${y}\" -gt \"${minor}\" ]; then\n            is_larger=1\n        elif [ -z \"${patch}\" ]; then\n            is_larger=1\n        elif [ -n \"${z}\" ] && [ \"${x}\" -eq \"${major}\" ] && [ \"${y}\" -eq \"${minor}\" ] && [ \"${z}\" -gt \"${patch}\" ]; then\n            is_larger=1\n        fi\n\n        if [ -n \"${is_larger}\" ]; then\n            major=\"${x}\"\n            minor=\"${y}\"\n            patch=\"${z}\"\n        fi\n    done\n\n    result=\"${major}.${minor}\"\n    if [ -n \"${patch}\" ]; then\n        result=\"${result}.${patch}\"\n    fi\n    echo \"${result}\"\n}\n\nreadelf_all() {\n    # weirdly, readelf -a can produce a non-zero error code.\n    set +e\n    readelf -a \"$1\"\n    set -e\n}\n\nread_solaris_libc() {\n    # we can read the libc version from the libc symbols\n    # first, we need to use our compiler name to get the libdir\n    #    67: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS SUNW_1.21.1\n    # there will be many of these, so we want to grab the highest one.\n    local libc_so=\"${1}\"\n    lines=$(readelf_all \"${libc_so}\" | grep 'ABS SUNW_')\n    lines=$(echo \"${lines}\" | grep -o 'ABS .*')\n    # shellcheck disable=SC2207\n    libc_versions=($(echo \"$lines\" | cut -d ' ' -f 2 | cut -d '_' -f 2))\n    max_solaris_libc_version \"${libc_versions[@]}\"\n}\n\n\n# output variables\nlibc=\ncc=\ncxx=\nqemu=\n\n# select toolchain information\ncompiler_suffix=\"${target//-/_}\"\ncc_var=\"CC_${compiler_suffix}\"\ncxx_var=\"CXX_${compiler_suffix}\"\ncc_regex=\ncase \"${target}\" in\n    *-*-android*)\n        cc_regex=\".* clang version ([0-9]+.[0-9]+.[0-9]+) .*\"\n        ;;\n    *-*-*-musl*)\n        cc_regex=\".*gcc \\(GCC\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-linux-gnu*)\n        cc_regex=\".*gcc \\(.*\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-windows-gnu*)\n        # MinGW only reports major/minor versions, and can\n        # have a -posix or -win32 suffix, eg: 7.5-posix\n        cc_regex=\".*gcc.* \\(GCC\\) ([0-9]+.[0-9]+).*\"\n        ;;\n    *-*-freebsd)\n        cc_regex=\".*gcc \\(GCC\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-netbsd)\n        cc_regex=\".*gcc \\(.*\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-dragonfly)\n        cc_regex=\".*gcc \\(GCC\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-solaris)\n        cc_regex=\".*gcc \\(GCC\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-illumos)\n        cc_regex=\".*gcc \\(GCC\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-*-emscripten)\n        cc_regex=\"clang version ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *-none-*)\n        cc_regex=\".*gcc \\(.*\\) ([0-9]+.[0-9]+.[0-9]+).*\"\n        ;;\n    *)\n        echo \"TODO: Currently unsupported\"  1>&2\n        exit 1\n        ;;\nesac\n\n# select qemu arch\nqarch=\"${arch}\"\ncase \"${arch}\" in\n    arm*)\n        qarch=\"arm\"\n        ;;\n    i*86)\n        qarch=\"i386\"\n        ;;\n    powerpc)\n        qarch=\"ppc\"\n        ;;\n    powerpc64)\n        qarch=\"ppc64\"\n        ;;\n    powerpc64le)\n        qarch=\"ppc64le\"\n        ;;\n    riscv64*)\n        qarch=\"riscv64\"\n        ;;\nesac\nqemu_regex=\"qemu-${qarch} version ([0-9]+.[0-9]+.[0-9]+).*\"\n\n# evaluate our toolchain info\ncc_bin=\ncxx_bin=\ncase \"${target}\" in\n    i*86-unknown-linux-gnu | x86_64-unknown-linux-gnu)\n        cc_bin=\"gcc\"\n        cxx_bin=\"g++\"\n        ;;\n    thumb*-none-eabi* | arm*-none-eabi*)\n        # the ARM/THUMB targets don't have a CC_${compiler_suffix}\n        cc_bin=arm-none-eabi-gcc\n        cxx_bin=arm-none-eabi-g++\n        ;;\n    *-*-emscripten)\n        cc_bin=\"${EMSDK}/upstream/bin/clang\"\n        cxx_bin=\"${cc_bin}\"\n        ;;\n    *)\n        cc_bin=\"${!cc_var}\"\n        cxx_bin=\"${!cxx_var}\"\n        ;;\nesac\ncc=$(extract_regex_version \"${cc_bin}\" \"${cc_regex}\" compiler)\nif command -v \"${cxx_bin}\" &>/dev/null; then\n    # test we can compile a c++ program that requires the c++ stdlib\n    cat <<EOT >> main.cc\n#include <iostream>\nint main() {\n    std::cout << \"Testing this\" << std::endl;\n}\nEOT\n    cxx_flags=()\n    if [[ \"${target}\" == *-none-* ]]; then\n        cxx_flags=(\"${cxx_flags[@]}\" \"-nostartfiles\")\n    fi\n    if [[ \"${target}\" == *-*-emscripten ]]; then\n        # need to switch to the real c++ compiler here, not the wrapper\n        cxx_bin=emcc\n        # shellcheck disable=SC2206\n        cxx_flags=($EMCC_CFLAGS)\n    fi\n    if \"${cxx_bin}\" \"${cxx_flags[@]}\" main.cc >/dev/null 2>&1; then\n        cxx=1\n    fi\nfi\n\ncase \"${target}\" in\n    *-*-android*)\n        libc=\"${cc}\"\n        ;;\n    *-*-*-musl*)\n        toolchain_prefix=\"${!cc_var//-gcc/}\"\n        libdir=\"/usr/local/${toolchain_prefix}/lib\"\n        libc_regex=\".*Version ([0-9]+.[0-9]+.[0-9]+).*\"\n        if [[ \"${arch}\" = i[3-7]86 ]] || [ \"${arch}\" == x86_64 ]; then\n            libc_cmd=\"${libdir}/libc.so\"\n        else\n            libc_cmd=\"qemu-${qarch} ${libdir}/libc.so\"\n            if ! command -v \"qemu-${qarch}\" &>/dev/null; then\n                echo \"Unable to get qemu version for ${target}: qemu not found.\" 1>&2\n                exit 1\n            fi\n        fi\n        libc=$(extract_regex_version \"${libc_cmd}\" \"${libc_regex}\" libc)\n        ;;\n    arm-unknown-linux-gnueabihf)\n        # this is for crosstool-ng-based images with glibc\n        libdir=\"/x-tools/${target}/${target}/sysroot/lib/\"\n        libc=$(max_glibc_version \"${libdir}\")\n        ;;\n    i*86-unknown-linux-gnu)\n        libdir=\"/lib/x86_64-linux-gnu/\"\n        libc=$(max_glibc_version \"${libdir}\")\n        ;;\n    x86_64-unknown-linux-gnu)\n        libdir=\"/lib64/\"\n        libc=$(max_glibc_version \"${libdir}\")\n        if [ \"$libc\" == \"\" ]; then\n            libdir=\"/lib/x86_64-linux-gnu/\"\n            libc=$(max_glibc_version \"${libdir}\")\n        fi\n        ;;\n    riscv64gc-unknown-linux-gnu)\n        libc=\"$(dpkg-query --showformat='${Version}' --show libc6-riscv64-cross | cut -d- -f1)\"\n        ;;\n    *-*-linux-gnu*)\n        toolchain_prefix=\"${!cc_var//-gcc/}\"\n        libdir=\"/usr/${toolchain_prefix}/lib\"\n        libc=$(max_glibc_version \"${libdir}\")\n        ;;\n    *-*-windows-gnu)\n        # no libc, intentionally omitted.\n        ;;\n    *-*-freebsd)\n        # we write the FreeBSD version to /opt/freebsd-version\n        # the symbol versioning can be found here:\n        #   https://wiki.freebsd.org/SymbolVersioning\n        version=$(cat /opt/freebsd-version)\n        if [[ \"${version}\" =~ ([0-9]+)\\.([0-9]+)\" (\"[A-Za-z0-9]+\")\" ]]; then\n            major_version=\"${BASH_REMATCH[1]}\"\n            minor_version=\"${BASH_REMATCH[2]}\"\n            case \"${major_version}\" in\n                7)\n                    libc=\"1.0\"\n                    ;;\n                8)\n                    libc=\"1.1\"\n                    ;;\n                9)\n                    libc=\"1.2\"\n                    ;;\n                10)\n                    libc=\"1.3\"\n                    ;;\n                11)\n                    libc=\"1.4\"\n                    ;;\n                12)\n                    libc=\"1.5\"\n                    ;;\n                13)\n                    libc=\"1.6\"\n                    ;;\n                14)\n                    libc=\"1.7\"\n                    ;;\n                *)\n                    echo \"Invalid FreeBSD version, got ${major_version}.${minor_version}.\" 1>&2\n                    exit 1\n                    ;;\n            esac\n        else\n            echo \"Unable to get libc version for ${target}: invalid FreeBSD release found.\" 1>&2\n            exit 1\n        fi\n        ;;\n    *-*-netbsd)\n        # We can read the NetBSD version from the libc symbols.\n        # The output follows:\n        #  NetBSD                0x00000004      IDENT 902000000 (9.2.0)\n        libdir=\"/usr/local/${target}/lib\"\n        version=$(readelf_all \"${libdir}\"/libc.so | grep NetBSD | head -n 1)\n        if [[ \"${version}\" =~ .+\" (\"([0-9]+)\".\"([0-9]+)\".\"([0-9]+)\")\" ]]; then\n            major_version=\"${BASH_REMATCH[1]}\"\n            minor_version=\"${BASH_REMATCH[2]}\"\n            patch_version=\"${BASH_REMATCH[3]}\"\n            libc=\"${major_version}.${minor_version}.${patch_version}\"\n        else\n            echo \"Unable to get libc version for ${target}: invalid NetBSD release found.\" 1>&2\n            exit 1\n        fi\n        ;;\n    *-*-dragonfly)\n        # we write the Dragonfly version to /opt/dragonfly-version\n        version=$(cat /opt/dragonfly-version)\n        if [[ \"${version}\" =~ ([0-9]+)\\.([0-9]+)\\.([0-9]+)\"_REL\" ]]; then\n            major_version=\"${BASH_REMATCH[1]}\"\n            minor_version=\"${BASH_REMATCH[2]}\"\n            patch_version=\"${BASH_REMATCH[3]}\"\n            libc=\"${major_version}.${minor_version}.${patch_version}\"\n        else\n            echo \"Unable to get libc version for ${target}: invalid Dragonfly release found.\" 1>&2\n            exit 1\n        fi\n        ;;\n    *-*-solaris)\n        prefix=\"${cc_bin//-gcc/}\"\n        libdir=\"/usr/local/${prefix}/lib\"\n        libc=$(read_solaris_libc \"${libdir}\"/libc.so)\n        ;;\n    *-*-illumos)\n        libdir=\"/usr/local/${target}/sysroot/lib\"\n        libc=$(read_solaris_libc \"${libdir}\"/libc.so)\n        ;;\n    *-*-emscripten)\n        # we want the emsdk version, which is the image version\n        libc_cmd=emcc\n        libc_regex=\"emcc \\(.* GNU ld) ([0-9]+.[0-9]+.[0-9]+).*\"\n        libc=$(extract_regex_version \"${libc_cmd}\" \"${libc_regex}\" libc)\n        ;;\n    thumb*-none-eabi* | arm*-none-eabi*)\n        # newlib kinda sucks. just query for the install package\n        pkg=$(dpkg --get-selections | grep -v deinstall | grep newlib | head -n 1)\n        pkg=$(echo \"${pkg}\" | cut -f 1)\n        version=$(dpkg-query --showformat='${Version}' --show \"${pkg}\")\n        if [[ \"${version}\" =~ ([0-9]+)\".\"([0-9]+)\".\"([0-9]+)[^0-9].* ]]; then\n            major_version=\"${BASH_REMATCH[1]}\"\n            minor_version=\"${BASH_REMATCH[2]}\"\n            patch_version=\"${BASH_REMATCH[3]}\"\n            libc=\"${major_version}.${minor_version}.${patch_version}\"\n        else\n            echo \"Unable to get libc version for ${target}: invalid THUMB release found.\" 1>&2\n            exit 1\n        fi\n        ;;\n    *)\n        echo \"TODO: Currently unsupported\" 1>&2\n        exit 1\n        ;;\nesac\n\nif command -v \"qemu-${qarch}\" &>/dev/null; then\n    qemu=$(extract_regex_version \"qemu-${qarch}\" \"${qemu_regex}\" qemu)\nfi\n\n# format our output\nprintf \"| %-36s |\" \"\\`${target}\\`\"\nif [ \"$libc\" != \"\" ]; then\n    printf \" %-6s |\" \"${libc}\"\nelse\n    printf \" N/A    |\"\nfi\nif [ \"$cc\" != \"\" ]; then\n    printf \" %-7s |\" \"${cc}\"\nelse\n    printf \" N/A     |\"\nfi\nif [ \"$cxx\" != \"\" ]; then\n    printf \" ✓   |\"\nelse\n    printf \"     |\"\nfi\nif [ \"$qemu\" != \"\" ]; then\n    printf \" %-5s |\" \"${qemu}\"\nelse\n    printf \" N/A   |\"\nfi\nif [ \"${HAS_TEST}\" != \"\" ]; then\n    printf \"   ✓    |\"\nelse\n    printf \"       |\"\nfi\nprintf \"\\n\"\n"
  },
  {
    "path": "xtask/src/util.rs",
    "content": "use std::env;\nuse std::fs;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\n\nuse cross::shell::MessageInfo;\nuse cross::{docker, CommandExt, ToUtf8};\n\nuse once_cell::sync::{Lazy, OnceCell};\nuse serde::Deserialize;\n\nstatic WORKSPACE: OnceCell<PathBuf> = OnceCell::new();\n\n/// Returns the cargo workspace for the manifest\npub fn get_cargo_workspace() -> &'static Path {\n    let manifest_dir = env!(\"CARGO_MANIFEST_DIR\");\n    WORKSPACE.get_or_init(|| {\n        cross::cargo_metadata_with_args(\n            Some(manifest_dir.as_ref()),\n            None,\n            &mut MessageInfo::create(2, false, None).expect(\"should not fail\"),\n        )\n        .unwrap()\n        .unwrap()\n        .workspace_root\n    })\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]\n#[serde(deny_unknown_fields, rename_all = \"kebab-case\")]\npub struct CiTarget {\n    /// The name of the target. This can either be a target triple, or if the image is \"special\", the name of the special thing it does.\n    pub target: String,\n    #[serde(default)]\n    pub special: bool,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub sub: Option<String>,\n    /// The runner to use in CI, see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners\n    ///\n    /// if this is not equal to `ubuntu-latest`, no docker image will be built unless it's been special cased.\n    pub os: String,\n    /// if `true` test more extensive cargo support, including tests and running binaries\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub run: Option<bool>,\n    /// if `true` publish the generated binaries for cross\n    #[serde(default)]\n    pub deploy: Option<bool>,\n    /// the platform to build this image for, defaults to `[\"linux/amd64\"]`, takes multiple\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    platforms: Option<Vec<String>>,\n    /// if `true` signal that this target requires `-Zbuild-std`\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub build_std: Option<bool>,\n    /// test the cpp compiler\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub cpp: Option<bool>,\n    /// test dylib support, requires `run = true`\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub dylib: Option<bool>,\n    /// qemu runners that can be used with this target, space separated.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub runners: Option<String>,\n    /// if `true` test no std support as if std does exists. If `false` build https://github.com/rust-lang/compiler-builtins\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub std: Option<bool>,\n    #[serde(skip_serializing_if = \"is_false\", default)]\n    pub disabled: bool,\n}\n\npub fn is_false(b: &bool) -> bool {\n    !*b\n}\n\nimpl CiTarget {\n    pub fn has_test(&self, target: &str) -> bool {\n        // bare-metal targets don't have unittests right now\n        self.run.unwrap_or_default() && !target.contains(\"-none-\")\n    }\n\n    pub fn to_image_target(&self) -> crate::ImageTarget {\n        crate::ImageTarget {\n            name: self.target.clone(),\n            sub: self.sub.clone(),\n        }\n    }\n\n    pub fn builds_image(&self) -> bool {\n        self.os == \"ubuntu-latest\"\n    }\n\n    pub fn platforms(&self) -> &[String] {\n        self.platforms.as_ref().unwrap_or(&DEFAULT_PLATFORMS_STRING)\n    }\n}\n\n/// Default platforms to build images with\n///\n///  if this is changed, make sure to update documentation on [CiTarget::platforms]\npub static DEFAULT_PLATFORMS: &[cross::docker::ImagePlatform] =\n    &[cross::docker::ImagePlatform::DEFAULT];\n\npub static DEFAULT_PLATFORMS_STRING: Lazy<Vec<String>> = Lazy::new(|| {\n    DEFAULT_PLATFORMS\n        .iter()\n        .map(|p| p.target.to_string())\n        .collect()\n});\n\nstatic MATRIX: OnceCell<Vec<CiTarget>> = OnceCell::new();\n\npub fn get_matrix() -> &'static Vec<CiTarget> {\n    #[derive(Deserialize)]\n    struct Targets {\n        target: Vec<CiTarget>,\n    }\n    MATRIX\n        .get_or_try_init::<_, eyre::Report>(|| {\n            let targets: Targets = toml::from_str(std::str::from_utf8(&std::fs::read(\n                get_cargo_workspace().join(\"targets.toml\"),\n            )?)?)?;\n            Ok(targets.target)\n        })\n        .unwrap()\n}\n\npub fn with_section_reports(\n    origin: eyre::Report,\n    iter: impl IntoIterator<Item = eyre::Report>,\n) -> eyre::Report {\n    use color_eyre::{Section as _, SectionExt as _};\n    iter.into_iter().fold(origin, |report, e| {\n        report.section(format!(\"{e:?}\").header(\"Error:\"))\n    })\n}\n\npub fn format_repo(registry: &str, repository: &str) -> String {\n    let mut output = String::new();\n    if !repository.is_empty() {\n        output = repository.to_string();\n    }\n    if !registry.is_empty() {\n        output = format!(\"{registry}/{output}\");\n    }\n\n    output\n}\n\npub fn pull_image(\n    engine: &docker::Engine,\n    image: &str,\n    msg_info: &mut MessageInfo,\n) -> cross::Result<()> {\n    let mut command = engine.subcommand(\"pull\");\n    command.arg(image);\n    let out = command.run_and_get_output(msg_info)?;\n    command.status_result(msg_info, out.status, Some(&out))?;\n    Ok(())\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]\npub struct ImageTarget {\n    pub name: String,\n    pub sub: Option<String>,\n}\n\nimpl ImageTarget {\n    pub fn image_name(&self, repository: &str, tag: &str) -> String {\n        cross::docker::image_name(&self.name, self.sub.as_deref(), repository, tag)\n    }\n\n    pub fn alt(&self) -> String {\n        if let Some(sub) = &self.sub {\n            format!(\"{}:{sub}\", self.name)\n        } else {\n            self.name.to_string()\n        }\n    }\n\n    /// Determines if this target has a ci image\n    pub fn has_ci_image(&self) -> bool {\n        let matrix = get_matrix();\n        matrix\n            .iter()\n            .any(|m| m.builds_image() && m.target == self.name && m.sub == self.sub)\n    }\n\n    /// Determine if this target is a \"normal\" target for a triplet\n    pub fn is_standard_target_image(&self) -> bool {\n        let matrix = get_matrix();\n\n        !matrix\n            .iter()\n            .filter(|m| m.special)\n            .any(|m| m.target == self.name)\n            && self.has_ci_image()\n    }\n\n    // this exists solely for zig, since we also want it as a provided target.\n    /// Determine if this target has a toolchain image\n    pub fn is_toolchain_image(&self) -> bool {\n        !matches!(self.name.as_ref(), \"cross\") && self.has_ci_image()\n    }\n\n    /// Determine if this target needs to interact with the project root.\n    pub fn needs_workspace_root_context(&self) -> bool {\n        self.name == \"cross\"\n    }\n\n    pub fn is_armv6(&self) -> bool {\n        matches!(\n            self.name.as_str(),\n            \"arm-unknown-linux-gnueabi\" | \"arm-unknown-linux-musleabi\"\n        )\n    }\n\n    pub fn is_armv7(&self) -> bool {\n        matches!(\n            self.name.as_str(),\n            \"armv7-unknown-linux-gnueabihf\" | \"armv7-unknown-linux-musleabihf\"\n        )\n    }\n}\n\nimpl std::str::FromStr for ImageTarget {\n    type Err = std::convert::Infallible;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        // we designate certain targets like `x86_64-unknown-linux-gnu.centos`,\n        // where `centos` is a subtype of `x86_64-unknown-linux-gnu`. however,\n        // LLVM triples can also contain `.` characters, such as with\n        // `thumbv8m.main-none-eabihf`, so we make sure it's only at the end.\n        if let Some((target, sub)) = s.rsplit_once('.') {\n            if sub.chars().all(|x| char::is_ascii_alphabetic(&x)) {\n                return Ok(ImageTarget {\n                    name: target.to_string(),\n                    sub: Some(sub.to_string()),\n                });\n            }\n        }\n\n        Ok(ImageTarget {\n            name: s.to_string(),\n            sub: None,\n        })\n    }\n}\n\nimpl std::fmt::Display for ImageTarget {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if let Some(sub) = &self.sub {\n            write!(f, \"{}.{sub}\", self.name)\n        } else {\n            write!(f, \"{}\", self.name)\n        }\n    }\n}\n\npub fn has_nightly(msg_info: &mut MessageInfo) -> cross::Result<bool> {\n    cross::cargo_command()\n        .arg(\"+nightly\")\n        .run_and_get_output(msg_info)\n        .map(|o| o.status.success())\n}\n\npub fn get_channel_prefer_nightly<'a>(\n    msg_info: &mut MessageInfo,\n    toolchain: Option<&'a str>,\n) -> cross::Result<Option<&'a str>> {\n    Ok(match toolchain {\n        Some(t) => Some(t),\n        None => match has_nightly(msg_info)? {\n            true => Some(\"nightly\"),\n            false => None,\n        },\n    })\n}\n\npub fn cargo(channel: Option<&str>) -> Command {\n    let mut command;\n    if let Some(channel) = channel {\n        command = Command::new(\"rustup\");\n        command.args([\"run\", channel, \"cargo\"]);\n    } else {\n        command = cross::cargo_command();\n    }\n    command\n}\n\npub fn cargo_metadata(msg_info: &mut MessageInfo) -> cross::Result<cross::CargoMetadata> {\n    cross::cargo_metadata_with_args(Some(Path::new(env!(\"CARGO_MANIFEST_DIR\"))), None, msg_info)?\n        .ok_or_else(|| eyre::eyre!(\"could not find cross workspace\"))\n}\n\npub fn project_dir(msg_info: &mut MessageInfo) -> cross::Result<PathBuf> {\n    Ok(cargo_metadata(msg_info)?.workspace_root)\n}\n\nmacro_rules! gha_output {\n    ($fmt:literal$(, $args:expr)* $(,)?) => {\n        #[cfg(not(test))]\n        println!($fmt $(, $args)*);\n        #[cfg(test)]\n        eprintln!($fmt $(,$args)*);\n    };\n}\n\n// note: for GHA actions we need to output these tags no matter the verbosity level\npub fn gha_print(content: &str) {\n    gha_output!(\"{}\", content);\n}\n\n// note: for GHA actions we need to output these tags no matter the verbosity level\npub fn gha_error(content: &str) {\n    gha_output!(\"::error {}\", content);\n}\n\n#[track_caller]\npub fn gha_output(tag: &str, content: &str) -> cross::Result<()> {\n    if content.contains('\\n') {\n        // https://github.com/actions/toolkit/issues/403\n        eyre::bail!(\"output `{tag}` contains newlines, consider serializing with json and deserializing in gha with fromJSON()\");\n    }\n    write_to_gha_env_file(\"GITHUB_OUTPUT\", &format!(\"{tag}={content}\"))?;\n    Ok(())\n}\n\npub fn read_dockerfiles(msg_info: &mut MessageInfo) -> cross::Result<Vec<(PathBuf, String)>> {\n    let root = project_dir(msg_info)?;\n    let docker = root.join(\"docker\");\n    let mut dockerfiles = vec![];\n    for entry in fs::read_dir(docker)? {\n        let entry = entry?;\n        let file_type = entry.file_type()?;\n        let file_name = entry.file_name();\n        if file_type.is_file() && file_name.to_utf8()?.starts_with(\"Dockerfile\") {\n            let contents = fs::read_to_string(entry.path())?;\n            dockerfiles.push((entry.path().to_path_buf(), contents));\n        }\n    }\n\n    Ok(dockerfiles)\n}\n\npub fn write_to_string(path: &Path, contents: &str) -> cross::Result<()> {\n    let mut file = fs::OpenOptions::new()\n        .write(true)\n        .truncate(true)\n        .create(true)\n        .open(path)?;\n    writeln!(file, \"{}\", contents)?;\n    Ok(())\n}\n\n// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files\npub fn write_to_gha_env_file(env_name: &str, contents: &str) -> cross::Result<()> {\n    eprintln!(\"{contents}\");\n    let path = if let Ok(path) = env::var(env_name) {\n        PathBuf::from(path)\n    } else {\n        eyre::ensure!(\n            env::var(\"GITHUB_ACTIONS\").is_err(),\n            \"expected GHA envfile to exist\"\n        );\n        return Ok(());\n    };\n    let mut file = fs::OpenOptions::new().append(true).open(path)?;\n    writeln!(file, \"{}\", contents)?;\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use cross::shell::Verbosity;\n    use std::collections::BTreeMap;\n\n    #[test]\n    fn test_parse_image_target() {\n        assert_eq!(\n            ImageTarget {\n                name: \"x86_64-unknown-linux-gnu\".to_owned(),\n                sub: None,\n            },\n            \"x86_64-unknown-linux-gnu\".parse().unwrap()\n        );\n        assert_eq!(\n            ImageTarget {\n                name: \"x86_64-unknown-linux-gnu\".to_owned(),\n                sub: Some(\"centos\".to_owned()),\n            },\n            \"x86_64-unknown-linux-gnu.centos\".parse().unwrap()\n        );\n        assert_eq!(\n            ImageTarget {\n                name: \"thumbv8m.main-none-eabihf\".to_owned(),\n                sub: None,\n            },\n            \"thumbv8m.main-none-eabihf\".parse().unwrap()\n        );\n        assert_eq!(\n            ImageTarget {\n                name: \"thumbv8m.main-unknown-linux-gnueabihf\".to_owned(),\n                sub: Some(\"alpine\".to_owned()),\n            },\n            \"thumbv8m.main-unknown-linux-gnueabihf.alpine\"\n                .parse()\n                .unwrap()\n        );\n    }\n\n    #[test]\n    fn check_ubuntu_base() -> cross::Result<()> {\n        // count all the entries of FROM for our images\n        let mut counts = BTreeMap::new();\n        let mut msg_info = Verbosity::Verbose(2).into();\n        let dockerfiles = read_dockerfiles(&mut msg_info)?;\n        for (path, dockerfile) in dockerfiles {\n            let lines: Vec<&str> = dockerfile.lines().collect();\n            let index = lines\n                .iter()\n                .map(|x| x.trim())\n                .position(|x| x.to_lowercase().starts_with(\"from\"))\n                .ok_or_else(|| eyre::eyre!(\"unable to find FROM instruction for {:?}\", path))?;\n            let tag = lines[index]\n                .split_whitespace()\n                .nth(1)\n                .ok_or_else(|| eyre::eyre!(\"invalid FROM instruction, got {}\", lines[index]))?;\n            if let Some(value) = counts.get_mut(tag) {\n                *value += 1;\n            } else {\n                counts.insert(tag.to_string(), 1);\n            }\n        }\n\n        // Now, get the most common and ensure our base is correct.\n        let actual_base = cross::docker::UBUNTU_BASE;\n        let max_base = counts\n            .iter()\n            .max_by(|x, y| x.1.cmp(y.1))\n            .map(|(k, _)| k)\n            .ok_or_else(|| eyre::eyre!(\"have no dockerfiles\"))?;\n\n        if actual_base != max_base {\n            eyre::bail!(\"most common base image is {max_base} but source code has {actual_base}\")\n        } else {\n            Ok(())\n        }\n    }\n}\n"
  }
]