ek9_compile
Compiles the workspace and answers status (ok | failed | found-no-sources | blocked), sourceCount, errorCount, warningCount and diagnostics[] - each with file, line, column, severity, errorCode and message, the ek9_query_diagnostics record - for every discovered .ek9 file.
When to use it
'Does the workspace compile, and what are the errors?' is one call: {} recompiles the initialised workspace, {projectDir:'.'} points it at a project root first.
When not to use it
- Reading the last compile's cached diagnostics without recompiling - use
ek9_query_diagnostics. - Running a program - use
ek9_run_program(it compiles first). - Running tests - use
ek9_run_tests(it compiles first). - Canonical source formatting - use
ek9_format.
Details
Two orthogonal dimensions: `mode` = cache strategy (default full; in V1 incremental/full/clean share the full-workspace compile path and only check-only differs - no artefacts) and artifact-intent flags (includeDev, withDebug, withProfiling, noLink, optimisation o0/o2-default/o3, phaseUpTo - any CompilationPhase name, default PRE_IR_CHECKS). Guards: includeDev implies withDebug; withProfiling implies withDebug and requires explicit optimisation:o0 (the o2 default blocks it); withDebug+o3 and withProfiling+noLink are blocked (status:'blocked' with a kebab-case reason). A success with zero discovered sources is status:'found-no-sources' (misconfigured projectDir); the .ek9 cache is not flag-keyed (use mode:full after switching debug/profile/opt). A failed compile with E07235 (fields but no '?') gets 'default operator ?' applied through the verified write and is compiled again - corrected[] and edits[] say where; repair:false only compiles.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
errorLevel | integer | Error detail level: 0 minimal, 1 visual, 2 + fuzzy suggestions, 3 + rich explanations. | |
includeDev | boolean | Include dev-only code (test sources, fixtures). Implies withDebug per the CLI -cd family bundling rule. | |
mode | string: incremental | full | check-only | clean | Cache strategy: incremental (recompile only changed files), full (re-emit all bytecode), check-only (compile + analyse without emitting artefacts), clean (wipe artefacts + dependencies before compile). | |
noLink | boolean | Compile + analyse without producing an executable artefact. Equivalent to mode: check-only when paired with mode: incremental or full. Rejected with withProfiling. | |
optimisation | string: o0 | o2 | o3 | o0 (none - fastest compile, max debug fidelity), o2 (balanced), o3 (aggressive - inlining, DCE, loop opts; breaks debug attributes and profiling probes). Auto-set to o0 when withProfiling: true. | |
phaseUpTo | string: READING | PARSING | SYMBOL_DEFINITION | DUPLICATION_CHECK | REFERENCE_CHECKS | EXPLICIT_TYPE_SYMBOL_DEFINITION | TYPE_HIERARCHY_CHECKS | FULL_RESOLUTION | POST_RESOLUTION_CHECKS | PRE_IR_CHECKS | PLUGIN_RESOLUTION | SYMBOLIC_ANALYSIS | IR_GENERATION | IR_ANALYSIS | IR_OPTIMISATION | CODE_GENERATION_PREPARATION | CODE_GENERATION_CONSTANTS | CODE_GENERATION_APPLICATIONS | CODE_GENERATION_AGGREGATES | CODE_OPTIMISATION | PLUGIN_LINKAGE | APPLICATION_PACKAGING | PACKAGING_POST_PROCESSING | Stop after the named compilation phase (e.g. FULL_RESOLUTION, IR_GENERATION) for pipeline-stage diagnostics. | |
projectDir | string | Project root to discover .ek9 files from - absolute, or relative to the workspace root; (re)initialises the workspace before compiling. Omit to reuse the initialised workspace. Shorthand for roots:[projectDir]. | |
repair | boolean | Apply the fixes the language leaves no choice about, then compile again: a slip with one reading (e.g. '-> a as Integer, b as String', 'Type?: value', a stray ')'), and 'default operator ?' for a class, record or component with fields and no '?' (E07235) - each listed in corrected[] and edits[]. false compiles only, and changes nothing. | |
roots | array of string | Several project roots at once, each as projectDir. Omit for one root (use projectDir) or to reuse the initialised workspace. | |
traceCompiler | boolean | Emit verbose compile-pipeline tracing to the build log. | |
withDebug | boolean | Emit debug info / SMAP for breakpoint + variable inspection. Compatible with optimisation o0 (max fidelity) or o2 (safe). Rejected with o3 (aggressive optimisation strips debug attributes). | |
withProfiling | boolean | Emit per-function profiling probes for hot-path / call-graph data. Forces optimisation: o0. Rejected with o2 / o3 (probes get inlined away). Rejected with noLink (no artefact to profile). Implies withDebug for source-line attribution. |
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it. 3 of the 5 recorded are shown.
Pre ir checks shape
Request
{
"phaseUpTo" : "PRE_IR_CHECKS",
"roots" : [ "<workspace-root>" ]
}
Response
{
"diagnostics" : [ ],
"errorCount" : 0,
"flags" : {
"includeDev" : false,
"mode" : "full",
"noLink" : false,
"optimisation" : "o2",
"phaseUpTo" : "PRE_IR_CHECKS",
"withDebug" : false,
"withProfiling" : false
},
"meta" : {
"serverMs" : "<timing>"
},
"sourceCount" : 1,
"status" : "ok",
"summary" : "Compilation succeeded (mode: full, phase: PRE_IR_CHECKS, optimisation: o2): 1 source, 0 errors, 0 warnings",
"warningCount" : 0
}
Is set operator repaired in both modules
Request
{ }
Response
{
"corrected" : [ "Point (alpha.ek9): added 'default operator ?' - a construct with fields must say when it is set (E07235), and 'default' means set only when every field is set. For a rule of your own, replace it with 'override operator ? as pure' and a body.", "Point (beta.ek9): added 'default operator ?' - a construct with fields must say when it is set (E07235), and 'default' means set only when every field is set. For a rule of your own, replace it with 'override operator ? as pure' and a body." ],
"diagnostics" : [ ],
"edits" : [ {
"after" : " default operator ?",
"endLine" : 11,
"file" : "alpha.ek9",
"startLine" : 11,
"type" : "insert"
}, {
"after" : " default operator ?",
"endLine" : 11,
"file" : "beta.ek9",
"startLine" : 11,
"type" : "insert"
} ],
"errorCount" : 0,
"flags" : {
"includeDev" : false,
"mode" : "full",
"noLink" : false,
"optimisation" : "o2",
"phaseUpTo" : "PRE_IR_CHECKS",
"withDebug" : false,
...
Cut short here: 10 more lines. The whole recorded call.
Is set operator applied then compiled
Request
{ }
Response (refused)
{
"corrected" : [ "Basket (task.ek9): added 'default operator ?' - a construct with fields must say when it is set (E07235), and 'default' means set only when every field is set. For a rule of your own, replace it with 'override operator ? as pure' and a body." ],
"diagnostics" : [ {
"column" : 6,
"errorCode" : "E08050",
"file" : "task.ek9",
"line" : 24,
"message" : "❌ error :[E08050]: '<-' on line 24 position 6: 'rtn': return value is not always initialised\n ──▶ task.ek9:24:7\n │\n 24 │ <- rtn as String?\n │ ^^ '<-'\n │\n └─ 🔗 See: ek9 -h E08050\n",
"richExplanation" : "[DIAGNOSIS]\nA function/method has a return variable that may not be initialized\non all possible execution paths.\n\n[EXAMPLES]\nCompilable examples demonstrating this error and the correct fix:\n ek9 -q 688 (How does EK9 enforce variable initialization order?)\n\n[MUTATING OPERATORS ON RETURN PARAMETERS]\nReturn parameters declared without initialization (e.g., <- rtn as String?)\nCANNOT be used with mutating operators until initialized:\n\n- :=: (copy) and :~: (merge) - require existing value to copy/merge INTO\n- +=, -=, *=, /= etc. - require existing value to modify\n- ++, -- - require existing value to increment/decrement\n\n[A PATH THAT ENDS IN A THROW]\nA path that ends in an unconditional throw never returns, so it does not have to set the\nreturn variable - every arm of an if/else or switch that throws counts as covered. With\n'<- rtn as Integer?', an 'if' arm that assigns 'rtn: amount' and an 'else' arm that is just\n'throw Exception(\"Not positive\")' leave nothing unset.\nA throw inside an 'if' with no 'else' covers nothing (the other path still falls through),\nand a catch or finally that can run after the throw does not see the variable as set:\nreading it there is still E08020.\n\n[DYNAMIC FUNCTIONS]\nA dynamic function ('() extends SecurityGate as function', '() is Routine of (Boolean, Integer) as function')\ninherits its super's returning variable, but its body replaces the super's body - so the inherited variable\nstarts unassigned, whatever the super declared, and every path through the body must assign it. The error is\nreported at the dynamic function. Assign a default first (rtn: Boolean()), or assign on every branch.\n\n[ACTION]\n1. Initialize return parameter at declaration: <- rtn <- defaultValue\n2. Or use simple assignment (:) instead of mutating operators\n3. Or initialize before using mutating operators\n\n[COMMON CAUSES]\n- Return only set in some if branches (and the others neither set it nor throw)\n- A dynamic function setting its inherited return on only some paths\n- Using :=: or :~: on uninitialized return parameter\n- Loop might not execute\n- Expecting mutating operator to initialize\n\n[SEE ALSO]\n- E08050: return not initialized on all paths (this error)\n- E08020: variable used before initialized\n- E08060: variable might not be initialized",
"severity" : "error"
} ],
"edits" : [ {
"after" : " default operator ?",
"endLine" : 18,
"file" : "task.ek9",
"startLine" : 18,
"type" : "insert"
} ],
"errorCount" : 1,
"flags" : {
"includeDev" : false,
"mode" : "full",
"noLink" : false,
"optimisation" : "o2",
...
Cut short here: 13 more lines. The whole recorded call.
See also
- Part of the feature_branch_start workflow: Set feature-branch version + add new dependencies + resolve + verify.
- Part of the investigate_build_failure workflow: Diagnose dependency-resolution or compilation failure: diagnostics → tree → cycles → fix → re-resolve.