Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,37 @@ repo:
`scripts\build_tslang_runtime_<debug|release>_x86.bat` in `TypeScriptCompiler`, which
puts it in `__build\tslang-runtime\<debug|release>\x86\`.

### Android

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
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\<abi>\defaultlib\{lib,dll}\<debug|release>\<gc|rc|none>\`, 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<TypeScriptCompiler>\3rdParty\gc\android\arm64-v8a\release\lib ^
-L<TypeScriptCompiler>\__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

```
Expand Down
124 changes: 124 additions & 0 deletions scripts/build_android.bat
Original file line number Diff line number Diff line change
@@ -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 (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
@rem Output, one complete default-library tree per ABI (a program compiled for that ABI takes
@rem --default-lib-path=__build\android\<abi>, the directory holding defaultlib\):
@rem __build\android\<abi>\defaultlib\lib\<mode>\<model>\libTypeScriptDefaultLib.a
@rem __build\android\<abi>\defaultlib\dll\<mode>\<model>\libTypeScriptDefaultLib.so
@rem __build\android\<abi>\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
55 changes: 55 additions & 0 deletions src/wrappers/http_stub.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <cstddef>

#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;
}