Improve android support & add CI (#2664)

* Change context destruction order.

* enable emulated tls on termux

* Fix stdlib on android

* Add a CI workflow for android termux

* update release notes

* use the new unified CI tests on android

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
Book-reader
2026-02-05 19:04:16 +00:00
committed by GitHub
parent eddbfb8ba3
commit bb9e9b54cf
15 changed files with 122 additions and 32 deletions

View File

@@ -352,9 +352,74 @@ jobs:
if [[ ${{ matrix.build_type }} = "Debug" ]]; then CHECK_NAME=".#c3c-debug-checks"; fi if [[ ${{ matrix.build_type }} = "Debug" ]]; then CHECK_NAME=".#c3c-debug-checks"; fi
nix build -L "$CHECK_NAME" nix build -L "$CHECK_NAME"
build-android:
strategy:
fail-fast: false
matrix:
build_type: [ Release, Debug ]
architecture: [ aarch64, x86_64 ]
include:
- architecture: aarch64
runner: ubuntu-24.04-arm
- architecture: x86_64
runner: ubuntu-24.04
runs-on: ${{matrix.runner}}
container:
image: termux/termux-docker:${{matrix.architecture}}
volumes:
- /tmp/node20:/__e/node20
env:
TERMUX_MAIN_PACKAGE_FORMAT: debian
ANDROID_ROOT: /system
ANDROID_DATA: /data
PREFIX: /data/data/com.termux/files/usr
HOME: /data/data/com.termux/files/home
PATH: /data/data/com.termux/files/usr/bin
TMPDIR: /data/data/com.termux/files/usr/tmp
LANG: en_US.UTF-8
TZ: UTC
steps:
- name: prepare termux
run: |
ln -sf ${PREFIX}/etc/termux/mirrors/default ${PREFIX}/etc/termux/chosen_mirrors
/entrypoint.sh pkg update
/entrypoint.sh bash -c "yes | pkg upgrade -y"
chmod -R o+x ${PREFIX}/bin
/entrypoint.sh pkg install -y nodejs-lts
ln -sf ${PREFIX}/bin /__e/node20/bin
- uses: actions/checkout@v4.2.2
- name: fix permissions after checkout (checkout runs as root and entrypoint.sh switches to user 1000)
run: chown -R 1000:1000 .
- name: setup
run: /entrypoint.sh pkg install -y llvm binutils libgc build-essential cmake git libedit zlib clang make mlir llvm-tools libpolly python libllvm-static
- name: build
run: |
cmake -Bbuild -DCMAKE_BUILD_TYPE=${{matrix.build_type}}
cmake --build build
- name: Run Unified Tests
run: ./scripts/tools/ci_tests.sh "./build/c3c" "android"
- name: bundle_output
run: |
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
cp README.md c3
cp releasenotes.md c3
tar czf c3-android-${{matrix.architecture}}-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: c3-android-${{matrix.architecture}}-${{matrix.build_type}}
path: c3-android-${{matrix.architecture}}-${{matrix.build_type}}.tar.gz
release: release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-alpine, build-openbsd, build-netbsd] needs: [build-msvc, build-linux, build-mac, build-linux-alpine, build-openbsd, build-netbsd, build-android]
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -372,6 +437,8 @@ jobs:
mv c3-linux-musl-Release/c3-linux-musl-Release.tar.gz c3-linux-musl.tar.gz mv c3-linux-musl-Release/c3-linux-musl-Release.tar.gz c3-linux-musl.tar.gz
mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd.tar.gz || true mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd.tar.gz || true
mv c3-netbsd-Release/c3-netbsd-Release.tar.gz c3-netbsd.tar.gz || true mv c3-netbsd-Release/c3-netbsd-Release.tar.gz c3-netbsd.tar.gz || true
mv c3-android-aarch64-Release/c3-android-aarch64-Release.tar.gz c3-android-aarch64.tar.gz
mv c3-android-x86_64-Release/c3-android-x86_64-Release.tar.gz c3-android-x86_64.tar.gz
# --- Debug Assets --- # --- Debug Assets ---
mv c3-windows-Debug/c3-windows-Debug.zip c3-windows-debug.zip mv c3-windows-Debug/c3-windows-Debug.zip c3-windows-debug.zip
@@ -381,6 +448,8 @@ jobs:
mv c3-linux-musl-Debug/c3-linux-musl-Debug.tar.gz c3-linux-musl-debug.tar.gz mv c3-linux-musl-Debug/c3-linux-musl-Debug.tar.gz c3-linux-musl-debug.tar.gz
mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-debug.tar.gz || true mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-debug.tar.gz || true
mv c3-netbsd-Debug/c3-netbsd-Debug.tar.gz c3-netbsd-debug.tar.gz || true mv c3-netbsd-Debug/c3-netbsd-Debug.tar.gz c3-netbsd-debug.tar.gz || true
mv c3-android-aarch64-Debug/c3-android-aarch64-Debug.tar.gz c3-android-aarch64-debug.tar.gz
mv c3-android-x86_64-Debug/c3-android-x86_64-Debug.tar.gz c3-android-x86_64-debug.tar.gz
- run: gh release delete latest-prerelease-tag --cleanup-tag -y || true - run: gh release delete latest-prerelease-tag --cleanup-tag -y || true
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV - run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
@@ -397,6 +466,8 @@ jobs:
c3-linux-musl.tar.gz c3-linux-musl.tar.gz
c3-openbsd.tar.gz c3-openbsd.tar.gz
c3-netbsd.tar.gz c3-netbsd.tar.gz
c3-android-aarch64.tar.gz
c3-android-x86_64.tar.gz
# --- Debug Artifacts --- # --- Debug Artifacts ---
c3-windows-debug.zip c3-windows-debug.zip
c3-macos-debug.zip c3-macos-debug.zip
@@ -404,3 +475,5 @@ jobs:
c3-linux-musl-debug.tar.gz c3-linux-musl-debug.tar.gz
c3-openbsd-debug.tar.gz c3-openbsd-debug.tar.gz
c3-netbsd-debug.tar.gz c3-netbsd-debug.tar.gz
c3-android-aarch64-debug.tar.gz
c3-android-x86_64-debug.tar.gz

View File

@@ -22,18 +22,20 @@ alias Pid_t = int;
alias Uid_t = uint; alias Uid_t = uint;
alias Gid_t = uint; alias Gid_t = uint;
const CUInt SA_ONSTACK = env::LINUX ? 0x08000000 : 0x0001; const CUInt SA_ONSTACK = env::LINUX || env::ANDROID ? 0x08000000 : 0x0001;
const CUInt SA_RESTART = env::LINUX ? 0x10000000 : 0x0002; const CUInt SA_RESTART = env::LINUX || env::ANDROID ? 0x10000000 : 0x0002;
const CUInt SA_RESETHAND = env::LINUX ? 0x80000000 : 0x0004; const CUInt SA_RESETHAND = env::LINUX || env::ANDROID ? 0x80000000 : 0x0004;
const CUInt SA_SIGINFO = env::LINUX ? 0x00000004 : 0x0040; const CUInt SA_SIGINFO = env::LINUX || env::ANDROID ? 0x00000004 : 0x0040;
alias Sigset_t @if((env::DARWIN || env::BSD_FAMILY) && !env::NETBSD) = uint; alias Sigset_t @if((env::DARWIN || env::BSD_FAMILY) && !env::NETBSD) = uint;
alias Sigset_t @if(env::NETBSD) = uint[4]; alias Sigset_t @if(env::NETBSD) = uint[4];
alias Sigset_t @if(env::LINUX) = ulong[16]; alias Sigset_t @if(env::LINUX) = ulong[16];
alias Sigset_t @if(env::ANDROID) = ulong;
alias SigActionFunction = fn void(CInt, void*, void*); alias SigActionFunction = fn void(CInt, void*, void*);
struct Sigaction struct Sigaction
{ {
CInt sa_flags @if(env::ANDROID);
union union
{ {
SignalFunction sa_handler; SignalFunction sa_handler;
@@ -43,20 +45,20 @@ struct Sigaction
CInt sa_flags @if(env::DARWIN || env::BSD_FAMILY ); CInt sa_flags @if(env::DARWIN || env::BSD_FAMILY );
Sigset_t sa_mask @if(env::DARWIN || env::BSD_FAMILY); Sigset_t sa_mask @if(env::DARWIN || env::BSD_FAMILY);
Sigset_t sa_mask @if(env::LINUX); Sigset_t sa_mask @if(env::LINUX || env::ANDROID);
CInt sa_flags @if(env::LINUX); CInt sa_flags @if(env::LINUX);
void* sa_restorer @if(env::LINUX); void* sa_restorer @if(env::LINUX || env::ANDROID);
} }
struct Stack_t struct Stack_t
{ {
void* ss_sp; void* ss_sp;
struct @if(!env::LINUX) struct @if(!env::LINUX && !env::ANDROID)
{ {
usz ss_size; usz ss_size;
CInt ss_flags; CInt ss_flags;
} }
struct @if(env::LINUX) struct @if(env::LINUX || env::ANDROID)
{ {
CInt ss_flags; CInt ss_flags;
usz ss_size; usz ss_size;

View File

@@ -1,7 +1,7 @@
module std::os::android @if(env::ANDROID); module std::os::android @if(env::ANDROID);
enum LogPriority : (CInt val) enum AndroidLogPriority : (CInt val)
{ {
ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT = 1, ANDROID_LOG_DEFAULT = 1,
@@ -14,7 +14,7 @@ enum LogPriority : (CInt val)
ANDROID_LOG_SILENT = 8 ANDROID_LOG_SILENT = 8
} }
enum LogId : (CInt val) enum AndroidLogId : (CInt val)
{ {
LOG_ID_MIN = 0, LOG_ID_MIN = 0,
LOG_ID_MAIN = 0, LOG_ID_MAIN = 0,
@@ -29,7 +29,7 @@ enum LogId : (CInt val)
LOG_ID_DEFAULT = 0x7FFFFFFF LOG_ID_DEFAULT = 0x7FFFFFFF
} }
struct LogMessage @packed struct AndroidLogMessage @packed
{ {
usz struct_size; usz struct_size;
CInt buffer_id; CInt buffer_id;
@@ -40,11 +40,11 @@ struct LogMessage @packed
ZString message; ZString message;
} }
extern fn CInt log_write(LogPriority prio, ZString tag, ZString text) @cname("__android_log_write"); extern fn CInt log_write(AndroidLogPriority prio, ZString tag, ZString text) @cname("__android_log_write");
extern fn CInt log_print(LogPriority prio, ZString tag, ZString fmt, ...) @cname("__android_log_print"); extern fn CInt log_print(AndroidLogPriority prio, ZString tag, ZString fmt, ...) @cname("__android_log_print");
extern fn void log_assert(ZString cond, ZString tag, ZString fmt, ...) @cname("__android_log_assert"); extern fn void log_assert(ZString cond, ZString tag, ZString fmt, ...) @cname("__android_log_assert");
fn bool log_id_is_valid(LogId id) fn bool log_id_is_valid(AndroidLogId id)
{ {
return id >= LOG_ID_MIN && id < LOG_ID_MAX; return id >= LOG_ID_MIN && id < LOG_ID_MAX;
} }
@@ -52,13 +52,13 @@ fn bool log_id_is_valid(LogId id)
extern fn CInt log_buf_write(CInt bufID, CInt prio, ZString tag, ZString text) @cname("__android_log_buf_write"); extern fn CInt log_buf_write(CInt bufID, CInt prio, ZString tag, ZString text) @cname("__android_log_buf_write");
extern fn CInt log_buf_print(CInt bufID, CInt prio, ZString tag, ZString fmt, ...) @cname("__android_log_buf_print"); extern fn CInt log_buf_print(CInt bufID, CInt prio, ZString tag, ZString fmt, ...) @cname("__android_log_buf_print");
alias LoggerFunction = fn void(LogMessage* log_message); alias LoggerFunction = fn void(AndroidLogMessage* log_message);
alias AborterFunction = fn void(ZString abort_message); alias AborterFunction = fn void(ZString abort_message);
extern fn void log_write_log_message(LogMessage log_message) @cname("__android_log_write_log_message"); extern fn void log_write_log_message(AndroidLogMessage log_message) @cname("__android_log_write_log_message");
extern fn void log_set_logger(LoggerFunction logger) @cname("__android_log_set_logger"); extern fn void log_set_logger(LoggerFunction logger) @cname("__android_log_set_logger");
extern fn void log_logd_logger(LogMessage log_message) @cname("__android_log_logd_logger"); extern fn void log_logd_logger(AndroidLogMessage log_message) @cname("__android_log_logd_logger");
extern fn void log_stderr_logger(LogMessage log_message) @cname("__android_log_stderr_logger"); extern fn void log_stderr_logger(AndroidLogMessage log_message) @cname("__android_log_stderr_logger");
extern fn void log_set_aborter(AborterFunction aborter) @cname("__android_log_set_aborter"); extern fn void log_set_aborter(AborterFunction aborter) @cname("__android_log_set_aborter");
extern fn void log_call_aborter(ZString abort_message) @cname("__android_log_call_aborter"); extern fn void log_call_aborter(ZString abort_message) @cname("__android_log_call_aborter");
extern fn void log_default_aborter(ZString abort_message) @cname("__android_log_default_aborter"); extern fn void log_default_aborter(ZString abort_message) @cname("__android_log_default_aborter");

View File

@@ -15,7 +15,7 @@ fn uint num_cpu()
return count; return count;
} }
module std::os @if(env::LINUX); module std::os @if(env::LINUX || env::ANDROID);
import std::os::posix; import std::os::posix;
fn uint num_cpu() fn uint num_cpu()

View File

@@ -12,7 +12,7 @@ const MAP_PRIVATE = 0x0002; // changes are private
const MAP_FILE = 0x0000; // map from file (default) const MAP_FILE = 0x0000; // map from file (default)
const MAP_ANONYMOUS = env::LINUX ??? 0x20 : 0x1000; // allocated from memory, swap space const MAP_ANONYMOUS = env::LINUX || env::ANDROID ??? 0x20 : 0x1000; // allocated from memory, swap space
const void* MAP_FAILED = (void *)(uptr)-1; // mmap failed const void* MAP_FAILED = (void *)(uptr)-1; // mmap failed
const MADV_NORMAL = 0; // no further special treatment const MADV_NORMAL = 0; // no further special treatment

View File

@@ -3,8 +3,8 @@ import std::thread;
import libc; import libc;
const PTHREAD_MUTEX_NORMAL = 0; const PTHREAD_MUTEX_NORMAL = 0;
const PTHREAD_MUTEX_ERRORCHECK = env::LINUX ? 2 : 1; const PTHREAD_MUTEX_ERRORCHECK = env::LINUX || env::ANDROID ? 2 : 1;
const PTHREAD_MUTEX_RECURSIVE = env::LINUX ? 1 : 2; const PTHREAD_MUTEX_RECURSIVE = env::LINUX || env::ANDROID ? 1 : 2;
alias PosixThreadFn = fn void*(void*); alias PosixThreadFn = fn void*(void*);
typedef Pthread_t = void*; typedef Pthread_t = void*;

View File

@@ -5,6 +5,7 @@
### Changes / improvements ### Changes / improvements
- Method resolution and `$define` now works together well unless definitions are out of order for real. - Method resolution and `$define` now works together well unless definitions are out of order for real.
- Improve error message when using functions as values #2856 - Improve error message when using functions as values #2856
- Improve support for Android with Termux.
### Stdlib changes ### Stdlib changes
- Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831 - Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831

View File

@@ -17,6 +17,8 @@
"c-include-dirs": [ "c-include-dirs": [
"C:\\" "C:\\"
] ]
} },
"android-aarch64": {},
"android-x86_64": {}
} }
} }

View File

@@ -12,6 +12,8 @@
"linux-x64": { "linux-x64": {
"cflags-override": "-fPIE" "cflags-override": "-fPIE"
}, },
"windows-x64": {} "windows-x64": {},
"android-aarch64": {},
"android-x86_64": {},
} }
} }

View File

@@ -118,7 +118,7 @@ run_cli_tests() {
cd "$ROOT_DIR/resources" cd "$ROOT_DIR/resources"
"$C3C_BIN" vendor-fetch raylib "$C3C_BIN" vendor-fetch raylib
if [ -f "/etc/alpine-release" ] || [[ "$SYSTEM_NAME" == "OpenBSD" ]] || [[ "$SYSTEM_NAME" == "NetBSD" ]]; then if [ -f "/etc/alpine-release" ] || [[ "$SYSTEM_NAME" == "OpenBSD" ]] || [[ "$SYSTEM_NAME" == "NetBSD" ]] || [[ "$OS_MODE" == "android" ]]; then
echo "Skipping raylib_arkanoid (vendor raylib doesn't support this platform)" echo "Skipping raylib_arkanoid (vendor raylib doesn't support this platform)"
return return
fi fi
@@ -128,8 +128,8 @@ run_cli_tests() {
run_dynlib_tests() { run_dynlib_tests() {
echo "--- Running Dynamic Lib Tests ---" echo "--- Running Dynamic Lib Tests ---"
# Skip openbsd, idk # Skip openbsd and android
if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]]; then return; fi if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]] || [[ "$OS_MODE" == "android" ]]; then return; fi
cd "$ROOT_DIR/resources/examples/dynlib-test" cd "$ROOT_DIR/resources/examples/dynlib-test"
"$C3C_BIN" -vv dynamic-lib add.c3 "$C3C_BIN" -vv dynamic-lib add.c3

View File

@@ -80,9 +80,9 @@ static void gencontext_destroy(GenContext *context)
{ {
ASSERT(llvm_is_global_eval(context)); ASSERT(llvm_is_global_eval(context));
LLVMDisposeBuilder(context->global_builder); LLVMDisposeBuilder(context->global_builder);
if (!context->shared_context) LLVMContextDispose(context->context);
LLVMDisposeTargetData(context->target_data);
LLVMDisposeTargetMachine(context->machine); LLVMDisposeTargetMachine(context->machine);
LLVMDisposeTargetData(context->target_data);
if (!context->shared_context) LLVMContextDispose(context->context);
free(context); free(context);
} }

View File

@@ -1982,6 +1982,8 @@ void *llvm_target_machine_create(void)
(LLVMCodeGenOptLevel)compiler.platform.llvm_opt_level, (LLVMCodeGenOptLevel)compiler.platform.llvm_opt_level,
reloc_mode, model); reloc_mode, model);
LLVMSetTargetMachineUseInitArray(result, true); LLVMSetTargetMachineUseInitArray(result, true);
if (compiler.platform.emulated_tls) LLVMSetTargetMachineEmulatedTLS(result, true);
if (!result) error_exit("Failed to create target machine."); if (!result) error_exit("Failed to create target machine.");
LLVMSetTargetMachineAsmVerbosity(result, 1); LLVMSetTargetMachineAsmVerbosity(result, 1);
return result; return result;
@@ -2213,6 +2215,7 @@ void target_setup(BuildTarget *target)
// ARM Cygwin // ARM Cygwin
// NVPTX // NVPTX
compiler.platform.tls_supported = os_target_use_thread_local(compiler.platform.os); compiler.platform.tls_supported = os_target_use_thread_local(compiler.platform.os);
compiler.platform.emulated_tls = compiler.platform.os == OS_TYPE_ANDROID;
compiler.platform.big_endian = arch_big_endian(compiler.platform.arch); compiler.platform.big_endian = arch_big_endian(compiler.platform.arch);
compiler.platform.width_pointer = arch_pointer_bit_width(compiler.platform.os, compiler.platform.arch); compiler.platform.width_pointer = arch_pointer_bit_width(compiler.platform.os, compiler.platform.arch);
compiler.platform.width_register = arch_int_register_bit_width(compiler.platform.os, compiler.platform.arch); compiler.platform.width_register = arch_int_register_bit_width(compiler.platform.os, compiler.platform.arch);

View File

@@ -145,6 +145,7 @@ typedef struct
} systemz; } systemz;
}; };
bool big_endian; bool big_endian;
bool emulated_tls;
bool tls_supported; bool tls_supported;
bool asm_supported; bool asm_supported;
bool float128; bool float128;

View File

@@ -79,6 +79,7 @@ bool LLVMHasUseList(LLVMValueRef value);
void LLVMBuilderSetFastMathFlags(LLVMBuilderRef Builder, FastMathOption option); void LLVMBuilderSetFastMathFlags(LLVMBuilderRef Builder, FastMathOption option);
void LLVMSetDSOLocal(LLVMValueRef Global, bool value); void LLVMSetDSOLocal(LLVMValueRef Global, bool value);
void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array); void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_array);
void LLVMSetTargetMachineEmulatedTLS(LLVMTargetMachineRef ref, bool emulated_tls);
void LLVMSetNoSanitizeAddress(LLVMValueRef Global); void LLVMSetNoSanitizeAddress(LLVMValueRef Global);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -323,6 +323,11 @@ void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_ar
auto machine = (llvm::TargetMachine*)ref; auto machine = (llvm::TargetMachine*)ref;
machine->Options.UseInitArray = use_init_array; machine->Options.UseInitArray = use_init_array;
} }
void LLVMSetTargetMachineEmulatedTLS(LLVMTargetMachineRef ref, bool emulated_tls)
{
auto machine = (llvm::TargetMachine*)ref;
machine->Options.EmulatedTLS = emulated_tls;
}
void LLVMSetDSOLocal(LLVMValueRef Global, bool value) void LLVMSetDSOLocal(LLVMValueRef Global, bool value)
{ {
llvm::unwrap<llvm::GlobalValue>(Global)->setDSOLocal(value); llvm::unwrap<llvm::GlobalValue>(Global)->setDSOLocal(value);