From 7f81f37e0ca0d1fe69f0439a81022287ad8cb549 Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Mon, 13 Jan 2025 15:58:27 +0100 Subject: [PATCH] revive llvm builder utility (#154) Pre-eliminary support for LLVM releases and resolc binary releases by streamlining the build process for all supported hosts platforms. - Introduce the revive-llvm-builder crate with the revive-llvm builder utilty. - Do not rely on the LLVM dependency in $PATH to decouple the system LLVM installation from the LLVM host dependency. - Fix the emscripten build by decoupling the host and native LLVM dependencies. Thus allowing a single LLVM emscripten release that can be used on any host platform. - An example Dockerfile building an alpine container with a fully statically linked resolc ELF binary. - Remove the Debian builder utilities and workflow. --- .dockerignore | 7 + .github/trigger-wasm-llvm-build | 1 + .github/workflows/build-revive-debian.yml | 38 - .github/workflows/build-revive-wasm.yml | 48 +- .github/workflows/rust.yml | 12 +- .gitignore | 6 +- CHANGELOG.md | 11 + Cargo.lock | 1028 +++++++++++++---- Cargo.toml | 12 +- Dockerfile | 32 + LLVM.lock | 2 + Makefile | 24 +- README.md | 34 +- SECURITY.md | 1 + build-llvm.sh | 88 -- crates/build-utils/.gitignore | 14 + crates/build-utils/Cargo.toml | 15 + crates/build-utils/README.md | 10 + crates/build-utils/src/lib.rs | 48 + crates/builtins/Cargo.toml | 3 +- crates/builtins/build.rs | 29 +- .../builtins/libclang_rt.builtins-riscv32.a | Bin 316226 -> 0 bytes .../builtins/libclang_rt.builtins-riscv64.a | Bin 333516 -> 0 bytes crates/lld-sys/Cargo.toml | 1 + crates/lld-sys/build.rs | 55 +- crates/llvm-builder/Cargo.toml | 40 + crates/llvm-builder/README.md | 132 +++ crates/llvm-builder/src/build_type.rs | 39 + crates/llvm-builder/src/builtins.rs | 137 +++ crates/llvm-builder/src/ccache_variant.rs | 31 + crates/llvm-builder/src/lib.rs | 340 ++++++ crates/llvm-builder/src/llvm_path.rs | 151 +++ crates/llvm-builder/src/llvm_project.rs | 39 + crates/llvm-builder/src/lock.rs | 37 + .../src/platforms/aarch64_linux_gnu.rs | 111 ++ .../src/platforms/aarch64_linux_musl.rs | 394 +++++++ .../src/platforms/aarch64_macos.rs | 105 ++ crates/llvm-builder/src/platforms/mod.rs | 45 + crates/llvm-builder/src/platforms/shared.rs | 232 ++++ .../src/platforms/wasm32_emscripten.rs | 224 ++++ .../src/platforms/x86_64_linux_gnu.rs | 111 ++ .../src/platforms/x86_64_linux_musl.rs | 394 +++++++ .../src/platforms/x86_64_macos.rs | 105 ++ .../src/platforms/x86_64_windows_gnu.rs | 127 ++ .../llvm-builder/src/revive_llvm/arguments.rs | 119 ++ crates/llvm-builder/src/revive_llvm/main.rs | 141 +++ crates/llvm-builder/src/sanitizer.rs | 50 + crates/llvm-builder/src/target_env.rs | 36 + crates/llvm-builder/src/target_triple.rs | 29 + crates/llvm-builder/src/utils.rs | 230 ++++ crates/llvm-builder/tests/build.rs | 158 +++ crates/llvm-builder/tests/checkout.rs | 48 + crates/llvm-builder/tests/clone.rs | 36 + crates/llvm-builder/tests/common.rs | 36 + crates/runner/Cargo.toml | 1 + crates/runtime-api/Cargo.toml | 3 + crates/runtime-api/build.rs | 9 +- crates/stdlib/Cargo.toml | 3 + crates/stdlib/build.rs | 11 +- deny.toml | 65 ++ emscripten-build-llvm.sh | 69 -- utils/build-debian-builder.sh | 7 - utils/build-revive.sh | 20 - utils/revive-builder-debian.dockerfile | 14 - utils/run-debian-builder.sh | 6 - 65 files changed, 4847 insertions(+), 557 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/trigger-wasm-llvm-build delete mode 100644 .github/workflows/build-revive-debian.yml create mode 100644 Dockerfile create mode 100644 LLVM.lock create mode 100644 SECURITY.md delete mode 100755 build-llvm.sh create mode 100644 crates/build-utils/.gitignore create mode 100644 crates/build-utils/Cargo.toml create mode 100644 crates/build-utils/README.md create mode 100644 crates/build-utils/src/lib.rs delete mode 100644 crates/builtins/libclang_rt.builtins-riscv32.a delete mode 100644 crates/builtins/libclang_rt.builtins-riscv64.a create mode 100644 crates/llvm-builder/Cargo.toml create mode 100644 crates/llvm-builder/README.md create mode 100644 crates/llvm-builder/src/build_type.rs create mode 100644 crates/llvm-builder/src/builtins.rs create mode 100644 crates/llvm-builder/src/ccache_variant.rs create mode 100644 crates/llvm-builder/src/lib.rs create mode 100644 crates/llvm-builder/src/llvm_path.rs create mode 100644 crates/llvm-builder/src/llvm_project.rs create mode 100644 crates/llvm-builder/src/lock.rs create mode 100644 crates/llvm-builder/src/platforms/aarch64_linux_gnu.rs create mode 100644 crates/llvm-builder/src/platforms/aarch64_linux_musl.rs create mode 100644 crates/llvm-builder/src/platforms/aarch64_macos.rs create mode 100644 crates/llvm-builder/src/platforms/mod.rs create mode 100644 crates/llvm-builder/src/platforms/shared.rs create mode 100644 crates/llvm-builder/src/platforms/wasm32_emscripten.rs create mode 100644 crates/llvm-builder/src/platforms/x86_64_linux_gnu.rs create mode 100644 crates/llvm-builder/src/platforms/x86_64_linux_musl.rs create mode 100644 crates/llvm-builder/src/platforms/x86_64_macos.rs create mode 100644 crates/llvm-builder/src/platforms/x86_64_windows_gnu.rs create mode 100644 crates/llvm-builder/src/revive_llvm/arguments.rs create mode 100644 crates/llvm-builder/src/revive_llvm/main.rs create mode 100644 crates/llvm-builder/src/sanitizer.rs create mode 100644 crates/llvm-builder/src/target_env.rs create mode 100644 crates/llvm-builder/src/target_triple.rs create mode 100644 crates/llvm-builder/src/utils.rs create mode 100644 crates/llvm-builder/tests/build.rs create mode 100644 crates/llvm-builder/tests/checkout.rs create mode 100644 crates/llvm-builder/tests/clone.rs create mode 100644 crates/llvm-builder/tests/common.rs create mode 100644 deny.toml delete mode 100755 emscripten-build-llvm.sh delete mode 100755 utils/build-debian-builder.sh delete mode 100755 utils/build-revive.sh delete mode 100644 utils/revive-builder-debian.dockerfile delete mode 100755 utils/run-debian-builder.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..394368a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +emsdk +llvm*/ +target-llvm/ +target/ +node_modules/ +utils/ +build/ diff --git a/.github/trigger-wasm-llvm-build b/.github/trigger-wasm-llvm-build new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/trigger-wasm-llvm-build @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/workflows/build-revive-debian.yml b/.github/workflows/build-revive-debian.yml deleted file mode 100644 index 57c1d0f..0000000 --- a/.github/workflows/build-revive-debian.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build revive-debian -on: - workflow_dispatch: - -env: - REVIVE_DEBIAN_PACKAGE: revive-debian-x86 - DEBIAN_CONTAINER: revive-builder-debian-x86 - DEBIAN_CONTAINER_BUILDER: build-debian-builder.sh - DEBIAN_CONTAINER_RUNNER: run-debian-builder.sh - REVIVE_DEBIAN_INSTALL: ${{ github.workspace }}/target/release - REVIVE_DEBIAN_BINARY: resolc - RUST_VERSION: "1.81" - -jobs: - build-revive-debian-x86: - name: debian-container-x86 - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - - name: build-container - run: | - (cd utils && ./${{ env.DEBIAN_CONTAINER_BUILDER}} --build-arg RUST_VERSION=${{ env.RUST_VERSION}} . ) - - - name: build-revive-debian - run: | - rustup show - cargo --version - rustup +nightly show - cargo +nightly --version - bash --version - utils/${{ env.DEBIAN_CONTAINER_RUNNER }} utils/build-revive.sh -o ${{ env.REVIVE_DEBIAN_INSTALL}} - - - uses: actions/upload-artifact@v4 - with: - name: ${{ env.REVIVE_DEBIAN_PACKAGE }} - path: ${{ env.REVIVE_DEBIAN_INSTALL }}/${{ env.REVIVE_DEBIAN_BINARY }} - retention-days: 1 diff --git a/.github/workflows/build-revive-wasm.yml b/.github/workflows/build-revive-wasm.yml index dd1db44..1567676 100644 --- a/.github/workflows/build-revive-wasm.yml +++ b/.github/workflows/build-revive-wasm.yml @@ -14,7 +14,7 @@ env: jobs: build-revive-wasm: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: shell: bash @@ -26,24 +26,30 @@ jobs: uses: actions/cache@v3 with: path: | - llvm18.0-emscripten + target-llvm # Use a unique key based on LLVM version or configuration files to avoid cache invalidation - key: llvm-build-${{ runner.os }}-${{ hashFiles('clone-llvm.sh', 'emscripten-build-llvm.sh') }} + key: llvm-build-${{ runner.os }}-${{ hashFiles('LLVM.lock', '.github/trigger-wasm-llvm-build') }} - name: Install Dependencies run: | - sudo apt-get update && sudo apt-get install -y cmake ninja-build libncurses5 + # system dependencies + sudo apt-get update && sudo apt-get install -y cmake ninja-build curl git libssl-dev pkg-config clang lld rustup target add wasm32-unknown-emscripten - # Install LLVM required for the compiler runtime, runtime-api and stdlib - curl -sSL --output llvm.tar.xz https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04.tar.xz - tar Jxf llvm.tar.xz - mv clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04 llvm18/ - echo "$(pwd)/llvm18/bin" >> $GITHUB_PATH - # Install Emscripten - git clone https://github.com/emscripten-core/emsdk.git - cd emsdk - ./emsdk install ${{ env.EMSCRIPTEN_VERSION }} - ./emsdk activate ${{ env.EMSCRIPTEN_VERSION }} + # host LLVM + curl -sSL --output llvm.tar.xz https://github.com/paritytech/revive/releases/download/v0.1.0-dev.7/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-24.04.tar.xz + mkdir llvm18 + tar Jxf llvm.tar.xz -C llvm18/ + echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV + # Clone LLVM and install the Emscripten SDK + make install-llvm-builder + revive-llvm --target-env emscripten clone + + - name: Build target LLVM + if: steps.cache-llvm.outputs.cache-hit != 'true' + run: | + source emsdk/emsdk_env.sh + revive-llvm --target-env emscripten build + echo "REVIVE_LLVM_TARGET_PREFIX=${PWD}/target-llvm/emscripten/target-final" >> $GITHUB_ENV - run: | rustup show @@ -52,13 +58,6 @@ jobs: cargo +nightly --version cmake --version bash --version - llvm-config --version - - - name: Build LLVM - if: steps.cache-llvm.outputs.cache-hit != 'true' - run: | - export EMSDK_ROOT=${PWD}/emsdk - ./emscripten-build-llvm.sh - name: Use Cached LLVM if: steps.cache-llvm.outputs.cache-hit == 'true' @@ -67,8 +66,7 @@ jobs: - name: Build revive run: | - export LLVM_LINK_PREFIX=${PWD}/llvm18.0-emscripten - source ./emsdk/emsdk_env.sh + source emsdk/emsdk_env.sh make install-wasm - uses: actions/upload-artifact@v4 @@ -83,7 +81,7 @@ jobs: needs: build-revive-wasm strategy: matrix: - os: ['ubuntu-24.04', 'macos-14', 'windows-2022'] + os: ["ubuntu-24.04", "macos-14", "windows-2022"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -100,7 +98,7 @@ jobs: - name: Set Up Node.js uses: actions/setup-node@v3 with: - node-version: '20' + node-version: "20" - name: Install Bun on Windows if: runner.os == 'Windows' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3b1c56d..c92b886 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ env: jobs: build-ubuntu-x86: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -25,16 +25,16 @@ jobs: - name: Install LLVM run: | - curl -sSL --output llvm.tar.xz https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04.tar.xz - tar Jxf llvm.tar.xz - mv clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04 llvm18/ - echo "$(pwd)/llvm18/bin" >> $GITHUB_PATH + curl -sSL --output llvm.tar.xz https://github.com/paritytech/revive/releases/download/v0.1.0-dev.7/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-24.04.tar.xz + mkdir llvm18 + tar Jxf llvm.tar.xz -C llvm18/ + echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV - name: Install apt dependencies run: | sudo add-apt-repository -y ppa:ethereum/ethereum sudo apt update - sudo apt install -y libtinfo5 ethereum + sudo apt install -y ethereum - name: Format run: make format diff --git a/.gitignore b/.gitignore index a8a0c39..87ea520 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +target-llvm *.dot .vscode/ .DS_Store @@ -6,9 +7,7 @@ /*.yul /*.ll /*.s -/llvm-project -/llvm18.0 -/llvm18.0-emscripten +/llvm* node_modules artifacts tmp @@ -16,3 +15,4 @@ package-lock.json /*.html /build soljson.js +emsdk diff --git a/CHANGELOG.md b/CHANGELOG.md index 347078b..2a9009a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## Unreleased +This is a development pre-release. + +### Added +- The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency. + +### Changed +- The minimum supported Rust version is `1.81.0`. + +### Fixed +- Decouple the LLVM target dependency from the LLVM host dependency. + ## v0.1.0-dev.7 This is a development pre-release. diff --git a/Cargo.lock b/Cargo.lock index b5c4876..6bdd9c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-core" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618bd382f0bc2ac26a7e4bfae01c9b015ca8f21b37ca40059ae35a7e62b3dc6" +checksum = "d0713007d14d88a6edb8e248cddab783b698dbb954a28b8eee4bab21cfb7e578" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41056bde53ae10ffbbf11618efbe1e0290859e5eab0fe9ef82ebdb62f12a866f" +checksum = "44e3b98c37b3218924cd1d2a8570666b89662be54e5b182643855f783ea68b33" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357da577dfb56998d01f574d81ad7a1958d248740a7981b205d69d65a7da404" +checksum = "731ea743b3d843bc657e120fb1d1e9cc94f5dab8107e35a82125a63e6420a102" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" +checksum = "788bb18e8f61d5d9340b52143f27771daf7e1dccbaf2741621d2493f9debf52e" dependencies = [ "alloy-rlp", "bytes", @@ -165,7 +165,6 @@ dependencies = [ "derive_more 1.0.0", "foldhash", "hashbrown 0.15.2", - "hex-literal", "indexmap 2.7.0", "itoa", "k256", @@ -203,23 +202,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d64f851d95619233f74b310f12bcf16e0cbc27ee3762b6115c14a84809280a" +checksum = "a07b74d48661ab2e4b50bb5950d74dbff5e61dd8ed03bb822281b706d54ebacb" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" +checksum = "19cc9c7f20b90f9be1a8f71a3d8e283a43745137b0837b1a1cb13159d37cad72" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -228,31 +227,31 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c02997ccef5f34f9c099277d4145f183b422938ed5322dc57a089fe9b9ad9ee" +checksum = "713b7e6dfe1cb2f55c80fb05fd22ed085a1b4e48217611365ed0ae598a74c6ac" dependencies = [ "const-hex", "dunce", "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce13ff37285b0870d0a0746992a4ae48efaf34b766ae4c2640fa15e5305f8e73" +checksum = "1eda2711ab2e1fb517fc6e2ffa9728c9a232e296d16810810e6957b781a1b8bc" dependencies = [ "serde", "winnow", @@ -260,9 +259,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1174cafd6c6d810711b4e00383037bdb458efc4fe3dbafafa16567e0320c54d8" +checksum = "e3b478bc9c0c4737a04cd976accde4df7eba0bdc0d90ad6ff43d58bc93cf79c1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -328,9 +327,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "approx" @@ -352,7 +351,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -603,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -641,7 +640,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -768,7 +767,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -833,6 +832,37 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efdb1fdb47602827a342857666feb372712cbc64b414172bd6b167a02927674" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "assert_matches" version = "1.5.0" @@ -895,13 +925,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -912,7 +942,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -931,7 +961,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.36.5", + "object 0.36.7", "rustc-demangle", "windows-targets 0.52.6", ] @@ -1094,9 +1124,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitvec" @@ -1433,6 +1463,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bstr" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +dependencies = [ + "memchr", + "regex-automata 0.4.9", + "serde", +] + [[package]] name = "build-helper" version = "0.1.1" @@ -1456,9 +1497,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -1515,9 +1556,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.5" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1579,9 +1620,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -1589,9 +1630,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -1601,14 +1642,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -1735,6 +1776,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpp_demangle" version = "0.3.5" @@ -2061,7 +2118,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2277,14 +2334,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "cxx" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d44ff199ff93242c3afe480ab588d544dd08d72e92885e152ffebc670f076ad" +checksum = "ad7c7515609502d316ab9a24f67dc045132d93bfd3f00713389e90d9898bf30d" dependencies = [ "cc", "cxxbridge-cmd", @@ -2296,47 +2353,47 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fd8f17ad454fc1e4f4ab83abffcc88a532e90350d3ffddcb73030220fcbd52" +checksum = "8bfd16fca6fd420aebbd80d643c201ee4692114a0de208b790b9cd02ceae65fb" dependencies = [ "cc", "codespan-reporting", "proc-macro2", "quote", "scratch", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4717c9c806a9e07fdcb34c84965a414ea40fafe57667187052cf1eb7f5e8a8a9" +checksum = "6c33fd49f5d956a1b7ee5f7a9768d58580c6752838d92e39d0d56439efdedc35" dependencies = [ "clap", "codespan-reporting", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "cxxbridge-flags" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6515329bf3d98f4073101c7866ff2bec4e635a13acb82e3f3753fff0bf43cb" +checksum = "be0f1077278fac36299cce8446effd19fe93a95eedb10d39265f3bf67b3036c9" [[package]] name = "cxxbridge-macro" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb93e6a7ce8ec985c02bbb758237a31598b340acbbc3c19c5a4fa6adaaac92ab" +checksum = "3da7e4d6e74af6b79031d264b2f13c3ea70af1978083741c41ffce9308f1f24f" dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2360,7 +2417,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2371,7 +2428,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2412,7 +2469,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2423,7 +2480,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2436,7 +2493,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2456,10 +2513,16 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "unicode-xid", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -2531,7 +2594,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2550,6 +2613,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "docify" version = "0.2.9" @@ -2571,7 +2640,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.90", + "syn 2.0.96", "termcolor", "toml 0.8.19", "walkdir", @@ -2583,6 +2652,19 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "downloader" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac1e888d6830712d565b2f3a974be3200be9296bc1b03db8251a4cbf18a4a34" +dependencies = [ + "futures", + "rand", + "reqwest", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "dunce" version = "1.0.5" @@ -2679,7 +2761,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2731,7 +2813,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2751,7 +2833,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2762,7 +2844,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2853,7 +2935,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -2985,6 +3067,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2997,6 +3089,21 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -3066,7 +3173,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3202,7 +3309,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd92c83d8ab159ddfc3c556)", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3214,7 +3321,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3224,7 +3331,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3291,6 +3398,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -3354,7 +3467,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3471,15 +3584,37 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", "libgit2-sys", "log", - "openssl-probe", - "openssl-sys", "url", ] +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.7.0", + "ignore", + "walkdir", +] + [[package]] name = "group" version = "0.13.0" @@ -3654,12 +3789,106 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iced-x86" version = "1.21.0" @@ -3784,7 +4013,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3814,6 +4043,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.9", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -3869,7 +4114,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3940,7 +4185,7 @@ source = "git+https://github.com/TheDan64/inkwell.git?rev=7b410298b6a93450adaa90 dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -3972,6 +4217,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + [[package]] name = "is-terminal" version = "0.4.13" @@ -4113,9 +4364,7 @@ checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" dependencies = [ "cc", "libc", - "libssh2-sys", "libz-sys", - "openssl-sys", "pkg-config", ] @@ -4141,7 +4390,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", "redox_syscall", ] @@ -4194,25 +4443,11 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "libc", @@ -4246,9 +4481,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -4262,6 +4497,7 @@ version = "0.1.0-dev.7" dependencies = [ "cc", "libc", + "revive-build-utils", ] [[package]] @@ -4312,7 +4548,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4326,7 +4562,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4337,7 +4573,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4348,7 +4584,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4388,7 +4624,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.42", + "rustix 0.38.43", ] [[package]] @@ -4430,6 +4666,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.2" @@ -4439,6 +4681,17 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "multi-stash" version = "0.2.0" @@ -4460,6 +4713,23 @@ dependencies = [ "typenum", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -4523,7 +4793,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -4601,9 +4871,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -4626,6 +4896,32 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.7.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -5208,7 +5504,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -5959,7 +6255,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -6683,15 +6979,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.8", + "thiserror 2.0.11", "ucd-trie", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -7240,14 +7536,14 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d2840cc62a0550156b1676fed8392271ddf2fab4a00661db56231424674624" +checksum = "2f2116a92e6e96220a398930f4c8a6cda1264206f3e2034fc9982bfd93f261f7" dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7257,7 +7553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c16669ddc7433e34c1007d31080b80901e3e8e523cb9d4b441c3910cf9294b" dependencies = [ "polkavm-derive-impl", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7283,7 +7579,7 @@ dependencies = [ "gimli 0.31.1", "hashbrown 0.14.5", "log", - "object 0.36.5", + "object 0.36.7", "polkavm-common", "regalloc2 0.9.3", "rustc-demangle", @@ -7334,13 +7630,40 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.25" +name = "predicates" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924b9a625d6df5b74b0b3cfbb5669b3f62ddf3d46a677ce12b1945471b4ae5c3" dependencies = [ "proc-macro2", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7421,7 +7744,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7432,14 +7755,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -7452,7 +7775,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.7.0", "lazy_static", "num-traits", "rand", @@ -7481,9 +7804,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -7566,7 +7889,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -7597,7 +7920,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -7675,6 +7998,46 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "revive-benchmarks" version = "0.1.0-dev.7" @@ -7687,9 +8050,16 @@ dependencies = [ "revive-runner", ] +[[package]] +name = "revive-build-utils" +version = "0.1.0-dev.7" + [[package]] name = "revive-builtins" version = "0.1.0-dev.7" +dependencies = [ + "revive-build-utils", +] [[package]] name = "revive-common" @@ -7749,6 +8119,29 @@ dependencies = [ "tempfile", ] +[[package]] +name = "revive-llvm-builder" +version = "0.1.0-dev.7" +dependencies = [ + "anyhow", + "assert_cmd", + "assert_fs", + "clap", + "downloader", + "env_logger", + "flate2", + "fs_extra", + "http", + "log", + "num_cpus", + "once_cell", + "path-slash", + "regex", + "serde", + "tar", + "toml 0.8.19", +] + [[package]] name = "revive-llvm-context" version = "0.1.0-dev.7" @@ -7795,6 +8188,7 @@ version = "0.1.0-dev.7" dependencies = [ "anyhow", "inkwell", + "revive-build-utils", "revive-common", ] @@ -7832,6 +8226,7 @@ name = "revive-stdlib" version = "0.1.0-dev.7" dependencies = [ "inkwell", + "revive-build-utils", ] [[package]] @@ -8005,22 +8400,37 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] [[package]] -name = "rustversion" -version = "1.0.18" +name = "rustls-pemfile" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rusty-fork" @@ -8051,9 +8461,9 @@ dependencies = [ [[package]] name = "safe_arch" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" dependencies = [ "bytemuck", ] @@ -8188,7 +8598,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -8216,7 +8626,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -8242,7 +8652,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -8272,10 +8682,19 @@ dependencies = [ ] [[package]] -name = "schnellru" -version = "0.2.3" +name = "schannel" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ "ahash", "cfg-if", @@ -8396,6 +8815,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.7.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.6.0" @@ -8449,9 +8891,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -8476,20 +8918,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -8516,6 +8958,18 @@ dependencies = [ "stacker", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serdect" version = "0.2.0" @@ -8965,6 +9419,16 @@ dependencies = [ "staging-xcm", ] +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "sp-api" version = "26.0.0" @@ -8998,7 +9462,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9238,7 +9702,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -9289,7 +9753,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd92c83d8ab159ddfc3c556)", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9299,17 +9763,17 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9325,7 +9789,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "environmental", "parity-scale-codec", @@ -9534,7 +9998,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -9560,20 +10024,20 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "Inflector", "expander", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9655,7 +10119,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?rev=243b751abbb94369bbd [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" [[package]] name = "sp-storage" @@ -9672,7 +10136,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9707,7 +10171,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "parity-scale-codec", "tracing", @@ -9786,7 +10250,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -9804,7 +10268,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4e805ca05067f6ed970f33f9be51483185b0cc0b" +source = "git+https://github.com/paritytech/polkadot-sdk#738282a2c4127f5e6a1c8d50235ba126b9f05025" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -10036,7 +10500,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10163,9 +10627,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -10174,14 +10638,23 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219389c1ebe89f8333df8bdfb871f6631c552ff399c23cac02480b6088aad8f0" +checksum = "31e89d8bf2768d277f40573c83a02a099e96d96dd3104e13ea676194e61ac4b0" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", ] [[package]] @@ -10192,7 +10665,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10201,6 +10674,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -10209,14 +10693,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", - "rustix 0.38.42", + "rustix 0.38.43", "windows-sys 0.59.0", ] @@ -10229,6 +10714,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "testnet-parachains-constants" version = "1.0.0" @@ -10255,11 +10746,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.8" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.8", + "thiserror-impl 2.0.11", ] [[package]] @@ -10270,18 +10761,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.8" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10356,9 +10847,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -10369,6 +10860,31 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -10412,6 +10928,33 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" @@ -10432,7 +10975,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -10496,6 +11039,12 @@ dependencies = [ "hash-db", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tt-call" version = "1.0.9" @@ -10695,6 +11244,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -10722,10 +11280,23 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.99" @@ -10744,7 +11315,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -11093,15 +11664,15 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.42", + "rustix 0.38.43", "windows-sys 0.48.0", ] [[package]] name = "wide" -version = "0.7.30" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" dependencies = [ "bytemuck", "safe_arch", @@ -11138,6 +11709,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -11354,9 +11955,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -11394,6 +11995,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xattr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +dependencies = [ + "libc", + "linux-raw-sys 0.4.15", + "rustix 0.38.43", +] + [[package]] name = "xcm-procedural" version = "7.0.0" @@ -11402,7 +12014,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11461,7 +12073,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "synstructure", ] @@ -11483,7 +12095,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11503,7 +12115,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", "synstructure", ] @@ -11524,7 +12136,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] @@ -11546,7 +12158,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.96", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0db12d0..6a2d989 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ revive-runtime-api = { version = "0.1.0-dev.7", path = "crates/runtime-api" } revive-runner = { version = "0.1.0-dev.7", path = "crates/runner" } revive-solidity = { version = "0.1.0-dev.7", path = "crates/solidity" } revive-stdlib = { version = "0.1.0-dev.7", path = "crates/stdlib" } +revive-build-utils = { version = "0.1.0-dev.7", path = "crates/build-utils" } hex = "0.4" petgraph = "0.6" @@ -63,7 +64,16 @@ env_logger = { version = "0.10.0", default-features = false } serde_stacker = "0.1" criterion = { version = "0.5", features = ["html_reports"] } log = { version = "0.4" } -git2 = "0.19.0" +git2 = { version = "0.19.0", default-features = false } +downloader = "0.2.8" +flate2 = "1.0.35" +http = "1.2.0" +fs_extra = "1.3.0" +num_cpus = "1" +tar = "0.4.4" +toml = "0.8" +assert_cmd = "2.0.16" +assert_fs = "1.1.2" # polkadot-sdk and friends codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..23d1c9b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM rust:1.84.0 AS llvm-builder +WORKDIR /opt/revive + +RUN apt update && \ + apt upgrade -y && \ + apt install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl + +COPY . . + +RUN make install-llvm-builder +RUN revive-llvm --target-env musl clone +RUN revive-llvm --target-env musl build + +FROM messense/rust-musl-cross:x86_64-musl AS resolc-builder +WORKDIR /opt/revive + +RUN apt update && \ + apt upgrade -y && \ + apt install -y pkg-config + +COPY . . +COPY --from=llvm-builder /opt/revive/target-llvm /opt/revive/target-llvm + +ENV LLVM_SYS_181_PREFIX=/opt/revive/target-llvm/musl/target-final +RUN make install-bin + +FROM alpine:latest +ADD https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux /usr/bin/solc +COPY --from=resolc-builder /root/.cargo/bin/resolc /usr/bin/resolc + +RUN apk add --no-cache libc6-compat +RUN chmod +x /usr/bin/solc diff --git a/LLVM.lock b/LLVM.lock new file mode 100644 index 0000000..4e37573 --- /dev/null +++ b/LLVM.lock @@ -0,0 +1,2 @@ +url = "https://github.com/llvm/llvm-project.git" +branch = "release/18.x" \ No newline at end of file diff --git a/Makefile b/Makefile index df875de..d1085a1 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: install format test test-solidity test-cli test-integration test-workspace test-wasm clean docs docs-build +.PHONY: install format test test-solidity test-cli test-integration test-workspace test-wasm clean install-llvm install-llvm-builder RUSTFLAGS_EMSCRIPTEN := \ -C link-arg=-sEXPORTED_FUNCTIONS=_main,_free,_malloc \ @@ -28,16 +28,16 @@ install-npm: install-wasm: install-npm RUSTFLAGS='$(RUSTFLAGS_EMSCRIPTEN)' cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features +install-llvm-builder: + cargo install --path crates/llvm-builder + +install-llvm: install-llvm-builder + revive-llvm clone + revive-llvm build + test-wasm: install-wasm npm run test:wasm -# install-revive: Build and install to the directory specified in REVIVE_INSTALL_DIR -ifeq ($(origin REVIVE_INSTALL_DIR), undefined) -REVIVE_INSTALL_DIR=`pwd`/release/revive-debian -endif -install-revive: - cargo install --path crates/solidity --root $(REVIVE_INSTALL_DIR) - format: cargo fmt --all --check @@ -53,11 +53,15 @@ test-solidity: install cargo test --package revive-solidity test-workspace: install - cargo test --workspace + cargo test --workspace --exclude revive-llvm-builder test-cli: install npm run test:cli +test-llvm-builder: + @echo "warning: the llvm-builder tests will take many hours" + cargo test --package revive-llvm-builder -- --test-threads=1 + bench-pvm: install-bin cargo criterion --bench execute --features bench-pvm-interpreter --message-format=json \ | criterion-table > crates/benchmarks/PVM.md @@ -72,9 +76,11 @@ bench: install-bin clean: cargo clean ; \ + revive-llvm clean ; \ rm -rf node_modules ; \ rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \ cargo uninstall revive-solidity ; \ + cargo uninstall revive-llvm-builder ; \ rm -f package-lock.json ; \ rm -rf js/dist ; \ rm -f js/src/resolc.{wasm,js} diff --git a/README.md b/README.md index 745afd4..940538b 100644 --- a/README.md +++ b/README.md @@ -21,34 +21,40 @@ Building from source requires a compatible LLVM build. ### LLVM -`revive` requires a build of LLVM 18.1.4 or later with the RISC-V _embedded_ target, including `compiler-rt`. Use the provided [build-llvm.sh](build-llvm.sh) build script to compile a compatible LLVM build locally in `$PWD/llvm18.0` (don't forget to add that to `$PATH` afterwards). +`revive` requires a build of LLVM v18.1.8 with the RISC-V _embedded_ target, including `compiler-rt`. Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation directory afterwards. ### The `resolc` Solidity frontend To install the `resolc` Solidity frontend executable: ```bash -bash build-llvm.sh -export PATH=${PWD}/llvm18.0/bin:$PATH +# Build the host LLVM dependency with PolkaVM target support +make install-llvm +export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final + +# Build the resolc frontend executable make install-bin resolc --version ``` + ### Cross-compilation to WASM -Cross-compiles the Revive compiler to WASM for running it in a Node.js or browser environment. - -Install [emscripten](https://emscripten.org/docs/getting_started/downloads.html). Tested on version 3.1.64. -To build resolc.js execute: +Cross-compile resolc.js frontend executable to Wasm for running it in a Node.js or browser environment. The `REVIVE_LLVM_TARGET_PREFIX` environment variable is used to control the target environment LLVM dependency. ```bash -bash build-llvm.sh -export PATH=${PWD}/llvm18.0/bin:$PATH -export EMSDK_ROOT= -bash emscripten-build-llvm.sh -source $EMSDK_ROOT/emsdk_env.sh -export LLVM_LINK_PREFIX=${PWD}/llvm18.0-emscripten -export PATH=$PATH:$PWD/llvm18.0-emscripten/bin/ +# Build the host LLVM dependency with PolkaVM target support +make install-llvm +export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final + +# Build the target LLVM dependency with PolkaVM target support +revive-llvm --target-env emscripten clone +source emsdk/emsdk_env.sh +revive-llvm --target-env emscripten build +export REVIVE_LLVM_TARGET_PREFIX=${PWD}/target-llvm/emscripten/target-final + +# Build the resolc frontend executable make install-wasm +make test-wasm ``` ### Development diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..50de6ef --- /dev/null +++ b/SECURITY.md @@ -0,0 +1 @@ +https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/SECURITY.md \ No newline at end of file diff --git a/build-llvm.sh b/build-llvm.sh deleted file mode 100755 index 26ba75a..0000000 --- a/build-llvm.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -INSTALL_DIR="${PWD}/llvm18.0" -mkdir -p ${INSTALL_DIR} - -# Build LLVM, clang -LLVM_SRC_PREFIX=${PWD}/llvm-project -LLVM_SRC_DIR=${LLVM_SRC_PREFIX}/llvm -LLVM_BUILD_DIR=${PWD}/build/llvm - -./clone-llvm.sh "${LLVM_SRC_PREFIX}" - -if [ ! -d ${LLVM_BUILD_DIR} ] ; then - mkdir -p ${LLVM_BUILD_DIR} -fi - -cmake -G Ninja \ - -S ${LLVM_SRC_DIR} \ - -B ${LLVM_BUILD_DIR} \ - -DLLVM_ENABLE_ASSERTIONS=On \ - -DLLVM_ENABLE_TERMINFO=Off \ - -DLLVM_ENABLE_LIBXML2=Off \ - -DLLVM_ENABLE_ZLIB=Off \ - -DLLVM_ENABLE_PROJECTS='clang;lld' \ - -DLLVM_TARGETS_TO_BUILD='RISCV' \ - -DLLVM_ENABLE_ZSTD=Off \ - -DCMAKE_BUILD_TYPE=MinSizeRel \ - -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} - -cmake --build ${LLVM_BUILD_DIR} -cmake --install ${LLVM_BUILD_DIR} - -# Build compiler builtins -COMPILER_RT_SRC_DIR=${LLVM_SRC_PREFIX}/compiler-rt -COMPILER_RT_BUILD_DIR=${PWD}/build/compiler-rt -if [ ! -d ${COMPILER_RT_BUILD_DIR} ] ; then - mkdir -p ${COMPILER_RT_BUILD_DIR} -fi - -build_compiler_rt() { - case "$1" in - 64) TARGET_ABI=lp64e ;; - 32) TARGET_ABI=ilp32e ;; - *) exit -1 - esac - CFLAGS="--target=riscv${1} -march=rv${1}em -mabi=${TARGET_ABI} -mcpu=generic-rv${1} -nostdlib -nodefaultlibs" - - cmake -G Ninja \ - -S ${COMPILER_RT_SRC_DIR} \ - -B ${COMPILER_RT_BUILD_DIR} \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ - -DCOMPILER_RT_BUILD_BUILTINS=ON \ - -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ - -DCOMPILER_RT_BUILD_MEMPROF=OFF \ - -DCOMPILER_RT_BUILD_PROFILE=OFF \ - -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ - -DCOMPILER_RT_BUILD_XRAY=OFF \ - -DCMAKE_C_COMPILER=${INSTALL_DIR}/bin/clang \ - -DCMAKE_C_COMPILER_TARGET=riscv${1} \ - -DCMAKE_ASM_COMPILER_TARGET=riscv${1} \ - -DCMAKE_CXX_COMPILER_TARGET=riscv${1} \ - -DCMAKE_C_TARGET_BITS=riscv${1} \ - -DCMAKE_ASM_TARGET_BITS=riscv${1} \ - -DCMAKE_AR=${INSTALL_DIR}/bin/llvm-ar \ - -DCMAKE_NM=${INSTALL_DIR}/bin/llvm-nm \ - -DCMAKE_RANLIB=${INSTALL_DIR}/bin/llvm-ranlib \ - -DCOMPILER_RT_BAREMETAL_BUILD=ON \ - -DLLVM_CONFIG_PATH=${INSTALL_DIR}/bin/llvm-config \ - -DCMAKE_C_FLAGS="${CFLAGS}" \ - -DCMAKE_ASM_FLAGS="${CFLAGS}" \ - -DCOMPILER_RT_TEST_COMPILER=${INSTALL_DIR}/bin/clang \ - -DCMAKE_CXX_FLAGS="${CFLAGS}" \ - -DCMAKE_SYSTEM_NAME=unknown \ - -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON - - cmake --build ${COMPILER_RT_BUILD_DIR} - cmake --install ${COMPILER_RT_BUILD_DIR} -} - -build_compiler_rt 32 -build_compiler_rt 64 - -echo "" -echo "success" -echo "add this directory to your PATH: ${INSTALL_DIR}/bin/" diff --git a/crates/build-utils/.gitignore b/crates/build-utils/.gitignore new file mode 100644 index 0000000..6ee06a1 --- /dev/null +++ b/crates/build-utils/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# IDE +/.idea/ +/.vscode/ diff --git a/crates/build-utils/Cargo.toml b/crates/build-utils/Cargo.toml new file mode 100644 index 0000000..edcf97a --- /dev/null +++ b/crates/build-utils/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "revive-build-utils" +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +authors = [ + "Cyrill Leutwiler ", +] +description = "Shared build utilities of the revive compiler" + +[lib] +doctest = false + +[dependencies] diff --git a/crates/build-utils/README.md b/crates/build-utils/README.md new file mode 100644 index 0000000..6eb27ae --- /dev/null +++ b/crates/build-utils/README.md @@ -0,0 +1,10 @@ +# revive: Compiler Common + +## License + +This library is distributed under the terms of either + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/crates/build-utils/src/lib.rs b/crates/build-utils/src/lib.rs new file mode 100644 index 0000000..16a5843 --- /dev/null +++ b/crates/build-utils/src/lib.rs @@ -0,0 +1,48 @@ +//! The compiler build utilities library. + +/// The revive LLVM host dependency directory prefix environment variable. +pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_181_PREFIX"; + +/// The revive LLVM target dependency directory prefix environment variable. +pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX"; + +/// Constructs a path to the LLVM tool `name`. +/// +/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable. +pub fn llvm_host_tool(name: &str) -> std::path::PathBuf { + std::env::var_os(REVIVE_LLVM_HOST_PREFIX) + .map(Into::::into) + .unwrap_or_else(|| { + panic!( + "install LLVM using the revive-llvm builder and export {REVIVE_LLVM_HOST_PREFIX}", + ) + }) + .join("bin") + .join(name) +} + +/// Returns the LLVM lib dir. +/// +/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable. +pub fn llvm_lib_dir() -> std::path::PathBuf { + std::path::PathBuf::from(llvm_config("--libdir").trim()) +} + +/// Returns the LLVM CXX compiler flags. +/// +/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable. +pub fn llvm_cxx_flags() -> String { + llvm_config("--cxxflags") +} + +/// Execute the `llvm-config` utility respecting the [`REVIVE_LLVM_HOST_PREFIX`] environment variable. +fn llvm_config(arg: &str) -> String { + let llvm_config = llvm_host_tool("llvm-config"); + let output = std::process::Command::new(&llvm_config) + .arg(arg) + .output() + .unwrap_or_else(|error| panic!("`{} {arg}` failed: {error}", llvm_config.display())); + + String::from_utf8(output.stdout) + .unwrap_or_else(|_| panic!("output of `{} {arg}` should be utf8", llvm_config.display())) +} diff --git a/crates/builtins/Cargo.toml b/crates/builtins/Cargo.toml index dfb79bc..f0ce56c 100644 --- a/crates/builtins/Cargo.toml +++ b/crates/builtins/Cargo.toml @@ -8,4 +8,5 @@ authors.workspace = true build = "build.rs" description = "compiler builtins for the revive compiler" -[dependencies] +[build-dependencies] +revive-build-utils = { workspace = true } diff --git a/crates/builtins/build.rs b/crates/builtins/build.rs index 6078ffd..b2a54f5 100644 --- a/crates/builtins/build.rs +++ b/crates/builtins/build.rs @@ -3,25 +3,32 @@ use std::{env, fs, io::Read, path::Path, process::Command}; pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a"; fn main() { - let mut llvm_lib_dir = String::new(); + println!( + "cargo:rerun-if-env-changed={}", + revive_build_utils::REVIVE_LLVM_HOST_PREFIX + ); - Command::new("llvm-config") - .args(["--libdir"]) + let llvm_config = revive_build_utils::llvm_host_tool("llvm-config"); + let mut llvm_lib_dir = String::new(); + Command::new(&llvm_config) + .arg("--libdir") .output() - .expect("llvm-config should be able to provide LD path") + .unwrap_or_else(|_| { + panic!( + "{} should be able to provide LD path", + llvm_config.display() + ) + }) .stdout .as_slice() .read_to_string(&mut llvm_lib_dir) .expect("llvm-config output should be utf8"); - let mut lib_path = std::path::PathBuf::from(llvm_lib_dir.trim()) - .join("linux") + let lib_path = revive_build_utils::llvm_lib_dir() + .join("unknown") .join(BUILTINS_ARCHIVE_FILE); - if !lib_path.exists() { - lib_path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join(BUILTINS_ARCHIVE_FILE); - } - let archive = fs::read(lib_path).expect("clang builtins not found"); + let archive = fs::read(&lib_path).expect("clang builtins not found"); + println!("cargo:rerun-if-env-changed={}", lib_path.display()); let out_dir = env::var_os("OUT_DIR").expect("has OUT_DIR"); let archive_path = Path::new(&out_dir).join(BUILTINS_ARCHIVE_FILE); diff --git a/crates/builtins/libclang_rt.builtins-riscv32.a b/crates/builtins/libclang_rt.builtins-riscv32.a deleted file mode 100644 index f13a8848deaddcde1ea17246b2fd40f1c47ace03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316226 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%0ATAyc;|vFGykR*We;qT9l+$)>KjPSL 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 D*s)1| diff --git a/crates/lld-sys/Cargo.toml b/crates/lld-sys/Cargo.toml index abefef7..93e0444 100644 --- a/crates/lld-sys/Cargo.toml +++ b/crates/lld-sys/Cargo.toml @@ -13,3 +13,4 @@ libc = { workspace = true } [build-dependencies] cc = { workspace = true } +revive-build-utils = { workspace = true } diff --git a/crates/lld-sys/build.rs b/crates/lld-sys/build.rs index 27a1a98..c8d8252 100644 --- a/crates/lld-sys/build.rs +++ b/crates/lld-sys/build.rs @@ -1,34 +1,16 @@ -use std::{ - env, - path::{Path, PathBuf}, -}; +fn set_rustc_link_flags() { + let llvm_lib_path = match std::env::var(revive_build_utils::REVIVE_LLVM_TARGET_PREFIX) { + Ok(path) => std::path::PathBuf::from(path).join("lib"), + _ => revive_build_utils::llvm_lib_dir(), + }; -const LLVM_LINK_PREFIX: &str = "LLVM_LINK_PREFIX"; - -fn locate_llvm_config() -> PathBuf { - let prefix = env::var_os(LLVM_LINK_PREFIX) - .map(|p| PathBuf::from(p).join("bin")) - .unwrap_or_default(); - prefix.join("llvm-config") -} - -fn llvm_config(llvm_config_path: &Path, arg: &str) -> String { - let output = std::process::Command::new(llvm_config_path) - .args([arg]) - .output() - .unwrap_or_else(|_| panic!("`llvm-config {arg}` failed")); - - String::from_utf8(output.stdout) - .unwrap_or_else(|_| panic!("output of `llvm-config {arg}` should be utf8")) -} - -fn set_rustc_link_flags(llvm_config_path: &Path) { println!( "cargo:rustc-link-search=native={}", - llvm_config(llvm_config_path, "--libdir") + llvm_lib_path.to_string_lossy() ); for lib in [ + // These are required by ld.lld "lldELF", "lldCommon", "lldMachO", @@ -91,18 +73,27 @@ fn set_rustc_link_flags(llvm_config_path: &Path) { } let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); if target_os == "linux" { - println!("cargo:rustc-link-lib=dylib=stdc++"); - println!("cargo:rustc-link-lib=tinfo"); + if target_env == "musl" { + println!("cargo:rustc-link-lib=static=c++"); + } else { + println!("cargo:rustc-link-lib=dylib=stdc++"); + } } } fn main() { - println!("cargo:rerun-if-env-changed={}", LLVM_LINK_PREFIX); + println!( + "cargo:rerun-if-env-changed={}", + revive_build_utils::REVIVE_LLVM_HOST_PREFIX + ); + println!( + "cargo:rerun-if-env-changed={}", + revive_build_utils::REVIVE_LLVM_TARGET_PREFIX + ); - let llvm_config_path = locate_llvm_config(); - - llvm_config(&llvm_config_path, "--cxxflags") + revive_build_utils::llvm_cxx_flags() .split_whitespace() .fold(&mut cc::Build::new(), |builder, flag| builder.flag(flag)) .flag("-Wno-unused-parameter") @@ -110,7 +101,7 @@ fn main() { .file("src/linker.cpp") .compile("liblinker.a"); - set_rustc_link_flags(&llvm_config_path); + set_rustc_link_flags(); println!("cargo:rerun-if-changed=build.rs"); } diff --git a/crates/llvm-builder/Cargo.toml b/crates/llvm-builder/Cargo.toml new file mode 100644 index 0000000..7491fb0 --- /dev/null +++ b/crates/llvm-builder/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "revive-llvm-builder" +description = "revive LLVM compiler framework builder" +authors = [ + "Oleksandr Zarudnyi ", + "Anton Baliasnikov ", + "Cyrill Leutwiler ", +] +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true + +[[bin]] +name = "revive-llvm" +path = "src/revive_llvm/main.rs" + +[lib] +doctest = false + +[dependencies] +clap = { workspace = true, features = ["std", "derive"] } +anyhow = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } +toml = { workspace = true } +num_cpus = { workspace = true } +fs_extra = { workspace = true } +path-slash = { workspace = true } +regex = { workspace = true } +downloader = { workspace = true } +tar = { workspace = true } +flate2 = { workspace = true } +http = { workspace = true } +env_logger = { workspace = true } +log = { workspace = true } +once_cell = { workspace = true } + +[dev-dependencies] +assert_cmd = { workspace = true } +assert_fs = { workspace = true } diff --git a/crates/llvm-builder/README.md b/crates/llvm-builder/README.md new file mode 100644 index 0000000..40be01c --- /dev/null +++ b/crates/llvm-builder/README.md @@ -0,0 +1,132 @@ +# revive LLVM builder + +Parity fork of the [Matter Labs zksync LLVM builder](https://github.com/matter-labs/era-compiler-llvm-builder) helper utility for compiling [revive](https://github.com/paritytech/revive) compatible LLVM builds. + +## Installation and usage + +The LLVM compiler framework for revive must be built with our tool called `revive-llvm`. +This is because the revive compiler has requirements not fullfilled in upstream builds: +- Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables +- The RISC-V target (the PolkaVM target) +- The compiler-rt builtins for the PolkaVM target +- We want to leave the assertions always on +- Various other specific configurations and optimization may be applied + +Obtain a compatible build for your host platform from the release section of this repository (TODO). Alternatively follow below steps to get a custom build: + +
+1. Install the system prerequisites. + + * Linux (Debian): + + Install the following packages: + ```shell + apt install cmake ninja-build curl git libssl-dev pkg-config clang lld + ``` + * Linux (Arch): + + Install the following packages: + ```shell + pacman -Syu which cmake ninja curl git pkg-config clang lld + ``` + + * MacOS: + + * Install the [HomeBrew](https://brew.sh) package manager. + * Install the following packages: + + ```shell + brew install cmake ninja coreutils + ``` + + * Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. +
+ +
+2. Install Rust. + + * Follow the latest [official instructions](https://www.rust-lang.org/tools/install: + ```shell + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + . ${HOME}/.cargo/env + ``` + + > Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. +
+ +
+3. Install the revive LLVM framework builder. + + * Install the builder using `cargo`: + ```shell + cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked + ``` + + > The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. + +
+ +
+4. (Optional) Create the `LLVM.lock` file. + + * The `LLVM.lock` dictates the LLVM source tree being used. + A default `./LLVM.lock` pointing to the release used for development is already provided. + +
+ +
+5. Build LLVM. + + * Clone and build the LLVM framework using the `revive-llvm` tool. + + The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default. + + ```shell + revive-llvm clone + revive-llvm build + ``` + + Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default. + The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`. + You now need to export the final target directory `$LLVM_SYS_181_PREFIX`: `export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final` + If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`. + +
+ +## Supported target architectures + +The following target platforms are supported: +- Linux GNU (x86) +- Linux MUSL (x86) +- MacOS (aarch64) +- Windows GNU (x86) +- Emscripten (wasm32) + +
+Building for MUSL + + * Via a musl build we can build revive into fully static ELF binaries. + Which is desirable for reproducible Solidity contracts builds. + The resulting binary is also very portable, akin to the`solc` frontend binary distribution. + + Clone and build the LLVM framework using the `revive-llvm` tool: + ```shell + revive-llvm --target-env musl clone + revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld + ``` + +
+ +
+Building for Emscripten + + * Via an emsdk build we can run revive in the browser and on node.js. + + Clone and build the LLVM framework using the `revive-llvm` tool: + ```shell + revive-llvm --target-env emscripten clone + revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld + ``` + +
+ diff --git a/crates/llvm-builder/src/build_type.rs b/crates/llvm-builder/src/build_type.rs new file mode 100644 index 0000000..0c4b508 --- /dev/null +++ b/crates/llvm-builder/src/build_type.rs @@ -0,0 +1,39 @@ +//! The revive LLVM build type. + +/// The revive LLVM build type. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BuildType { + /// The debug build. + Debug, + /// The release build. + Release, + /// The release with debug info build. + RelWithDebInfo, + /// The minimal size release build. + MinSizeRel, +} + +impl std::str::FromStr for BuildType { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "Debug" => Ok(Self::Debug), + "Release" => Ok(Self::Release), + "RelWithDebInfo" => Ok(Self::RelWithDebInfo), + "MinSizeRel" => Ok(Self::MinSizeRel), + value => Err(format!("Unsupported build type: `{}`", value)), + } + } +} + +impl std::fmt::Display for BuildType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Debug => write!(f, "Debug"), + Self::Release => write!(f, "Release"), + Self::RelWithDebInfo => write!(f, "RelWithDebInfo"), + Self::MinSizeRel => write!(f, "MinSizeRel"), + } + } +} diff --git a/crates/llvm-builder/src/builtins.rs b/crates/llvm-builder/src/builtins.rs new file mode 100644 index 0000000..1f6231c --- /dev/null +++ b/crates/llvm-builder/src/builtins.rs @@ -0,0 +1,137 @@ +//! Utilities for compiling the LLVM compiler-rt builtins. + +/// Static CFLAGS variable passed to the compiler building the compiler-rt builtins. +const C_FLAGS: [&str; 6] = [ + "--target=riscv64", + "-march=rv64emac", + "-mabi=lp64e", + "-mcpu=generic-rv64", + "-nostdlib", + "-nodefaultlibs", +]; + +/// Static CMAKE arguments for building the compiler-rt builtins. +const CMAKE_STATIC_ARGS: [&str; 14] = [ + "-DCOMPILER_RT_BUILD_BUILTINS='On'", + "-DCOMPILER_RT_BUILD_LIBFUZZER='Off'", + "-DCOMPILER_RT_BUILD_MEMPROF='Off'", + "-DCOMPILER_RT_BUILD_PROFILE='Off'", + "-DCOMPILER_RT_BUILD_SANITIZERS='Off'", + "-DCOMPILER_RT_BUILD_XRAY='Off'", + "-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'", + "-DCOMPILER_RT_BAREMETAL_BUILD='On'", + "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", + "-DCMAKE_EXPORT_COMPILE_COMMANDS='On'", + "-DCMAKE_SYSTEM_NAME='unknown'", + "-DCMAKE_C_COMPILER_TARGET='riscv64'", + "-DCMAKE_ASM_COMPILER_TARGET='riscv64'", + "-DCMAKE_CXX_COMPILER_TARGET='riscv64'", +]; + +/// Dynamic cmake arguments for building the compiler-rt builtins. +fn cmake_dynamic_args( + build_type: crate::BuildType, + target_env: crate::target_env::TargetEnv, +) -> anyhow::Result<[String; 13]> { + let llvm_compiler_rt_target = crate::LLVMPath::llvm_target_compiler_rt()?; + + // The Emscripten target needs to use the host LLVM tools. + let llvm_target_host = if target_env == crate::target_env::TargetEnv::Emscripten { + crate::LLVMPath::llvm_build_host()? + } else { + crate::LLVMPath::llvm_target_final()? + }; + + let mut clang_path = llvm_target_host.to_path_buf(); + clang_path.push("bin/clang"); + + let mut clangxx_path = llvm_target_host.to_path_buf(); + clangxx_path.push("bin/clang++"); + + let mut llvm_config_path = llvm_target_host.to_path_buf(); + llvm_config_path.push("bin/llvm-config"); + + let mut ar_path = llvm_target_host.to_path_buf(); + ar_path.push("bin/llvm-ar"); + + let mut nm_path = llvm_target_host.to_path_buf(); + nm_path.push("bin/llvm-nm"); + + let mut ranlib_path = llvm_target_host.to_path_buf(); + ranlib_path.push("bin/llvm-ranlib"); + + let mut linker_path = llvm_target_host.to_path_buf(); + linker_path.push("bin/ld.lld"); + + Ok([ + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_compiler_rt_target.to_string_lossy().as_ref(), + ), + format!("-DCMAKE_BUILD_TYPE='{build_type}'"), + format!( + "-DCOMPILER_RT_TEST_COMPILER='{}'", + clang_path.to_string_lossy() + ), + format!("-DCMAKE_C_FLAGS='{}'", C_FLAGS.join(" ")), + format!("-DCMAKE_ASM_FLAGS='{}'", C_FLAGS.join(" ")), + format!("-DCMAKE_CXX_FLAGS='{}'", C_FLAGS.join(" ")), + format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()), + format!("-DCMAKE_ASM_COMPILER='{}'", clang_path.to_string_lossy()), + format!("-DCMAKE_CXX_COMPILER='{}'", clangxx_path.to_string_lossy()), + format!("-DCMAKE_AR='{}'", ar_path.to_string_lossy()), + format!("-DCMAKE_NM='{}'", nm_path.to_string_lossy()), + format!("-DCMAKE_RANLIB='{}'", ranlib_path.to_string_lossy()), + format!( + "-DLLVM_CONFIG_PATH='{}'", + llvm_config_path.to_string_lossy() + ), + ]) +} + +/// Build the compiler-rt builtins library. +pub fn build( + build_type: crate::BuildType, + target_env: crate::target_env::TargetEnv, + default_target: Option, + extra_args: &[String], + ccache_variant: Option, + sanitizer: Option, +) -> anyhow::Result<()> { + log::info!("building compiler-rt for rv64emac"); + + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?; + let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?; + + crate::utils::command( + std::process::Command::new("cmake") + .args([ + "-S", + llvm_module_compiler_rt.to_string_lossy().as_ref(), + "-B", + llvm_compiler_rt_build.to_string_lossy().as_ref(), + "-G", + "Ninja", + ]) + .args(CMAKE_STATIC_ARGS) + .args(cmake_dynamic_args(build_type, target_env)?) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )), + "LLVM compiler-rt building cmake", + )?; + + crate::utils::ninja(&llvm_compiler_rt_build)?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/ccache_variant.rs b/crates/llvm-builder/src/ccache_variant.rs new file mode 100644 index 0000000..3dc1091 --- /dev/null +++ b/crates/llvm-builder/src/ccache_variant.rs @@ -0,0 +1,31 @@ +//! Compiler cache variants. + +/// The list compiler cache variants to be used as constants. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CcacheVariant { + /// Standard ccache. + Ccache, + /// Mozilla's sccache. + Sccache, +} + +impl std::str::FromStr for CcacheVariant { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "ccache" => Ok(Self::Ccache), + "sccache" => Ok(Self::Sccache), + value => Err(format!("Unsupported ccache variant: `{}`", value)), + } + } +} + +impl std::fmt::Display for CcacheVariant { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Ccache => write!(f, "ccache"), + Self::Sccache => write!(f, "sccache"), + } + } +} diff --git a/crates/llvm-builder/src/lib.rs b/crates/llvm-builder/src/lib.rs new file mode 100644 index 0000000..75369c8 --- /dev/null +++ b/crates/llvm-builder/src/lib.rs @@ -0,0 +1,340 @@ +//! The revive LLVM builder library. + +pub mod build_type; +pub mod builtins; +pub mod ccache_variant; +pub mod llvm_path; +pub mod llvm_project; +pub mod lock; +pub mod platforms; +pub mod sanitizer; +pub mod target_env; +pub mod target_triple; +pub mod utils; + +pub use self::build_type::BuildType; +pub use self::llvm_path::LLVMPath; +pub use self::lock::Lock; +pub use self::platforms::Platform; + +use std::collections::HashSet; +use std::path::{Path, PathBuf}; +use std::process::Command; +pub use target_env::TargetEnv; +pub use target_triple::TargetTriple; + +/// Executes the LLVM repository cloning. +pub fn clone(lock: Lock, deep: bool, target_env: TargetEnv) -> anyhow::Result<()> { + utils::check_presence("git")?; + + if target_env == TargetEnv::Emscripten { + utils::install_emsdk()?; + } + + let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE); + if destination_path.exists() { + log::warn!( + "LLVM repository directory {} already exists, falling back to checkout", + destination_path.display() + ); + return checkout(lock, false); + } + + let mut clone_args = vec!["clone", "--branch", lock.branch.as_str()]; + if !deep { + clone_args.push("--depth"); + clone_args.push("1"); + } + + utils::command( + Command::new("git") + .args(clone_args) + .arg(lock.url.as_str()) + .arg(destination_path.to_string_lossy().as_ref()), + "LLVM repository cloning", + )?; + + if let Some(r#ref) = lock.r#ref { + utils::command( + Command::new("git") + .args(["checkout", r#ref.as_str()]) + .current_dir(destination_path.to_string_lossy().as_ref()), + "LLVM repository commit checking out", + )?; + } + + Ok(()) +} + +/// Executes the checkout of the specified branch. +pub fn checkout(lock: Lock, force: bool) -> anyhow::Result<()> { + let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE); + + utils::command( + Command::new("git") + .current_dir(destination_path.as_path()) + .args(["fetch", "--all", "--tags"]), + "LLVM repository data fetching", + )?; + + if force { + utils::command( + Command::new("git") + .current_dir(destination_path.as_path()) + .args(["clean", "-d", "-x", "--force"]), + "LLVM repository cleaning", + )?; + } + + utils::command( + Command::new("git") + .current_dir(destination_path.as_path()) + .args(["checkout", "--force", lock.branch.as_str()]), + "LLVM repository data pulling", + )?; + + if let Some(r#ref) = lock.r#ref { + let mut checkout_command = Command::new("git"); + checkout_command.current_dir(destination_path.as_path()); + checkout_command.arg("checkout"); + if force { + checkout_command.arg("--force"); + } + checkout_command.arg(r#ref); + utils::command(&mut checkout_command, "LLVM repository checking out")?; + } + + Ok(()) +} + +/// Executes the building of the LLVM framework for the platform determined by the cfg macro. +/// Since cfg is evaluated at compile time, overriding the platform with a command-line +/// argument is not possible. So for cross-platform testing, comment out all but the +/// line to be tested, and perhaps also checks in the platform-specific build method. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + target_env: TargetEnv, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + log::trace!("build type: {:?}", build_type); + log::trace!("target env: {:?}", target_env); + log::trace!("targets: {:?}", targets); + log::trace!("llvm projects: {:?}", llvm_projects); + log::trace!("enable rtti: {:?}", enable_rtti); + log::trace!("default target: {:?}", default_target); + log::trace!("eneable tests: {:?}", enable_tests); + log::trace!("enable_coverage: {:?}", enable_coverage); + log::trace!("extra args: {:?}", extra_args); + log::trace!("sanitzer: {:?}", sanitizer); + log::trace!("enable valgrind: {:?}", enable_valgrind); + + if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() { + log::error!( + "LLVM project source directory {} does not exist (run `revive-llvm --target-env {} clone`)", + LLVMPath::DIRECTORY_LLVM_SOURCE, + target_env + ) + } + + std::fs::create_dir_all(llvm_path::DIRECTORY_LLVM_TARGET.get().unwrap())?; + + if cfg!(target_arch = "x86_64") { + if cfg!(target_os = "linux") { + if target_env == TargetEnv::MUSL { + platforms::x86_64_linux_musl::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else if target_env == TargetEnv::GNU { + platforms::x86_64_linux_gnu::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else if target_env == TargetEnv::Emscripten { + platforms::wasm32_emscripten::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else { + anyhow::bail!("Unsupported target environment for x86_64 and Linux"); + } + } else if cfg!(target_os = "macos") { + platforms::x86_64_macos::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + )?; + } else if cfg!(target_os = "windows") { + platforms::x86_64_windows_gnu::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + )?; + } else { + anyhow::bail!("Unsupported target OS for x86_64"); + } + } else if cfg!(target_arch = "aarch64") { + if cfg!(target_os = "linux") { + if target_env == TargetEnv::MUSL { + platforms::aarch64_linux_musl::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else if target_env == TargetEnv::GNU { + platforms::aarch64_linux_gnu::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else { + anyhow::bail!("Unsupported target environment for aarch64 and Linux"); + } + } else if cfg!(target_os = "macos") { + if target_env == TargetEnv::Emscripten { + platforms::wasm32_emscripten::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } else { + platforms::aarch64_macos::build( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + )?; + } + } else { + anyhow::bail!("Unsupported target OS for aarch64"); + } + } else { + anyhow::bail!("Unsupported target architecture"); + } + + crate::builtins::build( + build_type, + target_env, + default_target, + extra_args, + ccache_variant, + sanitizer, + )?; + + Ok(()) +} + +/// Executes the build artifacts cleaning. +pub fn clean() -> anyhow::Result<()> { + let remove_if_exists = |path: &Path| { + if !path.exists() { + return Ok(()); + } + log::info!("deleting {}", path.display()); + std::fs::remove_dir_all(path) + }; + + remove_if_exists( + llvm_path::DIRECTORY_LLVM_TARGET + .get() + .expect("target_env is always set because of the default value") + .parent() + .expect("target_env parent directory is target-llvm"), + )?; + remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_EMSDK_SOURCE))?; + remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE))?; + remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_HOST_SOURCE))?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/llvm_path.rs b/crates/llvm-builder/src/llvm_path.rs new file mode 100644 index 0000000..4b70d09 --- /dev/null +++ b/crates/llvm-builder/src/llvm_path.rs @@ -0,0 +1,151 @@ +//! The revive LLVM builder constants. + +use std::path::PathBuf; +use std::sync::OnceLock; + +pub static DIRECTORY_LLVM_TARGET: OnceLock = OnceLock::new(); + +/// The LLVM path resolver. +pub struct LLVMPath {} + +impl LLVMPath { + /// The LLVM source directory. + pub const DIRECTORY_LLVM_SOURCE: &'static str = "./llvm/"; + + /// The LLVM host source directory for stage 1 of multistage MUSL and Emscripten builds. + /// + /// We use upstream LLVM anyways; re-use the same tree for host and target builds. + pub const DIRECTORY_LLVM_HOST_SOURCE: &'static str = Self::DIRECTORY_LLVM_SOURCE; + + /// The Emscripten SDK source directory. + pub const DIRECTORY_EMSDK_SOURCE: &'static str = "./emsdk/"; + + /// Returns the path to the `llvm` stage 1 host LLVM source module directory. + pub fn llvm_host_module_llvm() -> anyhow::Result { + let mut path = PathBuf::from(Self::DIRECTORY_LLVM_HOST_SOURCE); + path.push("llvm"); + crate::utils::absolute_path(path).inspect(|absolute_path| { + log::debug!( + "llvm stage 1 host llvm source module: {}", + absolute_path.display() + ) + }) + } + + /// Returns the path to the `llvm` LLVM source module directory. + pub fn llvm_module_llvm() -> anyhow::Result { + let mut path = PathBuf::from(Self::DIRECTORY_LLVM_SOURCE); + path.push("llvm"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm source module: {}", absolute_path.display())) + } + + /// Returns the path to the MUSL source. + pub fn musl_source(name: &str) -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push(name); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("musl source: {}", absolute_path.display())) + } + + /// Returns the path to the MUSL build directory. + pub fn musl_build(source_directory: &str) -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push(source_directory); + path.push("build"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("musl build: '{}'", absolute_path.display())) + } + + /// Returns the path to the LLVM CRT build directory. + pub fn llvm_build_crt() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("build-crt"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm build crt: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM host build directory. + pub fn llvm_build_host() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("build-host"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm build host: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM final build directory. + pub fn llvm_build_final() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("build-final"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm build final: {}", absolute_path.display())) + } + + /// Returns the path to the MUSL target directory. + pub fn musl_target() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("target-musl"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("musl target: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM CRT target directory. + pub fn llvm_target_crt() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("target-crt"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm crt target: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM host target directory. + pub fn llvm_target_host() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("target-host"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm host target: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM final target directory. + pub fn llvm_target_final() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("target-final"); + crate::utils::absolute_path(path) + .inspect(|absolute_path| log::debug!("llvm final target: {}", absolute_path.display())) + } + + /// Returns the path to the LLVM compiler builtin target directory. + pub fn llvm_module_compiler_rt() -> anyhow::Result { + let mut path = PathBuf::from(Self::DIRECTORY_LLVM_SOURCE); + path.push("compiler-rt"); + crate::utils::absolute_path(path).inspect(|absolute_path| { + log::debug!("compiler-rt source dir: {}", absolute_path.display()) + }) + } + + /// Returns the path to the LLVM compiler-rt target directory. + pub fn llvm_target_compiler_rt() -> anyhow::Result { + Self::llvm_target_final() + } + + /// Returns the path to the LLVM compiler-rt build directory. + pub fn llvm_build_compiler_rt() -> anyhow::Result { + let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap()); + path.push("build-compiler-rt"); + crate::utils::absolute_path(path).inspect(|absolute_path| { + log::debug!("llvm compiler-rt build: {}", absolute_path.display()) + }) + } + + /// Returns the path to the LLVM target final bin path. + /// + pub fn llvm_target_final_bin( + target_env: crate::target_env::TargetEnv, + ) -> anyhow::Result { + let mut path = Self::llvm_target_final()?; + path.push("bin"); + path.push(format!("{target_env}")); + crate::utils::absolute_path(path).inspect(|absolute_path| { + log::debug!("llvm target final bin: {}", absolute_path.display()) + }) + } +} diff --git a/crates/llvm-builder/src/llvm_project.rs b/crates/llvm-builder/src/llvm_project.rs new file mode 100644 index 0000000..0a6a35a --- /dev/null +++ b/crates/llvm-builder/src/llvm_project.rs @@ -0,0 +1,39 @@ +//! The LLVM projects to enable during the build. + +/// The list of LLVM projects used as constants. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum LLVMProject { + /// The Clang compiler. + CLANG, + /// LLD, the LLVM linker. + LLD, + /// The LLVM debugger. + LLDB, + /// The MLIR compiler. + MLIR, +} + +impl std::str::FromStr for LLVMProject { + type Err = String; + + fn from_str(value: &str) -> Result { + match value.to_lowercase().as_str() { + "clang" => Ok(Self::CLANG), + "lld" => Ok(Self::LLD), + "lldb" => Ok(Self::LLDB), + "mlir" => Ok(Self::MLIR), + value => Err(format!("Unsupported LLVM project to enable: `{}`", value)), + } + } +} + +impl std::fmt::Display for LLVMProject { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::CLANG => write!(f, "clang"), + Self::LLD => write!(f, "lld"), + Self::LLDB => write!(f, "lldb"), + Self::MLIR => write!(f, "mlir"), + } + } +} diff --git a/crates/llvm-builder/src/lock.rs b/crates/llvm-builder/src/lock.rs new file mode 100644 index 0000000..b04004c --- /dev/null +++ b/crates/llvm-builder/src/lock.rs @@ -0,0 +1,37 @@ +//! The revive LLVM builder lock file. + +use anyhow::Context; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +use serde::Deserialize; +use serde::Serialize; + +/// The default lock file location. +pub const LLVM_LOCK_DEFAULT_PATH: &str = "LLVM.lock"; + +/// The lock file data. +/// +/// This file describes the exact reference of the LLVM framework. +#[derive(Debug, Deserialize, Serialize)] +pub struct Lock { + /// The LLVM repository URL. + pub url: String, + /// The LLVM repository branch. + pub branch: String, + /// The LLVM repository commit reference. + pub r#ref: Option, +} + +impl TryFrom<&PathBuf> for Lock { + type Error = anyhow::Error; + + fn try_from(path: &PathBuf) -> Result { + let mut config_str = String::new(); + let mut config_file = + File::open(path).with_context(|| format!("Error opening {:?} file", path))?; + config_file.read_to_string(&mut config_str)?; + Ok(toml::from_str(&config_str)?) + } +} diff --git a/crates/llvm-builder/src/platforms/aarch64_linux_gnu.rs b/crates/llvm-builder/src/platforms/aarch64_linux_gnu.rs new file mode 100644 index 0000000..4bda3b7 --- /dev/null +++ b/crates/llvm-builder/src/platforms/aarch64_linux_gnu.rs @@ -0,0 +1,111 @@ +//! The revive LLVM arm64 `linux-gnu` builder. + +use std::collections::HashSet; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + crate::utils::check_presence("lld")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + llvm_module_llvm.to_string_lossy().as_ref(), + "-B", + llvm_build_final.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_target_final.to_string_lossy().as_ref(), + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + "-DLLVM_USE_LINKER='lld'", + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::GNU, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )) + .args(crate::platforms::shared::shared_build_opts_valgrind( + enable_valgrind, + )), + "LLVM building cmake", + )?; + crate::utils::ninja(llvm_build_final.as_ref())?; + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/aarch64_linux_musl.rs b/crates/llvm-builder/src/platforms/aarch64_linux_musl.rs new file mode 100644 index 0000000..dd33a12 --- /dev/null +++ b/crates/llvm-builder/src/platforms/aarch64_linux_musl.rs @@ -0,0 +1,394 @@ +//! The revive LLVM arm64 `linux-musl` builder. + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; +use std::collections::HashSet; +use std::path::Path; +use std::process::Command; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + crate::utils::check_presence("lld")?; + crate::utils::check_presence("ninja")?; + + let musl_name = "musl-1.2.3"; + let musl_build = LLVMPath::musl_build(musl_name)?; + let musl_target = LLVMPath::musl_target()?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_host_module_llvm = LLVMPath::llvm_host_module_llvm()?; + + let llvm_build_crt = LLVMPath::llvm_build_crt()?; + let llvm_target_crt = LLVMPath::llvm_target_crt()?; + + let llvm_build_host = LLVMPath::llvm_build_host()?; + let llvm_target_host = LLVMPath::llvm_target_host()?; + + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + if !LLVMPath::musl_source(musl_name)?.exists() { + crate::utils::download_musl(musl_name)?; + } + crate::platforms::shared::build_musl(musl_build.as_path(), musl_target.as_path())?; + build_crt( + targets.clone(), + llvm_host_module_llvm.as_path(), + llvm_build_crt.as_path(), + llvm_target_crt.as_path(), + ccache_variant, + )?; + build_host( + llvm_host_module_llvm.as_path(), + llvm_build_host.as_path(), + llvm_target_host.as_path(), + musl_target.as_path(), + llvm_target_crt.as_path(), + ccache_variant, + )?; + build_target( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + llvm_module_llvm.as_path(), + llvm_build_final.as_path(), + llvm_target_final.as_path(), + musl_target.as_path(), + llvm_target_host.as_path(), + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + + Ok(()) +} + +/// +/// The `crt` building sequence. +/// +fn build_crt( + mut targets: HashSet, + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + ccache_variant: Option, +) -> anyhow::Result<()> { + targets.insert(Platform::AArch64); + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + "-DCMAKE_BUILD_TYPE='Release'", + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + "-DLLVM_ENABLE_PROJECTS='compiler-rt'", + format!("-DLLVM_TARGETS_TO_BUILD='{}'", Platform::AArch64).as_str(), + "-DLLVM_DEFAULT_TARGET_TRIPLE='aarch64-unknown-linux-musl'", + "-DLLVM_BUILD_TESTS='Off'", + "-DLLVM_BUILD_RUNTIMES='Off'", + "-DLLVM_BUILD_UTILS='Off'", + "-DLLVM_INCLUDE_TESTS='Off'", + "-DLLVM_INCLUDE_RUNTIMES='Off'", + "-DLLVM_INCLUDE_UTILS='Off'", + "-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'", + "-DCOMPILER_RT_BUILD_CRT='On'", + "-DCOMPILER_RT_BUILD_BUILTINS='On'", + "-DCOMPILER_RT_BUILD_SANITIZERS='Off'", + "-DCOMPILER_RT_BUILD_XRAY='Off'", + "-DCOMPILER_RT_BUILD_LIBFUZZER='Off'", + "-DCOMPILER_RT_BUILD_PROFILE='Off'", + "-DCOMPILER_RT_BUILD_MEMPROF='Off'", + "-DCOMPILER_RT_BUILD_ORC='Off'", + ]) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )), + "CRT building cmake", + )?; + + crate::utils::command( + Command::new("ninja") + .arg("-C") + .arg(build_directory) + .arg("install-crt"), + "CRT building ninja", + )?; + + Ok(()) +} + +/// +/// The host toolchain building sequence. +/// +fn build_host( + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + musl_target_directory: &Path, + crt_target_directory: &Path, + ccache_variant: Option, +) -> anyhow::Result<()> { + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DDEFAULT_SYSROOT='{}'", + musl_target_directory.to_string_lossy() + ) + .as_str(), + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + "-DCMAKE_BUILD_TYPE='Release'", + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + "-DCLANG_DEFAULT_CXX_STDLIB='libc++'", + "-DCLANG_DEFAULT_RTLIB='compiler-rt'", + "-DLLVM_DEFAULT_TARGET_TRIPLE='aarch64-unknown-linux-musl'", + "-DLLVM_TARGETS_TO_BUILD='AArch64'", + "-DLLVM_BUILD_TESTS='Off'", + "-DLLVM_BUILD_UTILS='Off'", + "-DLLVM_INCLUDE_TESTS='Off'", + "-DLLVM_INCLUDE_UTILS='Off'", + "-DLLVM_ENABLE_PROJECTS='clang;lld'", + "-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'", + "-DLIBCXX_CXX_ABI='libcxxabi'", + "-DLIBCXX_HAS_MUSL_LIBC='On'", + "-DLIBCXX_ENABLE_SHARED='Off'", + "-DLIBCXX_ENABLE_STATIC='On'", + "-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY='On'", + "-DLIBCXXABI_ENABLE_SHARED='Off'", + "-DLIBCXXABI_ENABLE_STATIC='On'", + "-DLIBCXXABI_ENABLE_STATIC_UNWINDER='On'", + "-DLIBCXXABI_USE_LLVM_UNWINDER='On'", + "-DLIBCXXABI_USE_COMPILER_RT='On'", + "-DLIBUNWIND_ENABLE_STATIC='On'", + "-DLIBUNWIND_ENABLE_SHARED='Off'", + "-DCOMPILER_RT_BUILD_CRT='On'", + "-DCOMPILER_RT_BUILD_SANITIZERS='Off'", + "-DCOMPILER_RT_BUILD_XRAY='Off'", + "-DCOMPILER_RT_BUILD_LIBFUZZER='Off'", + "-DCOMPILER_RT_BUILD_PROFILE='On'", + "-DCOMPILER_RT_BUILD_MEMPROF='Off'", + "-DCOMPILER_RT_BUILD_ORC='Off'", + "-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'", + "-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'", + ]) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )), + "LLVM host building cmake", + )?; + + let mut crt_lib_directory = crt_target_directory.to_path_buf(); + crt_lib_directory.push("lib/"); + + let mut build_lib_directory = build_directory.to_path_buf(); + build_lib_directory.push("lib/"); + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + content_only: true, + ..Default::default() + }; + fs_extra::dir::copy(crt_lib_directory, build_lib_directory, ©_options)?; + + crate::utils::command( + Command::new("ninja") + .arg("-C") + .arg(build_directory) + .arg("install"), + "LLVM host building ninja", + )?; + + Ok(()) +} + +/// +/// The target toolchain building sequence. +/// +#[allow(clippy::too_many_arguments)] +fn build_target( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + musl_target_directory: &Path, + host_target_directory: &Path, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + let mut clang_path = host_target_directory.to_path_buf(); + clang_path.push("bin/clang"); + + let mut clang_cxx_path = host_target_directory.to_path_buf(); + clang_cxx_path.push("bin/clang++"); + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + "-DBUILD_SHARED_LIBS='Off'", + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()).as_str(), + format!( + "-DCMAKE_CXX_COMPILER='{}'", + clang_cxx_path.to_string_lossy() + ) + .as_str(), + "-DCMAKE_FIND_LIBRARY_SUFFIXES='.a'", + "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", + "-DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -static'", + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::MUSL, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )) + .args(crate::platforms::shared::shared_build_opts_valgrind( + enable_valgrind, + )), + "LLVM target building cmake", + )?; + + crate::utils::ninja(build_directory)?; + + let mut musl_lib_directory = musl_target_directory.to_path_buf(); + musl_lib_directory.push("lib/"); + + let mut host_lib_directory = host_target_directory.to_path_buf(); + host_lib_directory.push("lib/aarch64-unknown-linux-musl/"); + + let mut target_lib_directory = target_directory.to_path_buf(); + target_lib_directory.push("lib/"); + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + content_only: true, + ..Default::default() + }; + fs_extra::dir::copy( + musl_lib_directory, + target_lib_directory.as_path(), + ©_options, + )?; + fs_extra::dir::copy( + host_lib_directory, + target_lib_directory.as_path(), + ©_options, + )?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/aarch64_macos.rs b/crates/llvm-builder/src/platforms/aarch64_macos.rs new file mode 100644 index 0000000..a33b827 --- /dev/null +++ b/crates/llvm-builder/src/platforms/aarch64_macos.rs @@ -0,0 +1,105 @@ +//! The revive LLVM arm64 `macos-aarch64` builder. + +use std::collections::HashSet; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + llvm_module_llvm.to_string_lossy().as_ref(), + "-B", + llvm_build_final.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_target_final.to_string_lossy().as_ref(), + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + "-DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'", + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::GNU, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::macos_build_opts_ignore_dupicate_libs_warnings()) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )), + "LLVM building cmake", + )?; + + crate::utils::ninja(llvm_build_final.as_ref())?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/mod.rs b/crates/llvm-builder/src/platforms/mod.rs new file mode 100644 index 0000000..4164c5a --- /dev/null +++ b/crates/llvm-builder/src/platforms/mod.rs @@ -0,0 +1,45 @@ +//! The revive LLVM builder platforms. + +pub mod aarch64_linux_gnu; +pub mod aarch64_linux_musl; +pub mod aarch64_macos; +pub mod shared; +pub mod wasm32_emscripten; +pub mod x86_64_linux_gnu; +pub mod x86_64_linux_musl; +pub mod x86_64_macos; +pub mod x86_64_windows_gnu; + +use std::str::FromStr; + +/// The list of platforms used as constants. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Platform { + /// The native X86 platform. + X86, + /// The native AArch64 platform. + AArch64, + /// The PolkaVM RISC-V platform. + PolkaVM, +} + +impl FromStr for Platform { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "PolkaVM" => Ok(Self::PolkaVM), + value => Err(format!("Unsupported platform: `{}`", value)), + } + } +} + +impl std::fmt::Display for Platform { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::X86 => write!(f, "X86"), + Self::AArch64 => write!(f, "AArch64"), + Self::PolkaVM => write!(f, "RISCV"), + } + } +} diff --git a/crates/llvm-builder/src/platforms/shared.rs b/crates/llvm-builder/src/platforms/shared.rs new file mode 100644 index 0000000..71e2e03 --- /dev/null +++ b/crates/llvm-builder/src/platforms/shared.rs @@ -0,0 +1,232 @@ +//! The shared options for building various platforms. + +use crate::ccache_variant::CcacheVariant; +use crate::sanitizer::Sanitizer; +use crate::target_env::TargetEnv; +use crate::target_triple::TargetTriple; +use std::path::Path; +use std::process::Command; + +/// The build options shared by all platforms. +pub const SHARED_BUILD_OPTS: [&str; 19] = [ + "-DPACKAGE_VENDOR='Parity Technologies'", + "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", + "-DLLVM_BUILD_DOCS='Off'", + "-DLLVM_INCLUDE_DOCS='Off'", + "-DLLVM_INCLUDE_BENCHMARKS='Off'", + "-DLLVM_INCLUDE_EXAMPLES='Off'", + "-DLLVM_ENABLE_DOXYGEN='Off'", + "-DLLVM_ENABLE_SPHINX='Off'", + "-DLLVM_ENABLE_OCAMLDOC='Off'", + "-DLLVM_ENABLE_ZLIB='Off'", + "-DLLVM_ENABLE_ZSTD='Off'", + "-DLLVM_ENABLE_LIBXML2='Off'", + "-DLLVM_ENABLE_BINDINGS='Off'", + "-DLLVM_ENABLE_TERMINFO='Off'", + "-DLLVM_ENABLE_LIBEDIT='Off'", + "-DLLVM_ENABLE_LIBPFM='Off'", + "-DCMAKE_EXPORT_COMPILE_COMMANDS='On'", + "-DPython3_FIND_REGISTRY='LAST'", // Use Python version from $PATH, not from registry + "-DBUG_REPORT_URL='https://github.com/paritytech/contract-issues/issues/'", +]; + +/// The build options shared by all platforms except MUSL. +pub const SHARED_BUILD_OPTS_NOT_MUSL: [&str; 4] = [ + "-DLLVM_OPTIMIZED_TABLEGEN='On'", + "-DLLVM_BUILD_RUNTIME='Off'", + "-DLLVM_BUILD_RUNTIMES='Off'", + "-DLLVM_INCLUDE_RUNTIMES='Off'", +]; + +/// The shared build options to treat warnings as errors. +/// +/// Disabled on Windows due to the following upstream issue with MSYS2 with mingw-w64: +/// ProgramTest.cpp:23:15: error: '__p__environ' redeclared without 'dllimport' attribute +pub fn shared_build_opts_werror(target_env: TargetEnv) -> Vec { + vec![format!( + "-DLLVM_ENABLE_WERROR='{}'", + if cfg!(target_os = "windows") || target_env == TargetEnv::Emscripten { + "Off" + } else { + "On" + }, + )] +} + +/// The build options to set the default target. +pub fn shared_build_opts_default_target(target: Option) -> Vec { + match target { + Some(target) => vec![format!( + "-DLLVM_DEFAULT_TARGET_TRIPLE='{}'", + target.to_string() + )], + None => vec![format!( + "-DLLVM_DEFAULT_TARGET_TRIPLE='{}'", + TargetTriple::PolkaVM + )], + } +} + +/// The `musl` building sequence. +pub fn build_musl(build_directory: &Path, target_directory: &Path) -> anyhow::Result<()> { + std::fs::create_dir_all(build_directory)?; + std::fs::create_dir_all(target_directory)?; + + crate::utils::command( + Command::new("../configure") + .current_dir(build_directory) + .arg(format!("--prefix={}", target_directory.to_string_lossy())) + .arg(format!( + "--syslibdir={}/lib/", + target_directory.to_string_lossy() + )) + .arg("--enable-wrapper='clang'"), + "MUSL configuring", + )?; + crate::utils::command( + Command::new("make") + .current_dir(build_directory) + .arg("-j") + .arg(num_cpus::get().to_string()), + "MUSL building", + )?; + crate::utils::command( + Command::new("make") + .current_dir(build_directory) + .arg("install"), + "MUSL installing", + )?; + + let mut include_directory = target_directory.to_path_buf(); + include_directory.push("include/"); + + let mut asm_include_directory = include_directory.clone(); + asm_include_directory.push("asm/"); + std::fs::create_dir_all(asm_include_directory.as_path())?; + + let mut types_header_path = asm_include_directory.clone(); + types_header_path.push("types.h"); + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + ..Default::default() + }; + fs_extra::dir::copy("/usr/include/linux", include_directory, ©_options)?; + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + content_only: true, + ..Default::default() + }; + fs_extra::dir::copy( + "/usr/include/asm-generic", + asm_include_directory, + ©_options, + )?; + + crate::utils::command( + Command::new("sed") + .arg("-i") + .arg("s/asm-generic/asm/") + .arg(types_header_path), + "types_header asm signature replacement", + )?; + + Ok(()) +} + +/// The build options to enable assertions. +pub fn shared_build_opts_assertions(enabled: bool) -> Vec { + vec![format!( + "-DLLVM_ENABLE_ASSERTIONS='{}'", + if enabled { "On" } else { "Off" }, + )] +} + +/// The build options to build with RTTI support. +pub fn shared_build_opts_rtti(enabled: bool) -> Vec { + vec![format!( + "-DLLVM_ENABLE_RTTI='{}'", + if enabled { "On" } else { "Off" }, + )] +} + +/// The build options to enable sanitizers. +pub fn shared_build_opts_sanitizers(sanitizer: Option) -> Vec { + match sanitizer { + Some(sanitizer) => vec![format!("-DLLVM_USE_SANITIZER='{}'", sanitizer)], + None => vec![], + } +} + +/// The build options to enable Valgrind for LLVM regression tests. +pub fn shared_build_opts_valgrind(enabled: bool) -> Vec { + if enabled { + vec!["-DLLVM_LIT_ARGS='-sv --vg --vg-leak'".to_owned()] + } else { + vec![] + } +} + +/// The LLVM tests build options shared by all platforms. +pub fn shared_build_opts_tests(enabled: bool) -> Vec { + vec![ + format!( + "-DLLVM_BUILD_UTILS='{}'", + if enabled { "On" } else { "Off" }, + ), + format!( + "-DLLVM_BUILD_TESTS='{}'", + if enabled { "On" } else { "Off" }, + ), + format!( + "-DLLVM_INCLUDE_UTILS='{}'", + if enabled { "On" } else { "Off" }, + ), + format!( + "-DLLVM_INCLUDE_TESTS='{}'", + if enabled { "On" } else { "Off" }, + ), + ] +} + +/// The code coverage build options shared by all platforms. +pub fn shared_build_opts_coverage(enabled: bool) -> Vec { + vec![format!( + "-DLLVM_BUILD_INSTRUMENTED_COVERAGE='{}'", + if enabled { "On" } else { "Off" }, + )] +} + +/// Use of compiler cache (ccache) to speed up the build process. +pub fn shared_build_opts_ccache(ccache_variant: Option) -> Vec { + match ccache_variant { + Some(ccache_variant) => vec![ + format!( + "-DCMAKE_C_COMPILER_LAUNCHER='{}'", + ccache_variant.to_string() + ), + format!( + "-DCMAKE_CXX_COMPILER_LAUNCHER='{}'", + ccache_variant.to_string() + ), + ], + None => vec![], + } +} + +/// Ignore duplicate libraries warnings for MacOS with XCode>=15. +pub fn macos_build_opts_ignore_dupicate_libs_warnings() -> Vec { + let xcode_version = + crate::utils::get_xcode_version().unwrap_or(crate::utils::XCODE_MIN_VERSION); + if xcode_version >= crate::utils::XCODE_VERSION_15 { + vec![ + "-DCMAKE_EXE_LINKER_FLAGS='-Wl,-no_warn_duplicate_libraries'".to_owned(), + "-DCMAKE_SHARED_LINKER_FLAGS='-Wl,-no_warn_duplicate_libraries'".to_owned(), + ] + } else { + vec![] + } +} diff --git a/crates/llvm-builder/src/platforms/wasm32_emscripten.rs b/crates/llvm-builder/src/platforms/wasm32_emscripten.rs new file mode 100644 index 0000000..5b209c2 --- /dev/null +++ b/crates/llvm-builder/src/platforms/wasm32_emscripten.rs @@ -0,0 +1,224 @@ +//! The revive LLVM `wasm32_unknown_emscripten` builder. +//! +//! Cross-compiling LLVM for Emscripten requires llvm-tblgen, clang-tblgen and llvm-config. + +use std::{collections::HashSet, path::Path, process::Command}; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: crate::BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("ninja")?; + crate::utils::check_presence("emsdk")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + if cfg!(target_os = "linux") { + crate::utils::check_presence("lld")?; + } + + let llvm_module_llvm = crate::LLVMPath::llvm_module_llvm()?; + let llvm_host_module_llvm = crate::LLVMPath::llvm_host_module_llvm()?; + + let llvm_build_host = crate::LLVMPath::llvm_build_host()?; + let llvm_target_host = crate::LLVMPath::llvm_target_host()?; + + let llvm_build_final = crate::LLVMPath::llvm_build_final()?; + let llvm_target_final = crate::LLVMPath::llvm_target_final()?; + + build_host( + llvm_host_module_llvm.as_path(), + llvm_build_host.as_path(), + llvm_target_host.as_path(), + ccache_variant, + )?; + + build_target( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + llvm_module_llvm.as_path(), + llvm_build_final.as_path(), + llvm_target_final.as_path(), + llvm_target_host.as_path(), + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + + Ok(()) +} + +/// The host toolchain building sequence. +fn build_host( + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + ccache_variant: Option, +) -> anyhow::Result<()> { + log::info!("building the LLVM Emscripten host utilities"); + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + &format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ), + "-DLLVM_BUILD_SHARED_LIBS='Off'", + "-DCMAKE_BUILD_TYPE='Release'", + &format!( + "-DLLVM_TARGETS_TO_BUILD='WebAssembly;{}'", + crate::Platform::PolkaVM + ), + "-DLLVM_ENABLE_PROJECTS='clang;lld'", + ]) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )), + "LLVM host building cmake config", + )?; + + crate::utils::ninja(build_directory)?; + + Ok(()) +} + +/// The target toolchain building sequence. +#[allow(clippy::too_many_arguments)] +fn build_target( + build_type: crate::BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + host_target_directory: &Path, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + let mut llvm_tblgen_path = host_target_directory.to_path_buf(); + llvm_tblgen_path.push("bin/llvm-tblgen"); + + let mut clang_tblgen_path = host_target_directory.to_path_buf(); + clang_tblgen_path.push("bin/clang-tblgen"); + + crate::utils::command( + Command::new("emcmake") + .env("EMCC_DEBUG", "2") + .env("CXXFLAGS", "-Dwait4=__syscall_wait4") + .env("LDFLAGS", "-lnodefs.js -s NO_INVOKE_RUN -s EXIT_RUNTIME -s INITIAL_MEMORY=64MB -s ALLOW_MEMORY_GROWTH -s EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE -s EXPORT_ES6 -s WASM_BIGINT") + .arg("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", + // Enable thin LTO but emscripten has various issues with it. + // FIXME: https://github.com/paritytech/revive/issues/148 + //"-DLLVM_ENABLE_LTO='Thin'", + //"-DCMAKE_EXE_LINKER_FLAGS='-Wl,-u,htons -Wl,-u,htonl -Wl,-u,fileno -Wl,-u,ntohs'", + &format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ), + &format!("-DCMAKE_BUILD_TYPE='{build_type}'"), + &format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ), + &format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ), + "-DLLVM_BUILD_SHARED_LIBS='Off'", + "-DLLVM_ENABLE_DUMP='Off'", + "-DLLVM_ENABLE_EXPENSIVE_CHECKS='Off'", + "-DLLVM_ENABLE_BACKTRACES='Off'", + "-DLLVM_ENABLE_BACKTRACES='Off'", + "-DLLVM_ENABLE_THREADS='Off'", + "-DLLVM_BUILD_TOOLS='Off'", + &format!("-DLLVM_TABLEGEN='{}'", llvm_tblgen_path.to_string_lossy()), + &format!("-DCLANG_TABLEGEN='{}'", clang_tblgen_path.to_string_lossy()), + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror(crate::target_env::TargetEnv::Emscripten)) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )) + .args(crate::platforms::shared::shared_build_opts_valgrind( + enable_valgrind, + )), + "LLVM target building cmake", + )?; + + crate::utils::ninja(build_directory)?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/x86_64_linux_gnu.rs b/crates/llvm-builder/src/platforms/x86_64_linux_gnu.rs new file mode 100644 index 0000000..c3aa733 --- /dev/null +++ b/crates/llvm-builder/src/platforms/x86_64_linux_gnu.rs @@ -0,0 +1,111 @@ +//! The revive LLVM amd64 `linux-gnu` builder. + +use std::collections::HashSet; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + crate::utils::check_presence("lld")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + llvm_module_llvm.to_string_lossy().as_ref(), + "-B", + llvm_build_final.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_target_final.to_string_lossy().as_ref(), + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + "-DLLVM_USE_LINKER='lld'", + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::GNU, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )) + .args(crate::platforms::shared::shared_build_opts_valgrind( + enable_valgrind, + )), + "LLVM building cmake", + )?; + crate::utils::ninja(llvm_build_final.as_ref())?; + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/x86_64_linux_musl.rs b/crates/llvm-builder/src/platforms/x86_64_linux_musl.rs new file mode 100644 index 0000000..74228bc --- /dev/null +++ b/crates/llvm-builder/src/platforms/x86_64_linux_musl.rs @@ -0,0 +1,394 @@ +//! The revive LLVM amd64 `linux-musl` builder. + +use std::collections::HashSet; +use std::path::Path; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + log::info!("building for target x86_64_linux_musl"); + + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + crate::utils::check_presence("lld")?; + crate::utils::check_presence("ninja")?; + + let musl_name = "musl-1.2.3"; + let musl_build = LLVMPath::musl_build(musl_name)?; + let musl_target = LLVMPath::musl_target()?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_host_module_llvm = LLVMPath::llvm_host_module_llvm()?; + + let llvm_build_crt = LLVMPath::llvm_build_crt()?; + let llvm_target_crt = LLVMPath::llvm_target_crt()?; + + let llvm_build_host = LLVMPath::llvm_build_host()?; + let llvm_target_host = LLVMPath::llvm_target_host()?; + + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + if !LLVMPath::musl_source(musl_name)?.exists() { + crate::utils::download_musl(musl_name)?; + } + crate::platforms::shared::build_musl(musl_build.as_path(), musl_target.as_path())?; + build_crt( + targets.clone(), + llvm_host_module_llvm.as_path(), + llvm_build_crt.as_path(), + llvm_target_crt.as_path(), + ccache_variant, + )?; + build_host( + llvm_host_module_llvm.as_path(), + llvm_build_host.as_path(), + llvm_target_host.as_path(), + musl_target.as_path(), + llvm_target_crt.as_path(), + ccache_variant, + )?; + build_target( + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + llvm_module_llvm.as_path(), + llvm_build_final.as_path(), + llvm_target_final.as_path(), + musl_target.as_path(), + llvm_target_host.as_path(), + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + + Ok(()) +} + +/// The `crt` building sequence. +fn build_crt( + mut targets: HashSet, + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + ccache_variant: Option, +) -> anyhow::Result<()> { + targets.insert(Platform::X86); + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + "-DCMAKE_BUILD_TYPE='Release'", + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + "-DLLVM_ENABLE_PROJECTS='compiler-rt'", + format!("-DLLVM_TARGETS_TO_BUILD='{}'", Platform::X86).as_str(), + "-DLLVM_DEFAULT_TARGET_TRIPLE='x86_64-pc-linux-musl'", + "-DLLVM_BUILD_TESTS='Off'", + "-DLLVM_BUILD_RUNTIMES='Off'", + "-DLLVM_BUILD_UTILS='Off'", + "-DLLVM_INCLUDE_TESTS='Off'", + "-DLLVM_INCLUDE_RUNTIMES='Off'", + "-DLLVM_INCLUDE_UTILS='Off'", + "-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'", + "-DCOMPILER_RT_BUILD_CRT='On'", + "-DCOMPILER_RT_BUILD_SANITIZERS='Off'", + "-DCOMPILER_RT_BUILD_XRAY='Off'", + "-DCOMPILER_RT_BUILD_LIBFUZZER='Off'", + "-DCOMPILER_RT_BUILD_PROFILE='Off'", + "-DCOMPILER_RT_BUILD_MEMPROF='Off'", + "-DCOMPILER_RT_BUILD_ORC='Off'", + ]) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )), + "CRT building cmake", + )?; + + crate::utils::command( + Command::new("ninja") + .arg("-C") + .arg(build_directory) + .arg("install-crt"), + "CRT building ninja", + )?; + + Ok(()) +} + +/// The host toolchain building sequence. +fn build_host( + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + musl_target_directory: &Path, + crt_target_directory: &Path, + ccache_variant: Option, +) -> anyhow::Result<()> { + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DDEFAULT_SYSROOT='{}'", + musl_target_directory.to_string_lossy() + ) + .as_str(), + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + "-DCMAKE_BUILD_TYPE='Release'", + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + "-DCLANG_DEFAULT_CXX_STDLIB='libc++'", + "-DCLANG_DEFAULT_RTLIB='compiler-rt'", + "-DLLVM_DEFAULT_TARGET_TRIPLE='x86_64-pc-linux-musl'", + "-DLLVM_TARGETS_TO_BUILD='X86'", + "-DLLVM_BUILD_TESTS='Off'", + "-DLLVM_BUILD_UTILS='Off'", + "-DLLVM_INCLUDE_TESTS='Off'", + "-DLLVM_INCLUDE_UTILS='Off'", + "-DLLVM_ENABLE_PROJECTS='clang;lld'", + "-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'", + "-DLIBCXX_CXX_ABI='libcxxabi'", + "-DLIBCXX_HAS_MUSL_LIBC='On'", + "-DLIBCXX_ENABLE_SHARED='Off'", + "-DLIBCXX_ENABLE_STATIC='On'", + "-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY='On'", + "-DLIBCXXABI_ENABLE_SHARED='Off'", + "-DLIBCXXABI_ENABLE_STATIC='On'", + "-DLIBCXXABI_ENABLE_STATIC_UNWINDER='On'", + "-DLIBCXXABI_USE_LLVM_UNWINDER='On'", + "-DLIBCXXABI_USE_COMPILER_RT='On'", + "-DLIBUNWIND_ENABLE_STATIC='On'", + "-DLIBUNWIND_ENABLE_SHARED='Off'", + "-DCOMPILER_RT_BUILD_CRT='On'", + "-DCOMPILER_RT_BUILD_SANITIZERS='Off'", + "-DCOMPILER_RT_BUILD_XRAY='Off'", + "-DCOMPILER_RT_BUILD_LIBFUZZER='Off'", + "-DCOMPILER_RT_BUILD_PROFILE='On'", + "-DCOMPILER_RT_BUILD_MEMPROF='Off'", + "-DCOMPILER_RT_BUILD_ORC='Off'", + "-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'", + "-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'", + "-DLIBCLANG_BUILD_STATIC='On'", + "-DBUILD_SHARED_LIBS='Off'", + ]) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )), + "LLVM host building cmake", + )?; + + let mut crt_lib_directory = crt_target_directory.to_path_buf(); + crt_lib_directory.push("lib/"); + + let mut build_lib_directory = build_directory.to_path_buf(); + build_lib_directory.push("lib/"); + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + content_only: true, + ..Default::default() + }; + fs_extra::dir::copy(crt_lib_directory, build_lib_directory, ©_options)?; + + crate::utils::command( + Command::new("ninja") + .arg("-C") + .arg(build_directory) + .arg("install"), + "LLVM host building ninja", + )?; + + Ok(()) +} + +/// The target toolchain building sequence. +#[allow(clippy::too_many_arguments)] +fn build_target( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + source_directory: &Path, + build_directory: &Path, + target_directory: &Path, + musl_target_directory: &Path, + host_target_directory: &Path, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, + enable_valgrind: bool, +) -> anyhow::Result<()> { + let mut clang_path = host_target_directory.to_path_buf(); + clang_path.push("bin/clang"); + + let mut clang_cxx_path = host_target_directory.to_path_buf(); + clang_cxx_path.push("bin/clang++"); + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + source_directory.to_string_lossy().as_ref(), + "-B", + build_directory.to_string_lossy().as_ref(), + "-G", + "Ninja", + "-DBUILD_SHARED_LIBS='Off'", + "-DLIBCLANG_BUILD_STATIC='On'", + "-DLLVM_BUILD_STATIC='On'", + "-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + target_directory.to_string_lossy() + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()).as_str(), + format!( + "-DCMAKE_CXX_COMPILER='{}'", + clang_cxx_path.to_string_lossy() + ) + .as_str(), + "-DCMAKE_FIND_LIBRARY_SUFFIXES='.a'", + "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", + "-DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -static'", + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::MUSL, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )) + .args(crate::platforms::shared::shared_build_opts_valgrind( + enable_valgrind, + )), + "LLVM target building cmake", + )?; + + crate::utils::ninja(build_directory)?; + + let mut musl_lib_directory = musl_target_directory.to_path_buf(); + musl_lib_directory.push("lib/"); + + let mut host_lib_directory = host_target_directory.to_path_buf(); + host_lib_directory.push("lib/x86_64-pc-linux-musl/"); + + let mut target_lib_directory = target_directory.to_path_buf(); + target_lib_directory.push("lib/"); + + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + copy_inside: true, + content_only: true, + ..Default::default() + }; + fs_extra::dir::copy( + musl_lib_directory, + target_lib_directory.as_path(), + ©_options, + )?; + fs_extra::dir::copy( + host_lib_directory, + target_lib_directory.as_path(), + ©_options, + )?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/x86_64_macos.rs b/crates/llvm-builder/src/platforms/x86_64_macos.rs new file mode 100644 index 0000000..4a03751 --- /dev/null +++ b/crates/llvm-builder/src/platforms/x86_64_macos.rs @@ -0,0 +1,105 @@ +//! The revive LLVM amd64 `macos` builder. + +use std::collections::HashSet; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_llvm = LLVMPath::llvm_module_llvm()?; + let llvm_build_final = LLVMPath::llvm_build_final()?; + let llvm_target_final = LLVMPath::llvm_target_final()?; + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + llvm_module_llvm.to_string_lossy().as_ref(), + "-B", + llvm_build_final.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_target_final.to_string_lossy().as_ref(), + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + "-DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'", + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::GNU, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::macos_build_opts_ignore_dupicate_libs_warnings()) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )), + "LLVM building cmake", + )?; + + crate::utils::ninja(llvm_build_final.as_ref())?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/platforms/x86_64_windows_gnu.rs b/crates/llvm-builder/src/platforms/x86_64_windows_gnu.rs new file mode 100644 index 0000000..0e80d9d --- /dev/null +++ b/crates/llvm-builder/src/platforms/x86_64_windows_gnu.rs @@ -0,0 +1,127 @@ +//! The revive LLVM amd64 `windows-gnu` builder. + +use std::collections::HashSet; +use std::path::PathBuf; +use std::process::Command; + +use crate::build_type::BuildType; +use crate::ccache_variant::CcacheVariant; +use crate::llvm_path::LLVMPath; +use crate::llvm_project::LLVMProject; +use crate::platforms::Platform; +use crate::sanitizer::Sanitizer; +use crate::target_triple::TargetTriple; + +/// The building sequence. +#[allow(clippy::too_many_arguments)] +pub fn build( + build_type: BuildType, + targets: HashSet, + llvm_projects: HashSet, + enable_rtti: bool, + default_target: Option, + enable_tests: bool, + enable_coverage: bool, + extra_args: &[String], + ccache_variant: Option, + enable_assertions: bool, + sanitizer: Option, +) -> anyhow::Result<()> { + crate::utils::check_presence("cmake")?; + crate::utils::check_presence("clang")?; + crate::utils::check_presence("clang++")?; + crate::utils::check_presence("lld")?; + crate::utils::check_presence("ninja")?; + + let llvm_module_llvm = + LLVMPath::llvm_module_llvm().and_then(crate::utils::path_windows_to_unix)?; + let llvm_build_final = + LLVMPath::llvm_build_final().and_then(crate::utils::path_windows_to_unix)?; + let llvm_target_final = + LLVMPath::llvm_target_final().and_then(crate::utils::path_windows_to_unix)?; + + crate::utils::command( + Command::new("cmake") + .args([ + "-S", + llvm_module_llvm.to_string_lossy().as_ref(), + "-B", + llvm_build_final.to_string_lossy().as_ref(), + "-G", + "Ninja", + format!( + "-DCMAKE_INSTALL_PREFIX='{}'", + llvm_target_final.to_string_lossy().as_ref(), + ) + .as_str(), + format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(), + "-DCMAKE_C_COMPILER='clang'", + "-DCMAKE_CXX_COMPILER='clang++'", + format!( + "-DLLVM_TARGETS_TO_BUILD='{}'", + targets + .into_iter() + .map(|platform| platform.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + format!( + "-DLLVM_ENABLE_PROJECTS='{}'", + llvm_projects + .into_iter() + .map(|project| project.to_string()) + .collect::>() + .join(";") + ) + .as_str(), + "-DLLVM_USE_LINKER='lld'", + ]) + .args(crate::platforms::shared::shared_build_opts_default_target( + default_target, + )) + .args(crate::platforms::shared::shared_build_opts_tests( + enable_tests, + )) + .args(crate::platforms::shared::shared_build_opts_coverage( + enable_coverage, + )) + .args(crate::platforms::shared::SHARED_BUILD_OPTS) + .args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL) + .args(crate::platforms::shared::shared_build_opts_werror( + crate::target_env::TargetEnv::GNU, + )) + .args(extra_args) + .args(crate::platforms::shared::shared_build_opts_ccache( + ccache_variant, + )) + .args(crate::platforms::shared::shared_build_opts_assertions( + enable_assertions, + )) + .args(crate::platforms::shared::shared_build_opts_rtti( + enable_rtti, + )) + .args(crate::platforms::shared::shared_build_opts_sanitizers( + sanitizer, + )), + "LLVM building cmake", + )?; + + crate::utils::ninja(llvm_build_final.as_ref())?; + + let libstdcpp_source_path = match std::env::var("LIBSTDCPP_SOURCE_PATH") { + Ok(libstdcpp_source_path) => PathBuf::from(libstdcpp_source_path), + Err(error) => anyhow::bail!( + "The `LIBSTDCPP_SOURCE_PATH` must be set to the path to the libstdc++.a static library: {}", error + ), + }; + let mut libstdcpp_destination_path = llvm_target_final; + libstdcpp_destination_path.push("./lib/libstdc++.a"); + fs_extra::file::copy( + crate::utils::path_windows_to_unix(libstdcpp_source_path)?, + crate::utils::path_windows_to_unix(libstdcpp_destination_path)?, + &fs_extra::file::CopyOptions::default(), + )?; + + Ok(()) +} diff --git a/crates/llvm-builder/src/revive_llvm/arguments.rs b/crates/llvm-builder/src/revive_llvm/arguments.rs new file mode 100644 index 0000000..0c58590 --- /dev/null +++ b/crates/llvm-builder/src/revive_llvm/arguments.rs @@ -0,0 +1,119 @@ +//! The revive LLVM builder arguments. + +use clap::Parser; +use revive_llvm_builder::ccache_variant::CcacheVariant; + +/// The revive LLVM builder arguments. +#[derive(Debug, Parser)] +#[command(version, about)] +pub struct Arguments { + /// Target environment to build LLVM (gnu, musl, emscripten). + #[arg(long, default_value_t = revive_llvm_builder::target_env::TargetEnv::GNU)] + pub target_env: revive_llvm_builder::target_env::TargetEnv, + + #[command(subcommand)] + pub subcommand: Subcommand, +} + +/// The revive LLVM builder arguments. +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Clone the branch specified in `LLVM.lock`. + Clone { + /// Clone with full commits history. + #[arg(long)] + deep: bool, + }, + + /// Build the LLVM framework. + Build { + /// LLVM build type (`Debug`, `Release`, `RelWithDebInfo`, or `MinSizeRel`). + #[arg(long, default_value_t = revive_llvm_builder::BuildType::Release)] + build_type: revive_llvm_builder::BuildType, + + /// Additional targets to build LLVM with. + #[arg(long)] + targets: Vec, + + /// LLVM projects to build LLVM with. + #[arg( + long, + default_values_t = vec![ + revive_llvm_builder::llvm_project::LLVMProject::CLANG, + revive_llvm_builder::llvm_project::LLVMProject::LLD + ] + )] + llvm_projects: Vec, + + /// Whether to build LLVM with run-time type information (RTTI) enabled. + #[arg(long)] + enable_rtti: bool, + + /// The default target to build LLVM with. + #[arg(long)] + default_target: Option, + + /// Whether to build the LLVM tests. + #[arg(long)] + enable_tests: bool, + + /// Whether to build LLVM for source-based code coverage. + #[arg(long)] + enable_coverage: bool, + + /// Extra arguments to pass to CMake. + /// A leading backslash will be unescaped. + #[arg(long)] + extra_args: Vec, + + /// Whether to use compiler cache (ccache) to speed-up builds. + #[arg(long)] + ccache_variant: Option, + + /// Whether to build with assertions enabled or not. + #[arg(long, default_value_t = true)] + enable_assertions: bool, + + /// Build LLVM with sanitizer enabled (`Address`, `Memory`, `MemoryWithOrigins`, `Undefined`, `Thread`, `DataFlow`, or `Address;Undefined`). + #[arg(long)] + sanitizer: Option, + + /// Whether to run LLVM unit tests under valgrind or not. + #[arg(long)] + enable_valgrind: bool, + }, + + /// Checkout the branch specified in `LLVM.lock`. + Checkout { + /// Remove all artifacts preventing the checkout (removes all local changes!). + #[arg(long)] + force: bool, + }, + + /// Clean the build artifacts. + Clean, + + /// Build the LLVM compiler-rt builtins for the PolkaVM target. + Builtins { + /// LLVM build type (`Debug`, `Release`, `RelWithDebInfo`, or `MinSizeRel`). + #[arg(long, default_value_t = revive_llvm_builder::BuildType::Release)] + build_type: revive_llvm_builder::BuildType, + + /// The default target to build LLVM with. + #[arg(long)] + default_target: Option, + + /// Extra arguments to pass to CMake. + /// A leading backslash will be unescaped. + #[arg(long)] + extra_args: Vec, + + /// Whether to use compiler cache (ccache) to speed-up builds. + #[arg(long)] + ccache_variant: Option, + + /// Build LLVM with sanitizer enabled (`Address`, `Memory`, `MemoryWithOrigins`, `Undefined`, `Thread`, `DataFlow`, or `Address;Undefined`). + #[arg(long)] + sanitizer: Option, + }, +} diff --git a/crates/llvm-builder/src/revive_llvm/main.rs b/crates/llvm-builder/src/revive_llvm/main.rs new file mode 100644 index 0000000..6e3b510 --- /dev/null +++ b/crates/llvm-builder/src/revive_llvm/main.rs @@ -0,0 +1,141 @@ +//! The revive LLVM builder. + +pub(crate) mod arguments; + +use std::collections::HashSet; +use std::path::PathBuf; +use std::str::FromStr; + +use anyhow::Context; +use clap::Parser; + +use self::arguments::{Arguments, Subcommand}; + +fn main() { + env_logger::init(); + + match main_inner() { + Ok(()) => std::process::exit(0), + Err(error) => { + log::error!("{error:?}"); + std::process::exit(1) + } + } +} + +fn main_inner() -> anyhow::Result<()> { + let arguments = Arguments::parse(); + + revive_llvm_builder::utils::directory_target_llvm(arguments.target_env); + + match arguments.subcommand { + Subcommand::Clone { deep } => { + let lock = revive_llvm_builder::Lock::try_from(&PathBuf::from( + revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH, + ))?; + revive_llvm_builder::clone(lock, deep, arguments.target_env)?; + } + + Subcommand::Build { + build_type, + targets, + llvm_projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + extra_args, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + } => { + let mut targets = targets + .into_iter() + .map(|target| revive_llvm_builder::Platform::from_str(target.as_str())) + .collect::, String>>() + .map_err(|platform| anyhow::anyhow!("Unknown platform `{}`", platform))?; + targets.insert(revive_llvm_builder::Platform::PolkaVM); + + log::info!("build targets: {:?}", &targets); + + let extra_args_unescaped: Vec = extra_args + .iter() + .map(|argument| { + argument + .strip_prefix('\\') + .unwrap_or(argument.as_str()) + .to_owned() + }) + .collect(); + + log::debug!("extra_args: {:#?}", extra_args); + log::debug!("extra_args_unescaped: {:#?}", extra_args_unescaped); + + if let Some(ccache_variant) = ccache_variant { + revive_llvm_builder::utils::check_presence(ccache_variant.to_string().as_str())?; + } + + let mut projects = llvm_projects + .into_iter() + .map(|project| { + revive_llvm_builder::llvm_project::LLVMProject::from_str( + project.to_string().as_str(), + ) + }) + .collect::, String>>( + ) + .map_err(|project| anyhow::anyhow!("Unknown LLVM project `{}`", project))?; + projects.insert(revive_llvm_builder::llvm_project::LLVMProject::LLD); + + log::info!("build projects: {:?}", &projects); + + revive_llvm_builder::build( + build_type, + arguments.target_env, + targets, + projects, + enable_rtti, + default_target, + enable_tests, + enable_coverage, + &extra_args_unescaped, + ccache_variant, + enable_assertions, + sanitizer, + enable_valgrind, + )?; + } + + Subcommand::Checkout { force } => { + let lock = revive_llvm_builder::Lock::try_from(&PathBuf::from( + revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH, + ))?; + revive_llvm_builder::checkout(lock, force)?; + } + + Subcommand::Clean => { + revive_llvm_builder::clean() + .with_context(|| "Unable to remove target LLVM directory")?; + } + + Subcommand::Builtins { + build_type, + default_target, + extra_args, + ccache_variant, + sanitizer, + } => { + revive_llvm_builder::builtins::build( + build_type, + arguments.target_env, + default_target, + &extra_args, + ccache_variant, + sanitizer, + )?; + } + } + + Ok(()) +} diff --git a/crates/llvm-builder/src/sanitizer.rs b/crates/llvm-builder/src/sanitizer.rs new file mode 100644 index 0000000..5b982e7 --- /dev/null +++ b/crates/llvm-builder/src/sanitizer.rs @@ -0,0 +1,50 @@ +//! LLVM sanitizers. + +/// LLVM sanitizers. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Sanitizer { + /// The address sanitizer. + Address, + /// The memory sanitizer. + Memory, + /// The memory with origins sanitizer. + MemoryWithOrigins, + /// The undefined behavior sanitizer + Undefined, + /// The thread sanitizer. + Thread, + /// The data flow sanitizer. + DataFlow, + /// Combine address and undefined behavior sanitizer. + AddressUndefined, +} + +impl std::str::FromStr for Sanitizer { + type Err = String; + fn from_str(value: &str) -> Result { + match value.to_lowercase().as_str() { + "address" => Ok(Self::Address), + "memory" => Ok(Self::Memory), + "memorywithorigins" => Ok(Self::MemoryWithOrigins), + "undefined" => Ok(Self::Undefined), + "thread" => Ok(Self::Thread), + "dataflow" => Ok(Self::DataFlow), + "address;undefined" => Ok(Self::AddressUndefined), + value => Err(format!("Unsupported sanitizer: `{}`", value)), + } + } +} + +impl std::fmt::Display for Sanitizer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Address => write!(f, "Address"), + Self::Memory => write!(f, "Memory"), + Self::MemoryWithOrigins => write!(f, "MemoryWithOrigins"), + Self::Undefined => write!(f, "Undefined"), + Self::Thread => write!(f, "Thread"), + Self::DataFlow => write!(f, "DataFlow"), + Self::AddressUndefined => write!(f, "Address;Undefined"), + } + } +} diff --git a/crates/llvm-builder/src/target_env.rs b/crates/llvm-builder/src/target_env.rs new file mode 100644 index 0000000..2ddbb1b --- /dev/null +++ b/crates/llvm-builder/src/target_env.rs @@ -0,0 +1,36 @@ +//! The target environments to build LLVM. + +/// The list of target environments used as constants. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TargetEnv { + /// The GNU target environment. + #[default] + GNU, + /// The MUSL target environment. + MUSL, + /// The wasm32 Emscripten environment. + Emscripten, +} + +impl std::str::FromStr for TargetEnv { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "gnu" => Ok(Self::GNU), + "musl" => Ok(Self::MUSL), + "emscripten" => Ok(Self::Emscripten), + value => Err(format!("Unsupported target environment: `{}`", value)), + } + } +} + +impl std::fmt::Display for TargetEnv { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::GNU => write!(f, "gnu"), + Self::MUSL => write!(f, "musl"), + Self::Emscripten => write!(f, "emscripten"), + } + } +} diff --git a/crates/llvm-builder/src/target_triple.rs b/crates/llvm-builder/src/target_triple.rs new file mode 100644 index 0000000..45a5512 --- /dev/null +++ b/crates/llvm-builder/src/target_triple.rs @@ -0,0 +1,29 @@ +//! The PolkaVM LLVM target triples. + +/// The list of target triples used as constants. +/// +/// It must be in the lowercase. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TargetTriple { + /// The PolkaVM RISC-V target triple. + PolkaVM, +} + +impl std::str::FromStr for TargetTriple { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "polkavm" => Ok(Self::PolkaVM), + value => Err(format!("Unsupported target triple: `{}`", value)), + } + } +} + +impl std::fmt::Display for TargetTriple { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::PolkaVM => write!(f, "riscv64-unknown-elf"), + } + } +} diff --git a/crates/llvm-builder/src/utils.rs b/crates/llvm-builder/src/utils.rs new file mode 100644 index 0000000..5d5dc18 --- /dev/null +++ b/crates/llvm-builder/src/utils.rs @@ -0,0 +1,230 @@ +//! The LLVM builder utilities. + +use std::fs::File; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; +use std::process::Stdio; +use std::time::Duration; + +use path_slash::PathBufExt; + +/// The LLVM host repository URL. +pub const LLVM_HOST_SOURCE_URL: &str = "https://github.com/llvm/llvm-project"; + +/// The LLVM host repository tag. +pub const LLVM_HOST_SOURCE_TAG: &str = "llvmorg-18.1.8"; + +/// The minimum required XCode version. +pub const XCODE_MIN_VERSION: u32 = 11; + +/// The XCode version 15. +pub const XCODE_VERSION_15: u32 = 15; + +/// The number of download retries if failed. +pub const DOWNLOAD_RETRIES: u16 = 16; + +/// The number of parallel download requests. +pub const DOWNLOAD_PARALLEL_REQUESTS: u16 = 1; + +/// The download timeout in seconds. +pub const DOWNLOAD_TIMEOUT_SECONDS: u64 = 300; + +/// The musl snapshots URL. +pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapshot"; + +/// The emscripten SDK git URL. +pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git"; + +/// The emscripten SDK version. +pub const EMSDK_VERSION: &str = "3.1.64"; + +/// The subprocess runner. +/// +/// Checks the status and prints `stderr`. +pub fn command(command: &mut Command, description: &str) -> anyhow::Result<()> { + log::debug!("executing '{command:?}' ({description})"); + + if std::env::var("DRY_RUN").is_ok() { + log::warn!("Only a dry run; not executing the command."); + return Ok(()); + } + + let status = command + .status() + .map_err(|error| anyhow::anyhow!("{} process: {}", description, error))?; + + if !status.success() { + log::error!("the command '{command:?}' failed!"); + anyhow::bail!("{} failed", description); + } + + Ok(()) +} + +/// Download a file from the URL to the path. +pub fn download(url: &str, path: &str) -> anyhow::Result<()> { + log::trace!("downloading '{url}' into '{path}'"); + + let mut downloader = downloader::Downloader::builder() + .download_folder(Path::new(path)) + .parallel_requests(DOWNLOAD_PARALLEL_REQUESTS) + .retries(DOWNLOAD_RETRIES) + .timeout(Duration::from_secs(DOWNLOAD_TIMEOUT_SECONDS)) + .build()?; + while let Err(error) = downloader.download(&[downloader::Download::new(url)]) { + log::error!("MUSL download from `{url}` failed: {error}"); + } + Ok(()) +} + +/// Unpack a tarball. +pub fn unpack_tar(filename: PathBuf, path: &str) -> anyhow::Result<()> { + let tar_gz = File::open(filename)?; + let tar = flate2::read::GzDecoder::new(tar_gz); + let mut archive = tar::Archive::new(tar); + archive.unpack(path)?; + Ok(()) +} + +/// The `musl` downloading sequence. +pub fn download_musl(name: &str) -> anyhow::Result<()> { + log::info!("downloading musl {name}"); + let tar_file_name = format!("{name}.tar.gz"); + let url = format!("{}/{tar_file_name}", MUSL_SNAPSHOTS_URL); + let target_path = crate::llvm_path::DIRECTORY_LLVM_TARGET + .get() + .unwrap() + .to_string_lossy(); + download(url.as_str(), &target_path)?; + let musl_tarball = crate::LLVMPath::musl_source(tar_file_name.as_str())?; + unpack_tar(musl_tarball, &target_path)?; + Ok(()) +} + +/// Call ninja to build the LLVM. +pub fn ninja(build_dir: &Path) -> anyhow::Result<()> { + let mut ninja = Command::new("ninja"); + ninja.args(["-C", build_dir.to_string_lossy().as_ref()]); + if std::env::var("DRY_RUN").is_ok() { + ninja.arg("-n"); + } + command(ninja.arg("install"), "Running ninja install")?; + Ok(()) +} + +/// Create an absolute path, appending it to the current working directory. +pub fn absolute_path>(path: P) -> anyhow::Result { + let mut full_path = std::env::current_dir()?; + full_path.push(path); + Ok(full_path) +} + +/// +/// Converts a Windows path into a Unix path. +/// +pub fn path_windows_to_unix + PathBufExt>(path: P) -> anyhow::Result { + path.to_slash() + .map(|pathbuf| PathBuf::from(pathbuf.to_string())) + .ok_or_else(|| anyhow::anyhow!("Windows-to-Unix path conversion error")) +} + +/// Checks if the tool exists in the system. +pub fn check_presence(name: &str) -> anyhow::Result<()> { + let description = &format!("checking the `{name}` executable"); + log::info!("{description}"); + + command(Command::new("which").arg(name), description) + .map_err(|_| anyhow::anyhow!("Tool `{}` is missing. Please install", name)) +} + +/// Identify XCode version using `pkgutil`. +pub fn get_xcode_version() -> anyhow::Result { + let pkgutil = Command::new("pkgutil") + .args(["--pkg-info", "com.apple.pkg.CLTools_Executables"]) + .stdout(Stdio::piped()) + .spawn() + .map_err(|error| anyhow::anyhow!("`pkgutil` process: {}", error))?; + let grep_version = Command::new("grep") + .arg("version") + .stdin(Stdio::from(pkgutil.stdout.expect( + "Failed to identify XCode version - XCode or CLI tools are not installed", + ))) + .output() + .map_err(|error| anyhow::anyhow!("`grep` process: {}", error))?; + let version_string = String::from_utf8(grep_version.stdout)?; + let version_regex = regex::Regex::new(r"version: (\d+)\..*")?; + let captures = version_regex + .captures(version_string.as_str()) + .ok_or(anyhow::anyhow!( + "Failed to parse XCode version: {version_string}" + ))?; + let xcode_version: u32 = captures + .get(1) + .expect("Always has a major version") + .as_str() + .parse() + .map_err(|error| anyhow::anyhow!("Failed to parse XCode version: {error}"))?; + Ok(xcode_version) +} + +/// Install the Emscripten SDK. +pub fn install_emsdk() -> anyhow::Result<()> { + log::info!("installing emsdk v{EMSDK_VERSION}"); + + let emsdk_source_path = PathBuf::from(crate::LLVMPath::DIRECTORY_EMSDK_SOURCE); + + if emsdk_source_path.exists() { + log::warn!( + "emsdk source path {emsdk_source_path:?} already exists. + Skipping the emsdk installation, delete the source path for re-installation" + ); + return Ok(()); + } + + crate::utils::command( + Command::new("git") + .arg("clone") + .arg(crate::utils::EMSDK_SOURCE_URL) + .arg(emsdk_source_path.to_string_lossy().as_ref()), + "Emscripten SDK repository cloning", + )?; + + crate::utils::command( + Command::new("git") + .arg("checkout") + .arg(format!("tags/{}", crate::utils::EMSDK_VERSION)) + .current_dir(&emsdk_source_path), + "Emscripten SDK repository version checkout", + )?; + + crate::utils::command( + Command::new("./emsdk") + .arg("install") + .arg(EMSDK_VERSION) + .current_dir(&emsdk_source_path), + "Emscripten SDK installation", + )?; + + crate::utils::command( + Command::new("./emsdk") + .arg("activate") + .arg(EMSDK_VERSION) + .current_dir(&emsdk_source_path), + "Emscripten SDK activation", + )?; + + log::warn!( + "run 'source {}emsdk_env.sh' to finish the emsdk installation", + emsdk_source_path.display() + ); + + Ok(()) +} + +/// The LLVM target directory default path. +pub fn directory_target_llvm(target_env: crate::target_env::TargetEnv) -> PathBuf { + crate::llvm_path::DIRECTORY_LLVM_TARGET + .get_or_init(|| PathBuf::from(format!("./target-llvm/{}/", target_env))) + .clone() +} diff --git a/crates/llvm-builder/tests/build.rs b/crates/llvm-builder/tests/build.rs new file mode 100644 index 0000000..e465885 --- /dev/null +++ b/crates/llvm-builder/tests/build.rs @@ -0,0 +1,158 @@ +pub mod common; + +use std::process::Command; + +use assert_cmd::prelude::*; + +/// This test verifies that the LLVM repository can be successfully cloned, built, and cleaned. +#[test] +fn clone_build_and_clean() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("build") + .arg("--llvm-projects") + .arg("clang") + .arg("--llvm-projects") + .arg("lld") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("builtins") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clean") + .assert() + .success(); + + Ok(()) +} + +/// This test verifies that the LLVM repository can be successfully cloned, built, and cleaned +/// with 2-staged build using MUSL as sysroot. +#[test] +#[cfg(target_os = "linux")] +fn clone_build_and_clean_musl() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .arg("clone") + .current_dir(test_dir.path()) + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .arg("--target-env") + .arg("musl") + .arg("build") + .arg("--llvm-projects") + .arg("clang") + .arg("--llvm-projects") + .arg("lld") + .current_dir(test_dir.path()) + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("builtins") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clean") + .assert() + .success(); + + Ok(()) +} + +/// This test verifies that the LLVM repository can be successfully cloned and built in debug mode +/// with tests and coverage enabled. +#[test] +fn debug_build_with_tests_coverage() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("build") + .arg("--enable-coverage") + .arg("--enable-tests") + .arg("--build-type") + .arg("Debug") + .assert() + .success(); + + Ok(()) +} + +/// This test verifies that the LLVM repository can be successfully built with address sanitizer. +#[test] +fn build_with_sanitizers() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("build") + .arg("--sanitizer") + .arg("Address") + .assert() + .success(); + + Ok(()) +} + +/// Tests the clone, build, and clean process of the LLVM repository for the emscripten target. +#[test] +#[cfg(any(target_os = "linux", target_os = "macos"))] +fn clone_build_and_clean_emscripten() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + let command = Command::cargo_bin(common::REVIVE_LLVM)?; + let program = command.get_program().to_string_lossy(); + let emsdk_wrapped_build_command = format!( + "{program} --target-env emscripten clone && \ + source {}emsdk_env.sh && \ + {program} --target-env emscripten build --llvm-projects clang --llvm-projects lld", + revive_llvm_builder::LLVMPath::DIRECTORY_EMSDK_SOURCE, + ); + + Command::new("sh") + .arg("-c") + .arg(emsdk_wrapped_build_command) + .current_dir(test_dir.path()) + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .arg("clean") + .current_dir(test_dir.path()) + .assert() + .success(); + + Ok(()) +} diff --git a/crates/llvm-builder/tests/checkout.rs b/crates/llvm-builder/tests/checkout.rs new file mode 100644 index 0000000..2fd8d1c --- /dev/null +++ b/crates/llvm-builder/tests/checkout.rs @@ -0,0 +1,48 @@ +pub mod common; + +use std::process::Command; + +use assert_cmd::prelude::*; + +/// This test verifies that after cloning the LLVM repository, checking out a specific branch +/// or reference works as expected. +#[test] +fn checkout_after_clone() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("checkout") + .assert() + .success(); + + Ok(()) +} + +/// This test verifies that after cloning the LLVM repository, checking out a specific branch +/// or reference with the `--force` option works as expected. +#[test] +fn force_checkout() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("checkout") + .arg("--force") + .assert() + .success(); + + Ok(()) +} diff --git a/crates/llvm-builder/tests/clone.rs b/crates/llvm-builder/tests/clone.rs new file mode 100644 index 0000000..8c2dede --- /dev/null +++ b/crates/llvm-builder/tests/clone.rs @@ -0,0 +1,36 @@ +pub mod common; + +use std::process::Command; + +use assert_cmd::prelude::*; + +/// This test verifies that the LLVM repository can be successfully cloned using a specific branch +/// and reference. +#[test] +fn clone() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .assert() + .success(); + + Ok(()) +} + +/// This test verifies that the LLVM repository can be successfully cloned using a specific branch +/// and reference with --deep option. +#[test] +fn clone_deep() -> anyhow::Result<()> { + let test_dir = common::TestDir::with_lockfile(None)?; + + Command::cargo_bin(common::REVIVE_LLVM)? + .current_dir(test_dir.path()) + .arg("clone") + .arg("--deep") + .assert() + .success(); + + Ok(()) +} diff --git a/crates/llvm-builder/tests/common.rs b/crates/llvm-builder/tests/common.rs new file mode 100644 index 0000000..78465fc --- /dev/null +++ b/crates/llvm-builder/tests/common.rs @@ -0,0 +1,36 @@ +use assert_fs::fixture::FileWriteStr; + +pub const REVIVE_LLVM: &str = "revive-llvm"; +pub const REVIVE_LLVM_REPO_URL: &str = "https://github.com/llvm/llvm-project"; +pub const REVIVE_LLVM_REPO_TEST_BRANCH: &str = "release/18.x"; + +pub struct TestDir { + _lockfile: assert_fs::NamedTempFile, + path: std::path::PathBuf, +} + +/// Creates a temporary lock file for testing. +impl TestDir { + pub fn with_lockfile(reference: Option) -> anyhow::Result { + let file = + assert_fs::NamedTempFile::new(revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH)?; + let lock = revive_llvm_builder::Lock { + url: REVIVE_LLVM_REPO_URL.to_string(), + branch: REVIVE_LLVM_REPO_TEST_BRANCH.to_string(), + r#ref: reference, + }; + file.write_str(toml::to_string(&lock)?.as_str())?; + + Ok(Self { + path: file + .parent() + .expect("lockfile parent dir always exists") + .into(), + _lockfile: file, + }) + } + + pub fn path(&self) -> &std::path::Path { + &self.path + } +} diff --git a/crates/runner/Cargo.toml b/crates/runner/Cargo.toml index c4efd9c..81a914f 100644 --- a/crates/runner/Cargo.toml +++ b/crates/runner/Cargo.toml @@ -8,6 +8,7 @@ authors.workspace = true description = "Execute revive contracts in a simulated blockchain runtime" [features] +std = ["polkadot-sdk/std"] default = ["solidity"] solidity = ["revive-solidity", "revive-differential"] diff --git a/crates/runtime-api/Cargo.toml b/crates/runtime-api/Cargo.toml index ccc3cd7..11d3550 100644 --- a/crates/runtime-api/Cargo.toml +++ b/crates/runtime-api/Cargo.toml @@ -12,3 +12,6 @@ anyhow = { workspace = true } inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] } revive-common = { workspace = true } + +[build-dependencies] +revive-build-utils = { workspace = true } diff --git a/crates/runtime-api/build.rs b/crates/runtime-api/build.rs index a922584..2e54fc4 100644 --- a/crates/runtime-api/build.rs +++ b/crates/runtime-api/build.rs @@ -14,7 +14,7 @@ const EXPORTS_BC: &str = "polkavm_exports.bc"; const EXPORTS_RUST: &str = "polkavm_exports.rs"; fn compile(source_path: &str, bitcode_path: &str) { - let output = Command::new("clang") + let output = Command::new(revive_build_utils::llvm_host_tool("clang")) .args([ TARGET_FLAG, "-Xclang", @@ -37,7 +37,7 @@ fn compile(source_path: &str, bitcode_path: &str) { source_path, ]) .output() - .expect("should be able to invoke C clang"); + .unwrap_or_else(|error| panic!("failed to execute clang: {}", error)); assert!( output.status.success(), @@ -59,6 +59,11 @@ fn build_module(source_path: &str, bitcode_path: &str, rust_file: &str) { } fn main() { + println!( + "cargo:rerun-if-env-changed={}", + revive_build_utils::REVIVE_LLVM_HOST_PREFIX + ); + build_module(IMPORTS_SOUCE, IMPORTS_BC, IMPORTS_RUST); build_module(EXPORTS_SOUCE, EXPORTS_BC, EXPORTS_RUST); diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index 33055e6..c474760 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -8,3 +8,6 @@ description = "revive compiler stdlib components" [dependencies] inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] } + +[build-dependencies] +revive-build-utils = { workspace = true } diff --git a/crates/stdlib/build.rs b/crates/stdlib/build.rs index 10f7513..8955381 100644 --- a/crates/stdlib/build.rs +++ b/crates/stdlib/build.rs @@ -1,18 +1,23 @@ use std::{env, fs, path::Path, process::Command}; fn main() { + println!( + "cargo:rerun-if-env-changed={}", + revive_build_utils::REVIVE_LLVM_HOST_PREFIX + ); + let lib = "stdlib.bc"; let out_dir = env::var_os("OUT_DIR").expect("env should have $OUT_DIR"); let bitcode_path = Path::new(&out_dir).join(lib); - - let output = Command::new("llvm-as") + let llvm_as = revive_build_utils::llvm_host_tool("llvm-as"); + let output = Command::new(llvm_as) .args([ "-o", bitcode_path.to_str().expect("$OUT_DIR should be UTF-8"), "stdlib.ll", ]) .output() - .expect("should be able to invoke llvm-as"); + .unwrap_or_else(|error| panic!("failed to execute llvm-as: {}", error)); assert!( output.status.success(), diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..74cac15 --- /dev/null +++ b/deny.toml @@ -0,0 +1,65 @@ +[advisories] +yanked = "warn" +ignore = [ + #"RUSTSEC-0000-0000", +] + +[licenses] +allow = [ + #"Apache-2.0 WITH LLVM-exception", + "MIT", + "Apache-2.0", + "ISC", + "Unlicense", + "MPL-2.0", + "Unicode-DFS-2016", + "Unicode-3.0", + "CC0-1.0", + "BSD-2-Clause", + "BSD-3-Clause", + "Zlib", + "LGPL-3.0", +] +confidence-threshold = 0.8 +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +unused-allowed-license = "allow" + +[licenses.private] +ignore = false +registries = [ + #"https://sekretz.com/registry +] + +[bans] +multiple-versions = "warn" +wildcards = "allow" +highlight = "all" +workspace-default-features = "allow" +external-default-features = "allow" +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, +] + +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +[sources] +unknown-registry = "deny" +unknown-git = "allow" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] diff --git a/emscripten-build-llvm.sh b/emscripten-build-llvm.sh deleted file mode 100755 index c7c7a29..0000000 --- a/emscripten-build-llvm.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -INSTALL_DIR="${PWD}/llvm18.0-emscripten" -mkdir -p "${INSTALL_DIR}" - -# Check if EMSDK_ROOT is defined -if [ -z "${EMSDK_ROOT:-}" ]; then - echo "Error: EMSDK_ROOT is not defined." - echo "Please set the EMSDK_ROOT environment variable to the root directory of your Emscripten SDK." - exit 1 -fi - -source "${EMSDK_ROOT}/emsdk_env.sh" - -LLVM_SRC="${PWD}/llvm-project" -LLVM_NATIVE="${PWD}/build/llvm-tools" -LLVM_WASM="${PWD}/build/llvm-wasm" - -./clone-llvm.sh "${LLVM_SRC}" - -# Cross-compiling LLVM requires a native build of "llvm-tblgen", "clang-tblgen" and "llvm-config" -if [ ! -d "${LLVM_NATIVE}" ]; then - cmake -G Ninja \ - -S "${LLVM_SRC}/llvm" \ - -B "${LLVM_NATIVE}" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_TARGETS_TO_BUILD=WebAssembly \ - -DLLVM_ENABLE_PROJECTS="clang" -fi - -cmake --build "${LLVM_NATIVE}" -- llvm-tblgen clang-tblgen llvm-config - -if [ ! -d "${LLVM_WASM}" ]; then - EMCC_DEBUG=2 \ - CXXFLAGS="-Dwait4=__syscall_wait4" \ - LDFLAGS="-lnodefs.js -s NO_INVOKE_RUN -s EXIT_RUNTIME -s INITIAL_MEMORY=64MB -s ALLOW_MEMORY_GROWTH -s \ - EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE -s EXPORT_ES6 -s WASM_BIGINT" \ - emcmake cmake -G Ninja \ - -S "${LLVM_SRC}/llvm" \ - -B "${LLVM_WASM}" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_TARGETS_TO_BUILD='RISCV' \ - -DLLVM_ENABLE_PROJECTS="clang;lld" \ - -DLLVM_ENABLE_DUMP=OFF \ - -DLLVM_ENABLE_ASSERTIONS=OFF \ - -DLLVM_ENABLE_EXPENSIVE_CHECKS=OFF \ - -DLLVM_ENABLE_BACKTRACES=OFF \ - -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_ENABLE_THREADS=OFF \ - -DLLVM_BUILD_LLVM_DYLIB=OFF \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DLLVM_ENABLE_TERMINFO=Off \ - -DLLVM_ENABLE_LIBXML2=Off \ - -DLLVM_ENABLE_ZLIB=Off \ - -DLLVM_ENABLE_ZSTD=Off \ - -DLLVM_TABLEGEN="${LLVM_NATIVE}/bin/llvm-tblgen" \ - -DCLANG_TABLEGEN="${LLVM_NATIVE}/bin/clang-tblgen" \ - -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" -fi - -cmake --build "${LLVM_WASM}" -cmake --install "${LLVM_WASM}" - -cp "${LLVM_NATIVE}/bin/llvm-config" "${INSTALL_DIR}/bin" - -echo "" -echo "LLVM cross-compilation for WebAssembly completed successfully." diff --git a/utils/build-debian-builder.sh b/utils/build-debian-builder.sh deleted file mode 100755 index 87c81e0..0000000 --- a/utils/build-debian-builder.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /usr/bin/env bash - -CONTAINER=revive-builder-debian-x86 -VERSION=latest -DOCKERFILE=revive-builder-debian.dockerfile - -docker build --rm -t ${CONTAINER}:${VERSION} -f ${DOCKERFILE} $@ diff --git a/utils/build-revive.sh b/utils/build-revive.sh deleted file mode 100755 index a1b1dde..0000000 --- a/utils/build-revive.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /usr/bin/env bash - -set -euo pipefail - -REVIVE_INSTALL_DIR=$(pwd)/target/release -while getopts "o:" option ; do - case $option in - o) # Output directory - REVIVE_INSTALL_DIR=$OPTARG - ;; - \?) echo "Error: Invalid option" - exit 1;; - esac -done -echo "Installing to ${REVIVE_INSTALL_DIR}" - -$(pwd)/build-llvm.sh -export PATH=$(pwd)/llvm18.0/bin:$PATH - -make install-revive REVIVE_INSTALL_DIR=${REVIVE_INSTALL_DIR} diff --git a/utils/revive-builder-debian.dockerfile b/utils/revive-builder-debian.dockerfile deleted file mode 100644 index 2631aed..0000000 --- a/utils/revive-builder-debian.dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# syntax=docker/dockerfile:1 -# Dockerfile for building revive in a Debian container. -FROM debian:12 -RUN <