Ponkadoodle
Ponkadoodle

Reputation: 5957

GNU Make does not include all auto-generated includes before executing dependent rule

My makefile makes use of auto-generated dependencies. To do this, I have in my top-level makefile something similar to:

# Makefile
include target1.deps
include target2.deps

all: target2.deps
    cat $^

target2.deps: target1.deps
target1.deps:
    echo "target2.deps:" > $@
    echo "  touch target2.deps" >> $@

Initially, target1.deps and target2.deps do not exist. When make is first instantiated, it parses the entire Makefile and searches for a way to generate these include files. After building them, it reinvokes itself, causing the Makefile to be reparsed and the include files to be included this time. At least, that's my understanding.

The issue is that when I run the above Makefile, Make first builds target1.deps, then executes the body of the all rule, never having built or included target2.deps. This causes cat to error: cat: target2.deps: No such file or directory

This seems like a contradiction to me. I explicitly tell Make that all depends on target2.deps, but it attempts to execute the rule before satisfying its prerequisites!

The intended behavior is that target1.deps should be built and included, then target2.deps should be built and included using the rule contained within target1.deps, and then all should be run. How do I achieve this?


Context: Since this is weirdly abstract, here's my goal: I have a target index.html, which gets generated from a template index.html.in, but I don't know anything about its dependencies. I need to find out (a) which files I need to create before building index.html and (b) which files index.html will depend on at runtime.

For example index.html includes some inline css that's pulled out of global.css - I need to therefore build global.css before building index.html. On the other hand, index.html links to about.html, so after I build index.html I want to also build about.html. I call the former "build dependencies" and the latter "runtime dependencies". So my makefile looks something like this:

include index.html.build_deps
include index.html.runtime_deps

all: index.html $(runtime_deps_index.html)

%.build_deps: %.in
    ./extract_build_deps %< -o %@
%.runtime_deps: %
    ./extract_runtime_deps %< -o %@

%: %.in
    ./compile_template %< -o $@

What I want to happen is for Make to follow these steps:

  1. Build index.html.build_deps
  2. Include index.html.build_deps
  3. Build global.css (now a known prerequisite of index.html)
  4. Build index.html
  5. Build index.html.runtime_deps
  6. Include index.html.runtime_deps
  7. Build about.html (contained inside $(runtime_deps_index.html) included from index.html.runtime_deps)
  8. Target all is reached

What actually happens:

  1. Make sees that index.html.build_deps can be directly build from index.html.in; does so.
  2. Make sees that index.html.runtime_deps can be built from index.html, can be built from index.html.in.
  3. Make builds index.html. It errors because global.css hasn't yet been built.

If Make had included index.html.build_deps after building that, then it would be aware of the global.css dependency. But because it tries to build all include files before expanding any of them, it's unaware of the dependency. I want to add a dependency "index.html.runtime_deps depends on index.html.build_deps having been included, but I'm not sure how to specify such a dependency.

Upvotes: 1

Views: 667

Answers (1)

MadScientist
MadScientist

Reputation: 100781

@Dario is correct. To be a bit more specific, these are the steps make will follow here:

  1. Read the makefile.
  2. Try to build target1.deps.
  3. Find a target target1.deps and execute the recipe.
  4. The recipe succeeds, but make observes that the file target1.deps still does not exist, so make doesn't mark the target as updated.
  5. Try to build target2.deps.
  6. There's a target for it that depends on target1.deps, which make already built, but there's no recipe for it so make doesn't mark target2.deps as updated (since it was never updated, as far as make can tell--it didn't run any recipe to update it).
  7. So, make decides none of the included makefiles were actually updated and it won't re-exec.
  8. Then make wants to build all; it sees that all depends on target2.deps but make already considered that target and decided it didn't need to be rebuilt, so now make is done with all its work.

You can run make -d and follow along with the decisions make takes.

Upvotes: 3

Related Questions