std.build

Named project tasks and dependency build recipes.

Importuse std.build

Types

struct BuildAction#

One portable project-task action. Fields not used by kind stay at the constructor defaults, keeping the wire shape simple and versionable.

kind: string

Which action this is; every other field is read only if this kind uses it.

argv: [string]

Program and its arguments; no shell is involved.

cwd: string

Working directory for the child, relative to the task root; empty inherits.

env: [string]

Environment additions spelled "KEY=VALUE".

timeout: string

Deadline as a duration string, such as "30s".

expect: string

Expected result of the child or service.

output: string

Output policy: failures (the default), always, never, or inherit.

stdout_path: string

Task-relative file the captured standard output is written to.

stderr_path: string

Task-relative file the captured standard error is written to.

stdin: string

Literal text fed to the child on standard input.

stdin_path: string

Task-relative file fed to the child on standard input.

stdin_delay: string

How long to hold the pipe empty before writing stdin, as a duration string.

ready_argv: [string]

Probe command retried until it succeeds or the deadline passes.

ready_path: string

File polled for the readiness fragment instead of running a probe.

ready_contains: string

Fragment ready_path must contain before the service counts as ready.

ready_interval: string

How long to wait between readiness attempts, as a duration string.

signal: string

Portable signal name: int, term, kill, or hup.

name: string

Name the action publishes or addresses — a service, temporary directory, or reserved port.

path: string

Task-relative path the action reads, writes, or removes.

content: string

Text the action writes, or the fragment it waits for.

source: string

Task-relative path to copy or rename from.

destination: string

Task-relative path to copy or rename to.

actual: string

Task-relative path holding what the run produced.

expected: string

Task-relative path holding what it should have produced.

update: bool

Whether a comparison rewrites expected from actual instead of failing.

missing_ok: bool

Whether removing a path that is not there succeeds anyway.

contains: [string]

Fragments the file must contain.

excludes: [string]

Fragments the file must not contain.

struct BuildTask#

A named root or dependency in a project task graph.

name: string

Task name, as moon task addresses it.

description: string

One line shown in the task listing.

deps: [string]

Names of tasks that must run first.

inputs: [string]

Files the task reads, for change detection.

outputs: [string]

Files the task produces, for change detection.

profile: string

Build profile the task runs under; empty takes the default.

working_directory: string

Directory the actions run in, relative to the project root.

requires: [string]

Capabilities the host must have, such as posix; unmet ones skip the task.

actions: [BuildAction]

The steps to run, in order.

cleanup: [BuildAction]

Steps run afterwards whether or not the task succeeded.

cache: string

When moon task may skip the task as already done: "outputs" (the default when empty) skips only a task with declared outputs whose inputs, actions, and outputs are unchanged; "inputs" also skips an output-less task whose declared inputs, actions, profile, and compiler are unchanged; "never" always runs.

struct NativeRecipe#

One closed native dependency recipe. configuration is used by CMake; args carries Meson options or Make targets. Libraries retain their platform selector until the host chooses the current target.

kind: string

Which build system drives the dependency: CMake, Meson, Make, or prebuilt.

headers: [string]

Header files the recipe installs for dependents.

libraries: [Library]

Library artifacts the recipe produces, one per platform.

configuration: string

CMake build configuration, such as Release.

args: [string]

Meson options or Make targets, depending on kind.

struct Library#

A platform-specific native library artifact emitted by a dependency recipe.

platform: string

Platform selector the artifact is for.

file: string

Path of the artifact, relative to the recipe output directory.

struct Watch#

A callback-style debounced watcher over std.fs.watch.

Public because watch hands one out: a call typed from its declaration resolves the declared return type, and a module-private type is unknown to the caller — as a user module's private struct would be.

roots: [string]

Roots to watch, each recursively.

debounce_ms: int

How long a group stays open after the last interesting event.

interesting: fn(string, string) -> bool

Called as interesting(kind, path); only a true opens or extends a group.

on_change: fn([string]) -> void

Called once per group with the paths it collected, deduplicated and in first-arrival order.

stop: Channel<int>

Anything sent here asks the running loop to stop.

task: Optional<Future<int>>

The running loop, so stop can wait for it; empty before start.

Constants

const WATCH_DEBOUNCE_MS: int#

Quiet period before a burst of filesystem events is reported. An editor save is several events (a temp file, a rename, a metadata touch); fifty milliseconds of silence is what separates one save from the next.

const WATCH_RERESOLVE: int#

Exit status of watch_main that asks the CLI to re-resolve the module graph and start again (a manifest changed).

Functions

fn lib(platform: string, file: string) -> Library#

Creates a native library descriptor.

  • platform: Platform selector the artifact is for.
  • file: Path of the artifact, relative to the recipe output directory.
extern fn os() -> string#

Returns the current target operating-system name.

extern fn repo_root() -> string#

Returns the absolute root of the current package graph.

extern fn out_dir() -> string#

Returns the native recipe's dedicated output directory.

extern fn dep_name() -> string#

Returns the dependency whose native recipe is executing.

fn cmake_recipe(headers: [string], libraries: [Library], configuration: string, args: [string]) -> NativeRecipe#

Creates a CMake native dependency recipe.

  • headers: Header files the recipe installs for dependents.
  • libraries: Library artifacts the recipe produces, one per platform.
  • configuration: CMake build configuration, such as Release.
  • args: Extra arguments passed to CMake.
use std.build

fn main() {
  let libraries = [
    build.lib("linux", "lib/libcodec.a"),
    build.lib("windows", "lib/codec.lib"),
  ]
  let recipe = build.cmake_recipe(
    ["include/codec.h"],
    libraries,
    "Release",
    ["-DBUILD_TESTING=OFF"],
  )
  build.emit_recipe(recipe)
  println(build.os() + ":" + build.dep_name())
  println(build.repo_root() + ":" + build.out_dir())
}
fn meson_recipe(headers: [string], libraries: [Library], args: [string]) -> NativeRecipe#

Creates a Meson native dependency recipe.

  • headers: Header files the recipe installs for dependents.
  • libraries: Library artifacts the recipe produces, one per platform.
  • args: Meson options for the configure step.
fn make_recipe(headers: [string], libraries: [Library], args: [string]) -> NativeRecipe#

Creates a Make native dependency recipe.

  • headers: Header files the recipe installs for dependents.
  • libraries: Library artifacts the recipe produces, one per platform.
  • args: Make targets to build.
fn prebuilt_recipe(headers: [string], libraries: [Library]) -> NativeRecipe#

Creates a recipe for prebuilt native artifacts.

  • headers: Header files the recipe installs for dependents.
  • libraries: Library artifacts the recipe produces, one per platform.
extern fn emit_recipe(recipe: NativeRecipe)#

Publishes the package's native dependency recipe.

  • recipe: The native dependency recipe to publish for this package.
fn process(argv: [string]) -> BuildAction#

A child process expressed as argv, never as a shell command string.

  • argv: Program and its arguments; no shell is involved.
use std.build

fn main() {
  let check = build.process(["moon", "check", "main.mn"])
  let captured = build.process_capture(["moon", "--version"], "", "version.txt")
  let binary = build.project_binary_to("app", ".moon/bin/app")
  build.emit([build.task("check", [check, captured, binary])])
}
fn project_binary(name: string) -> BuildAction#

Build one root-project executable selected by its manifest exe_name. The artifact is written to .moon/artifacts/<platform output name>.

  • name: Manifest exe_name of the executable to build.
fn project_binary_to(name: string, path: string) -> BuildAction#

Build one root-project executable selected by its manifest exe_name and write it to a task-relative path. The task's resolved profile is reused.

  • name: Manifest exe_name of the executable to build.
  • path: Task-relative path the binary is written to.
fn process_options(argv: [string], cwd: string, env: [string], timeout: string, expect: string, output: string) -> BuildAction#

A process with explicit cwd, environment additions, deadline, expected result (success, failure, or timeout) and output policy.

  • argv: Program and its arguments; no shell is involved.
  • cwd: Working directory for the child, relative to the task root; empty inherits.
  • env: Environment additions spelled "KEY=VALUE".
  • timeout: Deadline as a duration string, such as "30s".
  • expect: Expected result: success, failure, or timeout.
  • output: Output policy: failures (the default), always, never, or inherit.
fn process_capture(argv: [string], stdout_path: string, stderr_path: string) -> BuildAction#

A child process whose captured streams are written to task-relative files. A later compare_files action can then pin either stream without a shell redirect or pipeline.

  • argv: Program and its arguments; no shell is involved.
  • stdout_path: Task-relative file the captured standard output is written to.
  • stderr_path: Task-relative file the captured standard error is written to.
fn process_capture_options(argv: [string], cwd: string, env: [string], timeout: string, expect: string, output: string, stdout_path: string, stderr_path: string) -> BuildAction#

The complete process constructor for integration tasks: typed argv plus cwd/environment, a deadline and expected result, output policy, and stream capture destinations.

  • argv: Program and its arguments; no shell is involved.
  • cwd: Working directory for the child, relative to the task root; empty inherits.
  • env: Environment additions spelled "KEY=VALUE".
  • timeout: Deadline as a duration string, such as "30s".
  • expect: Expected result: success, failure, or timeout.
  • output: Output policy: failures (the default), always, never, or inherit.
  • stdout_path: Task-relative file the captured standard output is written to.
  • stderr_path: Task-relative file the captured standard error is written to.
fn process_input_options(argv: [string], cwd: string, env: [string], timeout: string, expect: string, output: string, stdin: string, stdin_path: string, stdin_delay: string, stdout_path: string, stderr_path: string) -> BuildAction#

A captured process with typed stdin. Set either stdin or stdin_path (not both); stdin_delay may keep the pipe empty first to exercise parked readers without a shell pipeline.

  • argv: Program and its arguments; no shell is involved.
  • cwd: Working directory for the child, relative to the task root; empty inherits.
  • env: Environment additions spelled "KEY=VALUE".
  • timeout: Deadline as a duration string, such as "30s".
  • expect: Expected result: success, failure, or timeout.
  • output: Output policy: failures (the default), always, never, or inherit.
  • stdin: Literal text fed on standard input; leave empty to use stdin_path.
  • stdin_path: Task-relative file fed on standard input; leave empty to use stdin.
  • stdin_delay: How long to hold the pipe empty first, as a duration string.
  • stdout_path: Task-relative file the captured standard output is written to.
  • stderr_path: Task-relative file the captured standard error is written to.
fn service(argv: [string], ready_argv: [string], cwd: string, env: [string], timeout: string, ready_interval: string) -> BuildAction#

A long-lived child kept alive through the selected task root. The readiness argv is retried until success or timeout; teardown is guaranteed.

  • argv: Program and arguments for the long-lived child.
  • ready_argv: Probe retried until it succeeds or timeout passes.
  • cwd: Working directory for the child, relative to the task root; empty inherits.
  • env: Environment additions spelled "KEY=VALUE".
  • timeout: How long readiness may take, as a duration string.
  • ready_interval: How long to wait between readiness attempts, as a duration string.
use std.build

fn main() {
  let port = "$" + "{PORT}"
  let server = build.service_options(
    "api",
    ["moon", "run", "server.mn"],
    "",
    ["PORT=" + port],
    "20s",
    "failures",
    ["curl", "-fsS", "http://127.0.0.1:" + port + "/health"],
    "",
    "",
    "100ms",
    "term",
  )
  let probe = build.wait_service("api", "20s", "100ms")
  let stop = build.signal_service("api", "term")
  build.emit([build.task("integration", [build.reserve_port("PORT"), server, probe, stop])])
}
fn service_options(name: string, argv: [string], cwd: string, env: [string], timeout: string, ready_interval: string, ready_argv: [string], ready_path: string, ready_contains: string, stdout_path: string, stderr_path: string) -> BuildAction#

A named service with live pipe-backed logs and either process or text readiness. Named services may be signalled and awaited by later actions.

  • name: Name later actions signal and await the service by.
  • argv: Program and arguments for the long-lived child.
  • cwd: Working directory for the child, relative to the task root; empty inherits.
  • env: Environment additions spelled "KEY=VALUE".
  • timeout: How long readiness may take, as a duration string.
  • ready_interval: How long to wait between readiness attempts, as a duration string.
  • ready_argv: Probe retried until it succeeds; leave empty to use text readiness.
  • ready_path: Log file polled for ready_contains instead of running a probe.
  • ready_contains: Fragment that marks the service ready.
  • stdout_path: Task-relative file the captured standard output is written to.
  • stderr_path: Task-relative file the captured standard error is written to.
fn signal_service(name: string, signal: string) -> BuildAction#

Deliver a portable named signal (int, term, kill, or hup) to a running named service. Signal actions are explicitly skipped by projects that do not declare a POSIX requirement.

  • name: Service to signal, as service_options named it.
  • signal: Portable signal name: int, term, kill, or hup.
fn temp_dir(name: string) -> BuildAction#

Create a unique temporary directory for the task session and publish its path as MOON_TASK_TEMP_<name> to later child/service actions.

  • name: Name the path is published under as MOON_TASK_TEMP_<name>.
fn reserve_port(name: string) -> BuildAction#

Reserve a loopback TCP port until the next child/service action and publish it as MOON_TASK_PORT_<name>.

  • name: Name the port is published under as MOON_TASK_PORT_<name>.
fn glob(pattern: string) -> [string]#

Every existing file matching a glob pattern, sorted, with / separators.

* and ? match inside one path segment; ** matches across segments, so src/**/*.mn reaches nested directories while src/*.mn does not. The leading literal segments select the directory that is walked, so a pattern never scans more of the tree than it names. A pattern with no wildcard is returned as itself.

Build scripts use this to declare one action per file — a task whose steps are files reports per file as it runs, instead of hiding a whole tree behind one opaque step.

  • pattern: Path pattern where * and ? stay inside a segment and ** spans them.
use std.build

fn main() {
  let actions = build.glob("src/**/*.mn").map(
    |path| build.process(["moon", "fmt", "--check", path]),
  )
  println(build.temp_path("generated"))
  println(build.temp_path_forward("generated"))
  println(build.temp_file_uri("generated"))
  println(build.reserved_port("HTTP_PORT"))
  build.emit([build.task("fmt", actions)])
}
fn temp_path(name: string) -> string#

The path temp_dir(name) publishes, spelled as the placeholder the runner expands in argv, paths, and templates. Build scripts name resources through these helpers rather than writing ${MOON_TASK_TEMP_<name>} by hand: the placeholder spelling is the runner's protocol, not the script's.

fn temp_path_forward(name: string) -> string#

temp_path with / separators on every host, for a value a child parses as a portable path rather than a native one.

fn temp_file_uri(name: string) -> string#

temp_path rendered as a portable file:// URL.

fn reserved_port(name: string) -> string#

The port reserve_port(name) publishes, as the placeholder the runner expands.

fn wait_service(name: string, timeout: string, expect: string) -> BuildAction#

Await a named service under a deadline. expect accepts success, failure, exit:N, or signal:int|term|kill|hup.

  • name: Service to await, as service_options named it.
  • timeout: Deadline as a duration string, such as "30s".
  • expect: Expected outcome: success, failure, exit:N, or signal:int|term|kill|hup.
fn wait_text(path: string, content: string, timeout: string, interval: string) -> BuildAction#

Poll a task-relative text file until it contains the requested fragment.

  • path: Task-relative file to poll.
  • content: Fragment that ends the wait once the file contains it.
  • timeout: Deadline as a duration string, such as "30s".
  • interval: How long to wait between polls, as a duration string.
fn write_file(path: string, content: string) -> BuildAction#

Creates an action that writes the supplied file contents.

  • path: Task-relative file to create or overwrite.
  • content: Text to write verbatim.
use std.build

fn main() {
  let files = [
    build.make_directory("out"),
    build.write_file("out/a.txt", "moon\n"),
    build.append_file("out/a.txt", "Tour\n"),
    build.copy_file("out/a.txt", "out/b.txt"),
    build.assert_text("out/b.txt", ["moon"], ["Sun"]),
    build.compare_files("out/a.txt", "out/b.txt"),
    build.remove_path("out"),
  ]
  build.emit([build.task("files", files)])
}
fn write_template(path: string, content: string) -> BuildAction#

Write text after expanding the same explicit ${NAME} placeholders used by argv and paths. ${NAME|path-forward} uses / separators and ${NAME|file-uri} renders an absolute path as a portable file URL; no shell interpolation is performed.

  • path: Task-relative file to create or overwrite.
  • content: Text to write after expanding ${NAME} placeholders.
fn append_file(path: string, content: string) -> BuildAction#

Append content to a task-relative file without invoking a shell redirect.

  • path: Task-relative file to append to.
  • content: Text to append verbatim.
fn copy_file(source: string, destination: string) -> BuildAction#

Creates an action that copies one file to another.

  • source: Task-relative file to read.
  • destination: Task-relative file to create or overwrite.
fn copy_tree(source: string, destination: string) -> BuildAction#

Recursively copy a source tree into a destination that must not exist. Symlinks and other special entries are rejected rather than followed.

  • source: Task-relative directory to copy.
  • destination: Task-relative directory that must not already exist.
fn filter_lines(source: string, destination: string, excluded_prefixes: [string]) -> BuildAction#

Copy text while dropping every line whose bytes begin with one of the supplied prefixes. Newline bytes for retained lines are preserved.

  • source: Task-relative text file to read.
  • destination: Task-relative file the retained lines are written to.
  • excluded_prefixes: Lines starting with any of these are dropped.
fn rename_path(source: string, destination: string) -> BuildAction#

Atomically rename one task-relative path to another.

  • source: Task-relative path to move.
  • destination: Task-relative path to move it to.
fn delay(duration: string) -> BuildAction#

Delay the next task action without relying on a platform sleep executable.

  • duration: How long to wait, as a duration string.
fn compare_files(actual: string, expected: string) -> BuildAction#

Creates an action that compares two files byte for byte.

  • actual: Task-relative file the run produced.
  • expected: Task-relative file it must match byte for byte.
fn compare_trees(actual: string, expected: string) -> BuildAction#

Require two directory trees to have the same relative file/directory inventory and byte-identical file contents. Special entries are rejected.

  • actual: Task-relative directory the run produced.
  • expected: Task-relative directory it must match entry for entry.
fn make_directory(path: string) -> BuildAction#

Creates an action that makes a directory and its parents.

  • path: Task-relative directory to create along with its parents.
fn remove_path(path: string) -> BuildAction#

Creates an action that removes a path tree.

  • path: Task-relative path to remove, together with everything under it.
fn assert_text(path: string, contains: [string], excludes: [string]) -> BuildAction#

Require every contains fragment and reject every excludes fragment in a task-relative UTF-8 text file.

  • path: Task-relative UTF-8 text file to inspect.
  • contains: Fragments the file must contain.
  • excludes: Fragments the file must not contain.
fn assert_json(path: string, checks: [string]) -> BuildAction#

Evaluate primitive JSON-path checks such as gc_telemetry.regions.created > 0 or gc_telemetry.regions.created == $gc_telemetry.regions.released against the first JSON object record in a file.

  • path: Task-relative file whose first JSON object record is checked.
  • `checks`: Primitive JSON-path assertions, such as `gc.regions.created > 0`.
fn assert_directory_count(path: string, max_exclusive: string) -> BuildAction#

Require a task-relative directory to contain fewer than max_exclusive entries. Useful for descriptor/procfs leak censuses without shell pipes.

  • path: Task-relative directory to census.
  • max_exclusive: Entry count the directory must stay below.
fn task(name: string, actions: [BuildAction]) -> BuildTask#

The concise task constructor. Use task_options when the root has metadata, requirements, declared files, or cleanup actions.

  • name: Task name, as moon task addresses it.
  • actions: Steps to run, in order.
use std.build

fn main() {
  let generate = build.task("generate", [build.write_file("out.txt", "moon")])
  let check = build.task_options(
    "check",
    "verify generated output",
    ["generate"],
    ["out.txt"],
    [],
    "",
    "",
    [],
    [build.assert_text("out.txt", ["moon"], [])],
    [],
  )
  build.emit([generate, check])
}
fn task_options(name: string, description: string, deps: [string], inputs: [string], outputs: [string], profile: string, working_directory: string, requires: [string], actions: [BuildAction], cleanup: [BuildAction]) -> BuildTask#

Creates a project task with explicit dependencies, resources, and actions.

  • name: Task name, as moon task addresses it.
  • description: One line shown in the task listing.
  • deps: Names of tasks that must run first.
  • inputs: Files the task reads.
  • outputs: Files the task produces.
  • profile: Build profile to run under; empty takes the default.
  • working_directory: Directory the actions run in.
  • requires: Capabilities the host must have, such as posix.
  • actions: Steps to run, in order.
  • cleanup: Steps run afterwards whether or not the task succeeded.
fn emit(tasks: [BuildTask])#

Emits the private, versioned graph declaration consumed by moon task.

  • tasks: The task graph to publish for moon task.
fn cached(task: BuildTask, policy: string) -> BuildTask#

The same task with an explicit cache policy: "outputs", "inputs", or "never". "inputs" is for a test task — declared inputs and no outputs — that need not run again while nothing it reads, nor the compiler, has changed; moon task --cache-status shows the decision and --no-cache overrides it.

  • task: The task to annotate.
  • policy: "outputs", "inputs", or "never".
fn main() {
  let unit = std.build.cached(
    std.build.task_options("unit", "unit tests", [], ["src", "tests"], [], "test", "", [], [
      std.build.process(["moon", "test"]),
    ], []),
    "inputs",
  )
  std.build.emit([unit])
}
fn hidden_path(path: string) -> bool#

Anything under a dot-directory, or a dotfile itself. A partially written file inside .tmp/ is not a change worth waking anything for.

  • path: Path to classify.
fn watch_ignored(path: string) -> bool#

The paths the toolchain never wakes for: hidden paths, .moon/, zig-out/, and vendor/, which are outputs or copies of inputs.

  • path: Path to classify.
fn watch(roots: [string], on_change: fn([string]) -> void) -> Watch#

A watcher over roots with the toolchain's defaults: a WATCH_DEBOUNCE_MS window and watch_ignored paths filtered out.

  • roots: Roots to watch, each recursively.
  • on_change: Called once per group of changes with the changed paths.
fn main() {
  let mut files = std.build.watch(["src"], |paths| println("changed: " + paths.join(", ")))
  files.start()
  // Later, from an async shutdown path: `await files.stop()`.
}
fn watch_options(roots: [string], debounce_ms: int, interesting: fn(string, string) -> bool, on_change: fn([string]) -> void) -> Watch#

A watcher with an explicit window and predicate.

  • roots: Roots to watch, each recursively.
  • debounce_ms: Quiet period that closes a group; non-positive takes WATCH_DEBOUNCE_MS.
  • interesting: Called as interesting(kind, path) for each raw event.
  • on_change: Called once per group with the paths it collected.
async fn watch_grouped_changes(handle: Watcher, debounce_ms: int, interesting: fn(string, string) -> bool, on_change: fn([string]) -> void, stop: Channel<int>) -> intasync#

Reports grouped file changes from an open watcher until asked to stop: on_change fires once per group with the paths it collected, deduplicated in first-arrival order. The first interesting event opens a group, every further one pushes the deadline out by debounce_ms, and the group closes after that much quiet. Returns how many groups were delivered.

The loop owns handle and closes it on the way out. A stop request is any value sent on stop; a group still open at that point is delivered first. This is the loop watch runs; a caller holding its own Watcher (moon_fswatch does) can drive it directly.

  • handle: The open watcher events are pulled from; closed when the loop returns.
  • debounce_ms: Quiet period that closes a group; non-positive takes WATCH_DEBOUNCE_MS.
  • interesting: Called as interesting(kind, path) for each raw event; only a true opens or extends a group.
  • on_change: Called once per group with the paths it collected.
  • stop: Receiving anything here ends the loop.
async fn watch_main() -> intasync#

The --watch driver. Reads the WatchSpec JSON at the path given as the program's first argument, runs the command once, then once per group of interesting changes, printing a header line per cycle. Exits WATCH_RERESOLVE when a manifest changes, so the CLI can rebuild the watch list from the new module graph; otherwise it runs until the process is stopped.

async fn main() {
  std.process.exit(await std.build.watch_main())
}