Build an Android app¶
Build an EGL application from Linux using the installed egcl-target-android
RPM. It supplies native libraries for ARM64 phones and x86-64 emulators, plus
egcl-android-new. Application builds need Python 3, GNU Make, a JDK, and the
Android SDK. They do not need Rust, the NDK, a EGCL checkout, or containers.
If you would rather not install the SDK and a JDK at all, skip to
build without the Android SDK, where
asdf:make assembles and signs an APK entirely in Lisp.
Generate the project¶
egcl-android-new hello --package org.example.hello \
--name "Hello EGCL" --host=aarch64-linux-android --template=egl
cd hello
Choose a new directory: the generator refuses to overwrite an existing one.
Install SDK tools¶
On Fedora, install a JDK if one is not already on your PATH:
install-tools uses an existing SDK manager or downloads a pinned,
checksum-verified copy of Google's command-line tools. Read and accept the
interactive SDK license prompts if you agree. It installs build-tools,
platform-tools, and the platform named by targetSdkVersion in the manifest.
The default SDK path is ~/Android/Sdk, overridden by ANDROID_HOME or
ANDROID_SDK_ROOT. To use another directory, create local.mk:
local.mk is ignored by Git. Ordinary make builds do not download tools.
Build and launch on a phone¶
Enable USB debugging on an ARM64 phone, connect it, and authorize the computer. Then run:
The EGL template draws a color and changes it when touched. Edit
assets/app.lisp, then repeat make install and make run. Stop logcat with
Ctrl-C. If multiple devices are attached, set SERIAL in local.mk or pass it
to each device command.
Build for an x86-64 emulator¶
Start an x86-64 Android emulator separately, then run:
The tools target an already running emulator; install-tools does not create
an AVD or install an emulator system image. To package both architectures:
The x86-64 APK has packaging validation; emulator execution remains a separate validation step. See platform support.
Sign a release¶
Supply your own keystore and alias, and read its password into the environment:
export KEYSTORE=/absolute/path/to/release.jks KEY_ALIAS=mykey
read -rsp 'Keystore password: ' KEYSTORE_PASSWORD; echo
export KEYSTORE_PASSWORD
make release
unset KEYSTORE_PASSWORD
Set KEY_PASSWORD too if the key password differs. Release builds disable
debugging. Use adb install explicitly to deploy a release APK; make install
builds and installs the debug APK. A differently signed APK cannot update an
existing installation without addressing the signing mismatch.
Build without the Android SDK¶
egcl-apk-asdf, also from egcl-target-android, builds and signs an APK with
no SDK, JDK, Make or Python — just the egcl the package already requires. The
project describes its APK in its own .asd rather than in a manifest and
Makefile, and asdf:make is the whole build:
;; The primary system exists so ASDF accepts the secondary name below. It has
;; no components because an EGL application's sources are target code: they
;; call bindings that only exist inside the APK runtime.
(asdf:defsystem "hello"
:components ())
(asdf:defsystem "hello/apk"
:defsystem-depends-on ("egcl-apk-asdf")
:class "egcl-apk-asdf:android-apk"
:build-operation "egcl-apk-asdf:apk-op"
:pathname "assets/"
:version "1.0"
:apk-package "org.example.hello"
:apk-label "Hello EGCL"
:apk-version-code 1
:components ((:static-file "app.lisp")))
cd hello
egcl --eval '(require :asdf)' \
--eval '(asdf:load-asd (truename "hello.asd"))' \
--eval '(asdf:make "hello/apk")'
That writes hello/build/hello.apk, signed with a P-256 key it creates once as
hello/.egcl-apk-key, mode 0600 whatever your umask is. Install it with
adb install. The system's file components are the APK's assets, in
declaration order, so the two cannot drift; :apk-entry (default app.lisp)
names the one that is loaded first.
Nothing has to be launched or configured: the builder and its dependencies
install into /usr/share/common-lisp/source/egcl-apk, which ASDF already
searches, and :defsystem-depends-on ("egcl-apk-asdf") in the .asd pulls them
in. (require :asdf) is a no-op on an installed egcl — its appended image
already holds ASDF — and asdf:load-asd wants an absolute pathname, hence
truename.
A build takes about 6 seconds, around 3 of which is ASDF loading the builder
and its dependencies. SBCL does the same build in about 1 second. If you build
many in a row, save.lisp in the builder's source tree writes an image of that
loaded world and takes a build to about 4.5 seconds; see the builder's README.
See the builder's README
for every :apk-* slot, and the
Android project reference for build paths,
variables, lifecycle callbacks, and asset limits.