From 34a0efa3647309096865e6299a596452716710bf Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 5 Oct 2026 13:35:02 +0100 Subject: [PATCH 1/2] Build the default library for Android scripts/build_android.bat builds it on a Windows host with the Android NDK for API 29 (arm64-v8a and x86_64; release and debug; gc, rc and none), the Android counterpart of scripts/build.sh. It stages one complete tree per ABI into __build/android//defaultlib, for --default-lib-path. libcurl is not part of Android, so HTTP is src/wrappers/http_stub.cpp: the same http_* functions, with every request failing, so fetch() throws and nothing links curl. Everything is position independent (Android executables are PIE), and the .so links libc++_shared.so. Verified by linking a program (class, assert, number to string, Date, async/await, fetch) with the NDK for both ABIs under gc, rc and none: it links against Bionic only (libc, libm, libdl). Co-Authored-By: Claude Opus 5.5 --- README.md | 31 ++++++++++ scripts/build_android.bat | 124 +++++++++++++++++++++++++++++++++++++ src/wrappers/http_stub.cpp | 55 ++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 scripts/build_android.bat create mode 100644 src/wrappers/http_stub.cpp diff --git a/README.md b/README.md index 9ffb4bc..e26a4b3 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,37 @@ repo: `scripts\build_tslang_runtime__x86.bat` in `TypeScriptCompiler`, which puts it in `__build\tslang-runtime\\x86\`. +### Android + +On a Windows host, with the Android NDK (r27 or later) in `ANDROID_NDK_HOME`: + +``` +scripts\build_android.bat # arm64-v8a and x86_64, release and debug, all models +scripts\build_android.bat arm64-v8a release gc # just that one +``` + +This builds for API level 29 (the lowest with `timespec_get`) into +`__build\android\\defaultlib\{lib,dll}\\\`, one complete tree +per ABI. It needs a `tslang.exe` that can target Android (`TOOL_PATH`, default: the compiler's +release build tree). Android has no libcurl, so HTTP is `src\wrappers\http_stub.cpp`: `fetch()` +throws. The `.so` links `libc++_shared.so`, which the app ships. + +The collector and the async runtime come from the compiler repo: +`scripts\build_gc_release_android.bat` and `scripts\build_tslang_runtime_release_android.bat`. +A program is compiled and then linked with the NDK, for example for arm64-v8a under `gc`: + +``` +tslang --emit=obj -mtriple=aarch64-linux-android29 -relocation-model=pic --opt -mm=gc ^ + --default-lib-path=__build\android\arm64-v8a app.ts -o app.o +aarch64-linux-android29-clang++ -pie -static-libstdc++ app.o ^ + -L__build\android\arm64-v8a\defaultlib\lib\release\gc ^ + -L\3rdParty\gc\android\arm64-v8a\release\lib ^ + -L\__build\tslang-runtime\release\android\arm64-v8a ^ + -lTypeScriptDefaultLib -lgc -lTypeScriptAsyncRuntime -lm -ldl -o app +``` + +(`-lgc` only under `gc`.) `tslang --emit=exe` does not link for Android yet. + ## Testing ``` diff --git a/scripts/build_android.bat b/scripts/build_android.bat new file mode 100644 index 0000000..134d040 --- /dev/null +++ b/scripts/build_android.bat @@ -0,0 +1,124 @@ +@rem Builds the default library for Android (API 29) on a Windows host, the Android counterpart of +@rem scripts/build.sh. +@rem +@rem scripts\build_android.bat [abi] [release|debug] [gc|rc|none] +@rem +@rem abi arm64-v8a | x86_64 (default: both) +@rem mode release | debug (default: both) +@rem model gc | rc | none (default: all three - see build.sh on why each needs its own) +@rem +@rem Needs: +@rem ANDROID_NDK_HOME the Android NDK (r27 or later), e.g. C:\Android\android-ndk-r30 +@rem TOOL_PATH the directory holding a tslang.exe with Android support (#506); defaults +@rem to the compiler's release build tree next to this repository +@rem +@rem Output, one complete default-library tree per ABI (a program compiled for that ABI takes +@rem --default-lib-path=__build\android\, the directory holding defaultlib\): +@rem __build\android\\defaultlib\lib\\\libTypeScriptDefaultLib.a +@rem __build\android\\defaultlib\dll\\\libTypeScriptDefaultLib.so +@rem __build\android\\defaultlib\*.d.ts ... +@rem +@rem Differences from build.sh: +@rem - HTTP is src\wrappers\http_stub.cpp, not http_linux.cpp: libcurl is not part of Android, so +@rem fetch() throws instead. The archive is therefore the library itself, with no linker script +@rem pulling in -lcurl. +@rem - Everything is position independent, the archive too: Android executables must be PIE. +@rem - The .so links the NDK's default C++ library, libc++_shared.so, which the app ships (Android +@rem allows one C++ runtime per app). GC_* stay undefined in it on purpose, as in build.sh. +@rem +@rem API 29 is the floor: lib.linux.ts calls timespec_get, which Bionic has from 29 on. +@echo off +setlocal + +if "%ANDROID_NDK_HOME%"=="" ( + echo ANDROID_NDK_HOME is not set: point it at the Android NDK, e.g. C:\Android\android-ndk-r30 + exit /b 1 +) + +set ABI=%~1 +set MODE=%~2 +set MM=%~3 + +if "%ABI%"=="" ( + call "%~f0" arm64-v8a "%MODE%" "%MM%" || exit /b 1 + call "%~f0" x86_64 "%MODE%" "%MM%" || exit /b 1 + exit /b 0 +) +if "%MODE%"=="" ( + call "%~f0" %ABI% release "%MM%" || exit /b 1 + call "%~f0" %ABI% debug "%MM%" || exit /b 1 + exit /b 0 +) +if "%MM%"=="" ( + call "%~f0" %ABI% %MODE% gc || exit /b 1 + call "%~f0" %ABI% %MODE% rc || exit /b 1 + call "%~f0" %ABI% %MODE% none || exit /b 1 + exit /b 0 +) + +if "%ABI%"=="arm64-v8a" ( + set TRIPLE=aarch64-linux-android29 +) else if "%ABI%"=="x86_64" ( + set TRIPLE=x86_64-linux-android29 +) else ( + echo unknown ABI "%ABI%": arm64-v8a or x86_64 + exit /b 1 +) + +if "%MODE%"=="release" ( + set DBG=--opt --opt_level=3 + set DBG_CXX=-O3 +) else if "%MODE%"=="debug" ( + set DBG=--di --opt_level=0 + set DBG_CXX=-g +) else ( + echo unknown mode "%MODE%": release or debug + exit /b 1 +) + +if not "%MM%"=="gc" if not "%MM%"=="rc" if not "%MM%"=="none" ( + echo unknown memory model "%MM%": gc, rc or none + exit /b 1 +) + +set ROOT=%~dp0.. +if "%TOOL_PATH%"=="" set TOOL_PATH=%ROOT%\..\TypeScriptCompiler\__build\tslang\windows-msbuild-2026-release\bin +set TSLANG="%TOOL_PATH%\tslang.exe" +if not exist %TSLANG% ( + echo tslang.exe not found in "%TOOL_PATH%": set TOOL_PATH + exit /b 1 +) + +set NDK_BIN=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin +set CXX="%NDK_BIN%\clang++.exe" --target=%TRIPLE% -fPIC -std=c++17 %DBG_CXX% +set AR="%NDK_BIN%\llvm-ar.exe" +set TSC=%TSLANG% -mtriple=%TRIPLE% -relocation-model=pic %DBG% -mm=%MM% --emit=obj --export=none --nowarn --no-default-lib + +set OUT=%ROOT%\__build\android\%ABI%\defaultlib +set OBJ=%ROOT%\__build\android\%ABI%\obj-%MODE%-%MM% +set LIB_OUT=%OUT%\lib\%MODE%\%MM% +set DLL_OUT=%OUT%\dll\%MODE%\%MM% + +echo === %ABI% %MODE% %MM% +if exist "%OBJ%" rd /s /q "%OBJ%" +if exist "%LIB_OUT%" rd /s /q "%LIB_OUT%" +if exist "%DLL_OUT%" rd /s /q "%DLL_OUT%" +md "%OBJ%" "%LIB_OUT%" "%DLL_OUT%" + +for %%w in (io datetime regex thread http_stub) do ( + %CXX% -c "%ROOT%\src\wrappers\%%w.cpp" -o "%OBJ%\%%w.o" || exit /b 1 +) + +%TSC% "%ROOT%\src\lib.linux.ts" -o "%OBJ%\lib.linux.o" || exit /b 1 +%TSC% "%ROOT%\src\lib.ts" -o "%OBJ%\lib.o" || exit /b 1 + +set OBJS="%OBJ%\lib.o" "%OBJ%\lib.linux.o" "%OBJ%\io.o" "%OBJ%\datetime.o" "%OBJ%\regex.o" "%OBJ%\thread.o" "%OBJ%\http_stub.o" +%AR% rcs "%LIB_OUT%\libTypeScriptDefaultLib.a" %OBJS% || exit /b 1 +%CXX% -shared %OBJS% -lm -o "%DLL_OUT%\libTypeScriptDefaultLib.so" || exit /b 1 + +rd /s /q "%OBJ%" + +rem the declarations a program compiled against this tree reads, and which compiler built it +xcopy /E /I /Y /Q "%ROOT%\src" "%OUT%" > nul || exit /b 1 +%TSLANG% --version > "%OUT%\COMPILER_VERSION.txt" 2>&1 +exit /b 0 diff --git a/src/wrappers/http_stub.cpp b/src/wrappers/http_stub.cpp new file mode 100644 index 0000000..5e8bd5e --- /dev/null +++ b/src/wrappers/http_stub.cpp @@ -0,0 +1,55 @@ +#include + +#include "tslang_export.h" + +// The http_* functions lib.ts calls (see http_linux.cpp), for a target with no HTTP client +// (Android, where libcurl is not part of the platform). Every request fails, as one does in +// http_linux.cpp when curl cannot start, so fetch() throws instead of the library failing to +// link. +struct HttpResponse +{ + int errorCode = -1; +}; + +extern "C" TSLANG_EXPORT HttpResponse *http_request(const char *, const char *, const char *, const char *, size_t) +{ + return new HttpResponse(); +} + +extern "C" TSLANG_EXPORT bool http_response_success(HttpResponse *) +{ + return false; +} + +extern "C" TSLANG_EXPORT int http_response_error_code(HttpResponse *r) +{ + return r->errorCode; +} + +extern "C" TSLANG_EXPORT int http_response_status(HttpResponse *) +{ + return 0; +} + +extern "C" TSLANG_EXPORT size_t http_response_headers_length(HttpResponse *) +{ + return 0; +} + +extern "C" TSLANG_EXPORT void http_response_headers_copy_to(HttpResponse *, char *, size_t) +{ +} + +extern "C" TSLANG_EXPORT size_t http_response_body_length(HttpResponse *) +{ + return 0; +} + +extern "C" TSLANG_EXPORT void http_response_body_copy_to(HttpResponse *, char *, size_t) +{ +} + +extern "C" TSLANG_EXPORT void http_response_free(HttpResponse *r) +{ + delete r; +} From eaefb23d2d6fac5d03c8736f378e5a81d29b8a4c Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 5 Oct 2026 13:38:21 +0100 Subject: [PATCH 2/2] Android build: name the NDK version it was tested with Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- scripts/build_android.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e26a4b3..dbac702 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ repo: ### Android -On a Windows host, with the Android NDK (r27 or later) in `ANDROID_NDK_HOME`: +On a Windows host, with the Android NDK (tested with r30) in `ANDROID_NDK_HOME`: ``` scripts\build_android.bat # arm64-v8a and x86_64, release and debug, all models diff --git a/scripts/build_android.bat b/scripts/build_android.bat index 134d040..ec2fe3f 100644 --- a/scripts/build_android.bat +++ b/scripts/build_android.bat @@ -8,7 +8,7 @@ @rem model gc | rc | none (default: all three - see build.sh on why each needs its own) @rem @rem Needs: -@rem ANDROID_NDK_HOME the Android NDK (r27 or later), e.g. C:\Android\android-ndk-r30 +@rem ANDROID_NDK_HOME the Android NDK (tested with r30), e.g. C:\Android\android-ndk-r30 @rem TOOL_PATH the directory holding a tslang.exe with Android support (#506); defaults @rem to the compiler's release build tree next to this repository @rem