lungo is configured in two places that describe the same settings:
lungo.toml, read by thelungocommand, for every output language;- a
lungo_build::Builderin a Cargobuild.rs, for the Rust output (compile_lean(project)isconfigure().compile_lean(project)).
Every setting has a default. Unknown keys are errors (LNG0107).
# lungo.toml
project = "lean"
[lean]
root-modules = ["Formal.Session"]
assurance-modules = ["Formal.Laws"]
[assurance]
require-claims = ["Formal.Session"]
[rust]
out-dir = "src/generated"
type-attributes = [
{ path = "Formal", attribute = "#[derive(serde::Serialize, serde::Deserialize)]" },
]
rust-externs = { host_log = "crate::host::log" }
[go]
out = "gen/formal"
options = { package = "formal" }
[plugins.kotlin]
out = "gen/kotlin"
// build.rs
fn main() -> lungo_build::Result<()> {
lungo_build::configure()
.root_module("Formal.Session")
.assurance_module("Formal.Laws")
.require_claims("Formal.Session")
.type_attribute("Formal", "#[derive(serde::Serialize, serde::Deserialize)]")
.rust_extern("host_log", "crate::host::log")
.compile_lean("lean")
}
The file
| Key | Meaning |
|---|---|
project |
The Lake project directory. Required. |
[lean] |
The settings every language shares: what is compiled and exported, the modules read for their assurance records, the trust policy, the program’s name, the worker, the toolchain. |
[assurance] |
The assurance policy: the exports that must carry a proved claim, and the assumptions nothing may rest on. |
[rust] |
The Rust generator’s settings. |
[c], [go], [python], [swift], [ts] |
A built-in generator’s output directory, options and extern types. |
[plugins.<name>] |
A plugin’s output directory and options. <name> is lowercase letters, digits and -, and not the name of a built-in generator. |
Paths in the file (project, out, out-dir, toolchain-dir) are relative to the file’s
directory. lungo keeps scratch files in .lungo/ next to the file; add it to
.gitignore.
Program
Set in [lean], or with the Builder methods:
| Builder | TOML key | Type | Default | Meaning |
|---|---|---|---|---|
root_module(m) |
root-modules |
list of module names | the default targets’ roots | Modules of the project whose code is compiled, with everything they import. Each must belong to the project’s root package. |
export(d) |
exports |
list of declaration names | [] |
Declarations that receive public functions. |
export_module(m) |
export-modules |
list of module names | [] |
Modules (with their submodules) whose public definitions receive public functions. |
assurance_module(m) |
assurance-modules |
list of module names | [] |
Modules loaded only for the assurance records they declare, such as laws proved apart from the code they are about. They are imported into the environment lungo reads but never compiled into the program, so they change nothing the program does. Each must belong to a package of the Lake workspace and not be a root module. |
name(n) |
name |
string | the Lake package’s name | The program’s name: of the generated module, package and symbol prefix. Letters, digits, _ and -, not starting with a digit or -. |
Which externs the host implements is not a setting: the program’s Lean code declares them, as
the operations of facilities (@[lungo_facility] and @[lungo_operation]; see how to
call host code from Lean). Every generated package
asks the host for every operation of every facility the program’s linked modules declare.
An operation’s extern key is the symbol of its @[extern "symbol"], or the declaration’s name
for the other extern forms.
Without root_module, the roots are what lake build builds: the root modules of every
default target of the root package (defaultTargets in lakefile.toml, @[default_target]
in lakefile.lean), which are Lean libraries (their roots) or Lean executables (their
root). A package without default targets needs root_module
(LNG0301).
Without export and export_module, the root modules and their submodules are exported.
From an exported module, definitions are exported unless they are private, internal
(_-prefixed components), instances or their methods, compiler-generated (recursors,
matchers, noConfusion, projections, constructors, structural auxiliaries), syntax node
kinds (syntax, notation, macro), or meta definitions.
Trust policy
Set in [lean]:
| Builder | TOML key | Default | Rejects an export that… |
|---|---|---|---|
deny_sorry(b) |
deny-sorry |
true |
depends on sorry |
deny_axioms(b) |
deny-axioms |
false |
depends on an axiom other than propext, Classical.choice, Quot.sound |
deny_unsafe(b) |
deny-unsafe |
false |
has an unsafe definition in its executable closure (outside the Lean toolchain) |
deny_sorry and deny_axioms also apply to the evidence of every claim (the theorem it
names): a claim proved with sorry, or with an axiom the policy denies, is refused as an export
would be. With deny-sorry = false, such a claim is reported with status incomplete, and is
not a proved claim for require-claims.
Violations fail the build with LNG0601.
Assurance policy
Set in [assurance], or with the Builder methods. lungo first checks the program’s assurance records
themselves (their form, the declarations they name, the shape of each claim); a problem with one
fails the build whatever the policy, with its own code (LNG0701–LNG0710). Then:
| Builder | TOML key | Default | Meaning |
|---|---|---|---|
require_claims(path) |
require-claims |
[] |
Every export the path selects must be the subject of at least one proved claim (LNG0602 otherwise). . selects every export; any other path selects the export it names and every export in it as a namespace (Formal selects Formal.apply, Formal.Se selects nothing). |
forbid_assumption(name) |
forbid-assumptions |
[] |
No proved claim may assume the assumption name; if name is a facility, no claim may assume any of its assumptions and no export may call any of its operations (LNG0603 otherwise). |
A path that selects no export, or a name that is neither an assumption nor a facility of the
program, is an error (LNG0107): it cannot have any effect.
The policy is part of what an output is generated from: tightening it makes lungo generate
check an output again rather than keep it as up to date.
Worker
Set in [lean]:
| Builder | TOML key | Default | Meaning |
|---|---|---|---|
worker_timeout(d) |
worker-timeout |
none | Wall-clock seconds after which the worker’s processes are killed (LNG0304). |
worker_cpu_limit(d) |
worker-cpu-limit |
none | Processor seconds the worker may use (LNG0305). |
worker_memory_limit(bytes) |
worker-memory-limit |
none | Memory the worker may use. Enforced on Linux and Windows; an error (LNG0104) elsewhere. |
hermetic(b) |
hermetic |
false |
Run the worker with only PATH, HOME, USERPROFILE, SystemRoot, SYSTEMROOT, TEMP, TMP, TMPDIR, LANG, LC_ALL, ELAN_HOME, LOCALAPPDATA, APPDATA from the environment, plus Lake’s environment for the project. |
hermetic_worker_cache(b) |
hermetic-worker-cache |
false |
Keep the compiled worker beside the build output instead of in the shared cache. |
lean_option(name, value) |
lean-options |
{} |
Lean options in effect while the worker loads the project and runs Lean metaprograms. |
max_errors(n) |
max-errors |
0 |
Report at most n errors from the worker; 0 reports all. |
Toolchain
Set in [lean]:
| Builder | TOML key | Default | Meaning |
|---|---|---|---|
toolchain_policy(p) |
install-toolchain |
Strict / false |
Install (true) installs a missing pinned toolchain with elan. Strict (false) fails with LNG0103. |
toolchain_dir(dir) |
toolchain-dir |
elan’s installation | Use the toolchain installed in dir. |
Rust
Set in [rust], or with the Builder methods:
| Builder | TOML key | Default | Meaning |
|---|---|---|---|
mode(m) |
mode |
pure-rust |
pure-rust generates Rust on the lungo runtime. lean-oracle runs Lean’s native backend behind the same API (see platforms). |
rust_extern(key, path) |
rust-externs |
{} |
The Rust function implementing each operation of the program’s facilities: extern key → Rust path. Every operation the program declares has one (LNG0401 otherwise). A key that is an extern but not an operation is refused (LNG0706), and a key no extern uses is unused (LNG0405). |
out_dir(d) |
out-dir |
$OUT_DIR/lungo |
The directory receiving <name>/. In build.rs, relative to the Cargo package, and include_lean! finds only the default (use include! for another). For lungo generate, the Rust output (--rust_out overrides it). |
facade_namespace(n) |
facade-namespace |
first component of the first root module | The Lean namespace placed at the root of the generated module. Names outside it are placed under _root_. |
embed_sources(b) |
embed-sources |
false |
Embed the local Lean sources, available through __meta::source. |
emit_rerun_if_changed(b) |
emit-rerun-if-changed |
whether the build runs under Cargo | Print cargo::rerun-if-changed for every input, and cargo::rerun-if-env-changed for the environment variables. |
One build script may compile several Lean projects; each generates its own module, and two
that would generate the same module are an error (LNG0107). Give one
of them another name.
Shaping the generated code
These settings select generated items by path. The path . selects every item; any
other path selects the Lean name it spells and every name in it as a namespace: Formal
selects Formal.Sess and Formal.Sess.isOpen, but Formal.Se selects neither. Fields are
named as in names.json: <Structure>.<field>,
<Constructor>.<binder>, or <Constructor>#<index>. A path other than . that selects
nothing is an error (LNG0107): it cannot have any effect.
| Builder | TOML key | Default | Meaning |
|---|---|---|---|
type_attribute(path, attr) |
type-attributes |
[] |
Adds the Rust attribute attr to every generated struct and enum path selects. |
struct_attribute(path, attr) |
struct-attributes |
[] |
The same, for structs only. |
enum_attribute(path, attr) |
enum-attributes |
[] |
The same, for enums only. |
field_attribute(path, attr) |
field-attributes |
[] |
Adds attr to every field of a generated type path selects. |
skip_debug(paths) |
skip-debug |
[] |
Generates the selected types without #[derive(Debug)], for the application to implement Debug. |
disable_comments(paths) |
disable-comments |
[] |
Generates the selected types and functions without documentation comments. |
extern_type(lean, path) |
extern-types |
{} |
Uses the existing Rust type at path for the Lean type lean instead of generating one. |
In TOML, attribute settings are lists of { path, attribute } tables, skip-debug and
disable-comments lists of paths, and extern-types a table from Lean type to Rust path.
Attributes are written as in Rust source, #[…], and are placed after the derives lungo
generates (Clone, Debug, and PartialEq, Eq, Hash where every field has them), so
they may be #[derive(…)]s of further traits or attributes of those derives, such as
#[serde(…)]. The serde feature of the lungo crate implements serde for the
facade types.
An extern type implements lungo::LeanType for the backend in use, has the Lean type’s
parameters, and represents its values as the Lean type does; in practice it is the type
another lungo build generated for the same Lean type. It is not generated, and types
containing it derive only Clone and Debug. It also implements lungo::LeanLayout with the
Lean type’s layout fingerprint, which the generated module checks when it is compiled (see
extern types); a type generated by lungo implements it already. See
How to share types between Lean projects.
Generator options
A language’s table holds its output directory, out, and its options, options, a table of
strings. --<language>_opt=KEY=VALUE on the command line adds to (and overrides) them. A
generator rejects options it does not know.
| Language | Option | Default | Meaning |
|---|---|---|---|
c |
embed |
absent | true: a directory of the host’s CMake project rather than a project of its own (embedded). |
go |
package |
the program’s C identifier, lowercased | The Go package name. |
python |
package |
the program’s C identifier, lowercased | The import name. |
python |
distribution |
package with _ as - |
The distribution (PyPI) name. |
python |
version |
0.1.0 |
The distribution’s version. |
python |
embed |
absent | A dotted module of the host’s package (host.formal, or host._formal for one private to it): the output is that module, embedded, with no distribution of its own. Excludes package, distribution and version. |
swift |
module |
the program’s name in UpperCamelCase | The Swift module; the C target is <module>Program. |
swift |
embed |
absent | true: the two targets for the host package to declare, with no Package.swift (embedded). |
ts |
package |
the program’s name, lowercased | The npm package name. |
ts |
version |
0.1.0 |
The package’s version. |
ts |
embed |
absent | true: a module of the host’s package, with no package.json (embedded). Excludes package and version. |
Go is always a package of the host’s module; it has no embed. See
generated packages for what each generator writes.
Extern types
A program built on another program’s model (a Lake package requiring another) uses that
program’s types. Generated separately, each package would have its own type for them, and a
value of one could not be passed to the other. A language’s extern-types table names, for a
Lean type, the other generated package’s type to use instead:
[go.extern-types."TaggedUrn.Exec.Wf"]
package = "github.com/machinefabric/tagged-urn-go/formal" # what an import names
name = "Wf" # the type, as that generator named it
| Language | package |
name |
|---|---|---|
c |
the providing package’s header (provider.h) |
the prefix of its items for the type (provider_pos) |
go |
the import path | the Go type |
python |
the module | the class |
swift |
the module | the type |
ts |
the module specifier | the type |
The type must be one the program’s functions or facility operations use
(LNG0107 otherwise). Values then pass between the two packages as they
are: data by value, and values held by handle as the same handle, since every package of a
process runs on the one runtime.
In TypeScript each program runs in a WebAssembly instance of its own, with a handle table of its own, so only types whose values are data can be extern types; a type whose values hold a handle (an opaque type, a function) is refused with the reason.
Both packages must have been generated from the same definition of the type, or one program
would read the other’s values at the wrong layout. Every generated type carries its layout
fingerprint (a digest of its constructors, their fields and every type they reach, with the
Lean version), and a package using an extern type checks the providing package’s before
anything runs: at compile time in Rust, when the package is loaded in Go (init), Python
(import), Swift and C (the program’s first use), and TypeScript (module evaluation). A
mismatch names the type and the package; regenerate both from the same Lean definition.
Environment variables
| Variable | Meaning |
|---|---|
ELAN_HOME |
Where elan keeps toolchains (default ~/.elan). |
LUNGO_CACHE_DIR |
Root of lungo’s cache: compiled workers in workers/, downloaded runtimes in runtime/, the wasi-sdk in wasi-sdk/. Defaults: $XDG_CACHE_HOME/lungo, ~/.cache/lungo, or %LOCALAPPDATA%\lungo. |
WASI_SDK_PATH |
A wasi-sdk to link the TypeScript binding’s WebAssembly with, instead of the pinned release lungo downloads. |
Entry points
| Function | Use |
|---|---|
compile_lean(project) |
In build.rs, with the default configuration. |
configure() |
A Builder with the default configuration. |
Builder::compile_lean(self, project) |
In build.rs. Generates into <out_dir>/<name> and prints Cargo directives: rerun-if-changed (see emit_rerun_if_changed) and, in LeanOracle mode, link directives. project is relative to the Cargo package. |
Builder::run(&self, project, &Environment) |
The same pipeline for a given Environment; returns a BuildOutcome. |
Builder::analyze / Builder::generate |
The two halves of run: the worker’s analysis, then code generation, without publishing. |
Builder::from_options(lean, rust, assurance) |
A Builder of a LeanOptions ([lean]), a RustOptions ([rust]) and an AssurancePolicy ([assurance]). |