-
Notifications
You must be signed in to change notification settings - Fork 0
10 Packaging Distribution
This chapter covers how to package and distribute compiled Zyntax programs, including the ZPack format for JIT execution and static library linking for AOT compilation.
Zyntax supports two primary compilation modes:
| Mode | Backend | Use Case | Runtime Symbols |
|---|---|---|---|
| JIT | Cranelift, LLVM | Development, scripting | Loaded from ZPack (.zpack) |
| AOT | LLVM | Production, distribution | Linked from static libraries (.a/.lib) |
JIT (Just-In-Time) compilation compiles and executes code immediately. It's fast to start and ideal for development workflows.
# JIT execution with runtime from ZPack
zyntax compile --jit --pack runtime.zpack \
--grammar lang.zyn --source main.langAOT (Ahead-Of-Time) compilation produces native executables. It generates optimized machine code suitable for production deployment.
# AOT compilation with static library linking
zyntax compile --backend llvm \
--grammar lang.zyn --source main.lang \
-o myapp --lib runtimeZPack is a compressed archive format designed for JIT execution. It bundles bytecode modules and platform-specific dynamic libraries.
my_runtime.zpack (ZIP archive)
├── manifest.json # Package metadata
├── modules/ # Compiled bytecode modules
│ ├── std/
│ │ ├── Array.zbc
│ │ ├── String.zbc
│ │ └── Math.zbc
│ └── main.zbc
└── lib/ # Platform-specific dynamic libraries
├── x86_64-apple-darwin/
│ └── runtime.zrtl
├── x86_64-unknown-linux-gnu/
│ └── runtime.zrtl
├── aarch64-apple-darwin/
│ └── runtime.zrtl
└── x86_64-pc-windows-msvc/
└── runtime.zrtl
| Extension | Description |
|---|---|
.zpack |
ZPack archive (ZIP format) |
.zbc |
Zyntax Bytecode (compiled HIR module) |
.zrtl |
Zyntax Runtime Library (dynamic library) |
The manifest.json describes the package:
{
"version": 1,
"name": "haxe-runtime",
"package_version": "1.0.0",
"description": "Haxe standard library runtime for Zyntax",
"source_language": "haxe",
"targets": [
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"x86_64-unknown-linux-gnu"
],
"modules": [
"std/Array",
"std/String",
"std/Math"
],
"exports": [
{
"name": "$haxe$trace$int",
"signature": "fn(i32) -> void",
"doc": "Print an integer to stdout"
}
]
}Use the zyntax pack create command:
# Create a ZPack with modules and runtime
zyntax pack create \
--output haxe-runtime.zpack \
--name haxe-runtime \
--version 1.0.0 \
--language haxe \
--module modules/std/ \
--runtime x86_64-apple-darwin:lib/darwin/runtime.zrtl \
--runtime x86_64-unknown-linux-gnu:lib/linux/runtime.zrtl \
--runtime aarch64-apple-darwin:lib/darwin-arm/runtime.zrtl# List contents
zyntax pack list runtime.zpack
# Verbose output with module details
zyntax pack list runtime.zpack -v
# Extract to directory
zyntax pack extract runtime.zpack --output ./extracted/
# Show current platform target
zyntax pack targetLoad a ZPack for JIT execution:
# Single pack
zyntax compile --jit --pack haxe-runtime.zpack \
--grammar haxe.zyn --source main.hx
# Multiple packs (combined)
zyntax compile --jit \
--pack haxe-runtime.zpack \
--pack my-extensions.zpack \
--grammar haxe.zyn --source main.hxRuntime libraries provide the implementation of external functions called by compiled code.
For JIT mode, build a shared library with exported symbols:
# macOS
clang -shared -fPIC -o runtime.zrtl runtime.c
# Linux
gcc -shared -fPIC -o runtime.zrtl runtime.c
# Windows
cl /LD runtime.c /Fe:runtime.zrtlFor AOT mode, build a static library:
# Unix (macOS/Linux)
clang -c runtime.c -o runtime.o
ar rcs libruntime.a runtime.o
# Windows
cl /c runtime.c
lib runtime.obj /OUT:runtime.libRuntime symbols follow a naming convention for cross-language interop:
$<language>$<type>$<operation>
Examples:
-
$haxe$trace$int- Haxe trace function for integers -
$haxe$Array$push- Array push method -
$zig$print- Zig print function
// runtime.c
#include <stdio.h>
#include <stdint.h>
// Trace function for integers
void haxe_trace_int(int32_t value) {
printf("%d\n", value);
}
// Trace function for strings
void haxe_trace_string(const char* value) {
printf("%s\n", value);
}
// Array operations
typedef struct {
void** data;
int32_t length;
int32_t capacity;
} HaxeArray;
void haxe_array_push(HaxeArray* arr, void* value) {
// Implementation...
}When compiled, use export_name attributes or linker scripts to set the exact symbol names:
// Using GCC/Clang attributes
__attribute__((visibility("default")))
void __attribute__((alias("haxe_trace_int")))
$haxe$trace$int(int32_t);Or with Rust using the zrtl_macros crate:
use zrtl_macros::{zrtl_plugin, zrtl_export};
// Define the plugin (required once per library)
zrtl_plugin!("haxe_runtime");
#[zrtl_export("$haxe$trace$int")]
pub extern "C" fn trace_int(value: i32) {
println!("{}", value);
}
#[zrtl_export("$haxe$trace$string")]
pub extern "C" fn trace_string(s: *const std::ffi::c_char) {
let c_str = unsafe { std::ffi::CStr::from_ptr(s) };
println!("{}", c_str.to_string_lossy());
}Build as a dynamic library (cdylib) for JIT mode:
# Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
zrtl_macros = { path = "path/to/sdk/zrtl_macros" }
inventory = "0.3"Or as a static library (staticlib) for AOT mode:
# Cargo.toml
[lib]
crate-type = ["staticlib"]# 1. Compile source to executable with linked runtime
zyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp --lib runtime
# 2. Or manually link (two-step process)
zyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp.o --emit-obj
cc myapp.o -L/usr/local/lib -lruntime -o myappWhen you specify --lib runtime, Zyntax searches for the library in standard locations:
macOS:
/usr/local/lib/opt/homebrew/lib/usr/lib/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
Linux:
/usr/local/lib/usr/lib/usr/lib/x86_64-linux-gnu/usr/lib/aarch64-linux-gnu
Windows:
C:\Windows\System32C:\Program Files\Common Files
The --lib flag accepts multiple formats:
# Full path
--lib /path/to/libruntime.a
# Library name (searches for libruntime.a)
--lib runtime
# File name (searches in standard paths)
--lib libruntime.azyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp \
--lib haxe-runtime \
--lib myextensions \
--lib pthreadInclude runtime libraries for all target platforms in a single ZPack:
zyntax pack create \
--output universal-runtime.zpack \
--name my-runtime \
--runtime x86_64-apple-darwin:lib/darwin-x64/runtime.zrtl \
--runtime aarch64-apple-darwin:lib/darwin-arm64/runtime.zrtl \
--runtime x86_64-unknown-linux-gnu:lib/linux-x64/runtime.zrtl \
--runtime x86_64-pc-windows-msvc:lib/windows-x64/runtime.zrtlUsers can run on any supported platform:
# Works on any platform with matching runtime in the pack
zyntax compile --jit --pack universal-runtime.zpack \
--grammar haxe.zyn --source main.hxBuild separate executables for each target:
# macOS x86_64
zyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp-darwin-x64 \
--lib darwin-x64/libruntime.a
# macOS ARM64
zyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp-darwin-arm64 \
--lib darwin-arm64/libruntime.a
# Linux x86_64
zyntax compile --backend llvm \
--grammar haxe.zyn --source main.hx \
-o myapp-linux-x64 \
--lib linux-x64/libruntime.a| Target Triple | Platform |
|---|---|
x86_64-apple-darwin |
macOS Intel |
aarch64-apple-darwin |
macOS Apple Silicon |
x86_64-unknown-linux-gnu |
Linux x64 (glibc) |
x86_64-unknown-linux-musl |
Linux x64 (musl) |
aarch64-unknown-linux-gnu |
Linux ARM64 |
x86_64-pc-windows-msvc |
Windows x64 (MSVC) |
x86_64-pc-windows-gnu |
Windows x64 (MinGW) |
Check your current target:
zyntax pack targetZyntax supports incremental compilation through bytecode caching.
By default, cached bytecode is stored in:
-
Unix:
~/.zyntax/cache/ -
Windows:
%USERPROFILE%\.zyntax\cache\
# View cache statistics
zyntax cache stats
# List cached modules
zyntax cache list
zyntax cache list -v # Verbose
# Clear the cache
zyntax cache clear
# Use a custom cache directory
zyntax compile --cache-dir ./my-cache \
--grammar haxe.zyn --source main.hx
# Disable caching
zyntax compile --no-cache \
--grammar haxe.zyn --source main.hx- Source files are hashed based on content
- If a matching
.zbcfile exists, it's loaded instead of recompiling - Dependencies trigger recompilation of dependent modules
Here's a complete example of building and distributing a Haxe-based application:
# Compile runtime for all targets
for target in darwin-x64 darwin-arm64 linux-x64; do
cd runtime && make TARGET=$target
donezyntax pack create \
--output dist/haxe-runtime.zpack \
--name haxe-runtime \
--version 1.0.0 \
--language haxe \
--description "Haxe runtime for Zyntax" \
--runtime x86_64-apple-darwin:runtime/darwin-x64/runtime.zrtl \
--runtime aarch64-apple-darwin:runtime/darwin-arm64/runtime.zrtl \
--runtime x86_64-unknown-linux-gnu:runtime/linux-x64/runtime.zrtl# For each platform
zyntax compile --backend llvm \
--grammar grammars/haxe.zyn --source src/Main.hx \
-o dist/myapp-darwin-x64 \
--lib runtime/darwin-x64/libruntime.a \
-O3
zyntax compile --backend llvm \
--grammar grammars/haxe.zyn --source src/Main.hx \
-o dist/myapp-linux-x64 \
--lib runtime/linux-x64/libruntime.a \
-O3dist/
├── haxe-runtime.zpack # For JIT users
├── myapp-darwin-x64 # Native binary (macOS Intel)
├── myapp-darwin-arm64 # Native binary (macOS ARM)
├── myapp-linux-x64 # Native binary (Linux)
└── README.md
JIT users:
# Download the zpack
curl -O https://example.com/dist/haxe-runtime.zpack
# Run directly with grammar and source
zyntax compile --jit --pack haxe-runtime.zpack \
--grammar haxe.zyn --source script.hxAOT users:
# Download the native binary for their platform
curl -O https://example.com/dist/myapp-linux-x64
chmod +x myapp-linux-x64
./myapp-linux-x64For users who want to bypass the CLI and use the compiler and runtime plugin framework directly, Zyntax provides a comprehensive Rust API.
Use zyntax_compiler to compile TypedAST directly to HIR and native code:
use std::sync::Arc;
use zyntax_compiler::{
compile_to_hir, CompilationConfig,
cranelift_backend::CraneliftBackend,
hir::HirModule,
};
use zyntax_typed_ast::{TypedProgram, TypeRegistry};
fn compile_program(program: &TypedProgram) -> Result<(), Box<dyn std::error::Error>> {
// Create type registry
let type_registry = Arc::new(TypeRegistry::new());
// Configure compilation
let config = CompilationConfig {
opt_level: 2,
debug_info: true,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
hot_reload: false,
enable_monomorphization: true,
memory_strategy: Some(zyntax_compiler::memory_management::MemoryStrategy::ARC),
async_runtime: None,
};
// Compile TypedAST → HIR
let hir_module = compile_to_hir(program, type_registry, config)?;
// Generate native code with Cranelift
let mut backend = CraneliftBackend::new()?;
backend.compile_module(&hir_module)?;
// Execute a function
let main_id = find_main_function(&hir_module)?;
let result = backend.execute_function::<i64>(main_id)?;
println!("Result: {}", result);
Ok(())
}Implement the RuntimePlugin trait to provide runtime symbols programmatically:
use zyntax_compiler::plugin::{RuntimePlugin, PluginRegistry};
pub struct MyRuntimePlugin;
impl RuntimePlugin for MyRuntimePlugin {
fn name(&self) -> &str {
"my_runtime"
}
fn runtime_symbols(&self) -> Vec<(&'static str, *const u8)> {
vec![
("$MyRuntime$add", add as *const u8),
("$MyRuntime$print", print_value as *const u8),
("$MyRuntime$allocate", allocate as *const u8),
]
}
fn on_load(&self) -> Result<(), String> {
// Initialize runtime state
println!("MyRuntime loaded");
Ok(())
}
fn on_unload(&self) -> Result<(), String> {
// Cleanup
Ok(())
}
}
extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
extern "C" fn print_value(value: i32) {
println!("{}", value);
}
extern "C" fn allocate(size: usize) -> *mut u8 {
let layout = std::alloc::Layout::from_size_align(size, 8).unwrap();
unsafe { std::alloc::alloc(layout) }
}
// Register with the compiler
fn setup_runtime() -> PluginRegistry {
let mut registry = PluginRegistry::new();
registry.register(Box::new(MyRuntimePlugin)).unwrap();
registry
}Load ZRTL plugins directly without using ZPack archives:
use zyntax_compiler::zrtl::{ZrtlPlugin, ZrtlRegistry};
fn load_runtime_plugins() -> Result<Vec<(&'static str, *const u8)>, Box<dyn std::error::Error>> {
let mut registry = ZrtlRegistry::new();
// Load individual plugins
registry.load_plugin("./runtime/haxe_runtime.zrtl")?;
registry.load_plugin("./runtime/math_extensions.zrtl")?;
// Or load all plugins from a directory
registry.load_directory("./plugins/")?;
// Collect all symbols for the backend
let symbols = registry.collect_symbols();
println!("Loaded {} runtime symbols", symbols.len());
Ok(symbols)
}Create and manipulate ZPack archives programmatically:
use zyntax_compiler::zpack::{ZPack, ZPackBuilder, ZPackManifest};
use std::path::Path;
fn create_custom_zpack() -> Result<(), Box<dyn std::error::Error>> {
let mut builder = ZPackBuilder::new("my-runtime", "1.0.0");
// Set metadata
builder.set_description("Custom runtime for my language");
builder.set_language("mylang");
builder.set_entry_point("Main.main");
// Add bytecode modules
builder.add_module(Path::new("modules/std/Array.zbc"))?;
builder.add_module(Path::new("modules/std/String.zbc"))?;
builder.add_module_dir(Path::new("modules/core/"))?;
// Add runtime libraries for different targets
builder.add_runtime("x86_64-apple-darwin", Path::new("lib/darwin/runtime.zrtl"))?;
builder.add_runtime("x86_64-unknown-linux-gnu", Path::new("lib/linux/runtime.zrtl"))?;
builder.add_runtime("aarch64-apple-darwin", Path::new("lib/darwin-arm/runtime.zrtl"))?;
// Add export declarations
builder.add_export("$MyLang$print", Some("fn(i32) -> void"), Some("Print an integer"));
builder.add_export("$MyLang$readLine", Some("fn() -> String"), Some("Read a line from stdin"));
// Build the archive
builder.build(Path::new("dist/my-runtime.zpack"))?;
Ok(())
}
fn load_and_use_zpack() -> Result<(), Box<dyn std::error::Error>> {
// Load a ZPack
let zpack = ZPack::load(Path::new("runtime.zpack"))?;
// Inspect contents
println!("Package: {} v{}", zpack.manifest().name, zpack.manifest().package_version);
println!("Modules: {:?}", zpack.manifest().modules);
println!("Targets: {:?}", zpack.manifest().targets);
// Get runtime symbols for current platform
let symbols = zpack.runtime_symbols();
println!("Loaded {} symbols", symbols.len());
// Extract bytecode modules
for module_path in &zpack.manifest().modules {
let bytecode = zpack.get_module(module_path)?;
println!("Module {}: {} bytes", module_path, bytecode.len());
}
Ok(())
}Build a complete custom compilation pipeline:
use std::sync::Arc;
use zyntax_compiler::{
compile_to_hir, CompilationConfig,
cranelift_backend::CraneliftBackend,
plugin::PluginRegistry,
zrtl::ZrtlRegistry,
};
use zyntax_typed_ast::{TypedProgram, TypeRegistry};
pub struct CustomCompiler {
plugin_registry: PluginRegistry,
zrtl_registry: ZrtlRegistry,
type_registry: Arc<TypeRegistry>,
config: CompilationConfig,
}
impl CustomCompiler {
pub fn new() -> Self {
Self {
plugin_registry: PluginRegistry::new(),
zrtl_registry: ZrtlRegistry::new(),
type_registry: Arc::new(TypeRegistry::new()),
config: CompilationConfig::default(),
}
}
pub fn with_opt_level(mut self, level: u8) -> Self {
self.config.opt_level = level;
self
}
pub fn load_zrtl(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
self.zrtl_registry.load_plugin(path)?;
Ok(())
}
pub fn register_plugin(&mut self, plugin: Box<dyn zyntax_compiler::plugin::RuntimePlugin>) -> Result<(), String> {
self.plugin_registry.register(plugin)
}
pub fn compile_and_run(&self, program: &TypedProgram) -> Result<i64, Box<dyn std::error::Error>> {
// Compile to HIR
let hir_module = compile_to_hir(program, self.type_registry.clone(), self.config.clone())?;
// Collect all runtime symbols
let mut symbols = self.plugin_registry.collect_symbols();
symbols.extend(self.zrtl_registry.collect_symbols());
// Create backend with runtime symbols
let mut backend = CraneliftBackend::with_runtime_symbols(&symbols)?;
backend.compile_module(&hir_module)?;
// Find and execute main
let main_id = hir_module.functions.iter()
.find(|(_, f)| f.name.resolve_global().map(|n| n == "main").unwrap_or(false))
.map(|(id, _)| *id)
.ok_or("No main function found")?;
let fn_ptr = backend.get_function_pointer(main_id)
.ok_or("Failed to get main function pointer")?;
let result = unsafe {
let f: extern "C" fn() -> i64 = std::mem::transmute(fn_ptr);
f()
};
Ok(result)
}
}
// Usage
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut compiler = CustomCompiler::new()
.with_opt_level(2);
// Load runtime plugins
compiler.load_zrtl("./runtime/haxe.zrtl")?;
compiler.register_plugin(Box::new(MyRuntimePlugin))?;
// Parse and compile your program
let program: TypedProgram = /* parse your source */;
let result = compiler.compile_and_run(&program)?;
println!("Program returned: {}", result);
Ok(())
}Build a complete AOT compilation pipeline using the LLVM backend:
use std::sync::Arc;
use std::path::Path;
use zyntax_compiler::{
compile_to_hir, CompilationConfig,
hir::HirModule,
};
use zyntax_typed_ast::{TypedProgram, TypeRegistry};
#[cfg(feature = "llvm-backend")]
use inkwell::{
context::Context,
targets::{
CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine,
},
OptimizationLevel,
};
pub struct AotCompiler {
type_registry: Arc<TypeRegistry>,
config: CompilationConfig,
target_triple: String,
}
impl AotCompiler {
pub fn new() -> Self {
Self {
type_registry: Arc::new(TypeRegistry::new()),
config: CompilationConfig::default(),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
}
}
pub fn with_opt_level(mut self, level: u8) -> Self {
self.config.opt_level = level;
self
}
pub fn with_target(mut self, triple: &str) -> Self {
self.target_triple = triple.to_string();
self.config.target_triple = triple.to_string();
self
}
/// Compile TypedAST to HIR
pub fn compile_to_hir(&self, program: &TypedProgram) -> Result<HirModule, Box<dyn std::error::Error>> {
let hir = compile_to_hir(program, self.type_registry.clone(), self.config.clone())?;
Ok(hir)
}
/// Compile HIR to object file using LLVM
#[cfg(feature = "llvm-backend")]
pub fn compile_to_object(
&self,
hir_module: &HirModule,
output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
use zyntax_compiler::llvm_backend::LLVMBackend;
// Initialize LLVM targets
Target::initialize_native(&InitializationConfig::default())
.map_err(|e| format!("Failed to initialize LLVM target: {}", e))?;
// Create LLVM context and backend
let context = Context::create();
let mut backend = LLVMBackend::new(&context, "zyntax_aot");
// Compile HIR to LLVM IR
let _llvm_ir = backend.compile_module(hir_module)?;
// Get target machine
let triple = TargetMachine::get_default_triple();
let target = Target::from_triple(&triple)?;
let opt_level = match self.config.opt_level {
0 => OptimizationLevel::None,
1 => OptimizationLevel::Less,
2 => OptimizationLevel::Default,
_ => OptimizationLevel::Aggressive,
};
let target_machine = target
.create_target_machine(
&triple,
"generic",
"",
opt_level,
RelocMode::Default,
CodeModel::Default,
)
.ok_or("Failed to create target machine")?;
// Write object file
target_machine
.write_to_file(backend.module(), FileType::Object, output_path)
.map_err(|e| format!("Failed to write object file: {}", e))?;
Ok(())
}
/// Compile HIR to LLVM IR string (for debugging)
#[cfg(feature = "llvm-backend")]
pub fn compile_to_llvm_ir(&self, hir_module: &HirModule) -> Result<String, Box<dyn std::error::Error>> {
use zyntax_compiler::llvm_backend::LLVMBackend;
let context = Context::create();
let mut backend = LLVMBackend::new(&context, "zyntax_aot");
let ir = backend.compile_module(hir_module)?;
Ok(ir)
}
/// Full pipeline: TypedAST → Object File → Executable
#[cfg(feature = "llvm-backend")]
pub fn compile_to_executable(
&self,
program: &TypedProgram,
output_path: &Path,
static_libs: &[&Path],
) -> Result<(), Box<dyn std::error::Error>> {
use std::process::Command;
// Compile to HIR
let hir_module = self.compile_to_hir(program)?;
// Compile to object file
let obj_path = output_path.with_extension("o");
self.compile_to_object(&hir_module, &obj_path)?;
// Link with system linker
let mut linker = Command::new("cc");
linker.arg(&obj_path);
linker.arg("-o");
linker.arg(output_path);
// Add static libraries
for lib in static_libs {
linker.arg(lib);
}
let status = linker.status()?;
if !status.success() {
return Err(format!("Linker failed with status: {}", status).into());
}
// Clean up object file
std::fs::remove_file(&obj_path)?;
Ok(())
}
}
// Usage
#[cfg(feature = "llvm-backend")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let compiler = AotCompiler::new()
.with_opt_level(3)
.with_target("x86_64-apple-darwin");
// Parse your source to TypedAST
let program: TypedProgram = /* parse source */;
// Option 1: Just get LLVM IR for inspection
let hir = compiler.compile_to_hir(&program)?;
let ir = compiler.compile_to_llvm_ir(&hir)?;
println!("LLVM IR:\n{}", ir);
// Option 2: Compile to object file only
compiler.compile_to_object(&hir, Path::new("output.o"))?;
// Option 3: Full compilation to executable with runtime linking
compiler.compile_to_executable(
&program,
Path::new("myapp"),
&[Path::new("/usr/local/lib/libruntime.a")],
)?;
Ok(())
}#[cfg(feature = "llvm-backend")]
use inkwell::targets::{Target, TargetTriple, InitializationConfig};
fn initialize_cross_compilation() {
// Initialize all targets for cross-compilation
Target::initialize_all(&InitializationConfig::default());
}
fn compile_for_target(
hir_module: &HirModule,
target_triple: &str,
output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
use inkwell::context::Context;
use inkwell::targets::{CodeModel, FileType, RelocMode, Target, TargetMachine, TargetTriple};
use zyntax_compiler::llvm_backend::LLVMBackend;
let context = Context::create();
let mut backend = LLVMBackend::new(&context, "cross_compile");
backend.compile_module(hir_module)?;
let triple = TargetTriple::create(target_triple);
let target = Target::from_triple(&triple)?;
let target_machine = target
.create_target_machine(
&triple,
"generic", // CPU
"", // Features
inkwell::OptimizationLevel::Aggressive,
RelocMode::PIC, // Position-independent code for shared libs
CodeModel::Default,
)
.ok_or("Failed to create target machine")?;
target_machine.write_to_file(backend.module(), FileType::Object, output_path)?;
Ok(())
}
// Cross-compile for multiple targets
fn build_all_platforms(hir: &HirModule) -> Result<(), Box<dyn std::error::Error>> {
initialize_cross_compilation();
let targets = [
("x86_64-apple-darwin", "myapp-darwin-x64.o"),
("aarch64-apple-darwin", "myapp-darwin-arm64.o"),
("x86_64-unknown-linux-gnu", "myapp-linux-x64.o"),
("aarch64-unknown-linux-gnu", "myapp-linux-arm64.o"),
];
for (triple, output) in targets {
println!("Compiling for {}...", triple);
compile_for_target(hir, triple, Path::new(output))?;
}
Ok(())
}Use the ZRTL DynamicValue type for runtime type-safe values:
use zyntax_compiler::zrtl::{DynamicValue, TypeId, TypeMeta, TypeRegistry};
// Create dynamic values
let int_val = DynamicValue::from_i32(42);
let float_val = DynamicValue::from_f64(3.14);
let string_val = DynamicValue::from_string("hello".to_string());
// Type checking
assert!(int_val.is_type(TypeId::I32));
assert!(float_val.is_type(TypeId::F64));
// Safe accessors
if let Some(n) = int_val.get_i32() {
println!("Integer: {}", n);
}
// Register custom types
let mut type_registry = TypeRegistry::new();
let my_type_id = type_registry.register(zyntax_compiler::zrtl::TypeInfo {
name: "MyStruct".to_string(),
size: std::mem::size_of::<MyStruct>() as u32,
alignment: std::mem::align_of::<MyStruct>() as u32,
dropper: Some(drop_my_struct),
category: zyntax_compiler::zrtl::TypeCategory::Struct,
});
extern "C" fn drop_my_struct(ptr: *mut u8) {
unsafe { let _ = Box::from_raw(ptr as *mut MyStruct); }
}[dependencies]
zyntax_compiler = { version = "0.1", features = ["cranelift-backend"] }
zyntax_typed_ast = "0.1"
# Optional: For LLVM backend
# zyntax_compiler = { version = "0.1", features = ["llvm-backend"] }
# For building ZRTL plugins
zrtl_macros = "0.1"
inventory = "0.3"| Use Case | Format | API/Command |
|---|---|---|
| Development/Scripting | ZPack + JIT | zyntax compile --jit --pack runtime.zpack -g lang.zyn -s main.lang |
| Production Deployment | Static Library + AOT | zyntax compile --backend llvm -g lang.zyn -s main.lang -o app --lib runtime |
| Cross-Platform JIT | Fat ZPack | Include all platform .zrtl files in one ZPack |
| Cross-Platform AOT | Per-Platform Builds | Build separate binaries with platform-specific .a files |
| Custom JIT Embedding | Rust API |
compile_to_hir() + CraneliftBackend
|
| Custom AOT Embedding | Rust API |
compile_to_hir() + LLVMBackend + linker |
| Cross-Compilation | LLVM API |
Target::initialize_all() + custom TargetTriple
|
| Plugin Development | RuntimePlugin | Implement RuntimePlugin trait or use zrtl_macros
|
- ZPack is for JIT - Contains dynamic libraries loaded at runtime
- Static libraries are for AOT - Linked at compile time by the system linker
- CLI is frontend-agnostic - Runtime symbols come from packs/libraries, not built-in
-
Library search -
--lib foosearches standard paths forlibfoo.a - Fat ZPacks - Include multiple platform runtimes for universal distribution
- RuntimePlugin trait - Embed Zyntax in your Rust application with custom runtime symbols
-
DynamicValue - Type-safe FFI for Haxe's
Dynamictype and generic containers