Skip to content

Commit e96f366

Browse files
committed
fix: use cargo-ndk for Android builds and pre-build uniffi-bindgen
- Switch from manual NDK config to cargo-ndk for Android cross-compilation - Build uniffi-bindgen for host first before any cross-compilation - Use pre-built uniffi-bindgen binary instead of cargo run - Add verification that expected org/ directory structure is created - Make script portable by removing bash 4+ associative array dependency
1 parent 9790d4f commit e96f366

File tree

2 files changed

+77
-74
lines changed

2 files changed

+77
-74
lines changed

.github/workflows/build-bindings.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ jobs:
108108
target/
109109
key: ${{ runner.os }}-cargo-kotlin-${{ hashFiles('**/Cargo.lock') }}
110110

111+
- name: Install cargo-ndk
112+
run: command -v cargo-ndk || cargo install cargo-ndk --locked
113+
111114
- name: Build for all Android architectures
112115
env:
113116
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}

scripts/build-kotlin.sh

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ ANDROID_TARGETS=(
3434
"i686-linux-android"
3535
)
3636

37+
# Get ABI name for a target
38+
get_abi_for_target() {
39+
local target=$1
40+
case "$target" in
41+
aarch64-linux-android) echo "arm64-v8a" ;;
42+
armv7-linux-androideabi) echo "armeabi-v7a" ;;
43+
x86_64-linux-android) echo "x86_64" ;;
44+
i686-linux-android) echo "x86" ;;
45+
*) echo "" ;;
46+
esac
47+
}
48+
49+
MIN_API=21
50+
3751
# Check for required tools
3852
check_requirements() {
3953
log_info "Checking requirements..."
@@ -49,6 +63,16 @@ check_requirements() {
4963
fi
5064
}
5165

66+
# Check and install cargo-ndk if needed
67+
check_cargo_ndk() {
68+
if ! command -v cargo-ndk &> /dev/null; then
69+
log_info "Installing cargo-ndk..."
70+
cargo install cargo-ndk --locked
71+
else
72+
log_info "cargo-ndk is already installed"
73+
fi
74+
}
75+
5276
# Check Android NDK
5377
check_android_ndk() {
5478
if [[ -z "$ANDROID_NDK_HOME" ]] && [[ -z "$NDK_HOME" ]]; then
@@ -76,65 +100,23 @@ install_targets() {
76100
done
77101
}
78102

79-
# Setup cargo config for Android NDK
80-
setup_cargo_config() {
81-
local ndk_home="${ANDROID_NDK_HOME:-$NDK_HOME}"
82-
local config_dir="${PROJECT_ROOT}/.cargo"
83-
local config_file="${config_dir}/config.toml"
84-
85-
mkdir -p "$config_dir"
86-
87-
# Detect host OS
88-
local host_os
89-
case "$OSTYPE" in
90-
linux*) host_os="linux" ;;
91-
darwin*) host_os="darwin" ;;
92-
*) host_os="linux" ;;
93-
esac
94-
95-
# Find the NDK toolchain
96-
local toolchain_dir="${ndk_home}/toolchains/llvm/prebuilt/${host_os}-x86_64"
97-
if [[ ! -d "$toolchain_dir" ]]; then
98-
log_error "NDK toolchain not found at: $toolchain_dir"
99-
return 1
100-
fi
101-
102-
local min_api=21
103-
104-
cat > "$config_file" << EOF
105-
# Auto-generated cargo config for Android NDK
106-
# NDK Path: $ndk_home
107-
108-
[target.aarch64-linux-android]
109-
ar = "${toolchain_dir}/bin/llvm-ar"
110-
linker = "${toolchain_dir}/bin/aarch64-linux-android${min_api}-clang"
111-
112-
[target.armv7-linux-androideabi]
113-
ar = "${toolchain_dir}/bin/llvm-ar"
114-
linker = "${toolchain_dir}/bin/armv7a-linux-androideabi${min_api}-clang"
115-
116-
[target.x86_64-linux-android]
117-
ar = "${toolchain_dir}/bin/llvm-ar"
118-
linker = "${toolchain_dir}/bin/x86_64-linux-android${min_api}-clang"
119-
120-
[target.i686-linux-android]
121-
ar = "${toolchain_dir}/bin/llvm-ar"
122-
linker = "${toolchain_dir}/bin/i686-linux-android${min_api}-clang"
123-
EOF
124-
125-
log_info "Cargo config written to: $config_file"
103+
# Build uniffi-bindgen for the host platform first
104+
build_uniffi_bindgen() {
105+
log_info "Building uniffi-bindgen for host platform..."
106+
cd "$PROJECT_ROOT"
107+
cargo build --features cli --bin uniffi-bindgen --release
126108
}
127109

128-
# Build for a specific target
129-
build_target() {
110+
# Build for a specific Android target using cargo-ndk
111+
build_android_target() {
130112
local target=$1
131113
log_info "Building for target: $target"
132114

133115
cd "$PROJECT_ROOT"
134-
cargo build --release --target "$target"
116+
cargo ndk --target "$target" --platform "$MIN_API" build --release
135117
}
136118

137-
# Generate Kotlin bindings
119+
# Generate Kotlin bindings using pre-built uniffi-bindgen
138120
generate_bindings() {
139121
log_info "Generating Kotlin bindings..."
140122

@@ -150,26 +132,51 @@ generate_bindings() {
150132
local candidate="${PROJECT_ROOT}/target/${target}/release/libcooklang_find.so"
151133
if [[ -f "$candidate" ]]; then
152134
lib_path="$candidate"
135+
log_info "Using library: $lib_path"
153136
break
154137
fi
155138
done
156139

157140
# Fall back to host target
158141
if [[ -z "$lib_path" ]]; then
142+
log_info "No Android library found, building for host..."
159143
cargo build --release
160144
lib_path="${PROJECT_ROOT}/target/release/libcooklang_find.so"
161145
if [[ ! -f "$lib_path" ]]; then
162146
lib_path="${PROJECT_ROOT}/target/release/libcooklang_find.dylib"
163147
fi
148+
if [[ ! -f "$lib_path" ]]; then
149+
log_error "Could not find built library"
150+
exit 1
151+
fi
164152
fi
165153

166-
cargo run --release --features cli --bin uniffi-bindgen -- generate \
154+
# Use the pre-built uniffi-bindgen binary
155+
local bindgen="${PROJECT_ROOT}/target/release/uniffi-bindgen"
156+
if [[ ! -f "$bindgen" ]]; then
157+
log_info "uniffi-bindgen not found, building it..."
158+
build_uniffi_bindgen
159+
fi
160+
161+
log_info "Running uniffi-bindgen..."
162+
"$bindgen" generate \
167163
--library "$lib_path" \
168164
--language kotlin \
169165
--config "${PROJECT_ROOT}/uniffi.toml" \
170166
--out-dir "$OUTPUT_DIR"
171167

168+
# Verify and show what was generated
172169
log_info "Kotlin bindings generated at: $OUTPUT_DIR"
170+
log_info "Generated files:"
171+
find "$OUTPUT_DIR" -name "*.kt" -type f
172+
173+
# Verify the expected structure exists
174+
if [[ ! -d "$OUTPUT_DIR/org" ]]; then
175+
log_error "Expected directory structure not created: $OUTPUT_DIR/org"
176+
log_error "Contents of $OUTPUT_DIR:"
177+
ls -la "$OUTPUT_DIR"
178+
exit 1
179+
fi
173180
}
174181

175182
# Create Android library structure
@@ -186,25 +193,15 @@ create_android_lib() {
186193
# Copy native libraries
187194
local copied=0
188195

189-
if [[ -f "${PROJECT_ROOT}/target/aarch64-linux-android/release/libcooklang_find.so" ]]; then
190-
cp "${PROJECT_ROOT}/target/aarch64-linux-android/release/libcooklang_find.so" "$jni_dir/arm64-v8a/"
191-
copied=$((copied + 1))
192-
fi
193-
194-
if [[ -f "${PROJECT_ROOT}/target/armv7-linux-androideabi/release/libcooklang_find.so" ]]; then
195-
cp "${PROJECT_ROOT}/target/armv7-linux-androideabi/release/libcooklang_find.so" "$jni_dir/armeabi-v7a/"
196-
copied=$((copied + 1))
197-
fi
198-
199-
if [[ -f "${PROJECT_ROOT}/target/x86_64-linux-android/release/libcooklang_find.so" ]]; then
200-
cp "${PROJECT_ROOT}/target/x86_64-linux-android/release/libcooklang_find.so" "$jni_dir/x86_64/"
201-
copied=$((copied + 1))
202-
fi
203-
204-
if [[ -f "${PROJECT_ROOT}/target/i686-linux-android/release/libcooklang_find.so" ]]; then
205-
cp "${PROJECT_ROOT}/target/i686-linux-android/release/libcooklang_find.so" "$jni_dir/x86/"
206-
copied=$((copied + 1))
207-
fi
196+
for target in "${ANDROID_TARGETS[@]}"; do
197+
local abi
198+
abi=$(get_abi_for_target "$target")
199+
local so_file="${PROJECT_ROOT}/target/${target}/release/libcooklang_find.so"
200+
if [[ -f "$so_file" ]]; then
201+
cp "$so_file" "$jni_dir/$abi/"
202+
copied=$((copied + 1))
203+
fi
204+
done
208205

209206
if [[ $copied -eq 0 ]]; then
210207
log_warn "No Android native libraries found to copy"
@@ -371,7 +368,7 @@ main() {
371368
echo "Usage: $0 [OPTIONS]"
372369
echo ""
373370
echo "Options:"
374-
echo " --all Build for all Android architectures"
371+
echo " --all Build for all Android architectures (requires cargo-ndk)"
375372
echo " --generate-only Only generate Kotlin bindings (no compilation)"
376373
echo " --help, -h Show this help message"
377374
echo ""
@@ -389,18 +386,21 @@ main() {
389386

390387
check_requirements
391388

389+
# Always build uniffi-bindgen first (before any cross-compilation)
390+
build_uniffi_bindgen
391+
392392
if [[ "$generate_only" == true ]]; then
393393
generate_bindings
394394
exit 0
395395
fi
396396

397397
if [[ "$build_all" == true ]]; then
398398
if check_android_ndk; then
399+
check_cargo_ndk
399400
install_targets
400-
setup_cargo_config
401401

402402
for target in "${ANDROID_TARGETS[@]}"; do
403-
build_target "$target"
403+
build_android_target "$target"
404404
done
405405
else
406406
log_info "Building for host platform only..."

0 commit comments

Comments
 (0)