Skip to content

Commit 378a3bc

Browse files
committed
feat: android support
1 parent 0e143e5 commit 378a3bc

5 files changed

Lines changed: 133 additions & 109 deletions

File tree

.github/workflows/ci.yml

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,26 +212,45 @@ jobs:
212212
run: cargo test -vv
213213

214214

215-
android-nix-cross-check:
216-
name: Cross-Compile (${{ matrix.check }}) building using nix
215+
android-cross-compile:
216+
name: Cross-Compile for Android (${{ matrix.target }})
217217
runs-on: ubuntu-latest
218218
strategy:
219219
fail-fast: false
220220
matrix:
221-
check:
222-
- android-aarch64
223-
- android-armv7
224-
- android-x86_64
221+
target:
222+
- aarch64-linux-android
223+
- armv7-linux-androideabi
224+
- x86_64-linux-android
225225
steps:
226-
- uses: actions/checkout@v4
226+
- uses: actions/checkout@v3
227227

228-
- uses: cachix/install-nix-action@v27
228+
- name: Install Rust toolchain
229+
uses: actions-rs/toolchain@v1
229230
with:
230-
extra_nix_config: |
231-
accept-flake-config = true
231+
profile: minimal
232+
toolchain: stable
233+
override: true
234+
target: ${{ matrix.target }}
235+
236+
- name: Cache dependencies
237+
uses: Swatinem/rust-cache@v2
232238

233-
- name: Build check
234-
run: nix build .#checks.x86_64-linux.${{ matrix.check }} -L
239+
- name: Install Boost library
240+
run: |
241+
sudo apt-get update
242+
sudo apt-get install -y libboost-all-dev
243+
244+
- name: Set up Android NDK
245+
uses: nttld/setup-ndk@v1
246+
id: setup-ndk
247+
with:
248+
ndk-version: r27c
249+
250+
- name: Build
251+
env:
252+
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
253+
run: cargo build -p libbitcoinkernel-sys --target ${{ matrix.target }} -vv
235254

236255
fuzz-corpus:
237256
name: Verify Fuzz Corpus

flake.nix

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -64,71 +64,6 @@
6464
}).rust-std
6565
];
6666

67-
# -- Source filtering --
68-
src = pkgs.lib.cleanSourceWith {
69-
src = self;
70-
filter = path: type:
71-
!(builtins.any (suffix: pkgs.lib.hasSuffix suffix path) [
72-
"/target"
73-
"/.git"
74-
"/result"
75-
]);
76-
};
77-
78-
# -- Stable Rust for checks (MSRV is too old for some vendored crate manifests) --
79-
rustStable = fenix.packages.${system}.stable;
80-
mkAndroidCheck = { name, rustTarget }:
81-
let
82-
androidStableToolchain = fenix.packages.${system}.combine [
83-
rustStable.rustc
84-
rustStable.cargo
85-
rustStable.rust-src
86-
rustStable.rust-std
87-
fenix.packages.${system}.targets.${rustTarget}.stable.rust-std
88-
];
89-
androidRustPlatform = pkgs.makeRustPlatform {
90-
cargo = androidStableToolchain;
91-
rustc = androidStableToolchain;
92-
};
93-
in
94-
androidRustPlatform.buildRustPackage {
95-
pname = "rust-bitcoinkernel-android-${name}";
96-
version = "0.2.0";
97-
inherit src;
98-
99-
cargoLock.lockFile = self + "/Cargo-recent.lock";
100-
101-
nativeBuildInputs = [
102-
pkgs.cmake
103-
pkgs.pkg-config
104-
pkgs.python3
105-
androidSdk
106-
];
107-
108-
buildInputs = [
109-
pkgs.boost.dev
110-
];
111-
112-
LIBCLANG_PATH = "${pkgs.llvmPackages.clang-unwrapped.lib}/lib/";
113-
ANDROID_HOME = "${androidSdk}/libexec/android-sdk";
114-
ANDROID_NDK_HOME = androidNdk;
115-
ANDROID_NDK_ROOT = androidNdk;
116-
CARGO_BUILD_TARGET = rustTarget;
117-
118-
buildPhase = ''
119-
runHook preBuild
120-
cargo build --target ${rustTarget} --release -p libbitcoinkernel-sys
121-
runHook postBuild
122-
'';
123-
doCheck = false;
124-
125-
installPhase = ''
126-
touch $out
127-
'';
128-
129-
dontFixup = true;
130-
};
131-
13267
rustfilt = rustPlatformNightly.buildRustPackage rec {
13368
pname = "rustfilt";
13469
version = "0.2.1";
@@ -192,26 +127,6 @@
192127
ANDROID_NDK_ROOT = androidNdk;
193128
};
194129

195-
# Verify Android cross-compilation. Run with:
196-
# nix build .#checks.x86_64-linux.android-aarch64 -L
197-
# nix build .#checks.x86_64-linux.android-armv7 -L
198-
# nix build .#checks.x86_64-linux.android-x86_64 -L
199-
checks = {
200-
android-aarch64 = mkAndroidCheck {
201-
name = "aarch64";
202-
rustTarget = "aarch64-linux-android";
203-
};
204-
205-
android-armv7 = mkAndroidCheck {
206-
name = "armv7";
207-
rustTarget = "armv7-linux-androideabi";
208-
};
209-
210-
android-x86_64 = mkAndroidCheck {
211-
name = "x86_64";
212-
rustTarget = "x86_64-linux-android";
213-
};
214-
};
215130
}
216131
);
217132
}

libbitcoinkernel-sys/CHANGELOG.md

Lines changed: 7 additions & 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+
1112
- New `btck_ConsensusParams` opaque type for holding consensus parameters
1213
- New `btck_chain_parameters_get_consensus_params` for extracting consensus params from `btck_ChainParameters` (lifetime-bound to the chain parameters object)
1314
- New `btck_block_check` for context-free block validation (size limits, coinbase structure, sigop limits, with optional POW and merkle-root checks via `btck_BlockCheckFlags`)
@@ -18,14 +19,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1819
- New `btck_block_header_to_bytes` for serializing a block header to its 80-byte consensus encoding
1920

2021
### Changed
22+
2123
- Replaced bindgen-generated bindings with hand-written FFI bindings
24+
- Added Android cross-compilation support in `build.rs` (aarch64, armv7, x86_64)
25+
- Made compile-time layout assertions pointer-width aware for 32-bit target support
2226
- `btck_chain_get_by_height` `block_height` parameter type changed from `int` to `int32_t`
2327
- `btck_chain_get_height` return type changed from `int` to `int32_t`
2428
- `btck_block_validation_state_destroy` now accepts a null pointer (removed `NONNULL` annotation)
2529

2630
## [0.2.0] - 2026-01-26
2731

2832
### Added
33+
2934
- New `btck_block_tree_entry_equals` function for comparing BlockTreeEntry objects (096924d39d64)
3035
- New `btck_PrecomputedTransactionData` object for holding transaction hashes required when validating scripts (eb0594e23f0c)
3136
- New `btck_BlockHeader` object for holding block headers (9a9d797ef6ed)
@@ -36,11 +41,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3641
- New `btck_block_validation_state_*` functions for managing an owned block validation state, that may be used for processing block headers (9a9d797ef6ed)
3742

3843
### Changed
44+
3945
- `data_directory` and `blocks_directory` parameters in `btck_chainstate_manager_options_create` now allow null values to represent empty paths (6657bcbdb4d0)
4046
- `btck_script_pubkey_verify` now takes a `btck_PrecomputedTransactionData` instead of an array of outputs for verifying taproot outputs (eb0594e23f0c)
4147

4248
## [0.1.1] - 2025-24-11
4349

4450
### Fixed
51+
4552
- Precise package excludes to ensure the test/fuzz directory is included
4653
in the packaged crate correctly.

libbitcoinkernel-sys/build.rs

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@ use std::env;
22
use std::path::Path;
33
use std::process::Command;
44

5+
/// Maps Rust target triples to Android ABI names used by the NDK.
6+
fn android_abi(target: &str) -> Option<&'static str> {
7+
match target {
8+
t if t.contains("aarch64") => Some("arm64-v8a"),
9+
t if t.contains("armv7") => Some("armeabi-v7a"),
10+
t if t.contains("x86_64") => Some("x86_64"),
11+
t if t.contains("i686") => Some("x86"),
12+
_ => None,
13+
}
14+
}
15+
16+
/// Maps Rust target triples to the NDK sysroot directory names.
17+
/// These differ from Rust triples for armv7 targets.
18+
fn android_sysroot_triple(target: &str) -> &str {
19+
if target.starts_with("armv7") {
20+
"arm-linux-androideabi"
21+
} else {
22+
target
23+
}
24+
}
25+
26+
/// Detect whether we are cross-compiling for Android and return the NDK home path if so.
27+
fn android_ndk_home() -> Option<String> {
28+
env::var("ANDROID_NDK_HOME")
29+
.or_else(|_| env::var("ANDROID_NDK_ROOT"))
30+
.or_else(|_| env::var("NDK_HOME"))
31+
.ok()
32+
}
33+
534
fn main() {
635
let bitcoin_dir = Path::new("bitcoin");
736
let out_dir = env::var("OUT_DIR").unwrap();
@@ -15,7 +44,12 @@ fn main() {
1544

1645
let build_config = "RelWithDebInfo";
1746

18-
Command::new("cmake")
47+
let target = env::var("TARGET").unwrap();
48+
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
49+
let is_android = target_os == "android";
50+
51+
let mut cmake_configure = Command::new("cmake");
52+
cmake_configure
1953
.arg("-B")
2054
.arg(&build_dir)
2155
.arg("-S")
@@ -38,9 +72,33 @@ fn main() {
3872
.arg("-DBUILD_SHARED_LIBS=OFF")
3973
.arg("-DCMAKE_INSTALL_LIBDIR=lib")
4074
.arg("-DENABLE_IPC=OFF")
41-
.arg(format!("-DCMAKE_INSTALL_PREFIX={}", install_dir.display()))
42-
.status()
43-
.expect("cmake should be installed and available in PATH");
75+
.arg(format!("-DCMAKE_INSTALL_PREFIX={}", install_dir.display()));
76+
77+
if is_android {
78+
let ndk =
79+
android_ndk_home().expect("Android target detected but ANDROID_NDK_HOME is not set");
80+
let toolchain_file = format!("{ndk}/build/cmake/android.toolchain.cmake");
81+
let abi =
82+
android_abi(&target).unwrap_or_else(|| panic!("unsupported Android target: {target}"));
83+
// API level 24+ is required because Bitcoin Core uses getifaddrs
84+
// which was introduced in Android API 24 (Nougat).
85+
let api_level = env::var("ANDROID_API_LEVEL").unwrap_or_else(|_| "24".to_string());
86+
87+
cmake_configure
88+
.arg(format!("-DCMAKE_TOOLCHAIN_FILE={toolchain_file}"))
89+
.arg(format!("-DANDROID_ABI={abi}"))
90+
.arg(format!("-DANDROID_PLATFORM=android-{api_level}"))
91+
.arg("-DCMAKE_SYSTEM_NAME=Android")
92+
.arg(format!("-DCMAKE_ANDROID_ARCH_ABI={abi}"))
93+
.arg(format!("-DCMAKE_SYSTEM_VERSION={api_level}"))
94+
.arg(format!("-DCMAKE_ANDROID_NDK={ndk}"))
95+
// The Android NDK toolchain sets CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
96+
// to ONLY, which prevents cmake from finding host packages via
97+
// CMAKE_PREFIX_PATH. Override it so Boost headers can be located.
98+
.arg("-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH");
99+
}
100+
101+
cmake_configure.status().unwrap();
44102

45103
let num_jobs = env::var("NUM_JOBS")
46104
.ok()
@@ -75,14 +133,27 @@ fn main() {
75133
println!("cargo:rustc-link-lib=static=bitcoinkernel");
76134

77135
let compiler = cc::Build::new().get_compiler();
78-
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
79136

80137
if target_os == "windows" {
81138
println!("cargo:rustc-link-lib=bcrypt");
82139
println!("cargo:rustc-link-lib=shell32");
83-
}
84-
85-
if compiler.is_like_clang() {
140+
} else if is_android {
141+
// Android NDK ships libc++_static.a and libc++abi.a in the
142+
// per-architecture sysroot directory (not the API-level subdirectory).
143+
if let Some(ndk) = android_ndk_home() {
144+
let ndk_triple = android_sysroot_triple(&target);
145+
let host_tag = if cfg!(target_os = "macos") {
146+
"darwin-x86_64"
147+
} else {
148+
"linux-x86_64"
149+
};
150+
let ndk_lib_dir =
151+
format!("{ndk}/toolchains/llvm/prebuilt/{host_tag}/sysroot/usr/lib/{ndk_triple}");
152+
println!("cargo:rustc-link-search=native={ndk_lib_dir}");
153+
}
154+
println!("cargo:rustc-link-lib=static=c++_static");
155+
println!("cargo:rustc-link-lib=static=c++abi");
156+
} else if compiler.is_like_clang() {
86157
if target_os == "macos" {
87158
println!("cargo:rustc-link-lib=dylib=c++");
88159
} else {

libbitcoinkernel-sys/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,10 +324,22 @@ pub struct btck_ValidationInterfaceCallbacks {
324324
const _: () = {
325325
assert!(core::mem::size_of::<btck_LoggingOptions>() == 20);
326326
assert!(core::mem::align_of::<btck_LoggingOptions>() == 4);
327-
assert!(core::mem::size_of::<btck_NotificationInterfaceCallbacks>() == 72);
328-
assert!(core::mem::align_of::<btck_NotificationInterfaceCallbacks>() == 8);
329-
assert!(core::mem::size_of::<btck_ValidationInterfaceCallbacks>() == 48);
330-
assert!(core::mem::align_of::<btck_ValidationInterfaceCallbacks>() == 8);
327+
assert!(
328+
core::mem::size_of::<btck_NotificationInterfaceCallbacks>()
329+
== 9 * core::mem::size_of::<*const ()>()
330+
);
331+
assert!(
332+
core::mem::align_of::<btck_NotificationInterfaceCallbacks>()
333+
== core::mem::align_of::<*const ()>()
334+
);
335+
assert!(
336+
core::mem::size_of::<btck_ValidationInterfaceCallbacks>()
337+
== 6 * core::mem::size_of::<*const ()>()
338+
);
339+
assert!(
340+
core::mem::align_of::<btck_ValidationInterfaceCallbacks>()
341+
== core::mem::align_of::<*const ()>()
342+
);
331343
};
332344

333345
// extern "C" declarations - grouped by type

0 commit comments

Comments
 (0)