Skip to content

Commit 7536799

Browse files
Build the default library for Android (#22)
* 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/<abi>/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 <noreply@anthropic.com> * Android build: name the NDK version it was tested with Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 9719dcd commit 7536799

3 files changed

Lines changed: 210 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,37 @@ repo:
3030
`scripts\build_tslang_runtime_<debug|release>_x86.bat` in `TypeScriptCompiler`, which
3131
puts it in `__build\tslang-runtime\<debug|release>\x86\`.
3232

33+
### Android
34+
35+
On a Windows host, with the Android NDK (tested with r30) in `ANDROID_NDK_HOME`:
36+
37+
```
38+
scripts\build_android.bat # arm64-v8a and x86_64, release and debug, all models
39+
scripts\build_android.bat arm64-v8a release gc # just that one
40+
```
41+
42+
This builds for API level 29 (the lowest with `timespec_get`) into
43+
`__build\android\<abi>\defaultlib\{lib,dll}\<debug|release>\<gc|rc|none>\`, one complete tree
44+
per ABI. It needs a `tslang.exe` that can target Android (`TOOL_PATH`, default: the compiler's
45+
release build tree). Android has no libcurl, so HTTP is `src\wrappers\http_stub.cpp`: `fetch()`
46+
throws. The `.so` links `libc++_shared.so`, which the app ships.
47+
48+
The collector and the async runtime come from the compiler repo:
49+
`scripts\build_gc_release_android.bat` and `scripts\build_tslang_runtime_release_android.bat`.
50+
A program is compiled and then linked with the NDK, for example for arm64-v8a under `gc`:
51+
52+
```
53+
tslang --emit=obj -mtriple=aarch64-linux-android29 -relocation-model=pic --opt -mm=gc ^
54+
--default-lib-path=__build\android\arm64-v8a app.ts -o app.o
55+
aarch64-linux-android29-clang++ -pie -static-libstdc++ app.o ^
56+
-L__build\android\arm64-v8a\defaultlib\lib\release\gc ^
57+
-L<TypeScriptCompiler>\3rdParty\gc\android\arm64-v8a\release\lib ^
58+
-L<TypeScriptCompiler>\__build\tslang-runtime\release\android\arm64-v8a ^
59+
-lTypeScriptDefaultLib -lgc -lTypeScriptAsyncRuntime -lm -ldl -o app
60+
```
61+
62+
(`-lgc` only under `gc`.) `tslang --emit=exe` does not link for Android yet.
63+
3364
## Testing
3465

3566
```

‎scripts/build_android.bat‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
@rem Builds the default library for Android (API 29) on a Windows host, the Android counterpart of
2+
@rem scripts/build.sh.
3+
@rem
4+
@rem scripts\build_android.bat [abi] [release|debug] [gc|rc|none]
5+
@rem
6+
@rem abi arm64-v8a | x86_64 (default: both)
7+
@rem mode release | debug (default: both)
8+
@rem model gc | rc | none (default: all three - see build.sh on why each needs its own)
9+
@rem
10+
@rem Needs:
11+
@rem ANDROID_NDK_HOME the Android NDK (tested with r30), e.g. C:\Android\android-ndk-r30
12+
@rem TOOL_PATH the directory holding a tslang.exe with Android support (#506); defaults
13+
@rem to the compiler's release build tree next to this repository
14+
@rem
15+
@rem Output, one complete default-library tree per ABI (a program compiled for that ABI takes
16+
@rem --default-lib-path=__build\android\<abi>, the directory holding defaultlib\):
17+
@rem __build\android\<abi>\defaultlib\lib\<mode>\<model>\libTypeScriptDefaultLib.a
18+
@rem __build\android\<abi>\defaultlib\dll\<mode>\<model>\libTypeScriptDefaultLib.so
19+
@rem __build\android\<abi>\defaultlib\*.d.ts ...
20+
@rem
21+
@rem Differences from build.sh:
22+
@rem - HTTP is src\wrappers\http_stub.cpp, not http_linux.cpp: libcurl is not part of Android, so
23+
@rem fetch() throws instead. The archive is therefore the library itself, with no linker script
24+
@rem pulling in -lcurl.
25+
@rem - Everything is position independent, the archive too: Android executables must be PIE.
26+
@rem - The .so links the NDK's default C++ library, libc++_shared.so, which the app ships (Android
27+
@rem allows one C++ runtime per app). GC_* stay undefined in it on purpose, as in build.sh.
28+
@rem
29+
@rem API 29 is the floor: lib.linux.ts calls timespec_get, which Bionic has from 29 on.
30+
@echo off
31+
setlocal
32+
33+
if "%ANDROID_NDK_HOME%"=="" (
34+
echo ANDROID_NDK_HOME is not set: point it at the Android NDK, e.g. C:\Android\android-ndk-r30
35+
exit /b 1
36+
)
37+
38+
set ABI=%~1
39+
set MODE=%~2
40+
set MM=%~3
41+
42+
if "%ABI%"=="" (
43+
call "%~f0" arm64-v8a "%MODE%" "%MM%" || exit /b 1
44+
call "%~f0" x86_64 "%MODE%" "%MM%" || exit /b 1
45+
exit /b 0
46+
)
47+
if "%MODE%"=="" (
48+
call "%~f0" %ABI% release "%MM%" || exit /b 1
49+
call "%~f0" %ABI% debug "%MM%" || exit /b 1
50+
exit /b 0
51+
)
52+
if "%MM%"=="" (
53+
call "%~f0" %ABI% %MODE% gc || exit /b 1
54+
call "%~f0" %ABI% %MODE% rc || exit /b 1
55+
call "%~f0" %ABI% %MODE% none || exit /b 1
56+
exit /b 0
57+
)
58+
59+
if "%ABI%"=="arm64-v8a" (
60+
set TRIPLE=aarch64-linux-android29
61+
) else if "%ABI%"=="x86_64" (
62+
set TRIPLE=x86_64-linux-android29
63+
) else (
64+
echo unknown ABI "%ABI%": arm64-v8a or x86_64
65+
exit /b 1
66+
)
67+
68+
if "%MODE%"=="release" (
69+
set DBG=--opt --opt_level=3
70+
set DBG_CXX=-O3
71+
) else if "%MODE%"=="debug" (
72+
set DBG=--di --opt_level=0
73+
set DBG_CXX=-g
74+
) else (
75+
echo unknown mode "%MODE%": release or debug
76+
exit /b 1
77+
)
78+
79+
if not "%MM%"=="gc" if not "%MM%"=="rc" if not "%MM%"=="none" (
80+
echo unknown memory model "%MM%": gc, rc or none
81+
exit /b 1
82+
)
83+
84+
set ROOT=%~dp0..
85+
if "%TOOL_PATH%"=="" set TOOL_PATH=%ROOT%\..\TypeScriptCompiler\__build\tslang\windows-msbuild-2026-release\bin
86+
set TSLANG="%TOOL_PATH%\tslang.exe"
87+
if not exist %TSLANG% (
88+
echo tslang.exe not found in "%TOOL_PATH%": set TOOL_PATH
89+
exit /b 1
90+
)
91+
92+
set NDK_BIN=%ANDROID_NDK_HOME%\toolchains\llvm\prebuilt\windows-x86_64\bin
93+
set CXX="%NDK_BIN%\clang++.exe" --target=%TRIPLE% -fPIC -std=c++17 %DBG_CXX%
94+
set AR="%NDK_BIN%\llvm-ar.exe"
95+
set TSC=%TSLANG% -mtriple=%TRIPLE% -relocation-model=pic %DBG% -mm=%MM% --emit=obj --export=none --nowarn --no-default-lib
96+
97+
set OUT=%ROOT%\__build\android\%ABI%\defaultlib
98+
set OBJ=%ROOT%\__build\android\%ABI%\obj-%MODE%-%MM%
99+
set LIB_OUT=%OUT%\lib\%MODE%\%MM%
100+
set DLL_OUT=%OUT%\dll\%MODE%\%MM%
101+
102+
echo === %ABI% %MODE% %MM%
103+
if exist "%OBJ%" rd /s /q "%OBJ%"
104+
if exist "%LIB_OUT%" rd /s /q "%LIB_OUT%"
105+
if exist "%DLL_OUT%" rd /s /q "%DLL_OUT%"
106+
md "%OBJ%" "%LIB_OUT%" "%DLL_OUT%"
107+
108+
for %%w in (io datetime regex thread http_stub) do (
109+
%CXX% -c "%ROOT%\src\wrappers\%%w.cpp" -o "%OBJ%\%%w.o" || exit /b 1
110+
)
111+
112+
%TSC% "%ROOT%\src\lib.linux.ts" -o "%OBJ%\lib.linux.o" || exit /b 1
113+
%TSC% "%ROOT%\src\lib.ts" -o "%OBJ%\lib.o" || exit /b 1
114+
115+
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"
116+
%AR% rcs "%LIB_OUT%\libTypeScriptDefaultLib.a" %OBJS% || exit /b 1
117+
%CXX% -shared %OBJS% -lm -o "%DLL_OUT%\libTypeScriptDefaultLib.so" || exit /b 1
118+
119+
rd /s /q "%OBJ%"
120+
121+
rem the declarations a program compiled against this tree reads, and which compiler built it
122+
xcopy /E /I /Y /Q "%ROOT%\src" "%OUT%" > nul || exit /b 1
123+
%TSLANG% --version > "%OUT%\COMPILER_VERSION.txt" 2>&1
124+
exit /b 0

‎src/wrappers/http_stub.cpp‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include <cstddef>
2+
3+
#include "tslang_export.h"
4+
5+
// The http_* functions lib.ts calls (see http_linux.cpp), for a target with no HTTP client
6+
// (Android, where libcurl is not part of the platform). Every request fails, as one does in
7+
// http_linux.cpp when curl cannot start, so fetch() throws instead of the library failing to
8+
// link.
9+
struct HttpResponse
10+
{
11+
int errorCode = -1;
12+
};
13+
14+
extern "C" TSLANG_EXPORT HttpResponse *http_request(const char *, const char *, const char *, const char *, size_t)
15+
{
16+
return new HttpResponse();
17+
}
18+
19+
extern "C" TSLANG_EXPORT bool http_response_success(HttpResponse *)
20+
{
21+
return false;
22+
}
23+
24+
extern "C" TSLANG_EXPORT int http_response_error_code(HttpResponse *r)
25+
{
26+
return r->errorCode;
27+
}
28+
29+
extern "C" TSLANG_EXPORT int http_response_status(HttpResponse *)
30+
{
31+
return 0;
32+
}
33+
34+
extern "C" TSLANG_EXPORT size_t http_response_headers_length(HttpResponse *)
35+
{
36+
return 0;
37+
}
38+
39+
extern "C" TSLANG_EXPORT void http_response_headers_copy_to(HttpResponse *, char *, size_t)
40+
{
41+
}
42+
43+
extern "C" TSLANG_EXPORT size_t http_response_body_length(HttpResponse *)
44+
{
45+
return 0;
46+
}
47+
48+
extern "C" TSLANG_EXPORT void http_response_body_copy_to(HttpResponse *, char *, size_t)
49+
{
50+
}
51+
52+
extern "C" TSLANG_EXPORT void http_response_free(HttpResponse *r)
53+
{
54+
delete r;
55+
}

0 commit comments

Comments
 (0)