How on-chain builds runalpha
Why an on-chain build takes minutes, what the compiler canister does at each stage, and what it keeps afterwards.
An Internet Computer message can run at most 40 billion instructions. A compile needs far more than that in total, so the compiler canister runs it as a job: a state machine that does a slice of work per message and keeps its progress in canister memory between messages.
Time is set by message count#
Each step uses a small part of the per-message limit. What takes time is that each message waits for its turn in a block, about one second on mainnet. So a build's duration is roughly its number of steps times the block time:
| Build | Source | Steps | Time |
|---|---|---|---|
hello.mo | 1 file | about 60 | about a minute |
| A 660-file application | 15 MB | about 4,000 | about an hour |
More cycles don't make a build faster. moxzi build --timings prints an estimate for your project from a local build.
Stages#
moxzi build --remote calls the canister's step(job) method in a loop. Each call returns #more until the job is #done or #err. The stages are:
| Stage | Work |
|---|---|
| Parse | Read and parse your files and their imports |
| Type check | Check each declaration, one per step |
| Lower | Translate to the compiler's intermediate form and run its passes |
| Code generation | Generate wasm, in many steps |
| Emit | Write the module, its names and its metadata |
| Link | Add the Motoko runtime |
Between steps the job is only data in the canister. If moxzi build stops, nothing is lost; continue with --resume-job ID.
The canister can also drive a job itself with timers (startSelfDriven), so a build continues with no client connected.
Repeated builds#
The canister can cache whole builds. A build with the same source files, the same options and the same compiler returns the stored result without compiling. The operator turns the cache on; until then, every build compiles.
What's kept#
When a job finishes, the canister keeps the module, its Candid interface, the diagnostics and the build record, and frees everything else. Stored modules are limited to 100 MiB in total; the oldest are dropped first. Build records are kept longer, in a ring of the most recent few hundred builds, and survive upgrades of the compiler canister.
A build in progress doesn't survive an upgrade of the compiler canister. Payments do.
Watching a build#
These methods on the compiler canister are open to the job's owner and to operators:
| Method | Returns |
|---|---|
status(job) | The current stage and the work done so far |
jobDiags(job) | Errors and warnings |
jobStats() | Totals for the canister. Open to anyone |
buildRecord(job) | The build record. Open to anyone |