diff --git a/.cargo/config.toml b/.cargo/config.toml index 1ca7145..09c0e74 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,4 +1,3 @@ -[alias] -test-unit = "test --lib" -test-integration = "test --test mod" -test-doc = "test --doc" +[target.aarch64-linux-android] +linker = "aarch64-linux-android21-clang" +ar = "llvm-ar" diff --git a/.gitignore b/.gitignore index d96c024..6c86aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,7 @@ src/ffi/bindings.rs *.debug # LLM -.kilocode \ No newline at end of file +.kilocode + +# bindings +.last_built_architecture \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 428e8f3..e52a7fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codex-bindings" -version = "0.1.3" +version = "0.1.3-android-1" edition = "2021" description = "Rust bindings for Codex, the Decentralized Durability Engine" license = "MIT" @@ -8,6 +8,7 @@ repository = "https://github.com/nipsysdev/codex-rust-bindings" homepage = "https://codex.storage" keywords = ["codex", "storage", "p2p", "distributed"] categories = ["api-bindings", "network-programming"] +exclude = ["vendor/nim-codex/*"] [lib] name = "codex_bindings" @@ -23,6 +24,8 @@ chrono = { version = "0.4", features = ["serde"] } once_cell = "1.21" bytesize = "2.1" futures = "0.3" +sha2 = "0.10" +clap = { version = "4.0", features = ["derive"] } [dependencies.tokio] version = "1" @@ -33,6 +36,11 @@ optional = true bindgen = "0.72" pkg-config = "0.3" cc = "1.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.10" +chrono = { version = "0.4", features = ["serde"] } +thiserror = "2.0" [dev-dependencies] tempfile = "3.23" @@ -41,6 +49,6 @@ env_logger = "0.10" tokio = { version = "1", features = ["macros", "io-util", "rt-multi-thread"] } [features] -default = ["tokio"] +default = ["tokio", "static-linking"] static-linking = [] dynamic-linking = [] diff --git a/README.md b/README.md index e7495f2..fe9bfe8 100644 --- a/README.md +++ b/README.md @@ -61,18 +61,28 @@ cargo test-doc This crate supports two linking modes via Cargo features: -### Dynamic Linking (Default) +### Static Linking (Default) ```bash cargo build # or explicitly +cargo build --features static-linking +``` + +### Dynamic Linking + +```bash cargo build --features dynamic-linking ``` -### Static Linking +## Android Builds + +To build for Android targets, you need to set the Android SDK and NDK environment variables: ```bash -cargo build --features static-linking +export ANDROID_SDK_ROOT=/path/to/your/Android/Sdk +export ANDROID_NDK_HOME=/path/to/your/Android/Sdk/ndk/ndk_version +cargo build --target aarch64-linux-android ``` ### In your Cargo.toml diff --git a/android-patches/arm64/fixes/rand_type_fix.patch b/android-patches/arm64/fixes/rand_type_fix.patch new file mode 100644 index 0000000..2b612ba --- /dev/null +++ b/android-patches/arm64/fixes/rand_type_fix.patch @@ -0,0 +1,14 @@ +--- a/vendor/nim-codex/codex/blockexchange/engine/engine.nim ++++ b/vendor/nim-codex/codex/blockexchange/engine/engine.nim +@@ -369,7 +369,7 @@ + else: + 0.milliseconds + + let retryDelay = +- max(secs(rand(self.pendingBlocks.retryInterval.secs)), nextDiscovery) ++ max(secs(rand(int(self.pendingBlocks.retryInterval.secs)).int), nextDiscovery) + + # We now wait for a bit and then retry. If the handle gets completed in the + # meantime (cause the presence handler might have requested the block and + # got it from another peer) the handle will be removed from the pending + # set and the retry will be cancelled. diff --git a/android-patches/arm64/fixes/secp256k1_asm_disable.patch b/android-patches/arm64/fixes/secp256k1_asm_disable.patch new file mode 100644 index 0000000..269a762 --- /dev/null +++ b/android-patches/arm64/fixes/secp256k1_asm_disable.patch @@ -0,0 +1,21 @@ +--- a/vendor/nim-codex/vendor/nim-secp256k1/vendor/secp256k1/src/scalar_4x64_impl.h ++++ b/vendor/nim-codex/vendor/nim-secp256k1/vendor/secp256k1/src/scalar_4x64_impl.h +@@ -347,6 +347,7 @@ + + static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +-#ifdef USE_ASM_X86_64 ++#if defined(USE_ASM_X86_64) && !defined(__aarch64__) && !defined(__arm__) ++/* Disable x86 assembly on ARM64/ARM to prevent compilation errors */ + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; +@@ -677,6 +678,7 @@ + } + + static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, const secp256k1_scalar *b) { +-#ifdef USE_ASM_X86_64 ++#if defined(USE_ASM_X86_64) && !defined(__aarch64__) && !defined(__arm__) ++/* Disable x86 assembly on ARM64/ARM to prevent compilation errors */ + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ \ No newline at end of file diff --git a/android-patches/arm64/intrinsics/addcarry_subborrow_android_fix.patch b/android-patches/arm64/intrinsics/addcarry_subborrow_android_fix.patch new file mode 100644 index 0000000..6ce1143 --- /dev/null +++ b/android-patches/arm64/intrinsics/addcarry_subborrow_android_fix.patch @@ -0,0 +1,68 @@ +--- a/vendor/nim-codex/vendor/constantine/constantine/platforms/intrinsics/addcarry_subborrow.nim ++++ b/vendor/nim-codex/vendor/constantine/constantine/platforms/intrinsics/addcarry_subborrow.nim +@@ -89,7 +89,7 @@ + # Note: GCC before 2017 had incorrect codegen in some cases: + # - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81300 + +-when X86: ++when X86 and not defined(android) and not defined(arm64) and not defined(arm): + when defined(windows): + {.pragma: intrinsics, header:"", nodecl.} + else: +@@ -114,7 +114,7 @@ + template subborrow_u64(borrowIn: Borrow, a, b: Ct[uint64], sum: var Ct[uint64]): Borrow = + subborrow_u64(borrowIn, cast[culonglong](a), cast[culonglong](b), cast[ptr culonglong](sum.addr)[]) + +-elif defined(clang): ++elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + func builtin_addcl(x, y: Ct[uint32], carryIn: Ct[uint32], carryOut: var Ct[uint32]): Ct[uint32] {.importc: "__builtin_addcl", nodecl.} + func builtin_subcl(x, y: Ct[uint32], carryIn: Ct[uint32], carryOut: var Ct[uint32]): Ct[uint32] {.importc: "__builtin_subcl", nodecl.} + +@@ -135,9 +135,9 @@ + func addC*(cOut: var Carry, sum: var Ct[uint32], a, b: Ct[uint32], cIn: Carry) {.inline.} = + ## Addition with carry + ## (CarryOut, Sum) <- a + b + CarryIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + cOut = addcarry_u32(cIn, a, b, sum) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var carryOut: Ct[uint32] + sum = builtin_addcl(a, b, cast[Ct[uint32]](cIn), carryOut) + cOut = cast[Carry](carryOut) +@@ -149,9 +149,9 @@ + func subB*(bOut: var Borrow, diff: var Ct[uint32], a, b: Ct[uint32], bIn: Borrow) {.inline.} = + ## Substraction with borrow + ## (BorrowOut, Diff) <- a - b - borrowIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + bOut = subborrow_u32(bIn, a, b, diff) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var borrowOut: Ct[uint32] + diff = builtin_subcl(a, b, cast[Ct[uint32]](bIn), borrowOut) + bOut = cast[Borrow](borrowOut) +@@ -164,9 +164,9 @@ + func addC*(cOut: var Carry, sum: var Ct[uint64], a, b: Ct[uint64], cIn: Carry) {.inline.} = + ## Addition with carry + ## (CarryOut, Sum) <- a + b + CarryIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + cOut = addcarry_u64(cIn, a, b, sum) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var carryOut: Ct[uint64] + sum = builtin_addcll(a, b, cast[Ct[uint64]](cIn), carryOut) + cOut = cast[Carry](carryOut) +@@ -189,9 +189,9 @@ + func subB*(bOut: var Borrow, diff: var Ct[uint64], a, b: Ct[uint64], bIn: Borrow) {.inline.} = + ## Substraction with borrow + ## (BorrowOut, Diff) <- a - b - borrowIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + bOut = subborrow_u64(bIn, a, b, diff) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var borrowOut: Ct[uint64] + diff = builtin_subcll(a, b, cast[Ct[uint64]](bIn), borrowOut) + bOut = cast[Borrow](borrowOut) diff --git a/android-patches/arm64/intrinsics/bitops_android_fix.patch b/android-patches/arm64/intrinsics/bitops_android_fix.patch new file mode 100644 index 0000000..9df8ab8 --- /dev/null +++ b/android-patches/arm64/intrinsics/bitops_android_fix.patch @@ -0,0 +1,48 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/bitops.nim ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/bitops.nim +@@ -416,7 +416,8 @@ + + const useBuiltinsRotate = (defined(amd64) or defined(i386)) and + (defined(gcc) or defined(clang) or defined(vcc) or +- (defined(icl) and not defined(cpp))) and useBuiltins ++ (defined(icl) and not defined(cpp))) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) + + template parityImpl[T](value: T): int = + # formula id from: https://graphics.stanford.edu/%7Eseander/bithacks.html#ParityParallel +@@ -657,7 +658,7 @@ + result = firstSetBit(x) - 1 + + when useBuiltinsRotate: +- when defined(gcc): ++ when defined(gcc) and not defined(android) and not defined(arm64) and not defined(arm): + # GCC was tested until version 4.8.1 and intrinsics were present. Not tested + # in previous versions. + func builtin_rotl8(value: uint8, shift: cint): uint8 +@@ -679,7 +680,7 @@ + when defined(amd64): + func builtin_rotr64(value: culonglong, shift: cint): culonglong + {.importc: "__rorq", header: "".} +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + # In CLANG, builtins have been present since version 8.0.0 and intrinsics + # since version 9.0.0. This implementation chose the builtins, as they have + # been around for longer. +@@ -706,7 +707,7 @@ + # shift is unsigned, refs https://github.com/llvm-mirror/clang/commit/892de415b7fde609dafc4e6c1643b7eaa0150a4d + func builtin_rotr64(value: culonglong, shift: culonglong): culonglong + {.importc: "__builtin_rotateright64", nodecl.} +- elif defined(vcc): ++ elif defined(vcc) and not defined(android) and not defined(arm64) and not defined(arm): + # Tested on Microsoft (R) C/C++ Optimizing Compiler 19.28.29335 x64 and x86. + # Not tested in previous versions. + # https://docs.microsoft.com/en-us/cpp/intrinsics/rotl8-rotl16?view=msvc-160 +@@ -731,7 +732,7 @@ + when defined(amd64): + func builtin_rotr64(value: culonglong, shift: cint): culonglong + {.importc: "_rotr64", header: "".} +- elif defined(icl): ++ elif defined(icl) and not defined(android) and not defined(arm64) and not defined(arm): + # Tested on Intel(R) C++ Intel(R) 64 Compiler Classic Version 2021.1.2 Build + # 20201208_000000 x64 and x86. Not tested in previous versions. + func builtin_rotl8(value: uint8, shift: cint): uint8 diff --git a/android-patches/arm64/intrinsics/countbits_android_fix.patch b/android-patches/arm64/intrinsics/countbits_android_fix.patch new file mode 100644 index 0000000..ea36126 --- /dev/null +++ b/android-patches/arm64/intrinsics/countbits_android_fix.patch @@ -0,0 +1,18 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/system/countbits_impl.nim 2025-12-01 19:16:10.844007452 -0500 ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/system/countbits_impl.nim 2025-12-01 19:17:38.995742925 -0500 +@@ -14,9 +14,12 @@ + const useBuiltins* = not defined(noIntrinsicsBitOpts) + const noUndefined* = defined(noUndefinedBitOpts) + const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or +- defined(clang)) and useBuiltins +-const useICC_builtins* = defined(icc) and useBuiltins +-const useVCC_builtins* = defined(vcc) and useBuiltins ++ defined(clang)) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) ++const useICC_builtins* = defined(icc) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) ++const useVCC_builtins* = defined(vcc) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) + const arch64* = sizeof(int) == 8 + + template countBitsImpl(n: uint32): int = diff --git a/android-patches/arm64/terminal/terminal_android_fix.patch b/android-patches/arm64/terminal/terminal_android_fix.patch new file mode 100644 index 0000000..d8d9f19 --- /dev/null +++ b/android-patches/arm64/terminal/terminal_android_fix.patch @@ -0,0 +1,58 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/terminal.nim ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/terminal.nim +@@ -331,7 +331,8 @@ + return int(win.ws_row) + return 0 + +- var L_ctermid{.importc, header: "".}: cint ++ when not defined(android): ++ var L_ctermid{.importc, header: "".}: cint + + proc terminalWidth*(): int = + ## Returns some reasonable terminal width from either standard file +@@ -355,12 +356,16 @@ + return w + w = terminalWidthIoctl([0, 1, 2]) # Try standard file descriptors + if w > 0: return w +- var cterm = newString(L_ctermid) # Try controlling tty +- var fd = open(ctermid(cstring(cterm)), O_RDONLY) +- if fd != -1: +- w = terminalWidthIoctl([int(fd)]) +- discard close(fd) +- if w > 0: return w ++ when not defined(android): ++ var cterm = newString(L_ctermid) # Try controlling tty ++ var fd = open(ctermid(cstring(cterm)), O_RDONLY) ++ if fd != -1: ++ w = terminalWidthIoctl([int(fd)]) ++ discard close(fd) ++ if w > 0: return w ++ when defined(android): ++ # Android doesn't have ctermid, use default width ++ return 80 + return 80 # Finally default to venerable value + + proc terminalHeight*(): int = +@@ -389,12 +394,16 @@ + return h + h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors + if h > 0: return h +- var cterm = newString(L_ctermid) # Try controlling tty +- var fd = open(ctermid(cstring(cterm)), O_RDONLY) +- if fd != -1: +- h = terminalHeightIoctl([int(fd)]) +- discard close(fd) +- if h > 0: return h ++ when not defined(android): ++ var cterm = newString(L_ctermid) # Try controlling tty ++ var fd = open(ctermid(cstring(cterm)), O_RDONLY) ++ if fd != -1: ++ h = terminalHeightIoctl([int(fd)]) ++ discard close(fd) ++ if h > 0: return h ++ when defined(android): ++ # Android doesn't have ctermid, use default height ++ return 24 + return 0 # Could not determine height + + proc terminalSize*(): tuple[w, h: int] = diff --git a/android-patches/shared/barriers/barriers_android_pthread_fix.patch b/android-patches/shared/barriers/barriers_android_pthread_fix.patch new file mode 100644 index 0000000..215534e --- /dev/null +++ b/android-patches/shared/barriers/barriers_android_pthread_fix.patch @@ -0,0 +1,20 @@ +--- a/vendor/nim-codex/vendor/nim-taskpools/taskpools/primitives/barriers_posix.nim ++++ b/vendor/nim-codex/vendor/nim-taskpools/taskpools/primitives/barriers_posix.nim +@@ -13,7 +13,7 @@ when not compileOption("threads"): + # Types + # ------------------------------------------------------- + +-when defined(osx): ++when defined(osx) or defined(android): + import ./barriers_macos + export PthreadBarrierAttr, PthreadBarrier, Errno, PTHREAD_BARRIER_SERIAL_THREAD + else: +@@ -31,7 +31,7 @@ else: + + # Pthread + # ------------------------------------------------------- +-when defined(osx): ++when defined(osx) or defined(android): + export pthread_barrier_init, pthread_barrier_wait, pthread_barrier_destroy + else: + # TODO careful, this function mutates `barrier` without it being `var` which diff --git a/android-patches/shared/bearssl/add_android_mk.patch b/android-patches/shared/bearssl/add_android_mk.patch new file mode 100644 index 0000000..bbad106 --- /dev/null +++ b/android-patches/shared/bearssl/add_android_mk.patch @@ -0,0 +1,71 @@ +--- /dev/null 2025-12-05 20:00:00.000000000 -0500 ++++ b/vendor/nim-codex/vendor/nim-bearssl/bearssl/csources/conf/Android.mk 2025-12-05 20:10:00.000000000 -0500 +@@ -0,0 +1,58 @@ ++# Configuration for Android cross-compilation ++# Respects Android NDK environment variables set by build.rs ++ ++# Build directory. ++BUILD = build ++ ++# Extension for executable files. ++E = ++ ++# Extension for object files. ++O = .o ++ ++# Prefix for library file name. ++LP = lib ++ ++# Extension for library file name. ++L = .a ++ ++# Prefix for DLL file name. ++DP = lib ++ ++# Extension for DLL file name. ++D = .so ++ ++# File deletion tool. ++RM = rm -f ++ ++# Directory creation tool. ++MKDIR = mkdir -p ++ ++# Use Android NDK toolchain from environment ++CC ?= aarch64-linux-android21-clang ++CXX ?= aarch64-linux-android21-clang++ ++AR ?= llvm-ar ++ ++# Android-specific compiler flags ++CFLAGS = -W -Wall -Os -fPIC -DANDROID -D__ANDROID_API__=21 --target=aarch64-linux-android21 ++CCOUT = -c -o ++ ++# Static library building tool. ++ARFLAGS = -rcs ++AROUT = ++ ++# DLL building tool. ++LDDLL = $(CC) ++LDDLLFLAGS = -shared ++LDDLLOUT = -o ++ ++# Static linker. ++LD ?= aarch64-linux-android21-clang ++# Force Android NDK linker if available ++ifndef LD ++ LD = aarch64-linux-android21-clang ++endif ++LDFLAGS = ++LDOUT = -o ++ ++# C# compiler; we assume usage of Mono. ++MKT0COMP = mk$PmkT0.sh ++RUNT0COMP = mono T0Comp.exe ++ ++# Set the values to 'no' to disable building of the corresponding element ++# by default. Building can still be invoked with an explicit target call ++# (e.g. 'make dll' to force build the DLL). ++#STATICLIB = no ++#DLL = no ++#TOOLS = no ++#TESTS = no \ No newline at end of file diff --git a/android-patches/shared/bearssl/csources_android_fix.patch b/android-patches/shared/bearssl/csources_android_fix.patch new file mode 100644 index 0000000..4715615 --- /dev/null +++ b/android-patches/shared/bearssl/csources_android_fix.patch @@ -0,0 +1,23 @@ +--- a/vendor/nim-codex/vendor/nim-bearssl/bearssl/abi/csources.nim ++++ b/vendor/nim-codex/vendor/nim-bearssl/bearssl/abi/csources.nim +@@ -40,6 +40,20 @@ const + {.passc: "-I" & quoteShell(bearIncPath)} + {.passc: "-I" & quoteShell(bearToolsPath)} + ++# Android cross-compilation support ++when defined(android): ++ when defined(arm64): ++ {.passc: "-fPIC".} ++ {.passc: "-DANDROID".} ++ {.passc: "-D__ANDROID_API__=21".} ++ {.passc: "--target=aarch64-linux-android21".} ++ # Ensure we use the correct compiler and linker for Android ++ when defined(CC_aarch64_linux_android): ++ {.passc: "-cc=" & getEnv("CC_aarch64-linux-android").} ++ {.passl: "-ld=" & getEnv("CC_aarch64-linux-android").} ++ when defined(CXX_aarch64_linux_android): ++ {.passc: "-cxx=" & getEnv("CXX_aarch64-linux-android").} ++ {.passl: "-ld=" & getEnv("CXX_aarch64-linux-android").} + + when defined(windows): + {.passc: "-DBR_USE_WIN32_TIME=1".} diff --git a/android-patches/shared/bearssl/single_unix_mk_android_detection.patch b/android-patches/shared/bearssl/single_unix_mk_android_detection.patch new file mode 100644 index 0000000..879d52b --- /dev/null +++ b/android-patches/shared/bearssl/single_unix_mk_android_detection.patch @@ -0,0 +1,16 @@ +--- a/vendor/nim-codex/vendor/nim-bearssl/bearssl/csources/mk/SingleUnix.mk ++++ b/vendor/nim-codex/vendor/nim-bearssl/bearssl/csources/mk/SingleUnix.mk +@@ -34,5 +34,13 @@ P = / + # Default configuration is 'Unix' (native build on a Unix-like system). + CONF = Unix + ++# Detect Android build environment and use Android configuration ++ifdef ANDROID ++ CONF = Android ++endif ++ifdef ANDROID_ARM64_BUILD ++ CONF = Android ++endif ++ + include conf/$(CONF).mk + include mk/Rules.mk diff --git a/android-patches/shared/bearssl/unix_mk_cross_compilation.patch b/android-patches/shared/bearssl/unix_mk_cross_compilation.patch new file mode 100644 index 0000000..08c05b1 --- /dev/null +++ b/android-patches/shared/bearssl/unix_mk_cross_compilation.patch @@ -0,0 +1,30 @@ +--- a/vendor/nim-codex/vendor/nim-bearssl/bearssl/csources/conf/Unix.mk ++++ b/vendor/nim-codex/vendor/nim-bearssl/bearssl/csources/conf/Unix.mk.patched +@@ -38,11 +38,26 @@ MKDIR = mkdir -p + + # C compiler and flags. + CC = cc ++# Respect cross-compilation environment variables ++CC ?= cc ++CXX ?= c++ ++AR ?= ar ++ ++# Android cross-compilation support ++ifdef CC_aarch64-linux-android ++ CC = $(CC_aarch64-linux-android) ++endif ++ifdef CXX_aarch64-linux-android ++ CXX = $(CXX_aarch64-linux-android) ++endif ++ifdef AR_aarch64-linux-android ++ AR = $(AR_aarch64-linux-android) ++endif ++ + CFLAGS = -W -Wall -Os -fPIC + CCOUT = -c -o + + # Static library building tool. +-AR = ar + ARFLAGS = -rcs + AROUT = + diff --git a/android-patches/shared/build/build.nims.patch b/android-patches/shared/build/build.nims.patch new file mode 100644 index 0000000..6ad8f75 --- /dev/null +++ b/android-patches/shared/build/build.nims.patch @@ -0,0 +1,115 @@ +--- a/vendor/nim-codex/build.nims ++++ b/vendor/nim-codex/build.nims +@@ -29,25 +29,84 @@ proc buildLibrary(name: string, srcDir = "./", params = "", `type` = "dynamic") + if not dirExists "build": + mkDir "build" + ++ var extra_params = params ++ ++ # Android-specific compiler flags ++ when defined(android): ++ let android_cc = getEnv("CODEX_ANDROID_CC") ++ let android_ndk = getEnv("ANDROID_NDK_HOME") ++ let android_clang_version = getEnv("ANDROID_CLANG_VERSION") ++ let target_arch = getEnv("TARGET_ARCH", "arm64") ++ let android_linker = android_ndk & "/toolchains/llvm/prebuilt/linux-x86_64/bin/ld.lld" ++ let android_omp_lib = android_ndk & "/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/" & android_clang_version & "/lib/linux/" & target_arch ++ let android_sysroot = android_ndk & "/toolchains/llvm/prebuilt/linux-x86_64/sysroot" ++ let android_lib_path = android_sysroot & "/usr/lib/" & target_arch & "-linux-android" ++ let android_lib_path21 = android_lib_path & "/21" ++ let android_lib_path31 = android_lib_path & "/31" ++ let android_clang_lib = android_ndk & "/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/" & android_clang_version & "/lib/linux" ++ let android_builtins = android_clang_lib & "/libclang_rt.builtins-" & target_arch & "-android.a" ++ let android_crtbegin = android_lib_path21 & "/crtbegin_so.o" ++ let android_crtend = android_lib_path21 & "/crtend_so.o" ++ ++ # Convert x86_64 to amd64 for Nim compiler ++ let nim_cpu = if target_arch == "x86_64": "amd64" else: target_arch ++ extra_params &= " --cpu:" & nim_cpu & " --os:android --cc:clang --clang.execlang=" & android_cc ++ extra_params &= " --passl:-fuse-ld=" & android_linker ++ extra_params &= " --passl:-L" & android_omp_lib ++ extra_params &= " --passl:-L" & android_clang_lib ++ extra_params &= " --passl:-L" & android_lib_path ++ extra_params &= " --passl:-L" & android_lib_path21 ++ extra_params &= " --passl:-L" & android_lib_path31 ++ extra_params &= " --passl:-nostdlib" ++ extra_params &= " --passl:" & android_crtbegin ++ extra_params &= " --passl:" & android_crtend ++ extra_params &= " --passl:" & android_builtins ++ extra_params &= " --passl:-lc" ++ extra_params &= " --passl:-lm" ++ extra_params &= " --passl:-ldl" ++ extra_params &= " --passl:-Wl,--allow-multiple-definition" ++ extra_params &= " --passl:-Wl,--undefined-version" ++ extra_params &= " --passl:--target=" & target_arch & "-linux-android21" ++ + if `type` == "dynamic": + let lib_name = ( + when defined(windows): name & ".dll" + elif defined(macosx): name & ".dylib" + else: name & ".so" + ) ++ # Set Leopard CMake flags based on environment variables ++ var leopard_cmake_flags = "-DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release" ++ when defined(android): ++ if existsEnv("ANDROID_ARM64_BUILD"): ++ leopard_cmake_flags &= " -DANDROID_ARM64_BUILD=1" ++ if existsEnv("ANDROID_X86_64_BUILD"): ++ leopard_cmake_flags &= " -DANDROID_X86_64_BUILD=1" ++ if existsEnv("NO_X86_INTRINSICS"): ++ leopard_cmake_flags &= " -DNO_X86_INTRINSICS=1" ++ + exec "nim c" & " --out:build/" & lib_name & + " --threads:on --app:lib --opt:size --noMain --mm:refc --header --d:metrics " & + "--nimMainPrefix:libcodex -d:noSignalHandler " & + "-d:LeopardExtraCompilerFlags=-fPIC " & "-d:chronicles_runtime_filtering " & +- "-d:chronicles_log_level=TRACE " & params & " " & srcDir & name & ".nim" ++ "-d:chronicles_log_level=TRACE -d:LeopardCmakeFlags=\"" & leopard_cmake_flags & "\" " & extra_params & " " & srcDir & name & ".nim" + else: ++ # Set Leopard CMake flags based on environment variables ++ var leopard_cmake_flags = "-DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release" ++ when defined(android): ++ if existsEnv("ANDROID_ARM64_BUILD"): ++ leopard_cmake_flags &= " -DANDROID_ARM64_BUILD=1" ++ if existsEnv("ANDROID_X86_64_BUILD"): ++ leopard_cmake_flags &= " -DANDROID_X86_64_BUILD=1" ++ if existsEnv("NO_X86_INTRINSICS"): ++ leopard_cmake_flags &= " -DNO_X86_INTRINSICS=1" ++ + exec "nim c" & " --out:build/" & name & + ".a --threads:on --app:staticlib --opt:size --noMain --mm:refc --header --d:metrics " & + "--nimMainPrefix:libcodex -d:noSignalHandler " & + "-d:LeopardExtraCompilerFlags=-fPIC " & + "-d:chronicles_runtime_filtering " & +- "-d:chronicles_log_level=TRACE " & +- params & " " & srcDir & name & ".nim" ++ "-d:chronicles_log_level=TRACE -d:LeopardCmakeFlags=\"" & leopard_cmake_flags & "\" " & ++ extra_params & " " & srcDir & name & ".nim" + + proc test(name: string, srcDir = "tests/", params = "", lang = "c") = + buildBinary name, srcDir, params +@@ -153,6 +212,11 @@ task libcodexDynamic, "Generate bindings": + if param.len > 0 and param.startsWith("-"): + params.add " " & param + ++ # Android-specific parameters ++ when defined(android): ++ if existsEnv("CODEX_ANDROID_DEFINES"): ++ params.add " " & getEnv("CODEX_ANDROID_DEFINES") ++ + let name = "libcodex" + buildLibrary name, "library/", params, "dynamic" + +@@ -163,5 +227,10 @@ task libcodexStatic, "Generate bindings": + if param.len > 0 and param.startsWith("-"): + params.add " " & param + ++ # Android-specific parameters ++ when defined(android): ++ if existsEnv("CODEX_ANDROID_DEFINES"): ++ params.add " " & getEnv("CODEX_ANDROID_DEFINES") ++ + let name = "libcodex" +- buildLibrary name, "library/", params, "static" ++ buildLibrary name, "library/", params, "static" +\ No newline at end of file diff --git a/android-patches/shared/build/build_nim_android_fix.patch b/android-patches/shared/build/build_nim_android_fix.patch new file mode 100644 index 0000000..d5eb00a --- /dev/null +++ b/android-patches/shared/build/build_nim_android_fix.patch @@ -0,0 +1,57 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/scripts/build_nim.sh 2025-12-04 21:32:53.100386993 -0500 ++++ b/vendor/nim-codex/vendor/nimbus-build-system/scripts/build_nim.sh 2025-12-04 21:32:59.597357281 -0500 +@@ -55,29 +55,33 @@ + pushd "${NIM_DIR}" >/dev/null + if [[ -n "${NIM_COMMIT}" ]]; then + # support old Git versions, like the one from Ubuntu-18.04 +- git restore . 2>/dev/null || git reset --hard +- if ! git checkout -q "${NIM_COMMIT}" 2>/dev/null; then +- # Pay the price for a non-default NIM_COMMIT here, by fetching everything. +- # The "upstream" remote (pointing at the same location as the "origin") +- # is kept for backward compatibility. +- if ! git remote | grep -q "^upstream$"; then +- git remote add upstream https://github.com/nim-lang/Nim ++ if [[ -z "${CODEX_SKIP_GIT_RESET}" ]]; then ++ git restore . 2>/dev/null || git reset --hard ++ if ! git checkout -q "${NIM_COMMIT}" 2>/dev/null; then ++ # Pay the price for a non-default NIM_COMMIT here, by fetching everything. ++ # The "upstream" remote (pointing at the same location as the "origin") ++ # is kept for backward compatibility. ++ if ! git remote | grep -q "^upstream$"; then ++ git remote add upstream https://github.com/nim-lang/Nim ++ fi ++ # If the user has specified a custom repo, add it here as a remote as well. ++ if [[ -n "${NIM_COMMIT_REPO}" ]]; then ++ git remote remove extra 2>/dev/null || true ++ git remote add extra "${NIM_COMMIT_REPO}" ++ fi ++ git fetch --all --tags --quiet ++ git checkout -q "${NIM_COMMIT}" || ++ { echo "Error: wrong NIM_COMMIT specified:'${NIM_COMMIT}'"; exit 1; } + fi +- # If the user has specified a custom repo, add it here as a remote as well. +- if [[ -n "${NIM_COMMIT_REPO}" ]]; then +- git remote remove extra 2>/dev/null || true +- git remote add extra "${NIM_COMMIT_REPO}" +- fi +- git fetch --all --tags --quiet +- git checkout -q "${NIM_COMMIT}" || +- { echo "Error: wrong NIM_COMMIT specified:'${NIM_COMMIT}'"; exit 1; } ++ # In case the local branch diverged and a fast-forward merge is not possible. ++ git fetch || true ++ git reset -q --hard origin/"${NIM_COMMIT}" 2>/dev/null || true ++ # In case NIM_COMMIT is a local branch that's behind the remote one it's tracking. ++ git pull -q 2>/dev/null || true ++ git checkout -q "${NIM_COMMIT}" ++ else ++ echo "Skipping git reset/checkout operations to preserve patches" + fi +- # In case the local branch diverged and a fast-forward merge is not possible. +- git fetch || true +- git reset -q --hard origin/"${NIM_COMMIT}" 2>/dev/null || true +- # In case NIM_COMMIT is a local branch that's behind the remote one it's tracking. +- git pull -q 2>/dev/null || true +- git checkout -q "${NIM_COMMIT}" + # We can't use "rev-parse" here, because it would return the tag object's + # hash instead of the commit hash, when NIM_COMMIT is a tag. + NIM_COMMIT_HASH="$(git rev-list -n 1 "${NIM_COMMIT}")" diff --git a/android-patches/shared/build/targets_mk_android_fix.patch b/android-patches/shared/build/targets_mk_android_fix.patch new file mode 100644 index 0000000..9df29b4 --- /dev/null +++ b/android-patches/shared/build/targets_mk_android_fix.patch @@ -0,0 +1,53 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/makefiles/targets.mk ++++ b/vendor/nim-codex/vendor/nimbus-build-system/makefiles/targets.mk +@@ -78,7 +78,13 @@ endif + #- macOS is also a special case, with its "ln" not supporting "-r" + #- the AppVeyor 32-bit build is done on a 64-bit image, so we need to override the architecture detection with ARCH_OVERRIDE + build-nim: | sanity-checks +- + if [[ -z "$(NIM_COMMIT)" ]]; then git submodule update --init --recursive "$(BUILD_SYSTEM_DIR)"; fi; \ ++ + if [[ -z "$(NIM_COMMIT)" ]]; then \ ++ if [[ -z "$(CODEX_SKIP_SUBMODULE_UPDATE)" ]]; then \ ++ git submodule update --init --recursive "$(BUILD_SYSTEM_DIR)"; \ ++ else \ ++ echo "Skipping submodule update to preserve patches"; \ ++ fi; \ ++ fi; \ + NIM_BUILD_MSG="$(BUILD_MSG) Nim compiler" \ + V=$(V) \ + CC=$(CC) \ +@@ -104,12 +110,17 @@ update-test: + #- allows parallel building with the '+' prefix + #- rebuilds the Nim compiler if the corresponding submodule is updated + update-common: | sanity-checks update-test +- git submodule foreach --quiet 'git ls-files --exclude-standard --recurse-submodules -z -- ":!:.*" | xargs -0 rm -rf' +- git $(GIT_SUBMODULE_CONFIG) submodule update --init --recursive || true + # changing URLs in a submodule's submodule means we have to sync and update twice +- git submodule sync --quiet --recursive +- git $(GIT_SUBMODULE_CONFIG) submodule update --init --recursive +- git submodule foreach --quiet --recursive 'git $(GIT_SUBMODULE_CONFIG) reset --quiet --hard' ++ ifndef CODEX_SKIP_SUBMODULE_RESET ++ git submodule foreach --quiet 'git ls-files --exclude-standard --recurse-submodules -z -- ":!:.*" | xargs -0 rm -rf' ++ git $(GIT_SUBMODULE_CONFIG) submodule update --init --recursive || true ++ # changing URLs in a submodule's submodule means we have to sync and update twice ++ git submodule sync --quiet --recursive ++ git $(GIT_SUBMODULE_CONFIG) submodule update --init --recursive ++ git submodule foreach --quiet --recursive 'git $(GIT_SUBMODULE_CONFIG) reset --quiet --hard' ++ else ++ echo "Skipping submodule reset operations to preserve patches" ++ endif + find . -type d -name nimcache -print0 | xargs -0 rm -rf + $(GET_CURRENT_COMMIT_TIMESTAMP) > $(UPDATE_TIMESTAMP) + rm -rf $(NIMBLE_DIR) +@@ -134,7 +145,11 @@ ifeq ($(OS), Windows_NT) + + [ -e vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/$@ ] || \ + PATH=".;$${PATH}" "$(MAKE)" -C vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc -f Makefile.mingw CC=$(CC) CFLAGS="-Os -fPIC" $@ $(HANDLE_OUTPUT) + else +- + "$(MAKE)" -C vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc CC=$(CC) CFLAGS="-Os -fPIC" build/$@ $(HANDLE_OUTPUT) ++ @if [ "$(ANDROID_NDK_HOME)" != "" ] && [ "$(TARGET_ARCH)" = "arm64" ]; then \ ++ "$(MAKE)" -C vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc CC=$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang CFLAGS="-Os -fPIC -DANDROID" build/$@ $(HANDLE_OUTPUT); \ ++ else \ ++ "$(MAKE)" -C vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc CC=$(CC) CFLAGS="-Os -fPIC" build/$@ $(HANDLE_OUTPUT); \ ++ fi + endif + + libnatpmp.a: | sanity-checks diff --git a/android-patches/shared/circom-compat-ffi/circom_compat_android_cross_compile.patch b/android-patches/shared/circom-compat-ffi/circom_compat_android_cross_compile.patch new file mode 100644 index 0000000..02fa845 --- /dev/null +++ b/android-patches/shared/circom-compat-ffi/circom_compat_android_cross_compile.patch @@ -0,0 +1,84 @@ +--- a/vendor/nim-codex/vendor/nim-circom-compat/circomcompat.nim 2025-12-11 14:52:49.397769664 -0500 ++++ b/vendor/nim-codex/vendor/nim-circom-compat/circomcompat.nim 2025-12-11 14:52:42.961271859 -0500 +@@ -5,14 +5,77 @@ + + const + currentDir = currentSourcePath().parentDir() +- libDir* = currentDir/"vendor/circom-compat-ffi/target"/"release" +- # libDir* = currentDir/"vendor/circom-compat-ffi/target"/"debug" # XXX: uncomment for debug build +- libPath* = libDir/"libcircom_compat_ffi.a" ++ ++when defined(android): ++ when defined(arm64): ++ const libDir* = currentDir/"vendor/circom-compat-ffi"/"target"/"aarch64-linux-android"/"release" ++ # const libDir* = currentDir/"vendor/circom-compat-ffi"/"target"/"aarch64-linux-android"/"debug" # XXX: uncomment for debug build ++ elif defined(x86_64): ++ const libDir* = currentDir/"vendor/circom-compat-ffi"/"target"/"x86_64-linux-android"/"release" ++ # const libDir* = currentDir/"vendor/circom-compat-ffi"/"target"/"x86_64-linux-android"/"debug" # XXX: uncomment for debug build ++ else: ++ const libDir* = currentDir/"vendor/circom-compat-ffi/target"/"release" ++ # const libDir* = currentDir/"vendor/circom-compat-ffi/target"/"debug" # XXX: uncomment for debug build ++else: ++ const libDir* = currentDir/"vendor/circom-compat-ffi/target"/"release" ++ # const libDir* = currentDir/"vendor/circom-compat-ffi/target"/"debug" # XXX: uncomment for debug build ++ ++const libPath* = libDir/"libcircom_compat_ffi.a" + + static: +- let ++ var + cmd = "cargo build --release --manifest-path=vendor/circom-compat-ffi/Cargo.toml" + ++ when defined(android): ++ when defined(arm64): ++ # Android ARM64 cross-compilation ++ cmd = "cargo build --release --target aarch64-linux-android --manifest-path=vendor/circom-compat-ffi/Cargo.toml" ++ ++ # Set environment variables for Android cross-compilation ++ putEnv("CARGO_BUILD_TARGET", "aarch64-linux-android") ++ putEnv("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", getEnv("CC_aarch64_linux_android")) ++ ++ # Set CC environment variable for Rust's cc crate ++ let android_cc = getEnv("CC_aarch64_linux_android") ++ if android_cc.len > 0: ++ putEnv("CC", android_cc) ++ putEnv("TARGET_CC", android_cc) ++ ++ # Set AR environment variable ++ let android_ar = getEnv("AR_aarch64_linux_android") ++ if android_ar.len > 0: ++ putEnv("AR", android_ar) ++ putEnv("TARGET_AR", android_ar) ++ ++ # Ensure Rust uses the correct linker ++ let android_linker = getEnv("CC_aarch64_linux_android") ++ if android_linker.len > 0: ++ putEnv("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", android_linker) ++ when defined(x86_64): ++ # Android x86_64 cross-compilation ++ cmd = "cargo build --release --target x86_64-linux-android --manifest-path=vendor/circom-compat-ffi/Cargo.toml" ++ ++ # Set environment variables for Android cross-compilation ++ putEnv("CARGO_BUILD_TARGET", "x86_64-linux-android") ++ putEnv("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", getEnv("CC_x86_64_linux_android")) ++ ++ # Set CC environment variable for Rust's cc crate ++ let android_cc = getEnv("CC_x86_64_linux_android") ++ if android_cc.len > 0: ++ putEnv("CC", android_cc) ++ putEnv("TARGET_CC", android_cc) ++ ++ # Set AR environment variable ++ let android_ar = getEnv("AR_x86_64_linux_android") ++ if android_ar.len > 0: ++ putEnv("AR", android_ar) ++ putEnv("TARGET_AR", android_ar) ++ ++ # Ensure Rust uses the correct linker ++ let android_linker = getEnv("CC_x86_64_linux_android") ++ if android_linker.len > 0: ++ putEnv("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", android_linker) ++ + warning "\nBuilding circom compat ffi: " + warning cmd + let (output, exitCode) = gorgeEx cmd diff --git a/android-patches/shared/config/config_nims_android.patch b/android-patches/shared/config/config_nims_android.patch new file mode 100644 index 0000000..80a6817 --- /dev/null +++ b/android-patches/shared/config/config_nims_android.patch @@ -0,0 +1,69 @@ +--- a/vendor/nim-codex/config.nims ++++ b/vendor/nim-codex/config.nims +@@ -47,6 +47,14 @@ when defined(windows): + # because these require direct manipulations of the stdout File object. + switch("define", "chronicles_colors=NoColors") + ++# Android-specific compiler flags ++when defined(arm64): ++ switch("passC", "-march=armv8-a") ++ # Set environment variable for BearSSL Android build ++ putEnv("ANDROID_ARM64_BUILD", "1") ++ putEnv("ANDROID", "1") ++ ++ + # This helps especially for 32-bit x86, which sans SSE2 and newer instructions + # requires quite roundabout code generation for cryptography, and other 64-bit + # and larger arithmetic use cases, along with register starvation issues. When +@@ -65,8 +73,50 @@ else: + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65782 + # ("-fno-asynchronous-unwind-tables" breaks Nim's exception raising, sometimes) + switch("passC", "-march=x86-64") +- else: switch("passC", "-march=native") ++ else: ++ when not defined(android): ++ switch("passC", "-march=native") + ++# Android-specific configurations ++when defined(android): ++ # Disable x86 intrinsics for Android ARM builds ++ switch("define", "noIntrinsicsBitOpts") ++ switch("define", "NO_X86_INTRINSICS") ++ switch("define", "__NO_INLINE_ASM__") ++ switch("define", "noX86Intrinsics") ++ switch("define", "noSimd") ++ switch("define", "noInlineAsm") ++ ++ # Set Android cross-compiler ++ let android_cc = getEnv("CODEX_ANDROID_CC", "aarch64-linux-android21-clang") ++ let android_ar = getEnv("CODEX_ANDROID_AR", "llvm-ar") ++ ++ switch("cc", "clang") ++ switch("clang.exe", android_cc) ++ switch("clang.linker", android_cc) ++ switch("clang.ar", android_ar) ++ ++ # Android-specific compiler flags ++ when defined(arm64): ++ switch("passC", "-march=armv8-a") ++ # Set environment variable for BearSSL Android build ++ putEnv("ANDROID_ARM64_BUILD", "1") ++ elif defined(arm): ++ switch("passC", "-march=armv7-a") ++ elif defined(amd64): ++ switch("passC", "-march=x86-64") ++ elif defined(i386): ++ switch("passC", "-march=i686") ++ ++ # Set Android environment variable for BearSSL ++ putEnv("ANDROID", "1") ++ ++ # Disable libbacktrace on Android ++ switch("define", "disable_libbacktrace") ++ ++ # Android-specific defines ++ switch("define", "android") ++ switch("define", "debug") + + --tlsEmulation: + off diff --git a/android-patches/shared/leopard/leopard_aligned_alloc_android_fix.patch b/android-patches/shared/leopard/leopard_aligned_alloc_android_fix.patch new file mode 100644 index 0000000..15d2764 --- /dev/null +++ b/android-patches/shared/leopard/leopard_aligned_alloc_android_fix.patch @@ -0,0 +1,16 @@ +--- a/vendor/nim-codex/vendor/nim-leopard/leopard/utils/allocs.nim ++++ b/vendor/nim-codex/vendor/nim-leopard/leopard/utils/allocs.nim +@@ -34,7 +34,7 @@ + + proc alignedFree*[T](p: ptr T) + {.importc: "_aligned_free", header: "".} +-elif defined(osx): ++elif defined(osx) or defined(android): + proc posix_memalign(mem: var pointer, alignment, size: csize_t) + {.importc, header:"".} + + proc alignedAlloc(alignment, size: csize_t): pointer {.inline.} = + posix_memalign(result, alignment, size) + + proc alignedFree*[T](p: ptr T) {.inline.} = + c_free(p) diff --git a/android-patches/shared/leopard/leopard_cmake_android_fix.patch b/android-patches/shared/leopard/leopard_cmake_android_fix.patch new file mode 100644 index 0000000..5bbfe48 --- /dev/null +++ b/android-patches/shared/leopard/leopard_cmake_android_fix.patch @@ -0,0 +1,32 @@ +--- a/vendor/nim-codex/vendor/nim-leopard/vendor/leopard/CMakeLists.txt ++++ b/vendor/nim-codex/vendor/nim-leopard/vendor/leopard/CMakeLists.txt +@@ -1,4 +1,14 @@ + cmake_minimum_required(VERSION 3.7) ++ ++# Skip -march=native on Android to avoid x86-specific flags on ARM ++if(ANDROID OR ANDROID_ARM64_BUILD) ++ message(STATUS "Android build detected: skipping -march=native to avoid architecture-specific flags") ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") ++else() ++ # Set default flags for non-Android ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") ++endif() ++ + project(leopard) + + include(CMakeDependentOption) +@@ -28,9 +38,12 @@ + set(CMAKE_BUILD_TYPE Release) + endif() + +-check_cxx_compiler_flag("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE) +-if(COMPILER_SUPPORTS_MARCH_NATIVE) ++# Only check for -march=native on non-Android platforms ++if(NOT ANDROID AND NOT ANDROID_ARM64_BUILD) ++ check_cxx_compiler_flag("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE) ++ if(COMPILER_SUPPORTS_MARCH_NATIVE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") ++ endif() + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") diff --git a/android-patches/shared/leveldb/leveldb_android_build_fix.patch b/android-patches/shared/leveldb/leveldb_android_build_fix.patch new file mode 100644 index 0000000..6a95fc0 --- /dev/null +++ b/android-patches/shared/leveldb/leveldb_android_build_fix.patch @@ -0,0 +1,46 @@ +--- a/vendor/nim-codex/vendor/nim-leveldbstatic/leveldbstatic/prelude.nim ++++ b/vendor/nim-codex/vendor/nim-leveldbstatic/leveldbstatic/prelude.nim +@@ -6,7 +6,9 @@ + envPosix = root/"vendor"/"util"/"env_posix.cc" + + LevelDbCMakeFlags {.strdefine.} = +- when defined(macosx): ++ when defined(android): ++ "-DCMAKE_BUILD_TYPE=Release -DLEVELDB_BUILD_BENCHMARKS=OFF -DLEVELDB_PLATFORM_POSIX=1 -DHAVE_PTHREAD=1 -DCMAKE_C_FLAGS=-march=armv8-a -DNO_X86_INTRINSICS -DCMAKE_CXX_FLAGS=-march=armv8-a -DNO_X86_INTRINSICS" ++ elif defined(macosx): + "-DCMAKE_BUILD_TYPE=Release -DLEVELDB_BUILD_BENCHMARKS=OFF" + elif defined(windows): + "-G\"MSYS Makefiles\" -DCMAKE_BUILD_TYPE=Release -DLEVELDB_BUILD_BENCHMARKS=OFF" +@@ -19,9 +21,13 @@ + buildDir = $(root/"build") + + proc buildLevelDb() = +- if fileExists(buildDir/"Makefile"): +- echo "LevelDB already build. Delete '" & buildDir & "' to force rebuild." +- return ++ when defined(android): ++ # Always force rebuild on Android to ensure proper cross-compilation ++ echo "Android build detected - forcing LevelDB rebuild" ++ else: ++ if fileExists(buildDir/"Makefile"): ++ echo "LevelDB already build. Delete '" & buildDir & "' to force rebuild." ++ return + + echo "Initializing submodule..." + discard gorge "git submodule deinit -f \"" & root & "\"" +@@ -48,6 +54,14 @@ + {.passc: "-D_UNICODE".} + {.passc: "-DUNICODE".} + +-when defined(posix): ++when defined(android): ++ {.compile: envPosix.} ++ {.passc: "-DLEVELDB_PLATFORM_POSIX".} ++ {.passc: "-DHAVE_PTHREAD".} ++ {.passc: "-DOS_ANDROID".} ++ # Android-specific flags to prevent linking issues ++ {.passc: "-DNO_SNAPPY".} ++ {.passc: "-DLEVELDB_NO_SNAPPY".} ++elif defined(posix): + {.compile: envPosix.} + {.passc: "-DLEVELDB_PLATFORM_POSIX".} diff --git a/android-patches/shared/nimcrypto/nimcrypto_explicit_bzero_fix.patch b/android-patches/shared/nimcrypto/nimcrypto_explicit_bzero_fix.patch new file mode 100644 index 0000000..f71d129 --- /dev/null +++ b/android-patches/shared/nimcrypto/nimcrypto_explicit_bzero_fix.patch @@ -0,0 +1,29 @@ +--- a/vendor/nim-codex/vendor/nimcrypto/nimcrypto/utils.nim ++++ b/vendor/nim-codex/vendor/nimcrypto/nimcrypto/utils.nim +@@ -195,7 +195,7 @@ + if i in allowed: + result &= i + +-when defined(linux): ++when defined(linux) and not defined(android): + proc c_explicit_bzero( + s: pointer, n: csize_t + ) {.importc: "explicit_bzero", header: "string.h".} +@@ -203,6 +203,17 @@ + proc burnMem*(p: pointer, size: Natural) = + c_explicit_bzero(p, csize_t size) + ++elif defined(android): ++ proc burnMem*(p: pointer, size: Natural) = ++ var sp {.volatile.} = cast[ptr byte](p) ++ var c = size ++ if not isNil(sp): ++ zeroMem(p, size) ++ while c > 0: ++ sp[] = 0 ++ sp = cast[ptr byte](cast[uint](sp) + 1) ++ dec(c) ++ + elif defined(windows): + proc cSecureZeroMemory( + s: pointer, n: csize_t diff --git a/android-patches/shared/posix/android_fix_h.patch b/android-patches/shared/posix/android_fix_h.patch new file mode 100644 index 0000000..9ed0d84 --- /dev/null +++ b/android-patches/shared/posix/android_fix_h.patch @@ -0,0 +1,56 @@ +--- a/vendor/nim-codex/android_fix.h ++++ b/vendor/nim-codex/android_fix.h +@@ -0,0 +1,52 @@ ++#ifndef ANDROID_FIX_H ++#define ANDROID_FIX_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// Android POSIX compatibility fixes ++#ifdef __ANDROID__ ++ ++// Android doesn't have all POSIX functions ++static inline char* android_getlogin(void) { ++ return getlogin() ? getlogin() : "android"; ++} ++ ++static inline int android_gethostname(char *name, size_t len) { ++ return gethostname(name, len); ++} ++ ++// Android-specific path handling ++static inline int android_mkdir(const char *pathname, mode_t mode) { ++ return mkdir(pathname, mode); ++} ++ ++// Android doesn't support all file locking mechanisms ++static inline int android_flock(int fd, int operation) { ++ return 0; // No-op on Android ++} ++ ++// Android-specific environment handling ++static inline char* android_getenv(const char *name) { ++ return getenv(name); ++} ++ ++// Android signal handling compatibility ++#define SIGUNUSED SIGSYS ++ ++// Android doesn't have all sysctl functionality ++static inline int android_sysctl(const int *name, unsigned int namelen, ++ void *oldp, size_t *oldlenp, ++ void *oldp, size_t *oldlenp, ++ const void *newp, size_t newlen) { ++ errno = ENOSYS; ++ return -1; ++} ++ ++#endif // __ANDROID__ ++ ++#endif // ANDROID_FIX_H diff --git a/android-patches/shared/posix/android_stubs_c.patch b/android-patches/shared/posix/android_stubs_c.patch new file mode 100644 index 0000000..adca32a --- /dev/null +++ b/android-patches/shared/posix/android_stubs_c.patch @@ -0,0 +1,79 @@ +--- a/vendor/nim-codex/android_stubs.c ++++ b/vendor/nim-codex/android_stubs.c +@@ -0,0 +1,76 @@ ++/* ++ * Android POSIX compatibility stubs ++ * Provides implementations for missing POSIX functions on Android ++ */ ++ ++#include "android_fix.h" ++#include ++#include ++ ++#ifdef __ANDROID__ ++ ++// Stub implementations for missing POSIX functions ++ ++char *getlogin(void) { ++ return android_getlogin(); ++} ++ ++int gethostname(char *name, size_t len) { ++ return android_gethostname(name, len); ++} ++ ++int flock(int fd, int operation) { ++ return android_flock(fd, operation); ++} ++ ++// Android doesn't have setrlimit, provide a stub ++int setrlimit(int resource, const struct rlimit *rlp) { ++ errno = ENOSYS; ++ return -1; ++} ++ ++// Android doesn't have getrlimit, provide a stub ++int getrlimit(int resource, struct rlimit *rlp) { ++ errno = ENOSYS; ++ return -1; ++} ++ ++// Android doesn't have getloadavg, provide a stub ++int getloadavg(double loadavg[], int nelem) { ++ if (nelem > 0) loadavg[0] = 0.0; ++ if (nelem > 1) loadavg[1] = 0.0; ++ if (nelem > 2) loadavg[2] = 0.0; ++ return 0; ++} ++ ++// Android doesn't have getpagesize, provide a stub ++int getpagesize(void) { ++ return 4096; // Standard page size ++} ++ ++// Android doesn't have getdtablesize, provide a stub ++int getdtablesize(void) { ++ return 1024; // Reasonable default ++} ++ ++// Android doesn't have ctermid, provide a stub ++char *ctermid(char *s) { ++ static char termname[] = "/dev/tty"; ++ if (s == NULL) { ++ return termname; ++ } else { ++ strcpy(s, termname); ++ return s; ++ } ++} ++ ++// Android doesn't have ttyname, provide a stub ++char *ttyname(int fd) { ++ static char devtty[] = "/dev/tty"; ++ if (isatty(fd)) { ++ return devtty; ++ } ++ return NULL; ++} ++ ++#endif // __ANDROID__ diff --git a/android-patches/shared/taskpools/taskpools_aligned_alloc_android_fix.patch b/android-patches/shared/taskpools/taskpools_aligned_alloc_android_fix.patch new file mode 100644 index 0000000..6f11203 --- /dev/null +++ b/android-patches/shared/taskpools/taskpools_aligned_alloc_android_fix.patch @@ -0,0 +1,11 @@ +--- a/vendor/nim-codex/vendor/nim-taskpools/taskpools/primitives/allocs.nim ++++ b/vendor/nim-codex/vendor/nim-taskpools/taskpools/primitives/allocs.nim +@@ -105,7 +105,7 @@ + proc aligned_alloc_windows(size, alignment: csize_t): pointer {.sideEffect,importc:"_aligned_malloc", header:"".} + # Beware of the arg order! + proc tp_freeAligned*[T](p: ptr T){.sideEffect,importc:"_aligned_free", header:"".} +-elif defined(osx): ++elif defined(osx) or defined(android): + proc posix_memalign(mem: var pointer, alignment, size: csize_t){.sideEffect,importc, header:"".} + proc aligned_alloc(alignment, size: csize_t): pointer {.inline.} = + posix_memalign(result, alignment, size) diff --git a/android-patches/x86_64/fixes/rand_type_fix.patch b/android-patches/x86_64/fixes/rand_type_fix.patch new file mode 100644 index 0000000..769ca52 --- /dev/null +++ b/android-patches/x86_64/fixes/rand_type_fix.patch @@ -0,0 +1,12 @@ +--- a/vendor/nim-codex/codex/blockexchange/engine/engine.nim ++++ b/vendor/nim-codex/codex/blockexchange/engine/engine.nim +@@ -369,7 +369,7 @@ + else: + 0.milliseconds + + let retryDelay = +- max(secs(rand(self.pendingBlocks.retryInterval.secs)), nextDiscovery) ++ max(secs(rand(int(self.pendingBlocks.retryInterval.secs)).int), nextDiscovery) + + # We now wait for a bit and then retry. If the handle gets completed in the + # meantime (cause the presence handler might have requested the block and diff --git a/android-patches/x86_64/fixes/secp256k1_asm_disable.patch b/android-patches/x86_64/fixes/secp256k1_asm_disable.patch new file mode 100644 index 0000000..892c701 --- /dev/null +++ b/android-patches/x86_64/fixes/secp256k1_asm_disable.patch @@ -0,0 +1,20 @@ +--- a/vendor/nim-codex/vendor/nim-secp256k1/vendor/secp256k1/src/scalar_4x64_impl.h ++++ b/vendor/nim-codex/vendor/nim-secp256k1/vendor/secp256k1/src/scalar_4x64_impl.h +@@ -347,6 +347,7 @@ + + static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +-#ifdef USE_ASM_X86_64 ++#if defined(USE_ASM_X86_64) && !defined(__aarch64__) && !defined(__arm__) && !defined(__x86_64__) ++/* Disable x86 assembly on ARM64/ARM/x86_64 Android to prevent compilation errors */ + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; +@@ -677,6 +678,7 @@ + + static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, const secp256k1_scalar *b) { +-#ifdef USE_ASM_X86_64 ++#if defined(USE_ASM_X86_64) && !defined(__aarch64__) && !defined(__arm__) && !defined(__x86_64__) ++/* Disable x86 assembly on ARM64/ARM/x86_64 Android to prevent compilation errors */ + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ diff --git a/android-patches/x86_64/intrinsics/addcarry_subborrow_android_fix.patch b/android-patches/x86_64/intrinsics/addcarry_subborrow_android_fix.patch new file mode 100644 index 0000000..6ce1143 --- /dev/null +++ b/android-patches/x86_64/intrinsics/addcarry_subborrow_android_fix.patch @@ -0,0 +1,68 @@ +--- a/vendor/nim-codex/vendor/constantine/constantine/platforms/intrinsics/addcarry_subborrow.nim ++++ b/vendor/nim-codex/vendor/constantine/constantine/platforms/intrinsics/addcarry_subborrow.nim +@@ -89,7 +89,7 @@ + # Note: GCC before 2017 had incorrect codegen in some cases: + # - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81300 + +-when X86: ++when X86 and not defined(android) and not defined(arm64) and not defined(arm): + when defined(windows): + {.pragma: intrinsics, header:"", nodecl.} + else: +@@ -114,7 +114,7 @@ + template subborrow_u64(borrowIn: Borrow, a, b: Ct[uint64], sum: var Ct[uint64]): Borrow = + subborrow_u64(borrowIn, cast[culonglong](a), cast[culonglong](b), cast[ptr culonglong](sum.addr)[]) + +-elif defined(clang): ++elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + func builtin_addcl(x, y: Ct[uint32], carryIn: Ct[uint32], carryOut: var Ct[uint32]): Ct[uint32] {.importc: "__builtin_addcl", nodecl.} + func builtin_subcl(x, y: Ct[uint32], carryIn: Ct[uint32], carryOut: var Ct[uint32]): Ct[uint32] {.importc: "__builtin_subcl", nodecl.} + +@@ -135,9 +135,9 @@ + func addC*(cOut: var Carry, sum: var Ct[uint32], a, b: Ct[uint32], cIn: Carry) {.inline.} = + ## Addition with carry + ## (CarryOut, Sum) <- a + b + CarryIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + cOut = addcarry_u32(cIn, a, b, sum) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var carryOut: Ct[uint32] + sum = builtin_addcl(a, b, cast[Ct[uint32]](cIn), carryOut) + cOut = cast[Carry](carryOut) +@@ -149,9 +149,9 @@ + func subB*(bOut: var Borrow, diff: var Ct[uint32], a, b: Ct[uint32], bIn: Borrow) {.inline.} = + ## Substraction with borrow + ## (BorrowOut, Diff) <- a - b - borrowIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + bOut = subborrow_u32(bIn, a, b, diff) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var borrowOut: Ct[uint32] + diff = builtin_subcl(a, b, cast[Ct[uint32]](bIn), borrowOut) + bOut = cast[Borrow](borrowOut) +@@ -164,9 +164,9 @@ + func addC*(cOut: var Carry, sum: var Ct[uint64], a, b: Ct[uint64], cIn: Carry) {.inline.} = + ## Addition with carry + ## (CarryOut, Sum) <- a + b + CarryIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + cOut = addcarry_u64(cIn, a, b, sum) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var carryOut: Ct[uint64] + sum = builtin_addcll(a, b, cast[Ct[uint64]](cIn), carryOut) + cOut = cast[Carry](carryOut) +@@ -189,9 +189,9 @@ + func subB*(bOut: var Borrow, diff: var Ct[uint64], a, b: Ct[uint64], bIn: Borrow) {.inline.} = + ## Substraction with borrow + ## (BorrowOut, Diff) <- a - b - borrowIn +- when X86: ++ when X86 and not defined(android) and not defined(arm64) and not defined(arm): + bOut = subborrow_u64(bIn, a, b, diff) +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + var borrowOut: Ct[uint64] + diff = builtin_subcll(a, b, cast[Ct[uint64]](bIn), borrowOut) + bOut = cast[Borrow](borrowOut) diff --git a/android-patches/x86_64/intrinsics/bitops_android_fix.patch b/android-patches/x86_64/intrinsics/bitops_android_fix.patch new file mode 100644 index 0000000..9df8ab8 --- /dev/null +++ b/android-patches/x86_64/intrinsics/bitops_android_fix.patch @@ -0,0 +1,48 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/bitops.nim ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/bitops.nim +@@ -416,7 +416,8 @@ + + const useBuiltinsRotate = (defined(amd64) or defined(i386)) and + (defined(gcc) or defined(clang) or defined(vcc) or +- (defined(icl) and not defined(cpp))) and useBuiltins ++ (defined(icl) and not defined(cpp))) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) + + template parityImpl[T](value: T): int = + # formula id from: https://graphics.stanford.edu/%7Eseander/bithacks.html#ParityParallel +@@ -657,7 +658,7 @@ + result = firstSetBit(x) - 1 + + when useBuiltinsRotate: +- when defined(gcc): ++ when defined(gcc) and not defined(android) and not defined(arm64) and not defined(arm): + # GCC was tested until version 4.8.1 and intrinsics were present. Not tested + # in previous versions. + func builtin_rotl8(value: uint8, shift: cint): uint8 +@@ -679,7 +680,7 @@ + when defined(amd64): + func builtin_rotr64(value: culonglong, shift: cint): culonglong + {.importc: "__rorq", header: "".} +- elif defined(clang): ++ elif defined(clang) and not defined(android) and not defined(arm64) and not defined(arm): + # In CLANG, builtins have been present since version 8.0.0 and intrinsics + # since version 9.0.0. This implementation chose the builtins, as they have + # been around for longer. +@@ -706,7 +707,7 @@ + # shift is unsigned, refs https://github.com/llvm-mirror/clang/commit/892de415b7fde609dafc4e6c1643b7eaa0150a4d + func builtin_rotr64(value: culonglong, shift: culonglong): culonglong + {.importc: "__builtin_rotateright64", nodecl.} +- elif defined(vcc): ++ elif defined(vcc) and not defined(android) and not defined(arm64) and not defined(arm): + # Tested on Microsoft (R) C/C++ Optimizing Compiler 19.28.29335 x64 and x86. + # Not tested in previous versions. + # https://docs.microsoft.com/en-us/cpp/intrinsics/rotl8-rotl16?view=msvc-160 +@@ -731,7 +732,7 @@ + when defined(amd64): + func builtin_rotr64(value: culonglong, shift: cint): culonglong + {.importc: "_rotr64", header: "".} +- elif defined(icl): ++ elif defined(icl) and not defined(android) and not defined(arm64) and not defined(arm): + # Tested on Intel(R) C++ Intel(R) 64 Compiler Classic Version 2021.1.2 Build + # 20201208_000000 x64 and x86. Not tested in previous versions. + func builtin_rotl8(value: uint8, shift: cint): uint8 diff --git a/android-patches/x86_64/intrinsics/countbits_android_fix.patch b/android-patches/x86_64/intrinsics/countbits_android_fix.patch new file mode 100644 index 0000000..1b4e9dc --- /dev/null +++ b/android-patches/x86_64/intrinsics/countbits_android_fix.patch @@ -0,0 +1,18 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/system/countbits_impl.nim ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/system/countbits_impl.nim +@@ -14,9 +14,12 @@ + const useBuiltins* = not defined(noIntrinsicsBitOpts) + const noUndefined* = defined(noUndefinedBitOpts) + const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or +- defined(clang)) and useBuiltins +-const useICC_builtins* = defined(icc) and useBuiltins +-const useVCC_builtins* = defined(vcc) and useBuiltins ++ defined(clang)) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) ++const useICC_builtins* = defined(icc) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) ++const useVCC_builtins* = defined(vcc) and useBuiltins and ++ not defined(android) and not defined(arm64) and not defined(arm) + const arch64* = sizeof(int) == 8 + + template countBitsImpl(n: uint32): int = diff --git a/android-patches/x86_64/terminal/terminal_android_fix.patch b/android-patches/x86_64/terminal/terminal_android_fix.patch new file mode 100644 index 0000000..d8d9f19 --- /dev/null +++ b/android-patches/x86_64/terminal/terminal_android_fix.patch @@ -0,0 +1,58 @@ +--- a/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/terminal.nim ++++ b/vendor/nim-codex/vendor/nimbus-build-system/vendor/Nim/lib/pure/terminal.nim +@@ -331,7 +331,8 @@ + return int(win.ws_row) + return 0 + +- var L_ctermid{.importc, header: "".}: cint ++ when not defined(android): ++ var L_ctermid{.importc, header: "".}: cint + + proc terminalWidth*(): int = + ## Returns some reasonable terminal width from either standard file +@@ -355,12 +356,16 @@ + return w + w = terminalWidthIoctl([0, 1, 2]) # Try standard file descriptors + if w > 0: return w +- var cterm = newString(L_ctermid) # Try controlling tty +- var fd = open(ctermid(cstring(cterm)), O_RDONLY) +- if fd != -1: +- w = terminalWidthIoctl([int(fd)]) +- discard close(fd) +- if w > 0: return w ++ when not defined(android): ++ var cterm = newString(L_ctermid) # Try controlling tty ++ var fd = open(ctermid(cstring(cterm)), O_RDONLY) ++ if fd != -1: ++ w = terminalWidthIoctl([int(fd)]) ++ discard close(fd) ++ if w > 0: return w ++ when defined(android): ++ # Android doesn't have ctermid, use default width ++ return 80 + return 80 # Finally default to venerable value + + proc terminalHeight*(): int = +@@ -389,12 +394,16 @@ + return h + h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors + if h > 0: return h +- var cterm = newString(L_ctermid) # Try controlling tty +- var fd = open(ctermid(cstring(cterm)), O_RDONLY) +- if fd != -1: +- h = terminalHeightIoctl([int(fd)]) +- discard close(fd) +- if h > 0: return h ++ when not defined(android): ++ var cterm = newString(L_ctermid) # Try controlling tty ++ var fd = open(ctermid(cstring(cterm)), O_RDONLY) ++ if fd != -1: ++ h = terminalHeightIoctl([int(fd)]) ++ discard close(fd) ++ if h > 0: return h ++ when defined(android): ++ # Android doesn't have ctermid, use default height ++ return 24 + return 0 # Could not determine height + + proc terminalSize*(): tuple[w, h: int] = diff --git a/build.rs b/build.rs index f9bfd96..9eaebfb 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,309 @@ use std::env; use std::path::PathBuf; use std::process::Command; +#[path = "src_build/patch_system.rs"] +mod patch_system; + +#[path = "src_build/build_android.rs"] +mod build_android; + +#[path = "src_build/parallelism.rs"] +mod parallelism; + +use build_android::*; +use parallelism::get_parallel_jobs; + +/// Gets the current target architecture string for comparison +fn get_current_architecture() -> String { + let target = env::var("TARGET").unwrap_or_default(); + if target.contains("android") { + format!("android-{}", target) + } else { + format!("desktop-{}", target) + } +} + +/// List of static library artifacts to check for architecture compatibility +static STATIC_ARTIFACTS: &[&str] = &[ + "build/libcodex.a", + "vendor/nim-nat-traversal/vendor/libnatpmp-upstream/libnatpmp.a", + "vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build/libminiupnpc.a", + "nimcache/release/libcodex/vendor_leopard/liblibleopard.a", + "vendor/nim-leveldbstatic/build/libleveldb.a", + "vendor/nim-leveldbstatic/libleveldb.a", +]; + +/// List of dynamic library artifacts to check for architecture compatibility +static DYNAMIC_ARTIFACTS: &[&str] = &[ + "build/libcodex.so", + "vendor/nim-leveldbstatic/libleveldb.so", +]; + +/// Checks if a file is compatible with the current target architecture +fn is_artifact_compatible(artifact_path: &PathBuf, current_arch: &str) -> bool { + if !artifact_path.exists() { + println!( + "cargo:warning=Artifact {} does not exist, assuming compatible", + artifact_path.display() + ); + return true; // Non-existent artifacts are trivially compatible + } + + println!( + "cargo:warning=Checking compatibility for {} with {}", + artifact_path.display(), + current_arch + ); + + // For static libraries (.a), we need to extract and check object files + if artifact_path.extension().map_or(false, |ext| ext == "a") { + let compatible = check_static_library_compatibility(artifact_path, current_arch); + println!( + "cargo:warning=Static library {} compatibility: {}", + artifact_path.display(), + compatible + ); + return compatible; + } + // For shared libraries (.so), we can check directly + else if artifact_path.extension().map_or(false, |ext| ext == "so") { + let compatible = check_shared_library_compatibility(artifact_path, current_arch); + println!( + "cargo:warning=Shared library {} compatibility: {}", + artifact_path.display(), + compatible + ); + return compatible; + } + + println!( + "cargo:warning=Unknown file type for {}, assuming compatible", + artifact_path.display() + ); + true // Unknown file types are assumed compatible +} + +/// Checks if a static library (.a) is compatible with the current architecture +fn check_static_library_compatibility(lib_path: &PathBuf, current_arch: &str) -> bool { + let temp_dir = lib_path.parent().unwrap_or(lib_path).join("temp_check"); + + // Create temporary directory for extraction + if let Err(_) = std::fs::create_dir_all(&temp_dir) { + println!( + "cargo:warning=Failed to create temp dir for {}, assuming compatible", + lib_path.display() + ); + return true; // If we can't create temp dir, assume compatible to avoid false positives + } + + // Extract the archive using absolute path + let extraction_result = Command::new("ar") + .arg("x") + .arg(&lib_path.canonicalize().unwrap_or_else(|_| lib_path.clone())) + .current_dir(&temp_dir) + .output(); + + let mut compatible = true; + + if let Ok(output) = extraction_result { + if output.status.success() { + // Check the first .o file we can find + if let Ok(entries) = std::fs::read_dir(&temp_dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_file() && path.extension().map_or(false, |ext| ext == "o") { + if let Ok(file_output) = Command::new("file").arg(&path).output() { + let file_info = String::from_utf8_lossy(&file_output.stdout); + println!( + "cargo:warning=Object file info: {} -> {}", + path.display(), + file_info.trim() + ); + + let object_compatible = + is_object_file_compatible(&file_info, current_arch); + println!( + "cargo:warning=Object file compatibility: {} for {}", + object_compatible, current_arch + ); + + if !object_compatible { + compatible = false; + break; + } + } + break; // Only need to check one object file + } + } + } + } else { + println!( + "cargo:warning=Failed to extract archive {}: {:?}", + lib_path.display(), + output + ); + println!( + "cargo:warning=stderr: {}", + String::from_utf8_lossy(&output.stderr) + ); + } + } else { + println!( + "cargo:warning=Failed to run ar command on {}", + lib_path.display() + ); + } + + // Clean up temp directory + let _ = std::fs::remove_dir_all(&temp_dir); + + println!( + "cargo:warning=Final static library compatibility for {}: {}", + lib_path.display(), + compatible + ); + compatible +} + +/// Checks if a shared library (.so) is compatible with the current architecture +fn check_shared_library_compatibility(lib_path: &PathBuf, current_arch: &str) -> bool { + if let Ok(output) = Command::new("file").arg(lib_path).output() { + let file_info = String::from_utf8_lossy(&output.stdout); + return is_object_file_compatible(&file_info, current_arch); + } + true // If we can't check, assume compatible +} + +fn is_object_file_compatible(file_info: &str, current_arch: &str) -> bool { + if current_arch.contains("aarch64") { + return file_info.contains("aarch64"); + } else if current_arch.contains("x86_64") { + return file_info.contains("x86-64"); + } + true +} + +/// Checks if all artifacts exist and are compatible with the current architecture +fn are_all_artifacts_compatible(nim_codex_dir: &PathBuf, current_arch: &str) -> bool { + println!( + "cargo:warning=Checking artifact compatibility for {}", + current_arch + ); + + // Check static artifacts + for artifact_path in STATIC_ARTIFACTS { + let full_path = nim_codex_dir.join(artifact_path); + println!( + "cargo:warning=Checking static artifact: {}", + full_path.display() + ); + if !is_artifact_compatible(&full_path, current_arch) { + println!( + "cargo:warning=Static artifact {} is incompatible with {}", + artifact_path, current_arch + ); + return false; + } + println!( + "cargo:warning=Static artifact {} is compatible", + artifact_path + ); + } + + // Check dynamic artifacts + for artifact_path in DYNAMIC_ARTIFACTS { + let full_path = nim_codex_dir.join(artifact_path); + println!( + "cargo:warning=Checking dynamic artifact: {}", + full_path.display() + ); + if !is_artifact_compatible(&full_path, current_arch) { + println!( + "cargo:warning=Dynamic artifact {} is incompatible with {}", + artifact_path, current_arch + ); + return false; + } + println!( + "cargo:warning=Dynamic artifact {} is compatible", + artifact_path + ); + } + + true +} + +/// Cleans all build artifacts and directories +fn clean_all_artifacts(nim_codex_dir: &PathBuf, is_android: bool) { + println!("cargo:warning=Cleaning build artifacts..."); + + // Execute the cleaning script + if let Ok(output) = Command::new("./clean_build_artifacts.sh") + .arg(nim_codex_dir.as_os_str()) + .output() + { + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + println!("cargo:warning={}", stdout); + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + println!( + "cargo:warning=⚠️ Cleaning script failed: {}\nstdout: {}\nstderr: {}", + output.status, stdout, stderr + ); + } + } else { + println!("cargo:warning=⚠️ Could not execute clean_build_artifacts.sh"); + } + + // Revert Android patches when switching away from Android + if !is_android { + println!("cargo:warning=Reverting Android patches for desktop build..."); + if let Ok(output) = Command::new("./revert_patches.sh").output() { + if output.status.success() { + println!("cargo:warning=✅ Successfully reverted Android patches"); + } else { + let stderr = String::from_utf8_lossy(&output.stderr); + println!( + "cargo:warning=⚠️ Failed to revert Android patches: {}", + stderr + ); + } + } else { + println!("cargo:warning=⚠️ Could not execute revert_patches.sh"); + } + } + + println!("cargo:warning=Build artifacts cleanup completed"); +} + +/// Simplified artifact cleaning function +/// Only cleans if artifacts are incompatible with current architecture +fn clean_build_artifacts() { + let current_arch = get_current_architecture(); + let nim_codex_dir = get_nim_codex_dir(); + let target = env::var("TARGET").unwrap_or_default(); + let is_android = target.contains("android"); + + // Check if all artifacts are compatible with current architecture + if are_all_artifacts_compatible(&nim_codex_dir, ¤t_arch) { + println!( + "cargo:warning=All artifacts are compatible with {}, no cleanup needed", + current_arch + ); + return; + } + + // If we get here, we need to clean + println!( + "cargo:warning=Incompatible artifacts detected for {}, cleaning...", + current_arch + ); + + clean_all_artifacts(&nim_codex_dir, is_android); +} + fn check_required_tools() { let tools = ["git", "make"]; for tool in &tools { @@ -64,11 +367,19 @@ fn get_nim_codex_dir() -> PathBuf { match source_mode { SourceMode::Submodule => PathBuf::from("vendor/nim-codex"), SourceMode::Cloned => { - let cloned_dir = out_dir.join("nim-codex"); + // Clone to OUT_DIR/vendor/nim-codex to maintain path consistency with patches + let vendor_dir = out_dir.join("vendor"); + let cloned_dir = vendor_dir.join("nim-codex"); + if !cloned_dir.exists() { + // Create vendor directory if it doesn't exist + if !vendor_dir.exists() { + std::fs::create_dir_all(&vendor_dir) + .expect("Failed to create vendor directory in OUT_DIR"); + } clone_nim_codex(&cloned_dir); } else { - println!("Using previously cloned nim-codex in OUT_DIR"); + println!("Using previously cloned nim-codex in OUT_DIR/vendor"); } cloned_dir } @@ -103,16 +414,27 @@ fn clone_nim_codex(target_dir: &PathBuf) { fn build_libcodex_static(nim_codex_dir: &PathBuf) { println!("Building libcodex with static linking..."); + let target = env::var("TARGET").unwrap_or_default(); + let is_android = target.contains("android"); let codex_params = env::var("CODEX_LIB_PARAMS").unwrap_or_default(); + if is_android { + build_libcodex_static_android(nim_codex_dir, &codex_params); + return; + } + let mut make_cmd = Command::new("make"); make_cmd.args(&[ + &format!("-j{}", get_parallel_jobs()), "-C", &nim_codex_dir.to_string_lossy(), "STATIC=1", "libcodex", ]); + make_cmd.env("USE_LIBBACKTRACE", "1"); + // For desktop static builds, ensure we don't use Android CPU + make_cmd.env("CODEX_ANDROID_CPU", ""); if !codex_params.is_empty() { make_cmd.env("CODEX_LIB_PARAMS", &codex_params); } @@ -135,19 +457,7 @@ fn build_libcodex_static(nim_codex_dir: &PathBuf) { eprintln!("Build stdout:"); eprintln!("{}", stdout); - panic!( - "Failed to build libcodex with static linking. This could be due to:\n\ - 1. Missing build dependencies (C compiler, make, git)\n\ - 2. Network issues during repository cloning\n\ - 3. Insufficient disk space or memory\n\ - 4. Build timeout in CI environments\n\ - \n\ - For troubleshooting, try building manually:\n\ - cd {:?}\n\ - make deps\n\ - make STATIC=1 libcodex", - nim_codex_dir - ); + panic!("Failed to build libcodex with static linking."); } println!("Successfully built libcodex (static)"); @@ -156,6 +466,14 @@ fn build_libcodex_static(nim_codex_dir: &PathBuf) { fn build_libcodex_dynamic(nim_codex_dir: &PathBuf) { println!("Building libcodex with dynamic linking..."); + let target = env::var("TARGET").unwrap_or_default(); + let is_android = target.contains("android"); + + if is_android { + build_libcodex_dynamic_android(nim_codex_dir, &target); + return; + } + let codex_params = env::var("CODEX_LIB_PARAMS").unwrap_or_default(); let mut make_cmd = Command::new("make"); @@ -165,17 +483,17 @@ fn build_libcodex_dynamic(nim_codex_dir: &PathBuf) { make_cmd.env("CODEX_LIB_PARAMS", &codex_params); } + make_cmd.env("V", "1"); + make_cmd.env("USE_SYSTEM_NIM", "0"); + make_cmd.env("USE_LIBBACKTRACE", "1"); + make_cmd.env("CODEX_LIB_PARAMS", "-d:release"); + let status = make_cmd .status() .expect("Failed to execute make command. Make sure make is installed and in PATH."); if !status.success() { - panic!( - "Failed to build libcodex with dynamic linking. Please ensure:\n\ - 1. Nim compiler is installed and in PATH\n\ - 2. All build dependencies are available\n\ - 3. The nim-codex repository is complete and not corrupted" - ); + panic!("Failed to build libcodex with dynamic linking."); } println!("Successfully built libcodex (dynamic)"); @@ -198,20 +516,119 @@ fn ensure_libcodex(nim_codex_dir: &PathBuf, lib_dir: &PathBuf, linking_mode: Lin } } +/// Compiles the cmdline_symbols.c file for all builds (desktop and Android) +fn compile_cmdline_symbols() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let cmdline_symbols_c = PathBuf::from("src_build/cmdline_symbols.c"); + let cmdline_symbols_o = out_dir.join("cmdline_symbols.o"); + + let target = env::var("TARGET").unwrap_or_default(); + let is_android = target.contains("android"); + + // Use appropriate compiler for the target + let cc = if is_android { + env::var(format!("CC_{}", target)).unwrap_or_else(|_| { + // Fallback to Android NDK clang if target-specific CC is not set + env::var("CODEX_ANDROID_CC").unwrap_or_else(|_| "clang".to_string()) + }) + } else { + "cc".to_string() + }; + + // Compile the C file + let mut compile_cmd = Command::new(&cc); + compile_cmd.args(&[ + "-c", + &cmdline_symbols_c.to_string_lossy(), + "-o", + &cmdline_symbols_o.to_string_lossy(), + ]); + + // Add Android-specific flags if needed + if is_android { + if let Ok(cflags) = env::var("CFLAGS") { + compile_cmd.args(cflags.split_whitespace()); + } + } + + let output = compile_cmd + .output() + .expect("Failed to compile cmdline_symbols.c"); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + panic!( + "Failed to compile cmdline_symbols.c with {}:\nstdout: {}\nstderr: {}", + cc, stdout, stderr + ); + } + + // Create static library + let ar = if is_android { + env::var(format!("AR_{}", target)) + .unwrap_or_else(|_| env::var("CODEX_ANDROID_AR").unwrap_or_else(|_| "ar".to_string())) + } else { + "ar".to_string() + }; + + let output = Command::new(&ar) + .args(&[ + "rcs", + &out_dir.join("libcmdline_symbols.a").to_string_lossy(), + &cmdline_symbols_o.to_string_lossy(), + ]) + .output() + .expect("Failed to create libcmdline_symbols.a"); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + panic!( + "Failed to create libcmdline_symbols.a with {}:\nstdout: {}\nstderr: {}", + ar, stdout, stderr + ); + } + + // Tell cargo to link the static library + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rerun-if-changed=src_build/cmdline_symbols.c"); +} + fn link_static_library(nim_codex_dir: &PathBuf, _lib_dir: &PathBuf) { - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-libbacktrace/vendor/libbacktrace-upstream/.libs") - .display() - ); + let target = env::var("TARGET").unwrap_or_default(); + let is_android = target.contains("android"); - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-circom-compat/vendor/circom-compat-ffi/target/release") - .display() - ); + // Compile and link cmdline_symbols.c for all builds (desktop and Android) + compile_cmdline_symbols(); + + // Only add libbacktrace search paths for non-Android builds + if !is_android { + println!( + "cargo:rustc-link-search=native={}", + nim_codex_dir + .join("vendor/nim-libbacktrace/vendor/libbacktrace-upstream/.libs") + .display() + ); + } + + let circom_dir = if is_android { + get_android_circom_dir(nim_codex_dir, &target) + } else { + nim_codex_dir.join("vendor/nim-circom-compat/vendor/circom-compat-ffi/target/release") + }; + + // Check if the Android-specific directory exists, fallback to regular directory + let circom_dir = if is_android && !circom_dir.exists() { + println!( + "cargo:warning=Android-specific circom directory not found, falling back to default" + ); + nim_codex_dir.join("vendor/nim-circom-compat/vendor/circom-compat-ffi/target/release") + } else { + circom_dir + }; + + println!("cargo:rustc-link-search=native={}", circom_dir.display()); println!( "cargo:rustc-link-search=native={}", @@ -227,27 +644,38 @@ fn link_static_library(nim_codex_dir: &PathBuf, _lib_dir: &PathBuf) { .display() ); - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-libbacktrace/install/usr/lib") - .display() - ); + // Only add libbacktrace install search paths for non-Android builds + if !is_android { + println!( + "cargo:rustc-link-search=native={}", + nim_codex_dir + .join("vendor/nim-libbacktrace/install/usr/lib") + .display() + ); + } - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("nimcache/release/libcodex/vendor_leopard") - .display() - ); + let leopard_dir_release = nim_codex_dir.join("nimcache/release/libcodex/vendor_leopard"); + let leopard_dir_debug = nim_codex_dir.join("nimcache/debug/libcodex/vendor_leopard"); + + let leopard_dir = if leopard_dir_release.exists() { + leopard_dir_release + } else { + println!("Warning: Leopard library not found in release directory, using debug directory"); + leopard_dir_debug + }; + + println!("cargo:rustc-link-search=native={}", leopard_dir.display()); println!("cargo:rustc-link-arg=-Wl,--whole-archive"); - println!("cargo:rustc-link-lib=static=backtrace"); + // Only link libbacktrace on non-Android builds (it's disabled for Android) + if !is_android { + println!("cargo:rustc-link-lib=static=backtrace"); + println!("cargo:rustc-link-lib=static=backtracenim"); + } println!("cargo:rustc-link-lib=static=circom_compat_ffi"); println!("cargo:rustc-link-lib=static=natpmp"); println!("cargo:rustc-link-lib=static=miniupnpc"); - println!("cargo:rustc-link-lib=static=backtracenim"); println!("cargo:rustc-link-lib=static=libleopard"); println!("cargo:rustc-link-lib=static=codex"); @@ -256,13 +684,17 @@ fn link_static_library(nim_codex_dir: &PathBuf, _lib_dir: &PathBuf) { println!("cargo:rustc-link-lib=stdc++"); - println!("cargo:rustc-link-lib=dylib=gomp"); + if is_android { + println!("cargo:rustc-link-lib=static=omp"); + } else { + println!("cargo:rustc-link-lib=dylib=gomp"); + } println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); println!("cargo:rustc-link-arg=-Wl,--defsym=__rust_probestack=0"); - println!("cargo:rustc-link-arg=-Wl,--defsym=cmdCount=0"); - println!("cargo:rustc-link-arg=-Wl,--defsym=cmdLine=0"); + // Link cmdline_symbols.o for all builds (desktop and Android) + println!("cargo:rustc-link-lib=static=cmdline_symbols"); } fn link_dynamic_library(lib_dir: &PathBuf) { @@ -270,11 +702,27 @@ fn link_dynamic_library(lib_dir: &PathBuf) { let lib_dir_abs = std::fs::canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf()); println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir_abs.display()); + + // Also add the absolute path to the library search path + println!("cargo:rustc-link-search=native={}", lib_dir_abs.display()); } fn generate_bindings(nim_codex_dir: &PathBuf) { let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let libcodex_header_path = nim_codex_dir.join("nimcache/release/libcodex/libcodex.h"); + let libcodex_header_path_release = nim_codex_dir.join("nimcache/release/libcodex/libcodex.h"); + let libcodex_header_path_debug = nim_codex_dir.join("nimcache/debug/libcodex/libcodex.h"); + let libcodex_header_path_library = nim_codex_dir.join("library/libcodex.h"); + + // Try release directory first, then debug directory, then library directory + let libcodex_header_path = if libcodex_header_path_release.exists() { + libcodex_header_path_release + } else if libcodex_header_path_debug.exists() { + println!("Warning: Header file not found in release directory, using debug directory"); + libcodex_header_path_debug + } else { + println!("Warning: Header file not found in release or debug directories, using library directory"); + libcodex_header_path_library + }; let mut builder = bindgen::Builder::default() .header(libcodex_header_path.to_str().expect("Invalid path")) @@ -288,7 +736,9 @@ fn generate_bindings(nim_codex_dir: &PathBuf) { .allowlist_var("codex_.*") .allowlist_var("RET_.*") .raw_line("#[allow(non_camel_case_types)]") - .raw_line("pub type CodexCallback = tyProc__crazOL9c5Gf8j9cqs2fd61EA;"); + .clang_arg("-D__STDC_VERSION__=201112L") // Define C11 standard for bool support + .clang_arg("-D__bool_true_false_are_defined=1") // Ensure bool is defined + .clang_arg("-includestdbool.h"); // Include stdbool.h for bool type let nim_lib_path = nim_codex_dir.join("vendor/nimbus-build-system/vendor/Nim/lib"); if nim_lib_path.exists() { @@ -307,16 +757,56 @@ fn generate_bindings(nim_codex_dir: &PathBuf) { fn main() { check_required_tools(); + setup_cargo_rerun_triggers(); let linking_mode = determine_linking_mode(); let nim_codex_dir = get_nim_codex_dir(); + let target = env::var("TARGET").unwrap_or_default(); + + if target.contains("android") { + setup_android_cross_compilation(target.clone()); + + match apply_android_patches_during_build(&nim_codex_dir) { + Ok(patches) => { + println!( + "cargo:warning=✅ Successfully applied {} Android patches with validation", + patches.len() + ); + } + Err(e) => { + println!("cargo:warning=❌ Android patch system failed: {}", e); + if e.to_string().contains("validation failed") { + panic!("Critical Android patch validation failed: {}. Build cannot continue with incorrect configuration.", e); + } + } + }; + } + + // Clean build artifacts to prevent cross-architecture contamination + clean_build_artifacts(); let lib_dir = nim_codex_dir.join("build"); let _include_dir = nim_codex_dir.join("nimcache/release/libcodex"); println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=vendor/nim-codex"); - println!("cargo:rerun-if-changed=vendor/libcodex.h"); + + // Set up appropriate rerun triggers based on source mode + match determine_source_mode() { + SourceMode::Submodule => { + println!("cargo:rerun-if-changed=vendor/nim-codex"); + println!("cargo:rerun-if-changed=vendor/libcodex.h"); + } + SourceMode::Cloned => { + // In cloned mode, watch the OUT_DIR directory + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + println!( + "cargo:rerun-if-changed={}", + out_dir.join("vendor/nim-codex").display() + ); + // Also watch the original vendor directory for libcodex.h + println!("cargo:rerun-if-changed=vendor/libcodex.h"); + } + } match linking_mode { LinkingMode::Static => { diff --git a/clean_build_artifacts.sh b/clean_build_artifacts.sh new file mode 100755 index 0000000..fcb0641 --- /dev/null +++ b/clean_build_artifacts.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Script to clean all build artifacts and directories +# This script is called from build.rs when architecture mismatch is detected + +set -e + +NIM_CODEX_DIR="${1:-vendor/nim-codex}" + +echo "🧹 Starting build artifacts cleanup..." +echo "📁 Target directory: $NIM_CODEX_DIR" + +# Clean Nim cache to prevent architecture conflicts +if [[ -n "$HOME" ]]; then + NIM_CACHE_PATH="$HOME/.cache/nim/libcodex_d" + if [[ -d "$NIM_CACHE_PATH" ]]; then + echo "🗑️ Removing Nim cache: $NIM_CACHE_PATH" + rm -rf "$NIM_CACHE_PATH" + fi +fi + +# Clean build directories +BUILD_DIRECTORIES=( + "vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build" + "vendor/nim-nat-traversal/vendor/libnatpmp-upstream/build" + "vendor/nim-circom-compat/vendor/circom-compat-ffi/target" + "vendor/nim-leveldbstatic/build" + "build" + "nimcache/release" + "nimcache/debug" +) + +for dir in "${BUILD_DIRECTORIES[@]}"; do + FULL_PATH="$NIM_CODEX_DIR/$dir" + if [[ -d "$FULL_PATH" ]]; then + echo "🗑️ Removing build directory: $dir" + rm -rf "$FULL_PATH" + fi +done + +# Clean any leftover .o files in specific directories +OBJECT_FILE_DIRS=( + "vendor/nim-nat-traversal/vendor/libnatpmp-upstream" + "vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc" +) + +for dir in "${OBJECT_FILE_DIRS[@]}"; do + DIR_PATH="$NIM_CODEX_DIR/$dir" + if [[ -d "$DIR_PATH" ]]; then + echo "🧹 Cleaning object files in: $dir" + find "$DIR_PATH" -name "*.o" -type f -delete 2>/dev/null || true + fi +done + +# Restore .gitkeep file in nim-leveldbstatic build directory +cd "$NIM_CODEX_DIR/vendor/nim-leveldbstatic" && git restore build/.gitkeep 2>/dev/null || true + +echo "✅ Build artifacts cleanup completed" \ No newline at end of file diff --git a/revert_patches.sh b/revert_patches.sh new file mode 100755 index 0000000..ca88fa8 --- /dev/null +++ b/revert_patches.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Simple script to revert all Android patches +# set -e # Disabled to prevent early exit on individual patch failures + +PATCHES_DIR="android-patches" +SUCCESS_COUNT=0 +FAIL_COUNT=0 +DELETED_FILES=0 + +echo "🔄 Starting patch reversion..." + +# Function to check if patch creates a new file +is_new_file_patch() { + local patch="$1" + # Check for @@ -0,0 +1, pattern which indicates a new file + # AND ensure there are no deletion lines (lines starting with - followed by a number) + if grep -q "^@@ -0,0" "$patch" && ! grep -q "^-[0-9]" "$patch"; then + return 0 + fi + return 1 +} + +# Function to get target file from patch +get_target_file() { + local patch="$1" + grep "^+++ b/" "$patch" | sed 's/^+++ b\///' | head -1 +} + +# Find and revert all patches +while IFS= read -r -d '' patch; do + patch_name=${patch#$PATCHES_DIR/} + echo "🔧 Reverting: $patch_name" + + if git apply -R "$patch" 2>/dev/null; then + echo " ✅ Success" + ((SUCCESS_COUNT++)) + + # Delete file if this was a new file patch + if is_new_file_patch "$patch"; then + target_file=$(get_target_file "$patch") + if [[ -n "$target_file" && -f "$target_file" ]]; then + echo " 🗑️ Deleting: $target_file" + rm -f "$target_file" + ((DELETED_FILES++)) + fi + fi + else + echo " ❌ Failed" + ((FAIL_COUNT++)) + fi +done < <(find "$PATCHES_DIR" -name "*.patch" -type f -print0 | sort -z) + +echo +echo "📊 Summary:" +echo " Reverted: $SUCCESS_COUNT" +echo " Failed: $FAIL_COUNT" +echo " Files deleted: $DELETED_FILES" \ No newline at end of file diff --git a/src_build/build_android.rs b/src_build/build_android.rs new file mode 100644 index 0000000..f1848a6 --- /dev/null +++ b/src_build/build_android.rs @@ -0,0 +1,575 @@ +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command; + +// Import patch_system from the main module +use crate::patch_system::{get_android_arch_from_target, PatchEngine}; + +// Import the parallelism module for get_parallel_jobs function +use super::parallelism::get_parallel_jobs; + +/// Detects the Clang version in the Android NDK +fn detect_clang_version(android_ndk: &str) -> Result> { + let clang_lib_path = + PathBuf::from(android_ndk).join("toolchains/llvm/prebuilt/linux-x86_64/lib/clang"); + + if !clang_lib_path.exists() { + return Err(format!("Clang lib path not found: {}", clang_lib_path.display()).into()); + } + + // Read the directory and find the highest version number + let mut versions = Vec::new(); + for entry in fs::read_dir(clang_lib_path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + if let Some(version_str) = path.file_name().and_then(|n| n.to_str()) { + if let Ok(version_num) = version_str.parse::() { + versions.push((version_num, version_str.to_string())); + } + } + } + } + + if versions.is_empty() { + return Err("No Clang version directories found".into()); + } + + // Sort by version number and take the highest + versions.sort_by_key(|(num, _)| *num); + let (_, latest_version) = versions.last().unwrap(); + + println!("cargo:warning=Detected Clang version: {}", latest_version); + Ok(latest_version.to_string()) +} + +/// Sets up Android cross-compilation environment +pub fn setup_android_cross_compilation(target: String) { + println!( + "cargo:warning=Setting up Android cross-compilation for target: {}", + target + ); + + // Try multiple environment variable names and fallback paths for Android SDK + let android_sdk = env::var("ANDROID_SDK_ROOT") + .or_else(|_| env::var("ANDROID_HOME")) + .expect("ANDROID_SDK_ROOT or ANDROID_HOME environment variable must be set"); + + let android_ndk = env::var("NDK_HOME") + .or_else(|_| env::var("ANDROID_NDK_HOME")) + .or_else(|_| env::var("ANDROID_NDK_ROOT")) + .expect("NDK_HOME, ANDROID_NDK_HOME or ANDROID_NDK_ROOT environment variable must be set"); + + if !std::path::Path::new(&android_sdk).exists() { + panic!("Android SDK not found at {}.", android_sdk); + } + if !std::path::Path::new(&android_ndk).exists() { + panic!("Android NDK not found at {}.", android_ndk); + } + + // Clean architecture-specific build artifacts to prevent cross-architecture contamination + // This will be handled by the main build.rs clean_architecture_specific_artifacts() function + // which is called before Android setup + + let target_clone = target.clone(); + + unsafe { + env::set_var(&format!("CARGO_TARGET_{}", target), "1"); + env::set_var(&format!("CARGO_LINKER_{}", target), "clang"); + + env::set_var("CARGO_TARGET_{}_LINKER", target); + } + + let (arch, _) = get_android_arch_from_target(&target_clone); + + // Detect Clang version dynamically + let clang_version = + detect_clang_version(&android_ndk).expect("Failed to detect Clang version in Android NDK"); + + let toolchain_path = format!("{}/toolchains/llvm/prebuilt/linux-x86_64/bin", android_ndk); + let cc = format!("{}/{}21-clang", toolchain_path, target_clone); + let cxx = format!("{}/{}21-clang++", toolchain_path, target_clone); + let ar = format!("{}/llvm-ar", toolchain_path); + let ranlib = format!("{}/llvm-ranlib", toolchain_path); + + println!("cargo:warning=Android CC path: {}", cc); + println!( + "cargo:warning=Android CC exists: {}", + std::path::Path::new(&cc).exists() + ); + + unsafe { + env::set_var(format!("CC_{}", target_clone), &cc); + env::set_var(format!("CXX_{}", target_clone), &cxx); + env::set_var(format!("AR_{}", target_clone), &ar); + env::set_var(format!("RANLIB_{}", target_clone), &ranlib); + + // Set architecture-specific environment variables + match target_clone.as_str() { + "aarch64-linux-android" => { + env::set_var("CC_aarch64_linux_android", &cc); + env::set_var("CXX_aarch64_linux_android", &cxx); + env::set_var("AR_aarch64_linux_android", &ar); + env::set_var("RANLIB_aarch64_linux_android", &ranlib); + } + "x86_64-linux-android" => { + env::set_var("CC_x86_64_linux_android", &cc); + env::set_var("CXX_x86_64_linux_android", &cxx); + env::set_var("AR_x86_64_linux_android", &ar); + env::set_var("RANLIB_x86_64_linux_android", &ranlib); + } + _ => panic!("Unsupported Android target: {}", target_clone), + } + } + + let sysroot = format!( + "{}/toolchains/llvm/prebuilt/linux-x86_64/sysroot", + android_ndk + ); + + println!( + "cargo:rustc-link-arg=-L{}/usr/lib/{}/21", + sysroot, target_clone + ); + println!( + "cargo:rustc-link-arg=-L{}/usr/lib/{}", + sysroot, target_clone + ); + + let arch_flag = match target_clone.as_str() { + "aarch64-linux-android" => "-march=armv8-a", + "x86_64-linux-android" => "-march=x86-64", + _ => panic!("Unsupported Android target: {}", target_clone), + }; + + let arch_define = match target_clone.as_str() { + "aarch64-linux-android" => "-d:arm64", + "x86_64-linux-android" => "-d:x86_64", + _ => panic!("Unsupported Android target: {}", target_clone), + }; + let android_defines = format!("{} -d:android -d:debug -d:disable_libbacktrace -d:noIntrinsicsBitOpts -d:NO_X86_INTRINSICS -d:__NO_INLINE_ASM__ -d:noX86 -d:noSSE -d:noAVX -d:noAVX2 -d:noAVX512 -d:noX86Intrinsics -d:noSimd -d:noInlineAsm", arch_define); + + unsafe { + env::set_var("NO_X86_INTRINSICS", "1"); + env::set_var("BR_NO_X86_INTRINSICS", "1"); + env::set_var("BR_NO_X86", "1"); + env::set_var("BR_NO_ASM", "1"); + } + + unsafe { + match target_clone.as_str() { + "aarch64-linux-android" => { + env::set_var("ANDROID_ARM64_BUILD", "1"); + env::set_var("TARGET_ARCH", "arm64"); + } + "x86_64-linux-android" => { + env::set_var("ANDROID_X86_64_BUILD", "1"); + env::set_var("TARGET_ARCH", "x86_64"); + } + _ => panic!("Unsupported Android target: {}", target_clone), + } + } + + unsafe { + env::set_var("CODEX_ANDROID_STATIC", "1"); + env::set_var("CODEX_ANDROID_CPU", arch); + env::set_var("CODEX_ANDROID_CC", &cc); + env::set_var("CODEX_ANDROID_AR", &ar); + env::set_var("CODEX_ANDROID_RANLIB", &ranlib); + env::set_var("CODEX_ANDROID_DEFINES", &android_defines); + env::set_var("CODEX_ANDROID_ARCH_FLAG", arch_flag); + + env::set_var("CODEX_LIB_PARAMS", &android_defines); + + env::set_var("NIM_TARGET", "android"); + env::set_var("NIM_ARCH", arch); + + env::set_var("ANDROID", "1"); + env::set_var("CODEX_SKIP_GIT_RESET", "1"); + env::set_var("CODEX_SKIP_SUBMODULE_RESET", "1"); + env::set_var("CODEX_SKIP_SUBMODULE_UPDATE", "1"); + + // CRITICAL: Set the environment variables that the build.nims patch expects + env::set_var("ANDROID_SDK_ROOT", &android_sdk); + env::set_var("ANDROID_NDK_HOME", &android_ndk); + env::set_var("ANDROID_NDK_ROOT", &android_ndk); + env::set_var("ANDROID_CLANG_VERSION", &clang_version); + } + + // Set Rust/Cargo cross-compilation environment variables for circom-compat-ffi + unsafe { + env::set_var("CARGO_BUILD_TARGET", &target_clone); + + // Set architecture-specific environment variables + match target_clone.as_str() { + "aarch64-linux-android" => { + env::set_var("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", &cc); + env::set_var("CC_aarch64_linux_android", &cc); + env::set_var("CXX_aarch64_linux_android", &cxx); + env::set_var("AR_aarch64_linux_android", &ar); + env::set_var("RANLIB_aarch64_linux_android", &ranlib); + } + "x86_64-linux-android" => { + env::set_var("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", &cc); + env::set_var("CC_x86_64_linux_android", &cc); + env::set_var("CXX_x86_64_linux_android", &cxx); + env::set_var("AR_x86_64_linux_android", &ar); + env::set_var("RANLIB_x86_64_linux_android", &ranlib); + } + _ => panic!("Unsupported Android target: {}", target_clone), + } + + // Set generic CC/AR for Rust's build scripts that don't use target-specific vars + env::set_var("CC", &cc); + env::set_var("CXX", &cxx); + env::set_var("AR", &ar); + env::set_var("RANLIB", &ranlib); + } + + println!("cargo:rustc-link-lib=dylib=android"); + println!("cargo:rustc-link-lib=dylib=log"); + println!("cargo:rustc-link-lib=dylib=OpenSLES"); + println!("cargo:rustc-link-lib=dylib=c++_shared"); + + println!( + "cargo:rustc-link-search=native={}/usr/lib/{}/21", + sysroot, target_clone + ); + println!( + "cargo:rustc-link-search=native={}/usr/lib/{}", + sysroot, target_clone + ); + + let (_, openmp_arch) = get_android_arch_from_target(&target_clone); + + let openmp_lib_path = format!( + "{}/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/{}/lib/linux/{}", + android_ndk, clang_version, openmp_arch + ); + println!("cargo:rustc-link-search=native={}", openmp_lib_path); + println!("cargo:rustc-link-lib=static=omp"); + + // Also set target-specific linker environment variables + println!("cargo:rustc-env=CC={}", cc); + println!("cargo:rustc-env=CXX={}", cxx); + println!("cargo:rustc-env=AR={}", ar); + println!("cargo:rustc-env=RANLIB={}", ranlib); + + // Force the use of Android NDK clang for all linking + println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); + println!("cargo:rustc-link-arg=-Wl,--undefined-version"); + + // Disable LSE atomics detection for Android to prevent getauxval() crash on Android 16 + // LSE atomics are a performance optimization that can be safely disabled + println!("cargo:rustc-env=RUSTFLAGS=-C target-feature=-lse"); + + // Force Rust to use the Android NDK linker directly + // Set the linker path in the environment so clang can find it + let android_ld_path = format!("{}/toolchains/llvm/prebuilt/linux-x86_64/bin", android_ndk); + + // Get the current system PATH and append Android NDK path to preserve system tools like bash + let current_path = env::var("PATH").unwrap_or_default(); + let new_path = format!("{}:{}", current_path, android_ld_path); + println!("cargo:rustc-env=PATH={}", new_path); + // Let Android NDK clang use its default linker + // println!("cargo:rustc-link-arg=-fuse-ld=lld"); + + // FIX: Force the use of Android NDK lld linker and use shared libc + // This prevents the PIC linking error with __cpu_model symbol + println!("cargo:rustc-link-arg=-fuse-ld=lld"); + + // Add linker flags to avoid the problematic static libraries + println!("cargo:rustc-link-arg=-Wl,--as-needed"); + println!("cargo:rustc-link-arg=-Wl,--gc-sections"); + + // Explicitly link against the shared libc from API level 21 + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{}/usr/lib/{}/21", + sysroot, target_clone + ); + + // Force dynamic linking for libc to avoid PIC issues + println!("cargo:rustc-link-lib=dylib=c"); + println!("cargo:rustc-link-lib=dylib=m"); + + // Manually add the Android runtime libraries that would normally be included + println!("cargo:rustc-link-lib=dylib=dl"); + + // pthread is built into libc on all Android architectures, no separate linking needed + + // Set linker environment variables that BearSSL will inherit + unsafe { + // Force BearSSL to use Android NDK linker + let android_linker = format!( + "{}/toolchains/llvm/prebuilt/linux-x86_64/bin/ld.lld", + android_ndk + ); + + env::set_var("LD", &android_linker); + env::set_var("BEARSSL_LD", &android_linker); + + // Add linker flags to force Android linker usage + let linker_flags = format!("-fuse-ld={}", android_linker); + env::set_var("LDFLAGS", linker_flags); + } + + println!( + "Android cross-compilation setup complete for {}", + target_clone + ); +} + +/// Builds libcodex with static linking for Android +pub fn build_libcodex_static_android(nim_codex_dir: &PathBuf, codex_params: &str) { + println!("Building libcodex with static linking for Android..."); + + let mut make_cmd = Command::new("make"); + make_cmd.args(&[ + &format!("-j{}", get_parallel_jobs()), + "-C", + &nim_codex_dir.to_string_lossy(), + "STATIC=1", + "libcodex", + ]); + + // CRITICAL: Set NIM_PARAMS FIRST to prevent .DEFAULT target from running + // This must be set before any other environment variables to prevent git submodule update + make_cmd.env("NIM_PARAMS", codex_params); // This prevents the .DEFAULT target from running + + make_cmd.env("USE_LIBBACKTRACE", "0"); + // CRITICAL: Prevent Makefile from updating submodules after patches are applied + // This ensures our patches don't get overwritten by git submodule update + make_cmd.env("CODEX_LIB_PARAMS", codex_params); + + // CRITICAL: Ensure NIM_PARAMS is also set as CODEX_LIB_PARAMS for consistency + // The Makefile adds CODEX_LIB_PARAMS to NIM_PARAMS, so this double-ensures NIM_PARAMS is set + if !codex_params.is_empty() { + make_cmd.env("NIM_PARAMS", codex_params); + } + + make_cmd.env("V", "1"); + make_cmd.env("USE_SYSTEM_NIM", "0"); + + println!("Running make command to build libcodex (this may take several minutes)..."); + + let output = make_cmd + .output() + .expect("Failed to execute make command. Make sure make is installed and in PATH."); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + + eprintln!("Build failed with stderr:"); + eprintln!("{}", stderr); + eprintln!("Build stdout:"); + eprintln!("{}", stdout); + + panic!("Failed to build libcodex with static linking for Android."); + } + + println!("Successfully built libcodex (static) for Android"); +} + +/// Builds libcodex with dynamic linking for Android +pub fn build_libcodex_dynamic_android(nim_codex_dir: &PathBuf, target: &str) { + println!("Building libcodex with make for Android..."); + + let cpu = env::var("CODEX_ANDROID_CPU").unwrap_or_default(); + let cc = env::var("CODEX_ANDROID_CC").unwrap_or_default(); + let cxx = env::var("CXX_").unwrap_or_else(|_| cc.replace("-clang", "-clang++")); + let ar = env::var("CODEX_ANDROID_AR").unwrap_or_default(); + let ranlib = env::var("CODEX_ANDROID_RANLIB").unwrap_or_default(); + let android_defines = env::var("CODEX_ANDROID_DEFINES").unwrap_or_default(); + let arch_flag = env::var("CODEX_ANDROID_ARCH_FLAG").unwrap_or_default(); + + let mut make_cmd = Command::new("make"); + make_cmd.args(&[ + &format!("-j{}", get_parallel_jobs()), + "-C", + &nim_codex_dir.to_string_lossy(), + "libcodex", + ]); + + make_cmd.env("NIM_PARAMS", &android_defines); + + make_cmd.env("USE_LIBBACKTRACE", "0"); + make_cmd.env("ANDROID", "1"); + make_cmd.env("CODEX_ANDROID_CPU", &cpu); + make_cmd.env("CODEX_ANDROID_CC", &cc); + make_cmd.env("CODEX_ANDROID_AR", &ar); + make_cmd.env("CODEX_ANDROID_RANLIB", &ranlib); + make_cmd.env("CODEX_ANDROID_DEFINES", &android_defines); + make_cmd.env("CODEX_ANDROID_ARCH_FLAG", &arch_flag); + make_cmd.env("V", "1"); + + make_cmd.env("CODEX_LIB_PARAMS", &android_defines); + + make_cmd.env("NO_X86_INTRINSICS", "1"); + make_cmd.env("BR_NO_X86_INTRINSICS", "1"); + make_cmd.env("BR_NO_X86", "1"); + make_cmd.env("BR_NO_ASM", "1"); + + match target { + "aarch64-linux-android" => { + make_cmd.env("ANDROID_ARM64_BUILD", "1"); + make_cmd.env("TARGET_ARCH", "arm64"); + } + "x86_64-linux-android" => { + make_cmd.env("ANDROID_X86_64_BUILD", "1"); + make_cmd.env("TARGET_ARCH", "x86_64"); + } + _ => {} + } + + let android_ndk = env::var("NDK_HOME") + .or_else(|_| env::var("ANDROID_NDK_HOME")) + .or_else(|_| env::var("ANDROID_NDK_ROOT")) + .expect("NDK_HOME, ANDROID_NDK_HOME or ANDROID_NDK_ROOT environment variable must be set"); + let sysroot = format!( + "{}/toolchains/llvm/prebuilt/linux-x86_64/sysroot", + android_ndk + ); + + make_cmd.env("CMAKE_C_COMPILER", &cc); + make_cmd.env("CMAKE_CXX_COMPILER", &cxx); + make_cmd.env("CMAKE_AR", &ar); + make_cmd.env("CMAKE_RANLIB", &ranlib); + + let cmake_android_defines = format!( + "-include -DNO_TERMIOS -DNO_TERMINFO -DNO_X86_INTRINSICS -DBR_NO_X86_INTRINSICS -DBR_NO_X86 -DBR_NO_ASM", + ); + make_cmd.env("CMAKE_C_FLAGS", &cmake_android_defines); + make_cmd.env("CMAKE_CXX_FLAGS", &cmake_android_defines); + make_cmd.env("CMAKE_SYSTEM_NAME", "Android"); + make_cmd.env("CMAKE_SYSTEM_PROCESSOR", &cpu); + make_cmd.env("CMAKE_ANDROID_STANDALONE_TOOLCHAIN", &android_ndk); + make_cmd.env("CMAKE_FIND_ROOT_PATH", &sysroot); + make_cmd.env("CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "NEVER"); + make_cmd.env("CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "ONLY"); + make_cmd.env("CMAKE_FIND_ROOT_PATH_MODE_INCLUDE", "ONLY"); + + make_cmd.env("CC", &cc); + make_cmd.env("CXX", &cxx); + make_cmd.env("LD", &cc); + make_cmd.env("LINKER", &cc); + make_cmd.env("AR", &ar); + make_cmd.env("RANLIB", &ranlib); + + make_cmd.env("NIM_TARGET", "android"); + make_cmd.env("NIM_ARCH", &cpu); + make_cmd.env("OS", "android"); + make_cmd.env("detected_OS", "android"); + + make_cmd.env("CFLAGS", "-O2 -fPIC"); + make_cmd.env("CXXFLAGS", "-O2 -fPIC"); + make_cmd.env("LDFLAGS", "-O2 -fPIC"); + + println!("Running make command: {:?}", make_cmd); + let output = make_cmd.output().expect("Failed to execute make command"); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + + eprintln!("Make build failed with stderr:"); + eprintln!("{}", stderr); + eprintln!("Make build stdout:"); + eprintln!("{}", stdout); + + panic!("Failed to build libcodex for Android"); + } + + println!("Successfully built libcodex (dynamic) for Android"); +} + +/// Gets the Android-specific circom directory +pub fn get_android_circom_dir(nim_codex_dir: &PathBuf, target: &str) -> PathBuf { + let circom_dir = match target { + "aarch64-linux-android" => "aarch64-linux-android", + "x86_64-linux-android" => "x86_64-linux-android", + _ => "aarch64-linux-android", + }; + + nim_codex_dir.join(format!( + "vendor/nim-circom-compat/vendor/circom-compat-ffi/target/{}/release", + circom_dir + )) +} + +/// Forces LevelDB rebuild for Android to ensure proper cross-compilation +pub fn force_leveldb_rebuild_android(nim_codex_dir: &PathBuf) { + println!("🔧 Forcing LevelDB rebuild for Android..."); + + let leveldb_dir = nim_codex_dir.join("vendor/nim-leveldbstatic"); + let leveldb_build_dir = leveldb_dir.join("build"); + + // Remove LevelDB build directory + if leveldb_build_dir.exists() { + println!( + " Removing LevelDB build directory: {:?}", + leveldb_build_dir + ); + let _ = fs::remove_dir_all(&leveldb_build_dir); + } + + // Remove any compiled LevelDB libraries + let leveldb_lib_patterns = ["libleveldb.a", "libleveldb.so", "leveldb"]; + + for pattern in &leveldb_lib_patterns { + let lib_path = leveldb_dir.join(pattern); + if lib_path.exists() { + println!(" Removing LevelDB library: {:?}", lib_path); + let _ = fs::remove_file(&lib_path); + } + } + + // Remove any cached Nim files for LevelDB + let home_dir = env::var("HOME").unwrap_or_default(); + let nim_cache_leveldb = PathBuf::from(home_dir) + .join(".cache/nim") + .join("leveldbstatic"); + + if nim_cache_leveldb.exists() { + println!(" Removing Nim LevelDB cache: {:?}", nim_cache_leveldb); + let _ = fs::remove_dir_all(&nim_cache_leveldb); + } + + println!(" ✅ LevelDB rebuild preparation complete"); +} + +/// Applies Android patches during build +pub fn apply_android_patches_during_build( + nim_codex_dir: &PathBuf, +) -> Result, Box> { + let target = env::var("TARGET").unwrap_or_default(); + let (arch, _) = get_android_arch_from_target(&target); + + println!( + "🔧 Applying Android patches for target: {} (arch: {})", + target, arch + ); + + // Force LevelDB rebuild for Android to ensure proper cross-compilation + force_leveldb_rebuild_android(nim_codex_dir); + + let engine = PatchEngine::new(true)?; + + let applied_patches = engine.apply_patches_for_arch(arch)?; + + println!( + "✅ Successfully applied {} patches for architecture {}", + applied_patches.len(), + arch + ); + + Ok(applied_patches) +} + +/// Set up cargo rerun triggers for patch system files +pub fn setup_cargo_rerun_triggers() { + println!("cargo:rerun-if-changed=android-patches/"); + println!("cargo:rerun-if-changed=src/patch_system.rs"); +} diff --git a/src_build/cmdline_symbols.c b/src_build/cmdline_symbols.c new file mode 100644 index 0000000..4a2ec50 --- /dev/null +++ b/src_build/cmdline_symbols.c @@ -0,0 +1,12 @@ +// These symbols are normally defined in the Nim-generated main function, +// but when building as a library, they need to be defined explicitly. + +#include + +#ifdef __ANDROID__ +__attribute__((weak)) int cmdCount = 0; +__attribute__((weak)) char** cmdLine = NULL; +#else +int cmdCount = 0; +char** cmdLine = NULL; +#endif \ No newline at end of file diff --git a/src_build/parallelism.rs b/src_build/parallelism.rs new file mode 100644 index 0000000..6f1771c --- /dev/null +++ b/src_build/parallelism.rs @@ -0,0 +1,13 @@ +pub fn get_parallel_jobs() -> String { + let num_jobs = std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(4); + + let jobs = std::cmp::max(4, num_jobs.saturating_sub(4)); + + println!( + "cargo:warning=Using {} parallel jobs for build (available cores: {})", + jobs, num_jobs + ); + jobs.to_string() +} diff --git a/src_build/patch_system.rs b/src_build/patch_system.rs new file mode 100644 index 0000000..f22af89 --- /dev/null +++ b/src_build/patch_system.rs @@ -0,0 +1,399 @@ +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +pub struct PatchEngine { + verbose: bool, + patches_dir: PathBuf, + base_dir: PathBuf, +} + +#[derive(Debug, thiserror::Error)] +pub enum PatchError { + #[error("Failed to discover patches: {0}")] + DiscoveryError(String), + + #[error("Patch application failed: {0}")] + ApplicationFailed(String), + + #[error("Patch validation failed: {0}")] + ValidationFailed(String), + + #[error("IO error: {0}")] + IoError(#[from] std::io::Error), +} + +impl PatchEngine { + pub fn new(verbose: bool) -> Result { + let patches_dir = PathBuf::from("android-patches"); + let base_dir = PathBuf::from("."); + + Ok(Self { + verbose, + patches_dir, + base_dir, + }) + } + + fn discover_all_patches(&self, arch: &str) -> Result<(Vec, Vec), PatchError> { + let mut arch_patches = Vec::new(); + let mut shared_patches = Vec::new(); + + let arch_dir = self.patches_dir.join(arch); + if arch_dir.exists() { + self.find_patch_files_recursive(&arch_dir, &mut arch_patches, "")?; + } + + let shared_dir = self.patches_dir.join("shared"); + if shared_dir.exists() { + self.find_patch_files_recursive(&shared_dir, &mut shared_patches, "")?; + } + + if self.verbose { + println!( + "Discovered {} architecture-specific patches for {}", + arch_patches.len(), + arch + ); + println!("Discovered {} shared patches", shared_patches.len()); + } + + Ok((arch_patches, shared_patches)) + } + + fn find_patch_files_recursive( + &self, + dir: &Path, + patches: &mut Vec, + prefix: &str, + ) -> Result<(), PatchError> { + let entries = fs::read_dir(dir).map_err(|e| { + PatchError::DiscoveryError(format!("Failed to read directory {}: {}", dir.display(), e)) + })?; + + for entry in entries { + let entry = entry.map_err(|e| { + PatchError::DiscoveryError(format!("Failed to read directory entry: {}", e)) + })?; + let path = entry.path(); + + if path.is_dir() { + let dir_name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown"); + + let new_prefix = if prefix.is_empty() { + dir_name.to_string() + } else { + format!("{}/{}", prefix, dir_name) + }; + + self.find_patch_files_recursive(&path, patches, &new_prefix)?; + } else if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { + if file_name.ends_with(".patch") { + let patch_path = if prefix.is_empty() { + file_name.to_string() + } else { + format!("{}/{}", prefix, file_name) + }; + patches.push(patch_path); + } + } + } + + Ok(()) + } + + pub fn apply_patches_for_arch(&self, arch: &str) -> Result, PatchError> { + let (arch_patches, shared_patches) = self.discover_all_patches(arch)?; + + if self.verbose { + println!( + "Applying {} architecture-specific patches for architecture {}", + arch_patches.len(), + arch + ); + } + + let mut applied_arch_patches = Vec::new(); + let mut failed_arch_patches = Vec::new(); + + for patch_file in arch_patches { + let patch_path = self.patches_dir.join(arch).join(&patch_file); + + if !patch_path.exists() { + if self.verbose { + println!(" ⚠️ Patch file not found: {}", patch_path.display()); + } + failed_arch_patches.push(patch_file.clone()); + continue; + } + + match self.apply_patch(&patch_path, &patch_file) { + Ok(()) => { + applied_arch_patches.push(patch_file.clone()); + } + Err(e) => { + if self.verbose { + println!(" ⚠️ Failed to apply patch {}: {}", patch_file, e); + } + failed_arch_patches.push(patch_file.clone()); + } + } + } + + let mut applied_shared_patches = Vec::new(); + let mut failed_shared_patches = Vec::new(); + + if self.verbose { + println!("Applying {} shared patches...", shared_patches.len()); + } + + for patch_file in shared_patches { + let patch_path = self.patches_dir.join("shared").join(&patch_file); + + if !patch_path.exists() { + if self.verbose { + println!( + " ⚠️ Shared patch file not found: {}", + patch_path.display() + ); + } + failed_shared_patches.push(patch_file.clone()); + continue; + } + + match self.apply_patch(&patch_path, &patch_file) { + Ok(()) => { + applied_shared_patches.push(patch_file.clone()); + } + Err(e) => { + if self.verbose { + println!(" ⚠️ Failed to apply shared patch {}: {}", patch_file, e); + } + failed_shared_patches.push(patch_file.clone()); + } + } + } + + let mut all_applied_patches = applied_arch_patches.clone(); + all_applied_patches.extend(applied_shared_patches.clone()); + + if self.verbose { + println!("\n=== Patch Summary for Architecture {} ===", arch); + println!( + "Architecture-specific patches: {} applied, {} failed", + applied_arch_patches.len(), + failed_arch_patches.len() + ); + println!( + "Shared patches: {} applied, {} failed", + applied_shared_patches.len(), + failed_shared_patches.len() + ); + println!( + "Total: {} patches applied, {} failed", + all_applied_patches.len(), + failed_arch_patches.len() + failed_shared_patches.len() + ); + + if !failed_arch_patches.is_empty() { + println!(" Failed architecture-specific patches:"); + for patch in &failed_arch_patches { + println!(" - {}", patch); + } + } + + if !failed_shared_patches.is_empty() { + println!(" Failed shared patches:"); + for patch in &failed_shared_patches { + println!(" - {}", patch); + } + } + println!("========================================\n"); + } + + if all_applied_patches.is_empty() + && (!failed_arch_patches.is_empty() || !failed_shared_patches.is_empty()) + { + Err(PatchError::ApplicationFailed(format!( + "No patches could be applied for architecture {} ({} failed)", + arch, + failed_arch_patches.len() + failed_shared_patches.len() + ))) + } else { + Ok(all_applied_patches) + } + } + + fn apply_patch(&self, patch_file: &Path, patch_name: &str) -> Result<(), PatchError> { + if self.verbose { + println!("Applying patch: {}", patch_name); + } + + println!("🔧 DEBUG: Attempting to apply patch: {}", patch_name); + println!("🔧 DEBUG: Patch file path: {}", patch_file.display()); + println!( + "🔧 DEBUG: Current time: {}", + std::process::Command::new("date") + .output() + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) + .unwrap_or_default() + ); + + if self.is_patch_already_applied(patch_file)? { + if self.verbose { + println!(" ✅ Patch {} already applied", patch_name); + } + println!("🔧 DEBUG: Patch {} already applied, skipping", patch_name); + return Ok(()); + } + + let output = Command::new("git") + .arg("apply") + .arg(patch_file) + .current_dir(&self.base_dir) + .output() + .map_err(|e| { + PatchError::ApplicationFailed(format!("Failed to run patch command: {}", e)) + })?; + + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + + if self.verbose { + println!(" Patch command stdout: {}", stdout); + println!(" Patch command stderr: {}", stderr); + println!(" Patch command exit status: {}", output.status); + } + + // DEBUG: Log patch application result + if output.status.success() { + println!("🔧 DEBUG: ✅ Successfully applied patch: {}", patch_name); + println!( + "🔧 DEBUG: Post-application time: {}", + std::process::Command::new("date") + .output() + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) + .unwrap_or_default() + ); + } else { + println!("🔧 DEBUG: ❌ Failed to apply patch: {}", patch_name); + } + + if !output.status.success() { + if stderr.contains("already applied") + || stderr.contains("previously applied") + || stdout.contains("already applied") + || stdout.contains("previously applied") + || stderr.contains("FAILED") + { + if self.verbose { + println!( + " ✅ Patch {} already applied or partially applied", + patch_name + ); + } + println!( + "🔧 DEBUG: Patch {} already applied or partially applied", + patch_name + ); + return Ok(()); + } + + return Err(PatchError::ApplicationFailed(format!( + "Patch {} failed: {}\nstdout: {}", + patch_name, stderr, stdout + ))); + } + + if self.verbose { + println!(" ✅ Applied patch: {}", patch_name); + } + + Ok(()) + } + + fn is_patch_already_applied(&self, patch_file: &Path) -> Result { + let output = Command::new("git") + .arg("apply") + .arg("--check") + .arg(patch_file) + .current_dir(&self.base_dir) + .output() + .map_err(|e| { + PatchError::ValidationFailed(format!("Failed to run patch --dry-run: {}", e)) + })?; + + let stderr = String::from_utf8_lossy(&output.stderr); + + let already_applied = stderr.contains("patch does not apply"); + + Ok(already_applied) + } + + #[allow(dead_code)] + pub fn validate_patches_for_arch(&self, arch: &str) -> Result<(), PatchError> { + let (arch_patches, shared_patches) = self.discover_all_patches(arch)?; + + if self.verbose { + println!( + "Validating {} architecture-specific patches and {} shared patches for architecture {}", + arch_patches.len(), + shared_patches.len(), + arch + ); + } + + for patch_file in arch_patches { + let patch_path = self.patches_dir.join(arch).join(&patch_file); + + if !patch_path.exists() { + return Err(PatchError::ValidationFailed(format!( + "Patch file not found: {}", + patch_path.display() + ))); + } + + if !self.is_patch_already_applied(&patch_path)? { + return Err(PatchError::ValidationFailed(format!( + "Architecture-specific patch {} is not applied", + patch_file + ))); + } + } + + for patch_file in shared_patches { + let patch_path = self.patches_dir.join("shared").join(&patch_file); + + if !patch_path.exists() { + return Err(PatchError::ValidationFailed(format!( + "Shared patch file not found: {}", + patch_path.display() + ))); + } + + if !self.is_patch_already_applied(&patch_path)? { + return Err(PatchError::ValidationFailed(format!( + "Shared patch {} is not applied", + patch_file + ))); + } + } + + if self.verbose { + println!("✅ All patches validated for architecture {}", arch); + } + + Ok(()) + } +} + +pub fn get_android_arch_from_target(target: &str) -> (&'static str, &'static str) { + match target { + "aarch64-linux-android" => ("arm64", "aarch64"), + "x86_64-linux-android" => ("x86_64", "x86_64"), + _ => panic!("Unsupported Android target: {}", target), + } +}