lazy Android build inside Termux

hi people.

first of all: I’m looking for help, but I’m also not looking for help. if you have something useful to say, I want to hear, if you don’t know the answer, I’ll find my answer myself after days reading of brain-damaging documentation. I’ll report my discoveries here.

I want to make a build for Android, preferably inside Termux, otherwise inside MSYS2, otherwise on Windows. I don’t use WSL2, I don’t have any Linux desktop. I have 22 GB spare in my laptop and 44 GB available in my smartphone. I’m also super lazy, so bear with me. I want to do stuff with the least amount of effort possible. like, I want to run make my-epic-game.apk and have stuff just work. I don’t want to sacrifice three albino elephants every time I want to test stuff on Android.

I’m still using SDL2 and not SDL3 because… that’s what is packaged for MSYS2 and this is what appears when running emcc --show-ports.

so, what do I even do? I never built anything for Android, not even a “Hello, world!” in Java. what is possible, what is the least worse?? like, there are these instructions:

which seem somewhat useful, but not for a noob and lazy person like me.

  1. it tells you to run create-android-project.py, but where do I find it? I actually know where :wink: :wink: :wink: but as I said, I’m lazy, I don’t want to search for it. why the hell ain’t there a hyperlink pointing directly to it?? we are in 21st century, hyperlinks exist!!
  2. how smart is this create-android-project.py? like, does it know I’m inside MSYS2 or Termux? if I’m missing something in my PATH or some dependency, will it know? if it is very smart, can it even tell me what to do to get going? well, I don’t know. you could put a warning telling the poor user how much trouble he will go through
  3. there is this SDL3 Android Archive, is it “easier”? but then I have to use sdl2-compat, but where do I find prebuilt binaries for all CPU architectures that Android uses, like ARM, that I can easily curl? like, the downloads link in this README.md is just broken. is my use case even possible?
  4. why is Android Studio so annoying?
    • like, it weighs 400 gigatons, but it doesn’t even setup my environment variables. useless. c’mon, I’m a Windows user, it should do stuff for me like I’m an idiot. if everyone has the same setup, things work more smoothly. thus, I’m not installing Android Studio, it seems to serve no purpose as far as I can tell.
    • then, the “Command line tools only” is only a zip, like, what do I do with it, do I extract it inside my project’s folder and add rules to .gitignore??? what?
    • if I do get everything right, am I supposed to work inside PowerShell? but then, I don’t have Python on Windows, only inside MSYS2. should I expose MSYS2 Python for PowerShell, or somehow get the Android stuff inside MSYS2? what about binary compatibility clashes between MSVC and mingw?
    • I just noticed I have android-tools installed in MSYS2, but it only provides adb, it doesn’t provide sdkmanager, nor NDK, nor anything else. there is nothing else relevant prepackaged for MSYS2
    • I’m just overall dazed that building HTML5 is incommensurably leagues easier than making a mobile app. what the hell?! I can compile, run and ship SDL inside a Web browser in three simple commands. what is Google doing at all? does it want apps for Android at all?
    • Termux provides ndk-multilib, ndk-multilib-native-static, ndk-multilib-native-stubs, ndk-sysroot, openjdk-17, openjdk-21, gradle, aapt, aapt2, dx, ecj and some other stuff. I heard stories about developing Android inside Android… but it always felt noisy with lots of error-prone manual steps to get anywhere.

relevant: How to build a SDL2 application for Android using command line - #11 by rtrussell

I will meditate over whatever people figured out there.

inspecting the “Command line tools only” for Windows:

$ find cmdline-tools/ -type f -exec file -b {} \; | sort | uniq
ASCII text
ASCII text, with CRLF line terminators
Java archive data (JAR)
Unicode text, UTF-8 text, with very long lines (754)
Zip archive data, at least v1.0 to extract, compression method=deflate
Zip archive data, at least v1.0 to extract, compression method=store
Zip archive data, at least v2.0 to extract, compression method=deflate

inspecting the “Command line tools only” for Linux:

$ find cmdline-tools/ -type f -exec file -b {} \; | sort | uniq
ASCII text
Java archive data (JAR)
Unicode text, UTF-8 text, with very long lines (754)
Zip archive data, at least v1.0 to extract, compression method=deflate
Zip archive data, at least v1.0 to extract, compression method=store
Zip archive data, at least v2.0 to extract, compression method=deflate
a sh script, ASCII text executable

I manually checked those Zip, they are actually JARs. so I believe both the Windows and Linux command line tools differ that one has scripts for cmd.exe and the other for POSIX shell.

thus, in order to use these tools on MSYS2, I could try to use the Linux shell scripts, and because everything else is Java, stuff might run on Termux just fine regardless of CPU architecture differences.

edit: yeah, I’m reading the source code for sdkmanager inside the Linux zip, it is actually aware of MSYS2 environments

I have added rules so that my Makefile downloads Linux cmdline-tools for Android. now I will try to download some SDK and NDK versions and look for more steps. I have studied briefly what is gradle as well.

edit: I have yet to test on Termux…

yeah like I ran sdkmanager --list and there are multiple versions of SDK 34:

platforms;android-34, platforms;android-34-ext8, platforms;android-34-ext10, platforms;android-34-ext11 and platforms;android-34-ext12. like, how do I even choose??? what even are these variations? if I search on Google, I see exactly 4 results and one is written in asian. also NDK got multiple micro versions for each major version, but at least NDK 16 has only one version available.

trial and error, I guess?

I just installed SDK 34, no extensions, and ndk;16.1.4479499. after running androidbuild.sh I ran into lots of errors. I guess the people who read the SDL README is so smart they all figure on their own that the script must be in a folder inside the project, and not at the “root” of the project. after doing this, I’ll discover whether I need to be inside this folder or at the root… lots of fun guessing!

I’ll keep trying… I wonder how delicious building for iOS will be!!

so I figured out that… I must be at the root of my “game” folder to run androidbuild.sh and… the entire source code of SDL2 must be below my game folder?? I just love divination. let’s gooooooo

every single step is error-prone, this is most absurd flaky build process I’ve ever seen so far as a young adult with no professional experience. this is all just absurd.

so. I did make progress. quite some bugs and patches to be reported to both SDL2 branch and NDK…

the entire ordeal is comedic. the entire HTML5 build is three lines in my Makefile, the Android build is 36 lines and counting.

print coming soon

hell yeah bitches, MSYS2 is building SDL Android APK! now let’s try to also build inside Termux

Android build on Termux is yielding some stupid nonsensical error. sorry, I used many swear words for variables and filepaths while experimenting.

I’m just puzzled. I’ll figure it out, again. brb

NSFW
$ make tdd.apk &> ~/storage/downloads/debug.txt
yes | android_sdk/cmdline-tools/latest/bin/sdkmanager --licenses
Loading local repository...                                                     
[=========                              ] 25% Loading local repository...       
[=========                              ] 25% Fetch remote repository...        
[==========                             ] 26% Fetch remote repository...        
[============                           ] 31% Fetch remote repository...        
[============                           ] 32% Fetch remote repository...        
[=============                          ] 33% Fetch remote repository...        
[=============                          ] 34% Fetch remote repository...        
[==============                         ] 35% Fetch remote repository...        
[==============                         ] 36% Fetch remote repository...        
[==============                         ] 38% Fetch remote repository...        
[===============                        ] 39% Fetch remote repository...        
[===============                        ] 40% Fetch remote repository...        
[================                       ] 41% Fetch remote repository...        
[================                       ] 42% Fetch remote repository...        
                                                                                
Warning: Errors during XML parse:
[================                       ] 42% Fetch remote repository...        
                                                                                
Warning: Additionally, the fallback loader failed to parse the XML.
[================                       ] 42% Fetch remote repository...        
[=================                      ] 43% Fetch remote repository...        
[=================                      ] 44% Fetch remote repository...        
[=================                      ] 45% Fetch remote repository...        
[==================                     ] 46% Fetch remote repository...        
[==================                     ] 47% Fetch remote repository...        
[===================                    ] 48% Fetch remote repository...        
[===================                    ] 49% Fetch remote repository...        
[====================                   ] 50% Fetch remote repository...        
[====================                   ] 51% Fetch remote repository...        
[====================                   ] 52% Fetch remote repository...        
[=====================                  ] 54% Fetch remote repository...        
[=====================                  ] 55% Fetch remote repository...        
[======================                 ] 56% Fetch remote repository...        
[======================                 ] 57% Fetch remote repository...        
[=======================                ] 58% Fetch remote repository...        
[=======================                ] 59% Fetch remote repository...        
[========================               ] 60% Fetch remote repository...        
[========================               ] 61% Fetch remote repository...        
[========================               ] 62% Fetch remote repository...        
[=========================              ] 63% Fetch remote repository...        
[=========================              ] 64% Fetch remote repository...        
[==========================             ] 65% Fetch remote repository...        
[==========================             ] 66% Fetch remote repository...        
[===========================            ] 68% Fetch remote repository...        
[===========================            ] 69% Fetch remote repository...        
[===========================            ] 70% Fetch remote repository...        
[============================           ] 71% Fetch remote repository...        
[============================           ] 72% Fetch remote repository...        
[=============================          ] 73% Fetch remote repository...        
[=============================          ] 74% Fetch remote repository...        
[=============================          ] 75% Fetch remote repository...        
[=============================          ] 75% Computing updates...              
[=======================================] 100% Computing updates...             
All SDK package licenses accepted.

COPYSOURCE=1 ANDROID_HOME=android_sdk ANDROID_NDK_HOME=android_sdk ../SDL2/build-scripts/androidbuild.sh penis.cock.dick camera.c main.c terrain.c camera.h v.h terrain.h util.h 
To build and install to a device for testing, run the following:
cd /data/data/com.termux/files/home/SDL2/build/penis.cock.dick
./gradlew installDebug
cd ../SDL2/build/penis.cock.dick; \
ANDROID_HOME=~/td-delivery/android_sdk ANDROID_NDK_HOME=~/td-delivery/android_sdk ./gradlew installDebug
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:javaPreCompileDebug
> Task :app:checkDebugAarMetadata
> Task :app:generateDebugResValues
> Task :app:mapDebugSourceSetPaths
> Task :app:generateDebugResources
> Task :app:mergeDebugResources
> Task :app:packageDebugResources
> Task :app:createDebugCompatibleScreenManifests
> Task :app:parseDebugLocalResources
> Task :app:extractDeepLinksDebug
> Task :app:processDebugMainManifest
> Task :app:processDebugManifest
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:configureNdkBuildDebug[arm64-v8a]

> Task :app:buildNdkBuildDebug[arm64-v8a] FAILED
C/C++: /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++[5]: syntax error: unexpected ')'
C/C++: /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3[1]: syntax error: unexpected '('
C/C++: /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang[5]: syntax error: unexpected ')'
C/C++: make[1]: *** [/data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/build/core/build-binary.mk:422: /data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/build/intermediates/cxx/Debug/29445a3u/obj/local/arm64-v8a/objs-debug/SDL2/src/SDL.o] Error 1

> Task :app:processDebugManifestForPackage

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:buildNdkBuildDebug[arm64-v8a]'.
> com.android.ide.common.process.ProcessException: make[1]: Entering directory '/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app'
  Android NDK: WARNING: Unsupported source file extensions in /data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/jni/src/Android.mk for module main    
  Android NDK:   camera.h v.h terrain.h util.h    
  make[1]: Leaving directory '/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app'
  make[1]: Entering directory '/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app'
  [arm64-v8a] Compile        : SDL2 <= SDL.c
  make[1]: Leaving directory '/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app'
  
  C++ build system [build] failed while executing:
      /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/ndk-build \
        NDK_PROJECT_PATH=null \
        APP_BUILD_SCRIPT=/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/jni/Android.mk \
        NDK_APPLICATION_MK=/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/jni/Application.mk \
        APP_ABI=arm64-v8a \
        NDK_ALL_ABIS=arm64-v8a \
        NDK_DEBUG=1 \
        NDK_OUT=/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/build/intermediates/cxx/Debug/29445a3u/obj \
        NDK_LIBS_OUT=/data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/build/intermediates/cxx/Debug/29445a3u/lib \
        APP_PLATFORM=android-19 \
        SDL2 \
        main
    from /data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app
  /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++[5]: syntax error: unexpected ')'
  /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3[1]: syntax error: unexpected '('
  /data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang[5]: syntax error: unexpected ')'
  make[1]: *** [/data/data/com.termux/files/home/td-delivery/android_sdk/ndk/25.1.8937393/build/core/build-binary.mk:422: /data/data/com.termux/files/home/SDL2/build/penis.cock.dick/app/build/intermediates/cxx/Debug/29445a3u/obj/local/arm64-v8a/objs-debug/SDL2/src/SDL.o] Error 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 8s
15 actionable tasks: 15 executed
make: *** [Makefile:62: tdd.apk] Error 1

gradle is useless to tell whatever is happening with NDK on Termux. flags --info, --debug and --stracktrace add nothing meaningful. --scan is useless, it is just a nice looking HTML version of the exact same thing that gradle spits on your terminal. nothing shows up on Google, I guess I’m the first person on Earth to face this issue. I think I’ll step back and study what is the purpose of gradle, if any, and study Google documentation on NDK and native stuff. I don’t know. big bummer. or not. I have more important stuff to do. I’ll be back in two weeks.

allow me rant a little

SDL and libuv are beautiful libraries, because they abstract stuff. I worry exclusively about their APIs, and they guarantee behavior everywhere. these last few days exploring Android builds just kills the entire premise of abstraction: there is none. here I am, wondering how much time I’ll spend acquiring useless knowledge about the gory details of NDK and whatever. again, I applaud the people from Emscripten for really worrying about abstraction.

this problem is consuming me. new findings. apparently, sdkmanager has downloaded x86-64 binaries to run in my Android device which is arm64/aarch64. smart choice, thanks! this was evidenced by the path that appeared in the Gradle errors, but I checked manually to be sure. here follows my bash session on Termux:

~/td-delivery/android_sdk $ file ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-14 > ../../storage/downloads/debug.txt
./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-14: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, stripped
~/td-delivery/android_sdk $ file ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3.9 >> ~/storage/downloads/debug.txt
./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3.9: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f49de9e667cf9e2060fad89002a75d41ef695d15, stripped
~/td-delivery/android_sdk $ sh ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-14 &>> ~/storage/downloads/debug.txt
./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-14: 5: Syntax error: ")" unexpected
~/td-delivery/android_sdk $ sh ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3.9 &>> ~/storage/downloads/debug.txt
./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3.9: 1: Syntax error: "(" unexpected
~/td-delivery/android_sdk $ hexdump -C ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-14 | grep -m 1 -C 5 ')' >> ~/storage/downloads/debug.txt
00000460  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000470  32 0d 00 00 12 00 00 00  00 00 00 00 00 00 00 00  |2...............|
00000480  00 00 00 00 00 00 00 00  57 0d 00 00 12 00 00 00  |........W.......|
00000490  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004a0  c5 0d 00 00 12 00 00 00  00 00 00 00 00 00 00 00  |................|
000004b0  00 00 00 00 00 00 00 00  29 0f 00 00 12 00 00 00  |........).......|
000004c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004d0  71 0f 00 00 12 00 00 00  00 00 00 00 00 00 00 00  |q...............|
000004e0  00 00 00 00 00 00 00 00  76 11 00 00 12 00 00 00  |........v.......|
000004f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000500  35 13 00 00 12 00 00 00  00 00 00 00 00 00 00 00  |5...............|
~/td-delivery/android_sdk $ hexdump -C ./ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/python3/bin/python3.9 | grep -m 1 -C 5 ')' >> ~/storage/downloads/debug.txt
000006e0  48 83 3d 08 07 20 00 00  74 1e b8 00 00 00 00 48  |H.=.. ..t......H|
000006f0  85 c0 74 14 55 bf f0 0d  60 00 48 89 e5 ff d0 5d  |..t.U...`.H....]|
00000700  e9 7b ff ff ff 0f 1f 00  e9 73 ff ff ff 0f 1f 00  |.{.......s......|
00000710  41 57 41 89 ff 41 56 49  89 f6 41 55 49 89 d5 41  |AWA..AVI..AUI..A|
00000720  54 4c 8d 25 b8 06 20 00  55 48 8d 2d b8 06 20 00  |TL.%.. .UH.-.. .|
00000730  53 4c 29 e5 31 db 48 c1  fd 03 48 83 ec 08 e8 7d  |SL).1.H...H....}|
00000740  fe ff ff 48 85 ed 74 1e  0f 1f 84 00 00 00 00 00  |...H..t.........|
00000750  4c 89 ea 4c 89 f6 44 89  ff 41 ff 14 dc 48 83 c3  |L..L..D..A...H..|
00000760  01 48 39 eb 75 ea 48 83  c4 08 5b 5d 41 5c 41 5d  |.H9.u.H...[]A\A]|
00000770  41 5e 41 5f c3 90 66 2e  0f 1f 84 00 00 00 00 00  |A^A_..f.........|
00000780  f3 c3 00 00 48 83 ec 08  48 83 c4 08 c3 00 00 00  |....H...H.......|
~/td-delivery/android_sdk $

as you can see, both clang-14 and python3.9 are executables built for x86-64, and for some reason they are run as if they are shell scripts. this is complicated, because in UNIX systems with UTF-8, text and binary files are identical and indistinguishable. the hexdump output shows that both binary files contains what could be interpreted at parenthesis characters in ASCII, even though they have a different meaning in ELF format, which what file suggests them to be. the numbers between square brackets of Gradle output is an useless conversion it applies over the well standardized “file: number: error” convention used by all utilities; regardless, the numbers are an attempt to show which line the unexpected parenthesis were found, even if that is nonsense.

kinda noisy output. I don’t understand. I have system-wide clang and Python installed on Termux that work just fine, why is sdkmanager downloading them at all? like, it already depends on lots utilities to be present in my system, such as GNU Make and which, so are they some patched version? many mysteries. Google has some documentation pages, I’ll read them eventually…

I found some high quality Google documentation on NDK:

https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md

so I’ve been reading Google documentation, and it is indeed high quality. I’m really delighted with all the effort their developers put in documentation. I have learned a lot and I started to think about issues that never occurred to me. however, I didn’t learn enough to truly understand what it takes to build an Android APK inside Termux.

there are four layers of abstraction to dive into. I have my project Makefile that calls Gradle, which calls ndk-build, which calls Google’s recursive GNU Make, which finally calls Clang with several parameters and other build tools.

deciphering Gradle is all about deciphering this:

I dunno what to do. it is a blind shot anyway, to believe that whatever Clang I have installed in Termux was built with enough features required to build a library usable by an APK

I’m thinking about directly building some kind of library that contains all of SDL and my game code, then telling Gradle “use this library, it has a function called main that is not an entry point”, because I hope that Gradle can handle at least the Java part adequately, even on Termux. or maybe I can throw away Gradle entirely, since there are some tutorials explaining how to build a Java APK without Gradle. but there is always the risk of becoming out-of-the-date, as whatever Gradle does may change over time. or maybe I could investigate the root cause of all my problems, which is sdkmanager downloading inappropriate executables.

lots of questions, no real progress at the moment. and not having professional experience with Java makes it all more painful.