One compiler, every target
eskiuc builds for the machine it runs on and, with --target,
for another one: the web, Android, iOS, Windows or a board with no operating system.
The compiler has the AArch64, x86 (x86-64 and 32-bit i686), 32-bit ARM and WebAssembly
(wasm32) backends built in. The extension of the -o name decides what is
built:
| Output | What you get |
|---|---|
app, app.exe | An executable, linked through the platform's C driver |
.o, .obj | An object file, to link yourself |
.a, .lib | A static library (the object in an archive, written by eskiuc itself) |
.so, .dylib, .dll | A shared library, linked with -shared |
A library is how an Eskiu module goes into an app that another build system links, such as an Xcode project, a Gradle module or an emscripten build. Eskiu functions are C symbols, so the app's C, C++, Objective-C or JNI code declares them and calls them.
macOS, Linux and Windows
Release builds of the compiler exist for four hosts. On macOS and Linux the install script picks the right one:
curl -fsSL https://eskiu-lang.org/install.sh | sh
With no --target, the program is built for the host:
eskiuc run app.esk # build, run, clean up
eskiuc app.esk -o app # keep the executable
eskiuc app.esk -o libapp.a # or a static library
Without --mcpu the CPU is the target's baseline, as in clang:
apple-m1 on arm64 Apple targets and generic elsewhere, so a
binary built on one machine runs on another of the same platform.
Windows is validated end to end on a native CI runner (exceptions, the blocking and async networking stacks) but is not yet part of the release gate.
WebAssembly
--target wasm32-unknown-emscripten builds for
emscripten, the toolchain most web projects use.
With no $CC, eskiuc links through emcc, so the
output's extension chooses what emcc writes (.js with its .wasm,
.html, or .wasm), and --link-arg passes emcc's
settings:
eskiuc app.esk --target wasm32-unknown-emscripten -o app.js --link-arg=-sEXIT_RUNTIME=1
node app.js
Inside an existing emscripten project
Build a static library and link it like any other:
eskiuc tracker.esk --target wasm32-unknown-emscripten -o libtracker.a
emcc main.cpp libtracker.a ...
Eskiu functions are C symbols, so C++ (an embind binding, for instance) declares
them extern "C" and calls them:
// main.cpp
extern "C" int tracker_score(int hits, int misses); // defined in tracker.esk
JavaScript reaches a function exported with emcc's
-sEXPORTED_FUNCTIONS=_name (for tracker_score,
-sEXPORTED_FUNCTIONS=_tracker_score).
What runs there
The standard library works as on other POSIX systems, with an event loop over
poll(), so futures, timers and channels run under node. Exceptions work on
emscripten through its JavaScript-based runtime: a program that throws or catches is
linked with -sDISABLE_EXCEPTION_CATCHING=0. Threads need emscripten's
-pthread build.
WASI and other wasm32 triples
wasm32-wasip1 and wasm32-unknown-unknown link with the LLVM
toolchain's own clang --target=.... WASI needs a sysroot
(--link-arg=--sysroot=...), and wasm32-unknown-unknown is
normally built --freestanding. On these triples try and
throw are a compile error.
Android
An Android triple carries the API level: aarch64-linux-android24,
armv7a-linux-androideabi24 or x86_64-linux-android24. With no
$CC, eskiuc links with the NDK's clang, which it finds through
$ANDROID_NDK_HOME, $ANDROID_NDK_ROOT, or the newest NDK under
the SDK ($ANDROID_HOME/ndk, or the SDK manager's default directory).
An app loads a shared library:
eskiuc tracker.esk --target aarch64-linux-android24 -o libtracker.so
The app loads it with System.loadLibrary("tracker"), and its JNI code, or
the native part of a React Native module, declares the Eskiu functions and calls them
like C functions. pthread is part of Android's libc, so a program with threads links no
-lpthread, and one that throws links the NDK's libc++_static
and libc++abi.
iOS
arm64-apple-ios15.0 builds for devices, and
arm64-apple-ios15.0-simulator (or x86_64-apple-ios15.0-simulator)
for the simulator. With no $CC, eskiuc links through
xcrun -sdk iphoneos clang (iphonesimulator for a simulator
triple). An app embeds a static library, one per slice, and
xcodebuild -create-xcframework combines them:
eskiuc tracker.esk --target arm64-apple-ios15.0 -o ios/libtracker.a
eskiuc tracker.esk --target arm64-apple-ios15.0-simulator -o sim/libtracker.a
xcodebuild -create-xcframework -library ios/libtracker.a -library sim/libtracker.a \
-output Tracker.xcframework
The C ABI is the Darwin AArch64 one, as on macOS.
Windows GUI programs
A program that opens its own window, such as one using the gpu package, links for the GUI subsystem, so Windows does not open a console next to it:
eskiuc viewer.esk -o viewer.exe --subsystem=windows
The entry point stays main: the driver passes -mwindows to a
mingw link, and /subsystem:windows with /entry:mainCRTStartup to
an MSVC one. On other targets the option has no effect. To build Windows objects from
another host (--target x86_64-pc-windows-gnu, linked with
lld-link), see Cross-compiling.
Bare metal
A triple whose OS is none targets hardware with no operating system.
Such a triple defines no OS macro (__linux__, __APPLE__,
_WIN32), so portable code guards that path itself.
--freestanding predefines __ESKIU_FREESTANDING__ and routes
alloc and free to an esk_alloc and
esk_free you write, instead of libc:
eskiuc kernel.esk --target aarch64-unknown-none-elf --freestanding -o kernel.o
The repository's ARM64 kernel is built this way and boots in QEMU; chapter 18 of
the Book walks through it.
--mcpu, --mattr and --reloc (pic,
static or dynamic-no-pic) tune the machine and the relocation
model.
Nintendo 3DS
The 3DS is an ARM11 MPCore (armv6k) with VFPv2, hard-float, and a homebrew
loader that applies static relocations:
eskiuc lib.esk -o lib.o \
--target armv6k-none-eabihf --mcpu mpcore --mattr +vfp2 \
--reloc static --freestanding
Then link it with the devkitARM toolchain, alongside a C entry point and libctru, and package the result:
arm-none-eabi-gcc -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft \
-I$DEVKITPRO/libctru/include -c main.c -o main.o
arm-none-eabi-gcc -specs=3dsx.specs -march=armv6k -mtune=mpcore -mfloat-abi=hard \
main.o lib.o -L$DEVKITPRO/libctru/lib -lctru -lm -o app.elf
3dsxtool app.elf app.3dsx
The hf suffix selects the hard-float ABI, which the linker requires to mix
the Eskiu object with hard-float libctru. The
3DS case study shows what was built this way: a
camera and gyroscope AR demo, verified on the console.
C types whose width depends on the target
C's size_t, ssize_t and long are 32 bits on
wasm32, 32-bit ARM and i686, and long is also 32 bits on Windows. Eskiu sees
each of them as an int64. Spelled size_t, ssize_t
or c_long (from import <ctypes>;) in an
extern's parameters or result, the declaration uses the target's C type, an
argument is converted to it and the result widened back:
import <ctypes>;
extern size_t strlen(string s); // right on 32-bit and 64-bit targets
The standard library declares the C functions it uses this way, so strlen,
memcpy, malloc, read or snprintf are
right on every target. A program that declares such a function itself should do the
same: an int64 there is wrong on a 32-bit target, and on WebAssembly the
call traps on the signature mismatch.
The full reference, with the platform macros and the Windows linker walkthrough, is Cross-compiling in the docs.
eskiu