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

MGS1012: cross-project output forms a dependency cycle

A target declared a cross-project input on a project and a cross-project output into that same project. The two imply opposite build orders, so there is no order that satisfies both.

[MGS1012] renderer: ctx.writesFiles writes "out.html" into "site", so site must run
after renderer, but renderer already depends on site; a target cannot both read
from and write into the same project

Why

The two cross-project declarations are not symmetric, and that is the whole point of having both:

Declaration Meaning Edge
ctx.readsFiles(site.file("src.md")) I read a file site owns renderer depends on site
ctx.writesFiles(site.file("out.html")) I produce a file site owns site depends on renderer

An input says build me after them, so I read finished bytes. An output says build them after me, so they see finished bytes. Declare both against one project and each is waiting on the other.

This is not an exotic mistake. It is the most natural thing to reach for: read a project's sources, render them, write the result back next to them. The shape is reasonable; it is the project boundary that is drawn in the wrong place.

Why it fails here and not later

The dependency graph would catch the cycle eventually, but far too late to be useful:

  • The error named neither project nor file - just graph: dependency cycle.
  • It took down magus graph and the entire affected pipeline for every project in the workspace, not the two involved.
  • It was scope-dependent. A run selecting only the writer never builds the full graph, so magus run build renderer reported success on a workspace that magus graph deps refused to load at all.

Failing at load makes it deterministic and attributable: the same workspace fails the same way for every command, and the message names both halves.

Resolution

Pick whichever is true of your target.

It is a consumer. If it only reads site and the output was a mistake, drop the cross-project output and write into its own tree:

ctx.readsFiles(site.file("src.md"));
ctx.writesFiles("dist/out.html");

It is a producer. If it genuinely generates part of site, drop the cross-project input. A writer does not usually need to declare an input on the project it writes into - and if it reads a different project's sources, declare the input there instead.

Both are true. Then the read and the write are two different concerns sharing one project by accident. Split the generated file into a third project that neither reads from, and have site declare a cross-project input on it. That is the shape the ordering model is built for, and it stays acyclic.

See also

MGS1012magusfileoutputsinputscross-projectcycleordering
Last updated (3da15dc9)
Earlier changes on this page (1)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

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.

Buzz

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

Affected

The set of projects touched by a change; magus affected <target> runs a target only over them. See affected.

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.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.