├── .gitignore ├── scripts ├── makeenv.sh ├── sdkenv.sh ├── toolchain.sh └── common.sh ├── config ├── gnustep-make-user.config ├── gnustep-base-cross.config └── gnustep-base-default.config ├── phases ├── 14-libiconv.sh ├── 40-gnustep-corebase.sh ├── 17-libxslt.sh ├── 11-libobjc2.sh ├── 13-libffi.sh ├── 16-libxml2.sh ├── 12-libdispatch.sh ├── 19-libcurl.sh ├── 30-gnustep-base.sh ├── 18-openssl.sh ├── 20-gnustep-make.sh └── 15-icu.sh ├── patches ├── libdispatch-fix-atomic.patch ├── libffi-forward-declare-open_temp_exec_file.patch ├── libdispatch-own-blocksruntime.patch ├── gnustep-base-langinfo.patch └── libdispatch-fix-muxnote-registration.patch ├── LICENSE.txt ├── .github └── workflows │ └── ci.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /cache 2 | /src 3 | -------------------------------------------------------------------------------- /scripts/makeenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "### Setting make environment" 4 | 5 | export GSCONFIG=${INSTALL_PREFIX}/bin/gnustep-config 6 | export ADB=${ANDROID_PLATFORM_TOOLS}/adb 7 | -------------------------------------------------------------------------------- /config/gnustep-make-user.config: -------------------------------------------------------------------------------- 1 | ### GNUstep user configuration file 2 | 3 | # remove non-existant directories from compiler/linker flags (e.g. $HOME/GNUstep/Library/Headers) 4 | REMOVE_EMPTY_DIRS=yes 5 | -------------------------------------------------------------------------------- /phases/14-libiconv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libiconv 8 | URL=https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $URL; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | echo -e "\n### Running configure" 18 | ./configure \ 19 | --host=${ANDROID_TARGET} \ 20 | --prefix="${INSTALL_PREFIX}" \ 21 | --enable-shared \ 22 | --disable-static \ 23 | 24 | echo -e "\n### Building" 25 | make -j${MAKE_JOBS} 26 | 27 | echo -e "\n### Installing" 28 | make install 29 | -------------------------------------------------------------------------------- /phases/40-gnustep-corebase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=gnustep-corebase 8 | GITHUB_REPO=gnustep/libs-corebase 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | echo "### Source GNUstep.sh" 18 | . "$INSTALL_PREFIX"/share/GNUstep/Makefiles/GNUstep.sh 19 | 20 | echo -e "\n### Running configure" 21 | ./configure \ 22 | --host=${ANDROID_TARGET} \ 23 | --prefix="${INSTALL_PREFIX}" \ 24 | 25 | echo -e "\n### Building" 26 | make -j${MAKE_JOBS} ${GNUSTEP_MAKE_OPTIONS} 27 | 28 | echo -e "\n### Installing" 29 | make install 30 | -------------------------------------------------------------------------------- /patches/libdispatch-fix-atomic.patch: -------------------------------------------------------------------------------- 1 | Fix various errors when importing stdatomic.h. 2 | 3 | This was brought up in the following NDK issues, but is still not working correctly with NDK r22 within extern "C" and with the custom atomic macros in libdispatch: 4 | https://github.com/android/ndk/issues/1177 5 | https://github.com/android/ndk/issues/1178 6 | 7 | diff --git a/src/shims/atomic.h b/src/shims/atomic.h 8 | index bc232f1..d2e6921 100644 9 | --- a/src/shims/atomic.h 10 | +++ b/src/shims/atomic.h 11 | @@ -35,7 +35,7 @@ 12 | #if defined(__cplusplus) 13 | #define _Bool bool 14 | #endif 15 | -#if defined(__ANDROID__) && __NDK_MAJOR__ >= 23 16 | +#if defined(__ANDROID__) && __has_include() 17 | #include 18 | #else 19 | #include 20 | -------------------------------------------------------------------------------- /phases/17-libxslt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libxslt 8 | GITHUB_REPO=GNOME/libxslt 9 | TAG=$(get_latest_github_release_tag $GITHUB_REPO) 10 | 11 | # load environment and prepare project 12 | if ! prepare_project $PROJECT $GITHUB_REPO $TAG; then 13 | exit 0 14 | fi 15 | 16 | . "$ROOT_DIR"/scripts/toolchain.sh 17 | 18 | echo -e "\n### Running cmake" 19 | mkdir -p build-${ABI_NAME} 20 | cd build-${ABI_NAME} 21 | 22 | ${CMAKE} .. \ 23 | ${CMAKE_OPTIONS} \ 24 | -DBUILD_SHARED_LIBS=NO \ 25 | -DLIBXSLT_WITH_PYTHON=NO \ 26 | -DLIBXSLT_WITH_TESTS=NO \ 27 | -DLIBXSLT_WITH_PROGRAMS=NO \ 28 | 29 | echo -e "\n### Building" 30 | make -j${MAKE_JOBS} 31 | 32 | echo -e "\n### Installing" 33 | make install 34 | -------------------------------------------------------------------------------- /phases/11-libobjc2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libobjc2 8 | GITHUB_REPO=gnustep/libobjc2 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | echo -e "\n### Running cmake" 18 | mkdir -p build-${ABI_NAME} 19 | cd build-${ABI_NAME} 20 | 21 | ${CMAKE} .. \ 22 | ${CMAKE_OPTIONS} \ 23 | -DGNUSTEP_CONFIG= `# prevent cmake from finding gnustep-config in install root` \ 24 | -DOLDABI_COMPAT=false `# we're using gnustep-2.0 ABI, which may not be mixed with earlier versions'` \ 25 | 26 | echo -e "\n### Building" 27 | make -j${MAKE_JOBS} 28 | 29 | echo -e "\n### Installing" 30 | make install 31 | -------------------------------------------------------------------------------- /phases/13-libffi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libffi 8 | GITHUB_REPO=libffi/libffi 9 | TAG=$(get_latest_github_release_tag $GITHUB_REPO) 10 | 11 | # load environment and prepare project 12 | if ! prepare_project $PROJECT $GITHUB_REPO $TAG; then 13 | exit 0 14 | fi 15 | 16 | . "$ROOT_DIR"/scripts/toolchain.sh 17 | 18 | echo -e "\n### Running autogen" 19 | ./autogen.sh 20 | 21 | echo -e "\n### Running configure" 22 | ./configure \ 23 | --host=${ANDROID_TARGET} \ 24 | --prefix="${INSTALL_PREFIX}" \ 25 | --disable-shared \ 26 | --disable-multi-os-directory `# fixes warning about unsupported -print-multi-os-directory with clang` \ 27 | --disable-docs \ 28 | 29 | echo -e "\n### Building" 30 | make -j${MAKE_JOBS} 31 | 32 | echo -e "\n### Installing" 33 | make install 34 | -------------------------------------------------------------------------------- /phases/16-libxml2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libxml2 8 | GITHUB_REPO=GNOME/libxml2 9 | TAG=$(get_latest_github_release_tag $GITHUB_REPO) 10 | 11 | # load environment and prepare project 12 | if ! prepare_project $PROJECT $GITHUB_REPO $TAG; then 13 | exit 0 14 | fi 15 | 16 | . "$ROOT_DIR"/scripts/toolchain.sh 17 | 18 | echo -e "\n### Running cmake" 19 | mkdir -p build-${ABI_NAME} 20 | cd build-${ABI_NAME} 21 | 22 | ${CMAKE} .. \ 23 | ${CMAKE_OPTIONS} \ 24 | -DBUILD_SHARED_LIBS=NO \ 25 | -DLIBXML2_WITH_LZMA=NO \ 26 | -DLIBXML2_WITH_PYTHON=NO \ 27 | -DLIBXML2_WITH_ZLIB=NO \ 28 | -DLIBXML2_WITH_TESTS=NO \ 29 | -DLIBXML2_WITH_PROGRAMS=NO \ 30 | -DLIBXML2_WITH_ICU=YES \ 31 | 32 | echo -e "\n### Building" 33 | make -j${MAKE_JOBS} 34 | 35 | echo -e "\n### Installing" 36 | make install 37 | -------------------------------------------------------------------------------- /phases/12-libdispatch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libdispatch 8 | GITHUB_REPO=apple/swift-corelibs-libdispatch 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | echo -e "\n### Running cmake" 18 | mkdir -p build-${ABI_NAME} 19 | cd build-${ABI_NAME} 20 | 21 | ${CMAKE} .. \ 22 | ${CMAKE_OPTIONS} \ 23 | -DBUILD_SHARED_LIBS=YES \ 24 | -DINSTALL_PRIVATE_HEADERS=YES \ 25 | `# use blocks runtime from libobjc2 with libdispatch-own-blocksruntime.patch` \ 26 | -DBlocksRuntime_INCLUDE_DIR="${INSTALL_PREFIX}/include" \ 27 | -DBlocksRuntime_LIBRARIES="${INSTALL_PREFIX}/lib/libobjc.so" \ 28 | 29 | echo -e "\n### Building" 30 | make -j${MAKE_JOBS} 31 | 32 | echo -e "\n### Installing" 33 | make install 34 | -------------------------------------------------------------------------------- /phases/19-libcurl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=libcurl 8 | GITHUB_REPO=curl/curl 9 | TAG=$(get_latest_github_release_tag $GITHUB_REPO curl-) 10 | 11 | # load environment and prepare project 12 | if ! prepare_project $PROJECT $GITHUB_REPO $TAG; then 13 | exit 0 14 | fi 15 | 16 | . "$ROOT_DIR"/scripts/toolchain.sh 17 | 18 | echo -e "\n### Running cmake" 19 | mkdir -p build-${ABI_NAME} 20 | cd build-${ABI_NAME} 21 | 22 | ${CMAKE} .. \ 23 | ${CMAKE_OPTIONS} \ 24 | -DBUILD_SHARED_LIBS=YES \ 25 | -DBUILD_CURL_EXE=NO \ 26 | -DCURL_CA_BUNDLE=NONE `# disable CA bundle path, needs to be read at runtime from app bundle` \ 27 | -DUSE_LIBIDN2=NO `# Prevent accidental detection of an idn2 installation` \ 28 | -DBUILD_LIBCURL_DOCS=NO \ 29 | -DCURL_USE_LIBPSL=NO \ 30 | -DBUILD_MISC_DOCS=NO \ 31 | 32 | echo -e "\n### Building" 33 | make -j${MAKE_JOBS} 34 | 35 | echo -e "\n### Installing" 36 | make install 37 | -------------------------------------------------------------------------------- /config/gnustep-base-cross.config: -------------------------------------------------------------------------------- 1 | # This configuration file contains the default values for AC_TRY_RUN 2 | # or AC_RUN_IFELSE checks performed by the configure script. 3 | cross_utf8literal_ok=no 4 | cross_reuseaddr_ok=0 5 | cross_gs_cv_objc_works=yes 6 | cross_VSPRINTF_RETURNS_LENGTH=1 7 | cross_VASPRINTF_RETURNS_LENGTH=1 8 | cross_NEED_WORD_ALIGNMENT=1 9 | cross_working_register_printf=1 10 | cross_wide_register_printf=1 11 | cross_gs_cv_program_invocation_name_worked=no 12 | cross_CMDLINE_TERMINATED=0 13 | cross_have_kvm_env=0 14 | cross_ffi_ok=yes 15 | cross_found_iconv_libc=no 16 | cross_found_iconv_lgiconv=no 17 | 18 | # changes from default cross.config 19 | cross_gs_cv_objc_compiler_supports_constant_string_class=yes 20 | cross_gs_cv_objc_load_method_worked=yes 21 | cross_non_fragile=yes 22 | cross_have_poll=yes 23 | cross_have_unexpected=yes 24 | cross_objc2_runtime=1 25 | cross_safe_initialize=yes 26 | cross_found_iconv_liconv=yes 27 | 28 | # Enable for true cross build 29 | if test "$cross_compiling"="yes"; then 30 | ac_cv_func_setpgrp_void=yes 31 | fi 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 algoriddim GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /phases/30-gnustep-base.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=gnustep-base 8 | GITHUB_REPO=gnustep/libs-base 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | echo "### Source GNUstep.sh" 18 | . "$INSTALL_PREFIX"/share/GNUstep/Makefiles/GNUstep.sh 19 | 20 | OPTIONS= 21 | if [[ "$ABI_NAME" != *"64"* ]]; then 22 | # remove _FILE_OFFSET_BITS definition for 32-bit 23 | # see https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md 24 | OPTIONS=--disable-largefile 25 | fi 26 | 27 | echo -e "\n### Running configure" 28 | ./configure \ 29 | --host=${ANDROID_TARGET} \ 30 | --disable-tls \ 31 | --with-cross-compilation-info=${ROOT_DIR}/config/gnustep-base-cross.config \ 32 | --with-default-config=${ROOT_DIR}/config/gnustep-base-default.config \ 33 | --with-config-file=/ \ 34 | --disable-environment-config-file \ 35 | ${OPTIONS} 36 | 37 | echo -e "\n### Building" 38 | make -j${MAKE_JOBS} ${GNUSTEP_MAKE_OPTIONS} 39 | 40 | echo -e "\n### Installing" 41 | make install 42 | -------------------------------------------------------------------------------- /phases/18-openssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=openssl 8 | GITHUB_REPO=KDAB/android_openssl 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | echo -e "\n### Installing headers" 16 | 17 | cp -Rf ssl_3/include/ ${INSTALL_PREFIX}/include 18 | 19 | echo -e "\n### Installing libraries" 20 | 21 | cp -f ssl_3/$ABI_NAME/*.so ${INSTALL_PREFIX}/lib 22 | 23 | # create version-less symlinks for libcrypto/libssl.so to versioned libraries 24 | libraries=`ls ssl_3/$ABI_NAME/*.so` 25 | cd ${INSTALL_PREFIX}/lib 26 | for lib in $libraries; do 27 | libname=`basename $lib` 28 | if [[ $libname =~ ([a-z]+)[0-9\_]+.so ]]; then 29 | ln -sf $libname ${BASH_REMATCH[1]}.so 30 | fi 31 | done 32 | 33 | echo -e "\n### Downloading CA bundle (must be installed into Android app bundle)" 34 | mkdir -p "$CACHE_ROOT" 35 | cd "$CACHE_ROOT" 36 | ETAG="cacert-etag.txt" 37 | [ -f $ETAG ] && ETAG_COMPARE="--etag-compare $ETAG" 38 | curl --show-error --fail-with-body --remote-name --etag-save $ETAG $ETAG_COMPARE https://curl.se/ca/cacert.pem 39 | mkdir -p ${INSTALL_PREFIX}/etc/ssl/ 40 | cp -f cacert.pem ${INSTALL_PREFIX}/etc/ssl/ 41 | -------------------------------------------------------------------------------- /phases/20-gnustep-make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=gnustep-make 8 | GITHUB_REPO=gnustep/tools-make 9 | 10 | # load environment and prepare project 11 | if ! prepare_project $PROJECT $GITHUB_REPO; then 12 | exit 0 13 | fi 14 | 15 | . "$ROOT_DIR"/scripts/toolchain.sh 16 | 17 | # copy user config file 18 | mkdir -p "${INSTALL_PREFIX}"/etc/GNUstep 19 | GNUSTEP_USER_CONFIG_FILE="${INSTALL_PREFIX}"/etc/GNUstep/GNUstep-user.conf 20 | cp "${ROOT_DIR}"/config/gnustep-make-user.config "${GNUSTEP_USER_CONFIG_FILE}" 21 | 22 | OPTIONS= 23 | if [ "$ABI_NAME" == "arm64-v8a" ]; then 24 | # enforce fast-path objc_msgSend() aka non-legacy dispatch (not enabled by default on arm64) 25 | # can be removed when Clang 18 containing this patch is available via the Android SDK: 26 | # https://github.com/llvm/llvm-project/pull/76694 27 | OPTIONS="OBJCFLAGS=-fno-objc-legacy-dispatch" 28 | fi 29 | 30 | echo -e "\n### Running configure" 31 | ./configure \ 32 | --host=${ANDROID_TARGET} \ 33 | --prefix="${INSTALL_PREFIX}" \ 34 | --with-library-combo=ng-gnu-gnu \ 35 | --with-user-config-file="${GNUSTEP_USER_CONFIG_FILE}" \ 36 | --with-runtime-abi=gnustep-2.2 \ 37 | ${OPTIONS} 38 | 39 | echo -e "\n### Installing" 40 | make install 41 | -------------------------------------------------------------------------------- /patches/libffi-forward-declare-open_temp_exec_file.patch: -------------------------------------------------------------------------------- 1 | From cbfb9b436ab13e4b4aba867d061e11d7f89a351c Mon Sep 17 00:00:00 2001 2 | From: serge-sans-paille 3 | Date: Wed, 1 Feb 2023 18:09:25 +0100 4 | Subject: [PATCH] Forward declare open_temp_exec_file 5 | 6 | It's defined in closures.c and used in tramp.c. 7 | Also declare it as an hidden symbol, as it should be. 8 | --- 9 | include/ffi_common.h | 4 ++++ 10 | src/tramp.c | 4 ++++ 11 | 2 files changed, 8 insertions(+) 12 | 13 | diff --git a/include/ffi_common.h b/include/ffi_common.h 14 | index 2bd31b03d..c53a79493 100644 15 | --- a/include/ffi_common.h 16 | +++ b/include/ffi_common.h 17 | @@ -128,6 +128,10 @@ void *ffi_data_to_code_pointer (void *data) FFI_HIDDEN; 18 | static trampoline. */ 19 | int ffi_tramp_is_present (void *closure) FFI_HIDDEN; 20 | 21 | +/* Return a file descriptor of a temporary zero-sized file in a 22 | + writable and executable filesystem. */ 23 | +int open_temp_exec_file(void) FFI_HIDDEN; 24 | + 25 | /* Extended cif, used in callback from assembly routine */ 26 | typedef struct 27 | { 28 | diff --git a/src/tramp.c b/src/tramp.c 29 | index b9d273a1a..c3f4c9933 100644 30 | --- a/src/tramp.c 31 | +++ b/src/tramp.c 32 | @@ -39,6 +39,10 @@ 33 | #ifdef __linux__ 34 | #define _GNU_SOURCE 1 35 | #endif 36 | + 37 | +#include 38 | +#include 39 | + 40 | #include 41 | #include 42 | #include 43 | -------------------------------------------------------------------------------- /patches/libdispatch-own-blocksruntime.patch: -------------------------------------------------------------------------------- 1 | diff --git a/CMakeLists.txt b/CMakeLists.txt 2 | index 45461eb..7c954fb 100644 3 | --- a/CMakeLists.txt 4 | +++ b/CMakeLists.txt 5 | @@ -178,6 +178,8 @@ if(NOT ANDROID) 6 | find_package(LibRT) 7 | endif() 8 | 9 | +find_package(BlocksRuntime QUIET) 10 | + 11 | check_function_exists(_pthread_workqueue_init HAVE__PTHREAD_WORKQUEUE_INIT) 12 | check_function_exists(getprogname HAVE_GETPROGNAME) 13 | check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME) 14 | @@ -331,7 +333,7 @@ add_subdirectory(dispatch) 15 | add_subdirectory(man) 16 | add_subdirectory(os) 17 | add_subdirectory(private) 18 | -if(NOT APPLE) 19 | +if(NOT BlocksRuntime_FOUND) 20 | add_subdirectory(src/BlocksRuntime) 21 | endif() 22 | add_subdirectory(src) 23 | diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt 24 | index ed5d2f4..257b02d 100644 25 | --- a/tests/CMakeLists.txt 26 | +++ b/tests/CMakeLists.txt 27 | @@ -80,9 +80,6 @@ function(add_unit_test name) 28 | # to reduce probability of test failures due to machine load. 29 | target_compile_options(${name} PRIVATE -DLENIENT_DEADLINES=1) 30 | endif() 31 | - target_include_directories(${name} 32 | - SYSTEM BEFORE PRIVATE 33 | - "${BlocksRuntime_INCLUDE_DIR}") 34 | if("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC") 35 | target_compile_options(${name} PRIVATE -Xclang -fblocks) 36 | target_compile_options(${name} PRIVATE /W3 -Wno-deprecated-declarations) 37 | -------------------------------------------------------------------------------- /patches/gnustep-base-langinfo.patch: -------------------------------------------------------------------------------- 1 | Fix using nl_langinfo() when not available. 2 | 3 | nl_langinfo() is not available on Android prior to API 26, but the header file is always present. 4 | 5 | This should be fixed more correctly via a new Autoconf compile check for nl_langinfo(), instead of using HAVE_LANGINFO_CODESET even though GSLocale.m is not using CODESET. 6 | 7 | diff --git a/Source/GSLocale.m b/Source/GSLocale.m 8 | index 43d013052..b2b994789 100644 9 | --- a/Source/GSLocale.m 10 | +++ b/Source/GSLocale.m 11 | @@ -50,7 +50,7 @@ 12 | is exposed through struct lconv. An example platform is Android. */ 13 | 14 | #include 15 | -#ifdef HAVE_LANGINFO_H 16 | +#ifdef HAVE_LANGINFO_CODESET 17 | #include 18 | #endif 19 | #import "Foundation/NSUserDefaults.h" 20 | @@ -95,7 +95,7 @@ 21 | NSMutableDictionary *dict; 22 | NSString *str1; 23 | NSString *str2; 24 | -#ifdef HAVE_LANGINFO_H 25 | +#ifdef HAVE_LANGINFO_CODESET 26 | int i; 27 | NSMutableArray *arr; 28 | #endif 29 | @@ -118,7 +118,7 @@ 30 | backupLocale = privateSetLocale(LC_ALL, nil); 31 | privateSetLocale(LC_ALL, @""); 32 | 33 | -#ifdef HAVE_LANGINFO_H 34 | +#ifdef HAVE_LANGINFO_CODESET 35 | /* Time/Date Information */ 36 | arr = [NSMutableArray arrayWithCapacity: 7]; 37 | for (i = 0; i < 7; i++) 38 | @@ -162,7 +162,7 @@ 39 | forKey: NSShortDateFormatString]; 40 | [dict setObject: GSLanginfo(T_FMT) 41 | forKey: NSTimeFormatString]; 42 | -#endif /* HAVE_LANGINFO_H */ 43 | +#endif /* HAVE_LANGINFO_CODESET */ 44 | 45 | lconv = localeconv(); 46 | 47 | -------------------------------------------------------------------------------- /patches/libdispatch-fix-muxnote-registration.patch: -------------------------------------------------------------------------------- 1 | Fix reuse when re-adding dispatch source for socket file descriptors. 2 | 3 | https://github.com/swiftlang/swift-corelibs-libdispatch/issues/833 4 | 5 | diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c 6 | index f31d13e..8654104 100644 7 | --- a/src/event/event_epoll.c 8 | +++ b/src/event/event_epoll.c 9 | @@ -268,7 +268,10 @@ _dispatch_unote_register_muxed(dispatch_unote_t du) 10 | if (events & ~_dispatch_muxnote_armed_events(dmn)) { 11 | events |= _dispatch_muxnote_armed_events(dmn); 12 | if (_dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD) < 0) { 13 | - dmn = NULL; 14 | + // The file descriptor was closed, reregister in epoll 15 | + if (_dispatch_epoll_update(dmn, events, EPOLL_CTL_ADD) < 0) { 16 | + dmn = NULL; 17 | + } 18 | } else { 19 | dmn->dmn_events |= events; 20 | dmn->dmn_disarmed_events &= ~events; 21 | @@ -319,6 +322,8 @@ _dispatch_unote_unregister_muxed(dispatch_unote_t du) 22 | dispatch_unote_linkage_t dul = _dispatch_unote_get_linkage(du); 23 | dispatch_muxnote_t dmn = dul->du_muxnote; 24 | uint32_t events = dmn->dmn_events; 25 | + int has_readers = 1; 26 | + int has_writers = 1; 27 | 28 | LIST_REMOVE(dul, du_link); 29 | _LIST_TRASH_ENTRY(dul, du_link); 30 | @@ -326,6 +331,7 @@ _dispatch_unote_unregister_muxed(dispatch_unote_t du) 31 | 32 | if (LIST_EMPTY(&dmn->dmn_readers_head)) { 33 | events &= (uint32_t)~EPOLLIN; 34 | + has_readers = 0; 35 | if (dmn->dmn_disarmed_events & EPOLLIN) { 36 | dmn->dmn_disarmed_events &= (uint16_t)~EPOLLIN; 37 | dmn->dmn_events &= (uint32_t)~EPOLLIN; 38 | @@ -333,13 +339,14 @@ _dispatch_unote_unregister_muxed(dispatch_unote_t du) 39 | } 40 | if (LIST_EMPTY(&dmn->dmn_writers_head)) { 41 | events &= (uint32_t)~EPOLLOUT; 42 | + has_writers = 0; 43 | if (dmn->dmn_disarmed_events & EPOLLOUT) { 44 | dmn->dmn_disarmed_events &= (uint16_t)~EPOLLOUT; 45 | dmn->dmn_events &= (uint32_t)~EPOLLOUT; 46 | } 47 | } 48 | 49 | - if (events & (EPOLLIN | EPOLLOUT)) { 50 | + if (events & (EPOLLIN | EPOLLOUT) || has_readers || has_writers) { 51 | if (events != _dispatch_muxnote_armed_events(dmn)) { 52 | dmn->dmn_events = events; 53 | events = _dispatch_muxnote_armed_events(dmn); 54 | -------------------------------------------------------------------------------- /scripts/sdkenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Host 4 | case "$OSTYPE" in 5 | darwin*) 6 | ANDROID_ROOT=${HOME}/Library/Android 7 | ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT:-$ANDROID_ROOT/sdk} 8 | HOST_TAG=darwin-x86_64 9 | MAKE_JOBS=`sysctl -n hw.ncpu` 10 | ;; 11 | linux*) 12 | ANDROID_ROOT=${HOME}/Android 13 | ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT:-$ANDROID_ROOT/Sdk} 14 | HOST_TAG=linux-x86_64 15 | MAKE_JOBS=`nproc` 16 | ;; 17 | *) 18 | echo "Error: Unsupported OS \"$OSTYPE\"." 19 | exit 1 20 | esac 21 | 22 | # Target (allow overrides) 23 | ABI_NAMES=${ABI_NAMES:-armeabi-v7a arm64-v8a x86 x86_64} 24 | ABI_NAME=${ABI_NAME:-armeabi-v7a} 25 | ANDROID_API_LEVEL=${ANDROID_API_LEVEL:-24} 26 | BUILD_TYPE=${BUILD_TYPE:-RelWithDebInfo} 27 | 28 | # ABI-dependant properties 29 | case $ABI_NAME in 30 | armeabi-v7a) 31 | ANDROID_TARGET=armv7a-linux-androideabi 32 | ;; 33 | arm64-v8a) 34 | ANDROID_TARGET=aarch64-linux-android 35 | ;; 36 | x86) 37 | ANDROID_TARGET=i686-linux-android 38 | ;; 39 | x86_64) 40 | ANDROID_TARGET=x86_64-linux-android 41 | ;; 42 | *) 43 | echo "Error: Unsupported ABI \"$ABI_NAME\"." 44 | echo "Supported ABIs are: armeabi-v7a, arm64-v8a, x86, x86_64" 45 | exit 1 46 | esac 47 | 48 | # Phases 49 | PHASE_GLOB="${ROOT_DIR}/phases/[0-9][0-9]-*.sh" 50 | phase_name() { 51 | name=`basename -s .sh $1` 52 | echo ${name/[0-9][0-9]-/} 53 | } 54 | 55 | # Directories 56 | SRCROOT=${SRCROOT:-$ROOT_DIR/src} 57 | CACHE_ROOT=${CACHE_ROOT:-$ROOT_DIR/cache} 58 | INSTALL_ROOT=${INSTALL_ROOT:-$ANDROID_ROOT/GNUstep} 59 | INSTALL_PREFIX=$INSTALL_ROOT/$ABI_NAME 60 | BUILD_TXT=${BUILD_TXT:-$INSTALL_ROOT/build.txt} 61 | BUILD_LOG=${BUILD_LOG:-$INSTALL_ROOT/build.log} 62 | 63 | # Android SDK/NDK 64 | if [ -z $ANDROID_NDK_ROOT ]; then 65 | ANDROID_NDK_ROOT=`ls -d $ANDROID_SDK_ROOT/ndk/*.*.* 2>/dev/null | sort -Vr | head -1` # use newest NDK 66 | fi 67 | ANDROID_PLATFORM_TOOLS=${ANDROID_PLATFORM_TOOLS:-$ANDROID_SDK_ROOT/platform-tools} 68 | 69 | # CMake 70 | CMAKE=${CMAKE:-cmake} 71 | CMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake 72 | 73 | # GNUstep Make 74 | case $BUILD_TYPE in 75 | Debug) 76 | GNUSTEP_MAKE_OPTIONS="$GNUSTEP_MAKE_OPTIONS debug=yes" 77 | ;; 78 | esac 79 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '0 4 1 * *' 9 | 10 | jobs: 11 | build: 12 | name: ${{ startsWith(matrix.os, 'macos-') && 'macOS' || 'Linux' }}-NDK-${{ matrix.ndk }} 13 | runs-on: ${{ matrix.os }} 14 | # don't run pull requests from local branches twice 15 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ macos-latest, ubuntu-latest ] 21 | ndk: [ r25c, r26d, r27d, r29 ] 22 | 23 | env: 24 | ANDROID_DIR: ${{ startsWith(matrix.os, 'macos-') && 'Library/Android' || 'Android' }} 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: nttld/setup-ndk@v1 30 | id: setup-ndk 31 | with: 32 | ndk-version: ${{ matrix.ndk }} 33 | link-to-sdk: true 34 | 35 | - name: Install packages 36 | run: | 37 | if [ "$RUNNER_OS" = "macOS" ]; then 38 | brew install autoconf automake libtool pkg-config 39 | elif [ "$RUNNER_OS" = "Linux" ]; then 40 | sudo apt-get install curl cmake make libtool pkg-config texinfo libltdl-dev 41 | 42 | # install autoconf 2.71 from source (required by libffi, not available via apt-get on Ubuntu focal) 43 | wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.71.tar.xz 44 | tar -xf autoconf-2.71.tar.xz 45 | cd autoconf-2.71 && ./configure --prefix=/usr/ && make && sudo make install 46 | autoconf --version 47 | 48 | wget http://ftp.gnu.org/gnu/automake/automake-1.16.4.tar.xz 49 | tar -xf automake-1.16.4.tar.xz 50 | cd automake-1.16.4 && ./configure --prefix=/usr/ && make && sudo make install 51 | automake --version 52 | else 53 | echo Unsupported OS $RUNNER_OS 54 | exit 1 55 | fi 56 | 57 | - name: Build toolchain 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | run: | 61 | ./build.sh --ndk $ANDROID_HOME/ndk/${{steps.setup-ndk.outputs.ndk-full-version}} --dist-root HOME/${{env.ANDROID_DIR}}/GNUstep 62 | 63 | - name: Package build 64 | run: | 65 | tar -a -cf GNUstep-Android-NDK-${{matrix.ndk}}-${{runner.os}}.tar.xz -C$HOME/${{env.ANDROID_DIR}} GNUstep 66 | 67 | - name: Upload build artifact 68 | uses: actions/upload-artifact@v4 69 | with: 70 | path: GNUstep-Android-NDK-${{matrix.ndk}}-${{runner.os}}.tar.xz 71 | name: GNUstep-Android-NDK-${{matrix.ndk}}-${{runner.os}} 72 | 73 | prerelease: 74 | needs: build 75 | runs-on: ubuntu-latest 76 | if: ${{ github.ref == 'refs/heads/master' }} 77 | 78 | steps: 79 | - name: Download artifacts 80 | uses: actions/download-artifact@v4 81 | 82 | - name: Update GitHub prerelease 83 | if: ${{ github.ref == 'refs/heads/master' }} 84 | uses: marvinpinto/action-automatic-releases@latest 85 | with: 86 | repo_token: ${{ secrets.GITHUB_TOKEN }} 87 | automatic_release_tag: latest 88 | prerelease: true 89 | title: "Latest Build" 90 | files: "**/GNUstep-Android-*.tar.xz" 91 | -------------------------------------------------------------------------------- /phases/15-icu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | . `dirname $0`/../scripts/common.sh 6 | 7 | PROJECT=icu 8 | GITHUB_REPO=unicode-org/icu 9 | TAG=$(get_latest_github_release_tag $GITHUB_REPO release-) 10 | 11 | # don't clean project for subsequent ABIs so that the build for the current 12 | # machine is preserved, and because each ABI builds into separate directory 13 | set -- $ABI_NAMES # splits space-separated ABI list into $1, $2 etc. 14 | if [ "${ABI_NAME}" != "$1" ]; then 15 | NO_CLEAN=true 16 | fi 17 | 18 | # load environment and prepare project 19 | if ! prepare_project $PROJECT $GITHUB_REPO $TAG; then 20 | exit 0 21 | fi 22 | 23 | # common configure options when building for either current machine or cross-compilation 24 | CONFIGURE_OPTIONS=" \ 25 | --disable-samples \ 26 | --disable-extras \ 27 | --disable-icuio \ 28 | --disable-layoutex \ 29 | --disable-tests \ 30 | " 31 | 32 | case "$OSTYPE" in 33 | darwin*) 34 | BUILD_PLATFORM=MacOSX 35 | ;; 36 | linux*) 37 | BUILD_PLATFORM=Linux 38 | ;; 39 | *) 40 | echo "Error: Unsupported OS \"$OSTYPE\"." 41 | exit 1 42 | esac 43 | 44 | cd icu4c 45 | 46 | CROSS_BUILD_DIR=`pwd`/build-$BUILD_PLATFORM 47 | 48 | # build for current machine if needed (i.e. only once for all architectures) 49 | 50 | if [ ! -d $CROSS_BUILD_DIR ]; then 51 | mkdir -p $CROSS_BUILD_DIR 52 | cd $CROSS_BUILD_DIR 53 | 54 | echo -e "\n### Running runConfigureICU" 55 | ../source/runConfigureICU $BUILD_PLATFORM \ 56 | --prefix="${INSTALL_PREFIX}" \ 57 | ${CONFIGURE_OPTIONS} 58 | 59 | echo -e "\n### Building for $BUILD_PLATFORM" 60 | make -j${MAKE_JOBS} 61 | 62 | cd .. 63 | fi 64 | 65 | # now cross-compile for Android 66 | 67 | . "$ROOT_DIR"/scripts/toolchain.sh 68 | 69 | mkdir -p build-${ABI_NAME} 70 | cd build-${ABI_NAME} 71 | 72 | echo -e "\n### Running configure" 73 | ../source/configure \ 74 | --host=${ANDROID_TARGET} \ 75 | --prefix="${INSTALL_PREFIX}" \ 76 | --with-cross-build=$CROSS_BUILD_DIR \ 77 | ${CONFIGURE_OPTIONS} \ 78 | 79 | echo -e "\n### Building for $ABI_NAME" 80 | make -j${MAKE_JOBS} 81 | 82 | echo -e "\n### Installing" 83 | make install 84 | 85 | cd ${INSTALL_PREFIX}/lib 86 | 87 | # delete test libraries, so they don't end up bundled in the app 88 | rm -f libicutest.so* libicutu.so* 89 | 90 | # Modify SONAME and DT_NEEDED references of all libraries to remove version number, as 91 | # they are not supported by the Android NDK. This is done hackily by replacing all 92 | # occurrences of the versioned SONAMEs to overwrite the version number with zeros. 93 | echo -e "\n### Modifying libraries SONAME" 94 | 95 | LIBS=(libicu*.so.*.*) 96 | SONAME_REPLACEMENTS= 97 | 98 | for LIB in "${LIBS[@]}"; do # => libicuXXX.so.x.y 99 | LIB_SONAME=${LIB%.*} # => libicuXXX.so.x 100 | LIB_NAME=${LIB%.*.*} # => libicuXXX.so 101 | PADDING_LEN=$((${#LIB_SONAME} - ${#LIB_NAME})) 102 | PADDING=`printf '\\\000%.0s' $(seq 1 $PADDING_LEN)` 103 | SONAME_REPLACEMENTS="$SONAME_REPLACEMENTS s/${LIB_SONAME//./\.}/${LIB_NAME}$PADDING/g;" 104 | echo "- ${LIB_SONAME} => ${LIB_NAME}" 105 | done 106 | 107 | for LIB in "${LIBS[@]}"; do 108 | perl -pe "$SONAME_REPLACEMENTS" < $LIB > $LIB.new 109 | mv -f $LIB.new $LIB 110 | done 111 | -------------------------------------------------------------------------------- /config/gnustep-base-default.config: -------------------------------------------------------------------------------- 1 | # 2 | # Android file system layout 3 | # 4 | # This is a *deployment* layout for Android application deployment, 5 | # with all the libraries and executables stored in one place. 6 | # 7 | # You can configure gnustep-base using 8 | # --with-config-file=./GNUstep.conf --with-default-config=standalone.conf 9 | # to provide built-in path information from this file, but permit it to be 10 | # overridden by a GNUstep.conf file at runtime. 11 | # 12 | # You can configure gnustep-base using 13 | # --with-config-file=./ --with-default-config=standalone.conf 14 | # to tell if to ignore any GNUstep.conf file and always use the built-in 15 | # location information obtained from this file. 16 | # 17 | 18 | GNUSTEP_DEFAULT_PREFIX= 19 | 20 | # These are only used to implement the NSUserDirectory API. 21 | # They are used literally, without the default prefix. 22 | GNUSTEP_SYSTEM_USERS_DIR=/home 23 | GNUSTEP_NETWORK_USERS_DIR=/home 24 | GNUSTEP_LOCAL_USERS_DIR=/home 25 | 26 | # This is used by gnustep-make when building/installing systems and is not 27 | # actually relevant to a deployed application. 28 | GNUSTEP_MAKEFILES=./Makefiles 29 | 30 | # The following path settings are designed to find all resources relative to 31 | # the configured location of the GNUstep config file, which should itself 32 | # be found at a location relative to the gnustep-base library. 33 | # So normally, the '.' in these paths represents the 'standalone' directory 34 | # you will have copied into your application. 35 | # The paths locate all binaries (libraries and executables) in the same 36 | # directory, but puts documentation and headers in subdirectories since 37 | # you may wish to delete those rather then distribute them with your app. 38 | GNUSTEP_SYSTEM_APPS=./ 39 | GNUSTEP_SYSTEM_ADMIN_APPS=./ 40 | GNUSTEP_SYSTEM_WEB_APPS=./ 41 | GNUSTEP_SYSTEM_TOOLS=./ 42 | GNUSTEP_SYSTEM_ADMIN_TOOLS=./ 43 | GNUSTEP_SYSTEM_LIBRARY=./ 44 | GNUSTEP_SYSTEM_HEADERS=./include 45 | GNUSTEP_SYSTEM_LIBRARIES=./ 46 | GNUSTEP_SYSTEM_DOC=./Documentation 47 | GNUSTEP_SYSTEM_DOC_MAN=./man 48 | GNUSTEP_SYSTEM_DOC_INFO=./info 49 | 50 | GNUSTEP_NETWORK_APPS=./ 51 | GNUSTEP_NETWORK_ADMIN_APPS=./ 52 | GNUSTEP_NETWORK_WEB_APPS=./ 53 | GNUSTEP_NETWORK_TOOLS=./ 54 | GNUSTEP_NETWORK_ADMIN_TOOLS=./ 55 | GNUSTEP_NETWORK_LIBRARY=./ 56 | GNUSTEP_NETWORK_HEADERS=./include 57 | GNUSTEP_NETWORK_LIBRARIES=./ 58 | GNUSTEP_NETWORK_DOC=./Documentation 59 | GNUSTEP_NETWORK_DOC_MAN=./man 60 | GNUSTEP_NETWORK_DOC_INFO=./info 61 | 62 | GNUSTEP_LOCAL_APPS=./ 63 | GNUSTEP_LOCAL_ADMIN_APPS=./ 64 | GNUSTEP_LOCAL_WEB_APPS=./ 65 | GNUSTEP_LOCAL_TOOLS=./ 66 | GNUSTEP_LOCAL_ADMIN_TOOLS=./ 67 | GNUSTEP_LOCAL_LIBRARY=./ 68 | GNUSTEP_LOCAL_HEADERS=./include 69 | GNUSTEP_LOCAL_LIBRARIES=./ 70 | GNUSTEP_LOCAL_DOC=./Documentation 71 | GNUSTEP_LOCAL_DOC_MAN=./man 72 | GNUSTEP_LOCAL_DOC_INFO=./info 73 | 74 | GNUSTEP_USER_DIR_APPS=Applications 75 | GNUSTEP_USER_DIR_ADMIN_APPS=Applications/Admin 76 | GNUSTEP_USER_DIR_WEB_APPS=WebApplications 77 | GNUSTEP_USER_DIR_TOOLS=Tools 78 | GNUSTEP_USER_DIR_ADMIN_TOOLS=Tools/Admin 79 | GNUSTEP_USER_DIR_LIBRARY=Library 80 | GNUSTEP_USER_DIR_HEADERS=Library/Headers 81 | GNUSTEP_USER_DIR_LIBRARIES=Library/Libraries 82 | GNUSTEP_USER_DIR_DOC=Library/Documentation 83 | GNUSTEP_USER_DIR_DOC_MAN=Library/Documentation/man 84 | GNUSTEP_USER_DIR_DOC_INFO=Library/Documentation/info 85 | 86 | GNUSTEP_USER_CONFIG_FILE= 87 | GNUSTEP_USER_DEFAULTS_DIR=Library/Preferences 88 | -------------------------------------------------------------------------------- /scripts/toolchain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "### Set toolchain vars" 4 | 5 | # Relevant documentation: 6 | # https://developer.android.com/ndk/guides/other_build_systems 7 | # https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md 8 | 9 | export TOOLCHAIN="${ANDROID_NDK_ROOT}"/toolchains/llvm/prebuilt/${HOST_TAG} 10 | export CC="${TOOLCHAIN}"/bin/${ANDROID_TARGET}${ANDROID_API_LEVEL}-clang 11 | export CXX="${TOOLCHAIN}"/bin/${ANDROID_TARGET}${ANDROID_API_LEVEL}-clang++ 12 | export OBJC="${CC}" 13 | export OBJCXX="${CXX}" 14 | export LD="${TOOLCHAIN}"/bin/ld.lld 15 | export AR="${TOOLCHAIN}"/bin/llvm-ar 16 | export AS="${CC}" 17 | export RANLIB="${TOOLCHAIN}"/bin/llvm-ranlib 18 | export STRIP="${TOOLCHAIN}"/bin/llvm-strip 19 | export NM="${TOOLCHAIN}"/bin/llvm-nm 20 | export OBJDUMP="${TOOLCHAIN}"/bin/llvm-objdump 21 | export PKG_CONFIG_PATH="${INSTALL_PREFIX}/lib/pkgconfig" 22 | 23 | # always generate debug info and build with optimizations 24 | if [ ! -n "${OPTFLAG+set}" ]; then 25 | OPTFLAG="-g -O2" 26 | fi 27 | 28 | # NOTE: The following compiler and linker flags mirror the NDK's CMake toolchain file 29 | # and are recommended by the Android Build System Maintainers Guide (see link above) 30 | 31 | # - emit stack guards to protect against security vulnerabilities caused by buffer overruns 32 | # - enable FORTIFY to try to catch incorrect use of standard functions 33 | # - generate position-independent code (PIC) to remove unsupported text relocations 34 | export CFLAGS="$OPTFLAG -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIC" 35 | 36 | # -L library search path required for some projects to find libraries (e.g. gnustep-corebase) 37 | # -fuse-ld=lld require to enforce LLD, which is needed e.g. for --no-rosegment flag 38 | # --build-id=sha1 required for Android Studio to locate debug information 39 | # --no-rosegment required for correct unwinding on devices prior to API 29 40 | # --gc-sections is recommended to decrease binary size 41 | export LDFLAGS="-L${INSTALL_PREFIX}/lib -fuse-ld=lld -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--gc-sections" 42 | 43 | ADDITIONAL_CMAKE_FLAGS= 44 | case $ABI_NAME in 45 | armeabi-v7a) 46 | # use Thumb instruction set for smaller code 47 | export CFLAGS="$CFLAGS -mthumb" 48 | # don't export symbols from libunwind 49 | export LDFLAGS="$LDFLAGS -Wl,--exclude-libs,libunwind.a" 50 | ;; 51 | arm64-v8a) 52 | export LDFLAGS="$LDFLAGS -Wl,-z,max-page-size=16384" 53 | ADDITIONAL_CMAKE_FLAGS="-Wl,-z,max-page-size=16384" 54 | ;; 55 | x86) 56 | # properly align stacks for global constructors when targeting API < 24 57 | if [ "$ANDROID_API_LEVEL" -lt "24" ]; then 58 | export CFLAGS="$CFLAGS -mstackrealign" 59 | fi 60 | ;; 61 | x86_64) 62 | export LDFLAGS="$LDFLAGS -Wl,-z,max-page-size=16384" 63 | ADDITIONAL_CMAKE_FLAGS="-Wl,-z,max-page-size=16384" 64 | ;; 65 | esac 66 | 67 | export CXXFLAGS=$CFLAGS 68 | 69 | # ensure libraries link against shared C++ runtime library 70 | export LIBS="-lc++_shared" 71 | 72 | # common options for CMake-based projects 73 | # CMAKE_FIND_USE_CMAKE_PATH=false fixes finding incorrect libraries in $TOOLCHAIN/lib[64] instead of $TOOLCHAIN/sysroot/usr/lib/$ANDROID_TARGET, e.g. for libc++abi.a for libobjc2. 74 | CMAKE_OPTIONS=" \ 75 | -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \ 76 | -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 77 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} \ 78 | -DCMAKE_FIND_USE_CMAKE_PATH=false \ 79 | -DCMAKE_FIND_ROOT_PATH=${INSTALL_PREFIX} \ 80 | -DANDROID_ABI=${ABI_NAME} \ 81 | -DANDROID_NDK=${ANDROID_NDK_ROOT} \ 82 | -DANDROID_PLATFORM=android-${ANDROID_API_LEVEL} \ 83 | -DANDROID_STL=c++_shared \ 84 | -DCMAKE_SHARED_LINKER_FLAGS=$ADDITIONAL_CMAKE_FLAGS \ 85 | " 86 | -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # make any subsequent failing command exit the script 4 | 5 | cd `dirname $0`/.. 6 | export ROOT_DIR="$PWD" 7 | 8 | # load environment 9 | . "$ROOT_DIR"/scripts/sdkenv.sh 10 | 11 | get_latest_github_release_tag () { 12 | GITHUB_REPO=$1 13 | TAG_PREFIX=$2 14 | 15 | # use GitHub token authentication on CI to prevent rate limit errors 16 | if [ -n "$GITHUB_TOKEN" ]; then 17 | GITHUB_AUTHORIZATION_HEADER="Authorization: Bearer $GITHUB_TOKEN" 18 | fi 19 | 20 | # get the tags JSON from the GitHub API and parse it manually, 21 | # or output it to stderr if the server returns an error 22 | # per_page=100 is required for some repositories with a lot of beta tags 23 | github_tags=`curl \ 24 | --silent --show-error --fail-with-body \ 25 | --header "$GITHUB_AUTHORIZATION_HEADER" \ 26 | https://api.github.com/repos/$GITHUB_REPO/tags?per_page=100` 27 | 28 | echo "$github_tags" \ 29 | | grep '"name":' \ 30 | | sed -E 's/.*"([^"]+)".*/\1/' \ 31 | | egrep "^${TAG_PREFIX:-[a-z_-]+}[0-9]+[\._-][0-9]+([\._-][0-9]+)?\$" \ 32 | | head -n 1 33 | } 34 | 35 | prepare_project () { 36 | PROJECT=$1 37 | REPO=$2 38 | TAG=$3 39 | 40 | # allow passing GitHub username/repo 41 | if [[ $REPO != http* ]]; then 42 | REPO=https://github.com/$REPO.git 43 | fi 44 | 45 | if [ ${REPO##*.} = "gz" ]; then 46 | # downloaded project 47 | 48 | archive_name=`basename $REPO` 49 | 50 | if [ ! -f "$CACHE_ROOT/$archive_name" ]; then 51 | echo -e "\n### Downloading project" 52 | mkdir -p "$CACHE_ROOT" 53 | cd "$CACHE_ROOT" 54 | curl -O -# $REPO 55 | fi 56 | 57 | cd "$SRCROOT" 58 | 59 | if [ ! -d "$PROJECT" -o "$NO_CLEAN" != true ]; then 60 | echo -e "\n### Extracting project" 61 | rm -rf $PROJECT 62 | mkdir $PROJECT 63 | tar -xzf "$CACHE_ROOT/$archive_name" -C $PROJECT --strip-components 1 64 | fi 65 | 66 | cd "$PROJECT" 67 | 68 | else 69 | # git project 70 | 71 | cd "$SRCROOT" 72 | 73 | if [ ! -d "$PROJECT" ]; then 74 | echo -e "\n### Cloning project" 75 | git clone --recursive $REPO $PROJECT 76 | fi 77 | 78 | cd "$PROJECT" 79 | 80 | if [ "$NO_CLEAN" != true ]; then 81 | echo -e "\n### Cleaning project" 82 | git clean -qfdx 83 | git submodule foreach --recursive git clean -qfdx 84 | git reset --hard 85 | git submodule foreach --recursive git reset --hard 86 | fi 87 | 88 | if [ "$NO_UPDATE" != true ]; then 89 | # check out tag/branch if any 90 | if [ -n "$TAG" ]; then 91 | echo -e "\n### Checking out \"$TAG\"" 92 | git fetch --tags 93 | git checkout -q $TAG 94 | fi 95 | 96 | # check if we should update project 97 | git_branch=`git symbolic-ref --short -q HEAD || echo "NONE"` 98 | if [ "$git_branch" != "NONE" ]; then 99 | # check if current branch has a remote 100 | git_remote=`git config --get branch.$git_branch.remote || echo "NONE"` 101 | if [ "$git_remote" != "NONE" ]; then 102 | echo -e "\n### Updating project" 103 | git pull --ff-only 104 | else 105 | echo -e "\n### NOT updating project (no remote for branch $git_branch)" 106 | fi 107 | elif [ -z $TAG ]; then 108 | echo -e "\n### NOT updating project (not on branch)" 109 | fi 110 | 111 | git submodule sync --recursive 112 | git submodule update --recursive --init # also init in case submodule was added with update 113 | fi 114 | 115 | fi 116 | 117 | if [ "$NO_PATCHES" != true ]; then 118 | for patch in {"${ROOT_DIR}"/patches,${ADDITIONAL_PATCHES}}/${PROJECT}-*.patch; do 119 | if [ -f $patch ] ; then 120 | patch_name=`basename "$patch"` 121 | echo -e "\n### Applying $patch_name" 122 | patch -p1 --forward < "$patch" || [ $? -eq 1 ] 123 | fi 124 | done 125 | fi 126 | 127 | if [ "$NO_BUILD" != true ]; then 128 | mkdir -p "${INSTALL_PREFIX}" 129 | return 0 130 | else 131 | return 1 132 | fi 133 | } 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GNUstep Android Toolchain 2 | ========================= 3 | 4 | [![CI](https://github.com/gnustep/tools-android/actions/workflows/ci.yml/badge.svg)](https://github.com/gnustep/tools-android/actions/workflows/ci.yml?query=branch%3Amaster) 5 | 6 | This project comprises a collection of scripts to build a GNUstep toolchain for Android. The toolchain can then be used in an Android project to compile and run Objective-C code using the Foundation and CoreFoundation libraries. 7 | 8 | The toolchain is built using the Android NDK (installed e.g. via [Android Studio](https://developer.android.com/studio)), and is set up to target Android API level 24 (7.0 / Nougat) and all available Android ABIs (armeabi-v7a, arm64-v8a, x86, x86_64). 9 | 10 | 11 | Libraries 12 | --------- 13 | 14 | The toolchain consists of the following libraries: 15 | 16 | * [GNUstep Base Library](https://github.com/gnustep/libs-base) (Foundation) 17 | * [GNUstep CoreBase Library](https://github.com/gnustep/libs-corebase) (CoreFoundation) 18 | * [libobjc2](https://github.com/gnustep/libobjc2) (using gnustep-2.0 runtime) 19 | * [libdispatch](https://github.com/apple/swift-corelibs-libdispatch) (official Apple release from the Swift Core Libraries) 20 | * [libffi](https://github.com/libffi/libffi) 21 | * [libiconv](https://www.gnu.org/software/libiconv/) 22 | * [libxml2](https://github.com/GNOME/libxml2) 23 | * [libxslt](https://github.com/GNOME/libxslt) 24 | * [libcurl](https://github.com/curl/curl) 25 | * [OpenSSL](https://github.com/KDAB/android_openssl) 26 | * [ICU](https://github.com/unicode-org/icu) 27 | 28 | 29 | Requirements 30 | ------------ 31 | 32 | Supported host platforms are macOS and Linux. 33 | 34 | The toolchain requires the Android NDK, which can be installed via [Android Studio](https://developer.android.com/studio)’s SDK Manager. The NDK installation will be located using the `ANDROID_NDK_ROOT` environment variable if set, or otherwise it will use the latest version in `~/Library/Android/sdk/ndk` (macOS) / `~/Android/sdk/ndk` (Linux). A different NDK path can also be provided using the `--ndk` flag when building the toolchain (see below). 35 | 36 | Please note that NDK r21 or later is required, as earlier NDK releases contain Clang versions with bugs which prevent usage of the gnustep-2.0 Objective-C runtime. 37 | 38 | 39 | Installation 40 | ------------ 41 | 42 | To install a pre-built release, download it from [the releases on GitHub](https://github.com/gnustep/tools-android/releases) and unpack the "GNUstep" folder into the following location depending on your OS (`$GNUSTEP_HOME`): 43 | 44 | * macOS: `~/Library/Android/GNUstep` 45 | * Linux: `~/Android/GNUstep` 46 | 47 | 48 | Using the Toolchain 49 | ------------------- 50 | 51 | The [android-examples](https://github.com/gnustep/android-examples) repository contains Android Studio and Qt example projects using this project that can be used as templates for integrating Objective-C code into new or existing Android projects. 52 | 53 | To use the toolchain from an Android project, you can use `$GNUSTEP_HOME/$ABI_NAME/bin/gnustep-config` to obtain various flags that should be used to compile and link Objective-C files, e.g.: 54 | 55 | * `gnustep-config --variable=CC` 56 | * `gnustep-config --objc-flags` (or `--debug-flags`) 57 | * `gnustep-config --base-libs` 58 | 59 | Call `gnustep-config --help` to obtain the full list of available variables. 60 | 61 | 62 | Status and Known Issues 63 | ----------------------- 64 | 65 | The toolchain is being used in production in an award-winnning music app available on Google Play. 66 | 67 | Following are some notes and known issues about using GNUstep on Android. 68 | 69 | * GNUstep base currently has no native integration between the Android run-loop and NSRunLoop or the libdispatch main queue, so things like `-performSelector:withObject:afterDelay:` or dispatching on `dispatch_get_main_queue()` will not work out of the box. An integration depends on the setup of the app (e.g. whether using Android Studio, Qt, or something else), and is possible to add in the app by swizzing NSRunLoop. Feel free to open an issue if this is of interest to you and you would like more information. 70 | * GNUstep Base is integrated with Android’s [app-specific storage](https://developer.android.com/training/data-storage) and uses the path returned by `Context.getFilesDir()` as `NSHomeDirectory()` and when querying for directory paths (`NSLibraryDirectory`, `NSApplicationSupportDirectory`, etc.). It also uses `Context.getCacheDir()` as `NSTemporaryDirectory` and `NSCachesDirectory` (with `NSUserDomainMask`). 71 | * GNUstep Base is further integrated with the [Android asset manager](https://developer.android.com/reference/android/content/res/AssetManager), and supports accessing the app’s resources from `[NSBundle mainBundle]` via APIs such as `-pathForResource:ofType:` and `-URLForResource:ofType:`, and reading them using NSFileManager, NSFileHandle, and NSDirectoryEnumerator APIs. This is done by returning paths from NSBundle APIs with a fixed, fake, per-app prefix (`Context.getPackageCodePath()` without extension + `/Resources`), which internally get routed through the NDK’s [AAsset](https://developer.android.com/ndk/reference/group/asset) API for reading. 72 | * Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a [limitation](https://issuetracker.google.com/issues/140538113) of the AAssetDir API. 73 | * The app must call `GSInitializeProcessAndroid()` (defined in `NSProcessInfo.h`) on launch in order to initialize the above Android-specific functionality in GNUstep. 74 | * GNUstep Base doesn’t currently get the system languages on Android, which combined with the inability to list directories in the main bundle (see above) means that `NSLocalizedString()` won’t work out of the box even if localized strings are present in the app’s assets. As a workaround, the app should manually call `-[NSUserDefaults setUserLanguages:]` with a list of supported locales ordered by the user’s system language preferences. 75 | * GNUstep Base will also currently not return the system locale as the current `NSLocale` on Android (the current locale will always default to `en_US_POSIX`). As a workaround, the app can manually set the system’s locale identifier for the key `"Locale"` in NSUserDefaults, and use `-[NSLocale autoupdatingCurrentLocale]` to retreive the locale. 76 | * Android will not output stdout or stderr to logcat by default, which might cause some log or error output from GNUstep or other libraries to be missing. You can run a thread in your app to write these streams to the Android log to work around this, which is recommended for debugging. 77 | 78 | For the last three points above please refer to [GSInitialize.m](https://github.com/gnustep/android-examples/blob/master/hello-objectivec/app/src/main/cpp/GSInitialize.m) in the examples for details. 79 | 80 | 81 | Prerequisites for Building 82 | -------------------------- 83 | 84 | The following packages are required for building the toolchain. 85 | 86 | **macOS** 87 | 88 | Install required packages via [Homebrew](https://brew.sh): 89 | 90 | ``` 91 | brew install git-lfs cmake autoconf automake libtool pkg-config 92 | git lfs install 93 | ``` 94 | 95 | **Linux** 96 | 97 | Install required packages via APT: 98 | 99 | ``` 100 | sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils 101 | git lfs install 102 | ``` 103 | 104 | Please note that you need to have CMake version 3.15.1 or later ([for libdispatch](https://github.com/apple/swift-corelibs-libdispatch/blob/master/CMakeLists.txt#L2)). 105 | 106 | 107 | Building the Toolchain 108 | ---------------------- 109 | 110 | Run the [build.sh](build.sh) script to build the toolchain yourself: 111 | 112 | ``` 113 | Usage: ./build.sh 114 | --prefix INSTALL_ROOT Install toolchain into given directory (default: see below) 115 | --dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines 116 | (use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep") 117 | -n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT) 118 | -a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64") 119 | -l, --level API_LEVEL Android API level being targeted (default: 23) 120 | -b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo) 121 | -u, --no-update Don't update projects to latest version from GitHub 122 | -c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built) 123 | -p, --patches DIR Apply additional patches from given directory 124 | -o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build) 125 | -h, --help Print usage information and exit 126 | ``` 127 | 128 | The toolchain builds and installs the GNUstep toolchain into the following location (`$GNUSTEP_HOME`): 129 | 130 | * macOS: `~/Library/Android/GNUstep` 131 | * Linux: `~/Android/GNUstep` 132 | 133 | The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI). 134 | 135 | 136 | Acknowledgements 137 | ---------------- 138 | 139 | Based on original work by Ivan Vučica. 140 | --------------------------------------------------------------------------------