crossbind
GitHub
GUIDE · PLATFORMS · EXECUTABLES

Executables

For programs with no JavaScript host, crossbind links a main() of yours, and the ports it uses, into a native executable for macOS, Linux and Windows, or into a WASI command for wasmtime. You can also run the upstream command-line tools of 12 ports straight from npm, or link the ports' archives into a build of your own.

Native executables

-e native links the main(int, char**) in src/native into an executable, where -e node would link a Node-API addon. A port joins through its desktop packages, one per platform you build:

shell
npm install @crossbind/port-zlib-linux@beta \
@crossbind/port-zlib-linuxmusl@beta \
@crossbind/port-zlib-darwin@beta \
@crossbind/port-zlib-win32@beta
npm install --save-dev crossbind@beta
crossbind.config.mjs
import zlibLinux from '@crossbind/port-zlib-linux/crossbind.config.js';
import zlibLinuxmusl from '@crossbind/port-zlib-linuxmusl/crossbind.config.js';
import zlibDarwin from '@crossbind/port-zlib-darwin/crossbind.config.js';
import zlibWin32 from '@crossbind/port-zlib-win32/crossbind.config.js';
 
export default {
general: { name: 'mytool' },
dependencies: [zlibLinux, zlibLinuxmusl, zlibDarwin, zlibWin32],
paths: { config: import.meta.url, output: 'dist' },
};
src/native/main.cpp
#include <cstdio>
#include <zlib.h>
 
int main(int argc, char **argv) {
printf("zlib %s, %d arguments\n", zlibVersion(), argc - 1);
return 0;
}
shell
npx crossbind build -p host -e native -b release
./dist/mytool.linux-x64
PlatformOutputRuns on
linuxdist/<name>.linux-<arch>glibc 2.28 or later, needing only libc, libm, libdl, libpthread and libgcc_s
linuxmusldist/<name>.linuxmusl-<arch>any Linux, musl or glibc: the executable is fully static
darwindist/<name>.darwin-<arch>macOS 11 or later
win32dist/<name>.win32-<arch>.exeWindows 10 or later, with the C++ runtime linked in

<arch> is arm64 or x64, and a debug build (-b debug) adds .debug before the extension. -p host builds this machine's platform for both: darwin on macOS, win32 on Windows, and on Linux linux or linuxmusl by the C library Node.js runs on. -p linux,linuxmusl,darwin,win32 builds all four. Linux and Windows executables build in Docker, macOS ones only on a Mac, with Xcode's command line tools.

  • Your own archive links whole; a port's archive keeps only what main reaches.
  • A port's data, such as GDAL_DATA and proj.db, is copied to dist/data, but nothing points the executable at it: set GDAL_DATA and PROJ_DATA before running it.
  • -e node,native makes the addon and the executable from the same archives. A Rust package (export.type: 'cargo') makes no executable.
  • The test suite runs the glibc build on Debian 10, the musl build on Alpine and Debian 13 and the macOS build on its host; the Windows build is checked for the machine it targets, not run.

WASI commands

-p wasi -e wasi builds the same kind of program into one wasm32-wasip3 command component, with no JavaScript glue. It runs under a WASI 0.3 runtime with Wasm 3.0 exception support, such as wasmtime 47 or newer. Ports link in from their -wasi packages; the -wasm ones are built for Emscripten and do not link here.

crossbind.config.mjs
import projWasi from '@crossbind/port-proj-wasi/crossbind.config.js';
 
export default {
general: { name: 'proj-tool' },
dependencies: [projWasi],
paths: { config: import.meta.url, output: 'dist' },
};
shell
npx crossbind build -p wasi -e wasi -b release
wasmtime run --dir=. --env PROJ_DATA=dist/data/proj dist/proj-tool-wasi-wasm32-st-release.wasm info EPSG:2056

The command and the data/ folder of its dependencies land in paths.output, .crossbind/build unless the config sets it. The program sees only the directories you preopen with --dir and the variables you pass with --env, and sockets work through wasi:sockets once granted with -S inherit-network=y -S allow-ip-name-lookup=y -S tcp=y. PROJ's page has the whole program, and every library page ends with one like it.

The build runs in the web Docker image, which ships the wasi-sdk. To build on the host instead, point crossbind at a wasi-sdk 34 or newer with the wasm32-wasip3 sysroot:

shell
CROSSBIND_RUNNER=LOCAL CROSSBIND_WASI_SDK_PATH=/opt/wasi-sdk npx crossbind build -p wasi -e wasi -b release
  • Single-threaded: the WASI threads ABI is still in flux.
  • No processes and no dynamic loading. What crossbind stubs out, such as the dlopen family, fails at run time instead of at instantiation.
  • No Rust: there is no wasm32-wasip3 Rust target yet.

Prebuilt tools on npm

Where an upstream project ships command-line tools, a -standalone-wasi package carries them prebuilt for wasm32-wasip3. Nothing compiles; wasmtime 47 or newer has to be on PATH:

shell
npm install --global @crossbind/port-gdal-standalone-wasi@beta
gdalinfo-wasi --version
ogr2ogr-wasi out.gpkg in.geojson

Each tool installs as <tool>-wasi, so it never shadows a native install of the same tool. The launcher preopens only the current directory, so paths must sit inside it; it mounts the data the package declares, such as GDAL's and PROJ's data folders and OpenSSL's CA bundle, sets the variables that point at them, and grants network access. To run a tool without installing it: npx -p @crossbind/port-gdal-standalone-wasi@beta gdal-wasi --version.

LibraryPackageCommands
cURL@crossbind/port-curl-standalone-wasicurl-wasi
Expat@crossbind/port-expat-standalone-wasixmlwf-wasi
GDAL@crossbind/port-gdal-standalone-wasi29 commands
GEOS@crossbind/port-geos-standalone-wasigeosop-wasi
GeoTIFF@crossbind/port-geotiff-standalone-wasiapplygeo-wasi, geotifcp-wasi, listgeo-wasi
libjpeg-turbo@crossbind/port-jpegturbo-standalone-wasicjpeg-wasi, djpeg-wasi, jpegtran-wasi
libTIFF@crossbind/port-tiff-standalone-wasi18 commands
OpenSSL@crossbind/port-openssl-standalone-wasiopenssl-wasi
PROJ@crossbind/port-proj-standalone-wasicct-wasi, cs2cs-wasi, geod-wasi, gie-wasi, proj-wasi, projinfo-wasi
SQLite@crossbind/port-sqlite3-standalone-wasisqlite3-wasi
WebP@crossbind/port-webp-standalone-wasicwebp-wasi, dwebp-wasi, webpinfo-wasi
Zstandard@crossbind/port-zstd-standalone-wasizstd-wasi

A package's npm license field is the compound expression of everything statically linked into it, and the package ships the full license texts, a CycloneDX SBOM and a provenance record of its build.

Prebuilt archives in your own build

Each desktop package, @crossbind/port-<name>-<platform> for darwin, linux, linuxmusl and win32, ships one install tree per architecture that a C or C++ build of your own can link:

layout
dist/prebuilt/<platform>-<arch>-mt-release/
├── include/ headers
├── lib/ static archives, compiled position-independent
├── lib/pkgconfig/ pkg-config files
├── lib/cmake/ CMake package configs, where upstream installs them
└── share/ data such as GDAL_DATA and proj.db
PlatformBuilt withRuns on
linuxclang 19 and a static libc++ 19, against Debian 10 packagesglibc 2.28 or later
linuxmuslclang 19 and a static libc++ 19, against Alpine packagesmusl 1.2.5 or later
darwinXcode's clang and the system libc++macOS 11 or later
win32llvm-mingwWindows 10 or later, through the Universal C Runtime
  • The metadata finds the tree wherever it lands. pkg-config files resolve through ${pcfiledir}, but a package names its dependencies by library name only (-lproj), so put the lib/pkgconfig of every dependency package on PKG_CONFIG_PATH too. Where upstream installs CMake configs, point CMAKE_PREFIX_PATH at the tree.
  • C++ means libc++. A package with C++ code, or one that links such a package, needs clang with -stdlib=libc++, and libc++ 19 or later on Linux: GEOS, PROJ and Lerc, and what links them, such as libtiff, libgeotiff, SpatiaLite and GDAL. C packages such as zlib, zstd, SQLite and Expat link with any compiler.
  • Data has to be pointed at. Set GDAL_DATA and PROJ_DATA to the share/gdal and share/proj directories of their packages; the path compiled into the archives belongs to the machine that built them.
shell
npm install @crossbind/port-geos-linux@beta
GEOS=node_modules/@crossbind/port-geos-linux/dist/prebuilt/linux-x64-mt-release
export PKG_CONFIG_PATH=$GEOS/lib/pkgconfig
clang -c app.c $(pkg-config --cflags geos)
clang++ -stdlib=libc++ app.o -o app $(pkg-config --static --libs geos)

examples/native-executable is a complete program, a main() with a library linked in for every desktop platform, and npm create crossbind@beta -- my-tool Native Executable scaffolds it.

Type to search every guide page and section.
↑↓ navigate↵ openesc close