diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1601b88a0..aa4c15472 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/lib/std/libc/os/posix.c3 b/lib/std/libc/os/posix.c3 index 3b6f2e880..1924fae1a 100644 --- a/lib/std/libc/os/posix.c3 +++ b/lib/std/libc/os/posix.c3 @@ -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; diff --git a/lib/std/os/android/log.c3 b/lib/std/os/android/log.c3 index 0d4a7bf65..479f5c5c3 100644 --- a/lib/std/os/android/log.c3 +++ b/lib/std/os/android/log.c3 @@ -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"); diff --git a/lib/std/os/cpu.c3 b/lib/std/os/cpu.c3 index 0754210cc..8e7ffb09c 100644 --- a/lib/std/os/cpu.c3 +++ b/lib/std/os/cpu.c3 @@ -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() diff --git a/lib/std/os/posix/mman.c3 b/lib/std/os/posix/mman.c3 index a0e72b480..4adfa00a9 100644 --- a/lib/std/os/posix/mman.c3 +++ b/lib/std/os/posix/mman.c3 @@ -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(); \ No newline at end of file +extern fn CInt getpagesize(); diff --git a/lib/std/os/posix/threads.c3 b/lib/std/os/posix/threads.c3 index 4a21c6a75..b985f4d1e 100644 --- a/lib/std/os/posix/threads.c3 +++ b/lib/std/os/posix/threads.c3 @@ -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*; diff --git a/releasenotes.md b/releasenotes.md index 4768ada0d..f259184a7 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -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 diff --git a/resources/testproject/lib/clib.c3l/manifest.json b/resources/testproject/lib/clib.c3l/manifest.json index fd9b8ca5b..a5913b5b8 100644 --- a/resources/testproject/lib/clib.c3l/manifest.json +++ b/resources/testproject/lib/clib.c3l/manifest.json @@ -17,6 +17,8 @@ "c-include-dirs": [ "C:\\" ] - } + }, + "android-aarch64": {}, + "android-x86_64": {} } } diff --git a/resources/testproject/lib/clib2.c3l/manifest.json b/resources/testproject/lib/clib2.c3l/manifest.json index 7eec8c903..14e9f7e6a 100644 --- a/resources/testproject/lib/clib2.c3l/manifest.json +++ b/resources/testproject/lib/clib2.c3l/manifest.json @@ -12,6 +12,8 @@ "linux-x64": { "cflags-override": "-fPIE" }, - "windows-x64": {} + "windows-x64": {}, + "android-aarch64": {}, + "android-x86_64": {}, } } diff --git a/scripts/tools/ci_tests.sh b/scripts/tools/ci_tests.sh index ec46d3d5f..2c07025cc 100755 --- a/scripts/tools/ci_tests.sh +++ b/scripts/tools/ci_tests.sh @@ -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 diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 698781883..e4b8d4297 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -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); } diff --git a/src/compiler/target.c b/src/compiler/target.c index 5540bd2ef..62d0c7e9a 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -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); diff --git a/src/compiler/target.h b/src/compiler/target.h index 1edf3ff4c..b6e23b2c6 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -145,6 +145,7 @@ typedef struct } systemz; }; bool big_endian; + bool emulated_tls; bool tls_supported; bool asm_supported; bool float128; diff --git a/wrapper/include/c3_llvm.h b/wrapper/include/c3_llvm.h index 4728e3aff..342d6fce5 100644 --- a/wrapper/include/c3_llvm.h +++ b/wrapper/include/c3_llvm.h @@ -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 \ No newline at end of file +#endif diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index f9a32cb95..ef0c6f5b2 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -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(Global)->setDSOLocal(value);