Lyric v4.4.0

Packing a program

A Lyric program normally travels as a .lyrbc module and runs wherever a lyrvm is. Packing removes that condition: one command wraps your program and the runtime into a single executable that runs on a machine that has never heard of Lyric.

lyric pack app.lyr
./app arg1 arg2

That is the whole workflow. lyric pack compiles the file and hands the module to lyrpack, which copies a prebuilt runtime — the stub — and appends the module to it. No compiler ships inside the result, nothing is installed on the target machine, and packing takes about as long as copying the file, because that is what it is.

An already compiled module packs directly, which is how a build.lyr project packs its artifact:

lyric build
lyric pack out/app.lyrbc -o dist/app

What a packed program is

It is your program, owning the whole command line. Every argument goes to main unchanged — there is no -- protocol and no wrapper option, not even --help. If your program wants a help text, it prints one:

import std.io.console;

fn main(args: string[]): int {
    if (args.length == 0) {
        console.println("usage: app <name>");
        return 2;
    }

    for (name in args) {
        console.println("hello, " + name);
    }
    return 0;
}

The exit code is main's return value, a panic exits with 101 and prints its backtrace to stderr, and the source map travels inside the module, so the backtrace names your lines — the program behaves exactly as it did under lyric run, because it is the same runtime executing the same bytes.

A packed program runs with every capability, like any standalone program. The sandbox belongs to hosts that embed the runtime; a program someone ships as an executable answers to the operating system, the way every executable does.

What to know before shipping

The byte-level layout — the footer, its versioning, and what the stub does in which failure — is specified in Pack.md.