eskiuv0.12.0
Plataformas

Un compilador para todas las plataformas

eskiuc compila para la máquina en la que corre y, con --target, para otra: la web, Android, iOS, Windows o una tarjeta sin sistema operativo.

El compilador trae integrados los backends de AArch64, x86 (x86-64 e i686 de 32 bits), ARM de 32 bits y WebAssembly (wasm32). La extensión del nombre que le pasas a -o decide qué se genera:

SalidaQué obtienes
app, app.exeUn ejecutable, enlazado con el driver de C de la plataforma
.o, .objUn archivo objeto, para que lo enlaces tú
.a, .libUna biblioteca estática (el objeto dentro de un archivo, que escribe el propio eskiuc)
.so, .dylib, .dllUna biblioteca compartida, enlazada con -shared

Con una biblioteca metes un módulo de Eskiu en una app que se enlaza con otro sistema de build, como un proyecto de Xcode, un módulo de Gradle o un build de emscripten. Las funciones de Eskiu son símbolos de C, así que el código C, C++, Objective-C o JNI de la app las declara y las llama.

macOS, Linux y Windows

Hay builds del compilador para cuatro sistemas. En macOS y Linux, el script de instalación elige el correcto:

curl -fsSL https://eskiu-lang.org/install.sh | sh
↓ macOS (arm64) ↓ Linux (x86-64) ↓ Linux (arm64) ↓ Windows (x86-64)

Sin --target, el programa se compila para la máquina donde estás:

eskiuc run app.esk          # compila, ejecuta y limpia
eskiuc app.esk -o app       # conserva el ejecutable
eskiuc app.esk -o libapp.a  # o genera una biblioteca estática

Si no pasas --mcpu, el CPU es el base de la plataforma, como en clang: apple-m1 en Apple arm64 y generic en lo demás. Así, un binario compilado en una máquina corre en otra de la misma plataforma.

Windows se valida de punta a punta en un runner nativo de CI (excepciones y las pilas de red bloqueante y asíncrona), pero todavía no forma parte del release gate.

WebAssembly

--target wasm32-unknown-emscripten compila para emscripten, la herramienta que usa la mayoría de los proyectos web. Si no hay $CC, eskiuc enlaza con emcc, así que la extensión de la salida decide qué escribe emcc (.js con su .wasm, .html o .wasm), y --link-arg le pasa opciones a emcc:

eskiuc app.esk --target wasm32-unknown-emscripten -o app.js --link-arg=-sEXIT_RUNTIME=1
node app.js

Dentro de un proyecto de emscripten que ya tienes

Genera una biblioteca estática y enlázala como cualquier otra:

eskiuc tracker.esk --target wasm32-unknown-emscripten -o libtracker.a
emcc main.cpp libtracker.a ...

Las funciones de Eskiu son símbolos de C, así que en C++ (en un binding de embind, por ejemplo) se declaran extern "C" y se llaman directo:

// main.cpp
extern "C" int tracker_score(int hits, int misses);   // definida en tracker.esk

Desde JavaScript llegas a una función que exportas con -sEXPORTED_FUNCTIONS=_nombre de emcc (para tracker_score, -sEXPORTED_FUNCTIONS=_tracker_score).

Qué funciona ahí

La biblioteca estándar funciona igual que en otros sistemas POSIX, con un event loop sobre poll(), así que los futures, los timers y los canales corren en node. Las excepciones funcionan en emscripten con su runtime basado en JavaScript: un programa que lanza o atrapa excepciones se enlaza con -sDISABLE_EXCEPTION_CATCHING=0. Los hilos necesitan el build -pthread de emscripten.

WASI y otros triples de wasm32

wasm32-wasip1 y wasm32-unknown-unknown se enlazan con el propio clang --target=... de LLVM. WASI necesita un sysroot (--link-arg=--sysroot=...) y wasm32-unknown-unknown normalmente se compila con --freestanding. En estos triples, try y throw son un error de compilación.

Android

En Android el triple lleva el nivel de API: aarch64-linux-android24, armv7a-linux-androideabi24 o x86_64-linux-android24. Si no hay $CC, eskiuc enlaza con el clang del NDK, que busca en $ANDROID_NDK_HOME, en $ANDROID_NDK_ROOT o en el NDK más nuevo del SDK ($ANDROID_HOME/ndk, o el directorio que usa por defecto el SDK manager).

Una app carga una biblioteca compartida:

eskiuc tracker.esk --target aarch64-linux-android24 -o libtracker.so

La app la carga con System.loadLibrary("tracker"), y su código JNI, o la parte nativa de un módulo de React Native, declara las funciones de Eskiu y las llama como funciones de C. pthread viene en la libc de Android, así que un programa con hilos no enlaza -lpthread, y uno que lanza excepciones enlaza libc++_static y libc++abi del NDK.

iOS

arm64-apple-ios15.0 compila para dispositivos, y arm64-apple-ios15.0-simulator (o x86_64-apple-ios15.0-simulator) para el simulador. Si no hay $CC, eskiuc enlaza con xcrun -sdk iphoneos clang (iphonesimulator para un triple de simulador). Una app incluye una biblioteca estática por cada slice, y xcodebuild -create-xcframework las junta:

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

La ABI de C es la de Darwin AArch64, igual que en macOS.

Programas de Windows con ventana

Un programa que abre su propia ventana, como uno que usa el paquete gpu, se enlaza para el subsistema gráfico, y así Windows no le abre una consola al lado:

eskiuc viewer.esk -o viewer.exe --subsystem=windows

El punto de entrada sigue siendo main: el driver le pasa -mwindows a un enlace de mingw, y /subsystem:windows con /entry:mainCRTStartup a uno de MSVC. En otras plataformas la opción no hace nada. Para generar objetos de Windows desde otro sistema (--target x86_64-pc-windows-gnu, enlazados con lld-link), revisa Cross-compiling.

Bare metal

Un triple cuyo sistema operativo es none apunta a hardware sin sistema operativo. Ese triple no define ninguna macro de sistema (__linux__, __APPLE__, _WIN32), así que el código portable tiene que contemplar ese caso por su cuenta. --freestanding predefine __ESKIU_FREESTANDING__ y manda alloc y free a un esk_alloc y un esk_free que escribes tú, en lugar de usar libc:

eskiuc kernel.esk --target aarch64-unknown-none-elf --freestanding -o kernel.o

El kernel de ARM64 del repositorio se compila así y arranca en QEMU; el capítulo 18 del libro lo explica paso a paso. --mcpu, --mattr y --reloc (pic, static o dynamic-no-pic) ajustan la máquina y el modelo de relocalización.

Nintendo 3DS

La 3DS tiene un ARM11 MPCore (armv6k) con VFPv2, hard-float, y un cargador de homebrew que aplica relocalizaciones estáticas:

eskiuc lib.esk -o lib.o \
    --target armv6k-none-eabihf --mcpu mpcore --mattr +vfp2 \
    --reloc static --freestanding

Después lo enlazas con el toolchain de devkitARM, junto con un punto de entrada en C y libctru, y empaquetas el resultado:

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

El sufijo hf elige la ABI hard-float, que el linker necesita para juntar el objeto de Eskiu con libctru, que es hard-float. El caso de estudio de la 3DS muestra lo que se hizo así: un demo de realidad aumentada con cámara y giroscopio, probado en la consola.

Tipos de C cuyo ancho depende de la plataforma

En C, size_t, ssize_t y long miden 32 bits en wasm32, ARM de 32 bits e i686, y long también mide 32 bits en Windows. Eskiu ve cada uno como un int64. Si los escribes como size_t, ssize_t o c_long (de import <ctypes>;) en los parámetros o el resultado de un extern, la declaración usa el tipo de C de la plataforma, el argumento se convierte a ese tipo y el resultado se vuelve a ensanchar:

import <ctypes>;

extern size_t strlen(string s);   // correcto en 32 y en 64 bits

La biblioteca estándar declara así las funciones de C que usa, por lo que strlen, memcpy, malloc, read o snprintf quedan bien en cualquier plataforma. Si tu programa declara una de estas funciones por su cuenta, debería hacer lo mismo: un int64 ahí está mal en una plataforma de 32 bits, y en WebAssembly la llamada truena por la firma que no coincide.

La referencia completa, con las macros de cada plataforma y el enlace paso a paso en Windows, está en Cross-compiling, en la documentación (en inglés).

Empieza en un minuto.

Instala el compilador con un comando, o corre un programa en el navegador primero.