Skip to main content
QuickJS-ng supports cross-compilation to various target platforms. This guide covers the configuration and best practices for cross-compiling the library and tools.

Overview

Cross-compilation is the process of building QuickJS-ng on one platform (the host) to run on a different platform (the target). This is commonly used for:
  • Building for embedded systems
  • Creating ARM binaries on x86 machines
  • Compiling for mobile platforms (iOS, Android)
  • WebAssembly builds
  • Windows builds from Linux

Build System Support

Meson

Recommended for cross-compilation with excellent toolchain support and automatic handling of native builds

CMake

Supported via toolchain files, requires manual configuration of cross-compilation settings

Meson Cross-Compilation

Meson provides the best cross-compilation experience with automatic handling of native vs. target builds.

Native vs. Target Builds

When cross-compiling, Meson automatically builds two versions of certain tools:
  • Native tools: Built for the host machine (e.g., qjsc to compile JavaScript to bytecode)
  • Target binaries: Built for the target platform (e.g., qjs runtime for ARM)
This is necessary because the bytecode compiler (qjsc) needs to run on the build machine during compilation.

Cross-Compilation File

Create a cross-compilation file describing your target platform:
[binaries]
c = 'aarch64-linux-gnu-gcc'
cpp = 'aarch64-linux-gnu-g++'
ar = 'aarch64-linux-gnu-ar'
strip = 'aarch64-linux-gnu-strip'
pkg-config = 'aarch64-linux-gnu-pkg-config'

[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'armv8'
endian = 'little'

Building with Cross-Compilation

# Set up build with cross-compilation file
meson setup build --cross-file cross-arm64.ini

# Build (Meson automatically handles native vs. target builds)
meson compile -C build

# Install
meson install -C build

How Meson Handles Native Builds

When cross-compiling, Meson automatically:
  1. Detects that a native qjsc is needed to compile JavaScript files
  2. Builds a native version of qjsc and qjs for the host machine
  3. Uses the native tools during the build process
  4. Builds the target binaries for the cross-compilation target
From meson.build:342-401:
if meson.is_cross_build()
  # Build native versions of qjsc and qjs
  qjs_native_lib = static_library(
    'qjs_native',
    qjs_srcs,
    qjs_libc_srcs,
    native: true,
  )
  
  meson.override_find_program(
    'qjsc',
    executable(
      'qjsc_native',
      qjsc_srcs,
      native: true,
    ),
  )
endif

CMake Cross-Compilation

Toolchain Files

CMake uses toolchain files for cross-compilation:
# arm64-linux-toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Building with Toolchain File

cmake -B build \
  -DCMAKE_TOOLCHAIN_FILE=arm64-linux-toolchain.cmake \
  -DCMAKE_BUILD_TYPE=Release

cmake --build build -j $(nproc)
When cross-compiling with CMake, you may need to build a native version of qjsc separately if you’re building from source (not using pre-generated files).

Platform-Specific Cross-Compilation

Android (NDK)

QuickJS-ng supports Android NDK 26.0.10792818 or later.
# Create cross-file for Android
cat > android-arm64.ini << EOF
[binaries]
c = '$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang'
cpp = '$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang++'
ar = '$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar'
strip = '$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip'

[host_machine]
system = 'android'
cpu_family = 'aarch64'
cpu = 'armv8'
endian = 'little'
EOF

meson setup build --cross-file android-arm64.ini
meson compile -C build

iOS

iOS cross-compilation is typically done on macOS using Xcode toolchains.
# Using CMake with iOS toolchain
cmake -B build \
  -DCMAKE_SYSTEM_NAME=iOS \
  -DCMAKE_OSX_ARCHITECTURES=arm64 \
  -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0

cmake --build build
CLI tools (qjs, qjsc) are not installed for iOS, tvOS, or watchOS builds. Only the library is built.

WebAssembly (WASI)

WASI is the WebAssembly System Interface standard.
# Download and extract wasi-sdk
export WASI_SDK_PATH=/path/to/wasi-sdk

cmake -B build \
  -DCMAKE_SYSTEM_NAME=WASI \
  -DCMAKE_C_COMPILER=$WASI_SDK_PATH/bin/clang \
  -DCMAKE_SYSROOT=$WASI_SDK_PATH/share/wasi-sysroot

cmake --build build

Emscripten

Emscripten compiles C/C++ to WebAssembly for web browsers.
# Using emcmake wrapper
emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

# Output: qjs_wasm.js and qjs_wasm.wasm

MinGW (Windows on Linux)

meson setup build --cross-file mingw-w64.ini
meson compile -C build
MinGW builds automatically link statically with libgcc when QJS_BUILD_CLI_STATIC is enabled.

Common Issues and Solutions

Issue: qjsc Not Found During Cross-Compilation

Problem: Native qjsc is needed to compile JavaScript files during build. Solution:
Meson handles this automatically. No action needed.

Issue: Thread Library Not Found

Problem: Cross-compilation toolchain doesn’t provide pthread library. Solution: WASI builds automatically skip thread library:
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WASI")
    list(APPEND qjs_libs ${CMAKE_THREAD_LIBS_INIT})
endif()

Issue: Module Loading on Windows

Problem: Windows cannot resolve symbols at runtime like Unix systems. Solution: QuickJS-ng automatically links modules to the main library on Windows:
if(WIN32)
    # Explicitly link modules to qjs on Windows
    target_link_libraries(qjs_module_lib INTERFACE qjs)
endif()

Issue: Static vs. Shared Libraries

Problem: Binary modules with static qjs on Windows can cause runtime errors. Solution: Use shared library builds on Windows when loading binary modules:
cmake -B build -DBUILD_SHARED_LIBS=ON

Testing Cross-Compiled Binaries

Using QEMU

Test ARM binaries on x86 using QEMU:
# Install QEMU user-mode emulation
sudo apt-get install qemu-user-static

# Run ARM64 binary
qemu-aarch64-static -L /usr/aarch64-linux-gnu ./build/qjs --version

# Run ARM32 binary
qemu-arm-static -L /usr/arm-linux-gnueabihf ./build/qjs --version

Using Wine

Test Windows binaries on Linux:
# Install Wine
sudo apt-get install wine64

# Run Windows binary
wine ./build/qjs.exe --version

Using wasmtime

Test WASI binaries:
# Install wasmtime
curl https://wasmtime.dev/install.sh -sSf | bash

# Run WASI binary
wasmtime build/qjs.wasm --version

Advanced Configuration

Custom Sysroot

Specify a custom sysroot for cross-compilation:
cmake -B build \
  -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
  -DCMAKE_SYSROOT=/path/to/sysroot

Cross-Compiling with Dependencies

When using mimalloc or other dependencies:
# Ensure dependencies are built for target platform
meson setup build \
  --cross-file cross-arm64.ini \
  -Dcli_mimalloc=enabled \
  --pkg-config-path=/path/to/arm64/pkgconfig

Multi-Architecture Builds

Build for multiple architectures:
for arch in arm64 armv7 x86_64; do
  meson setup build-$arch --cross-file cross-$arch.ini
  meson compile -C build-$arch
done

Best Practices

  1. Use Meson for cross-compilation - Better automatic handling of native vs. target builds
  2. Test early and often - Use emulators (QEMU, Wine) to test cross-compiled binaries
  3. Static linking for distribution - Use static builds when distributing to avoid dependency issues
  4. Verify toolchain - Ensure cross-compilation toolchain matches target platform exactly
  5. Check platform features - Some platforms have limited features (no threads on WASI)
  6. Use shared libs carefully - Static libraries are safer for cross-compilation

Toolchain Installation

Ubuntu/Debian

# ARM64
sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

# ARM32
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf

# MinGW (Windows)
sudo apt-get install mingw-w64

# Android NDK
wget https://dl.google.com/android/repository/android-ndk-r26b-linux.zip
unzip android-ndk-r26b-linux.zip

macOS

# Install via Homebrew
brew install mingw-w64  # For Windows cross-compilation

# Android NDK
brew install --cask android-ndk

Build docs developers (and LLMs) love