diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..35049cbc --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8fff11e0..ac58041e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,8 +48,7 @@ jobs: run: cargo fmt --all -- --check build: - # We don't use ubuntu-latest because we care about the apt packages available. - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.target.os }} strategy: fail-fast: false matrix: @@ -57,10 +56,30 @@ jobs: - stable - beta - nightly - llvm: - - 19 - - source - name: rustc=${{ matrix.rust }} llvm=${{ matrix.llvm }} + # We don't use ubuntu-latest because we care about the apt packages available. + target: + - os: macos-14 + target: aarch64-apple-darwin + build-type: native + - os: macos-13 + target: x86_64-apple-darwin + build-type: native + - os: ubuntu-22.04 + target: aarch64-unknown-linux-gnu + build-type: cross + - os: ubuntu-22.04 + target: aarch64-unknown-linux-musl + build-type: cross + - os: ubuntu-22.04 + target: riscv64gc-unknown-linux-gnu + build-type: cross + - os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + build-type: native + - os: ubuntu-22.04 + target: x86_64-unknown-linux-musl + build-type: cross + name: rustc=${{ matrix.rust }} target=${{ matrix.target.target }} needs: llvm env: @@ -85,9 +104,11 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Check (default features, no system LLVM) + if: matrix.build-type == 'native' run: cargo check - name: Build (default features, no system LLVM) + if: matrix.build-type == 'native' run: cargo build - name: Install btfdump @@ -95,51 +116,36 @@ jobs: run: cargo install btfdump - name: Install prerequisites - if: matrix.rust == 'nightly' + if: matrix.rust == 'nightly' && runner.os == 'Linux' && startsWith(matrix.target.target, 'x86_64') # ubuntu-22.04 comes with clang 13-15[0]; support for signed and 64bit # enum values was added in clang 15[1] which isn't in `$PATH`. # # gcc-multilib provides at least which is referenced by libbpf. # + # libelf is a dependency of libbpf. + # # [0] https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md # # [1] https://github.com/llvm/llvm-project/commit/dc1c43d run: | set -euxo pipefail sudo apt update - sudo apt -y install gcc-multilib + sudo apt -y install gcc-multilib libelf-dev echo /usr/lib/llvm-15/bin >> $GITHUB_PATH - - name: Install LLVM - if: matrix.llvm != 'source' + - name: Install prerequisites + if: runner.os == 'macOS' + # We need system-wide LLVM only for FileCheck. run: | set -euxo pipefail - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc - echo -e deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ matrix.llvm }} main | sudo tee /etc/apt/sources.list.d/llvm.list - - sudo apt update - # TODO(vadorovsky): Remove the requirement of libpolly. - # - # Packages from apt.llvm.org are being built all at once, with one - # cmake build with superset of options, then different binaries and - # libraries are being included in different packages. - # - # That results in `llvm-config --libname --link-static` mentioning - # libpolly, even if it's not installed. The output of that command is - # being used in build.rs of llvm-sys, so building llvm-sys on such - # system is complaining about lack of libpolly. - # - # Hopefully that nightmare goes away once we switch to binstalls and - # ditch the system LLVM option. - sudo apt -y install llvm-${{ matrix.llvm }}-dev libpolly-${{ matrix.llvm }}-dev - echo /usr/lib/llvm-${{ matrix.llvm }}/bin >> $GITHUB_PATH + brew install llvm + echo $(brew --prefix)/opt/llvm/bin >> $GITHUB_PATH - name: Restore LLVM - if: matrix.llvm == 'source' uses: actions/cache/restore@v4 with: path: llvm-install - key: ${{ needs.llvm.outputs.cache-key }} + key: ${{ needs.llvm.outputs[format('cache-key-{0}', matrix.target.target)] }} fail-on-cache-miss: true - name: Add LLVM to PATH && LD_LIBRARY_PATH @@ -160,17 +166,51 @@ jobs: cargo clean -p llvm-sys --release - uses: taiki-e/install-action@cargo-hack + if: matrix.build-type == 'native' + + # Run cargo commands with `cargo hack` for native targets. + # Run cargo commands with `xtask` for all targets. - name: Check + if: matrix.build-type == 'native' run: cargo hack check --feature-powerset + - name: Check (xtask) + run: | + cargo xtask check \ + --container-engine podman \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --pull always \ + --target ${{ matrix.target.target }} + - name: Build + if: matrix.build-type == 'native' run: cargo hack build --feature-powerset + - name: Build (xtask) + run: | + cargo xtask build \ + --container-engine podman \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --pull always \ + --target ${{ matrix.target.target }} + - name: Test - if: matrix.rust == 'nightly' + if: matrix.build-type == 'native' && matrix.rust == 'nightly' run: cargo hack test --feature-powerset + - name: Test (xtask) + if: matrix.rust == 'nightly' + run: | + cargo xtask test \ + --container-engine podman \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --pull always \ + --target ${{ matrix.target.target }} + - uses: actions/checkout@v4 if: matrix.rust == 'nightly' with: @@ -179,10 +219,16 @@ jobs: submodules: recursive - name: Install - if: matrix.rust == 'nightly' - run: cargo install --path . --no-default-features + if: matrix.rust == 'nightly' && runner.os == 'Linux' && matrix.target.target == 'x86_64-unknown-linux-musl' + run: | + cargo xtask install \ + --container-engine podman \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --pull always \ + --target ${{ matrix.target.target }} - name: Run aya integration tests - if: matrix.rust == 'nightly' + if: matrix.rust == 'nightly' && runner.os == 'Linux' && matrix.target.target == 'x86_64-unknown-linux-musl' working-directory: aya run: cargo xtask integration-test local diff --git a/.github/workflows/llvm.yml b/.github/workflows/llvm.yml index 04bdd3ea..9e88e3af 100644 --- a/.github/workflows/llvm.yml +++ b/.github/workflows/llvm.yml @@ -3,16 +3,53 @@ name: LLVM on: workflow_call: outputs: - cache-key: - value: ${{ jobs.llvm.outputs.cache-key }} + cache-key-aarch64-apple-darwin: + value: ${{ jobs.llvm.outputs.cache-key-aarch64-apple-darwin }} + cache-key-x86_64-apple-darwin: + value: ${{ jobs.llvm.outputs.cache-key-x86_64-apple-darwin }} + cache-key-aarch64-unknown-linux-gnu: + value: ${{ jobs.llvm.outputs.cache-key-aarch64-unknown-linux-gnu }} + cache-key-aarch64-unknown-linux-musl: + value: ${{ jobs.llvm.outputs.cache-key-aarch64-unknown-linux-musl }} + cache-key-riscv64gc-unknown-linux-gnu: + value: ${{ jobs.llvm.outputs.cache-key-riscv64gc-unknown-linux-gnu }} + cache-key-x86_64-unknown-linux-gnu: + value: ${{ jobs.llvm.outputs.cache-key-x86_64-unknown-linux-gnu }} + cache-key-x86_64-unknown-linux-musl: + value: ${{ jobs.llvm.outputs.cache-key-x86_64-unknown-linux-musl }} jobs: llvm: - runs-on: ubuntu-22.04 - name: llvm + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-14 + target: aarch64-apple-darwin + - os: macos-13 + target: x86_64-apple-darwin + - os: ubuntu-22.04 + target: aarch64-unknown-linux-gnu + - os: ubuntu-22.04 + target: aarch64-unknown-linux-musl + - os: ubuntu-22.04 + target: riscv64gc-unknown-linux-gnu + - os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + - os: ubuntu-22.04 + target: x86_64-unknown-linux-musl + name: llvm ${{ matrix.target }} outputs: - cache-key: ${{ steps.cache-key.outputs.cache-key }} + cache-key-aarch64-apple-darwin: ${{ steps.cache-key.outputs.cache-key-aarch64-apple-darwin }} + cache-key-x86_64-apple-darwin: ${{ steps.cache-key.outputs.cache-key-x86_64-apple-darwin }} + cache-key-aarch64-unknown-linux-gnu: ${{ steps.cache-key.outputs.cache-key-aarch64-unknown-linux-gnu }} + cache-key-aarch64-unknown-linux-musl: ${{ steps.cache-key.outputs.cache-key-aarch64-unknown-linux-musl }} + cache-key-riscv64gc-unknown-linux-gnu: ${{ steps.cache-key.outputs.cache-key-riscv64gc-unknown-linux-gnu }} + cache-key-x86_64-unknown-linux-gnu: ${{ steps.cache-key.outputs.cache-key-x86_64-unknown-linux-gnu }} + cache-key-x86_64-unknown-linux-musl: ${{ steps.cache-key.outputs.cache-key-x86_64-unknown-linux-musl }} steps: + - uses: actions/checkout@v4 + - id: ls-remote run: | set -euxo pipefail @@ -20,82 +57,65 @@ jobs: echo "sha=$value" >> "$GITHUB_OUTPUT" - id: cache-key - run: echo "cache-key=llvm-${{ steps.ls-remote.outputs.sha }}-1" >> "$GITHUB_OUTPUT" + run: echo "cache-key-${{ matrix.target }}=llvm-${{ matrix.target }}-${{ steps.ls-remote.outputs.sha }}-1" >> "$GITHUB_OUTPUT" - name: Cache id: cache-llvm uses: actions/cache@v4 with: path: llvm-install - key: ${{ steps.cache-key.outputs.cache-key }} + key: ${{ steps.cache-key.outputs[format('cache-key-{0}', matrix.target)] }} lookup-only: true - - name: Install Tools - if: steps.cache-llvm.outputs.cache-hit != 'true' + - name: Free disk space + if: steps.cache-llvm.outputs.cache-hit != 'true' && runner.os == 'Linux' run: | set -euxo pipefail - wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | \ - gpg --dearmor - | \ - sudo tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null - echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | \ - sudo tee /etc/apt/sources.list.d/kitware.list >/dev/null - - sudo apt update - sudo apt -y install cmake ninja-build clang lld + sudo apt -y remove \ + azure-cli \ + '^aspnetcore-.*' \ + '^dotnet-.*' \ + firefox \ + '^google-cloud-.*' \ + '^google-chrome-*' \ + '^imagemagick.*' \ + java-common \ + kubectl \ + '^libmagick.*' \ + '^libmono.*' \ + '^libnginx.*' \ + '^microsoft-edge.*' \ + '^mongodb-.*' \ + mono-devel \ + '^mysql-.*' \ + '^php.*' \ + powershell \ + '^r-.*' + sudo rm -rf \ + /opt/ghc \ + /opt/hostedtoolcache \ + /usr/lib/jvm \ + /usr/local/.ghcup \ + /usr/local/aws* \ + /usr/local/bin/{aliyun,azcopy,bicep,helm,minikube,oc,occopy,packer,pulumi*,stack,terraform} \ + /usr/local/julia* \ + /usr/local/lib/android \ + /usr/local/share/{chromium,powershell,vcpkg} \ + /usr/share/dotnet || true + sudo docker image prune --all --force || true - - name: Checkout LLVM Source - if: steps.cache-llvm.outputs.cache-hit != 'true' - uses: actions/checkout@v4 - with: - repository: aya-rs/llvm-project - ref: ${{ steps.ls-remote.outputs.sha }} - path: llvm-project - - - name: Configure LLVM - if: steps.cache-llvm.outputs.cache-hit != 'true' + - name: Install dependencies + if: steps.cache-llvm.outputs.cache-hit != 'true' && runner.os == 'macOS' run: | set -euxo pipefail - cmake \ - -S llvm-project/llvm \ - -B llvm-build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/llvm-install" \ - -DLLVM_BUILD_LLVM_DYLIB=ON \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLVM_ENABLE_PROJECTS= \ - -DLLVM_ENABLE_RUNTIMES= \ - -DLLVM_INSTALL_UTILS=ON \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DLLVM_TARGETS_TO_BUILD=BPF \ - -DLLVM_USE_LINKER=lld - - - name: Install LLVM - if: steps.cache-llvm.outputs.cache-hit != 'true' - env: - # Create symlinks rather than copies to conserve disk space. At the time of this writing, - # GitHub-hosted runners have 14GB of SSD space - # (https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). - # - # Since the LLVM build creates a bunch of symlinks (and this setting does not turn those - # into symlinks-to-symlinks), use absolute symlinks so we can distinguish the two cases. - CMAKE_INSTALL_MODE: ABS_SYMLINK - run: cmake --build llvm-build --target install + brew install cmake lld llvm ninja + echo $(brew --prefix)/opt/llvm/bin >> $GITHUB_PATH - - name: Rewrite LLVM Symlinks + - name: Build LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' - # Move targets over the symlinks that point to them. - # - # This whole dance would be simpler if CMake supported CMAKE_INSTALL_MODE=MOVE. run: | set -euxo pipefail - find llvm-install -type l -execdir sh -eux -c ' - for link in "$@"; do - target=$(readlink "$link") - case $target in - /*) mv "$target" "$link" ;; - esac - done - ' sh {} + + cargo xtask build-llvm \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --target "${{ matrix.target }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1cb4c1c3..099b6811 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,15 +9,31 @@ jobs: uses: ./.github/workflows/llvm.yml upload-bins: - # TODO: Build for macos someday. - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-13 + target: aarch64-apple-darwin + - os: macos-13 + target: x86_64-apple-darwin + - os: ubuntu-22.04 + target: aarch64-unknown-linux-musl + - os: ubuntu-22.04 + target: riscv64-unknown-linux-gnu + - os: ubuntu-22.04 + target: x86_64-unknown-linux-musl needs: llvm + + env: + RUST_BACKTRACE: full + steps: - name: Restore LLVM uses: actions/cache/restore@v4 with: path: llvm-install - key: ${{ needs.llvm.outputs.cache-key }} + key: ${{ needs.llvm.outputs[format('cache-key-{0}', matrix.target-llvm)] }} fail-on-cache-miss: true - name: Add LLVM to PATH @@ -28,9 +44,19 @@ jobs: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - - uses: taiki-e/upload-rust-binary-action@v1 + - name: Build + run: | + cargo xtask build \ + --container-engine podman \ + --container-tag initial \ + --llvm-install-dir "${{ github.workspace }}/llvm-install" \ + --release \ + --pull always \ + --target ${{ matrix.target.target }} + + - uses: svenstaro/upload-release-action@v2 with: - bin: bpf-linker - features: llvm-sys/force-static - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/${{ matrix.target }}/release/bpf-linker + asset_name: bpf-linker-${{ matrix.target }} + tag: ${{ github.ref }} diff --git a/Cargo.lock b/Cargo.lock index b318a327..fb229838 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,16 +13,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is_terminal_polyfill", "utf8parse", ] @@ -34,27 +33,27 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -92,9 +91,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.6.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bpf-linker" @@ -111,6 +116,7 @@ dependencies = [ "log", "regex", "rustc-build-sysroot", + "target-lexicon", "thiserror", "tracing", "tracing-appender", @@ -121,18 +127,18 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" dependencies = [ "serde", ] @@ -153,9 +159,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "shlex", ] @@ -202,20 +208,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "compiletest_rs" -version = "0.11.0" -source = "git+https://github.com/Manishearth/compiletest-rs.git#98a3e2da7b32003aea2394b3aebfb42350500957" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fcc3c0c91b59c137b3cf8073cbc2f72a49b3d5505660ec88f94da3ed4bb1de" dependencies = [ "diff", "filetime", @@ -235,18 +242,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "deranged" @@ -286,9 +293,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "equivalent" @@ -298,9 +305,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", @@ -314,20 +321,20 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "libredox", - "windows-sys 0.59.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] @@ -341,9 +348,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -363,9 +370,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -390,31 +397,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" @@ -424,30 +425,30 @@ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] name = "libredox" -version = "0.1.3" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags", + "bitflags 2.5.0", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "llvm-sys" @@ -480,17 +481,17 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miow" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -505,11 +506,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -530,9 +531,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "overload" @@ -542,9 +546,15 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "portable-atomic" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -554,9 +564,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", "syn", @@ -564,36 +574,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", @@ -634,9 +644,9 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" +checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" [[package]] name = "regex-syntax" @@ -664,9 +674,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] @@ -685,11 +695,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -698,15 +708,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -719,9 +729,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -748,9 +758,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.129" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -787,32 +797,37 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strsim" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" -version = "3.13.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -979,7 +994,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c" dependencies = [ - "nu-ansi-term 0.50.1", + "nu-ansi-term 0.50.0", "tracing-core", "tracing-log", "tracing-subscriber", @@ -987,21 +1002,30 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "utf8parse" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] [[package]] name = "valuable" @@ -1056,11 +1080,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "windows-sys 0.59.0", + "winapi", ] [[package]] @@ -1071,17 +1095,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.5", ] [[package]] @@ -1090,7 +1108,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1099,7 +1117,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1120,9 +1153,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -1132,9 +1165,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -1144,9 +1177,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -1162,9 +1195,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -1174,9 +1207,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -1186,9 +1219,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -1198,9 +1231,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -1213,3 +1246,20 @@ name = "winsafe" version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "target-lexicon", + "thiserror", + "uuid", + "which", +] + +[[patch.unused]] +name = "compiletest_rs" +version = "0.10.2" +source = "git+https://github.com/Manishearth/compiletest-rs.git#072c4ce3d211932d6a8a9417adcfe1dfeeb621a7" diff --git a/Cargo.toml b/Cargo.toml index 85a0a38f..0bc98eec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,9 @@ log = { version = "0.4.22" } thiserror = { version = "1.0.64" } tracing = "0.1" +[build-dependencies] +target-lexicon = { workspace = true } + [dev-dependencies] compiletest_rs = { version = "0.11.0" } regex = { version = "1.11.0", default-features = false } @@ -54,3 +57,22 @@ debug = true [patch.crates-io] compiletest_rs = { git = "https://github.com/Manishearth/compiletest-rs.git" } + +[workspace] +members = [ + "xtask", +] + +[workspace.package] +version = "0.9.13" +license = "MIT OR Apache-2.0" +repository = "https://github.com/aya-rs/bpf-linker" +edition = "2021" + +[workspace.dependencies] +anyhow = "1.0.89" +clap = { version = "4.5", features = ["derive"] } +target-lexicon = "0.12" +thiserror = { version = "1.0.64" } +uuid = { version = "1.10", features = ["v4"] } +which = "6.0" diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..fe7cbcdf --- /dev/null +++ b/build.rs @@ -0,0 +1,106 @@ +//! Build script which optionally links libLLVM. + +use std::{env, fs::read_dir, path::Path, str::FromStr}; + +use target_lexicon::{Architecture, Environment, OperatingSystem, Triple}; + +fn main() { + // Execute the build script only when custom LLVM path is provided. + // It's not recommended to trigger that option manually. Using `xtask` + // should be preferred. + let llvm_prefix = match env::var("BPF_LINKER_LLVM_PREFIX") { + Ok(llvm_prefix) => llvm_prefix, + Err(_) => return, + }; + println!("cargo::rustc-link-search={llvm_prefix}/lib"); + + let target = env::var("TARGET").unwrap(); + let target = Triple::from_str(&target).unwrap(); + + if target == target_lexicon::HOST { + // For native builds, use the standard directories. + println!("cargo::rustc-link-search=/lib"); + println!("cargo::rustc-link-search=/usr/lib"); + println!("cargo::rustc-link-search=/usr/local/lib"); + } else { + // For cross builds on platforms supported by cross-llvm, use the + // expected sysroot directories. Otherwise, fall back to the standard + // ones. + match ( + target.architecture, + target.operating_system, + target.environment, + ) { + (Architecture::Aarch64(_), OperatingSystem::Linux, Environment::Gnu) => { + println!("cargo::rustc-link-search=/usr/aarch64-linux-gnu/lib"); + println!("cargo::rustc-link-search=/usr/lib/aarch64-linux-gnu"); + } + (Architecture::Aarch64(_), OperatingSystem::Linux, Environment::Musl) => { + println!("cargo::rustc-link-arg=--sysroot=/usr/aarch64-unknown-linux-musl"); + println!("cargo::rustc-link-search=/usr/aarch64-unknown-linux-musl/lib"); + println!("cargo::rustc-link-search=/usr/aarch64-unknown-linux-musl/usr/lib"); + } + (Architecture::Riscv64(_), OperatingSystem::Linux, Environment::Gnu) => { + println!("cargo::rustc-link-search=/usr/riscv64-linux-gnu/lib"); + println!("cargo::rustc-link-search=/usr/lib/riscv64-linux-gnu"); + } + (Architecture::Riscv64(_), OperatingSystem::Linux, Environment::Musl) => { + println!("cargo::rustc-link-arg=--sysroot=/usr/aarch64-unknown-linux-musl"); + println!("cargo::rustc-link-search=/usr/riscv64-unknown-linux-musl/lib"); + println!("cargo::rustc-link-search=/usr/riscv64-unknown-linux-musl/usr/lib"); + } + (Architecture::X86_64, OperatingSystem::Linux, Environment::Gnu) => { + println!("cargo::rustc-link-search=/usr/x86_64-linux-gnu/lib"); + println!("cargo::rustc-link-search=/usr/lib/x86_64-linux-gnu"); + } + (Architecture::X86_64, OperatingSystem::Linux, Environment::Musl) => { + println!("cargo::rustc-link-arg=--sysroot=/usr/x86_64-unknown-linux-musl"); + println!("cargo::rustc-link-search=/usr/x86_64-unknown-linux-musl/lib"); + println!("cargo::rustc-link-search=/usr/x86_64-unknown-linux-musl/usr/lib"); + } + (_, _, _) => { + println!("cargo::rustc-link-search=/lib"); + println!("cargo::rustc-link-search=/usr/lib"); + println!("cargo::rustc-link-search=/usr/local/lib"); + } + } + } + + let link_type = if target.environment == Environment::Gnu { + // On GNU/Linux: + // - Use libstdc++. + // - Link system libraries dynamically. The reason being - Debian + // doesn't ship static zlib and zstd. + println!("cargo::rustc-link-lib=dylib=stdc++"); + "dylib" + } else { + // LLVM libc++ and static linking works fine on other systems (BSDs, + // macOS, musl/Linux). + println!("cargo::rustc-link-lib=static=c++_static"); + println!("cargo::rustc-link-lib=static=c++abi"); + "static" + }; + + println!("cargo::rustc-link-lib={link_type}=rt"); + println!("cargo::rustc-link-lib={link_type}=dl"); + println!("cargo::rustc-link-lib={link_type}=m"); + println!("cargo::rustc-link-lib={link_type}=z"); + println!("cargo::rustc-link-lib={link_type}=zstd"); + + // Link libLLVM using the artifacts from the provided prefix. + for entry in + read_dir(Path::new(&llvm_prefix).join("lib")).expect("LLVM build directory not found") + { + let entry = entry.expect("failed to retrieve the file in the LLVM build directory"); + let path = entry.path(); + if path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("a") { + let libname = path.file_name().unwrap().to_string_lossy(); + let libname = libname + .strip_prefix("lib") + .unwrap() + .strip_suffix(".a") + .unwrap(); + println!("cargo::rustc-link-lib={link_type}={libname}") + } + } +} diff --git a/tests/tests.rs b/tests/tests.rs index da54d021..5e9604ba 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,7 +1,9 @@ use std::{ env, ffi::{OsStr, OsString}, - fs, + fs::{self, remove_file, File}, + io::Write, + os::unix::fs::PermissionsExt, path::{Path, PathBuf}, process::Command, }; @@ -20,7 +22,45 @@ fn run_mode( sysroot: Option<&Path>, cfg: Option, ) { - let mut target_rustcflags = format!("-C linker={}", env!("CARGO_BIN_EXE_bpf-linker")); + let bpf_linker_exe = env!("CARGO_BIN_EXE_bpf-linker"); + let bpf_linker = match env::var_os("BPF_LINKER_QEMU") { + // If we are running tests in a user-space emulator, we need to run + // bpf-linker in it as well. + Some(qemu) => { + // Create a wrapper script which runs bpf-linker with qemu. + // + // Unfortunately, passing + // `-C linker='qemu-aarch64 ./target/aarch64-uknown-linux-musl/debug/bpf-linker'` + // doesn't work, `compiletest_rs::Config` is going to split this + // argument because of a whitespace. + let script_path = Path::new("/tmp/qemu_bpf_linker_wrapper.sh"); + if script_path.exists() { + remove_file(script_path).expect("Could not remove the QEMU wrapper file"); + } + // It's an environment variable set by us. Safe to assume it's UTF-8. + let qemu = qemu.to_string_lossy(); + let script_content = format!( + r#"#!/bin/bash +{qemu} "{bpf_linker_exe}" "$@" +"# + ); + let mut file = + File::create(script_path).expect("Failed to create the QEMU wrapper file"); + file.write_all(script_content.as_bytes()) + .expect("Failed to write to the QEMU wrapper file"); + let metadata = file + .metadata() + .expect("Failed to retrieve the metadata of the QEMU wrapper file"); + let mut permissions = metadata.permissions(); + permissions.set_mode(0o755); + file.set_permissions(permissions) + .expect("Failed to set permissions of the QEMU wrapper file"); + + script_path.to_str().unwrap() + } + None => env!("CARGO_BIN_EXE_bpf-linker"), + }; + let mut target_rustcflags = format!("-C linker={}", bpf_linker); if let Some(sysroot) = sysroot { let sysroot = sysroot.to_str().unwrap(); target_rustcflags += &format!(" --sysroot {sysroot}"); @@ -136,6 +176,8 @@ fn compile_test() { Some(&directory), None::, ); + + #[cfg(not(target_os = "macos"))] run_mode( target, "assembly", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..a4417a94 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +clap = { workspace = true } +target-lexicon = { workspace = true } +thiserror = { workspace = true } +uuid = { workspace = true } +which = { workspace = true } diff --git a/xtask/src/build.rs b/xtask/src/build.rs new file mode 100644 index 00000000..46aacf4f --- /dev/null +++ b/xtask/src/build.rs @@ -0,0 +1,7 @@ +use std::ffi::OsString; + +use crate::cargo::{run_cargo, CargoArgs}; + +pub fn build(args: CargoArgs) -> anyhow::Result<()> { + run_cargo(args, OsString::from("build"), &[]) +} diff --git a/xtask/src/build_llvm.rs b/xtask/src/build_llvm.rs new file mode 100644 index 00000000..aebe13ef --- /dev/null +++ b/xtask/src/build_llvm.rs @@ -0,0 +1,275 @@ +use std::{ + env, + ffi::{OsStr, OsString}, + fs::{self, create_dir_all, remove_dir_all}, + path::Path, + process::{Command, Stdio}, +}; + +use clap::Parser; +use target_lexicon::Triple; +use thiserror::Error; + +use crate::{ + containers::{Container, ContainerEngine, PullPolicy}, + target::{SupportedTriple, TripleExt}, + tempdir::TempDir, +}; + +#[derive(Debug, Error)] +pub enum LlvmBuildError { + #[error("target {0} is not supported")] + TargetNotSupported(String), + #[error("cmake build failed")] + CmakeBuild, +} + +#[derive(Parser)] +pub struct BuildLlvmArgs { + /// Container engine (if not provided, is going to be autodetected). + #[arg(long)] + container_engine: Option, + + /// Container image repository. + #[arg(long, default_value = "ghcr.io/exein-io/cross-llvm")] + container_repository: String, + + /// Tag of the container image. + #[arg(long, default_value = "latest")] + container_tag: String, + + /// Prefix in which LLVM libraries are going to be installed after build. + #[arg(long)] + llvm_install_dir: Option, + + /// Path to the LLVM repository directory. If not provided, it will be + /// cloned automatically in a temporary location. + #[arg(long)] + llvm_repository_dir: Option, + + /// URL to the LLVM repository. Irrelevant if `--llvm-repository-dir` is + /// specified. + #[arg(long, default_value = "https://github.com/aya-rs/llvm-project")] + llvm_repository_url: String, + + /// Branch of the LLVM repository. Irrelevant if `--llvm-repository-dir` is + /// specified. + #[arg(long, default_value = "rustc/19.1-2024-09-17")] + llvm_repository_branch: String, + + /// Preserve the build directory. + #[arg(long)] + preserve_build_dir: bool, + + /// Pull image policy. + #[arg(long, default_value_t = PullPolicy::default())] + pull: PullPolicy, + + /// Target triple (optional). + #[arg(short, long)] + target: Option, +} + +fn clone_repo( + llvm_repository_url: &String, + llvm_repository_branch: &str, + destination: &OsStr, +) -> anyhow::Result<()> { + // NOTE(vadorovsky): Sadly, git2 crate doesn't support specyfing depth when + // cloning. + Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("--branch") + .arg(llvm_repository_branch) + .arg(llvm_repository_url) + .arg(destination) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status()?; + + Ok(()) +} + +pub fn build_llvm(args: BuildLlvmArgs) -> anyhow::Result<()> { + let BuildLlvmArgs { + container_engine, + container_repository, + container_tag, + llvm_install_dir, + llvm_repository_dir, + llvm_repository_url, + llvm_repository_branch, + preserve_build_dir, + pull, + target, + } = args; + + let build_tempdir = TempDir::new("aya-llvm-build", preserve_build_dir)?; + + let workdir = match llvm_repository_dir { + Some(llvm_repository_dir) => llvm_repository_dir, + None => { + let destination = build_tempdir.to_os_string(); + clone_repo(&llvm_repository_url, &llvm_repository_branch, &destination)?; + destination + } + }; + println!("Building LLVM in directory {}", workdir.to_string_lossy()); + + let triple: Triple = match target { + Some(target) => target.into(), + None => target_lexicon::HOST, + }; + + let llvm_install_dir = match llvm_install_dir { + Some(llvm_install_dir) => llvm_install_dir, + None => Path::new("/tmp") + .join(format!("aya-llvm-{triple}")) + .into_os_string(), + }; + if Path::new(&llvm_install_dir).exists() { + remove_dir_all(&llvm_install_dir)?; + } + create_dir_all(&llvm_install_dir)?; + + let llvm_build_config = triple + .llvm_build_config(&llvm_install_dir) + .ok_or(LlvmBuildError::TargetNotSupported(triple.to_string()))?; + + let mut cmake_args = llvm_build_config.cmake_args(); + + let build_dir = format!("aya-build-{}", llvm_build_config.target_triple); + let build_dir_path = Path::new(&workdir).join(&build_dir); + if build_dir_path.exists() { + fs::remove_dir_all(Path::new(&workdir).join(&build_dir))?; + } + + match triple.container_image(&container_repository, &container_tag) { + Some((container_image, _)) => { + cmake_args.insert(0, OsString::from("cmake")); + let container = Container { + args: cmake_args, + container_engine: container_engine.clone(), + container_image: container_image.clone(), + interactive: false, + llvm_install_dir: Some(llvm_install_dir.clone()), + pull: pull.clone(), + triple: triple.clone(), + workdir: workdir.clone(), + }; + container.run()?; + + let container = Container { + args: vec![ + OsString::from("cmake"), + OsString::from("--build"), + OsString::from(build_dir), + OsString::from("--target"), + OsString::from("install"), + ], + container_engine, + container_image, + interactive: false, + llvm_install_dir: Some(llvm_install_dir.clone()), + pull, + triple, + workdir, + }; + container.run()?; + + // println!("Using container image {container_image}"); + + // let container_engine = container_engine.unwrap_or(ContainerEngine::autodetect()?); + + // let mut workdir_arg = llvm_repository_dir; + // workdir_arg.push(":/usr/local/src/llvm:z"); + + // let mut llvm_install_arg = llvm_install_dir.clone(); + // llvm_install_arg.push(":"); + // llvm_install_arg.push(&llvm_install_dir); + + // let mut cmd = Command::new(container_engine.to_string()); + // cmd.args([ + // OsStr::new("run"), + // OsStr::new("--rm"), + // OsStr::new("-t"), + // OsStr::new("--pull"), + // OsStr::new(&pull.to_string()), + // OsStr::new("-w"), + // OsStr::new("/usr/local/src/llvm"), + // OsStr::new("-v"), + // &workdir_arg, + // OsStr::new("-v"), + // &llvm_install_arg, + // OsStr::new(&container_image), + // OsStr::new("cmake"), + // ]) + // .args(cmake_args) + // .stdout(Stdio::inherit()) + // .stderr(Stdio::inherit()); + // println!("{cmd:?}"); + // if !cmd.status()?.success() { + // return Err(LlvmBuildError::CmakeBuild.into()); + // } + + // let mut cmd = Command::new(container_engine.to_string()); + // cmd.args([ + // OsStr::new("run"), + // OsStr::new("--rm"), + // OsStr::new("-e"), + // OsStr::new("-t"), + // OsStr::new("-w"), + // OsStr::new("/usr/local/src/llvm"), + // OsStr::new("-v"), + // &workdir_arg, + // OsStr::new("-v"), + // &llvm_install_arg, + // OsStr::new(&container_image), + // OsStr::new("cmake"), + // OsStr::new("--build"), + // OsStr::new(&build_dir), + // OsStr::new("-j"), + // OsStr::new("--target"), + // OsStr::new("install"), + // ]) + // .stdout(Stdio::inherit()) + // .stderr(Stdio::inherit()); + // println!("{cmd:?}"); + // if !cmd.status()?.success() { + // return Err(LlvmBuildError::CmakeBuild.into()); + // } + } + None => { + println!("Building on host"); + + env::set_current_dir(workdir)?; + + let mut cmd = Command::new("cmake"); + cmd.args(cmake_args) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + println!("{cmd:?}"); + if !cmd.status()?.success() { + return Err(LlvmBuildError::CmakeBuild.into()); + } + + let mut cmd = Command::new("cmake"); + cmd.args(["--build", &build_dir, "-j", "--target", "install"]) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + println!("{cmd:?}"); + if !cmd.status()?.success() { + return Err(LlvmBuildError::CmakeBuild.into()); + } + } + } + + println!( + "Installed LLVM artifacts in {}", + llvm_install_dir.to_string_lossy() + ); + + Ok(()) +} diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs new file mode 100644 index 00000000..3c525af2 --- /dev/null +++ b/xtask/src/cargo.rs @@ -0,0 +1,157 @@ +use std::{ffi::OsString, process::Command}; + +use clap::{ArgAction, Parser}; +use target_lexicon::Triple; + +use crate::{ + containers::{Container, ContainerEngine, PullPolicy}, + git::top_directory, + target::{SupportedTriple, TripleExt}, +}; + +#[derive(Parser)] +pub struct CargoArgs { + /// Container engine (if not provided, is going to be autodetected). + #[arg(long)] + container_engine: Option, + + /// Container image repository. + #[arg(long, default_value = "ghcr.io/exein-io/cross-llvm")] + container_repository: String, + + /// Tag of the container image. + #[arg(long, default_value = "latest")] + container_tag: String, + + /// Space or comma separated list of features to activate. + #[arg(short, long)] + features: Vec, + + /// Activate all available features. + #[arg(long)] + all_features: bool, + + /// Prefix in which LLVM libraries are going to be installed after build. + #[arg(long)] + llvm_install_dir: Option, + + /// Pull image policy. + #[arg(long, default_value_t = PullPolicy::default())] + pull: PullPolicy, + + /// Build artifacts in release mode, with optimizations. + #[arg(long)] + release: bool, + + /// Target triple (optional). + #[arg(short, long)] + target: Option, + + /// Use verbose output (-vv very verbose/build.rs output). + #[arg(short, long, action = ArgAction::Count)] + verbose: u8, +} + +pub fn run_cargo( + args: CargoArgs, + command: OsString, + extra_args: &[OsString], +) -> anyhow::Result<()> { + let CargoArgs { + container_engine, + container_repository, + container_tag, + mut features, + all_features, + llvm_install_dir, + pull, + release, + target, + verbose, + } = args; + + // Disable the LLVM linking capabilities from llvm-sys, they don't support + // cross-compilation. Instead, we are building our own linking flags, based + // on the specified `llvm_install_dir`. + features.push(OsString::from("llvm-sys/no-llvm-linking")); + + let triple: Triple = match target { + Some(target) => target.into(), + None => target_lexicon::HOST, + }; + + let workdir = top_directory().unwrap(); + + let mut rustflags = OsString::from("RUSTFLAGS=-C linker=clang -C link-arg=-fuse-ld=lld"); + if triple.is_cross() { + rustflags.push(format!(" -C link-arg=--target={}", triple.clang_triple())); + } + + let mut cargo_args = vec![ + command.to_owned(), + OsString::from("--target"), + OsString::from(triple.to_string()), + OsString::from("--no-default-features"), + ]; + if triple.is_cross() { + cargo_args.extend([ + OsString::from("--config"), + OsString::from("target.aarch64-unknown-linux-gnu.runner = 'qemu-aarch64 -L /usr/aarch64-linux-gnu/lib -L /usr/lib/aarch64-linux-gnu'"), + OsString::from("--config"), + OsString::from("target.aarch64-unknown-linux-musl.runner = 'qemu-aarch64'"), + OsString::from("--config"), + OsString::from("target.riscv64gc-unknown-linux-gnu.runner = 'qemu-riscv64 -L /usr/riscv64-linux-gnu/lib -L /usr/lib/riscv64-linux-gnu'"), + OsString::from("--config"), + OsString::from("target.riscv64gc-unknown-linux-musl.runner = 'qemu-riscv64'"), + OsString::from("--config"), + OsString::from("target.x86_64-unknown-linux-gnu.runner = 'qemu-x86_64 -L /usr/x86_64-linux-gnu/lib -L /usr/lib/x86_64-linux-gnu'"), + OsString::from("--config"), + OsString::from("target.x86_64-unknown-linux-musl.runner = 'qemu-x86_64'"), + ]); + } + + cargo_args.extend_from_slice(extra_args); + + match verbose { + 0 => {} + 1 => { + cargo_args.push(OsString::from("-v")); + } + _ => { + cargo_args.push(OsString::from("-vv")); + } + } + if release { + cargo_args.push(OsString::from("--release")); + } + if !features.is_empty() { + cargo_args.push(OsString::from("--features")); + cargo_args.extend(features); + } + if all_features { + cargo_args.push(OsString::from("--all-features")); + } + + match triple.container_image(&container_repository, &container_tag) { + Some((container_image, _)) => { + cargo_args.insert(0, OsString::from("cargo")); + let container = Container { + args: cargo_args, + container_engine, + container_image, + interactive: false, + llvm_install_dir, + pull, + triple, + workdir, + }; + container.run()?; + } + None => { + let mut cmd = Command::new("cargo"); + cmd.args(cargo_args); + } + } + + Ok(()) +} diff --git a/xtask/src/check.rs b/xtask/src/check.rs new file mode 100644 index 00000000..4fb10538 --- /dev/null +++ b/xtask/src/check.rs @@ -0,0 +1,7 @@ +use std::ffi::OsString; + +use crate::cargo::{run_cargo, CargoArgs}; + +pub fn check(args: CargoArgs) -> anyhow::Result<()> { + run_cargo(args, OsString::from("check"), &[]) +} diff --git a/xtask/src/containers.rs b/xtask/src/containers.rs new file mode 100644 index 00000000..8f10ebc5 --- /dev/null +++ b/xtask/src/containers.rs @@ -0,0 +1,177 @@ +use std::{ + env, + ffi::{OsStr, OsString}, + fmt::Display, + path::Path, + process::{Command, Stdio}, +}; + +use clap::ValueEnum; +use target_lexicon::Triple; +use thiserror::Error; +use which::which; + +use crate::target::TripleExt; + +#[derive(Debug, Error)] +pub enum ContainerError { + #[error("no supported container engine (docker, podman) was found")] + ContainerEngineNotFound, + #[error("failed to execute the container")] + Run, +} + +#[derive(Clone, ValueEnum)] +pub enum ContainerEngine { + Docker, + Podman, +} + +impl Display for ContainerEngine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Docker => write!(f, "docker"), + Self::Podman => write!(f, "podman"), + } + } +} + +impl ContainerEngine { + pub fn autodetect() -> Result { + if which("docker").is_ok() { + Ok(Self::Docker) + } else if which("podman").is_ok() { + Ok(Self::Podman) + } else { + Err(ContainerError::ContainerEngineNotFound) + } + } +} + +#[derive(Clone, ValueEnum)] +pub enum PullPolicy { + Always, + Missing, + Never, + Newer, +} + +impl Default for PullPolicy { + fn default() -> Self { + Self::Missing + } +} + +impl Display for PullPolicy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Always => write!(f, "always"), + Self::Missing => write!(f, "missing"), + Self::Never => write!(f, "never"), + Self::Newer => write!(f, "newer"), + } + } +} + +pub struct Container { + pub args: Vec, + pub container_engine: Option, + pub container_image: String, + pub interactive: bool, + pub llvm_install_dir: Option, + pub pull: PullPolicy, + pub triple: Triple, + pub workdir: OsString, +} + +impl Container { + pub fn run(&self) -> anyhow::Result<()> { + let Self { + args, + container_engine, + container_image, + interactive, + llvm_install_dir, + pull, + triple, + workdir, + } = self; + + println!("Using container image {container_image}"); + + let container_engine = container_engine + .clone() + .unwrap_or(ContainerEngine::autodetect()?); + + let llvm_install_dir = match llvm_install_dir { + Some(llvm_install_dir) => llvm_install_dir, + None => &Path::new("/tmp") + .join(format!("aya-llvm-{triple}")) + .into_os_string(), + }; + + let mut llvm_prefix = OsString::from("BPF_LINKER_LLVM_PREFIX="); + llvm_prefix.push(llvm_install_dir); + + let rustup_toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); + let rustup_toolchain = rustup_toolchain.split('-').next().unwrap(); + let mut rustup_toolchain_triple = target_lexicon::HOST; + rustup_toolchain_triple.environment = triple.environment; + let rustup_toolchain = format!("{rustup_toolchain}-{}", rustup_toolchain_triple); + let mut rustup_toolchain_arg = OsString::from("RUSTUP_TOOLCHAIN="); + rustup_toolchain_arg.push(rustup_toolchain); + + let cargo_dir = Path::new(&env::var_os("HOME").unwrap()).join(".cargo"); + let mut cargo_dir_arg = cargo_dir.into_os_string(); + cargo_dir_arg.push(":/root/host-cargo:z"); + + let mut workdir_arg = workdir.clone(); + workdir_arg.push(":/home/cross/src:z"); + + let mut llvm_install_arg = llvm_install_dir.clone(); + llvm_install_arg.push(":"); + llvm_install_arg.push(llvm_install_dir); + + let mut cmd = Command::new(container_engine.to_string()); + cmd.args([ + OsStr::new("run"), + OsStr::new("--rm"), + OsStr::new("-e"), + &llvm_prefix, + OsStr::new("-e"), + &triple.rustflags(), + OsStr::new("-e"), + &rustup_toolchain_arg, + ]); + if triple.is_cross() { + let mut qemu = OsString::from("BPF_LINKER_QEMU="); + qemu.push(triple.qemu()); + cmd.args([OsStr::new("-e"), &qemu]); + } + if *interactive { + cmd.arg("-i"); + } + cmd.args([ + OsStr::new("-t"), + OsStr::new("--pull"), + OsStr::new(&pull.to_string()), + OsStr::new("-w"), + OsStr::new("/home/cross/src"), + OsStr::new("-v"), + &cargo_dir_arg, + OsStr::new("-v"), + &workdir_arg, + OsStr::new("-v"), + &llvm_install_arg, + OsStr::new(&container_image), + ]); + cmd.args(args); + cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit()); + println!("{cmd:?}"); + if !cmd.status()?.success() { + return Err(ContainerError::Run.into()); + } + + Ok(()) + } +} diff --git a/xtask/src/git.rs b/xtask/src/git.rs new file mode 100644 index 00000000..e975f608 --- /dev/null +++ b/xtask/src/git.rs @@ -0,0 +1,25 @@ +use std::{ffi::OsString, os::unix::ffi::OsStringExt, process::Command}; + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum GitError { + #[error("could not find a git repository")] + RepositoryNotFound, +} + +pub fn top_directory() -> Result { + let workdir = Command::new("git") + .args(["rev-parse", "--show-toplevel"]) + .output(); + match workdir { + Ok(output) if output.status.success() => { + Ok(OsString::from_vec( + // Remove the trailing `\n` character. + output.stdout[..output.stdout.len() - 1].to_vec(), + )) + } + Ok(_) => Err(GitError::RepositoryNotFound), + Err(_) => Err(GitError::RepositoryNotFound), + } +} diff --git a/xtask/src/install.rs b/xtask/src/install.rs new file mode 100644 index 00000000..1da4fb99 --- /dev/null +++ b/xtask/src/install.rs @@ -0,0 +1,16 @@ +use std::ffi::OsString; + +use crate::cargo::{run_cargo, CargoArgs}; + +pub fn install(args: CargoArgs) -> anyhow::Result<()> { + run_cargo( + args, + OsString::from("install"), + &[ + OsString::from("--path"), + OsString::from("."), + OsString::from("--root"), + OsString::from("/root/host-cargo"), + ], + ) +} diff --git a/xtask/src/llvm.rs b/xtask/src/llvm.rs new file mode 100644 index 00000000..f37f60fe --- /dev/null +++ b/xtask/src/llvm.rs @@ -0,0 +1,131 @@ +use std::{ffi::OsString, fmt::Display, path::Path}; + +pub enum System { + Darwin, + Linux, +} + +impl Display for System { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Darwin => write!(f, "Darwin"), + Self::Linux => write!(f, "Linux"), + } + } +} + +pub enum Processor { + Aarch64, + Riscv64, + X86_64, +} + +impl Display for Processor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Aarch64 => write!(f, "aarch64"), + Self::Riscv64 => write!(f, "riscv64"), + Self::X86_64 => write!(f, "x86_64"), + } + } +} + +pub struct LlvmBuildConfig { + pub c_compiler: String, + pub cxx_compiler: String, + pub compiler_target: Option, + pub cxxflags: Option, + pub ldflags: Option, + pub install_prefix: OsString, + pub skip_install_rpath: bool, + pub system: System, + pub processor: Processor, + pub static_build: bool, + pub target_triple: String, +} + +impl LlvmBuildConfig { + pub fn cmake_args(&self) -> Vec { + let LlvmBuildConfig { + c_compiler, + cxx_compiler, + compiler_target, + cxxflags, + ldflags, + install_prefix, + skip_install_rpath, + system, + processor, + static_build, + target_triple, + } = self; + + // NOTE(vadorovsky): I wish there was a `format!` equivalent for + // `OsString`... + let mut install_arg = OsString::from("-DCMAKE_INSTALL_PREFIX="); + install_arg.push(install_prefix); + let mut rpath_arg = OsString::from("-DCMAKE_INSTALL_RPATH="); + rpath_arg.push(Path::new(install_prefix).join("lib")); + + let mut args = vec![ + OsString::from("-S"), + OsString::from("llvm"), + OsString::from("-B"), + OsString::from(format!("aya-build-{}", target_triple)), + OsString::from("-G"), + OsString::from("Ninja"), + OsString::from("-DCMAKE_BUILD_TYPE=RelWithDebInfo"), + OsString::from(format!("-DCMAKE_ASM_COMPILER={c_compiler}")), + OsString::from("-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON"), + OsString::from(format!("-DCMAKE_C_COMPILER={c_compiler}")), + OsString::from(format!("-DCMAKE_CXX_COMPILER={cxx_compiler}")), + install_arg, + rpath_arg, + OsString::from(format!("-DCMAKE_SYSTEM_NAME={}", system)), + OsString::from(format!("-DCMAKE_SYSTEM_PROCESSOR={}", processor)), + OsString::from("-DLLVM_BUILD_EXAMPLES=OFF"), + OsString::from("-DLLVM_ENABLE_ASSERTIONS=ON"), + OsString::from("-DLLVM_ENABLE_LIBCXX=ON"), + OsString::from("-DLLVM_ENABLE_LIBXML2=OFF"), + OsString::from("-DLLVM_ENABLE_PROJECTS="), + OsString::from("-DLLVM_ENABLE_RUNTIMES="), + OsString::from(format!("-DLLVM_HOST_TRIPLE={target_triple}")), + OsString::from("-DLLVM_INCLUDE_TESTS=OFF"), + OsString::from("-DLLVM_INCLUDE_TOOLS=OFF"), + OsString::from("-DLLVM_INCLUDE_UTILS=OFF"), + OsString::from("-DLLVM_TARGETS_TO_BUILD=BPF"), + OsString::from("-DLLVM_USE_LINKER=lld"), + ]; + + if let Some(compiler_target) = compiler_target { + args.push(OsString::from(format!( + "-DCMAKE_ASM_COMPILER_TARGET={compiler_target}" + ))); + args.push(OsString::from(format!( + "-DCMAKE_C_COMPILER_TARGET={compiler_target}" + ))); + args.push(OsString::from(format!( + "-DCMAKE_CXX_COMPILER_TARGET={compiler_target}" + ))); + } + if let Some(cxxflags) = cxxflags { + args.push(OsString::from(format!("-DCMAKE_CXX_FLAGS='{cxxflags}'"))); + } + if let Some(ldflags) = ldflags { + args.push(OsString::from(format!( + "-DCMAKE_EXE_LINKER_FLAGS='{ldflags}'" + ))); + args.push(OsString::from(format!( + "-DCMAKE_SHARED_LINKER_FLAGS='{ldflags}" + ))); + } + if *skip_install_rpath { + args.push(OsString::from("-DCMAKE_SKIP_INSTALL_RPATH=ON".to_owned())); + } + if *static_build { + args.push(OsString::from("-DLLVM_BUILD_STATIC=ON")); + } + + args + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..5d9efdbe --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,66 @@ +use clap::{Parser, Subcommand}; +use install::install; +use shell::{run_shell, ShellArgs}; + +mod build; +mod build_llvm; +mod cargo; +mod check; +mod containers; +mod git; +mod install; +mod llvm; +mod shell; +mod target; +mod tempdir; +mod test; + +use crate::{ + build::build, + build_llvm::{build_llvm, BuildLlvmArgs}, + cargo::CargoArgs, + check::check, + test::test, +}; + +/// The `xtask` CLI. +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Subcommands + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Build bpf-linker. + Build(CargoArgs), + /// Build LLVM. + BuildLlvm(BuildLlvmArgs), + /// Check the bpf-linker package. + Check(CargoArgs), + /// Install bpf-linker. + Install(CargoArgs), + /// Run a shell inside a cross environment. + Shell(ShellArgs), + /// Test bpf-linker. + Test(CargoArgs), +} + +fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Build(args) => build(args)?, + Commands::BuildLlvm(args) => { + build_llvm(args)?; + } + Commands::Check(args) => check(args)?, + Commands::Install(args) => install(args)?, + Commands::Shell(args) => run_shell(args)?, + Commands::Test(args) => test(args)?, + } + + Ok(()) +} diff --git a/xtask/src/shell.rs b/xtask/src/shell.rs new file mode 100644 index 00000000..ea100b87 --- /dev/null +++ b/xtask/src/shell.rs @@ -0,0 +1,76 @@ +use std::ffi::OsString; + +use clap::Parser; +use target_lexicon::Triple; + +use crate::{ + containers::{Container, ContainerEngine, PullPolicy}, + git::top_directory, + target::{SupportedTriple, TripleExt}, +}; + +#[derive(Parser)] +pub struct ShellArgs { + /// Container engine (if not provided, is going to be autodetected). + #[arg(long)] + container_engine: Option, + + /// Container image repository. + #[arg(long, default_value = "ghcr.io/exein-io/cross-llvm")] + container_repository: String, + + /// Tag of the container image. + #[arg(long, default_value = "latest")] + container_tag: String, + + /// Prefix in which LLVM libraries are going to be installed after build. + #[arg(long)] + llvm_install_dir: Option, + + /// Pull image policy. + #[arg(long, default_value_t = PullPolicy::default())] + pull: PullPolicy, + + /// Target triple (optional). + #[arg(short, long)] + target: Option, +} + +pub fn run_shell(args: ShellArgs) -> anyhow::Result<()> { + let ShellArgs { + container_engine, + container_repository, + container_tag, + llvm_install_dir, + pull, + target, + } = args; + + let triple: Triple = match target { + Some(target) => target.into(), + None => target_lexicon::HOST, + }; + + let workdir = top_directory().unwrap(); + + match triple.container_image(&container_repository, &container_tag) { + Some((container_image, _)) => { + let container = Container { + args: vec![OsString::from("bash")], + container_engine, + container_image, + interactive: true, + llvm_install_dir, + pull, + triple, + workdir, + }; + container.run()?; + } + None => { + println!("Shell is supported only for containerized targets"); + } + } + + Ok(()) +} diff --git a/xtask/src/target.rs b/xtask/src/target.rs new file mode 100644 index 00000000..eccdec8d --- /dev/null +++ b/xtask/src/target.rs @@ -0,0 +1,348 @@ +use std::ffi::{OsStr, OsString}; + +use clap::ValueEnum; +use target_lexicon::{ + Aarch64Architecture, Architecture, BinaryFormat, Environment, OperatingSystem, + Riscv64Architecture, Triple, Vendor, +}; + +use crate::llvm::{LlvmBuildConfig, Processor, System}; + +#[derive(Clone)] +pub enum SupportedTriple { + Aarch64AppleDarwin, + Aarch64UnknownLinuxGnu, + Aarch64UnknownLinuxMusl, + Riscv64GcUnknownLinuxGnu, + X86_64AppleDarwin, + X86_64UnknownLinuxGnu, + X86_64UnknownLinuxMusl, +} + +impl ValueEnum for SupportedTriple { + fn value_variants<'a>() -> &'a [Self] { + &[ + Self::Aarch64AppleDarwin, + Self::Aarch64UnknownLinuxGnu, + Self::Aarch64UnknownLinuxMusl, + Self::Riscv64GcUnknownLinuxGnu, + Self::X86_64AppleDarwin, + Self::X86_64UnknownLinuxGnu, + Self::X86_64UnknownLinuxMusl, + ] + } + + fn to_possible_value(&self) -> Option { + Some(match self { + Self::Aarch64AppleDarwin => clap::builder::PossibleValue::new("aarch64-apple-darwin"), + Self::Aarch64UnknownLinuxGnu => { + clap::builder::PossibleValue::new("aarch64-unknown-linux-gnu") + } + Self::Aarch64UnknownLinuxMusl => { + clap::builder::PossibleValue::new("aarch64-unknown-linux-musl") + } + Self::Riscv64GcUnknownLinuxGnu => { + clap::builder::PossibleValue::new("riscv64gc-unknown-linux-gnu") + } + Self::X86_64AppleDarwin => clap::builder::PossibleValue::new("x86_64-apple-darwin"), + Self::X86_64UnknownLinuxGnu => { + clap::builder::PossibleValue::new("x86_64-unknown-linux-gnu") + } + Self::X86_64UnknownLinuxMusl => { + clap::builder::PossibleValue::new("x86_64-unknown-linux-musl") + } + }) + } +} + +impl From for Triple { + fn from(value: SupportedTriple) -> Self { + match value { + SupportedTriple::Aarch64AppleDarwin => Triple { + architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64), + vendor: Vendor::Apple, + operating_system: OperatingSystem::Darwin, + environment: Environment::Unknown, + binary_format: BinaryFormat::Macho, + }, + SupportedTriple::Aarch64UnknownLinuxGnu => Triple { + architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64), + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Gnu, + binary_format: BinaryFormat::Elf, + }, + SupportedTriple::Aarch64UnknownLinuxMusl => Triple { + architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64), + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Musl, + binary_format: BinaryFormat::Elf, + }, + SupportedTriple::Riscv64GcUnknownLinuxGnu => Triple { + architecture: Architecture::Riscv64(Riscv64Architecture::Riscv64gc), + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Gnu, + binary_format: BinaryFormat::Elf, + }, + SupportedTriple::X86_64AppleDarwin => Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Apple, + operating_system: OperatingSystem::Darwin, + environment: Environment::Unknown, + binary_format: BinaryFormat::Macho, + }, + SupportedTriple::X86_64UnknownLinuxGnu => Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Gnu, + binary_format: BinaryFormat::Elf, + }, + SupportedTriple::X86_64UnknownLinuxMusl => Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Musl, + binary_format: BinaryFormat::Elf, + }, + } + } +} + +pub trait TripleExt { + /// Returns a clang-compatible triple. + /// + /// Clang supports just a subset of target triples defined by Rust. For + /// example, it supports only `riscv64-unknown-linux-gnu`, while rust + /// defines multiple RISC-V 64 targets (e.g. `riscv64gc-[...]`). + fn clang_triple(&self) -> Triple; + /// Determines if the build for the given target should be perfomed in a + /// container. + fn containerized_build(&self) -> bool; + /// Returns the container repository and tag for the given target. + fn container_image( + &self, + container_repository: &str, + container_tag: &str, + ) -> Option<(String, String)>; + /// Returns CMake options for building LLVM for the given target. + fn llvm_build_config(&self, install_prefix: &OsStr) -> Option; + /// Determines if the target is a cross target. + fn is_cross(&self) -> bool; + /// Returns the QEMU user-space emulator for the given target. + fn qemu(&self) -> OsString; + /// Returns RUSTFLAGS for the given target. + fn rustflags(&self) -> OsString; +} + +impl TripleExt for Triple { + fn clang_triple(&self) -> Triple { + let Triple { + architecture, + vendor, + operating_system, + environment, + binary_format, + } = self; + let architecture = match architecture { + // Default all RISC-V 64 variants to `riscv64`. + Architecture::Riscv64(_) => Architecture::Riscv64(Riscv64Architecture::Riscv64), + _ => *architecture, + }; + Triple { + architecture, + vendor: vendor.clone(), + operating_system: *operating_system, + environment: *environment, + binary_format: *binary_format, + } + } + + fn containerized_build(&self) -> bool { + let Triple { + operating_system, .. + } = self; + *operating_system == OperatingSystem::Linux + } + + fn container_image( + &self, + container_repository: &str, + container_tag: &str, + ) -> Option<(String, String)> { + let prefix = if self.is_cross() { "cross" } else { "native" }; + if self.containerized_build() { + let image_name = format!("{prefix}-{self}:{container_tag}"); + let full_name = format!("{container_repository}/{image_name}"); + let dockerfile = format!("docker/Dockerfile.{image_name}"); + Some((full_name, dockerfile)) + } else { + None + } + } + + fn llvm_build_config(&self, install_prefix: &OsStr) -> Option { + let Triple { + architecture, + operating_system, + environment, + .. + } = self; + let install_prefix = install_prefix.to_owned(); + + match (architecture, operating_system, environment) { + (Architecture::Aarch64(_), OperatingSystem::Darwin, Environment::Unknown) => { + Some(LlvmBuildConfig { + c_compiler: "clang".to_owned(), + cxx_compiler: "clang++".to_owned(), + compiler_target: None, + cxxflags: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Darwin, + processor: Processor::Aarch64, + static_build: false, + target_triple: "aarch64-apple-darwin".to_owned(), + }) + } + (Architecture::Aarch64(_), OperatingSystem::Linux, Environment::Gnu) => { + Some(LlvmBuildConfig { + c_compiler: "clang".to_owned(), + cxx_compiler: "clang++".to_owned(), + compiler_target: Some("aarch64-linux-gnu".to_owned()), + cxxflags: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Linux, + processor: Processor::Aarch64, + static_build: true, + target_triple: "aarch64-linux-gnu".to_owned(), + }) + } + (Architecture::Aarch64(_), OperatingSystem::Linux, Environment::Musl) => { + Some(LlvmBuildConfig { + c_compiler: if self.is_cross() { + "aarch64-unknown-linux-musl-clang".to_owned() + } else { + "clang".to_owned() + }, + cxx_compiler: if self.is_cross() { + "aarch64-unknown-linux-musl-clang++".to_owned() + } else { + "clang++".to_owned() + }, + // The clang wrapper specified above takes care of setting + // the target. + compiler_target: None, + cxxflags: Some("-stdlib=libc++".to_owned()), + ldflags: Some( + "-rtlib=compiler-rt -unwindlib=libunwind -lc++ -lc++abi".to_owned(), + ), + install_prefix, + skip_install_rpath: false, + system: System::Linux, + processor: Processor::Aarch64, + static_build: true, + target_triple: "aarch64-unknown-linux-musl".to_owned(), + }) + } + (Architecture::Riscv64(_), OperatingSystem::Linux, Environment::Gnu) => { + Some(LlvmBuildConfig { + c_compiler: "clang".to_owned(), + cxx_compiler: "clang++".to_owned(), + compiler_target: Some("riscv64-linux-gnu".to_owned()), + cxxflags: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Linux, + processor: Processor::Riscv64, + static_build: true, + target_triple: "riscv64-linux-gnu".to_owned(), + }) + } + (Architecture::X86_64, OperatingSystem::Darwin, Environment::Unknown) => { + Some(LlvmBuildConfig { + c_compiler: "clang".to_owned(), + cxx_compiler: "clang++".to_owned(), + cxxflags: None, + compiler_target: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Darwin, + processor: Processor::X86_64, + static_build: false, + target_triple: "x86_64-apple-darwin".to_owned(), + }) + } + (Architecture::X86_64, OperatingSystem::Linux, Environment::Gnu) => { + Some(LlvmBuildConfig { + c_compiler: "clang".to_owned(), + cxx_compiler: "clang++".to_owned(), + compiler_target: Some("x86_64-linux-gnu".to_owned()), + cxxflags: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Linux, + processor: Processor::X86_64, + static_build: true, + target_triple: "x86_64-linux-gnu".to_owned(), + }) + } + (Architecture::X86_64, OperatingSystem::Linux, Environment::Musl) => { + Some(LlvmBuildConfig { + c_compiler: if self.is_cross() { + "x86_64-unknown-linux-musl-clang".to_owned() + } else { + "clang".to_owned() + }, + cxx_compiler: if self.is_cross() { + "x86_64-unknown-linux-musl-clang++".to_owned() + } else { + "clang++".to_owned() + }, + // The clang wrapper specified above takes care of setting + // the target. + compiler_target: None, + cxxflags: None, + ldflags: None, + install_prefix, + skip_install_rpath: false, + system: System::Linux, + processor: Processor::X86_64, + static_build: true, + target_triple: "x86_64-unknown-linux-musl".to_owned(), + }) + } + (_, _, _) => None, + } + } + + fn is_cross(&self) -> bool { + self.architecture != target_lexicon::HOST.architecture + } + + fn qemu(&self) -> OsString { + match self.architecture { + Architecture::Aarch64(_) => OsString::from("qemu-aarch64"), + Architecture::Riscv64(_) => OsString::from("qemu-riscv64"), + Architecture::X86_64 => OsString::from("qemu-x86_64"), + _ => unreachable!(), + } + } + + fn rustflags(&self) -> OsString { + let mut rustflags = OsString::from("RUSTFLAGS=-C linker=clang -C link-arg=-fuse-ld=lld"); + if self.is_cross() { + rustflags.push(format!(" -C link-arg=--target={}", self.clang_triple())); + } + + rustflags + } +} diff --git a/xtask/src/tempdir.rs b/xtask/src/tempdir.rs new file mode 100644 index 00000000..477a7a99 --- /dev/null +++ b/xtask/src/tempdir.rs @@ -0,0 +1,52 @@ +use std::{ + env, + ffi::{OsStr, OsString}, + fs::{self, create_dir}, + io, + path::{Path, PathBuf}, +}; + +use uuid::Uuid; + +/// A temporary directory which is cleaned on `drop` (unless the `preserve` field +/// is `true`. +pub struct TempDir { + /// Path to the temp directory. + dir_path: PathBuf, + /// Whether to preserve the temp directory after `drop`. If `false`, it + /// gets removed automatically. + preserve: bool, +} + +impl TempDir { + pub fn new(prefix: &str, preserve: bool) -> io::Result { + let dir_path = env::temp_dir().join(format!("{prefix}-{}", Uuid::new_v4())); + create_dir(dir_path.as_path())?; + Ok(Self { dir_path, preserve }) + } + + pub fn to_os_string(&self) -> OsString { + self.dir_path.as_os_str().to_owned() + } +} + +impl AsRef for TempDir { + fn as_ref(&self) -> &OsStr { + self.dir_path.as_os_str() + } +} + +impl AsRef for TempDir { + fn as_ref(&self) -> &Path { + self.dir_path.as_path() + } +} + +impl Drop for TempDir { + /// Removes the temp directory if requested. + fn drop(&mut self) { + if !self.preserve && self.dir_path.exists() { + let _ = fs::remove_dir_all(&self.dir_path); + } + } +} diff --git a/xtask/src/test.rs b/xtask/src/test.rs new file mode 100644 index 00000000..3f2b4573 --- /dev/null +++ b/xtask/src/test.rs @@ -0,0 +1,7 @@ +use std::ffi::OsString; + +use crate::cargo::{run_cargo, CargoArgs}; + +pub fn test(args: CargoArgs) -> anyhow::Result<()> { + run_cargo(args, OsString::from("test"), &[]) +}