diff --git a/.cargo/config.toml.offline b/.cargo/config.toml.offline new file mode 100644 index 000000000000..f98e1b632fa7 --- /dev/null +++ b/.cargo/config.toml.offline @@ -0,0 +1,32 @@ +# Linux +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-gnu-gcc" + +[target.armv7-unknown-linux-musleabihf] +linker = "arm-linux-gnueabihf-gcc" + +[target.powerpc64le-unknown-linux-musl] +linker = "powerpc64le-linux-gnu-gcc" + +[target.riscv64gc-unknown-linux-musl] +linker = "riscv64-linux-gnu-gcc" + +[target.x86_64-unknown-linux-musl] +linker = "x86_64-linux-gnu-gcc" + +# Windows +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" + +# macOS +# [target.aarch64-apple-darwin] +# linker = "aarch64-apple-darwin-gcc" + +# [target.x86_64-apple-darwin] +# linker = "x86_64-apple-darwin-gcc" + +[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +# The directory for this source is set to RUST_VENDORED_SOURCES by rust/Makefile.am diff --git a/.github/workflows/build-depends.yml b/.github/workflows/build-depends.yml index e79c5fa4248e..7ff24d85e869 100644 --- a/.github/workflows/build-depends.yml +++ b/.github/workflows/build-depends.yml @@ -15,6 +15,11 @@ on: description: "Short hash of the CI base image manifest for cache busting" required: true type: string + rust-vendor-artifact: + description: "Artifact holding freshly generated Rust vendor archives" + required: false + type: string + default: "" runs-on: description: "Runner label to use (e.g., ubuntu-24.04 or ubuntu-24.04-arm)" required: true @@ -141,8 +146,32 @@ jobs: uses: actions/cache/restore@v5 with: path: depends/sources - key: depends-sources-${{ hashFiles('depends/packages/*') }} - restore-keys: depends-sources- + key: depends-sources-v2-${{ hashFiles('depends/packages/*', 'depends/patches/native_cxxbridge/Cargo.lock', 'Cargo.lock') }} + restore-keys: depends-sources-v2- + + - name: Restore Rust vendor sources + id: rust-vendor-cache + if: inputs.build-target == 'linux64_rust' + uses: actions/cache/restore@v5 + with: + path: | + depends/sources/native_cxxbridge-*-vendored.tar.gz + depends/sources/vendored-crates-*.tar.gz + key: depends-rust-vendor-sources-${{ hashFiles('depends/Makefile', 'depends/funcs.mk', 'depends/packages/native_cxxbridge.mk', 'depends/packages/native_rust.mk', 'depends/patches/native_cxxbridge/Cargo.lock', 'Cargo.lock') }} + + - name: Download Rust vendor sources + if: inputs.build-target == 'linux64_rust' && steps.rust-vendor-cache.outputs.cache-hit != 'true' && inputs.rust-vendor-artifact != '' + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.rust-vendor-artifact }} + path: depends/sources + + - name: Check Rust vendor sources are present + if: inputs.build-target == 'linux64_rust' && steps.rust-vendor-cache.outputs.cache-hit != 'true' && inputs.rust-vendor-artifact == '' + run: | + echo "::error::Rust vendor source cache missed and no same-run artifact was provided" + exit 1 + shell: bash - name: Restore SDKs cache id: sdk-cache diff --git a/.github/workflows/build-src.yml b/.github/workflows/build-src.yml index 47a757d3941e..64608392dfe2 100644 --- a/.github/workflows/build-src.yml +++ b/.github/workflows/build-src.yml @@ -29,6 +29,11 @@ on: required: false type: string default: "" + rust-vendor-artifact: + description: "Artifact holding freshly generated Rust vendor archives" + required: false + type: string + default: "" sdk-artifact: description: "Artifact holding prepared SDKs, used if the cache restore misses" required: false @@ -97,6 +102,30 @@ jobs: exit 1 shell: bash + - name: Restore Rust vendor sources + id: rust-vendor-cache + if: inputs.build-target == 'linux64_rust' + uses: actions/cache/restore@v5 + with: + path: | + depends/sources/native_cxxbridge-*-vendored.tar.gz + depends/sources/vendored-crates-*.tar.gz + key: depends-rust-vendor-sources-${{ hashFiles('depends/Makefile', 'depends/funcs.mk', 'depends/packages/native_cxxbridge.mk', 'depends/packages/native_rust.mk', 'depends/patches/native_cxxbridge/Cargo.lock', 'Cargo.lock') }} + + - name: Download Rust vendor sources + if: inputs.build-target == 'linux64_rust' && steps.rust-vendor-cache.outputs.cache-hit != 'true' && inputs.rust-vendor-artifact != '' + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.rust-vendor-artifact }} + path: depends/sources + + - name: Check Rust vendor sources are present + if: inputs.build-target == 'linux64_rust' && steps.rust-vendor-cache.outputs.cache-hit != 'true' && inputs.rust-vendor-artifact == '' + run: | + echo "::error::Rust vendor source cache missed and no same-run artifact was provided" + exit 1 + shell: bash + - name: Restore depends cache id: depends-cache uses: actions/cache/restore@v5 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce8191dc99ea..0ee3c4b93e3e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -141,6 +141,18 @@ jobs: base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} + depends-linux64_rust: + name: x86_64-pc-linux-gnu_rust + uses: ./.github/workflows/build-depends.yml + needs: [check-skip, container, cache-sources] + if: ${{ vars.SKIP_LINUX64_RUST == '' }} + with: + build-target: linux64_rust + container-path: ${{ needs.container.outputs.path }} + base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }} + rust-vendor-artifact: ${{ needs.cache-sources.outputs.rust-vendor-artifact }} + runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} + depends-linux64_multiprocess: name: linux64_multiprocess uses: ./.github/workflows/build-depends.yml @@ -222,6 +234,21 @@ jobs: depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }} runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} + src-linux64_rust: + name: linux64_rust-build + uses: ./.github/workflows/build-src.yml + needs: [check-skip, container, cache-sources, depends-linux64_rust] + if: ${{ vars.SKIP_LINUX64_RUST == '' }} + with: + build-target: linux64_rust + container-path: ${{ needs.container.outputs.path }} + depends-key: ${{ needs.depends-linux64_rust.outputs.key }} + depends-host: ${{ needs.depends-linux64_rust.outputs.host }} + depends-dep-opts: ${{ needs.depends-linux64_rust.outputs.dep-opts }} + depends-artifact: ${{ needs.depends-linux64_rust.outputs.built-artifact }} + rust-vendor-artifact: ${{ needs.cache-sources.outputs.rust-vendor-artifact }} + runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }} + src-linux64_asan: name: linux64_asan-build uses: ./.github/workflows/build-src.yml diff --git a/.github/workflows/cache-depends-sources.yml b/.github/workflows/cache-depends-sources.yml index 122851d8cd72..bffbe357d1cf 100644 --- a/.github/workflows/cache-depends-sources.yml +++ b/.github/workflows/cache-depends-sources.yml @@ -8,6 +8,10 @@ on: required: false type: string default: ubuntu-24.04-arm + outputs: + rust-vendor-artifact: + description: "Artifact holding freshly generated Rust vendor archives" + value: ${{ jobs.cache-sources.outputs.rust-vendor-artifact }} schedule: # Run daily at 6 AM UTC on the default branch to keep cache warm - cron: '0 6 * * *' @@ -18,6 +22,8 @@ jobs: # Intentionally keep scheduled cache warming on GitHub-hosted ARM runners. # Blacksmith caches are expected to persist long enough without a warmup cron. runs-on: ${{ inputs.runs-on || 'ubuntu-24.04-arm' }} + outputs: + rust-vendor-artifact: ${{ steps.vendor-artifact.outputs.name }} steps: - name: Checkout code uses: actions/checkout@v6 @@ -31,10 +37,43 @@ jobs: uses: actions/cache@v5 with: path: depends/sources - key: depends-sources-${{ hashFiles('depends/packages/*') }} - restore-keys: depends-sources- + key: depends-sources-v2-${{ hashFiles('depends/packages/*', 'depends/patches/native_cxxbridge/Cargo.lock', 'Cargo.lock') }} + restore-keys: depends-sources-v2- lookup-only: true + - name: Cache Rust vendor sources + id: rust-vendor-cache + uses: actions/cache@v5 + with: + path: | + depends/sources/native_cxxbridge-*-vendored.tar.gz + depends/sources/vendored-crates-*.tar.gz + key: depends-rust-vendor-sources-${{ hashFiles('depends/Makefile', 'depends/funcs.mk', 'depends/packages/native_cxxbridge.mk', 'depends/packages/native_rust.mk', 'depends/patches/native_cxxbridge/Cargo.lock', 'Cargo.lock') }} + - name: Download sources - if: steps.cache-check.outputs.cache-hit != 'true' - run: make -C depends download + if: | + steps.cache-check.outputs.cache-hit != 'true' || + steps.rust-vendor-cache.outputs.cache-hit != 'true' + run: | + make -C depends RUST=1 download + # The cache producer normally runs on ARM, while the Rust consumer + # runs on x86_64. Native Rust is selected from the build architecture, + # so fetch the x86_64 compiler archive explicitly as well. + make -C depends BUILD=x86_64-pc-linux-gnu RUST=1 download-one + + - name: Select Rust vendor artifact + id: vendor-artifact + if: github.event_name != 'schedule' && steps.rust-vendor-cache.outputs.cache-hit != 'true' + run: echo "name=depends-rust-vendor-sources-${{ github.run_id }}" >> "$GITHUB_OUTPUT" + + - name: Upload Rust vendor sources + if: steps.vendor-artifact.outputs.name != '' + uses: actions/upload-artifact@v6 + with: + name: ${{ steps.vendor-artifact.outputs.name }} + path: | + depends/sources/native_cxxbridge-*-vendored.tar.gz + depends/sources/vendored-crates-*.tar.gz + compression-level: 0 + retention-days: 1 + overwrite: true diff --git a/.gitignore b/.gitignore index eab9f3f83ec1..e6638debe3e7 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,27 @@ compile_commands.json # Linux perf profiling artifacts perf.data perf.data.old + +# Generated by Cargo +/target/ +/rust/target/ + +# Cargo configuration +/.cargo/.configured-for-* +/.cargo/config +/.cargo/config.toml +/.cargo/dash-build.toml +/.cargo/*-linker + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# FFI bridge +/rust/*/gen/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000000..1a4a89d2d32e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,304 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "built" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" + +[[package]] +name = "cc" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9066c49992464636f92905fa096ec58baaa4d57ec19a5c096c68d3e25ef3d136" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "chirp" +version = "0.1.0" +dependencies = [ + "built", + "cxx", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cxx" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe442a792c7c736eea18b32a7f8a3b63cf8aafabda6760042dc2fdeda456291" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3184a94384c663718698311a78a51ac00c484c10b4eeac06fb0a068c5f64fa2" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0148d8fd1199329ddf1d157a5e134e51ceff37c6a7ddd38615c399d81cb05d8d" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52850339faed2eaadd24e286dc1d8268cc6f8a7bd9524d713adc9099566b4c89" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c77c856545d886c9bd5215409ebb63b925e262135248b50c79e5a5f194ee47c" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashrust" +version = "0.1.0" +dependencies = [ + "chirp", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000000..7defb05f6393 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ + "rust/chirp", + "rust/dashrust", +] +resolver = "2" + +[profile.release] +codegen-units = 1 +lto = true +panic = "abort" + +[profile.dev] +codegen-units = 1 +panic = "abort" diff --git a/Makefile.am b/Makefile.am index ea2cba9fd2fa..6c77fdd262ab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,10 +8,15 @@ print-%: FORCE @echo '$*'='$($*)' ACLOCAL_AMFLAGS = -I build-aux/m4 -SUBDIRS = src +SUBDIRS = +if ENABLE_RUST +SUBDIRS += rust +endif +SUBDIRS += src if ENABLE_MAN SUBDIRS += doc/man endif +DIST_SUBDIRS = rust src doc/man .PHONY: deploy FORCE .INTERMEDIATE: $(COVERAGE_INFO) @@ -65,6 +70,13 @@ WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/dash.ico \ OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_INSTALLER_ICONS) \ $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh +CARGO_FILES = \ + $(top_srcdir)/.cargo/config.toml.offline \ + $(top_srcdir)/Cargo.lock \ + $(top_srcdir)/Cargo.toml \ + $(top_srcdir)/rust-toolchain.toml \ + $(top_srcdir)/contrib/devtools/cargo-vendor-git-sources.sh + COVERAGE_INFO = $(COV_TOOL_WRAPPER) baseline.info \ test_dash_filtered.info total_coverage.info \ baseline_filtered.info functional_test.info functional_test_filtered.info \ @@ -249,7 +261,7 @@ endif dist_noinst_SCRIPTS = autogen.sh -EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) +EXTRA_DIST = $(DIST_SHARE) $(DIST_CONTRIB) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) $(CARGO_FILES) EXTRA_DIST += \ test/functional \ diff --git a/ci/dash/matrix.sh b/ci/dash/matrix.sh index 49117d71c82f..f75bbb1da650 100755 --- a/ci/dash/matrix.sh +++ b/ci/dash/matrix.sh @@ -20,6 +20,8 @@ if [ "$BUILD_TARGET" = "aarch64-linux" ]; then source ./ci/test/00_setup_env_aarch64.sh elif [ "$BUILD_TARGET" = "linux64" ]; then source ./ci/test/00_setup_env_native_qt5.sh +elif [ "$BUILD_TARGET" = "linux64_rust" ]; then + source ./ci/test/00_setup_env_native_rust.sh elif [ "$BUILD_TARGET" = "linux64_asan" ]; then source ./ci/test/00_setup_env_native_asan.sh elif [ "$BUILD_TARGET" = "linux64_fuzz" ]; then diff --git a/ci/test/00_setup_env_native_rust.sh b/ci/test/00_setup_env_native_rust.sh new file mode 100755 index 000000000000..fe12acd24db9 --- /dev/null +++ b/ci/test/00_setup_env_native_rust.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +export CONTAINER_NAME=ci_native_rust +export HOST=x86_64-pc-linux-gnu +export DEP_OPTS="RUST=1 NO_QT=1 NO_QR=1" +export RUN_FUNCTIONAL_TESTS="false" +export GOAL="install" +export BITCOIN_CONFIG="--enable-rust --with-gui=no --enable-reduce-exports" diff --git a/configure.ac b/configure.ac index d36f8046cfa2..ac779eac1ce2 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,7 @@ if test "${ARFLAGS+set}" != "set"; then ARFLAGS="cr" fi +AC_CANONICAL_BUILD AC_CANONICAL_HOST AH_TOP([#ifndef DASH_CONFIG_H]) @@ -60,7 +61,9 @@ AM_INIT_AUTOMAKE([1.13 no-define subdir-objects foreign]) AM_MAINTAINER_MODE([enable]) dnl make the compilation flags quiet unless V=1 is used -AM_SILENT_RULES([yes]) +m4_pattern_allow([AM_DEFAULT_VERBOSITY]) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],[AM_DEFAULT_VERBOSITY=1]) +AM_CONDITIONAL([SILENT_RULES], [test "$AM_DEFAULT_VERBOSITY" = '0']) dnl Compiler checks (here before libtool). if test "${CXXFLAGS+set}" = "set"; then @@ -125,6 +128,47 @@ AC_PATH_TOOL([DSYMUTIL], [dsymutil]) AC_PATH_PROG([DOXYGEN], [doxygen]) AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) +dnl Rust toolchain detection. These programs are required only when a +dnl Rust-dependent component is explicitly enabled. +AC_PATH_PROG([CARGO], [cargo]) +AC_PATH_PROG([CXXBRIDGE], [cxxbridge]) +AC_PATH_PROG([RUSTC], [rustc]) +CXXBRIDGE_REQUIRED_VERSION="1.0.198" +RUSTC_REQUIRED_VERSION="1.92.0" +CARGO_REQUIRED_VERSION="$RUSTC_REQUIRED_VERSION" +AC_SUBST([CXXBRIDGE_REQUIRED_VERSION]) + +AC_ARG_VAR([CARGO_INCREMENTAL], [Enable incremental compilation for Rust]) +if test "${CARGO_INCREMENTAL+set}" != "set"; then + CARGO_INCREMENTAL="0" +fi + +AC_ARG_VAR([RUSTFLAGS], [Flags for Rust compiler]) +if test "${RUSTFLAGS+set}" != "set"; then + RUSTFLAGS="-C embed-bitcode=yes -C relocation-model=pic" +fi + +AC_ARG_VAR([RUST_VENDORED_SOURCES], [Directory containing vendored Rust crate sources for offline builds]) + +AC_ARG_ENABLE([rust], + [AS_HELP_STRING([--enable-rust], + [build the Rust/C++ bridge smoke component (default is no)])], + [enable_rust=$enableval], + [enable_rust=no]) + +AC_ARG_ENABLE([online-rust], + [AS_HELP_STRING([--enable-online-rust], + [allow Cargo to fetch Rust dependencies from the internet (default is no, using vendored dependencies offline)])], + [enable_online_rust=$enableval], + [enable_online_rust=no]) +AM_CONDITIONAL([ENABLE_ONLINE_RUST], [test "$enable_online_rust" != "no"]) + +dnl Cargo features enabling optional components of the umbrella crate +dnl (rust/dashrust). Populated by feature-specific configure flags below; +dnl empty means only the always-on components are built. +RUST_CRATE_FEATURES="" +AC_SUBST(RUST_CRATE_FEATURES) + AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--disable-wallet], [disable wallet (enabled by default)])], @@ -275,6 +319,7 @@ AC_ARG_ENABLE([debug], [use compiler flags and macros suited for debugging (default is no)])], [enable_debug=$enableval], [enable_debug=no]) +AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "yes"]) dnl Enable exception stacktraces AC_ARG_ENABLE([stacktraces], @@ -1694,6 +1739,193 @@ if test "$use_zmq" = "yes"; then esac fi +dnl Rust support is deliberately confined to the hosts we validate (the Guix +dnl release set plus native development hosts). Any other host resolves to an +dnl empty triple, so --enable-rust fails explicitly rather than emitting +dnl binaries for a target we never build or test. +AC_DEFUN([RS_SET_TRIPLE], [ + rs_triple_libc=$3 + case $2 in + *-linux-android*) dnl unsupported; must stay above the generic linux arms + $1="" ;; + aarch64-*-linux*|arm64-*-linux*) + $1="aarch64-unknown-linux-${rs_triple_libc}" ;; + riscv64-*-linux*) $1="riscv64gc-unknown-linux-${rs_triple_libc}" ;; + x86_64-*-linux*) $1="x86_64-unknown-linux-${rs_triple_libc}" ;; + x86_64-*-darwin*) $1="x86_64-apple-darwin" ;; + aarch64-*-darwin*|arm64-*-darwin*) + $1="aarch64-apple-darwin" ;; + x86_64-*-mingw*) $1="x86_64-pc-windows-gnu" ;; + *) $1="" ;; + esac +]) + +RUST_LIBS="" +RUST_MACOS_DEPLOYMENT_TARGET="" +RUST_OSX_SDK="" +RUST_TARGET="" +RUST_NATIVE="" + +if test "$enable_rust" = "yes"; then + if test -z "$CARGO" || test -z "$RUSTC" || test -z "$CXXBRIDGE"; then + AC_MSG_ERROR([cargo, rustc and cxxbridge are required by --enable-rust]) + fi + AC_MSG_CHECKING([for cxxbridge $CXXBRIDGE_REQUIRED_VERSION]) + cxxbridge_version=`$CXXBRIDGE --version 2>/dev/null` + if test "$cxxbridge_version" != "cxxbridge $CXXBRIDGE_REQUIRED_VERSION"; then + AC_MSG_RESULT([no ($cxxbridge_version)]) + AC_MSG_ERROR([cxxbridge $CXXBRIDGE_REQUIRED_VERSION is required by --enable-rust]) + fi + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([for cargo $CARGO_REQUIRED_VERSION]) + cargo_version=`$CARGO --version 2>/dev/null | cut -d ' ' -f 2` + if test "$cargo_version" != "$CARGO_REQUIRED_VERSION"; then + AC_MSG_RESULT([no ($cargo_version)]) + AC_MSG_ERROR([cargo $CARGO_REQUIRED_VERSION is required by --enable-rust]) + fi + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([for rustc $RUSTC_REQUIRED_VERSION]) + rustc_version=`$RUSTC --version 2>/dev/null | cut -d ' ' -f 2` + if test "$rustc_version" != "$RUSTC_REQUIRED_VERSION"; then + AC_MSG_RESULT([no ($rustc_version)]) + AC_MSG_ERROR([rustc $RUSTC_REQUIRED_VERSION is required by --enable-rust]) + fi + AC_MSG_RESULT([yes]) + rust_uses_depends=no + if test -n "$depends_prefix" && test "$RUSTC" = "$depends_prefix/native/bin/rustc"; then + rust_uses_depends=yes + fi + if test "$enable_online_rust" = "no"; then + if test -z "$RUST_VENDORED_SOURCES"; then + AC_MSG_ERROR([--enable-rust requires RUST_VENDORED_SOURCES for offline builds or --enable-online-rust]) + fi + if test ! -d "$RUST_VENDORED_SOURCES"; then + AC_MSG_ERROR([Rust vendored sources directory does not exist: $RUST_VENDORED_SOURCES]) + fi + fi + AC_DEFINE([ENABLE_RUST], [1], [Define this symbol to enable Rust components]) + + case $host in + *mingw*) + RUST_LIBS="$RUST_LIBS -luserenv -lntdll" + ;; + *linux*) + RUST_LIBS="$RUST_LIBS -ldl" + ;; + esac + + case $host in + *darwin*) + AC_MSG_CHECKING([for target macOS version]) + if test -z "$OSX_MIN_VERSION" && test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Cross-compilation requires OSX_MIN_VERSION to be set, cannot continue!]) + fi + if test -n "$OSX_MIN_VERSION"; then + RUST_MACOS_DEPLOYMENT_TARGET="$OSX_MIN_VERSION" + elif test -n "$MACOSX_DEPLOYMENT_TARGET"; then + RUST_MACOS_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" + else + dnl Match the C/C++ compiler's effective deployment target so Rust + dnl objects never claim a different minimum macOS version than the + dnl C++ objects they are linked with. + rust_macos_min_raw=`echo __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ | $CXX $CXXFLAGS -E -xc++ - 2>/dev/null | tail -n 1` + case $rust_macos_min_raw in + [[0-9]][[0-9]]*) + RUST_MACOS_DEPLOYMENT_TARGET="`expr $rust_macos_min_raw / 10000`.`expr $rust_macos_min_raw % 10000 / 100`" + ;; + *) + RUST_MACOS_DEPLOYMENT_TARGET=`sw_vers -productVersion` + ;; + esac + fi + AC_MSG_RESULT([$RUST_MACOS_DEPLOYMENT_TARGET]) + ;; + esac + + AC_MSG_CHECKING([for Rust target]) + rust_target_libc=gnu + case $host in + *-linux-musl*) rust_target_libc=musl ;; + esac + if test "$rust_uses_depends" = "yes"; then + rust_target_libc=musl + fi + RS_SET_TRIPLE([RUST_TARGET], [$host], [$rust_target_libc]) + if test "$RUST_TARGET" = ""; then + AC_MSG_RESULT([unknown]) + AC_MSG_ERROR([Unsupported target for Rust: $host]) + fi + AC_MSG_RESULT([$RUST_TARGET]) + + AC_MSG_CHECKING([for Rust target standard library]) + rust_target_libdir=`$RUSTC --print target-libdir --target "$RUST_TARGET" 2>/dev/null` + if test -z "$rust_target_libdir" || test ! -d "$rust_target_libdir"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Rust standard library for $RUST_TARGET is not installed]) + fi + AC_MSG_RESULT([$rust_target_libdir]) + + case $host in + *darwin*) + AC_MSG_CHECKING([for Xcode SDK]) + if test -z "$OSX_SDK" && test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Cross-compilation requires OSX_SDK to be set to path of Xcode SDK, cannot continue!]) + fi + RUST_OSX_SDK="${OSX_SDK:-$(xcrun --sdk macosx --show-sdk-path)}" + AC_MSG_RESULT([$RUST_OSX_SDK]) + ;; + esac + + AC_MSG_CHECKING([for Rust host]) + rust_native_libc=gnu + if test "$rust_uses_depends" = "no"; then + case $build in + *-linux-musl*) rust_native_libc=musl ;; + esac + fi + RS_SET_TRIPLE([RUST_NATIVE], [$build], [$rust_native_libc]) + if test "$RUST_NATIVE" = ""; then + if test "$cross_compiling" = "yes"; then + AC_MSG_RESULT([unknown]) + AC_MSG_ERROR([Unsupported host for Rust: $build]) + fi + RUST_NATIVE="$RUST_TARGET" + fi + AC_MSG_RESULT([$RUST_NATIVE]) + + AC_MSG_CHECKING([for host archiver]) + if test -z "$NATIVE_AR"; then + if test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Cross-compilation requires NATIVE_AR to be set to host archiver, cannot continue!]) + fi + NATIVE_AR="$AR" + fi + AC_MSG_RESULT([$NATIVE_AR]) + + AC_MSG_CHECKING([for host C compiler]) + if test -z "$NATIVE_CC"; then + if test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Cross-compilation requires NATIVE_CC to be set to host C compiler, cannot continue!]) + fi + NATIVE_CC="$CC" + fi + AC_MSG_RESULT([$NATIVE_CC]) + + AC_MSG_CHECKING([for host C++ compiler]) + if test -z "$NATIVE_CXX"; then + if test "$cross_compiling" = "yes"; then + AC_MSG_ERROR([Cross-compilation requires NATIVE_CXX to be set to host C++ compiler, cannot continue!]) + fi + NATIVE_CXX="$CXX" + fi + AC_MSG_RESULT([$NATIVE_CXX]) + + if test "$rust_uses_depends" = "yes"; then + RUSTUP_TOOLCHAIN="" + fi +fi +AC_SUBST(RUSTUP_TOOLCHAIN) + dnl check if libgmp is present TEMP_CPPFLAGS="$CPPFLAGS" TEMP_LDFLAGS="$LDFLAGS" @@ -1953,6 +2185,8 @@ AM_CONDITIONAL([USE_QRCODE], [test "$use_qr" = "yes"]) AM_CONDITIONAL([USE_LCOV], [test "$use_lcov" = "yes"]) AM_CONDITIONAL([USE_LIBEVENT], [test "$use_libevent" = "yes"]) AM_CONDITIONAL([HARDEN], [test "$use_hardening" = "yes"]) +AM_CONDITIONAL([ENABLE_RUST], [test "$enable_rust" = "yes"]) +AM_CONDITIONAL([ENABLE_RUST_FEATURES], [test -n "$RUST_CRATE_FEATURES"]) AM_CONDITIONAL([ENABLE_SSSE3], [test "$enable_ssse3" = "yes"]) AM_CONDITIONAL([ENABLE_SSE42], [test "$enable_sse42" = "yes"]) AM_CONDITIONAL([ENABLE_SSE41], [test "$enable_sse41" = "yes"]) @@ -1998,6 +2232,17 @@ AC_SUBST(BITCOIN_WALLET_TOOL_NAME) AC_SUBST(BITCOIN_MP_NODE_NAME) AC_SUBST(BITCOIN_MP_GUI_NAME) +AC_SUBST(NATIVE_AR) +AC_SUBST(NATIVE_CC) +AC_SUBST(NATIVE_CXX) +AC_SUBST(RUST_LIBS) +AC_SUBST(RUST_MACOS_DEPLOYMENT_TARGET) +AC_SUBST(RUST_NATIVE) +AC_SUBST(RUST_OSX_SDK) +AC_SUBST(RUST_TARGET) +AC_SUBST(RUST_VENDORED_SOURCES) +AC_SUBST(RUSTUP_TOOLCHAIN) + AC_SUBST(RELDFLAGS) AC_SUBST(CORE_LDFLAGS) AC_SUBST(CORE_CPPFLAGS) @@ -2051,7 +2296,7 @@ AC_SUBST(HAVE_MM_PREFETCH) AC_SUBST(HAVE_STRONG_GETAUXVAL) AC_SUBST(ANDROID_ARCH) AC_SUBST(HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR) -AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini]) +AC_CONFIG_FILES([Makefile rust/Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])]) AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py]) @@ -2145,3 +2390,17 @@ echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $CORE_LDFLAGS $BACKTRA echo " AR = $AR" echo " ARFLAGS = $ARFLAGS" echo +echo " CARGO = $CARGO" +echo " CARGO_INCREMENTAL = $CARGO_INCREMENTAL" +echo " CXXBRIDGE = $CXXBRIDGE" +echo " NATIVE_AR = $NATIVE_AR" +echo " NATIVE_CC = $NATIVE_CC" +echo " NATIVE_CXX = $NATIVE_CXX" +echo " RUSTC = $RUSTC" +echo " RUSTFLAGS = $RUSTFLAGS" +echo " RUST_MACOS_DEPLOYMENT_TARGET = $RUST_MACOS_DEPLOYMENT_TARGET" +echo " RUST_NATIVE = $RUST_NATIVE" +echo " RUST_OSX_SDK = $RUST_OSX_SDK" +echo " RUST_TARGET = $RUST_TARGET" +echo " RUST_VENDORED_SOURCES = $RUST_VENDORED_SOURCES" +echo diff --git a/contrib/devtools/cargo-vendor-git-sources.sh b/contrib/devtools/cargo-vendor-git-sources.sh new file mode 100755 index 000000000000..7028a402bca4 --- /dev/null +++ b/contrib/devtools/cargo-vendor-git-sources.sh @@ -0,0 +1,39 @@ +#!/bin/sh +export LC_ALL=C + +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Emit cargo source-replacement stanzas for every git source in a Cargo.lock, +# for appending to an offline .cargo/config.toml whose vendored-sources +# directory holds the output of `cargo vendor`. `cargo vendor` prints these +# stanzas itself, but only at vendor time; deriving them from the lockfile +# lets the build system reconstruct the config without re-running vendor. +# +# usage: cargo-vendor-git-sources.sh path/to/Cargo.lock + +set -eu + +if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then + echo "usage: $0 path/to/Cargo.lock" >&2 + exit 1 +fi + +# Lockfile entries look like: +# source = "git+https://github.com/dashpay/platform?tag=v4.1.0#bfc80249..." +# The stanza key is the source without the fragment; the query parameter +# (tag=/branch=/rev=), when present, becomes a key of the same name. +grep '^source = "git+' "$1" | sed 's/^source = "//; s/"$//; s/#.*//' | sort -u | \ +while IFS= read -r src; do + url_query="${src#git+}" + url="${url_query%%\?*}" + printf '\n[source."%s"]\ngit = "%s"\n' "$src" "$url" + case "$url_query" in + *\?*) + query="${url_query#*\?}" + printf '%s = "%s"\n' "${query%%=*}" "${query#*=}" + ;; + esac + printf 'replace-with = "vendored-sources"\n' +done diff --git a/contrib/devtools/update-native-cxxbridge.py b/contrib/devtools/update-native-cxxbridge.py new file mode 100755 index 000000000000..319fe9ada6b3 --- /dev/null +++ b/contrib/devtools/update-native-cxxbridge.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import hashlib +import re +import shutil +import subprocess +import sys +import tarfile +import tempfile +import urllib.request +from pathlib import Path + + +def get_cxx_version(makefile_path: Path) -> str: + content = makefile_path.read_text() + match = re.search(r"\$\(package\)_version:=(.+)", content) + if not match: + raise RuntimeError("Could not find cxx version in makefile") + return match.group(1).strip() + + +def download_and_hash(url: str, dest: Path) -> str: + hasher = hashlib.sha256() + dest.parent.mkdir(parents=True, exist_ok=True) + temporary_path = None + try: + with tempfile.NamedTemporaryFile(dir=dest.parent, delete=False) as output: + temporary_path = Path(output.name) + with urllib.request.urlopen(url) as response: + while chunk := response.read(8192): + hasher.update(chunk) + output.write(chunk) + temporary_path.replace(dest) + except BaseException: + if temporary_path is not None: + temporary_path.unlink(missing_ok=True) + raise + return hasher.hexdigest() + + +def write_stamp(stamps_dir: Path, version: str, sha256: str, file_name: str) -> None: + stamps_dir.mkdir(parents=True, exist_ok=True) + stamp_path = stamps_dir / f".stamp_fetched-native_cxxbridge-{version}-{sha256}.hash" + stamp_path.write_text(f"{sha256} {file_name}\n") + + +def update_value_in_file(path: Path, pattern: str, value: str) -> None: + content = path.read_text() + regex = re.compile(pattern, re.MULTILINE) + new_content, replacements = regex.subn( + lambda match: f"{match.group(1)}{value}{match.group(2) if match.lastindex == 2 else ''}", content + ) + if replacements != 1: + raise RuntimeError(f"Expected one matching value in {path}, found {replacements}") + path.write_text(new_content) + + +def main() -> int: + script_dir = Path(__file__).resolve().parent + repo_root = script_dir / "../.." + repo_root = repo_root.resolve() + + makefile_path = repo_root / "depends/packages/native_cxxbridge.mk" + if not makefile_path.exists(): + print(f"Error: {makefile_path} not found", file=sys.stderr) + return 1 + + version = get_cxx_version(makefile_path) + print(f"cxx version: {version}") + + sources_dir = repo_root / "depends/sources" + file_name = f"native_cxxbridge-{version}.tar.gz" + tarball_path = sources_dir / file_name + url = f"https://github.com/dtolnay/cxx/archive/refs/tags/{version}.tar.gz" + print(f"Downloading {url}") + hash_value = download_and_hash(url, tarball_path) + print(f"sha256: {hash_value}") + + toolchain_path = repo_root / "rust-toolchain.toml" + if not toolchain_path.exists(): + print(f"Error: {toolchain_path} not found", file=sys.stderr) + return 1 + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + print(f"Working in {tmp_path}") + + # Copy rust-toolchain.toml + shutil.copy(toolchain_path, tmp_path / "rust-toolchain.toml") + + # Extract tarball + print(f"Extracting {tarball_path}") + with tarfile.open(tarball_path, "r:gz") as tar: + # getattr keeps this compatible with the older tarfile type stubs + # used by the Python 3.10 lint environment. Supported Python 3.10 + # releases include the security filter at runtime. + getattr(tar, "extractall")(tmp_path, filter="data") + + cxx_dir = tmp_path / f"cxx-{version}" + if not cxx_dir.exists(): + print(f"Error: Expected directory {cxx_dir} not found after extraction", file=sys.stderr) + return 1 + + # Copy rust-toolchain.toml into cxx directory + shutil.copy(toolchain_path, cxx_dir / "rust-toolchain.toml") + + # Run cargo check + print("Running cargo check --release --package=cxxbridge-cmd --bin=cxxbridge") + result = subprocess.run( + ["cargo", "check", "--release", "--package=cxxbridge-cmd", "--bin=cxxbridge"], + cwd=cxx_dir, + ) + if result.returncode != 0: + print("Error: cargo check failed", file=sys.stderr) + return 1 + + # Copy Cargo.lock to patches directory + cargo_lock_src = cxx_dir / "Cargo.lock" + cargo_lock_dst = repo_root / "depends/patches/native_cxxbridge/Cargo.lock" + if not cargo_lock_src.exists(): + print(f"Error: {cargo_lock_src} not found after cargo check", file=sys.stderr) + return 1 + + print("Updating the workspace cxx crates") + result = subprocess.run(["cargo", "update", "-p", "cxx", "--precise", version], cwd=repo_root) + if result.returncode != 0: + print("Error: workspace cargo update failed", file=sys.stderr) + return 1 + + cargo_lock_dst.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(cargo_lock_src, cargo_lock_dst) + print(f"Copied Cargo.lock to {cargo_lock_dst}") + + update_value_in_file(makefile_path, r"^(\$\(package\)_sha256_hash:=).*$", hash_value) + configure_path = repo_root / "configure.ac" + update_value_in_file(configure_path, r'^(CXXBRIDGE_REQUIRED_VERSION=")[^"]*(")$', version) + write_stamp(sources_dir / "download-stamps", version, hash_value, file_name) + + print("\nDone!") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/contrib/devtools/update-rust-hashes.py b/contrib/devtools/update-rust-hashes.py new file mode 100755 index 000000000000..a2cee608f33e --- /dev/null +++ b/contrib/devtools/update-rust-hashes.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2021-2022 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import hashlib +import re +import sys +import urllib.request +from pathlib import Path + +# Rust standard libraries provisioned in rust_stdlib.mk. Confined to the +# hosts we validate (the Guix release set); see rust_stdlib.mk. +CROSS_TARGETS = [ + # Linux + "aarch64-unknown-linux-musl", + "riscv64gc-unknown-linux-musl", + "x86_64-unknown-linux-musl", + # Windows + "x86_64-pc-windows-gnu", + # macOS + "aarch64-apple-darwin", + "x86_64-apple-darwin", +] + +# Native compilers provisioned in native_rust.mk (build hosts for depends) +NATIVE_TARGETS = [ + # Linux + ("aarch64-unknown-linux-gnu", "aarch64_linux"), + ("x86_64-unknown-linux-gnu", "x86_64_linux"), + # macOS + ("aarch64-apple-darwin", "aarch64_darwin"), + ("x86_64-apple-darwin", "x86_64_darwin"), +] + + +def get_rust_version(makefile_path: Path) -> str: + content = makefile_path.read_text() + match = re.search(r"\$\(package\)_version:=(.+)", content) + if not match: + raise RuntimeError("Could not find Rust version in makefile") + return match.group(1).strip() + + +def compute_sha256(url: str) -> str: + hasher = hashlib.sha256() + with urllib.request.urlopen(url) as response: + while chunk := response.read(8192): + hasher.update(chunk) + return hasher.hexdigest() + + +def update_hash_in_file(makefile_path: Path, pattern: str, new_hash: str) -> None: + content = makefile_path.read_text() + regex = re.compile(rf"^(\$\(package\)_{pattern}:=).*$", re.MULTILINE) + if not regex.search(content): + raise RuntimeError(f"Could not find pattern {pattern} in makefile") + new_content = regex.sub(rf"\g<1>{new_hash}", content) + makefile_path.write_text(new_content) + + +def update_version_in_file(path: Path, pattern: str, version: str) -> None: + content = path.read_text() + regex = re.compile(pattern, re.MULTILINE) + new_content, replacements = regex.subn( + lambda match: f"{match.group(1)}{version}{match.group(2) if match.lastindex == 2 else ''}", content + ) + if replacements != 1: + raise RuntimeError(f"Expected one version pin in {path}, found {replacements}") + path.write_text(new_content) + + +def compute_rust_hash(rust_version: str, rust_target: str) -> str: + url = f"https://static.rust-lang.org/dist/rust-{rust_version}-{rust_target}.tar.gz" + return compute_sha256(url) + + +def compute_stdlib_hash(rust_version: str, rust_target: str) -> str: + url = f"https://static.rust-lang.org/dist/rust-std-{rust_version}-{rust_target}.tar.gz" + return compute_sha256(url) + + +def main() -> int: + script_dir = Path(__file__).resolve().parent + native_rust_path = script_dir / "../../depends/packages/native_rust.mk" + native_rust_path = native_rust_path.resolve() + rust_stdlib_path = script_dir / "../../depends/packages/rust_stdlib.mk" + rust_stdlib_path = rust_stdlib_path.resolve() + toolchain_path = (script_dir / "../../rust-toolchain.toml").resolve() + configure_path = (script_dir / "../../configure.ac").resolve() + + for path in (native_rust_path, rust_stdlib_path, toolchain_path, configure_path): + if not path.exists(): + print(f"Error: {path} not found", file=sys.stderr) + return 1 + + rust_version = get_rust_version(native_rust_path) + + print(f"Rust version: {rust_version}\n") + print("Downloading native compiler hashes:") + + native_hashes = {} + for rust_target, makefile_id in NATIVE_TARGETS: + native_hashes[makefile_id] = compute_rust_hash(rust_version, rust_target) + print(f" Downloaded sha256_hash_{makefile_id}") + + print("\nDownloading stdlib hashes:") + stdlib_hashes = {} + for rust_target in CROSS_TARGETS: + stdlib_hashes[rust_target] = compute_stdlib_hash(rust_version, rust_target) + print(f" Downloaded sha256_hash_{rust_target}") + + for makefile_id, hash_value in native_hashes.items(): + update_hash_in_file(native_rust_path, f"sha256_hash_{makefile_id}", hash_value) + for rust_target, hash_value in stdlib_hashes.items(): + update_hash_in_file(rust_stdlib_path, f"sha256_hash_{rust_target}", hash_value) + + update_version_in_file(rust_stdlib_path, r"^(\$\(package\)_version:=).*$", rust_version) + update_version_in_file(toolchain_path, r'^(channel = ")[^"]*(")$', rust_version) + update_version_in_file(configure_path, r'^(RUSTC_REQUIRED_VERSION=")[^"]*(")$', rust_version) + print("\nSynchronized rust_stdlib.mk, rust-toolchain.toml, and configure.ac") + + print("\nDone!") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/depends/Makefile b/depends/Makefile index c8510f4cc010..de1fad05b415 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -44,6 +44,7 @@ NO_UPNP ?= NO_USDT ?= NO_NATPMP ?= MULTIPROCESS ?= +RUST ?= LTO ?= NO_HARDEN ?= FALLBACK_DOWNLOAD_PATH ?= http://dash-depends-sources.s3-website-us-west-2.amazonaws.com @@ -60,6 +61,12 @@ DOWNLOAD_RETRIES:=3 HOST_ID_SALT ?= salt BUILD_ID_SALT ?= salt +CRATE_REGISTRY := vendored-sources +CRATE_LOCK = $(BASEDIR)/../Cargo.lock +CRATE_LOCK_HASH = $(shell $(build_SHA256SUM) $(CRATE_LOCK) | cut -c-$(HASH_LENGTH)) +CRATE_ARCHIVE = $(SOURCES_PATH)/vendored-crates-$(CRATE_LOCK_HASH).tar.gz +CRATE_REGISTRY_STAMP = $(host_prefix)/.$(CRATE_REGISTRY).stamp + ifneq ($(DEBUG),) release_type=debug else @@ -179,6 +186,13 @@ usdt_packages_$(NO_USDT) = $(usdt_$(host_os)_packages) packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(boost_packages_) $(libevent_packages_) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) $(natpmp_packages_) $(usdt_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) +ifeq ($(RUST),1) +packages += $(rust_packages) +native_packages += $(rust_native_packages) +rust_download_targets = download-rust-std vendor-crates +endif + +native_cargo_packages = $(filter native_cxxbridge,$(native_packages)) ifneq ($(zmq_packages_),) packages += $(zmq_packages) @@ -202,9 +216,39 @@ $(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) mkdir -p $(@D) echo copying packages: $^ echo to: $(@D) - cd $(@D); $(foreach package,$^, $(build_TAR) xf $($(package)_cached); ) + cd $(@D); $(foreach package,$^, $(build_TAR) --no-same-owner -xf $($(package)_cached); ) touch $@ +# Vendors Rust crate dependencies for offline builds +# Uses pre-built archive if available, otherwise runs 'cargo vendor' +$(CRATE_REGISTRY_STAMP): $(host_prefix)/.stamp_$(final_build_id) $(CRATE_LOCK) + @rm -rf $(host_prefix)/$(CRATE_REGISTRY) + @if test -f $(CRATE_ARCHIVE); \ + then echo Extracting pre-vendored crates from $(CRATE_ARCHIVE)...; \ + $(build_TAR) --no-same-owner -xf $(CRATE_ARCHIVE) -C $(host_prefix); \ + else echo Vendoring crates...; \ + $(host_prefix)/native/bin/cargo vendor --locked --manifest-path $(BASEDIR)/../Cargo.toml $(host_prefix)/$(CRATE_REGISTRY); \ + fi + @test -d $(host_prefix)/$(CRATE_REGISTRY) + @touch $@ + +# Vendors crates for cargo packages +vendor-dep-crates: $(foreach package,$(native_cargo_packages),vendor-$(package)-crates) + +# Vendors project-wide cargo packages +vendor-all-crates: $(native_rust_cached) $(CRATE_LOCK) + @rm -rf $(WORK_PATH)/vendor-main + @mkdir -p $(WORK_PATH)/vendor-main + @$(build_TAR) --no-same-owner -xf $(native_rust_cached) -C $(WORK_PATH)/vendor-main + @echo "Vendoring main project crates..." + @$(WORK_PATH)/vendor-main/native/bin/cargo vendor --locked --manifest-path $(BASEDIR)/../Cargo.toml $(WORK_PATH)/vendor-main/$(CRATE_REGISTRY) + @cd $(WORK_PATH)/vendor-main; find $(CRATE_REGISTRY) | sort | $(build_TAR) --no-recursion -czf $(CRATE_ARCHIVE) -T - + @rm -rf $(WORK_PATH)/vendor-main + @echo "Created $(CRATE_ARCHIVE)" + +# Vendors everything +vendor-crates: vendor-dep-crates vendor-all-crates + # $PATH is not preserved between ./configure and make by convention. Its # modification and overriding at ./configure time is (as I understand it) # supposed to be captured by the AC_{PROG_{,OBJ}CXX,PATH_{PROG,TOOL}} macros, @@ -232,13 +276,18 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ @mkdir -p $(@D) sed -e 's|@HOST@|$(host)|' \ -e 's|@CC@|$(host_CC)|' \ + -e 's|@NATIVE_CC@|$(build_CC)|' \ -e 's|@CXX@|$(host_CXX)|' \ + -e 's|@NATIVE_CXX@|$(build_CXX)|' \ -e 's|@AR@|$(host_AR)|' \ + -e 's|@NATIVE_AR@|$(build_AR)|' \ -e 's|@RANLIB@|$(host_RANLIB)|' \ -e 's|@NM@|$(host_NM)|' \ -e 's|@STRIP@|$(host_STRIP)|' \ -e 's|@OBJDUMP@|$(host_OBJDUMP)|' \ -e 's|@DSYMUTIL@|$(host_DSYMUTIL)|' \ + -e 's|@OSX_MIN_VERSION@|$(OSX_MIN_VERSION)|' \ + -e 's|@OSX_SDK@|$(OSX_SDK)|' \ -e 's|@WINDRES@|$(host_WINDRES)|' \ -e 's|@build_os@|$(build_os)|' \ -e 's|@host_os@|$(host_os)|' \ @@ -257,6 +306,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@no_usdt@|$(NO_USDT)|' \ -e 's|@no_natpmp@|$(NO_NATPMP)|' \ -e 's|@multiprocess@|$(MULTIPROCESS)|' \ + -e 's|@rust@|$(RUST)|' \ -e 's|@lto@|$(LTO)|' \ -e 's|@no_harden@|$(NO_HARDEN)|' \ -e 's|@debug@|$(DEBUG)|' \ @@ -293,21 +343,29 @@ clean-all: clean clean: @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) *.log +ifeq ($(RUST),1) +install: check-packages $(host_prefix)/share/config.site $(CRATE_REGISTRY_STAMP) +else install: check-packages $(host_prefix)/share/config.site +endif download-one: check-sources $(all_sources) download-osx: - @$(MAKE) -s HOST=x86_64-apple-darwin download-one + @$(MAKE) -s RUST=$(RUST) HOST=x86_64-apple-darwin download-one download-linux: - @$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one + @$(MAKE) -s RUST=$(RUST) HOST=x86_64-unknown-linux-gnu download-one download-win: - @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one -download: download-osx download-linux download-win + @$(MAKE) -s RUST=$(RUST) HOST=x86_64-w64-mingw32 download-one +download-rust-std: + @mkdir -p $(SOURCES_PATH) + @mkdir -p $(SOURCES_PATH)/download-stamps + @$(foreach target,$(rust_stdlib_targets),$(call download_rust_std_target,$(target)) && ) true +download: download-osx download-linux download-win $(rust_download_targets) $(foreach package,$(all_packages),$(eval $(call ext_add_stages,$(package)))) -.PHONY: install cached clean clean-all download-one download-osx download-linux download-win download check-packages check-sources +.PHONY: install cached clean clean-all download-one download-osx download-linux download-win download download-rust-std check-packages check-sources vendor-crates vendor-dep-crates vendor-all-crates .PHONY: FORCE $(V).SILENT: diff --git a/depends/README.md b/depends/README.md index f504627729f0..dbb285c22023 100644 --- a/depends/README.md +++ b/depends/README.md @@ -92,6 +92,8 @@ The following can be set when running make: `make FOO=bar` build script logic) are searched for among the host system packages using `pkg-config`. It allows building with packages of other (newer) versions - `MULTIPROCESS`: build libmultiprocess (experimental, requires cmake) +- `RUST`: Download/build/cache the Rust toolchain, target standard library, `cxxbridge` + and vendored crate sources needed for `--enable-rust` (see [doc/rust.md](../doc/rust.md)) - `DEBUG`: Disable some optimizations and enable more runtime checking - `HOST_ID_SALT`: Optional salt to use when generating host package ids - `BUILD_ID_SALT`: Optional salt to use when generating build package ids diff --git a/depends/config.site.in b/depends/config.site.in index 398a09b74c63..34b00d898daa 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -50,6 +50,10 @@ if test -z "$enable_multiprocess" && test -n "@multiprocess@"; then enable_multiprocess=yes fi +if test -z "$enable_rust" && test -n "@rust@"; then + enable_rust=yes +fi + if test -z "$with_miniupnpc" && test -n "@no_upnp@"; then with_miniupnpc=no fi @@ -95,14 +99,25 @@ LDFLAGS="-L${depends_prefix}/lib ${LDFLAGS}" if test -n "@CC@" -a -z "${CC}"; then CC="@CC@" fi +if test -n "@NATIVE_CC@" -a -z "${NATIVE_CC}"; then + NATIVE_CC="@NATIVE_CC@" +fi + if test -n "@CXX@" -a -z "${CXX}"; then CXX="@CXX@" fi +if test -n "@NATIVE_CXX@" -a -z "${NATIVE_CXX}"; then + NATIVE_CXX="@NATIVE_CXX@" +fi if test -n "@AR@"; then AR="@AR@" ac_cv_path_AR="${AR}" fi +if test -n "@NATIVE_AR@"; then + NATIVE_AR="@NATIVE_AR@" + ac_cv_path_NATIVE_AR="${NATIVE_AR}" +fi if test -n "@RANLIB@"; then RANLIB="@RANLIB@" @@ -129,6 +144,14 @@ if test "@host_os@" = darwin; then DSYMUTIL="@DSYMUTIL@" ac_cv_path_DSYMUTIL="${DSYMUTIL}" fi + + if test -n "@OSX_MIN_VERSION@"; then + OSX_MIN_VERSION="@OSX_MIN_VERSION@" + fi + + if test -n "@OSX_SDK@"; then + OSX_SDK="@OSX_SDK@" + fi fi if test "@host_os@" = mingw32; then @@ -154,3 +177,16 @@ fi if test -n "@LDFLAGS@"; then LDFLAGS="@LDFLAGS@ ${LDFLAGS}" fi + +if test -x "${depends_prefix}/native/bin/cargo"; then + CARGO="${depends_prefix}/native/bin/cargo" +fi +if test -x "${depends_prefix}/native/bin/rustc"; then + RUSTC="${depends_prefix}/native/bin/rustc" +fi +if test -x "${depends_prefix}/native/bin/cxxbridge"; then + CXXBRIDGE="${depends_prefix}/native/bin/cxxbridge" +fi +if test -z "$RUST_VENDORED_SOURCES"; then + RUST_VENDORED_SOURCES="${depends_prefix}/vendored-sources" +fi diff --git a/depends/funcs.mk b/depends/funcs.mk index 566f83a9868e..4fa1585a883b 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -197,6 +197,18 @@ $(1)_cmake += -DCMAKE_C_COMPILER_TARGET=$(host) $(1)_cmake += -DCMAKE_CXX_COMPILER_TARGET=$(host) endif endif + +$(1)_cargo=env CC="$$($(1)_cc)" \ + CXX="$$($(1)_cxx)" \ + CFLAGS="$$($(1)_cppflags) $$($(1)_cflags)" \ + CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \ + LDFLAGS="$$($(1)_ldflags)" \ + RUSTFLAGS="-C linker=$$(firstword $($(1)_cc))" \ + LD_LIBRARY_PATH="$$($($(1)_type)_prefix)/lib" +ifeq ($(host_os),darwin) +$(1)_cargo += SDKROOT="$(OSX_SDK)" +endif +$(1)_cargo += cargo endef define int_add_cmds @@ -269,6 +281,37 @@ $(foreach stage,$(stages), .PHONY: $(1)_$(stage)) endef +# Template for vendoring a native package's Rust crate dependencies +# Packages opt-in by defining $(package)_vendored_file_name and $(package)_cargo_manifest +define int_vendor_crates +ifneq ($($(1)_vendored_file_name),) +$(1)_vendored_archive = $(SOURCES_PATH)/$($(1)_vendored_file_name) + +vendor-$(1)-crates: $(native_rust_cached) $($(1)_fetched) + @rm -rf $(WORK_PATH)/vendor-$(1) + @mkdir -p $(WORK_PATH)/vendor-$(1) + @$(build_TAR) --no-same-owner -xf $(native_rust_cached) -C $(WORK_PATH)/vendor-$(1) + @echo "Vendoring $(1) crates..." + @mkdir -p $(WORK_PATH)/vendor-$(1)/src + @cd $(WORK_PATH)/vendor-$(1)/src && $(build_TAR) --no-same-owner --strip-components=1 -xf $(SOURCES_PATH)/$($(1)_file_name) + @cp $(PATCHES_PATH)/$(1)/Cargo.lock $(WORK_PATH)/vendor-$(1)/src/ + @$(WORK_PATH)/vendor-$(1)/native/bin/cargo vendor --locked --manifest-path $(WORK_PATH)/vendor-$(1)/src/$($(1)_cargo_manifest) $(WORK_PATH)/vendor-$(1)/src/vendored + @cd $(WORK_PATH)/vendor-$(1)/src; find vendored | sort | $(build_TAR) --no-recursion -czf $$($(1)_vendored_archive) -T - + @rm -rf $(WORK_PATH)/vendor-$(1) + @echo "Created $$($(1)_vendored_archive)" +.PHONY: vendor-$(1)-crates +endif +endef + +define download_rust_std_target +([ -f "$(SOURCES_PATH)/rust-std-$(rust_stdlib_version)-$(1).tar.gz" ] && \ + echo "Already have rust-std-$(rust_stdlib_version)-$(1).tar.gz" || \ + (echo "Downloading rust-std-$(rust_stdlib_version)-$(1).tar.gz..." && \ + $(build_DOWNLOAD) "$(SOURCES_PATH)/rust-std-$(rust_stdlib_version)-$(1).tar.gz" "$(rust_stdlib_download_path)/rust-std-$(rust_stdlib_version)-$(1).tar.gz")) && \ +echo "$(rust_stdlib_sha256_hash_$(1)) $(SOURCES_PATH)/rust-std-$(rust_stdlib_version)-$(1).tar.gz" | $(build_SHA256SUM) -c - && \ +echo "$(rust_stdlib_sha256_hash_$(1)) rust-std-$(rust_stdlib_version)-$(1).tar.gz" > "$(SOURCES_PATH)/download-stamps/.stamp_fetched-rust_stdlib-$(rust_stdlib_version)-$(rust_stdlib_sha256_hash_$(1)).hash" +endef + # These functions create the build targets for each package. They must be # broken down into small steps so that each part is done for all packages # before moving on to the next step. Otherwise, a package's info @@ -286,6 +329,18 @@ $(foreach package,$(all_packages),$(eval $(call int_vars,$(package)))) $(foreach native_package,$(native_packages),$(eval include packages/$(native_package).mk)) $(foreach package,$(packages),$(eval include packages/$(package).mk)) +# Extend preprocess_cmds for cargo packages to extract vendored crates +define int_cargo_preprocess_ext +$(1)_preprocess_cmds += && \ + if test -f $(SOURCES_PATH)/$($(1)_vendored_file_name); then \ + echo "Extracting vendored crates for $(1)..." && \ + $(build_TAR) --no-same-owner -xf $(SOURCES_PATH)/$($(1)_vendored_file_name) && \ + mkdir -p .cargo && \ + cp $(PATCHES_PATH)/$(1)/cargo-config.toml .cargo/config.toml; \ + fi +endef +$(foreach cargo_package,$(native_cargo_packages),$(eval $(call int_cargo_preprocess_ext,$(cargo_package)))) + #compute a hash of all files that comprise this package's build recipe $(foreach package,$(all_packages),$(eval $(call int_get_build_recipe_hash,$(package)))) @@ -297,3 +352,6 @@ $(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$ #create build targets $(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package)))) + +#create vendor targets for cargo packages +$(foreach cargo_package,$(native_cargo_packages),$(eval $(call int_vendor_crates,$(cargo_package)))) diff --git a/depends/packages/native_cxxbridge.mk b/depends/packages/native_cxxbridge.mk new file mode 100644 index 000000000000..3c5a1cf15cf4 --- /dev/null +++ b/depends/packages/native_cxxbridge.mk @@ -0,0 +1,34 @@ +# Copyright (c) 2022-2025 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# To update the package, change the version below and then run the script +# ./contrib/devtools/update-native-cxxbridge.py + +package:=native_cxxbridge +$(package)_version:=1.0.198 +$(package)_download_path:=https://github.com/dtolnay/cxx/archive/refs/tags +$(package)_file_name:=native_cxxbridge-$($(package)_version).tar.gz +$(package)_download_file:=$($(package)_version).tar.gz +$(package)_sha256_hash:=e8c54f11e12b00f80a7da8cc2eac55db5c4688cb2bc483f5e923261fa3a285bf +$(package)_build_subdir:=bridge/cmd +$(package)_dependencies:=native_rust +$(package)_patches:=Cargo.lock cargo-config.toml ../native_rust/fix-elf-interpreter.sh +$(package)_vendored_file_name:=native_cxxbridge-$($(package)_version)-vendored.tar.gz +$(package)_cargo_manifest:=bridge/cmd/Cargo.toml + +define $(package)_preprocess_cmds + cp $($(package)_patch_dir)/Cargo.lock . +endef + +define $(package)_build_cmds + $($(package)_cargo) build --locked --release --package=cxxbridge-cmd --bin=cxxbridge +endef + +define $(package)_stage_cmds + $($(package)_cargo) install --locked --path=. --bin=cxxbridge --root=$($(package)_staging_prefix_dir) && \ + mkdir -p $($(package)_staging_prefix_dir)/lib && \ + bash $($(package)_patch_dir)/fix-elf-interpreter.sh \ + $($(package)_staging_prefix_dir)/lib \ + $($(package)_staging_prefix_dir)/bin/cxxbridge +endef diff --git a/depends/packages/native_rust.mk b/depends/packages/native_rust.mk new file mode 100644 index 000000000000..b474a0971ae3 --- /dev/null +++ b/depends/packages/native_rust.mk @@ -0,0 +1,55 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# To update the Rust compiler, change the version below and then run the script +# ./contrib/devtools/update-rust-hashes.py + +package:=native_rust +$(package)_version:=1.92.0 +$(package)_download_path:=https://static.rust-lang.org/dist +$(package)_patches:=fix-elf-interpreter.sh + +# Linux (ARMv8) +$(package)_file_name_aarch64_linux:=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz +$(package)_sha256_hash_aarch64_linux:=c812028423c3d7dd7ba99f66101e9e1aa3f66eab44a1285f41c363825d49dca4 + +# Linux (x86_64) +$(package)_file_name_x86_64_linux:=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz +$(package)_sha256_hash_x86_64_linux:=6e5efd6c25953b2732d4e6b1842512536650c68cf72a8b99a0fc566012dd6ca5 + +# macOS (ARMv8) +$(package)_file_name_aarch64_darwin:=rust-$($(package)_version)-aarch64-apple-darwin.tar.gz +$(package)_sha256_hash_aarch64_darwin:=235a6cca2dd4881130a9ae61ad1149bbf28bba184dd4621700f0c98c97457716 + +# macOS (x86_64) +$(package)_file_name_x86_64_darwin:=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz +$(package)_sha256_hash_x86_64_darwin:=fc6868991e61e9262272effbb8956b23428430f5f4300c1b48eaae3969f8af2a + +$(package)_file_name=$($(package)_file_name_$(build_arch)_$(build_os)) +$(package)_sha256_hash=$($(package)_sha256_hash_$(build_arch)_$(build_os)) + +define $(package)_set_vars +$(package)_stage_opts=--disable-ldconfig +$(package)_stage_build_opts=--without=rust-docs-json-preview,rust-docs +endef + +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name),$($(package)_file_name),$($(package)_sha256_hash)) +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_dir)/$(host_prefix)/native/bin && \ + mkdir -p $($(package)_staging_dir)/$(host_prefix)/native/lib/rustlib && \ + cp cargo/bin/cargo $($(package)_staging_dir)/$(host_prefix)/native/bin/ && \ + cp rustc/bin/rustc $($(package)_staging_dir)/$(host_prefix)/native/bin/ && \ + cp rustc/bin/rustdoc $($(package)_staging_dir)/$(host_prefix)/native/bin/ && \ + cp -r rustc/lib/* $($(package)_staging_dir)/$(host_prefix)/native/lib/ && \ + cp -r rust-std-*/lib/rustlib/* $($(package)_staging_dir)/$(host_prefix)/native/lib/rustlib/ && \ + bash $($(package)_patch_dir)/fix-elf-interpreter.sh \ + $($(package)_staging_dir)/$(host_prefix)/native/lib \ + $($(package)_staging_dir)/$(host_prefix)/native/bin/cargo \ + $($(package)_staging_dir)/$(host_prefix)/native/bin/rustc \ + $($(package)_staging_dir)/$(host_prefix)/native/bin/rustdoc +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 7e0bb2633219..f52ba3cd36ae 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -2,6 +2,9 @@ packages:=gmp backtrace boost_packages = boost +rust_packages = rustcxx rust_stdlib +rust_native_packages = native_rust native_cxxbridge + libevent_packages = libevent qrencode_linux_packages = qrencode diff --git a/depends/packages/rust_stdlib.mk b/depends/packages/rust_stdlib.mk new file mode 100644 index 000000000000..d54594c875e2 --- /dev/null +++ b/depends/packages/rust_stdlib.mk @@ -0,0 +1,69 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# This version is synchronized from native_rust.mk by update-rust-hashes.py. + +package:=rust_stdlib +$(package)_version:=1.92.0 +$(package)_download_path:=https://static.rust-lang.org/dist +$(package)_dependencies:=native_rust +$(package)_target=$(or \ + $($(package)_target_$(canonical_host)),\ + $($(package)_target_$(subst -pc-,-unknown-,$(canonical_host))),\ + $($(package)_target_$(subst -unknown-,-pc-,$(canonical_host))),\ + $($(package)_target_$(subst -linux-,-unknown-linux-,$(canonical_host))),\ + $(if $(findstring -apple-darwin,$(canonical_host)),$(host_arch)-apple-darwin)) + +# Rust support is deliberately confined to the hosts we actually validate +# (the Guix release set plus native development hosts). RUST=1 on any other +# host fails explicitly below rather than fetching a stdlib we never test. + +# Linux (ARMv8) +$(package)_targets += aarch64-unknown-linux-musl +$(package)_target_aarch64-unknown-linux-gnu:=aarch64-unknown-linux-musl +$(package)_sha256_hash_aarch64-unknown-linux-musl:=715fbcfd8712c723947a020d0371c8a1a21f7531f2b696aeaed50ac23ba675c9 + +# Linux (RISCV64GC) +$(package)_targets += riscv64gc-unknown-linux-musl +$(package)_target_riscv64-unknown-linux-gnu:=riscv64gc-unknown-linux-musl +$(package)_target_riscv64gc-unknown-linux-gnu:=riscv64gc-unknown-linux-musl +$(package)_sha256_hash_riscv64gc-unknown-linux-musl:=34f5722ff2a0940bcd7ff6603a7748d2b963de72f6f713579c39c74ead06a7a0 + +# Linux (x86_64) +$(package)_targets += x86_64-unknown-linux-musl +$(package)_target_x86_64-unknown-linux-gnu:=x86_64-unknown-linux-musl +$(package)_sha256_hash_x86_64-unknown-linux-musl:=8bfd9a42c8295949d556587201acdb35d2bfb8b7ce55223845f337aa5614f9a3 + +# macOS (ARMv8) +$(package)_targets += aarch64-apple-darwin +$(package)_target_aarch64-apple-darwin:=aarch64-apple-darwin +$(package)_target_arm64-apple-darwin:=aarch64-apple-darwin +$(package)_sha256_hash_aarch64-apple-darwin:=b1f55aac4bc982ea67b68b262b711263005e470d31cab5d09d534bc1866d455a + +# macOS (x86_64) +$(package)_targets += x86_64-apple-darwin +$(package)_target_x86_64-apple-darwin:=x86_64-apple-darwin +$(package)_sha256_hash_x86_64-apple-darwin:=1e5a8fee4e038ea2d35d82a680e2b9bf44ffccb3746aaf9dbdc56cb14152dcb8 + +# Windows (x86_64) +$(package)_targets += x86_64-pc-windows-gnu +$(package)_target_x86_64-w64-mingw32:=x86_64-pc-windows-gnu +$(package)_sha256_hash_x86_64-pc-windows-gnu:=6256f3497e3b14b6650511e84fdfb51fc632db1908ae5a173dffcdc96c80b7ce + +ifeq ($($(package)_target),) +$(error Unsupported Rust standard library target: $(canonical_host)) +endif + +$(package)_file_name=rust-std-$($(package)_version)-$($(package)_target).tar.gz +$(package)_sha256_hash=$($(package)_sha256_hash_$($(package)_target)) + +define $(package)_fetch_cmds + $(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name),$($(package)_file_name),$($(package)_sha256_hash)) +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_dir)/$(host_prefix)/native/lib/rustlib && \ + cp -r rust-std-$($(package)_target)/lib/rustlib/$($(package)_target) $($(package)_staging_dir)/$(host_prefix)/native/lib/rustlib/ +endef diff --git a/depends/packages/rustcxx.mk b/depends/packages/rustcxx.mk new file mode 100644 index 000000000000..13125507f290 --- /dev/null +++ b/depends/packages/rustcxx.mk @@ -0,0 +1,17 @@ +# Copyright (c) 2022-2023 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +package:=rustcxx +$(package)_version:=$(native_cxxbridge_version) +$(package)_file_name:=$(native_cxxbridge_file_name) +$(package)_sha256_hash:=$(native_cxxbridge_sha256_hash) + +define $(package)_fetch_cmds + $(call native_cxxbridge_fetch_cmds,native_cxxbridge) +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_prefix_dir)/include/rust && \ + cp include/cxx.h $($(package)_staging_prefix_dir)/include/rust +endef diff --git a/depends/patches/native_cxxbridge/Cargo.lock b/depends/patches/native_cxxbridge/Cargo.lock new file mode 100644 index 000000000000..91f145808c7f --- /dev/null +++ b/depends/patches/native_cxxbridge/Cargo.lock @@ -0,0 +1,568 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "cc" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cxx" +version = "1.0.198" +dependencies = [ + "cc", + "cxx-build", + "cxx-gen", + "cxx-test-suite", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "indoc", + "link-cplusplus", + "proc-macro2", + "quote", + "rustversion", + "scratch", + "target-triple", + "tempfile", + "trybuild", +] + +[[package]] +name = "cxx-build" +version = "1.0.198" +dependencies = [ + "cc", + "codespan-reporting", + "cxx", + "cxx-gen", + "indexmap", + "pkg-config", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxx-gen" +version = "0.7.198" +dependencies = [ + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxx-test-suite" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", + "cxxbridge-flags", + "serde", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.198" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.198" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.198" +dependencies = [ + "cxx", + "indexmap", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "demo" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "dissimilar" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeda16ab4059c5fd2a83f2b9c9e9c981327b18aa8e3b313f7e6563799d4f093e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-triple" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a6bfce3d99adfa72d24750a61f782f3036a81e7f86d8841ee1326deaebd171" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "toml" +version = "1.1.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "trybuild" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5c9f7b7b1a048dd2bebdb7260b0cc71ec5587b19352fa5c3cd9e1c067103f0" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/depends/patches/native_cxxbridge/cargo-config.toml b/depends/patches/native_cxxbridge/cargo-config.toml new file mode 100644 index 000000000000..01ab67f3bd1f --- /dev/null +++ b/depends/patches/native_cxxbridge/cargo-config.toml @@ -0,0 +1,5 @@ +[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendored" diff --git a/depends/patches/native_rust/fix-elf-interpreter.sh b/depends/patches/native_rust/fix-elf-interpreter.sh new file mode 100755 index 000000000000..cc8b186ee49a --- /dev/null +++ b/depends/patches/native_rust/fix-elf-interpreter.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +export LC_ALL=C + +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +LIBDIR="$1" +shift + +if ! command -v patchelf >/dev/null 2>&1; then + echo "patchelf not found, skipping ELF fix" + exit 0 +fi + +# Get the interpreter from a known working binary (ls) +LS_PATH=$(command -v ls) +GUIX_INTERP=$(patchelf --print-interpreter "$LS_PATH" 2>/dev/null) + +if [ -z "$GUIX_INTERP" ]; then + echo "Could not detect interpreter, skipping" + exit 0 +fi + +echo "Detected interpreter: $GUIX_INTERP" + +# Find and copy libgcc_s.so.1 into our lib directory +LIBGCC_SRC="" + +# Method 1: Use gcc to find it +if command -v gcc >/dev/null 2>&1; then + GCC_LIBDIR=$(dirname "$(gcc -print-libgcc-file-name)" 2>/dev/null) + if [ -f "$GCC_LIBDIR/libgcc_s.so.1" ]; then + LIBGCC_SRC="$GCC_LIBDIR/libgcc_s.so.1" + else + GCC_PATH=$(command -v gcc) + GCC_PREFIX=$(dirname "$(dirname "$GCC_PATH")") + if [ -f "$GCC_PREFIX/lib/libgcc_s.so.1" ]; then + LIBGCC_SRC="$GCC_PREFIX/lib/libgcc_s.so.1" + fi + fi +fi + +# Method 2: Search LIBRARY_PATH +if [ -z "$LIBGCC_SRC" ] && [ -n "$LIBRARY_PATH" ]; then + IFS=':' read -ra LIB_PATHS <<< "$LIBRARY_PATH" + for libpath in "${LIB_PATHS[@]}"; do + if [ -f "$libpath/libgcc_s.so.1" ]; then + LIBGCC_SRC="$libpath/libgcc_s.so.1" + break + fi + done +fi + +if [ -n "$LIBGCC_SRC" ]; then + # Resolve symlinks and copy the actual file + LIBGCC_REAL=$(readlink -f "$LIBGCC_SRC") + echo "Copying libgcc_s.so.1 from: $LIBGCC_REAL" + cp "$LIBGCC_REAL" "$LIBDIR/libgcc_s.so.1" +else + echo "WARNING: Could not find libgcc_s.so.1 to copy" +fi + +# RPATH just needs $ORIGIN/../lib - everything is self-contained +GUIX_RPATH="\$ORIGIN/../lib" +echo "Using RPATH: $GUIX_RPATH" + +for binary in "$@"; do + if [ -f "$binary" ]; then + echo "Patching: $binary" + patchelf --set-interpreter "$GUIX_INTERP" "$binary" + patchelf --set-rpath "$GUIX_RPATH" "$binary" + fi +done + +if [ -n "$1" ]; then + echo "Verifying first binary:" + patchelf --print-interpreter "$1" + patchelf --print-rpath "$1" +fi diff --git a/doc/README.md b/doc/README.md index 384ba6442f66..585f9ba2b4a8 100644 --- a/doc/README.md +++ b/doc/README.md @@ -34,6 +34,7 @@ Building The following are developer notes on how to build Dash Core on your native platform. They are not complete guides, but include notes on the necessary libraries, compile flags, etc. - [Dependencies](dependencies.md) +- [Rust](rust.md) - [macOS Build Notes](build-osx.md) - [Unix Build Notes](build-unix.md) - [Windows Build Notes](build-windows.md) diff --git a/doc/rust.md b/doc/rust.md new file mode 100644 index 000000000000..6fd1252365a8 --- /dev/null +++ b/doc/rust.md @@ -0,0 +1,134 @@ +# Rust in Dash Core + +Dash Core has optional support for components written in Rust, bridged into +the C++ codebase with [cxx](https://cxx.rs/). Rust support is **disabled by +default**: a default `./configure && make` performs no Rust work at all and +requires no Rust tooling. + +The Rust code lives in `rust/`. Component crates (currently only `chirp`, a +small smoke-test component) are compiled as dependencies of the umbrella crate +`rust/dashrust`, which cargo builds into a single static library that is +linked into the C++ binaries. The C++ side of each bridge is generated from +the crate's `src/lib.rs` by the `cxxbridge` code generator. + +## Toolchain requirements + +`--enable-rust` requires exact tool versions, enforced at configure time: + +| Tool | Version | +|-------------|------------| +| `rustc` | 1.92.0 | +| `cargo` | 1.92.0 | +| `cxxbridge` | 1.0.198 | + +The version pins live in `configure.ac`, `rust-toolchain.toml` (picked up +automatically by rustup) and the depends packages. To update them, change the +version in `depends/packages/native_rust.mk` and run +`contrib/devtools/update-rust-hashes.py`, and/or change +`depends/packages/native_cxxbridge.mk` and run +`contrib/devtools/update-native-cxxbridge.py`, keeping `configure.ac` in sync. + +## Building with depends (recommended) + +The depends system can provision the whole Rust toolchain, the Rust standard +library for the target, and offline copies of all crate dependencies: + +```bash +make -C depends RUST=1 HOST=x86_64-pc-linux-gnu +./configure --prefix=$(pwd)/depends/x86_64-pc-linux-gnu +make +``` + +`RUST=1` builds/installs into the depends prefix: + +- `native_rust`: the pinned Rust compiler and cargo for the build machine; +- `native_cxxbridge`: the pinned `cxxbridge` code generator; +- `rust_stdlib`: the pre-built Rust standard library for the target triple; +- `rustcxx`: the `rust/cxx.h` header; +- `vendored-sources/`: all crates from the workspace `Cargo.lock`, vendored + for offline use. + +The generated `share/config.site` then makes `./configure` default to +`--enable-rust` with `CARGO`, `RUSTC`, `CXXBRIDGE` and +`RUST_VENDORED_SOURCES` pointing into the depends prefix, so no extra +configure flags are needed. Everything after the depends downloads works +offline. + +For an offline sources mirror, `make -C depends RUST=1 download` additionally +fetches the Rust standard libraries for all supported targets +(`download-rust-std`) and creates a pre-vendored crate archive +(`vendor-crates`) in `SOURCES_PATH`. + +## Supported hosts + +Rust support is deliberately confined to the hosts that are validated in CI +and release builds. `--enable-rust` (and `RUST=1` in depends) fails +explicitly on any other host rather than producing binaries for a target that +is never tested: + +- Linux: `x86_64`, `aarch64`, `riscv64` (glibc and musl; depends builds use + the musl-targeted standard library) +- macOS: `x86_64`, `arm64` +- Windows: `x86_64` (MinGW-w64) + +Android is explicitly unsupported. Additional targets can be added later +together with CI lanes that exercise them. + +## Building with a system toolchain + +Instead of depends, a system Rust toolchain can be used as long as it matches +the pinned versions exactly. With rustup, the pinned toolchain from +`rust-toolchain.toml` is selected automatically; the matching code generator +can be installed with: + +```bash +rustup toolchain install 1.92.0 +cargo install cxxbridge-cmd --version 1.0.198 --locked +``` + +Note that rustup resolves `rust-toolchain.toml` from the current working +directory, so for out-of-tree builds export `RUSTUP_TOOLCHAIN=1.92.0` (the +build system propagates it to all cargo invocations). + +Configure builds are offline by default, so a system-toolchain build must +either provide vendored crates or opt in to network access: + +```bash +# Offline: vendor the workspace dependencies once, then point configure at them +cargo vendor --locked /path/to/vendored-sources +./configure --enable-rust RUST_VENDORED_SOURCES=/path/to/vendored-sources + +# Online: let cargo fetch dependencies from the network (developer convenience) +./configure --enable-rust --enable-online-rust +``` + +In offline mode the build generates a cargo config from +`.cargo/config.toml.offline`, adds the vendored directory as the +`crates-io` replacement, and reconstructs source-replacement stanzas for any +git-sourced crates in `Cargo.lock` via +`contrib/devtools/cargo-vendor-git-sources.sh`. Cargo then runs with +`--locked --offline`. In online mode no config is injected and the +developer's own cargo configuration (e.g. in `~/.cargo`) is left in effect; +cargo still runs with `--locked`. + +Useful configure variables (see `./configure --help`): + +- `RUST_VENDORED_SOURCES`: directory containing vendored crate sources + (required for offline builds outside depends); +- `RUSTFLAGS`: defaults to `-C embed-bitcode=yes -C relocation-model=pic`; +- `CARGO_INCREMENTAL`: defaults to `0`; +- `NATIVE_CC`/`NATIVE_CXX`/`NATIVE_AR`: build-machine tools, required when + cross-compiling (depends sets them automatically). + +`--enable-debug` builds the Rust code with cargo's debug profile instead of +the release profile. + +## Source tarballs + +Source distributions generated from a Rust-enabled tree (`make dist`) ship +the generated C++ bridge sources under `rust//gen/` together with a +stamp recording the `cxxbridge` version that produced them. Builds from such +a tarball reuse the shipped artifacts instead of regenerating them, provided +the artifacts are at least as new as the stamp and strictly newer than the +crate's `lib.rs`; editing `lib.rs` forces regeneration with the pinned +`cxxbridge`. diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000000..f19782d3c556 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.92.0" diff --git a/rust/Makefile.am b/rust/Makefile.am new file mode 100644 index 000000000000..6664efd64d02 --- /dev/null +++ b/rust/Makefile.am @@ -0,0 +1,40 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +include $(top_srcdir)/rust/Makefile.common.include +include $(top_srcdir)/rust/Makefile.libs.include + +EXTRA_DIST = \ + $(LIBRUSTDEPS_FILES) \ + $(LIBRUST_CHIRP_FILES) + +if ENABLE_RUST +all-local: $(CARGO_CONFIGURED) cargo-build-dashrust cxxbridge-chirp + +cargo-build: $(CARGO_CONFIGURED) cargo-build-dashrust +else +all-local: + +cargo-build: +endif + +cargo-clean: cargo-clean-dashrust + +# Aggregate dist hook: per-crate includes define dist- targets; new +# crates append here instead of defining their own dist-hook (automake allows +# only one dist-hook recipe per Makefile). +if ENABLE_RUST +dist-hook: dist-chirp +endif + +cargo-clean-config: + $(AM_V_at)rm -f $(abs_top_builddir)/.cargo/.configured-for-online + $(AM_V_at)rm -f $(abs_top_builddir)/.cargo/.configured-for-offline + $(AM_V_at)rm -f $(CARGO_BUILD_CONFIG) + $(AM_V_at)rm -f $(RUST_NATIVE_LINKER) $(RUST_TARGET_LINKER) + +clean-local: cargo-clean-dashrust cxxbridge-clean-chirp cargo-clean-config + +.PHONY: cargo-build cargo-clean cargo-clean-config diff --git a/rust/Makefile.chirp.include b/rust/Makefile.chirp.include new file mode 100644 index 000000000000..e11b81459189 --- /dev/null +++ b/rust/Makefile.chirp.include @@ -0,0 +1,94 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# cxxbridge codegen for the `chirp` component. Compilation happens through +# the umbrella crate `dashrust` (see Makefile.libs.include); this file only +# generates and distributes the C++ side of the bridge. + +LIBRUST_CHIRP_CRATE_DIR = $(top_srcdir)/rust/chirp +LIBRUST_CHIRP_GEN_DIR = $(abs_top_builddir)/rust/chirp/gen +LIBRUST_CHIRP_GEN_SRCDIR = $(abs_top_srcdir)/rust/chirp/gen +LIBRUST_CHIRP_GEN_STAMP = $(LIBRUST_CHIRP_GEN_DIR)/.cxxbridge-@CXXBRIDGE_REQUIRED_VERSION@ +LIBRUST_CHIRP_GEN_SRCSTAMP = $(LIBRUST_CHIRP_GEN_SRCDIR)/.cxxbridge-@CXXBRIDGE_REQUIRED_VERSION@ +LIBRUST_CHIRP_MANIFEST = $(LIBRUST_CHIRP_CRATE_DIR)/Cargo.toml + +LIBRUST_CHIRP_SRCS = \ + $(LIBRUST_CHIRP_CRATE_DIR)/src/lib.rs + +LIBRUST_CHIRP_BUILD = \ + $(LIBRUST_CHIRP_CRATE_DIR)/build.rs + +LIBRUST_CHIRP_CPP = \ + $(LIBRUST_CHIRP_GEN_DIR)/src/lib.cpp + +LIBRUST_CHIRP_H = \ + $(LIBRUST_CHIRP_GEN_DIR)/include/rust/chirp/lib.h + +LIBCXXBRIDGE_H = \ + $(LIBRUST_CHIRP_GEN_DIR)/include/rust/cxx.h + +LIBRUST_CHIRP_INCLUDES = \ + -I$(LIBRUST_CHIRP_GEN_DIR)/include + +LIBRUST_CHIRP_FILES = \ + $(LIBRUST_CHIRP_BUILD) \ + $(LIBRUST_CHIRP_MANIFEST) \ + $(LIBRUST_CHIRP_SRCS) + +$(LIBRUST_CHIRP_SRCS): ; + +# Pre-generated bridge sources shipped in a source distribution are used when +# they are strictly newer than lib.rs (an edited lib.rs must force +# regeneration) and at least as new as the distributed stamp. The stamp +# comparison must accept equal timestamps: ustar archives store mtimes at +# one-second precision, so the stamp and the artifacts it vouches for often +# extract with identical times. + +$(LIBRUST_CHIRP_GEN_STAMP): $(CXXBRIDGE) + $(AM_V_at)$(MKDIR_P) $(@D) + $(AM_V_at)rm -f $(@D)/.cxxbridge-* + $(AM_V_at)touch $@ + +$(LIBRUST_CHIRP_CPP): $(LIBRUST_CHIRP_SRCS) $(LIBRUST_CHIRP_GEN_STAMP) + $(AM_V_at)$(MKDIR_P) $(@D) + $(AM_V_GEN)if test -f $(LIBRUST_CHIRP_GEN_SRCDIR)/src/lib.cpp && \ + test -f $(LIBRUST_CHIRP_GEN_SRCSTAMP) && \ + test $(LIBRUST_CHIRP_GEN_SRCDIR)/src/lib.cpp -nt $< && \ + ! test $(LIBRUST_CHIRP_GEN_SRCSTAMP) -nt $(LIBRUST_CHIRP_GEN_SRCDIR)/src/lib.cpp && \ + test "$(LIBRUST_CHIRP_GEN_SRCDIR)" != "$(LIBRUST_CHIRP_GEN_DIR)"; then \ + cp $(LIBRUST_CHIRP_GEN_SRCDIR)/src/lib.cpp $@; \ + else \ + $(CXXBRIDGE) $< -o $@; \ + fi + +$(LIBRUST_CHIRP_H): $(LIBRUST_CHIRP_SRCS) $(LIBRUST_CHIRP_GEN_STAMP) + $(AM_V_at)$(MKDIR_P) $(@D) + $(AM_V_GEN)if test -f $(LIBRUST_CHIRP_GEN_SRCDIR)/include/rust/chirp/lib.h && \ + test -f $(LIBRUST_CHIRP_GEN_SRCSTAMP) && \ + test $(LIBRUST_CHIRP_GEN_SRCDIR)/include/rust/chirp/lib.h -nt $< && \ + ! test $(LIBRUST_CHIRP_GEN_SRCSTAMP) -nt $(LIBRUST_CHIRP_GEN_SRCDIR)/include/rust/chirp/lib.h && \ + test "$(LIBRUST_CHIRP_GEN_SRCDIR)" != "$(LIBRUST_CHIRP_GEN_DIR)"; then \ + cp $(LIBRUST_CHIRP_GEN_SRCDIR)/include/rust/chirp/lib.h $@; \ + else \ + $(CXXBRIDGE) $< --header -o $@; \ + fi + +$(LIBCXXBRIDGE_H): $(LIBRUST_CHIRP_GEN_STAMP) + $(AM_V_at)$(MKDIR_P) $(@D) + $(AM_V_GEN)$(CXXBRIDGE) --header -o $@ + +cxxbridge-clean-chirp: + $(AM_V_at)rm -rf $(LIBRUST_CHIRP_GEN_DIR) + +cxxbridge-chirp: $(LIBRUST_CHIRP_CPP) $(LIBRUST_CHIRP_H) $(LIBCXXBRIDGE_H) + +dist-chirp: cxxbridge-chirp + $(MKDIR_P) $(distdir)/chirp/gen/src + $(MKDIR_P) $(distdir)/chirp/gen/include/rust/chirp + cp $(LIBRUST_CHIRP_GEN_STAMP) $(distdir)/chirp/gen/ + cp $(LIBRUST_CHIRP_CPP) $(distdir)/chirp/gen/src/ + cp $(LIBRUST_CHIRP_H) $(distdir)/chirp/gen/include/rust/chirp/ + +.PHONY: cxxbridge-chirp cxxbridge-clean-chirp dist-chirp diff --git a/rust/Makefile.common.include b/rust/Makefile.common.include new file mode 100644 index 000000000000..5b6ea08e23fc --- /dev/null +++ b/rust/Makefile.common.include @@ -0,0 +1,75 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +CARGO_BUILD_OPTS = --locked --target $(RUST_TARGET) +CARGO_BUILD_CONFIG = $(abs_top_builddir)/.cargo/dash-build.toml +CARGO_ENV = \ + MACOSX_DEPLOYMENT_TARGET="$(RUST_MACOS_DEPLOYMENT_TARGET)" \ + CARGO_INCREMENTAL="$(CARGO_INCREMENTAL)" \ + RUSTC="$(RUSTC)" \ + RUSTFLAGS="$(RUSTFLAGS)" \ + TERM="dumb" \ + $(if $(RUST_OSX_SDK),SDKROOT="$(RUST_OSX_SDK)") \ + $(if $(RUSTUP_TOOLCHAIN),RUSTUP_TOOLCHAIN="$(RUSTUP_TOOLCHAIN)") + +# 'cargo' needs uppercase +RUST_NATIVE_ENV_UPPER = $(shell echo '$(RUST_NATIVE)' | tr 'a-z-' 'A-Z_') +RUST_NATIVE_LINKER = $(abs_top_builddir)/.cargo/native-linker +CARGO_ENV += \ + AR="$(NATIVE_AR)" \ + CC="$(NATIVE_CC)" \ + CXX="$(NATIVE_CXX)" \ + CARGO_TARGET_$(RUST_NATIVE_ENV_UPPER)_LINKER="$(RUST_NATIVE_LINKER)" + +# 'cc' needs preserved case +RUST_TARGET_ENV_LOWER = $(shell echo '$(RUST_TARGET)' | tr '-' '_') +# 'cargo' needs uppercase +RUST_TARGET_ENV_UPPER = $(shell echo '$(RUST_TARGET_ENV_LOWER)' | tr 'a-z' 'A-Z') +RUST_TARGET_LINKER = $(abs_top_builddir)/.cargo/target-linker +CARGO_ENV += \ + AR_$(RUST_TARGET_ENV_LOWER)="$(AR)" \ + CC_$(RUST_TARGET_ENV_LOWER)="$(CC)" \ + CFLAGS_$(RUST_TARGET_ENV_LOWER)="$(CFLAGS)" \ + CXX_$(RUST_TARGET_ENV_LOWER)="$(CXX)" \ + CXXFLAGS_$(RUST_TARGET_ENV_LOWER)="$(CXXFLAGS)" \ + CARGO_TARGET_$(RUST_TARGET_ENV_UPPER)_LINKER="$(RUST_TARGET_LINKER)" + +$(RUST_NATIVE_LINKER): Makefile + $(AM_V_at)$(MKDIR_P) $(abs_top_builddir)/.cargo + $(AM_V_at)printf '%s\n' '#!/bin/sh' 'exec $(NATIVE_CC) "$$@"' > $@ + $(AM_V_at)chmod +x $@ + +$(RUST_TARGET_LINKER): Makefile + $(AM_V_at)$(MKDIR_P) $(abs_top_builddir)/.cargo + $(AM_V_at)printf '%s\n' '#!/bin/sh' 'exec $(CC) "$$@"' > $@ + $(AM_V_at)chmod +x $@ + +if !ENABLE_DEBUG +CARGO_BUILD_OPTS += --release +endif # !ENABLE_DEBUG + +if !SILENT_RULES +CARGO_BUILD_OPTS += --verbose +endif # !SILENT_RULES + +if ENABLE_ONLINE_RUST +CARGO_CONFIGURED = $(abs_top_builddir)/.cargo/.configured-for-online + +$(CARGO_CONFIGURED): $(RUST_NATIVE_LINKER) $(RUST_TARGET_LINKER) + $(AM_V_at)rm -f $(abs_top_builddir)/.cargo/.configured-for-offline + $(AM_V_at)rm -f $(CARGO_BUILD_CONFIG) + $(AM_V_at)$(MKDIR_P) $(abs_top_builddir)/.cargo + $(AM_V_at)touch $@ +else +CARGO_BUILD_OPTS += --offline --config "$(CARGO_BUILD_CONFIG)" +CARGO_CONFIGURED = $(CARGO_BUILD_CONFIG) + +$(CARGO_CONFIGURED): $(RUST_NATIVE_LINKER) $(RUST_TARGET_LINKER) $(top_srcdir)/.cargo/config.toml.offline $(top_srcdir)/Cargo.lock + $(AM_V_at)rm -f $(abs_top_builddir)/.cargo/.configured-for-online + $(AM_V_at)$(MKDIR_P) $(abs_top_builddir)/.cargo + $(AM_V_at)cp $(top_srcdir)/.cargo/config.toml.offline $(CARGO_BUILD_CONFIG) + $(AM_V_at)echo "directory = \"$(RUST_VENDORED_SOURCES)\"" >> $(CARGO_BUILD_CONFIG) + $(AM_V_at)$(top_srcdir)/contrib/devtools/cargo-vendor-git-sources.sh $(top_srcdir)/Cargo.lock >> $(CARGO_BUILD_CONFIG) +endif # ENABLE_ONLINE_RUST diff --git a/rust/Makefile.libs.include b/rust/Makefile.libs.include new file mode 100644 index 000000000000..9f74b0efa2ff --- /dev/null +++ b/rust/Makefile.libs.include @@ -0,0 +1,70 @@ +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2026 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +if ENABLE_DEBUG +CARGO_PROFILE=debug +else +CARGO_PROFILE=release +endif + +# Per-crate includes contribute cxxbridge codegen artifacts (generated C++ +# sources/headers and dist file lists). Compilation is NOT per-crate: all +# component crates are rlib dependencies of the umbrella crate `dashrust`, +# which cargo builds in one invocation into a single deduplicated staticlib. +# This avoids merging per-crate archives with `ar -x`, which silently drops +# object files with colliding member names once two crates share dependencies. +include $(top_srcdir)/rust/Makefile.chirp.include + +LIBRUSTDEPS_INCLUDES = \ + $(LIBRUST_CHIRP_INCLUDES) + +LIBRUSTDEPS_CPP = \ + $(LIBRUST_CHIRP_CPP) + +LIBRUSTDEPS_H = \ + $(LIBCXXBRIDGE_H) \ + $(LIBRUST_CHIRP_H) + +# Umbrella staticlib # +LIBRUSTDEPS_TARGET_DIR = $(abs_top_builddir)/rust/target +LIBRUSTDEPS_CRATE_DIR = $(top_srcdir)/rust/dashrust +LIBRUSTDEPS_MANIFEST = $(LIBRUSTDEPS_CRATE_DIR)/Cargo.toml +LIBRUSTDEPS = $(LIBRUSTDEPS_TARGET_DIR)/$(RUST_TARGET)/$(CARGO_PROFILE)/libdashrust.a + +LIBRUSTDEPS_CARGO_ENV = $(CARGO_ENV) CARGO_TARGET_DIR="$(LIBRUSTDEPS_TARGET_DIR)" +LIBRUSTDEPS_CARGO_BUILD_OPTS = $(CARGO_BUILD_OPTS) --manifest-path $(LIBRUSTDEPS_MANIFEST) + +# Cargo features enabling optional components, provided by configure. +if ENABLE_RUST_FEATURES +LIBRUSTDEPS_CARGO_BUILD_OPTS += --features "$(RUST_CRATE_FEATURES)" +endif + +LIBRUSTDEPS_SRCS = \ + $(LIBRUSTDEPS_CRATE_DIR)/src/lib.rs + +$(LIBRUSTDEPS_SRCS): ; + +LIBRUSTDEPS_FILES = \ + $(LIBRUSTDEPS_MANIFEST) \ + $(LIBRUSTDEPS_SRCS) + +LIBRUSTDEPS_COMPONENT_SRCS = \ + $(LIBRUST_CHIRP_FILES) + +$(LIBRUSTDEPS): $(CARGO_CONFIGURED) \ + $(top_srcdir)/Cargo.toml \ + $(top_srcdir)/Cargo.lock \ + $(LIBRUSTDEPS_SRCS) \ + $(LIBRUSTDEPS_MANIFEST) \ + $(LIBRUSTDEPS_COMPONENT_SRCS) + $(AM_V_GEN)$(LIBRUSTDEPS_CARGO_ENV) $(CARGO) build $(LIBRUSTDEPS_CARGO_BUILD_OPTS) + +cargo-build-dashrust: $(LIBRUSTDEPS) + +cargo-clean-dashrust: + $(AM_V_at)rm -rf $(LIBRUSTDEPS_TARGET_DIR) + +.PHONY: cargo-build-dashrust cargo-clean-dashrust +# diff --git a/rust/chirp/Cargo.toml b/rust/chirp/Cargo.toml new file mode 100644 index 000000000000..a886c80bab5d --- /dev/null +++ b/rust/chirp/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "chirp" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["rlib"] + +[dependencies] +cxx = "1.0.192" + +[build-dependencies] +built = "0.8.0" diff --git a/rust/chirp/build.rs b/rust/chirp/build.rs new file mode 100644 index 000000000000..e57a97ad2171 --- /dev/null +++ b/rust/chirp/build.rs @@ -0,0 +1,3 @@ +fn main() { + built::write_built_file().expect("Failed to write build info"); +} diff --git a/rust/chirp/src/lib.rs b/rust/chirp/src/lib.rs new file mode 100644 index 000000000000..1d27c03613f3 --- /dev/null +++ b/rust/chirp/src/lib.rs @@ -0,0 +1,19 @@ +mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +#[cxx::bridge(namespace = "chirp")] +mod ffi { + extern "Rust" { + fn chirp() -> String; + } +} + +fn chirp() -> String { + format!( + "{} {} built with {} reports \"cheep cheep\"", + built_info::PKG_NAME, + built_info::PKG_VERSION, + built_info::RUSTC_VERSION, + ) +} diff --git a/rust/dashrust/Cargo.toml b/rust/dashrust/Cargo.toml new file mode 100644 index 000000000000..3f8ccfa6c7c4 --- /dev/null +++ b/rust/dashrust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "dashrust" +version = "0.1.0" +edition = "2021" + +# Umbrella crate: the single staticlib linked into Dash Core binaries. +# Individual Rust components are rlib dependencies re-exported here, so cargo +# performs deduplication and LTO across the whole graph and emits one archive +# with no colliding object names. Optional components are cargo features so +# the build system can enable them per configure flags. + +[lib] +crate-type = ["staticlib"] + +[dependencies] +chirp = { path = "../chirp" } diff --git a/rust/dashrust/src/lib.rs b/rust/dashrust/src/lib.rs new file mode 100644 index 000000000000..b1107c19d269 --- /dev/null +++ b/rust/dashrust/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +//! Umbrella crate bundling all Rust components linked into Dash Core. +//! +//! Each component keeps its own `#[cxx::bridge]`; the explicit `extern crate` +//! declarations root the component crates so their exported bridge symbols +//! survive into the staticlib. + +extern crate chirp; diff --git a/src/Makefile.am b/src/Makefile.am index fb1ffde2529a..e7065269bfe3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,6 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers -# Copyright (c) 2014-2018 The Dash Core developers +# Copyright (c) 2016-2025 The Zcash developers +# Copyright (c) 2014-2026 The Dash Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +8,9 @@ print-%: FORCE @echo '$*'='$($*)' +include $(top_srcdir)/rust/Makefile.common.include +include $(top_srcdir)/rust/Makefile.libs.include + DIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) $(CORE_LDFLAGS) $(BACKTRACE_LDFLAGS) @@ -21,6 +25,13 @@ AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) $(CORE_CPPFLAGS) AM_LIBTOOLFLAGS = --preserve-dup-deps PTHREAD_FLAGS = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) EXTRA_LIBRARIES = +BUILT_SOURCES = +if ENABLE_RUST +BUILT_SOURCES += $(LIBRUSTDEPS_H) + +libbitcoin_node_a-init.$(OBJEXT) \ +test/test_dash-system_tests.$(OBJEXT): $(LIBRUSTDEPS_H) +endif lib_LTLIBRARIES = noinst_LTLIBRARIES = @@ -48,6 +59,9 @@ endif # ENABLE_STACKTRACES BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(LEVELDB_CPPFLAGS) BITCOIN_INCLUDES+=-isystem$(srcdir)/dashbls/include -isystem$(srcdir)/dashbls/depends/relic/include -isystem$(srcdir)/dashbls/depends/minialloc/include BITCOIN_INCLUDES+=-isystem$(srcdir)/immer +if ENABLE_RUST +BITCOIN_INCLUDES+=$(LIBRUSTDEPS_INCLUDES) +endif LIBBITCOIN_NODE=libbitcoin_node.a LIBBITCOIN_COMMON=libbitcoin_common.a @@ -116,6 +130,12 @@ $(LIBDASHBLS): $(LIBSECP256K1): $(wildcard secp256k1/src/*.h) $(wildcard secp256k1/src/*.c) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +if ENABLE_RUST +LIBCXXBRIDGE=libcxxbridge.la +RUST_COMPONENT_LIBS=$(LIBCXXBRIDGE) $(LIBRUSTDEPS) +RUST_SYSTEM_LIBS=$(RUST_LIBS) +endif + # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ @@ -129,6 +149,10 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_WALLET_TOOL) \ $(LIBBITCOIN_ZMQ) +if ENABLE_RUST +noinst_LTLIBRARIES += $(LIBCXXBRIDGE) +endif + if BUILD_BITCOIND bin_PROGRAMS += dashd endif @@ -1091,9 +1115,10 @@ bitcoin_bin_ldadd = \ $(LIBDASHBLS) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ - $(LIBSECP256K1) + $(LIBSECP256K1) \ + $(RUST_COMPONENT_LIBS) -bitcoin_bin_ldadd += $(BACKTRACE_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(GMP_LIBS) +bitcoin_bin_ldadd += $(BACKTRACE_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(GMP_LIBS) $(RUST_SYSTEM_LIBS) dashd_SOURCES = $(bitcoin_daemon_sources) init/bitcoind.cpp dashd_CPPFLAGS = $(bitcoin_bin_cppflags) @@ -1407,6 +1432,17 @@ libdashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # +# CXX bridge library # +if ENABLE_RUST +libcxxbridge_la_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(PIC_FLAGS) +libcxxbridge_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(PIC_FLAGS) -static +libcxxbridge_la_LDFLAGS = $(AM_LDFLAGS) -static +nodist_libcxxbridge_la_SOURCES = $(LIBRUSTDEPS_CPP) + +$(libcxxbridge_la_OBJECTS): $(LIBRUSTDEPS_H) +endif +# + CTAES_DIST = crypto/ctaes/bench.c CTAES_DIST += crypto/ctaes/ctaes.c CTAES_DIST += crypto/ctaes/ctaes.h diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 7dec51f2b0c1..78a51e5f5128 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -77,12 +77,14 @@ bench_bench_dash_LDADD = \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ + $(RUST_COMPONENT_LIBS) \ $(LIBUNIVALUE) \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) \ $(MINIUPNPC_LIBS) \ $(NATPMP_LIBS) \ $(GMP_LIBS) \ + $(RUST_SYSTEM_LIBS) \ $(BACKTRACE_LIBS) if ENABLE_ZMQ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 16296207eca1..7bfb3ae10980 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -462,8 +462,8 @@ if ENABLE_ZMQ bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBDASHBLS) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BACKTRACE_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(LIBSECP256K1) \ - $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) + $(BACKTRACE_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(LIBSECP256K1) $(RUST_COMPONENT_LIBS) \ + $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) $(RUST_SYSTEM_LIBS) bitcoin_qt_ldflags = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 62071d40e5a5..c5e416bdfb67 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -60,8 +60,8 @@ qt_test_test_dash_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_dash_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBDASHBLS) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBMEMENV) $(BACKTRACE_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ - $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(LIBSECP256K1) \ - $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) + $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(LIBSECP256K1) $(RUST_COMPONENT_LIBS) \ + $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) $(RUST_SYSTEM_LIBS) qt_test_test_dash_qt_LDFLAGS = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) qt_test_test_dash_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index f83e11e2895e..94208fa2e99d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -56,10 +56,12 @@ FUZZ_SUITE_LD_COMMON = \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ + $(RUST_COMPONENT_LIBS) \ $(MINISKETCH_LIBS) \ $(EVENT_LIBS) \ $(EVENT_PTHREADS_LIBS) \ $(GMP_LIBS) \ + $(RUST_SYSTEM_LIBS) \ $(BACKTRACE_LIBS) if USE_UPNP @@ -271,10 +273,10 @@ test_test_dash_LDADD += $(LIBBITCOIN_WALLET) test_test_dash_CPPFLAGS += $(BDB_CPPFLAGS) endif test_test_dash_LDADD += $(LIBBITCOIN_NODE) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \ - $(LIBDASHBLS) $(LIBLEVELDB) $(LIBMEMENV) $(BACKTRACE_LIBS) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) $(MINISKETCH_LIBS) + $(LIBDASHBLS) $(LIBLEVELDB) $(LIBMEMENV) $(BACKTRACE_LIBS) $(LIBSECP256K1) $(RUST_COMPONENT_LIBS) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) $(MINISKETCH_LIBS) test_test_dash_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_dash_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) +test_test_dash_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) $(RUST_SYSTEM_LIBS) test_test_dash_LDFLAGS = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static if ENABLE_ZMQ diff --git a/src/init.cpp b/src/init.cpp index 7bb5019fd4b4..14f14db2a13d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -126,6 +126,10 @@ #include #endif // ENABLE_WALLET +#ifdef ENABLE_RUST +#include +#endif + #include #include #include @@ -1476,6 +1480,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return false; } +#ifdef ENABLE_RUST + LogPrintf("Rust bridge: %s\n", std::string(chirp::chirp())); +#endif + LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index ce7ed3912c79..e1a245dbc334 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -6,6 +6,10 @@ #include #include +#ifdef ENABLE_RUST +#include +#endif // ENABLE_RUST + #ifdef ENABLE_EXTERNAL_SIGNER #include #endif // ENABLE_EXTERNAL_SIGNER @@ -21,6 +25,15 @@ BOOST_AUTO_TEST_CASE(dummy) BOOST_CHECK(true); } +#ifdef ENABLE_RUST +BOOST_AUTO_TEST_CASE(rust_bridge) +{ + const std::string result{chirp::chirp()}; + BOOST_CHECK(result.starts_with("chirp 0.1.0 built with rustc ")); + BOOST_CHECK(result.ends_with(" reports \"cheep cheep\"")); +} +#endif // ENABLE_RUST + #ifdef ENABLE_EXTERNAL_SIGNER BOOST_AUTO_TEST_CASE(run_command)