magus v0.3.0 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
2 min read

MGS1014: cross-project output was never produced

A target declared an output into another project's tree, ran successfully, and produced nothing matching that path.

[MGS1014] target "build" declared an output into another project
("site/generated.txt") but produced no file matching it; check the path the
target actually writes

Why

Nothing connects the path you declare to the path the target writes. They are two independent strings:

ctx.writesFiles(site.file("generated.txt"));   // the declaration
fs\writeFile("../site/generated.text", body);  // the write - note the typo

The declaration is what magus caches, orders, and replays. The write is what actually happens. A mismatch between them is invisible at authoring time and, without this check, invisible at run time too.

Ordinary outputs are lenient about this: a glob matching nothing is common and usually harmless, so magus only complains when a target's entire output set matched nothing. That leniency is what let this slip through. A target declaring its own dist/** alongside a cross-project output passes the check on dist/** alone, the manifest quietly omits the foreign file, and the run reports success.

A cross-project output cannot be treated that way, because another project's build order hangs off it. The owning project is scheduled to run after this target specifically so it sees the finished bytes. If the file was never written:

  • The cache manifest omits it, so every later hit replays a partial output set into a tree this target does not own.
  • The owning project builds against a file that is missing, or worse, stale from some earlier run - and reports success.

So a cross-project output is required rather than best-effort: it must match at least one file, or the run fails before anything is cached.

Resolution

Make the declared path and the written path agree. The declared glob is relative to the owning project's root; the write is relative to the running target's working directory, which is its own project directory. For a producer writing into a sibling site, those are spelled differently for the same file:

ctx.writesFiles(site.file("generated.txt"));       // relative to site/
fs\writeFile("../site/generated.txt", body);   // relative to producer/

Check, in order:

  1. A typo in either path. This is the common case, and the two spellings differing by convention is what hides it.
  2. A conditional write. If the body only writes the file on some branch, the declaration is unconditional but the write is not. Either write it every time or move the declaration onto a target that does.
  3. A different destination than you think. Relative writes resolve against the target's project directory. Confirm with magus run <target> -vv and look at what the body actually touched.

See also

MGS1014magusfileoutputscross-projectcachesnapshot
Last updated (3da15dc9)
Earlier changes on this page (1)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Snapshot

A point-in-time view of live state - the pool's occupancy or a tick of exported metrics - as opposed to accumulated history. See daemon.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.