Make a Generated File Rebuild When Its Input Changes

Describe CMake generated-file outputs and dependencies so changed inputs trigger the intended rebuild, with separate checks for no-change and missing outputs.

Contrast OUTPUT/DEPENDS custom command with always-out-of-date custom target; connect target/file dependencies and inspect clean, unchanged and changed-input builds.

Substantively revised October 21, 2026. Author interpretation, documented cases and hypothetical examples are distinguished below.

A clean build can hide a broken dependency. Everything is absent, so the generator runs and produces the expected file. The maintainer changes an input later, rebuilds without cleaning and sees yesterday's output remain. The build succeeded at executing an available path while failing to describe when that path should be taken again.

The opposite failure is easier to notice but no more informative: the generator runs on every build, including when nothing relevant changed. Repeated work becomes the substitute for a dependency model. The useful question is which file is produced, which inputs make it stale and which requested target makes that rule reachable.

Direct answer

Model a generated file through an output-producing custom command with explicit file dependencies, then connect that output to the target that needs it. Do not use an always-out-of-date custom target's command as an accidental substitute for incremental invalidation. Examine initial generation, an unchanged rebuild, a changed-input rebuild and a missing-output rebuild separately. CMake's versioned 4.2 documentation distinguishes custom-command outputs, file dependencies and custom targets. This article uses that documentation as a bounded reference, not as a claim about the latest release. [S1] [S2] [S3]

The examples below are proposed local build exercises with synthetic text files and expected behavior. CMake was not available in the authoring environment, so no executable build experiment is reported. An adopter must run the actual project and generator before relying on the rule. A correct dependency graph also does not prove that the generating program produces semantically correct content.

Key takeaways

Generation and invalidation are different requirements. A command can know how to produce a file while the build system lacks a reason to run it after an input changes. Record both the producing operation and the inputs whose changes should matter.

Reachability is another separate requirement. A well-described output rule accomplishes little if the target being built never depends on that output. Name the consumer and inspect the path from the requested target to the generated file instead of relying on the generator's presence somewhere in a configuration file.

Compare more than clean success. An unchanged rebuild asks whether work is needlessly repeated. A changed-input rebuild asks whether the rule becomes stale. A missing-output rebuild asks whether the output can be recovered. Those observations provide different evidence and should remain distinguishable in a maintenance note.

Definitions and relationships

A generated output is a file a declared build operation is expected to create. A file dependency is an input relationship relevant to deciding when that operation needs to run again. A target is a logical part of the build graph that a user or another target can request. A custom target and an output-producing custom command serve related but different purposes.

In this article, invalidation means that the build system considers an existing output in need of regeneration under the declared relationships and actual build-tool behavior. Reachability means that the requested build includes the rule needed to supply the output. A consumer is the part of the project that requires that generated file.

The producing tool is another dependency consideration. If changing a generator implementation should change its output, the graph needs an appropriate relationship for that change as well. Merely arranging for a tool to exist before a command executes is not automatically the same as making a tool change invalidate the generated result.

The problem with clean-build evidence

A clean build begins without the file whose freshness matters. That state is useful but unusually forgiving. A missing output can cause its producing operation to run even when the graph says too little about the inputs. The maintainer sees a new file and concludes that the dependency model is complete.

The defect appears in ordinary reuse, when most files already exist. A colleague edits a source description and expects the derived text to change. If the input is absent from the rule's dependencies, the build system may have no declared reason to regenerate it. Cleaning the build hides the problem again by returning to the forgiving missing-output state.

Begin with the everyday cycle the team wants to support. The exercise should preserve a built output while changing one relevant input, then request the same consumer target. That is the state the dependency model must explain. A successful clean build cannot stand in for it, even when the clean build is slower and feels more thorough.

Root cause: ordering is mistaken for freshness

Some build relationships answer when one operation must happen relative to another. Others answer whether an existing file is stale after an input changes. The distinction can disappear in a maintainer's explanation: the generator runs first becomes an assurance that its output is current.

That assurance is too broad. A tool can be available before the output rule while changes to the tool or its input are not represented in the needed file-level relationships. Conversely, making a wrapper target run every time can force repeated commands without explaining which output depends on which input.

The remedy is to write the freshness claim with concrete file names. This output depends on these inputs, and this consumer requests that output. Then identify any separate target-ordering relationship. A useful build model makes both visible, so the next maintainer does not have to infer invalidation from a sequence of successful commands.

Framework: the Generated File Chain

The Generated File Chain is an author-proposed reasoning aid with four components: Producer, Inputs, Output and Consumer. It is not a CMake feature or a formal verification method. Its purpose is to connect an incremental build expectation to inspectable project relationships.

Producer names the operation that actually writes the intended file. Identify the executable or script, its relevant arguments and the expected location of its result. A declaration cannot be repaired if nobody has established whether the command produces the path named by the rule.

Inputs names the files whose changes should alter that result. Include the source data and other relevant file-level dependencies rather than a vague instruction to depend on the project. A broader relationship can cause unnecessary work; an incomplete one can preserve stale content. The useful set follows the actual transformation.

Output names the generated artifact the graph tracks. Distinguish it from incidental files, logs and byproducts. If the command writes somewhere other than the declared path, the build system's model and the operation disagree even though a human can find a similar file nearby.

Consumer names the requested target or dependent operation that needs the output. Trace how requesting that work brings the producing rule into the graph. A rule that is accurate but unreachable does not supply the consumer's requirement. A consumer that reads an old copied file instead of the declared output is a different mismatch again.

Apply the chain to one generated file before expanding it. Write a short sentence for each component and identify the four exercise states: initially missing, unchanged, input changed and output removed. The method is deliberately small enough to expose a missing relationship without turning one file into a general redesign of the build system.

Three documented CMake mechanisms

The add_custom_command reference describes an OUTPUT-producing rule and its DEPENDS relationships. It also distinguishes file dependencies from certain automatically supplied target-ordering relationships. The documented distinction prevents a maintainer from assuming that mentioning an executable target in an argument necessarily covers every reason its derived output should be regenerated. [S1]

The add_custom_target reference states that a custom target has no output file and is always considered out of date. It also describes using dependencies on custom-command outputs. This supports a wrapper target that requests a generated file without confusing the wrapper's own status with a requirement to run the file-producing command unconditionally. [S2]

The buildsystem manual describes CMake projects as logical targets connected by dependency relationships that determine build order and regeneration. That supplies the consumer side of the example. A standalone producer declaration needs a path into the work the user actually requests; simply appearing in a configuration file does not complete that relationship. [S3]

These are documented build mechanisms, not three measured project outcomes. The hypothetical case below supplies a specific chain and an exercise plan. It does not claim that a real team's build time improved, that every backend has identical diagnostics or that the sample was run during authoring.

Hypothetical case study: a generated project note

Imagine an invented local project that copies a synthetic input.txt into a generated.txt in the build tree. The copy is deliberately simpler than a real generator, allowing the dependency relationship to be discussed without introducing a network service, compiler toolchain or unpublished script. It is a teaching example rather than a proposed production architecture.

The intended chain is explicit. Producer is CMake's own command-line helper performing a file copy. Input is the synthetic source-tree input.txt. Output is build-tree generated.txt. Consumer is a convenience target named build_note that requests that output. The names are illustrative and should not be confused with files already present in a reader's project.

The proposed output-producing rule can be expressed as add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated.txt" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/input.txt" "${CMAKE_CURRENT_BINARY_DIR}/generated.txt" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/input.txt" VERBATIM). A corresponding requester is add_custom_target(build_note DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/generated.txt"). These are illustrative declarations within a suitable configured CMake project, not a complete top-level project or an executed experiment.

The first planned exercise starts with the output absent and requests build_note. The expected operation is creation of the declared output through its producer rule. The adopter should inspect the actual file and command evidence under the chosen CMake version and generated build backend rather than infer execution solely from a target name in a log.

The second planned exercise changes nothing and requests the same target again. The wrapper target's out-of-date status should not be confused with a requirement for the output-producing copy command to run again. Investigate the actual file rule and backend behavior. An unconditional copy command placed directly on a custom target would describe a different model.

The third planned exercise edits the synthetic input and requests the consumer again, under ordinary conditions where the build tool recognizes the input as newer. The intended file dependency should make the derived output eligible for regeneration. Inspect the resulting content as well as whether the operation ran. A command can execute successfully while writing content the consumer did not intend.

The fourth planned exercise removes only the disposable generated.txt and requests build_note. The intended graph should provide a route to create it again. Perform such an exercise only on the known synthetic output in the disposable build area, not by deleting an arbitrary project directory to imitate a clean build.

These four states answer four distinct questions. The example contains one source file, one generated output and one consumer target, but those counts do not define sufficient testing for a real generator. A production transformation may depend on templates, settings or tool binaries that need their own representation and exercise cases.

The negative variant: omit the input relationship

For a deliberately defective comparison, imagine the same producing declaration without the relevant input file in DEPENDS. The initial missing-output case can still lead to generation. That expected clean success is precisely why it does not establish that later edits to the input are modeled.

Keep the output, change the input under conditions the backend normally recognizes and request the same target. The question is whether the graph has declared the reason to regenerate. A reader should not repair the demonstration by manually invoking the producer or cleaning all outputs after each edit; those operations sidestep the missing relationship instead of describing it.

This negative variant is a proposed inspection exercise, not an observed failure from a named project. Its value is the contrast between generation and invalidation. The actual result must be examined with the chosen build backend and filesystem behavior before making a claim about a particular implementation.

A second distinction: tools and incidental files

A real generator may write a primary output and other files. Decide which are part of the declared relationship rather than rely on their appearance after one run. CMake documents BYPRODUCTS for outputs whose modification-time behavior may differ and notes backend requirements, including Ninja's need for rules for generated files it depends on. Do not generalize every backend detail into a universal recovery guarantee. [S1]

The producing tool also needs deliberate treatment. If the generator changes, should the generated file change? If so, inspect the appropriate dependency for the actual executable or script. A relationship that merely ensures a tool is available before use does not automatically express every file-level invalidation condition.

Keep this second distinction separate from the simple copy example. The example intentionally has no separate compiled generator or extra output. Adding these concepts to a real project requires another chain inspection rather than assuming a one-file demonstration has already covered the more complex transformation.

Failure example: the stamp that hides a missing artifact

A maintainer tracks a small stamp file as the apparent output of a generation step. The actual consumer reads another generated file. Later, that real artifact disappears while the stamp remains. The build can appear current if the model does not connect the consumer's needed file to a rule that supplies it.

In this hypothetical failure, touching the stamp again is not an explanation of the missing relationship. The maintainer needs to identify the real output, incidental files and the consumer's dependency. A stamp can be part of a deliberate design, but its existence cannot stand in for an artifact the graph does not properly describe.

Use the Generated File Chain to ask which file the consumer actually opens and why that file should exist at the required moment. Then inspect the backend-specific handling of missing generated files. The repair should connect the model to the required artifact rather than add another reassuring timestamp that the consumer never uses.

Counterargument and tradeoff: unconditional generation can be intentional

Some tasks legitimately need to run whenever they are requested. A convenience operation with no stable file result may be a reasonable custom target. The counterargument to incremental modeling is therefore valid: not every command should be forced into an output-file abstraction that misrepresents its purpose.

The decision depends on the task. If the purpose is to materialize a file derived from identifiable inputs, a file rule can express when it becomes stale. If the purpose is an explicitly requested action with no persistent artifact, an always-run target may be appropriate. Name the intended behavior instead of using one mechanism out of habit.

The maintenance cost is the accuracy of the dependency model. An incomplete incremental rule can be worse than an honest unconditional operation because it supplies stale output with a successful status. Do not pursue fewer commands executed at the expense of the consumer's actual correctness requirement.

Common mistakes and their repairs

Listing an output that the producer never writes creates a disagreement the build system cannot resolve by intention. Compare the declared path with the actual command's destination. Keep source-tree and build-tree locations distinct so that an old artifact elsewhere does not disguise the missing declared result.

Keeping an output rule outside the consumer's dependency path makes it accurate but irrelevant to the requested work. Trace the actual target request. A manual generator invocation demonstrates that the producer can run; it does not demonstrate that the normal build knows when to request it.

Using clean builds as the only check repeats the most forgiving state. Add the unchanged and changed-input contrasts. Inspect what the backend considered stale and what content the consumer receives instead of treating every successful build as equivalent evidence.

Assuming timestamps are content verification overstates the mechanism. The actual build tool and filesystem conditions determine freshness behavior. A dependency rule is not a cryptographic comparison or proof that the producer interpreted its inputs correctly. Use appropriate content checks for the actual transformation and describe their scope separately.

Action plan for the next generated artifact

Choose one consumer-visible file and write the four chain components. Identify the actual command that writes it and all relevant source files. Declare the output and file dependencies through an appropriate custom command, then connect the output to the consumer's requested work. Keep unrelated cleanup operations out of the experiment.

Create a disposable example or build area with known synthetic inputs. Run the initial, unchanged, changed-input and missing-output exercises under the actual selected backend. Record the CMake version, backend and observations, including whether the producer ran and whether the output content matched the expected transformation.

Investigate disagreement before broadening the design. If unchanged builds repeatedly invoke the producer, inspect whether the command lives on an always-run target or fails to create its declared output. If input changes are missed, inspect dependencies and actual filesystem conditions rather than make cleaning the permanent workaround.

Extend the graph when the real generator introduces other inputs, outputs or tools. Keep their relationships explicit and add the corresponding exercise cases. The maintained result is an understandable dependency model supported by actual observations, not a universal promise that every future incremental build will be correct.

FAQ

Does a successful clean build prove dependencies are complete?

No. The missing-output state can cause generation while leaving later input changes unmodeled. Keep the output and exercise an ordinary relevant input change. The contrast asks a question the clean case does not answer.

Should the producing command live on a custom target?

That depends on the intended operation. For the file-generation task here, CMake documents output-producing custom commands and dependencies. A target whose own command runs whenever requested describes a different behavior and can be deliberate for other tasks. [S1] [S2]

Is an empty custom target useless?

No. A convenience target can request an output through its dependencies without carrying the producing command itself. The important question is how that relationship reaches the actual file rule, not whether the wrapper has a command body.

Does target ordering imply file invalidation?

Not universally. CMake documents distinctions among certain target-level and file-level relationships. Identify the reason an existing generated output should become stale rather than assume that arranging tool availability expresses every needed rebuild condition. [S1]

Why name the consumer?

It identifies the actual work that requires the output. A producer rule that the requested target never reaches does not satisfy that requirement. Trace the graph from the ordinary target request instead of relying on a successful manual generator run.

Were the four example states executed?

No. They are proposed exercises with expected relationships, and CMake was unavailable in the authoring environment. An adopter should run them in the actual chosen implementation before relying on the illustrative declarations.

Is the shown declaration a complete CMake project?

No. It illustrates the producer and requester within a suitable configured project. Project setup, backend selection and actual execution still need their own valid environment. The article does not claim a ready-made tested build package.

What does a missing-output check establish?

It investigates whether the requested work has a route to recreate that particular disposable artifact. It does not establish every missing-byproduct case or every backend's behavior. Keep the observation attached to the file and rule actually exercised.

Can the same output have several independent producers?

That introduces a conflict risk, especially under parallel execution. The cited custom-command guidance warns against listing the output in independent targets that can build in parallel. Design a clear producing relationship instead of hope that whichever command finishes last is acceptable. [S1]

Are byproducts the same as ordinary inputs?

No. They are files produced by an operation, with documented handling distinct from its source dependencies. Identify which generated files other rules need and consult the actual backend requirements rather than hide them as incidental side effects.

Does a dependency rule verify output content?

No. It models when an operation should run under the build system. The program can still produce incorrect content. Check the transformation itself with evidence appropriate to the generated format and its consumer.

What if a generator script changes?

Decide whether that change should invalidate the output and represent the actual relevant file relationship. Do not rely solely on the fact that the script's name appears in the command. The graph needs an explicit account of what makes the output stale.

Should every build be made incremental?

No. An intentionally requested action with no stable file result can reasonably run whenever requested. Choose the model that describes the task honestly. Avoid an incomplete file rule whose apparent efficiency preserves stale content.

Why use versioned documentation?

It gives the technical discussion a bounded reference instead of depending on a moving latest page. Check the actual version and policies in the project before applying the guidance. The cited 4.2 documentation is not presented as the current newest release.

What should the maintenance record say?

Name the producer, relevant inputs, output and consumer, then record the actual exercise results with version and backend. Describe unresolved conditions. Another maintainer should be able to explain why the file is rebuilt rather than rely on an instruction to clean whenever it seems wrong.

Recommended books

The Pragmatic Programmer, 20th Anniversary Edition: your journey to mastery, by David Thomas and Andrew Hunt, offers background on making recurring knowledge explicit in tools. Its relevance is the choice to encode an input relationship rather than remember to run a command. The CMake documentation supports the operational semantics here.

John Ousterhout's A Philosophy of Software Design, second edition, supplies context for considering where complexity belongs in a maintained interface. Use it to question a build rule whose real dependency knowledge lives in a separate human instruction. It does not certify the illustrated declarations or the Generated File Chain.

Software Engineering at Google, curated by Titus Winters, Tom Manshreck and Hyrum Wright, provides broader context on maintaining engineering systems over time. It can inform the concern for future consumers of a generated artifact. It is not evidence that a particular build backend was exercised or that the hypothetical project obtained measured savings.

Further exploration

Choose one real stale-output incident that the team is authorized to investigate. Reconstruct which output existed, which input changed and which consumer was requested. Separate missing graph relationships from producer defects and filesystem conditions. The distinctions help avoid solving every failure by adding more unconditional commands.

Then inspect a generator with more than one meaningful output. Ask which files are tracked, which consumers depend on them and what happens when one is missing. Expand only with a clear producing relationship and actual backend observations. A reusable build system improves when it can explain both why work runs and why unchanged work can remain untouched.

Primary evidence sources

Related concepts: systems thinking, wealth structure, and the survival loop. Read the editorial policy and citation guide.