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
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | #! |
| 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. |
| 17 | set -euo pipefail |
| 18 | HERE="$(cd "$(dirname "$0")" && pwd)" |
| 19 | ANDROID_DIR="$(cd "$HERE/.." && pwd)" |
| 20 | REPO="$(cd "$ANDROID_DIR/../.." && pwd)" |
| 21 | OUT="${OUT:-$HERE/build}" |
| 22 | JAVA_HOME="${JAVA_HOME:-$(dirname "$(dirname "$(readlink -f "$(command -v javac)")")")}" |
| 23 | CXX="${CXX:-g++}" |
| 24 | KT_SRC="$ANDROID_DIR/ckey/src/main/kotlin" |
| 25 | JNI_SRC="$ANDROID_DIR/ckey/src/main/cpp/ckey_jni.cpp" |
| 26 | rm -rf "$OUT" && mkdir -p "$OUT" |
| 27 | |
| 28 | echo "== 1. JNI signatures (Kotlin -> javac -h -> g++)" |
| 29 | python3 "$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" |
| 32 | JNI_INC=(-I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -I"$REPO/include" -I"$REPO/src") |
| 33 | CERTIFY=0 |
| 34 | SOURCES=("$REPO/src/ckey_codec.cpp" "$REPO/src/ckey_scan.cpp" "$REPO/src/ckey_render.cpp" "$REPO/src/telemetry.cpp") |
| 35 | if [ -f "$REPO/src/ckey_certify.cpp" ]; then CERTIFY=1; SOURCES+=("$REPO/src/ckey_certify.cpp"); fi |
| 36 | DEFS=(-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" |
| 39 | for 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" |
| 41 | done |
| 42 | echo "signatures match; module-off variants compile" |
| 43 | |
| 44 | echo "== 2. host libckey_jni.so" |
| 45 | mkdir -p "$OUT/obj" |
| 46 | OBJS=() |
| 47 | for 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") |
| 51 | done |
| 52 | "$CXX" -std=c++20 -O2 -fPIC -fvisibility=hidden -Wall -Wextra "${JNI_INC[@]}" "${DEFS[@]}" -c "$JNI_SRC" -o "$OUT/obj/ckey_jni.o" & |
| 53 | wait |
| 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 |
| 56 | declared=$(grep -o 'native [^ ]* [A-Za-z]*(' "$OUT/mirror-src/org/openckey/ckey/CKeyNative.java" | awk '{print $3}' | tr -d '(' | sort) |
| 57 | exported=$(nm -D --defined-only "$OUT/libckey_jni.so" | awk '{print $3}' | grep '^Java_' | sed 's/^Java_org_openckey_ckey_CKeyNative_//' | sort) |
| 58 | if [ "$declared" != "$exported" ]; then |
| 59 | echo "declared vs exported native methods differ:"; diff <(echo "$declared") <(echo "$exported") || true; exit 1 |
| 60 | fi |
| 61 | others=$(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; } |
| 63 | echo "exports: JNI_OnLoad + $(echo "$exported" | wc -l) native methods, nothing else (certify=$CERTIFY)" |
| 64 | |
| 65 | echo "== 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 | |
| 70 | echo "== 4. Kotlin sources" |
| 71 | if [ -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)" |
| 74 | fi |
| 75 | if [ -z "$KOTLINC_JAR" ] || [ ! -f "$KOTLINC_JAR" ]; then |
| 76 | echo "skip: no Kotlin compiler (set KOTLINC_JAR or put Gradle on PATH)"; exit 0 |
| 77 | fi |
| 78 | LIB="$(dirname "$KOTLINC_JAR")" |
| 79 | # Optional jars may be missing from a Gradle distribution; never fail on that. |
| 80 | jar_of() { { ls "$LIB"/$1-*.jar 2>/dev/null || true; } | head -n1; } |
| 81 | KCP="$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)" |
| 82 | STDLIB="$(jar_of kotlin-stdlib)" |
| 83 | JUNIT="$(jar_of junit):$(jar_of hamcrest-core)" |
| 84 | kotlinc() { "$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). |
| 87 | MAIN_KT=$(find "$KT_SRC" -name '*.kt' ! -name 'CKeyView.kt') |
| 88 | kotlinc -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. |
| 91 | natives() { |
| 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 | } |
| 96 | natives "$OUT/kotlin-main" > "$OUT/natives-kotlin.txt" |
| 97 | natives "$OUT/mirror" > "$OUT/natives-mirror.txt" |
| 98 | diff "$OUT/natives-mirror.txt" "$OUT/natives-kotlin.txt" || { echo "Kotlin and mirror native descriptors differ"; exit 1; } |
| 99 | echo "Kotlin CKeyNative: $(wc -l < "$OUT/natives-kotlin.txt") static native methods, descriptors identical to the mirror" |
| 100 | RUN_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" |
| 102 | echo "-- 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 |
| 104 | echo "-- 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 |
| 106 | echo "-- JUnit unit tests (ckey/src/test)" |
| 107 | kotlinc -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; } |
| 110 | TESTS=$(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; } |
| 113 | grep -E '^OK' "$OUT/junit.txt" |