Skip to main content
This guide covers cross-compilation of Giac for various target platforms, with a focus on Android cross-compilation from Linux.

Android Cross-Compilation

This tutorial explains how to create Linux → Android cross-compilers and compile the Giac library for Android. The steps have been tested on Kubuntu 14.04, Ubuntu 14.10, and Ubuntu 16.04.

Supported Android Architectures

Giac supports cross-compilation for four Android architectures:
ArchitectureABI NameHost TripleDescription
ARMarmeabi-v7aarm-linux-androideabi32-bit ARM
x86x86i686-linux-android32-bit Intel
x86_64x86_64x86_64-linux-android64-bit Intel
ARM64arm64-v8aaarch64-linux-android64-bit ARM

Prerequisites

Download Crystax NDK

Giac uses Crystax NDK 10.2.1 for Android cross-compilation. This is a modified Android NDK with better C++ support.
1

Download Crystax NDK 10.2.1

Download from crystax.netRequired disk space:
  • Download: ~2 GB
  • Extracted: ~8 GB
2

Extract to android-sdks directory

mkdir -p ~/android-sdks
cd ~/Downloads
tar -xf crystax-ndk-10.2.1-linux-x86_64.tar.xz -C ~/android-sdks/
3

Set environment variables

export NDK_DIR=~/android-sdks/crystax-ndk-10.2.1
export CC_DIR=~/cross-compilers

Creating Standalone Toolchains

For each target architecture, you need to create a standalone toolchain.

ARM (32-bit)

1

Set architecture variables

export ARCH=arm
export HOST=arm-linux-androideabi
2

Create standalone toolchain

$NDK_DIR/build/tools/make-standalone-toolchain.sh \
  --ndk-dir=$NDK_DIR \
  --arch=$ARCH \
  --platform=android-21 \
  --install-dir=$CC_DIR/$ARCH
This creates a self-contained toolchain in ~/cross-compilers/arm/.
3

Add to PATH

export PATH=$CC_DIR/$ARCH/bin/:$PATH
4

Build GMP and MPFR (Optional)

If you need to rebuild GMP and MPFR (versions in src/jni/prebuilt/android/README.txt):
# For GMP:
cd /path/to/gmp-source
CFLAGS="-fPIC" ./configure \
  --host=$HOST \
  --prefix=$CC_DIR/$ARCH/sysroot/usr \
  --disable-assembly
make && make install

# For MPFR:
cd /path/to/mpfr-source
CFLAGS="-fPIC" ./configure \
  --host=$HOST \
  --prefix=$CC_DIR/$ARCH/sysroot/usr \
  --disable-assembly \
  --with-gmp=$CC_DIR/$ARCH/sysroot/usr
make && make install
This step requires additional 300-500 MB disk space per architecture.
5

Verify libraries

Static and shared libraries are located in:
ls $CC_DIR/$ARCH/sysroot/usr/lib
# Should contain: libgmp.a, libgmp.so, libmpfr.a, libmpfr.so

x86 (32-bit)

Repeat the process with different architecture settings:
export ARCH=x86
export HOST=i686-linux-android

$NDK_DIR/build/tools/make-standalone-toolchain.sh \
  --ndk-dir=$NDK_DIR \
  --arch=$ARCH \
  --platform=android-21 \
  --install-dir=$CC_DIR/$ARCH

export PATH=$CC_DIR/$ARCH/bin/:$PATH

x86_64 (64-bit Intel)

export ARCH=x86_64
export HOST=x86_64-linux-android

$NDK_DIR/build/tools/make-standalone-toolchain.sh \
  --ndk-dir=$NDK_DIR \
  --arch=$ARCH \
  --platform=android-21 \
  --install-dir=$CC_DIR/$ARCH

export PATH=$CC_DIR/$ARCH/bin/:$PATH

ARM64 (64-bit ARM)

export ARCH=arm64
export HOST=aarch64-linux-android

$NDK_DIR/build/tools/make-standalone-toolchain.sh \
  --ndk-dir=$NDK_DIR \
  --arch=$ARCH \
  --platform=android-21 \
  --install-dir=$CC_DIR/$ARCH

export PATH=$CC_DIR/$ARCH/bin/:$PATH

Compiling Giac for Android

Once the toolchains are set up:
1

Ensure PATH is set

export PATH=$CC_DIR/arm/bin/:$CC_DIR/x86/bin/:$CC_DIR/x86_64/bin/:$CC_DIR/arm64/bin/:$PATH
Or set individually for each architecture as shown above.
2

Build Android AAR package

../gradlew androidAar
This will:
  • Compile Giac for all four Android architectures
  • Create JNI libraries (libjavagiac.so) for each architecture
  • Package everything into an Android Archive (.aar) file
3

Locate output

The compiled libraries will be copied to:
giac-android/src/main/jniLibs/
├── armeabi-v7a/libjavagiac.so
├── x86/libjavagiac.so
├── x86_64/libjavagiac.so
└── arm64-v8a/libjavagiac.so
The final .aar package will be in:
giac-android/build/outputs/aar/

Gradle-Based Android Cross-Compilation

The build.gradle file configures cross-compilation for Android:

Toolchain Configuration

build.gradle
android(Clang) {
    target('androideabi') {
        cppCompiler.executable 'armv7a-linux-androideabi35-clang++'
        linker.executable 'armv7a-linux-androideabi35-clang++'
    }
    target('androidx86') {
        cppCompiler.executable 'i686-linux-android35-clang++'
        linker.executable 'i686-linux-android35-clang++'
    }
    target('androidx86_64') {
        cppCompiler.executable 'x86_64-linux-android35-clang++'
        linker.executable 'x86_64-linux-android35-clang++'
    }
    target('androidarm64') {
        cppCompiler.executable 'aarch64-linux-android35-clang++'
        linker.executable 'aarch64-linux-android35-clang++'
    }
}
Ensure the cross-compiler executables are in your PATH. The build will fail if these compilers cannot be found.

Build Individual Architectures

../gradlew javagiacAndroideabiSharedLibrary

Android-Specific Compilation Settings

Preprocessor Definitions

DefinitionPurpose
HAVE_UNISTD_HPOSIX unistd.h available
NO_BSDBSD extensions not available
HAVE_NO_HOME_DIRECTORYNo home directory access
GIAC_GGBGeoGebra integration

Compiler Flags

-fno-strict-aliasing    # Prevent strict aliasing issues
-DPIC                   # Position independent code (define)
-fPIC                   # Position independent code (flag)
-iquote [headers]       # Override headers with Android-specific versions
The -fPIC flag is critical for Android 6.0+, which doesn’t load libraries with text relocations.

Android-Specific Headers

Custom Android headers override standard headers:
src/giac/headers/android/
├── arm/
├── x86/
├── x86_64/
└── arm64/
These are included with:
-iquote src/giac/headers/android/${architecture}

Troubleshooting

SDK Directory Not Found

Error:
SDK location not found. Define location with sdk.dir in the local.properties file
Solution: Create local.properties:
sdk.dir=/path/to/android/sdk
Or set environment variable:
export ANDROID_SDK=/path/to/android/sdk

Compiler Not in PATH

Error:
Could not find armv7a-linux-androideabi35-clang++
Solution:
export PATH=$CC_DIR/arm/bin/:$PATH
which arm-linux-androideabi-clang++

Missing libcrystax

Error:
error while loading shared libraries: libcrystax.so
Solution: Crystax libraries should be in the toolchain. Verify:
ls $CC_DIR/arm/sysroot/usr/lib/libcrystax.so

Text Relocations Error

Error when loading library on Android:
java.lang.UnsatisfiedLinkError: text relocations
Solution: Ensure -fPIC flag is used:
cppCompiler.args '-fPIC'

Out of Memory During Build

Error:
GC overhead limit exceeded
Solution: Increase Gradle heap size in gradle.properties:
org.gradle.jvmargs=-Xmx4096m

Cleanup

After successful build, you can save disk space:
# Remove NDK (saves ~8 GB)
rm -rf $NDK_DIR

# Keep toolchains in $CC_DIR for future builds

iOS Cross-Compilation

Giac also supports cross-compilation for iOS from macOS.

iOS Device (ARM64)

../gradlew giacIos_arm64StaticLibrary

iPhone Simulator

../gradlew giacIphonesimulator_x86_64StaticLibrary

Configuration

The iOS cross-compilation uses:
Compiler Configuration
-isysroot [SDK path]              # iOS SDK path
-target [arch]-apple-ios[version] # Target specification
-miphoneos-version-min=9.0        # Minimum iOS version
-fembed-bitcode                   # Embed bitcode for App Store
-stdlib=libc++                    # C++ standard library
SDK paths are detected via xcrun:
xcrun --sdk iphoneos --show-sdk-path
xcrun --sdk iphonesimulator --show-sdk-path

Prebuilt Libraries Location

Giac includes prebuilt GMP and MPFR libraries for all supported platforms:
src/jni/prebuilt/
├── android/
│   ├── arm/
│   ├── arm64/
│   ├── x86/
│   └── x86_64/
├── ios/
│   └── arm64/
├── iphonesimulator/
│   ├── arm64/
│   └── x86_64/
├── maccatalyst/
│   ├── arm64/
│   └── x86_64/
├── linux/
│   └── x86-64/
├── windows/
│   ├── clang64/
│   └── clang32/
└── osx/
    ├── x86_64/
    └── arm64/
Each directory contains:
  • libgmp.a - Static GMP library
  • libmpfr.a - Static MPFR library

Using Modern Android NDK

The instructions above use Crystax NDK 10.2.1. For modern Android NDK (r21+), the process differs. The toolchain executables have different names and locations.
For modern NDK:
  1. Use standalone toolchain from $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/
  2. Compiler names: armv7a-linux-androideabi21-clang++, etc.
  3. No separate toolchain creation step required

Additional Resources

Build docs developers (and LLMs) love