Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/build-depends.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -144,6 +149,28 @@ jobs:
key: depends-sources-${{ hashFiles('depends/packages/*') }}
restore-keys: depends-sources-

- name: Restore Rust vendor sources
id: rust-vendor-cache
if: inputs.build-target == 'linux64_platform_gui'
uses: actions/cache/restore@v5
with:
path: depends/sources/platform-cxx-*-vendored.tar.gz
key: depends-rust-vendor-sources-${{ hashFiles('depends/Makefile', 'depends/funcs.mk', 'depends/packages/native_rust.mk', 'depends/packages/platform_cxx.mk', 'depends/patches/platform_cxx/cargo-config.toml') }}

- name: Download Rust vendor sources
if: inputs.build-target == 'linux64_platform_gui' && 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_platform_gui' && 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
uses: actions/cache/restore@v5
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,18 @@ jobs:
base-image-digest: ${{ needs.check-skip.outputs.base-image-digest }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

depends-linux64_platform_gui:
name: x86_64-pc-linux-gnu_platform_gui
uses: ./.github/workflows/build-depends.yml
needs: [check-skip, container, cache-sources]
if: ${{ vars.SKIP_LINUX64_PLATFORM_GUI == '' }}
with:
build-target: linux64_platform_gui
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-mac:
name: x86_64-apple-darwin
uses: ./.github/workflows/build-depends.yml
Expand Down Expand Up @@ -277,6 +289,20 @@ jobs:
depends-artifact: ${{ needs.depends-linux64_nowallet.outputs.built-artifact }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

src-linux64_platform_gui:
name: linux64_platform_gui-build
uses: ./.github/workflows/build-src.yml
needs: [check-skip, container, depends-linux64_platform_gui]
if: ${{ vars.SKIP_LINUX64_PLATFORM_GUI == '' }}
with:
build-target: linux64_platform_gui
container-path: ${{ needs.container.outputs.path }}
depends-key: ${{ needs.depends-linux64_platform_gui.outputs.key }}
depends-host: ${{ needs.depends-linux64_platform_gui.outputs.host }}
depends-dep-opts: ${{ needs.depends-linux64_platform_gui.outputs.dep-opts }}
depends-artifact: ${{ needs.depends-linux64_platform_gui.outputs.built-artifact }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

src-linux64_sqlite:
name: linux64_sqlite-build
uses: ./.github/workflows/build-src.yml
Expand Down Expand Up @@ -372,6 +398,17 @@ jobs:
container-path: ${{ needs.container-slim.outputs.path }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

test-linux64_platform_gui:
name: linux64_platform_gui-test
uses: ./.github/workflows/test-src.yml
needs: [check-skip, container-slim, src-linux64_platform_gui, lint]
if: ${{ vars.SKIP_LINUX64_PLATFORM_GUI == '' }}
with:
bundle-key: ${{ needs.src-linux64_platform_gui.outputs.key }}
build-target: linux64_platform_gui
container-path: ${{ needs.container-slim.outputs.path }}
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}

test-linux64_sqlite:
name: linux64_sqlite-test
uses: ./.github/workflows/test-src.yml
Expand Down
40 changes: 38 additions & 2 deletions .github/workflows/cache-depends-sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 * * *'
Expand All @@ -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
Expand All @@ -35,6 +41,36 @@ jobs:
restore-keys: depends-sources-
lookup-only: true

- name: Cache Rust vendor sources
id: rust-vendor-cache
uses: actions/cache@v5
with:
path: depends/sources/platform-cxx-*-vendored.tar.gz
key: depends-rust-vendor-sources-${{ hashFiles('depends/Makefile', 'depends/funcs.mk', 'depends/packages/native_rust.mk', 'depends/packages/platform_cxx.mk', 'depends/patches/platform_cxx/cargo-config.toml') }}

- 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 PLATFORM_GUI=1 download
make -C depends PLATFORM_GUI=1 vendor-platform_cxx-crates
# The cache producer normally runs on ARM, while the platform_gui
# consumer runs on x86_64. Native Rust is selected from the build
# architecture, so fetch the x86_64 compiler archive explicitly too.
make -C depends BUILD=x86_64-pc-linux-gnu PLATFORM_GUI=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/platform-cxx-*-vendored.tar.gz
compression-level: 0
retention-days: 1
overwrite: true
2 changes: 2 additions & 0 deletions ci/dash/matrix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ elif [ "$BUILD_TARGET" = "linux64_multiprocess" ]; then
source ./ci/test/00_setup_env_native_multiprocess.sh
elif [ "$BUILD_TARGET" = "linux64_nowallet" ]; then
source ./ci/test/00_setup_env_native_nowallet_libbitcoinkernel.sh
elif [ "$BUILD_TARGET" = "linux64_platform_gui" ]; then
source ./ci/test/00_setup_env_native_platform_gui.sh
elif [ "$BUILD_TARGET" = "linux64_sqlite" ]; then
source ./ci/test/00_setup_env_native_sqlite.sh
elif [ "$BUILD_TARGET" = "linux64_tsan" ]; then
Expand Down
25 changes: 25 additions & 0 deletions ci/test/00_setup_env_native_platform_gui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/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

# Builds depends with PLATFORM_GUI=1 so mbedtls and the Platform-owned CXX
# binding archive (libdash_platform_cxx.a, built offline from vendored crates)
# are produced, hash-verified and installed into the depends prefix, then
# builds dash-qt against that prefix. The --enable-platform-gui configure flag
# and the platform_* unit-test suites arrive with the Platform client library
# and are added to BITCOIN_CONFIG there; until then this lane proves the
# depends knob end to end and that the enriched prefix stays link-compatible.
# Functional tests are skipped: there is no dashd-only surface to drive.
export CONTAINER_NAME=ci_native_platform_gui
export HOST=x86_64-pc-linux-gnu
export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
export DEP_OPTS="PLATFORM_GUI=1"
export RUN_UNIT_TESTS="true"
export RUN_UNIT_TESTS_SEQUENTIAL="false"
export RUN_FUNCTIONAL_TESTS="false"
export GOAL="install"
export BITCOIN_CONFIG="--with-gui=qt5 --enable-zmq --with-libs=no --enable-reduce-exports LDFLAGS=-static-libstdc++"
130 changes: 130 additions & 0 deletions contrib/devtools/update-rust-hashes.py
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +91 to +97

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop requiring nonexistent Rust consumer files

Running the newly documented contrib/devtools/update-rust-hashes.py in this commit always exits here because the repository contains no rust-toolchain.toml; configure.ac also has no RUSTC_REQUIRED_VERSION assignment for the later update. As a result, maintainers cannot use the script to update either of the Rust depends pins it was added to maintain. Limit synchronization to files present in this change, or add the expected consumer files before making them mandatory.

Useful? React with 👍 / 👎.

Comment on lines +91 to +97

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Do not require absent Rust consumer files

The updater always exits here because this revision has no repository-level rust-toolchain.toml. In addition, configure.ac contains no RUSTC_REQUIRED_VERSION assignment, so the updates at lines 121-122 would fail even if the existence check were bypassed. This makes the script referenced by native_rust.mk unusable for maintaining the new native_rust.mk and rust_stdlib.mk pins. Limit synchronization in this PR to the two depends package files, or add the consumer files and expected version assignment before requiring them.

source: ['codex']


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())
Loading
Loading