From ee0a3dfad77a4993b8ef04680d2993e151686207 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 21 Jul 2026 09:14:40 -0400 Subject: [PATCH 1/6] ci: Improve test debuggability (#7619) Co-authored-by: Ayaz Salikhov --- .../workflows/reusable-build-test-config.yml | 42 +++++++++-------- src/test/app/Invariants_test.cpp | 45 +++++++++++-------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index d4135207fe..69fa7a7bef 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -223,11 +223,13 @@ jobs: BUILD_TYPE: ${{ inputs.build_type }} CMAKE_TARGET: ${{ inputs.cmake_target }} run: | + set -o pipefail cmake \ --build . \ --config "${BUILD_TYPE}" \ --parallel "${BUILD_NPROC}" \ - --target "${CMAKE_TARGET}" + --target "${CMAKE_TARGET}" \ + 2>&1 | tee "${GITHUB_WORKSPACE}/build.log" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} @@ -322,7 +324,7 @@ jobs: PRELOAD="" fi - LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log + LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee "${GITHUB_WORKSPACE}/unittest.log" # Smoke-run every benchmark module with a single repetition to confirm the # benchmarks still build and execute. This is a correctness check, not a @@ -341,25 +343,27 @@ jobs: done < <(find src/benchmarks -type f -perm -u+x -name 'xrpl.bench.*') exit "${rc}" - - name: Show test failure summary - if: ${{ failure() && !inputs.build_only }} - env: - WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} + - name: Show build/test failure summary + if: ${{ failure() }} run: | - if [ ! -d "${WORKING_DIR}" ]; then - echo "Working directory '${WORKING_DIR}' does not exist." - exit 0 - fi + cd "${GITHUB_WORKSPACE}" - cd "${WORKING_DIR}" - - if [ ! -f unittest.log ]; then - echo "unittest.log not found; embedded tests may not have run." - exit 0 - fi - - if ! grep -E "failed" unittest.log; then - echo "Log present but no failure lines found in unittest.log." + if [ -f unittest.log ]; then + if ! grep -E "failed" unittest.log | grep -vE "^I[0-9]|^[0-9]+> (ERR:|FTL:)"; then + echo "unittest.log present but no failure lines found." + fi + elif [ -f build.log ]; then + # GCC/Clang emit "error:" (covers "fatal error:"); MSVC emits + # "error C####:", "error LNK####:", and "fatal error LNK####:". + # -A6 prints the lines that follow each match (source line, caret, + # notes, and the "N errors generated" tally) to capture the whole + # diagnostic block. + if ! grep -E -A6 "error:|error C[0-9]{4}|error LNK[0-9]{4}|fatal error" build.log; then + echo "build.log present but no compile errors found." + fi + else + echo "unittest.log/build.log not found; something went wrong." + exit 1 fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 076e39a42b..eaf1f2704c 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,12 @@ class Invariants_test : public beast::unit_test::Suite return xrpl::test::jtx::testableAmendments() | fixCleanup3_1_3 | fixCleanup3_2_0; } + test::jtx::Env + makeEnv(FeatureBitset features) + { + return {*this, test::jtx::envconfig(), features, nullptr, beast::Severity::Disabled}; + } + /** * Run a specific test case to put the ledger into a state that will be * detected by an invariant. Simulates the actions of a transaction that @@ -130,7 +137,7 @@ class Invariants_test : public beast::unit_test::Suite TxAccount setTxAccount = TxAccount::None) { doInvariantCheck( - test::jtx::Env(*this, defaultAmendments()), + makeEnv(defaultAmendments()), expectLogs, precheck, fee, @@ -1494,7 +1501,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain with no rules."}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { return createPermissionedDomain(ac, a1, a2, 0).get(); @@ -1507,7 +1514,7 @@ class Invariants_test : public beast::unit_test::Suite static constexpr auto kTooBig = kMaxPermissionedDomainCredentialsArraySize + 1; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain bad credentials size " + std::to_string(kTooBig)}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { return !!createPermissionedDomain(ac, a1, a2, kTooBig); @@ -1518,7 +1525,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain 3"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't sorted"}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { auto slePd = createPermissionedDomain(ac, a1, a2, 0); @@ -1542,7 +1549,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain 4"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't unique"}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { auto slePd = createPermissionedDomain(ac, a1, a2, 0); @@ -1565,7 +1572,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 1"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain with no rules."}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1586,7 +1593,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 2"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain bad credentials size " + std::to_string(kTooBig)}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1617,7 +1624,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 3"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't sorted"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1647,7 +1654,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 4"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't unique"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1688,7 +1695,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain set 2 domains "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badMoreThan1 : emptyV, [](Account const& a1, Account const& a2, ApplyContext& ac) { createPermissionedDomain(ac, a1, a2); @@ -1734,7 +1741,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain set 0 domains "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badNoDomains : emptyV, [](Account const&, Account const&, ApplyContext&) { return true; }, XRPAmount{}, @@ -1757,7 +1764,7 @@ class Invariants_test : public beast::unit_test::Suite env1.close(); doInvariantCheck( - Env(*this, features), + makeEnv(features), a1, a2, fixEnabled ? badNoDomains : emptyV, @@ -1798,7 +1805,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain del, create domain "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badNotDeleted : emptyV, [](Account const& a1, Account const& a2, ApplyContext& ac) { createPermissionedDomain(ac, a1, a2); @@ -1995,7 +2002,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDEX" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"domain doesn't exist"}}, [](Account const& a1, Account const&, ApplyContext& ac) { Keylet const offerKey = keylet::offer(a1.id(), 10); @@ -2022,7 +2029,7 @@ class Invariants_test : public beast::unit_test::Suite // missing domain ID in offer object doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"hybrid offer is malformed"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { Keylet const offerKey = keylet::offer(a2.id(), 10); @@ -4342,7 +4349,7 @@ class Invariants_test : public beast::unit_test::Suite }; doInvariantCheck( - Env{*this, defaultAmendments() - fixCleanup3_2_0}, + makeEnv(defaultAmendments() - fixCleanup3_2_0), {}, [](Account const&, Account const&, ApplyContext&) { return true; }, XRPAmount{}, @@ -5468,7 +5475,7 @@ class Invariants_test : public beast::unit_test::Suite // sfHighLimit issue, not the keylet currency). testcase << "overwrite: NoXRPTrustLines" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"an XRP trust line was created"}} : std::vector{}, [&insertOrderedTrustLinePair](Account const& a1, Account const& a2, ApplyContext& ac) { @@ -5496,7 +5503,7 @@ class Invariants_test : public beast::unit_test::Suite // Regression: bad deep-freeze trust line followed by a valid one. testcase << "overwrite: NoDeepFreeze" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"a trust line with deep freeze flag without " "normal freeze was created"}} : std::vector{}, @@ -5530,7 +5537,7 @@ class Invariants_test : public beast::unit_test::Suite // still fires ("a MPT issuance was created"). testcase << "overwrite: NoZeroEscrow MPT" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"escrow specifies invalid amount"}} : std::vector{{"a MPT issuance was created"}}, [](Account const& a1, Account const&, ApplyContext& ac) { From 4918f3bb200c1c56a2769e7a94ec701728138216 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 21 Jul 2026 13:38:26 +0000 Subject: [PATCH 2/6] chore: Add doxygen in pre-commit image (#7836) --- bin/pre-commit/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/pre-commit/Dockerfile b/bin/pre-commit/Dockerfile index fcd07146c3..3e803297ce 100644 --- a/bin/pre-commit/Dockerfile +++ b/bin/pre-commit/Dockerfile @@ -10,6 +10,7 @@ ARG DEBIAN_FRONTEND=noninteractive RUN < Date: Tue, 21 Jul 2026 13:39:37 +0000 Subject: [PATCH 3/6] ci: Add cargo to pre-commit image (#7835) --- .github/workflows/build-pre-commit-image.yml | 2 ++ bin/pre-commit/Dockerfile | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/build-pre-commit-image.yml b/.github/workflows/build-pre-commit-image.yml index 1d0a479846..9b20dc7951 100644 --- a/.github/workflows/build-pre-commit-image.yml +++ b/.github/workflows/build-pre-commit-image.yml @@ -7,10 +7,12 @@ on: paths: - ".github/workflows/build-pre-commit-image.yml" - "bin/pre-commit/Dockerfile" + - "rust-toolchain.toml" pull_request: paths: - ".github/workflows/build-pre-commit-image.yml" - "bin/pre-commit/Dockerfile" + - "rust-toolchain.toml" workflow_dispatch: concurrency: diff --git a/bin/pre-commit/Dockerfile b/bin/pre-commit/Dockerfile index 3e803297ce..a96f3e4c25 100644 --- a/bin/pre-commit/Dockerfile +++ b/bin/pre-commit/Dockerfile @@ -37,3 +37,19 @@ ENV NIX_SSL_CERT_FILE="/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt # Verify nix installation RUN nix --version + +ENV RUSTUP_HOME="/opt/rust/rustup" +ENV CARGO_HOME="/opt/rust/cargo" +ENV PATH="/opt/rust/cargo/bin:${PATH}" + +WORKDIR /tmp +COPY rust-toolchain.toml /tmp/rust-toolchain.toml +RUN < Date: Tue, 21 Jul 2026 15:37:47 +0000 Subject: [PATCH 4/6] ci: Update pre-commit image (#7838) --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 37207c41ef..9970e9a07d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,4 +17,4 @@ jobs: uses: XRPLF/actions/.github/workflows/pre-commit.yml@1bde119a1ab71305ba5d3716e7a82cea1c7bdede with: runs_on: ubuntu-latest - container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-4c869ee" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-f56b79f" }' From ba01b05f33d4b28c30ce347d0cd68c4d83005359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:04:45 +0000 Subject: [PATCH 5/6] ci: [DEPENDABOT] bump actions/setup-python from 6.3.0 to 7.0.0 (#7830) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-package.yml | 2 +- .github/workflows/reusable-strategy-matrix.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 6feecbfb75..55bc20dc5c 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index b6091b99d9..de8d9cfc8e 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" From 7edf39e62223b6d8296ec68051c749432a4d24e1 Mon Sep 17 00:00:00 2001 From: Sergey Kuznetsov Date: Wed, 22 Jul 2026 14:00:17 +0000 Subject: [PATCH 6/6] ci: Use rust-overlay to bring Rust into nix (#7837) --- .cspell.config.yaml | 1 + .github/workflows/build-nix-images.yml | 2 ++ .gitignore | 3 ++ flake.lock | 23 +++++++++++- flake.nix | 17 +++++++-- nix/docker/Dockerfile | 1 + nix/docker/README.md | 35 +++++++++++-------- nix/docker/test_files/rust/compile-sources.sh | 23 ++++++++++++ .../test_files/rust/proc_macro/Cargo.lock | 14 ++++++++ .../test_files/rust/proc_macro/Cargo.toml | 3 ++ .../rust/proc_macro/echo_macro/Cargo.toml | 8 +++++ .../rust/proc_macro/echo_macro/src/lib.rs | 6 ++++ .../rust/proc_macro/test_macro/Cargo.toml | 8 +++++ .../rust/proc_macro/test_macro/src/main.rs | 9 +++++ nix/docker/test_files/rust/run-binaries.sh | 7 ++-- nix/packages.nix | 8 ++--- nix/utils.nix | 13 +++++-- rust-toolchain.toml | 6 +--- 18 files changed, 154 insertions(+), 33 deletions(-) create mode 100644 nix/docker/test_files/rust/proc_macro/Cargo.lock create mode 100644 nix/docker/test_files/rust/proc_macro/Cargo.toml create mode 100644 nix/docker/test_files/rust/proc_macro/echo_macro/Cargo.toml create mode 100644 nix/docker/test_files/rust/proc_macro/echo_macro/src/lib.rs create mode 100644 nix/docker/test_files/rust/proc_macro/test_macro/Cargo.toml create mode 100644 nix/docker/test_files/rust/proc_macro/test_macro/src/main.rs diff --git a/.cspell.config.yaml b/.cspell.config.yaml index e220cd0249..e26eef9227 100644 --- a/.cspell.config.yaml +++ b/.cspell.config.yaml @@ -216,6 +216,7 @@ words: - Nyffenegger - onlatest - ostr + - oxalica - pargs - partitioner - paychan diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml index da28b8db49..fd480cff67 100644 --- a/.github/workflows/build-nix-images.yml +++ b/.github/workflows/build-nix-images.yml @@ -8,6 +8,7 @@ on: - ".github/workflows/build-nix-images.yml" - "flake.nix" - "flake.lock" + - "rust-toolchain.toml" - "nix/**" - "!nix/docker/README.md" - "!nix/devshell.nix" @@ -18,6 +19,7 @@ on: - ".github/workflows/build-nix-images.yml" - "flake.nix" - "flake.lock" + - "rust-toolchain.toml" - "nix/**" - "!nix/docker/README.md" - "!nix/devshell.nix" diff --git a/.gitignore b/.gitignore index 6bd34ece04..13b59a7e2c 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,9 @@ DerivedData # Python __pycache__ +# Rust build artifacts. +target/ + # Direnv's directory /.direnv diff --git a/flake.lock b/flake.lock index 80243ccf15..cd9289c998 100644 --- a/flake.lock +++ b/flake.lock @@ -36,7 +36,28 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", - "nixpkgs-custom-glibc": "nixpkgs-custom-glibc" + "nixpkgs-custom-glibc": "nixpkgs-custom-glibc", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1784611586, + "narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "14f58845249f3552a89b07772626b8d3c632fa86", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index c52f4d050e..ee2fd13efc 100644 --- a/flake.nix +++ b/flake.nix @@ -10,12 +10,25 @@ url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562"; flake = false; }; + # Pinned Rust toolchains, delivered from the Nix store. Lets the Nix CI + # image and dev shell honour the single `rust-toolchain.toml` pin (shared + # with the rustup-based non-Nix runners) while staying hermetic — the + # toolchain lands in the image's Nix closure and is locked by flake.lock. + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = - { nixpkgs, nixpkgs-custom-glibc, ... }: + { + nixpkgs, + nixpkgs-custom-glibc, + rust-overlay, + ... + }: let - forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc; }; + forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc rust-overlay; }; in { devShells = forEachSystem (import ./nix/devshell.nix); diff --git a/nix/docker/Dockerfile b/nix/docker/Dockerfile index 7222cc8fa8..8b851fd9e0 100644 --- a/nix/docker/Dockerfile +++ b/nix/docker/Dockerfile @@ -12,6 +12,7 @@ COPY nix/packages.nix /tmp/build/nix/packages.nix COPY nix/utils.nix /tmp/build/nix/utils.nix COPY flake.nix /tmp/build/ COPY flake.lock /tmp/build/ +COPY rust-toolchain.toml /tmp/build/ WORKDIR /tmp/build FROM builder-source AS builder diff --git a/nix/docker/README.md b/nix/docker/README.md index 23ad05049e..7b4d4b9387 100644 --- a/nix/docker/README.md +++ b/nix/docker/README.md @@ -47,7 +47,9 @@ work without `ca-certificates` being installed in the base image. [`test_files/cpp/sources/`](./test_files/cpp/sources) with both `g++` and `clang++`, and sanitizers, and - compiles the Rust test programs in - [`test_files/rust/sources/`](./test_files/rust/sources) with `rustc`. + [`test_files/rust/sources/`](./test_files/rust/sources) with `rustc`, and + builds the [`test_files/rust/proc_macro/`](./test_files/rust/proc_macro) + workspace with `cargo` to exercise proc-macro dylib loading. 3. **`tester`** — Start again from a clean `BASE_IMAGE` (no Nix toolchain), install only the sanitizer runtime libraries ([`install-sanitizer-libs.sh`](./install-sanitizer-libs.sh)), and run the @@ -76,20 +78,23 @@ toolchain being present at runtime. Two pieces make that work: [`loader-path.sh`](./loader-path.sh) reports the expected loader path for the current architecture, so we can patch the binaries to use the correct loader. -The build then verifies all of this end to end: the C++ test programs in -`test_files/cpp/sources/` (a regular binary plus ASan/TSan/UBSan variants) and -the Rust test programs in `test_files/rust/sources/` (a hello binary plus panic -and overflow-check variants) are compiled in `final`, their `PT_INTERP` is -patched to the target loader, and they are run in the clean `tester` stage to -confirm each emits the expected diagnostic on a stock base image. +The build then verifies all of this end to end, and the C++ and Rust programs +go through the same pipeline: each is compiled in `final`, has its `PT_INTERP` +patched to the target loader, and is then run in the clean `tester` stage to +confirm it emits the expected diagnostic on a stock base image. The C++ programs +are in `test_files/cpp/sources/` (a regular binary plus ASan/TSan/UBSan +variants); the Rust programs are in `test_files/rust/sources/` (a hello binary +plus panic and overflow-check variants), plus the `test_files/rust/proc_macro/` +workspace — a crate whose compilation additionally loads a proc-macro dylib, and +whose resulting binary is patched and run like the others. ## Files -| File | Purpose | -| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| [`./Dockerfile`](./Dockerfile) | Multi-stage build described above. | -| [`./loader-path.sh`](./loader-path.sh) | Print the dynamic-linker (`PT_INTERP`) path for the current architecture. | -| [`./test_files/cpp/`](./test_files/cpp) | C++ sanitizer smoke test: sources + compile/run scripts. | -| [`./test_files/rust/`](./test_files/rust) | Rust rustc smoke test: sources + compile/run scripts. | -| [`/bin/check-tools.sh`](../../bin/check-tools.sh) | Verify every expected tools are present and runnable. | -| [`/bin/install-sanitizer-libs.sh`](../../bin/install-sanitizer-libs.sh) | Install `libasan`/`libtsan`/`libubsan` runtimes on the supported base images. | +| File | Purpose | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| [`./Dockerfile`](./Dockerfile) | Multi-stage build described above. | +| [`./loader-path.sh`](./loader-path.sh) | Print the dynamic-linker (`PT_INTERP`) path for the current architecture. | +| [`./test_files/cpp/`](./test_files/cpp) | C++ sanitizer smoke test: sources + compile/run scripts. | +| [`./test_files/rust/`](./test_files/rust) | Rust smoke test: rustc sources + a cargo proc-macro workspace + compile/run scripts. | +| [`/bin/check-tools.sh`](../../bin/check-tools.sh) | Verify every expected tools are present and runnable. | +| [`/bin/install-sanitizer-libs.sh`](../../bin/install-sanitizer-libs.sh) | Install `libasan`/`libtsan`/`libubsan` runtimes on the supported base images. | diff --git a/nix/docker/test_files/rust/compile-sources.sh b/nix/docker/test_files/rust/compile-sources.sh index 5e5ecc0dcb..106855a3ab 100755 --- a/nix/docker/test_files/rust/compile-sources.sh +++ b/nix/docker/test_files/rust/compile-sources.sh @@ -40,6 +40,29 @@ compile hello compile panic compile overflow "-C overflow-checks=on" +function compile_proc_macro() { + local proj="${src_dir}/../proc_macro" + + echo "=== Building proc-macro workspace (cargo) ===" + cargo build --manifest-path "${proj}/Cargo.toml" --offline + + local built="${proj}/target/debug/test_macro" + if [ ! -f "${built}" ]; then + echo "ERROR: built test_macro binary not found at ${built}" >&2 + exit 1 + fi + + local binary="${dst_dir}/proc_macro" + cp "${built}" "${binary}" + + echo "=== Patching ${binary} to use ${loader} as PT_INTERP ===" + patchelf --set-interpreter "${loader}" --remove-rpath "${binary}" + + rm -rf "${proj}/target" +} + +compile_proc_macro + echo "=== All binaries compiled ===" ls -la "${dst_dir}" diff --git a/nix/docker/test_files/rust/proc_macro/Cargo.lock b/nix/docker/test_files/rust/proc_macro/Cargo.lock new file mode 100644 index 0000000000..acab3fa0b9 --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "echo_macro" +version = "0.0.0" + +[[package]] +name = "test_macro" +version = "0.0.0" +dependencies = [ + "echo_macro", +] diff --git a/nix/docker/test_files/rust/proc_macro/Cargo.toml b/nix/docker/test_files/rust/proc_macro/Cargo.toml new file mode 100644 index 0000000000..d54955de3d --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["echo_macro", "test_macro"] diff --git a/nix/docker/test_files/rust/proc_macro/echo_macro/Cargo.toml b/nix/docker/test_files/rust/proc_macro/echo_macro/Cargo.toml new file mode 100644 index 0000000000..b0f92ce75a --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/echo_macro/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "echo_macro" +version = "0.0.0" +edition = "2024" +publish = false + +[lib] +proc-macro = true diff --git a/nix/docker/test_files/rust/proc_macro/echo_macro/src/lib.rs b/nix/docker/test_files/rust/proc_macro/echo_macro/src/lib.rs new file mode 100644 index 0000000000..e4b90d58fe --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/echo_macro/src/lib.rs @@ -0,0 +1,6 @@ +use proc_macro::TokenStream; + +#[proc_macro] +pub fn define_echo(item: TokenStream) -> TokenStream { + format!("fn echo() -> u32 {{ {item} }}").parse().unwrap() +} diff --git a/nix/docker/test_files/rust/proc_macro/test_macro/Cargo.toml b/nix/docker/test_files/rust/proc_macro/test_macro/Cargo.toml new file mode 100644 index 0000000000..25b6ba8e45 --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/test_macro/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "test_macro" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +echo_macro = { path = "../echo_macro" } diff --git a/nix/docker/test_files/rust/proc_macro/test_macro/src/main.rs b/nix/docker/test_files/rust/proc_macro/test_macro/src/main.rs new file mode 100644 index 0000000000..77718b9d75 --- /dev/null +++ b/nix/docker/test_files/rust/proc_macro/test_macro/src/main.rs @@ -0,0 +1,9 @@ +use echo_macro::define_echo; + +define_echo!(42); + +fn main() { + let a = echo(); + println!("proc-macro answer = {a}"); + assert_eq!(a, 42, "proc-macro expansion produced the wrong value"); +} diff --git a/nix/docker/test_files/rust/run-binaries.sh b/nix/docker/test_files/rust/run-binaries.sh index b627c12609..4cafee00ec 100755 --- a/nix/docker/test_files/rust/run-binaries.sh +++ b/nix/docker/test_files/rust/run-binaries.sh @@ -1,7 +1,7 @@ #!/bin/bash # Run pre-compiled Rust binaries and confirm each emits its expected diagnostic. # Binaries must already exist in as for name in -# {hello,panic,overflow}. +# {hello,panic,overflow,proc_macro}. set -eo pipefail @@ -54,12 +54,13 @@ declare -A expect=( [hello]="Hello from main thread" [panic]="explicit panic from test" [overflow]="attempt to add with overflow" + [proc_macro]="proc-macro answer = 42" ) -for name in hello panic overflow; do +for name in hello panic overflow proc_macro; do binary="${bins_dir}/${name}" - if [ "${name}" = "hello" ]; then + if [ "${name}" = "hello" ] || [ "${name}" = "proc_macro" ]; then expected_rc=0 else expected_rc=nonzero diff --git a/nix/packages.nix b/nix/packages.nix index 41d7e97328..dbd9597c25 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -15,6 +15,8 @@ let runClangTidy = pkgs.writeShellScriptBin "run-clang-tidy" '' exec ${pkgs.python3}/bin/python3 ${llvmPackages.clang-unwrapped}/bin/run-clang-tidy "$@" ''; + + rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ../rust-toolchain.toml; in { inherit @@ -63,14 +65,10 @@ in vim zip # Rust packages - cargo cargo-audit cargo-llvm-cov cargo-nextest - clippy corrosion - rust-analyzer - rustc - rustfmt + rustToolchain ]; } diff --git a/nix/utils.nix b/nix/utils.nix index d83e612c16..0b70183ef3 100644 --- a/nix/utils.nix +++ b/nix/utils.nix @@ -1,4 +1,8 @@ -{ nixpkgs, nixpkgs-custom-glibc }: +{ + nixpkgs, + nixpkgs-custom-glibc, + rust-overlay, +}: function: nixpkgs.lib.genAttrs [ @@ -10,7 +14,12 @@ nixpkgs.lib.genAttrs ( system: function { - pkgs = import nixpkgs { inherit system; }; + # rust-overlay adds `pkgs.rust-bin`, from which we build the pinned Rust + # toolchain (see packages.nix). Consumed by both the CI image and dev shell. + pkgs = import nixpkgs { + inherit system; + overlays = [ (import rust-overlay) ]; + }; # glibc 2.31 — matches the system libc on Ubuntu 20.04 LTS. Sourced # from the nixpkgs snapshot pinned via the `nixpkgs-custom-glibc` # flake input, so the build uses the compiler from that snapshot diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dbc9e74c5d..3206cf59c0 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,8 +1,4 @@ -# Rust toolchain pin for rustup-based CI runners and local development. -# rustup reads this file and installs the pinned toolchain (see the -# prepare-runner action in XRPLF/actions, which runs `rustup toolchain install`). -# NOTE: the Nix CI image and development shell ignore this file; its rustc comes from flake.lock. [toolchain] channel = "1.95" -components = ["rustfmt", "clippy"] +components = ["rustfmt", "clippy", "rust-analyzer"] profile = "minimal"