Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

GNUstep Android Toolchain

CI

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.

The toolchain is built using the Android NDK (installed e.g. via Android 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).

Libraries

The toolchain consists of the following libraries:

Requirements

Supported host platforms are macOS and Linux.

The toolchain requires the Android NDK, which can be installed via Android 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).

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.

Installation

To install a pre-built release, download it from the releases on GitHub and unpack the "GNUstep" folder into the following location depending on your OS ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

Using the Toolchain

The 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.

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.:

  • gnustep-config --variable=CC
  • gnustep-config --objc-flags (or --debug-flags)
  • gnustep-config --base-libs

Call gnustep-config --help to obtain the full list of available variables.

Status and Known Issues

The toolchain is being used in production in an award-winnning music app available on Google Play.

Following are some notes and known issues about using GNUstep on Android.

  • 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.
  • GNUstep Base is integrated with Android’s app-specific 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).
  • GNUstep Base is further integrated with the Android asset manager, 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 API for reading.
  • Note that NSDirectoryEnumerator is not able to enumerate directories in the app’s main bundle due to a limitation of the AAssetDir API.
  • The app must call GSInitializeProcessAndroid() (defined in NSProcessInfo.h) on launch in order to initialize the above Android-specific functionality in GNUstep.
  • 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.
  • 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.
  • 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.

For the last three points above please refer to GSInitialize.m in the examples for details.

Prerequisites for Building

The following packages are required for building the toolchain.

macOS

Install required packages via Homebrew:

brew install git-lfs cmake autoconf automake libtool pkg-config
git lfs install

Linux

Install required packages via APT:

sudo apt install git git-lfs curl cmake make autoconf libtool pkg-config texinfo python3-distutils
git lfs install

Please note that you need to have CMake version 3.15.1 or later (for libdispatch).

Building the Toolchain

Run the build.sh script to build the toolchain yourself:

Usage: ./build.sh
--prefix INSTALL_ROOT Install toolchain into given directory (default: see below)
--dist-root DIST_ROOT Make toolchain relocatable to given path relative to home folder on other machines
(use "HOME" as placeholder for home folder, e.g. "HOME/Library/Android/GNUstep")
-n, --ndk NDK_PATH Path to existing Android NDK (default: ANDROID_NDK_ROOT)
-a, --abis ABI_NAMES ABIs being targeted (default: "armeabi-v7a arm64-v8a x86 x86_64")
-l, --level API_LEVEL Android API level being targeted (default: 23)
-b, --build BUILD_TYPE Build type "Debug" or "Release" or "RelWithDebInfo" (default: RelWithDebInfo)
-u, --no-update Don't update projects to latest version from GitHub
-c, --no-clean Don't clean projects during build (e.g. for building local changes, only applies to first ABI being built)
-p, --patches DIR Apply additional patches from given directory
-o, --only PHASE Build only the given phase (e.g. "gnustep-base", requires previous build)
-h, --help Print usage information and exit

The toolchain builds and installs the GNUstep toolchain into the following location ($GNUSTEP_HOME):

  • macOS: ~/Library/Android/GNUstep
  • Linux: ~/Android/GNUstep

The build for each supported ABI is installed into its separate subfolder at that location (both libraries and header files differ per ABI).

Acknowledgements

Based on original work by Ivan Vučica.

About

Objective-C on Android with Foundation, CoreFoundation, and libdispatch.

Topics

Resources

Stars

50 stars

Watchers

9 watching

Forks

Releases

Used by

Contributors

Languages