Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

run.sh

v0.1.0-build.85 Latest Commit ede38f5

Size
7.7 KB
Lines
113
Language
shellscript
SHA-256
013136a4d427…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1#!/usr/bin/env bash
2# Host-JVM verification of the Android JNI bridge (no Android SDK/NDK needed).
3#
4# 1. Generates a Java mirror of CKeyNative.kt's `@JvmStatic external fun`s
5# and runs `javac -h` on it; ckey_jni.cpp is then compiled with that
6# header force-included, so any JNI name or parameter-type mismatch
7# between Kotlin and C++ is a compile error ("conflicting declaration").
8# 2. Builds libckey_jni.so for the host from the repository's core sources
9# (same file list and switches as src/main/cpp/CMakeLists.txt) and checks
10# every declared native method is exported.
11# 3. Runs the Java harness (encode -> render RGBA -> gray -> decode, styles,
12# exports, telemetry) against the mirror via System.load.
13# 4. If a Kotlin compiler is available (KOTLINC_JAR, or the one bundled with
14# Gradle), compiles the real Kotlin sources (against tiny android.* stubs),
15# reruns the harness against the Kotlin-compiled CKeyNative, runs the
16# Kotlin-API checks and the JUnit unit tests in ckey/src/test.
17set -euo pipefail
18HERE="$(cd "$(dirname "$0")" && pwd)"
19ANDROID_DIR="$(cd "$HERE/.." && pwd)"
20REPO="$(cd "$ANDROID_DIR/../.." && pwd)"
21OUT="${OUT:-$HERE/build}"
22JAVA_HOME="${JAVA_HOME:-$(dirname "$(dirname "$(readlink -f "$(command -v javac)")")")}"
23CXX="${CXX:-g++}"
24KT_SRC="$ANDROID_DIR/ckey/src/main/kotlin"
25JNI_SRC="$ANDROID_DIR/ckey/src/main/cpp/ckey_jni.cpp"
26rm -rf "$OUT" && mkdir -p "$OUT"
27
28echo "== 1. JNI signatures (Kotlin -> javac -h -> g++)"
29python3 "$HERE/gen_mirror.py" "$KT_SRC/org/openckey/ckey/CKeyNative.kt" "$OUT/mirror-src"
30"$JAVA_HOME/bin/javac" -d "$OUT/mirror" -h "$OUT/jni-h" \
31 "$OUT/mirror-src/org/openckey/ckey/CKeyNative.java" "$OUT/mirror-src/org/openckey/ckey/CKeyException.java"
32JNI_INC=(-I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -I"$REPO/include" -I"$REPO/src")
33CERTIFY=0
34SOURCES=("$REPO/src/ckey_codec.cpp" "$REPO/src/ckey_scan.cpp" "$REPO/src/ckey_render.cpp" "$REPO/src/telemetry.cpp")
35if [ -f "$REPO/src/ckey_certify.cpp" ]; then CERTIFY=1; SOURCES+=("$REPO/src/ckey_certify.cpp"); fi
36DEFS=(-DCKEY_JNI_SCAN=1 -DCKEY_JNI_RENDER=1 -DCKEY_JNI_TELEMETRY=1 -DCKEY_JNI_CERTIFY=$CERTIFY)
37"$CXX" -std=c++20 -Wall -Wextra -Werror -fsyntax-only "${JNI_INC[@]}" "${DEFS[@]}" \
38 -include "$OUT/jni-h/org_openckey_ckey_CKeyNative.h" "$JNI_SRC"
39for variant in "-DCKEY_JNI_SCAN=0" "-DCKEY_JNI_RENDER=0" "-DCKEY_JNI_TELEMETRY=0"; do
40 "$CXX" -std=c++20 -Wall -Wextra -Werror -fsyntax-only "${JNI_INC[@]}" $variant "$JNI_SRC"
41done
42echo "signatures match; module-off variants compile"
43
44echo "== 2. host libckey_jni.so"
45mkdir -p "$OUT/obj"
46OBJS=()
47for s in "${SOURCES[@]}"; do
48 o="$OUT/obj/$(basename "$s" .cpp).o"
49 "$CXX" -std=c++20 -O2 -fPIC -fvisibility=hidden -Wno-unused-function -I"$REPO/include" -I"$REPO/src" -c "$s" -o "$o" &
50 OBJS+=("$o")
51done
52"$CXX" -std=c++20 -O2 -fPIC -fvisibility=hidden -Wall -Wextra "${JNI_INC[@]}" "${DEFS[@]}" -c "$JNI_SRC" -o "$OUT/obj/ckey_jni.o" &
53wait
54"$CXX" -shared -o "$OUT/libckey_jni.so" "$OUT/obj/ckey_jni.o" "${OBJS[@]}" \
55 -Wl,--version-script="$ANDROID_DIR/ckey/src/main/cpp/ckey_jni.map" -Wl,--gc-sections -pthread
56declared=$(grep -o 'native [^ ]* [A-Za-z]*(' "$OUT/mirror-src/org/openckey/ckey/CKeyNative.java" | awk '{print $3}' | tr -d '(' | sort)
57exported=$(nm -D --defined-only "$OUT/libckey_jni.so" | awk '{print $3}' | grep '^Java_' | sed 's/^Java_org_openckey_ckey_CKeyNative_//' | sort)
58if [ "$declared" != "$exported" ]; then
59 echo "declared vs exported native methods differ:"; diff <(echo "$declared") <(echo "$exported") || true; exit 1
60fi
61others=$(nm -D --defined-only "$OUT/libckey_jni.so" | awk '{print $3}' | grep -v -e '^Java_' -e '^JNI_OnLoad$' || true)
62[ -z "$others" ] || { echo "unexpected exports: $others"; exit 1; }
63echo "exports: JNI_OnLoad + $(echo "$exported" | wc -l) native methods, nothing else (certify=$CERTIFY)"
64
65echo "== 3. Java harness against the generated mirror"
66"$JAVA_HOME/bin/javac" -cp "$OUT/mirror" -d "$OUT/harness" "$HERE/java/HostHarness.java"
67"$JAVA_HOME/bin/java" -Xcheck:jni -Dorg.openckey.ckey.library="$OUT/libckey_jni.so" -Dorg.openckey.ckey.vectors="$REPO/tests/vectors/safe-actions-v1.json" \
68 -cp "$OUT/mirror:$OUT/harness" HostHarness 2>&1 | grep -v -e '^WARNING' -e '^Picked up' | tail -n 3
69
70echo "== 4. Kotlin sources"
71if [ -z "${KOTLINC_JAR:-}" ]; then
72 GRADLE_LIB="$(dirname "$(readlink -f "$(command -v gradle 2>/dev/null || echo /nonexistent)")")/../lib"
73 KOTLINC_JAR="$(ls "$GRADLE_LIB"/kotlin-compiler-embeddable-*.jar 2>/dev/null | head -n1 || true)"
74fi
75if [ -z "$KOTLINC_JAR" ] || [ ! -f "$KOTLINC_JAR" ]; then
76 echo "skip: no Kotlin compiler (set KOTLINC_JAR or put Gradle on PATH)"; exit 0
77fi
78LIB="$(dirname "$KOTLINC_JAR")"
79# Optional jars may be missing from a Gradle distribution; never fail on that.
80jar_of() { { ls "$LIB"/$1-*.jar 2>/dev/null || true; } | head -n1; }
81KCP="$KOTLINC_JAR:$(jar_of kotlin-stdlib):$(jar_of kotlin-script-runtime):$(jar_of kotlin-reflect):$(jar_of kotlin-daemon-embeddable):$(jar_of trove4j):$(jar_of annotations):$(jar_of kotlinx-coroutines-core-jvm)"
82STDLIB="$(jar_of kotlin-stdlib)"
83JUNIT="$(jar_of junit):$(jar_of hamcrest-core)"
84kotlinc() { "$JAVA_HOME/bin/java" -cp "$KCP" org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -no-stdlib -no-reflect -jvm-target 17 "$@"; }
85"$JAVA_HOME/bin/javac" -d "$OUT/stubs" $(find "$HERE/stubs" -name '*.java')
86# Everything in the core module except CKeyView.kt (android.view).
87MAIN_KT=$(find "$KT_SRC" -name '*.kt' ! -name 'CKeyView.kt')
88kotlinc -module-name ckey_release -cp "$STDLIB:$OUT/stubs" -d "$OUT/kotlin-main" $MAIN_KT 2>&1 | grep -v '^warning' || true
89[ -f "$OUT/kotlin-main/org/openckey/ckey/CKeyNative.class" ] || { echo "Kotlin compile failed"; exit 1; }
90# The Kotlin-compiled class must have exactly the mirror's static native methods and descriptors.
91natives() {
92 "$JAVA_HOME/bin/javap" -cp "$1" -s org.openckey.ckey.CKeyNative 2>/dev/null > "$OUT/javap.txt"
93 if grep ' native ' "$OUT/javap.txt" | grep -qv ' static '; then echo "non-static native method in $1" >&2; return 1; fi
94 grep -A1 ' native ' "$OUT/javap.txt" | grep -o -e '[A-Za-z0-9_]*(' -e 'descriptor: .*' | paste - - | sort
95}
96natives "$OUT/kotlin-main" > "$OUT/natives-kotlin.txt"
97natives "$OUT/mirror" > "$OUT/natives-mirror.txt"
98diff "$OUT/natives-mirror.txt" "$OUT/natives-kotlin.txt" || { echo "Kotlin and mirror native descriptors differ"; exit 1; }
99echo "Kotlin CKeyNative: $(wc -l < "$OUT/natives-kotlin.txt") static native methods, descriptors identical to the mirror"
100RUN_CP="$STDLIB:$OUT/stubs:$OUT/kotlin-main"
101"$JAVA_HOME/bin/javac" -cp "$RUN_CP" -d "$OUT/harness-kt" "$HERE/java/HostHarness.java" "$HERE/java/KotlinApiCheck.java"
102echo "-- harness against the Kotlin-compiled CKeyNative"
103"$JAVA_HOME/bin/java" -Xcheck:jni -Dorg.openckey.ckey.library="$OUT/libckey_jni.so" -Dorg.openckey.ckey.vectors="$REPO/tests/vectors/safe-actions-v1.json" -cp "$RUN_CP:$OUT/harness-kt" HostHarness 2>&1 | grep -v -e '^WARNING' -e '^Picked up' | tail -n 1
104echo "-- Kotlin public API from Java"
105"$JAVA_HOME/bin/java" -Xcheck:jni -Dorg.openckey.ckey.library="$OUT/libckey_jni.so" -Dorg.openckey.ckey.vectors="$REPO/tests/vectors/safe-actions-v1.json" -cp "$RUN_CP:$OUT/harness-kt" KotlinApiCheck 2>&1 | grep -v -e '^WARNING' -e '^Picked up' | tail -n 4
106echo "-- JUnit unit tests (ckey/src/test)"
107kotlinc -module-name ckey_test -Xfriend-paths="$OUT/kotlin-main" -cp "$STDLIB:$OUT/stubs:$OUT/kotlin-main:$JUNIT" -d "$OUT/kotlin-test" \
108 $(find "$ANDROID_DIR/ckey/src/test" -name '*.kt') 2>&1 | grep -v '^warning' || true
109[ -d "$OUT/kotlin-test" ] || { echo "Kotlin test compile failed"; exit 1; }
110TESTS=$(cd "$OUT/kotlin-test" && find . -name '*Test.class' | sed 's|^\./||; s|\.class$||; s|/|.|g' | sort)
111"$JAVA_HOME/bin/java" -cp "$RUN_CP:$OUT/kotlin-test:$JUNIT" org.junit.runner.JUnitCore $TESTS > "$OUT/junit.txt" 2>&1 \
112 || { cat "$OUT/junit.txt"; exit 1; }
113grep -E '^OK' "$OUT/junit.txt"