MoonNinja is a MoonBit-native subset Ninja build engine for OSC2026. It focuses on the core path that the acceptance review cares about most:
real build.ninja-style text parsing
dependency-graph construction with Tarjan SCC diagnostics and cycle detection
incremental rebuild decisions from real native MTime plus content hash snapshots
deterministic lock-free dependency waves for host-provided parallel runners
native command execution through a bounded atomic-index worker pool and a real WASM-GC/JS host execution ABI
schedulable command rendering with $in and $out expansion
named variable expansion, Make-style depfile ingestion, and response-file materialization
deterministic inspectable build plans, persistent state sidecars, path safety, and command validation
reproducible examples, committed C benchmark inputs, boundary tests, CI, and self-check scripts
The project is intentionally scoped to a well-documented subset of Ninja so that the implementation remains readable, testable, and publishable as a MoonBit ecosystem package.
Current Scope
MoonNinja currently supports:
rule <name> blocks with command = ...
build <outputs>: <rule> <inputs> declarations
multiple outputs in one build edge
implicit and order-only dependencies written with | and ||
comments beginning with #
topological traversal and cycle detection
strongly connected component reporting for cyclic manifests
incremental stale-check decisions driven by MTime plus content fingerprints
native stat/streaming-hash snapshots through src/native/native_stub.c
native process execution through system
native parallel-wave execution through NativeParallelWaveExecutor, with bounded workers and deterministic failure indices
WASM-GC host import moon_ninja.execute_command and JS host import MoonNinjaHost.execute_command
portable ExpansionContext, Depfile, MaterializedPlan, BuildState, and CommandLine APIs
MoonNinja does not yet aim to be a drop-in replacement for the full Ninja specification. The repository documents this boundary explicitly and tests the supported subset end to end.
Quick Start
moon fmt --check
moon check --deny-warn
moon build --target all
moon test --deny-warn
moon run src/main
Native backend validation:
moon test --deny-warn --target native
The default demo is intentionally plan-only, so it is runnable on every
backend. Native command execution is covered by the native-only integration
test and requires a C compiler. WASM-GC command execution is not a fake local
process: the host must provide an import named moon_ninja.execute_command
whose argument is a MoonBit String and whose return value is an integer exit
code. JavaScript hosts provide the equivalent MoonNinjaHost.execute_command.
For the full backend matrix, run:
moon check --target all --deny-warn
moon build --target all --deny-warn
moon test --target all --deny-warn
rule cc
command = gcc -c $in -o $out
rule link
command = gcc $in -o $out
build util.o: cc util.c
build main.o: cc main.c | generated.h
build app: link main.o util.o
The demo entry at src/main/main.mbt parses a manifest like the one above, builds a dependency graph, evaluates which targets are stale, and prints the commands that would execute. LocalExecutor is available for native hosts; DryRunExecutor is used by the portable demo.
Fixture benchmark and boundary coverage
examples/benchmarks/medium.build.ninja
is a committed, reproducible workload. It compiles three real C inputs, tracks
a header dependency, archives two objects, and links a downstream demo. The
inputs live in examples/fixtures, so CI never depends on
files that are created implicitly by a test.
examples/benchmarks/large.build.ninja
adds ten independent compile edges, two header families, an archive, and a
final link. It is intended for stable wave-width and transitive-input
inspection rather than synthetic line counting.
The public Manifest::benchmark and Manifest::analyze APIs report edge and
node counts, dependency depth, wave width, fan-in/fan-out, leaf inputs, and
rendered commands. src/validation_test.mbt, src/graph_boundary_test.mbt,
and the parser tests cover empty outputs, duplicate declarations, unknown
rules/targets, self-cycles, multi-node cycles, disconnected cycles, order-only
inputs, CRLF text, punctuation in paths, and missing/change-sensitive
fingerprints.
The checked-in implementation and tests contain more than 2,500 lines of
effective MoonBit source and more than 4,000 tracked MoonBit/C implementation
and test/interface lines. These are evidence-based counts performed by
scripts/verify_acceptance.ps1, not generated filler; the competition guidance
also makes clear that maintainable scope and working evidence matter more than
an arbitrary line count.
For a target-level explanation, call Manifest::inspect_target. For a stable
execution artifact, call Manifest::materialize_plan, then serialize
BuildState::to_text after a successful host run. Compiler-generated depfiles
can be parsed with parse_depfile and merged into the producing edge. Commands
that use @flags.rsp can be expanded from an in-memory response-file table with
materialize_response_command; no host filesystem access is required by these
portable APIs.
Design notes
DepGraph::strongly_connected_components uses Tarjan’s algorithm and reports
the complete cyclic component. DepGraph::parallel_waves emits independent
ready sets in deterministic order. Scheduler::run_parallel_waves never
shares a mutable ready queue between waves; a native thread-pool or WASM host
can implement WaveExecutor to run each wave concurrently.
FileFingerprint stores seconds, nanoseconds, size, and a portable 64-bit
content hash. The native adapter reads real file metadata and streams the file
through the hash; equal timestamps therefore do not hide content changes.
The GitHub repo is used for CI and Mooncakes-facing metadata.
The GitLink repo is kept as the competition mirror and can remain single-contributor on that platform.
Reference projects and license boundary
MoonNinja targets a documented subset of the Ninja file model. The
interoperability references are:
Ninja, an Apache-2.0 build system;
see its license.
n2, a Ninja-compatible Apache-2.0 build
system; see its license.
No Ninja or n2 source is copied into this repository. Their syntax and public
documentation are referenced only to define compatibility scope; this project
contains original MoonBit code under its own Apache License 2.0.
Verification Checklist
moon fmt --check (run locally and in CI)
CI push trigger is explicitly bound to GitHub main
CI verifies MoonBit 0.10.7
moon check --target all --deny-warn
moon build --target all --deny-warn
moon test --target all --deny-warn
native integration test with a system C compiler
CI workflow for Linux, macOS, and Windows
License file present
README explains scope, usage, examples, package metadata, and references
MoonNinja
MoonNinja is a MoonBit-native subset Ninja build engine for OSC2026. It focuses on the core path that the acceptance review cares about most:
build.ninja-style text parsing$inand$outexpansionThe project is intentionally scoped to a well-documented subset of Ninja so that the implementation remains readable, testable, and publishable as a MoonBit ecosystem package.
Current Scope
MoonNinja currently supports:
rule <name>blocks withcommand = ...build <outputs>: <rule> <inputs>declarations|and||#stat/streaming-hash snapshots throughsrc/native/native_stub.csystemNativeParallelWaveExecutor, with bounded workers and deterministic failure indicesmoon_ninja.execute_commandand JS host importMoonNinjaHost.execute_commandExpansionContext,Depfile,MaterializedPlan,BuildState, andCommandLineAPIsMoonNinja does not yet aim to be a drop-in replacement for the full Ninja specification. The repository documents this boundary explicitly and tests the supported subset end to end.
Quick Start
Native backend validation:
The default demo is intentionally plan-only, so it is runnable on every backend. Native command execution is covered by the native-only integration test and requires a C compiler. WASM-GC command execution is not a fake local process: the host must provide an import named
moon_ninja.execute_commandwhose argument is a MoonBitStringand whose return value is an integer exit code. JavaScript hosts provide the equivalentMoonNinjaHost.execute_command.For the full backend matrix, run:
Acceptance self-check:
验收脚本默认使用当前
PATH中的 MoonBit。正式验收使用最新的 MoonBit0.10.7+bc794d341,也可以显式传入同版本工具链目录:脚本会先校验
moon.exe、moonc.exe和moonrun.exe三个工具均存在,再执行 格式化、全目标检查、构建和测试;CI 与本地验收使用同一版本。Example
Example input file: examples/sample.build.ninja
The demo entry at src/main/main.mbt parses a manifest like the one above, builds a dependency graph, evaluates which targets are stale, and prints the commands that would execute.
LocalExecutoris available for native hosts;DryRunExecutoris used by the portable demo.Fixture benchmark and boundary coverage
examples/benchmarks/medium.build.ninja is a committed, reproducible workload. It compiles three real C inputs, tracks a header dependency, archives two objects, and links a downstream demo. The inputs live in examples/fixtures, so CI never depends on files that are created implicitly by a test.
examples/benchmarks/large.build.ninja adds ten independent compile edges, two header families, an archive, and a final link. It is intended for stable wave-width and transitive-input inspection rather than synthetic line counting.
The public
Manifest::benchmarkandManifest::analyzeAPIs report edge and node counts, dependency depth, wave width, fan-in/fan-out, leaf inputs, and rendered commands.src/validation_test.mbt,src/graph_boundary_test.mbt, and the parser tests cover empty outputs, duplicate declarations, unknown rules/targets, self-cycles, multi-node cycles, disconnected cycles, order-only inputs, CRLF text, punctuation in paths, and missing/change-sensitive fingerprints.The checked-in implementation and tests contain more than 2,500 lines of effective MoonBit source and more than 4,000 tracked MoonBit/C implementation and test/interface lines. These are evidence-based counts performed by
scripts/verify_acceptance.ps1, not generated filler; the competition guidance also makes clear that maintainable scope and working evidence matter more than an arbitrary line count.For a target-level explanation, call
Manifest::inspect_target. For a stable execution artifact, callManifest::materialize_plan, then serializeBuildState::to_textafter a successful host run. Compiler-generated depfiles can be parsed withparse_depfileand merged into the producing edge. Commands that use@flags.rspcan be expanded from an in-memory response-file table withmaterialize_response_command; no host filesystem access is required by these portable APIs.Design notes
DepGraph::strongly_connected_componentsuses Tarjan’s algorithm and reports the complete cyclic component.DepGraph::parallel_wavesemits independent ready sets in deterministic order.Scheduler::run_parallel_wavesnever shares a mutable ready queue between waves; a native thread-pool or WASM host can implementWaveExecutorto run each wave concurrently.FileFingerprintstores seconds, nanoseconds, size, and a portable 64-bit content hash. The native adapter reads real file metadata and streams the file through the hash; equal timestamps therefore do not hide content changes.Repository Layout
Mooncakes Metadata
The package metadata needed for Mooncakes publication is declared in moon.mod:
Zcxssxx/moon-ninjaApache-2.0https://github.com/Zcxssxx/Zcxproject11README.mdBefore publishing, use:
Competition Notes
Reference projects and license boundary
MoonNinja targets a documented subset of the Ninja file model. The interoperability references are:
No Ninja or n2 source is copied into this repository. Their syntax and public documentation are referenced only to define compatibility scope; this project contains original MoonBit code under its own Apache License 2.0.
Verification Checklist
moon fmt --check(run locally and in CI)mainmoon check --target all --deny-warnmoon build --target all --deny-warnmoon test --target all --deny-warnLicense
Apache License 2.0. See LICENSE.