lungo generates code from a Lean program the way protoc does from .proto files: one
command, one output directory per language, and plugins for further languages.
lungo [OPTIONS] <COMMAND>
lungo generate --c_out=gen/c --go_out=gen/go --python_out=gen/py --rust_out=gen/rust
Install
With Homebrew, apt, dnf, Cargo, or a direct download for macOS, Linux and Windows: see
installing lungo. Built from lungo-rs, lungo is a development build (see
the runtime).
Configuration
The configuration is lungo.toml in the current directory, another file given with
--config, or the Lake project given with --project and the default settings (see
configuration). Giving both a file and --project is an error.
| Option | Meaning |
|---|---|
--config <FILE> |
A configuration file. Default: ./lungo.toml when it exists. |
--project <DIR> |
The Lake project directory, instead of a configuration file. |
--root <MODULE> |
A root module. Repeatable. Adds to root-modules. |
--export <DECL> |
A declaration to export. Repeatable. |
--export-module <MODULE> |
A module whose declarations to export. Repeatable. |
--assurance-module <MODULE> |
A module to read assurance records from without compiling it into the program. Repeatable. Adds to assurance-modules. |
--name <NAME> |
The program’s name. |
These options are global: they may precede or follow the command.
generate
lungo generate [--<language>_out=DIR]... [--<language>_opt=KEY=VALUE[,KEY=VALUE]...]... [--runtime-dir DIR] [--wasi-sdk DIR] [--verify | --link]
| Language | Output |
|---|---|
rust |
The Rust module build.rs generates, in DIR/<name>/ (see generated code). |
c |
A CMake project of a C library with the program’s C API. |
go |
A Go package (cgo). |
python |
A Python project (scikit-build-core). |
swift |
A Swift package, with the C API for Objective-C. |
ts |
An npm package (ES module, type declarations, program.wasm). |
| any other name | The output of the plugin lungo-gen-<name> found on PATH (see plugins). |
Generated packages describes each language’s package. DIRs on the
command line are relative to the current directory; the configuration’s out directories
to the configuration file. Without any --<language>_out, generate writes the outputs the
configuration names (every language table with out, and [rust] with out-dir); with
none, it fails (LNG0107). --<language>_opt adds options to the
language’s options and overrides them; rust has no options on the command line (its
settings are typed: see configuration).
Each output directory belongs to lungo: it is replaced as a whole, and a directory lungo did
not create (one without its build-info.json) is never replaced
(LNG0107). An output whose inputs (the Lean project, the toolchain,
lungo, the generator and its options, the trust and assurance policies) are unchanged, and which still holds exactly the files
it generated (none edited, removed or added since), is reused: generate prints
up to date: <language> <DIR> instead of generated <language> into <DIR>. The project is
analyzed once for every output (once more for ts, whose target is 32-bit WebAssembly).
Generated code is often committed, so that a package’s users need neither Lean nor lungo.
--verify checks that it is current: it generates every output without writing, compares
each directory’s generated sources with it, prints verified: <language> <DIR> for each that
matches, and fails with LNG0110 naming every file that changed, is
missing or is extra. Neither build-info.json nor a platform product is compared.
A platform product is a file an output links for the machine that writes it: the TypeScript
binding’s program.wasm, the program linked with the WebAssembly runtime. It runs on every
platform, but its bytes depend on the machine that built the runtime it links, so two
machines link different modules from the same sources. It is a build product: generated with
the sources, never committed (ignore it in version control; npm packs it from the package’s
files). --link writes only the platform products, for this machine, into outputs whose
sources are current — linked: <language> <DIR> for this machine — leaving the sources as
they are; where they are not current it links nothing and fails with
LNG0110. A checkout runs --link before it tests the TypeScript
binding.
| Option | Meaning |
|---|---|
--runtime-dir <DIR> |
A local lungo distribution to use instead of this release’s (see the runtime). |
--wasi-sdk <DIR> |
The wasi-sdk linking the TypeScript binding’s WebAssembly. Default: WASI_SDK_PATH, else the pinned wasi-sdk release, downloaded into the cache and checked against its SHA-256 digest. |
--verify |
Compare the generated sources instead of writing (above). |
--link |
Link this machine’s platform products into outputs whose sources are current (above). |
assurance
lungo assurance [DECLARATION] [--format human|json] [--claim-kind RELATION]... [--facility NAME]...
[--assumption NAME]... [--trust-issue ISSUE]... [--policy] [--compose FILE]...
Reports, export by export, what the program’s Lean code claims and proves of it, what its proofs
do not cover (its trust) and what its claims assume of the host, keeping the three apart. It
builds and analyzes the project as check does, and refuses a program whose assurance records
are invalid (LNG0701–LNG0710): a report of them would mislead.
The human report starts with the program and its provenance (Lean version and commit, lungo
version, assurance schema), then one block per export, with the sections == Claims ==,
== Trust ==, == Assumptions == and == Facilities ==; an empty section reads (none). A
claim’s status is written in brackets, [proved] or [incomplete] (resting on sorry), with
its relation, the specifications it is about and the assumptions it rests on. An assumption is
written [assumed] with what it says — its definition when it is one, otherwise its type — and
its facility:
[assumed] Polyglot.ScalesMonotonically : ∀ (a b : Nat), a ≤ b → Polyglot.hostScale a ≤ Polyglot.hostScale b (of Polyglot.Scaler)
An async export is marked (async). --format json prints the same as a JSON object: schema_version,
program, provenance, exports (as in assurance.json),
the claims about them, policy (the violations, with --policy) and composition (the
mismatches).
| Option | Meaning |
|---|---|
DECLARATION |
Report only this export (an error if it is not one). |
--format <FORMAT> |
human (default) or json. |
--claim-kind <RELATION> |
Only exports with a claim of this relation, such as lungo.decides. Repeatable. |
--facility <NAME> |
Only exports needing this facility, by Lean name or identifier. Repeatable. |
--assumption <NAME> |
Only exports whose claims assume this, by Lean name. Repeatable. |
--trust-issue <ISSUE> |
Only exports with this issue: sorry, axioms (beyond propext, Classical.choice, Quot.sound), unsafe, partial, facility (needs a facility of the host), no-claim, incomplete (a claim about it rests on sorry), assumption (a claim about it is conditional on an assumption). Repeatable. |
--policy |
Check the trust and assurance policies; prints policy: satisfied, or the violations. |
--compose <FILE> |
Another package’s assurance.json to check against. Repeatable. |
Filters of different kinds must all match; values of one kind match if any does.
--compose checks that separately generated packages describe every record they share (a
specification, claim, facility or assumption, by Lean name) the same way: from the same package,
with the same fingerprint. A record described differently — a specification, or a definition of
the workspace it uses, changed in one package and not regenerated in the other — is reported as LNG0708 with
each document’s package and fingerprint. With a configuration (a lungo.toml, --config or
--project), the project’s own document is one of those compared; without one, the files given
are compared with each other, and at least two are needed. A file that is not an assurance
document lungo reads is LNG0711.
| Exit status | Meaning |
|---|---|
0 |
The report was printed; the policies hold and the documents agree (when checked). |
1 |
An error (error[LNGxxxx]: …). |
2 |
A policy violation (--policy). |
3 |
A composition mismatch (--compose). |
The runtime
Every language runs the program on the same runtime, prebuilt for every
platform and published with each lungo release: generated packages refer to
the release’s runtime archives by URL and SHA-256 digest (the C package’s CMake downloads and
checks it; the Go, Python, Swift and TypeScript support libraries carry it). A release of
lungo knows its runtime release from the manifest built into it.
A development build of lungo (built from source) knows no release. Generating a package
other than Rust then needs a local distribution: a directory laid out as a release
(runtime/, wasm/, go/, python/, lungo-swift/, ts/), built from lungo-rs
with cargo run -p lungo-dist -- local --out DIR, and given with --runtime-dir
DIR. With --python PATH it also holds lungo-py’s wheel for this machine in wheels/, for pip
to find (--find-links DIR/wheels) when it builds a generated Python package. Without one the command fails with LNG0108. The generated
packages then refer to that directory, so they build only on that machine.
lungo runtime fetch [--target TRIPLE]
lungo runtime path [--target TRIPLE]
lungo runtime verify [--target TRIPLE]
| Command | Meaning |
|---|---|
runtime fetch |
Downloads the runtime archive for the target into the cache and unpacks it, after checking its SHA-256 digest; prints its directory. A cached archive that was checked is reused. The default target is the one lungo itself was built for: on Windows, the released command is built for the GNU ABI, so x86_64-pc-windows-gnu; name --target x86_64-pc-windows-msvc for the MSVC runtime. |
runtime path |
Prints the directory of the cached runtime for the target, or fails if it was not fetched. |
runtime verify |
Downloads the archive again and checks it. |
A download whose digest differs from the release’s is discarded and reported as
LNG0109. The runtime package holds include/lungo.h, the static and
shared libraries in lib/, lib/cmake/lungo/lungoConfig.cmake and
lib/pkgconfig/lungo.pc.
Updating
lungo self update
Replaces this lungo with the latest release of the channel it was published on, and prints
lungo <old> -> <new>, or lungo <version> is the latest release when there is nothing newer.
It reads the release manifest of the registry this lungo was published in (the one
installing lungo names), and installs a release only when all of
this holds:
- the manifest’s signature chains to the keys built into this
lungo: a certificate for the release key, signed by two of those keys, valid now, and the manifest signed by that release key; - the archive has the size and the SHA-256 digest the manifest lists;
- the command in the archive runs and reports the version it was published as.
Anything else is LNG0112, and the installed command is left as it is. The
new command takes the place of the old one in one step, where the old one is, so lungo on your
PATH is the new release from then on. On Windows the running command cannot be removed: it is
left beside the new one as lungo.exe.old, and the next update removes it.
Two kinds of lungo are not updated this way, and say what updates them
(LNG0111):
| Installed with | Update with |
|---|---|
| Homebrew | brew upgrade machinefabric/tap/lungo |
| apt | sudo apt update && sudo apt install --only-upgrade lungo |
| dnf | sudo dnf upgrade lungo |
| Cargo, or built from lungo-rs | cargo install lungo-cli, or build the newer source |
A package manager’s lungo is recognized by the file installed-by its package puts beside the
command. A lungo installed where you cannot write — /usr/local/bin, with sudo install — is
updated with the same rights: sudo lungo self update.
Other commands
| Command | Output |
|---|---|
check |
Builds the project and translates it (Rust and C) without writing output; prints the toolchain, the number of modules, compiled declarations, externs and exports, the program’s assurance (specifications, claims proved and incomplete, facilities, assumptions, and how many exports have proved claims), and any warnings. |
inspect <DECL> |
For a declaration: Lean type, module, source (relative to the configuration); for an export, its claims, trust, assumptions and facilities as assurance reports them, then its trust in full; for a theorem that is a claim’s evidence, the claim (status, relation, subjects, statement, specifications, assumptions, the evidence’s axioms, fingerprint); for a specification, its kind, fingerprint, what it says (its definition when it is one, otherwise its type) and the claims citing it; for an assumption, its facility and what it says (likewise); for a facility, what it is; then the compiled signature, compiler auxiliaries, extern entry. |
ir <DECL> |
The Bridge IR of the declaration and of the auxiliaries the compiler derived from it. |
rust <DECL> |
The generated Rust of the declaration: its compiler-layer code and, when exported, its facade function. |
externs |
externs.json: every extern the program reaches and its Rust implementation. |
mappings |
names.json: every Lean-to-Rust name mapping. |
prepare |
Builds (or verifies) the worker for the project’s toolchain; prints the Lean version, commit, worker identity and location. |
setup |
Installs the pinned toolchain with elan, materializes the dependencies locked in lake-manifest.json, and prepares the worker. |
Errors are printed as error[LNGxxxx]: … (see errors) and the command exits
with status 1 (assurance has two more statuses: see above).
Example
$ lungo generate --go_out=gen/formal --python_out=gen/py
generated go into gen/formal
generated python into gen/py
$ lungo inspect Formal.step
Formal.step : Formal.Sess → Formal.Op → Bool × Formal.Sess
module: Formal.Session
source: lean/Formal/Session.lean
exported: yes
== Claims ==
[proved] lungo.preserves Formal.step_implements (of Formal.Spec)
== Trust ==
(none)
== Assumptions ==
(none)
== Facilities ==
(none)
== Trust, in full ==
axioms: (none)
depends on sorry: false
unsafe dependencies: (none)
partial dependencies: (none)
extern dependencies: lean_nat_add
compiled: (obj, u8) -> obj in module Formal.Session
compiler auxiliaries: Formal.step._boxed