| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 1 | # The APK, as derivations rather than as a buck2 graph. |
| 2 | # |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 3 | # android/BUCK builds the same things, and does it better for a person at |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 4 | # a terminal: it is incremental, and it fetches jolt-native's release by digest |
| 5 | # rather than rebuilding the world. Nothing here replaces that. What this adds |
| 6 | # is the other build — from nothing, on a |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 7 | # machine with no Android SDK, no NDK, no hand-built Chez cross target and no |
| 8 | # ~/.cache at all: |
| 9 | # |
| 10 | # nix build .#apk |
| 11 | # |
| 12 | # On a machine with a remote builder configured, prefer |
| 13 | # |
| 14 | # nix build .#apk --store ssh-ng://eu.nixbuild.net --eval-store auto |
| 15 | # |
| 16 | # rather than letting `builders` do it. With `builders`, nix copies the output |
| 17 | # of every remotely-built derivation back, and androidenv's NDK is both |
| 18 | # `preferLocalBuild` and absent from cache.nixos.org — so the 3.1 GB unpacked |
| 19 | # toolchain is built here and uploaded. With the remote as the *store* the |
| 20 | # whole graph stays there, only .drv files go up, and the builder fetches |
| 21 | # Google's zip over its own link. |
| 22 | # |
| 23 | # Every path .buckconfig.local answers for is answered here by the store |
| 24 | # instead. The steps are in the same order and do the same work; where a |
| 25 | # genrule read `read_root_config`, a derivation takes an argument. |
| 26 | { pkgs, lib, self, chez-src, jolt-native, glimmer, joltAndroid, androidSdk, ndk }: |
| 27 | |
| 28 | let |
| 29 | # What the APK targets, in the three spellings the tools want it in. |
| 30 | apiLevel = "28"; |
| 31 | targetSdk = "36"; |
| 32 | abi = "arm64-v8a"; |
| 33 | package = "uk.nandi.frq"; |
| 34 | version = "0.1.0"; |
| 35 | |
| 36 | sdk = "${androidSdk}/libexec/android-sdk"; |
| 37 | buildTools = "${sdk}/build-tools/36.0.0"; |
| 38 | androidJar = "${sdk}/platforms/android-${targetSdk}/android.jar"; |
| 39 | |
| 40 | # The NDK's clang finds its sysroot, resource directory and the rest of LLVM |
| 41 | # relative to itself, so it is named by path rather than copied anywhere. |
| 42 | # androidenv installs the tree under libexec/android-sdk and leaves |
| 43 | # `ndk-bundle` pointing at the versioned directory beside it. |
| 44 | ndkRoot = "${ndk}/libexec/android-sdk/ndk-bundle"; |
| 45 | ndkBin = "${ndkRoot}/toolchains/llvm/prebuilt/linux-x86_64/bin"; |
| 46 | cc = "${ndkBin}/aarch64-linux-android${apiLevel}-clang"; |
| 47 | |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 48 | # What comes out of jolt-native's releases, by the digests scripts/*.dotslash |
| 49 | # pin. Same bytes buck fetches; DotSlash's `digest` is over the archive, |
| 50 | # which is what fetchurl hashes too. |
| 51 | # |
| 52 | # One archive, two libraries: libvidya (the retained-tree UI) and libjoltmoq |
| 53 | # (the AV media plane). They are built together and only make sense together |
| 54 | # — libjoltapp links both — so there is one pin for the pair rather than two |
| 55 | # that could drift apart. |
| 56 | nativeRelease = pkgs.fetchurl { |
| 57 | url = "https://gitlab.com/nandithebull/jolt-native/-/releases/v0.1.3/downloads/jolt-native-android-arm64-v0.1.3.tar.gz"; |
| 58 | sha256 = "4519745bae6db9a791a71b38a26478879e246166802b0a48dbf7d45e4d45a108"; |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 59 | }; |
| 60 | |
| 61 | glue = pkgs.fetchurl { |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 62 | url = "https://gitlab.com/nandithebull/jolt-native/-/releases/v0.1.3/downloads/jolt-native-android-glue-v0.1.3.tar.gz"; |
| 63 | sha256 = "83313eda124f2a0cfff6827cf4473600c1f71db2f208d654b97068a85af38da5"; |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 64 | }; |
| 65 | |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 66 | # Unpacked once, so the consumers below name files rather than repeat the tar. |
| 67 | nativeLibs = pkgs.runCommand "jolt-native-android" { } '' |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 68 | mkdir -p "$out" |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 69 | tar -xzf ${nativeRelease} -C "$out" |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 70 | ''; |
| 71 | |
| 72 | glueSrc = pkgs.runCommand "jolt-android-glue" { } '' |
| 73 | mkdir -p "$out" |
| 74 | tar -xzf ${glue} -C "$out" --strip-components=1 |
| 75 | ''; |
| 76 | |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 77 | # The C++ runtime, out of the same NDK the glue is compiled with. |
| 78 | # |
| 79 | # openh264 is C++, and its build script asks to be linked against |
| 80 | # `libc++_shared.so` by name — so libjoltmoq carries that as a DT_NEEDED. An |
| 81 | # app's linker namespace will not hand out the platform's own copy (there is |
| 82 | # no stable one to hand out), so the APK carries it, exactly as it carries |
| 83 | # OpenSSL below and for the same reason. |
| 84 | libcxx = "${ndkRoot}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so"; |
| 85 | |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 86 | # --- Chez's arm64 cross target ------------------------------------------ |
| 87 | # The one piece with no nixpkgs equivalent: `pkgs.chez` builds a Scheme for |
| 88 | # this machine, and what the boot image needs is Chez's `tarm64le` workarea — |
| 89 | # the target boot files, the cross compiler's xpatch, and the arm64 |
| 90 | # libkernel.a that libjoltapp links. |
| 91 | # |
| 92 | # Three builds in one derivation, because each needs the one before it: |
| 93 | # |
| 94 | # ta6le a host Scheme, which is what cross-compiles anything |
| 95 | # boot XM=... the target's boot files and xpatch, made by that host |
| 96 | # tarm64le the target kernel, compiled by the NDK |
| 97 | # |
| 98 | # The flags are the ones the hand-built tree under ~/.cache/vidya-chez-android |
| 99 | # was configured with, read back out of its Mf-config. zlib is Android's own |
| 100 | # (`-lz`, which Bionic has); lz4 is the in-tree submodule, built for arm64 |
| 101 | # here because a cross configure links it rather than building it. |
| 102 | chezAndroid = pkgs.stdenv.mkDerivation { |
| 103 | pname = "chez-scheme-android"; |
| 104 | version = "10.4.1"; |
| 105 | src = chez-src; |
| 106 | |
| 107 | strictDeps = true; |
| 108 | nativeBuildInputs = with pkgs; [ gnumake which ]; |
| 109 | |
| 110 | dontConfigure = true; |
| 111 | |
| 112 | buildPhase = '' |
| 113 | runHook preBuild |
| 114 | |
| 115 | # Both workareas turn off the expression editor's two dependencies, which |
| 116 | # is what ~/.cache/vidya-chez-android was configured with (its Mf-config |
| 117 | # has empty cursesLib/ncursesLib, and disablex11=yes on the target). The |
| 118 | # host Scheme here is only ever a cross compiler, and Bionic has no |
| 119 | # curses.h at all — so on the target it is not a preference but a |
| 120 | # requirement. |
| 121 | ./configure -m=ta6le --disable-x11 --disable-curses CC_FOR_BUILD="$CC" |
| 122 | make -j"$NIX_BUILD_CORES" |
| 123 | make boot XM=tarm64le -j"$NIX_BUILD_CORES" |
| 124 | |
| 125 | # lz4 for the phone, not for this machine: the host build above left an |
| 126 | # x86_64 liblz4.a in the same place, and the cross link needs it gone. |
| 127 | make -C lz4/lib clean |
| 128 | make -C lz4/lib liblz4.a -j"$NIX_BUILD_CORES" \ |
| 129 | CC=${cc} AR=${ndkBin}/llvm-ar |
| 130 | |
| 131 | # --disable-auto-flags stops configure appending -lrt and -lpthread, which |
| 132 | # is what its unix branch does for a glibc host and what Bionic has no |
| 133 | # separate libraries for — both live in libc there. Everything it would |
| 134 | # otherwise add is passed explicitly below, matching the Mf-config of |
| 135 | # the tree this was reconstructed from. |
| 136 | ./configure -m=tarm64le --cross --disable-x11 --disable-curses \ |
| 137 | --disable-auto-flags \ |
| 138 | LIBS="-ldl -lm" \ |
| 139 | CC=${cc} \ |
| 140 | AR=${ndkBin}/llvm-ar \ |
| 141 | CC_FOR_BUILD="$CC" \ |
| 142 | ZLIB=-lz \ |
| 143 | LZ4="$PWD/lz4/lib/liblz4.a" \ |
| 144 | CPPFLAGS="-I$PWD/lz4/lib" \ |
| 145 | CFLAGS="-O2 -D_REENTRANT -pthread -fPIC" |
| 146 | make -j"$NIX_BUILD_CORES" |
| 147 | |
| 148 | runHook postBuild |
| 149 | ''; |
| 150 | |
| 151 | # The whole workarea, at the paths CHEZ_ANDROID means: the host Scheme |
| 152 | # loads xpatch out of xc-tarm64le/s, and the link below reads two archives |
| 153 | # from elsewhere in the tree. Pruning it would only be guessing at which |
| 154 | # of those the cross compiler still opens. |
| 155 | installPhase = '' |
| 156 | runHook preInstall |
| 157 | mkdir -p "$out" |
| 158 | cp -r . "$out/" |
| 159 | runHook postInstall |
| 160 | ''; |
| 161 | |
| 162 | # A Scheme built for this machine and a kernel built for another one; the |
| 163 | # usual fixups have an opinion about both, and neither wants it. |
| 164 | dontStrip = true; |
| 165 | dontPatchELF = true; |
| 166 | }; |
| 167 | |
| 168 | hostScheme = "${chezAndroid}/ta6le/bin/ta6le/scheme"; |
| 169 | targetBoot = "${chezAndroid}/boot/tarm64le"; |
| 170 | xpatch = "${chezAndroid}/xc-tarm64le/s/xpatch"; |
| 171 | |
| 172 | # --- the Jolt half -------------------------------------------------------- |
| 173 | # frq's Scheme, cross-compiled to an arm64 boot image. android/build-jolt-boot.bb |
| 174 | # does this by hand-writing a deps.edn of :paths and then driving Chez; so |
| 175 | # does this, for the same reason — there is no dependency resolution inside a |
| 176 | # cross compile, so every source root deps.edn would have resolved is named. |
| 177 | # |
| 178 | # The jolt that runs it is the fork, not upstream and not the one the desktop |
| 179 | # package builds: upstream reads the socket address out of `struct addrinfo` |
| 180 | # at glibc's offset, which on Bionic is `ai_canonname`, and an APK built with |
| 181 | # it cannot open a TLS connection at all. |
| 182 | joltBoot = pkgs.stdenv.mkDerivation { |
| 183 | pname = "frq-jolt-boot"; |
| 184 | inherit version; |
| 185 | |
| 186 | dontUnpack = true; |
| 187 | strictDeps = true; |
| 188 | nativeBuildInputs = [ joltAndroid ]; |
| 189 | |
| 190 | buildPhase = '' |
| 191 | runHook preBuild |
| 192 | |
| 193 | export HOME="$TMPDIR" |
| 194 | mkdir -p project cross |
| 195 | |
| 196 | cat > project/deps.edn <<EOF |
| 197 | {:paths ["${self}/src" "${glimmer}/src" "${jolt-native}/jolt/glimmer-vidya/src"]} |
| 198 | EOF |
| 199 | |
| 200 | # The flat build, which is the one shape make-boot-file can take. |
| 201 | ( cd project && JOLT_NO_FLAT_SPLIT=1 jolt build -m frq.app -o app ) |
| 202 | |
| 203 | cat > cross/compile.ss <<EOF |
| 204 | (import (chezscheme)) |
| 205 | (load "${xpatch}") |
| 206 | (optimize-level 2) |
| 207 | (generate-inspector-information #f) |
| Ship the APK smaller: strip the libraries, pack the image 7434fee nandi 18d ago | 208 | ;; Packed harder, not packed for the first time: fasl output is |
| 209 | ;; compressed already, but with lz4 at its fastest setting, and on |
| 210 | ;; this image that leaves 2.3 MB on the table. What reads it back is |
| 211 | ;; the kernel linked into libjoltapp, which has zlib because |
| 212 | ;; chezAndroid is configured ZLIB=-lz — so nothing extra ships to |
| 213 | ;; decompress it. |
| 214 | ;; |
| 215 | ;; Less than the ratio of the whole file suggests (15.9 MB to 13.6): |
| 216 | ;; compression is per fasl entry rather than over the image. |
| 217 | (fasl-compressed #t) |
| 218 | (compress-format 'gzip) |
| 219 | (compress-level 'maximum) |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 220 | (compile-file "$PWD/project/app.build/flat.ss" "$PWD/cross/flat.so") |
| 221 | (make-boot-file "$PWD/jolt.boot" '() |
| 222 | "${targetBoot}/petite.boot" |
| 223 | "${targetBoot}/scheme.boot" |
| 224 | "$PWD/cross/flat.so") |
| 225 | EOF |
| 226 | |
| 227 | SCHEMEHEAPDIRS="${chezAndroid}/ta6le/boot/ta6le" \ |
| 228 | ${hostScheme} --script cross/compile.ss |
| 229 | |
| 230 | runHook postBuild |
| 231 | ''; |
| 232 | |
| 233 | # scheme.h travels with the image because jolt_main.c includes it. |
| 234 | installPhase = '' |
| 235 | runHook preInstall |
| 236 | mkdir -p "$out" |
| 237 | cp jolt.boot "$out/jolt.boot" |
| 238 | cp ${targetBoot}/scheme.h "$out/scheme.h" |
| 239 | runHook postInstall |
| 240 | ''; |
| 241 | }; |
| 242 | |
| 243 | # The image travels as a blob in an object file's data section. The |
| 244 | # _binary_jolt_boot_{start,end} symbols jolt_main.c reads are named after the |
| 245 | # input *path*, so this copies the file somewhere it is called exactly |
| 246 | # `jolt.boot` before converting it. |
| 247 | joltBootObj = pkgs.runCommand "jolt-boot-obj" { } '' |
| 248 | cp ${joltBoot}/jolt.boot jolt.boot |
| 249 | ${ndkBin}/llvm-objcopy \ |
| 250 | --input-target=binary --output-target=elf64-littleaarch64 \ |
| 251 | --binary-architecture=aarch64 jolt.boot "$out" |
| 252 | ''; |
| 253 | |
| 254 | # The glue: jolt-native's jolt_main.c over the boot image, linked against |
| 255 | # libvidya by name. --no-undefined is what makes a symbol the Scheme side |
| 256 | # registers but the ABI no longer exports a build failure here rather than a |
| 257 | # crash on the phone. |
| 258 | libjoltapp = pkgs.runCommand "libjoltapp.so" { } '' |
| 259 | mkdir -p lib |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 260 | cp ${nativeLibs}/libvidya.so lib/libvidya.so |
| 261 | cp ${nativeLibs}/libjoltmoq.so lib/libjoltmoq.so |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 262 | |
| 263 | ${cc} -shared -fPIC -O2 -o "$out" \ |
| 264 | ${glueSrc}/android/jolt_main.c \ |
| 265 | ${joltBootObj} \ |
| 266 | -I${joltBoot} \ |
| 267 | -I${glueSrc}/include \ |
| 268 | -Llib \ |
| 269 | ${chezAndroid}/tarm64le/boot/tarm64le/libkernel.a \ |
| 270 | ${chezAndroid}/lz4/lib/liblz4.a \ |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 271 | -lvidya -ljoltmoq -landroid -llog -lz -ldl -lm -Wl,--no-undefined |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 272 | ''; |
| 273 | |
| 274 | # --- the Java half -------------------------------------------------------- |
| 275 | # One class: the photo chooser's result has to land somewhere, and native |
| 276 | # code is not somewhere. |
| 277 | classesDex = pkgs.runCommand "classes.dex" |
| 278 | { |
| 279 | nativeBuildInputs = [ pkgs.jdk17 ]; |
| 280 | } '' |
| 281 | mkdir -p classes out |
| 282 | # -encoding, because a build sandbox has no locale and javac then reads |
| 283 | # the source as US-ASCII — on which the comments' em dashes are errors. |
| 284 | javac --release 17 -encoding UTF-8 --class-path ${androidJar} -d classes \ |
| 285 | $(find ${self}/android/java -name '*.java') |
| 286 | ${buildTools}/d8 --min-api ${apiLevel} --output out $(find classes -name '*.class') |
| 287 | cp out/classes.dex "$out" |
| 288 | ''; |
| 289 | |
| 290 | # --- the package ---------------------------------------------------------- |
| 291 | # OpenSSL travels with the app because the platform's own is not ours to |
| 292 | # load: an app's linker namespace refuses /system/lib64/libssl.so, and |
| 293 | # without one there is no TLS on the phone at all. |
| 294 | # Built by the NDK rather than by pkgsCross.aarch64-android: that cross |
| 295 | # stdenv cannot build its own compiler-rt on this nixpkgs — os_version_check.c |
| 296 | # includes <pthread.h> and the sysroot it is handed has no such header — and |
| 297 | # an APK has no use for a second toolchain anyway. OpenSSL's own android-arm64 |
| 298 | # target wants the NDK's llvm on PATH and takes the API level from the flag. |
| 299 | # The version is the one ~/.cache/frq-openssl-android was built from. |
| 300 | opensslAndroid = pkgs.stdenv.mkDerivation { |
| 301 | pname = "openssl-android"; |
| 302 | version = "3.5.4"; |
| 303 | |
| 304 | src = pkgs.fetchurl { |
| 305 | url = "https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz"; |
| 306 | sha256 = "16ay6ppxsky3qhg6573370iz93kihfwx9n5ipmlnjcam97w12wwn"; |
| 307 | }; |
| 308 | |
| 309 | strictDeps = true; |
| 310 | nativeBuildInputs = with pkgs; [ perl ]; |
| 311 | |
| 312 | configurePhase = '' |
| 313 | runHook preConfigure |
| 314 | export ANDROID_NDK_ROOT="${ndkRoot}" |
| 315 | export PATH="${ndkBin}:$PATH" |
| 316 | # Through perl rather than as a program: its shebang is /usr/bin/env, |
| 317 | # which a build sandbox does not have. |
| 318 | perl ./Configure android-arm64 -D__ANDROID_API__=${apiLevel} \ |
| 319 | shared no-tests no-docs \ |
| 320 | --prefix="$out" --openssldir="$out/etc/ssl" |
| 321 | runHook postConfigure |
| 322 | ''; |
| 323 | |
| 324 | # install_sw, not install: the rest of an OpenSSL install is for a machine |
| 325 | # that runs it, and this one only ships two .so files into an APK. |
| 326 | installTargets = [ "install_sw" ]; |
| 327 | |
| 328 | dontStrip = true; |
| 329 | dontPatchELF = true; |
| 330 | }; |
| 331 | |
| 332 | # The libraries are stored rather than deflated: the loader maps them |
| 333 | # straight out of the APK. The dex is read rather than mapped, so it may as |
| 334 | # well compress. |
| 335 | apkUnsigned = pkgs.runCommand "frq-unsigned.apk" |
| 336 | { |
| 337 | nativeBuildInputs = [ pkgs.zip ]; |
| 338 | } '' |
| 339 | mkdir -p stage/lib/${abi} |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 340 | cp ${nativeLibs}/libvidya.so stage/lib/${abi}/libvidya.so |
| 341 | cp ${nativeLibs}/libjoltmoq.so stage/lib/${abi}/libjoltmoq.so |
| 342 | cp ${libcxx} stage/lib/${abi}/libc++_shared.so |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 343 | cp ${libjoltapp} stage/lib/${abi}/libjoltapp.so |
| 344 | cp ${opensslAndroid.out}/lib/libssl.so stage/lib/${abi}/libssl.so |
| 345 | cp ${opensslAndroid.out}/lib/libcrypto.so stage/lib/${abi}/libcrypto.so |
| 346 | cp ${classesDex} stage/classes.dex |
| 347 | chmod -R u+w stage |
| 348 | |
| Ship the APK smaller: strip the libraries, pack the image 7434fee nandi 18d ago | 349 | # Everything the loader needs is in .dynsym, and that is what --strip-all |
| 350 | # keeps: what goes is .symtab and the debug sections, which are read by a |
| 351 | # debugger and by nothing on the phone. Worth about a third of the package |
| 352 | # — libjoltmoq and libc++_shared are most of it, and the release libraries |
| 353 | # arrive unstripped because jolt-native's own build does not strip them. |
| 354 | # |
| 355 | # Here rather than in the derivations that produce them: the inputs stay |
| 356 | # whole (a stripped libjoltapp is a worse thing to hand a debugger, and |
| 357 | # `nix build .#libjoltapp` is how it is looked at), and this is the one |
| 358 | # place that knows the difference between an object and a shipped one. |
| 359 | # The NDK's, not nixpkgs' — the host strip has no opinion worth trusting |
| 360 | # about an arm64 object. |
| 361 | ${ndkBin}/llvm-strip --strip-all stage/lib/${abi}/*.so |
| 362 | |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 363 | ${buildTools}/aapt2 link -o "$out" -I ${androidJar} \ |
| 364 | --manifest ${self}/android/AndroidManifest.xml \ |
| 365 | --min-sdk-version ${apiLevel} --target-sdk-version ${targetSdk} \ |
| 366 | --version-code 1 --version-name ${version} |
| 367 | |
| 368 | ( cd stage && \ |
| Ask for the camera and the microphone from the app 6c745df nandi 18d ago | 369 | zip -q -0 "$out" lib/${abi}/libvidya.so lib/${abi}/libjoltmoq.so \ |
| 370 | lib/${abi}/libc++_shared.so lib/${abi}/libjoltapp.so \ |
| Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago | 371 | lib/${abi}/libssl.so lib/${abi}/libcrypto.so && \ |
| 372 | zip -q "$out" classes.dex ) |
| 373 | ''; |
| 374 | |
| 375 | # Aligned and signed with a debug key. The key is generated here rather than |
| 376 | # read from ~/.android, which is the one place this build is deliberately |
| 377 | # not the buck one: a keystore outside the store would make the output |
| 378 | # depend on the machine, and a release key has no business in the store at |
| 379 | # all. So this output is installable and not reproducible — keytool stamps |
| 380 | # the certificate with the time — and anything meant for a store should be |
| 381 | # signed from .#apk-unsigned instead. |
| 382 | apk = pkgs.runCommand "frq-${version}.apk" |
| 383 | { |
| 384 | nativeBuildInputs = [ pkgs.jdk17 ]; |
| 385 | meta = { |
| 386 | description = "frq for Android, debug-signed"; |
| 387 | platforms = [ "x86_64-linux" ]; |
| 388 | }; |
| 389 | } '' |
| 390 | export HOME="$TMPDIR" |
| 391 | keytool -genkeypair -keystore debug.keystore \ |
| 392 | -storepass android -keypass android -alias androiddebugkey \ |
| 393 | -keyalg RSA -keysize 2048 -validity 10000 \ |
| 394 | -dname 'CN=Android Debug,O=Android,C=US' |
| 395 | |
| 396 | ${buildTools}/zipalign -f -p 4 ${apkUnsigned} aligned.apk |
| 397 | ${buildTools}/apksigner sign --ks debug.keystore \ |
| 398 | --ks-key-alias androiddebugkey \ |
| 399 | --ks-pass pass:android --key-pass pass:android \ |
| 400 | --out "$out" aligned.apk |
| 401 | ${buildTools}/apksigner verify "$out" |
| 402 | ''; |
| 403 | in |
| 404 | { |
| 405 | inherit chezAndroid joltBoot libjoltapp classesDex apk; |
| 406 | apk-unsigned = apkUnsigned; |
| 407 | } |