Skip to content

Commit a620e07

Browse files
committed
feat(nix): Android build outputs
This commit introduces nix outputs in flake.nix that prepares the environment gathering dependencies that the Android build instructions needs and testing instructions. Testing android outputs is restricted to x86_64-linux and automatically triggered when the build process ends. QEMU user-mode emulation is configured as the Cargo test runner for cross-compiled Android targets.
1 parent fb5e1f7 commit a620e07

4 files changed

Lines changed: 142 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
Cargo.lock
33
coverage_report
4+
result

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- Added Nix package outputs for Android with bundled NDK r24, Rust toolchains, Boost, and cmake.
1112
- Added `Block::check` to perform context-free validation of a block (size, weight, coinbase, transactions, sigops), with optional proof-of-work and merkle-root checks toggled via the `BLOCK_CHECK_BASE` / `_POW` / `_MERKLE` / `_ALL` flags. Returns a `BlockCheckResult` enum carrying the validation state on failure.
1213
- Implemented `Debug` for `BlockValidationResult`, `BlockValidationStateRef`, `ProcessBlockHeaderresult` and `BlockCheckResult`, enabling inspection via `{:?}` in logs and test output.
1314
- Added `#[must_use]` to `BlockCheckResult` and `ProcessBlockHeaderResult` to warn when validation results are silently ignored.

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ dependencies. Once setup, run:
3434
cargo b
3535
```
3636

37+
### Android Cross-Compilation
38+
39+
Android cross-compilation requires [Nix](https://nixos.org/).
40+
41+
Nix handles the exact NDK version, Rust toolchains, Boost, and cmake
42+
automatically, giving you a reproducible build environment with no manual setup.
43+
44+
```bash
45+
nix build .#android-aarch64
46+
nix build .#android-armv7
47+
nix build .#android-x86_64
48+
```
49+
50+
The resulting libraries are placed in `result/lib/`.
51+
52+
Output targets Android API 24+ (Nougat) minimum.
53+
3754
## MSRV (Minimum Supported Rust Version)
3855

3956
The minimum supported Rust version is 1.71. Users on rustc older than

flake.nix

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
};
2626

2727
rustVersion = "1.71.0";
28+
rustToolchainSha256 = "sha256-ks0nMEGGXKrHnfv4Fku+vhQ7gx76ruv6Ij4fKZR3l78=";
2829
rustToolchain = fenix.packages.${system}.fromToolchainName {
2930
name = rustVersion;
30-
sha256 = "sha256-ks0nMEGGXKrHnfv4Fku+vhQ7gx76ruv6Ij4fKZR3l78=";
31+
sha256 = rustToolchainSha256;
3132
};
3233
rustBuildToolchain = fenix.packages.${system}.combine [
3334
rustToolchain.rustc
@@ -85,6 +86,127 @@
8586
pkgs.gcc.cc.lib
8687
];
8788
};
89+
90+
packages =
91+
# Android build infrastructure (unfree NDK + SDK).
92+
let
93+
ndkVersion = "27.2.12479018"; # which NDK release to download
94+
lockfile = ./Cargo-minimal.lock;
95+
ANDROID_API_LEVEL = "24";
96+
crateVersion =
97+
(builtins.fromTOML (builtins.readFile ./libbitcoinkernel-sys/Cargo.toml)).package.version;
98+
99+
androidPkgs = import nixpkgs {
100+
inherit system;
101+
config.android_sdk.accept_license = true;
102+
config.allowUnfree = true;
103+
};
104+
androidComposition = androidPkgs.androidenv.composeAndroidPackages {
105+
# platformVersions is the SDK tooling version, not the minimum API level.
106+
# The NDK target floor is set via ANDROID_API_LEVEL in build.rs (default 24).
107+
platformVersions = [ "34" ];
108+
ndkVersions = [ ndkVersion ];
109+
includeNDK = true;
110+
};
111+
androidSdk = androidComposition.androidsdk;
112+
androidNdk = "${androidSdk}/libexec/android-sdk/ndk/${ndkVersion}";
113+
114+
runTests = system == "x86_64-linux";
115+
116+
# x86_64-linux-android is excluded from tests: Rust 1.71's
117+
# compiler_builtins lacks the f128 routines (__eqtf2, __multf3, …)
118+
# that bionic's static libc.a requires on x86_64.
119+
testableTargets = [
120+
"aarch64-linux-android"
121+
"armv7-linux-androideabi"
122+
];
123+
124+
mkAndroidPackage =
125+
rustTarget:
126+
let
127+
canTest = runTests && builtins.elem rustTarget testableTargets;
128+
rustTargetToolchain = fenix.packages.${system}.combine [
129+
rustToolchain.rustc
130+
rustToolchain.cargo
131+
rustToolchain.rust-src
132+
rustToolchain.rust-std
133+
(fenix.packages.${system}.targets.${rustTarget}.fromToolchainName {
134+
name = rustVersion;
135+
sha256 = rustToolchainSha256;
136+
}).rust-std
137+
];
138+
rustPlatform = androidPkgs.makeRustPlatform {
139+
cargo = rustTargetToolchain;
140+
rustc = rustTargetToolchain;
141+
};
142+
qemuBin =
143+
if builtins.match "aarch64.*" rustTarget != null then
144+
"qemu-aarch64"
145+
else if builtins.match "armv7.*" rustTarget != null then
146+
"qemu-arm"
147+
else
148+
throw "Unsupported Android target: ${rustTarget}";
149+
# NDK clang wrapper triple: armv7 uses "armv7a-linux-androideabi",
150+
# all others match the Rust target triple.
151+
ndkClangTriple =
152+
if builtins.match "armv7.*" rustTarget != null then "armv7a-linux-androideabi" else rustTarget;
153+
ndkLinker = "${androidNdk}/toolchains/llvm/prebuilt/linux-x86_64/bin/${ndkClangTriple}${ANDROID_API_LEVEL}-clang";
154+
cargoTargetPrefix = "CARGO_TARGET_${
155+
builtins.replaceStrings [ "-" ] [ "_" ] (pkgs.lib.toUpper rustTarget)
156+
}";
157+
cargoRunnerEnvVar = "${cargoTargetPrefix}_RUNNER";
158+
cargoLinkerEnvVar = "${cargoTargetPrefix}_LINKER";
159+
in
160+
rustPlatform.buildRustPackage {
161+
pname = "libbitcoinkernel-${rustTarget}";
162+
version = crateVersion;
163+
src = ./.;
164+
cargoLock.lockFile = lockfile;
165+
postPatch = ''
166+
cp ${lockfile} Cargo.lock
167+
'';
168+
nativeBuildInputs = [
169+
androidPkgs.cmake
170+
androidPkgs.boost.dev
171+
androidSdk
172+
]
173+
++ pkgs.lib.optionals canTest [
174+
pkgs.qemu
175+
];
176+
177+
ANDROID_HOME = "${androidSdk}/libexec/android-sdk";
178+
ANDROID_NDK_HOME = androidNdk;
179+
CMAKE_PREFIX_PATH = "${androidPkgs.boost.dev}";
180+
181+
# cargoBuildHook hardcodes the host --target at
182+
# derivation time, so we bypass it for cross builds.
183+
dontCargoBuild = true;
184+
doCheck = canTest;
185+
buildPhase = ''
186+
cargo build -p libbitcoinkernel-sys --target ${rustTarget} --offline --release
187+
'';
188+
checkPhase = pkgs.lib.optionalString canTest ''
189+
export ${cargoLinkerEnvVar}=${ndkLinker}
190+
export ${cargoRunnerEnvVar}=${pkgs.qemu}/bin/${qemuBin}
191+
export QEMU_LD_PREFIX=${androidNdk}/toolchains/llvm/prebuilt/linux-x86_64/sysroot
192+
export RUSTFLAGS="-C target-feature=+crt-static"
193+
cargo test --target ${rustTarget} --offline --release --verbose
194+
'';
195+
installPhase = ''
196+
mkdir -p $out/lib $out/include
197+
find target/${rustTarget}/release -path "*/out/install/lib/*.a" \
198+
-exec cp {} $out/lib/ \;
199+
find target/${rustTarget}/release -path "*/out/install/include/*" \
200+
-exec cp {} $out/include/ \;
201+
'';
202+
};
203+
in
204+
{
205+
android-aarch64 = mkAndroidPackage "aarch64-linux-android";
206+
android-armv7 = mkAndroidPackage "armv7-linux-androideabi";
207+
android-x86_64 = mkAndroidPackage "x86_64-linux-android";
208+
# i686 omitted: not a current target
209+
};
88210
}
89211
);
90212
}

0 commit comments

Comments
 (0)