Skip to main content
ubx compile runs the full XCL pipeline — lex → parse → typecheck → IR → codegen — and writes the generated Pulumi Python program to .ubx/.

Usage

ubx compile [path] [--all]
FormDescription
ubx compileCompile .xcl files in the current directory
ubx compile ./networkingCompile .xcl files in ./networking
ubx compile --all .Compile every stack in the workspace (recursive)

Output files

All output is written to <path>/.ubx/:
FileDescription
__main__.pyThe Pulumi Python program
requirements.txtProvider package dependencies
Pulumi.yamlPulumi project file (stack name = directory name)

Pipeline phases

The compiler prints a phase line for each stage:
● Compile    ./networking → networking/.ubx/__main__.py
● Typecheck  3 file(s)
● IR         building…
● Output     networking/.ubx/__main__.py

✓  compiled 3 file(s) → networking/.ubx/__main__.py
Phases:
  1. Compile — discover .xcl files, lex and parse them
  2. Typecheck — 5-pass typechecker (symbol collection, reference resolution, locals/resource validation, when conditions, input type checking)
  3. IR — build the intermediate representation
  4. Output — write .ubx/__main__.py, requirements.txt, Pulumi.yaml

Error handling

Errors stop the pipeline at the phase where they occur. No output is written when there are parse or typecheck errors. Parse error example:
stack.xcl:5  unexpected token "}" — expected identifier
Typecheck error example:
stack.xcl:8  undefined reference: subnet.vpc_id (undefined reference "subnet")
Type mismatch example:
input.xcl:3  default value is a number literal but field type is string

Cross-stack reference discovery

When the stack contains @stackName.outputName references, the compiler:
  1. Scans for all @name references in the parsed AST
  2. Looks for sibling directories (at ../name/ relative to the current stack)
  3. Parses the sibling’s .xcl files to extract declared output field names
  4. Validates that every @name.output refers to a real declared output
If a sibling directory is not found:
stack "networking" not found — expected a sibling directory named "networking"
If an output field is not declared in the sibling:
"endpoint" is not an output of stack "networking" — declared outputs are: [vpc_id, subnet_ids]

Single-stack mode (default)

Without --all, ubx compile targets one directory:
ubx compile ./networking    # compiles networking/ only
If the directory contains .iac files instead of .xcl files, the compiler reports an error and suggests using ubx plan/apply. If the directory contains both .xcl and .iac files, compilation is aborted with a “mixed pipeline” error.

Workspace mode (—all)

ubx compile --all .
ubx compile --all ./workspace
With --all, the compiler:
  1. Recursively discovers all subdirectories containing .xcl files
  2. Builds a dependency graph from @stackName cross-stack references
  3. Topologically sorts stacks (dependencies compile first)
  4. Compiles each stack in order, showing ━━━ separators and (N/total) counters
● Resolving  dependency graph
             networking/ → cluster/ → addons/
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● networking/         (1/3)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● Typecheck  2 file(s)
● IR         building…
● Output     networking/.ubx/__main__.py

✓  compiled 2 file(s) → networking/.ubx/__main__.py
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● cluster/            (2/3)
...

Workspace error handling

Individual stack failures do not abort the workspace compile. All stacks are attempted and a summary is printed at the end:
✗  2/3 stacks failed: cluster, addons
Cycle detection:
✗  circular dependency: networking → cluster → networking
Duplicate stack names (two directories with the same basename):
✗  ambiguous stack name "@networking" — found at /workspace/infra/networking and /workspace/staging/networking, rename one directory to resolve

Nested workspaces

--all recurses into subdirectories at any depth. Each directory that contains .xcl files is treated as an independent stack. Cross-stack references (@name) resolve via the same-parent rule — the sibling must be in a directory adjacent to the current stack’s parent. See Multi-stack workspaces for the layout rules.

Stack name

The Pulumi project name (written to Pulumi.yaml) is derived from the directory name:
ubx compile ./networking     # stack name: "networking"
ubx compile ./infra/cluster  # stack name: "cluster"

Flags

FlagDefaultDescription
--allfalseCompile every XCL stack in the workspace recursively