MGS1034: a target reads a file it declares as its own output
One target names the same path in both ctx.readsFiles and ctx.writesFiles:
[fail] source-is-also-output: 3 path(s) declared as both a source and an output of one
target; the cache restores the file before the target reads it, so an edit to it can
neither miss the cache nor be seen
magus: buzz-test declares "assets/buzz-coverage.svg" as both a source and an output
magus: coverage-badge declares "assets/coverage.svg" as both a source and an output
Why it cannot work
An output is a file magus owns end to end. On a cache hit it is restored wholesale from the snapshot, which is what makes a hit equivalent to a run.
Declaring it as an input too puts that same file in the cache key. The two facts cannot both hold: the bytes the key is computed from are the bytes the cache itself wrote a moment earlier. Edit the file by hand and the next run still hits, because by the time anything hashes it, the restored content is back. The target never sees your edit, and neither do you.
It is worst in the case that most invites it: a target that GENERATES a file and then compares what it generated against what is on disk, to fail when the committed copy is stale. Declaring the file it verifies looks like honesty about the footprint. What it actually builds is a check that compares the cache against itself and passes whatever the tree holds.
Resolve it
Drop it from ctx.readsFiles. A derived file is a function of the real inputs, so
keying on those is what regenerates it; keying on the derived file buys nothing.
// before: the badge is on both sides
ctx.readsFiles("badge.buzz", "coverage.buzz", "assets/coverage.svg");
ctx.writesFiles("assets/coverage.svg");
// after: an output, and only an output
ctx.readsFiles("badge.buzz", "coverage.buzz");
ctx.writesFiles("assets/coverage.svg");
The comparison itself needs no declaration. Reading a file in the target body works whether or not it is in the footprint; the footprint decides what keys the cache, not what the body may open.
When the file is not really yours
Sometimes the overlap is a signal that ctx.writesFiles was the wrong declaration
rather than ctx.readsFiles being the wrong one. Ask who owns the file:
- magus produces it end to end, every time, from the inputs. It is an output.
Declare it with
ctx.writesFilesand do not read it. - The file is committed, several machines each refresh a part of it, and no single run
here can rebuild the whole. It is an update. Declare it with
ctx.modifiesExistingFiles, which is deliberately absent from the replay set, so a snapshot taken on one machine never replays over another's contribution andmagus cleannever deletes what no run here can produce. It is then free to stay an input, which is what makes a teammate's commit invalidate this target.
A per-platform coverage record is the second kind. A rendered badge is the first.
What this check cannot see
Only DECLARED refs. A target that writes a file without ctx.writesFiles is invisible
here, and so is a footprint call the static walk cannot reach
(MGS1004). It under-reports rather than over-reports.