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
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:
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'
env:
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-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-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 ---
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-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-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: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
@@ -397,6 +466,8 @@ jobs:
c3-linux-musl.tar.gz
c3-openbsd.tar.gz
c3-netbsd.tar.gz
c3-android-aarch64.tar.gz
c3-android-x86_64.tar.gz
# --- Debug Artifacts ---
c3-windows-debug.zip
c3-macos-debug.zip
@@ -404,3 +475,5 @@ jobs:
c3-linux-musl-debug.tar.gz
c3-openbsd-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 Gid_t = uint;
const CUInt SA_ONSTACK = env::LINUX ? 0x08000000 : 0x0001;
const CUInt SA_RESTART = env::LINUX ? 0x10000000 : 0x0002;
const CUInt SA_RESETHAND = env::LINUX ? 0x80000000 : 0x0004;
const CUInt SA_SIGINFO = env::LINUX ? 0x00000004 : 0x0040;
const CUInt SA_ONSTACK = env::LINUX || env::ANDROID ? 0x08000000 : 0x0001;
const CUInt SA_RESTART = env::LINUX || env::ANDROID ? 0x10000000 : 0x0002;
const CUInt SA_RESETHAND = env::LINUX || env::ANDROID ? 0x80000000 : 0x0004;
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::NETBSD) = uint[4];
alias Sigset_t @if(env::LINUX) = ulong[16];
alias Sigset_t @if(env::ANDROID) = ulong;
alias SigActionFunction = fn void(CInt, void*, void*);
struct Sigaction
{
CInt sa_flags @if(env::ANDROID);
union
{
SignalFunction sa_handler;
@@ -43,20 +45,20 @@ struct Sigaction
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::LINUX);
Sigset_t sa_mask @if(env::LINUX || env::ANDROID);
CInt sa_flags @if(env::LINUX);
void* sa_restorer @if(env::LINUX);
void* sa_restorer @if(env::LINUX || env::ANDROID);
}
struct Stack_t
{
void* ss_sp;
struct @if(!env::LINUX)
struct @if(!env::LINUX && !env::ANDROID)
{
usz ss_size;
CInt ss_flags;
}
struct @if(env::LINUX)
struct @if(env::LINUX || env::ANDROID)
{
CInt ss_flags;
usz ss_size;

View File

@@ -1,7 +1,7 @@
module std::os::android @if(env::ANDROID);
enum LogPriority : (CInt val)
enum AndroidLogPriority : (CInt val)
{
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT = 1,
@@ -14,7 +14,7 @@ enum LogPriority : (CInt val)
ANDROID_LOG_SILENT = 8
}
enum LogId : (CInt val)
enum AndroidLogId : (CInt val)
{
LOG_ID_MIN = 0,
LOG_ID_MAIN = 0,
@@ -29,7 +29,7 @@ enum LogId : (CInt val)
LOG_ID_DEFAULT = 0x7FFFFFFF
}
struct LogMessage @packed
struct AndroidLogMessage @packed
{
usz struct_size;
CInt buffer_id;
@@ -40,11 +40,11 @@ struct LogMessage @packed
ZString message;
}
extern fn CInt log_write(LogPriority 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_write(AndroidLogPriority prio, ZString tag, ZString text) @cname("__android_log_write");
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");
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;
}
@@ -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_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);
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_logd_logger(LogMessage 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_logd_logger(AndroidLogMessage log_message) @cname("__android_log_logd_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_call_aborter(ZString abort_message) @cname("__android_log_call_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;
}
module std::os @if(env::LINUX);
module std::os @if(env::LINUX || env::ANDROID);
import std::os::posix;
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_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 MADV_NORMAL = 0; // no further special treatment
@@ -26,4 +26,4 @@ extern fn CInt munmap(void*, usz);
extern fn CInt mprotect(void*, usz, CInt);
extern fn int madvise(void*, usz, CInt);
extern fn CInt getpagesize();
extern fn CInt getpagesize();

View File

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

View File

@@ -5,6 +5,7 @@
### Changes / improvements
- 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 support for Android with Termux.
### Stdlib changes
- 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:\\"
]
}
},
"android-aarch64": {},
"android-x86_64": {}
}
}

View File

@@ -12,6 +12,8 @@
"linux-x64": {
"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"
"$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)"
return
fi
@@ -128,8 +128,8 @@ run_cli_tests() {
run_dynlib_tests() {
echo "--- Running Dynamic Lib Tests ---"
# Skip openbsd, idk
if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]]; then return; fi
# Skip openbsd and android
if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]] || [[ "$OS_MODE" == "android" ]]; then return; fi
cd "$ROOT_DIR/resources/examples/dynlib-test"
"$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));
LLVMDisposeBuilder(context->global_builder);
if (!context->shared_context) LLVMContextDispose(context->context);
LLVMDisposeTargetData(context->target_data);
LLVMDisposeTargetMachine(context->machine);
LLVMDisposeTargetData(context->target_data);
if (!context->shared_context) LLVMContextDispose(context->context);
free(context);
}

View File

@@ -1982,6 +1982,8 @@ void *llvm_target_machine_create(void)
(LLVMCodeGenOptLevel)compiler.platform.llvm_opt_level,
reloc_mode, model);
LLVMSetTargetMachineUseInitArray(result, true);
if (compiler.platform.emulated_tls) LLVMSetTargetMachineEmulatedTLS(result, true);
if (!result) error_exit("Failed to create target machine.");
LLVMSetTargetMachineAsmVerbosity(result, 1);
return result;
@@ -2213,6 +2215,7 @@ void target_setup(BuildTarget *target)
// ARM Cygwin
// NVPTX
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.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);

View File

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

View File

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

View File

@@ -323,6 +323,11 @@ void LLVMSetTargetMachineUseInitArray(LLVMTargetMachineRef ref, bool use_init_ar
auto machine = (llvm::TargetMachine*)ref;
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)
{
llvm::unwrap<llvm::GlobalValue>(Global)->setDSOLocal(value);