GNU Make: obtain list of primary prerequisites of a rule - makefile

Consider the following Makefile:
.SUFFIXES:
.SUFFIXES: .c.o
.PHONY: all
all: foo.o
foo.o: foo.h bar.h xyzzy.h
%.o: %.c
#printf "prerequisites of %s are %s\n" $# "$^"
All the files exist except for foo.o, and the output is:
prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h
Correctly, the automatic variable $^ gives us all the prerequisites, including the ones obtained from dependencies stated in other rules.
Let us call the prerequisites given in the rule itself primary prerequisites, and the prerequisites that come from other dependencies secondary prerequisites.
Above, the primary prerequisites are:
foo.c
and the secondary ones are:
foo.h bar.h xyzzy.h
The category is important because the primary prerequisites are the objects that the rule actually works with, which are needed to build the program. The secondary prerequisites are only involved in the correct triggering of incremental builds, not in a full build. A full build from scratch will work even if we remove the dependency line:
foo.o: foo.h bar.h xyzzy.h
This is reflected in our Makefile structure. We usually don't write Makefiles with rules like these:
foo.o: foo.c foo.h bar.h xyzzy.h
# commands
the additional prerequisites after foo.c are factored out elsewhere, often into a completely separate dependency makefile that is generated by tools, and that can be deleted completely, without affecting the ability to do a complete build from scratch.
The question is: how can we obtain just the list of the primary prerequisites, not including the secondary prerequisites?
This should be doable in a generic way, without any hard coding. For instance, if I have some recipe lines defined as a macro, they can be re-used in multiple rules.
define RULE_BODY
#printf "the primary prerequisites of target %s are %s\n" $# [what goes here?]
endef
%.o: %.c
$(call RULE_BODY)
I don't want to pass arguments to RULE_BODY for this it should "just know", the same way it knows the target and the total prerequisites.
Note that the use of a pattern rule is a red herring: we can replace %.o: %.c by foo.o: foo.c.

A possible solution is to add an an intermediate dependency node, which captures the secondary prerequisites, and represents them as a single prerequisite. The phony prerequisite has a certain recognizable lexical form based on which it can be filtered out:
Proof of concept, closely based on Makefile in the question:
.SUFFIXES:
.SUFFIXES: .c.o
all: foo.o
secondary_foo.o: foo.h bar.h xyzzy.h
echo $^ > $#
foo.o: secondary_foo.o
define RULE_BODY
#printf "prerequisites of %s are %s\n" $# "$^"
#printf "primary prerequisites of %s are %s\n" $# "$(filter-out secondary_$#,$^)"
#printf "secondary prerequisites of %s are %s\n" $# "$(shell cat secondary_$#)"
endef
%.o: %.c
$(call RULE_BODY)
touch $#
Output:
prerequisites of foo.o are foo.c secondary_foo.o
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h
touch foo.o
Unfortunately, the build directory is littered with these intermediate files. Even if the propagation of the secondary prerequisites is handled in some other way, the secondary_foo.o file still cannot be a phony target; at the very least it must be an empty time stamp file.
The following alternative solution is more complicated, requiring computed variables, eval, and the use of a trick to store dependencies in variables, which are used to generate rules. However, it has the virtue that it doesn't generate a proliferation of timestamp files.
.SUFFIXES:
.SUFFIXES: .c.o
OBJS := foo.o bar.o
all: $(OBJS)
# These variables give secondary dependencies for the objectg files,
# in place of rules. These would typeically be "farmed out" to
# a machine-generated dependency makefile which is included:
DEP_foo.o := foo.h bar.h xyzzy.h
DEP_bar.o := bar.h xyzzy.h
define RULE_BODY
#printf "\n"
#printf "prerequisites of %s are %s\n" $# "$^"
#printf "primary prerequisites of %s are %s\n" $# "$(filter-out $(DEP_$#),$^)"
#printf "secondary prerequisites of %s are %s\n" $# "$(DEP_$#)"
endef
%.o: %.c
$(call RULE_BODY)
# Now the trickery: generate the dependency rules from OBJS and DEP_ vars:
# $(NL) provides newline, so we can insert newline into eval expansions
define NL
endef
# For each object <obj>, generate the rule <obj>: $(DEP_<obj>)
$(eval $(foreach obj,$(OBJS),$(obj): $(DEP_$(obj))$(NL)))
Output:
prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h
prerequisites of bar.o are bar.c bar.h xyzzy.h
primary prerequisites of bar.o are bar.c
secondary prerequisites of bar.o are bar.h xyzzy.h
The disadvantage is that any additional dependencies must be inserted into the variables rather than asserted via an ordinary rule. For instance, suppose we want to recompile all the $(OBJS) if the config.make makefile is touched. We cannot just do this:
$(OBJS): config.make # Oops, config.make is now considered primary
Instead, we stick to the DEP_ variable scheme and do it like this:
$(eval $(foreach obj,$(OBJS),DEP_$(obj) += config.make$(NL)))
In other words, loop over $(OBJS), and generate a += variable assignment for each DEP_ variable which adds config.make, followed by a newline, and eval the whole thing as if it were Makefile text.
When the above eval is inserted into to our Makefile (in front of the existing eval, not after) the output shows that config.make has been added to foo.o and bar.o as a secondary prerequisite:
prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h config.make
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h config.make
prerequisites of bar.o are bar.c bar.h xyzzy.h config.make
primary prerequisites of bar.o are bar.c
secondary prerequisites of bar.o are bar.h xyzzy.h config.make
This is a workable solution which avoids temporary files, but is more challenging to understand for Makefile maintainers.
Also note that since GNU Make allows periods and slashes in variable names, something like the following is not a problem:
DEP_libs/parser/scan.o := config.h libs/parser/parser.h ...
In a rule, where libs/parser/scan.o is the $# target, $(DEP_$#) nicely gives us config.h libs/parser/parser.h ....
Lastly, note that instead of the eval lines, the dependency generator could just generate the code and stick it into the dependency makefile. That is to say, generate the file along these lines:
DEP_foo.o := foo.h bar.h xyzzy.h config.make # config.make tacked on
foo.o: $(DEP_foo.o) # also generated
DEP_bar.o := ... # and so forth
bar.o: $(DEP_bar.o)

Related

why do echo work in %: foo.o rule but not in %: %.o?

This is my current makefile
.PHONY = all clean
all: foo
#echo "Dependencies: $<"
%: %.o
#echo "Checking.. $#, <- $<"
gcc -lm foo.o -o foo
#echo "\n"
%.o: %.c
#echo "Creating object.. $#, <- $<"
gcc -c foo.c
#echo "\n"
clean:
#echo "Cleaning up..."
rm -rvf foo.o foo
When I run make, it doesn't print out any echoed strings, but I still get the executable file. This is all the things that get printed to the terminal
gcc foo.c -o foo
echo "Dependencies: foo"
When I replace %: %.0 rule with %: foo.o, everything is printed to the terminal normally
Creating object.. foo.o, <- foo.c
gcc -c foo.c
Checking.. foo, <- foo.o
gcc -lm foo.o -o foo
Dependencies: foo
rm foo.o
In both cases, I still get the executable file foo and it works normally, but why do I get 2 different results in the terminal?
When I run make, it doesn't print out any echoed strings, but I still get the executable file.
Since you do not have an explicit rule for building foo, (GNU) make performs an implicit rule search, by which it attempts to find a chain of one or more pattern rules, whether user-provided or built-in, by which it can build foo. Although it could choose to apply your rule to build foo.o from foo.c and then your rule to build foo from foo.o, it has a shorter chain available: a built-in rule for building foo directly from foo.c. It chooses the latter because it's shorter.
When I replace %: %.0 rule with %: foo.o, everything is printed to the terminal normally
This is a bit of a quirk of the implicit rule search procedure. When you make that change, make identifies the revised rule as "applicable" for building foo on account of the only prerequisite having an explicit name (this is item 5.3 in the algorithm described in the manual). The built-in rule for building directly from %.c is also applicable, but the one given in the makefile has precedence (among rule chains of the same length). The fact that make has to figure out separately how to make foo.o doesn't enter into consideration in this case (this is the quirky part, but follows clearly from the docs).
Of course, this particular quirk is rarely triggered, because a rule of the form of your revised one is almost always inappropriate. It says that whatever target is to be built, it can be built from foo.o, via the provided recipe, but that recipe really works only for building foo. Instead of %: foo.o, then, you really ought to make it foo: foo.o:
.PHONY = all clean
all: foo
#echo "Dependencies: $<"
foo: foo.o
#echo "Checking.. $#, <- $<"
gcc -o $# $< -lm
#echo "\n"
%.o: %.c
#echo "Creating object.. $#, <- $<"
gcc -c foo.c
#echo "\n"
clean:
#echo "Cleaning up..."
rm -rvf foo.o foo
Additional notes:
link library options such as -lm should come at the end of the link line. The relative order of these and object files on the command line matters.
Avoid repeating yourself. Rules' recipes should use automatic variables where possible to avoid repeating the target or prerequisite names.

make pattern to automatically generate dependencies

I want to allow pattern rules to generate the correct header dependencies for me. I've attempted do this by, but it's failing
For example, if I have files foo.cpp, foo.h, bar.cpp, bar.h and foo.h includes bar.h. with Makefile:
foo_H=foo.h $(bar_H)
bar_H=bar.h
%.o: %.cpp $(%_H)
$(CPP) $(CPPFLAGS) $< -o $#
but make will not update when foo.h or bar.h is changed.
why does this fail and how can it be fixed?
If you really want to do that you can use secondary expansion for it:
foo_H = foo.h $(bar_H)
bar_H = bar.h
.SECONDEXPANSION:
%.o: %.cpp $$($$*_H)
$(CPP) $(CPPFLAGS) $< -o $#
That's not the way gnu make works.
the variable bar_H is undefined when assigning foo_H. So foo_H will just have the value foo.h.
the pattern expansion will not work inside $(...). It just will look up the variable %_H which does not exists, i.e. is empty.
%< and %# is wrong. You probably intended to write $< and $#.
You makefile needs at least one non target. A patterned rule is not sufficient.
A patterned rule will not be used unless all dependencies exist. This might not be intended here.
Your patterned rule will not apply because it searches for a foo.c rather than foo.cpp.
The intended behavior is achieved by
foo.o : foo.h bar.h
bar.o : bar.h
%.o: %.cpp
$(CPP) $(CPPFLAGS) $< -o $#
Note that the dependencies are specified separately from the executable commands.
Another note: if you want to get rid of the include dependency hell you might want to have a look at cmake.
$(%_H) could not expand because as stated in
https://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html
Note that expansion using ‘%’ in pattern rules occurs after any variable or function expansions, which take place when the makefile is read
so it seems like using patterns to achieve this kind of logic is a dead end
As an alternative, I used foreach and include as follows:
makedep.mk
$(CUR_OBJ): $(CUR_OBJ:.o=.cpp) $($(CUR_OBJ:.o=_H))
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $#
Makefile
foo_H=foo.h $(bar_H)
bar_H=bar.h
SRCS=foo.cpp bar.cpp
OBJS=$(SRCS:.cpp=.o)
$(foreach obj,$(OBJS),$(eval CUR_OBJ:=$(obj)) $(eval include makedep.mk))
since there are only make variables and no pattern matching % everything can expand properly

Rule with empty recipe and empty prequisite and pattern rule

This is a snippet of the make file:
main.o :
%.o: main.c strlen.h main.h common.h
#echo $^
My question is that since main.o has an empty prerequisite and recipe then why the pattern rule is listing the dependency for main.o? As output shows like :
main.o : main.c strlen.h main.h common.h
Please explain any body help is appreciated!!!!!!!
A rule without a recipe simply adds any provided prerequisites to the given target. In other words, there can be only one rule with a recipe for a given target, but there can be as many rules without recipes for the same target as you like.
This:
foo.o: foo.h
foo.o: bar.h
foo.o:
foo.o: biz.h
foo.o: foo.c
$(CC) -c -o $# $<
is identical, from the point of view of make, to this:
foo.o: foo.c foo.h bar.h biz.h
$(CC) -c -o $# $<

Why does patsubst stop working when using secondary expansion of $$*?

Here is an example from the GNU Make manual section on Secondary Expansion (slightly simplified):
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$# expands to the target ("foo" in this case)
foo: $$(patsubst %.c,%.o,$$($$#_SRCS))
This works great; it builds bar.o and baz.o:
cc -c -o bar.o bar.c
cc -c -o baz.o baz.c
But if I tweak this example only slightly, the patsubst stops working:
all: foo.a
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $# $^
It is no longer building bar.o and baz.o, and instead is using the *.c files directly as prerequisites!
ar rcs foo.a bar.c baz.c
Please note that the $$($$*_SRCS) part is clearly working, as evidenced by the fact that it found foo_SRCS and used that as the prerequisites. But for some reason the patsubst part has become a no-op! Instead of replacing %.c with %.o, it is just using foo_SRCS directly.
What is going on here? How can I get my example to work?
EDIT: I had a theory that the % characters inside the patsubst were getting evaluated early, using the stem match (foo), so that the patsubst itself was looking something like this:
$(patsubst foo.c,foo.o,bar.c baz.c)
To test this theory, I added a file called foo.c to foo_SRCS:
all: foo.a
foo_SRCS := foo.c bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $# $^
That resulted in something even weirder:
make: *** No rule to make target `foo.a', needed by `all'. Stop.
The percent characters are being read by make as matches to the wildcard in the stem and are being replaced with the stem match. If you check the make -p output for your example you'll see that the parsed target line looks like this:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
Which, as far as make is concerned, is just a really odd set of patterned targets (or something like that).
If you escape the percent characters from make parsing in a similar way to how you escape the $ from make evaluation you can get what you want to work:
pc := %
$$(patsubst $$(pc).c,$$(pc).o,$$($$*_SRCS))
For added information substitution references (i.e. $(foo_SRCS:.c=.o)) can be used for transformations like this in place of the longer call to patsubst. In this case however, while it works in this scenario with a similar escaping of : (via c := :) it doesn't seem to function as the sole prerequisite of the target (with make giving a Makefile:23: *** commands commence before first target. Stop. error that I don't quite understand) at least with GNU Make 3.81.
You're mixing three features that don't go well together: secondary expansion, pattern rules, and patsubst. I'll try to explain in detail what make is doing when evaluating your code (AFAIUI).
Let's start with your first Makefile:
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $# $^
Read phase. All dollar signs are escaped, so no evaluation happens here. Make enters the following rule in its database:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
Pattern substitution. As far as make is concerned, this is just another pattern rule, with target %.a and two prerequisites separated by whitespace: $(patsubst and %.c,%.o,$($*_SRCS)).
foo.a matches the target pattern, and so the first % in each prerequisite will be replaced by foo. The rule becomes:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
Target update phase. As you requested secondary expansion, the pattern is evaluated again in the target update phase:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,bar.c baz.c)
==> foo.a: bar.c baz.c
And so make ends up executing the command
ar rcs foo.a bar.c baz.c
And what about foo.c? If you add foo.c to foo_SRCS, the secondary expansion looks like this:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,foo.c bar.c baz.c)
==> foo.a: %.o bar.c baz.c
And the rule fails because make doesn't know how to build %.o.
Work-around. You can escape the % characters with a backslash:
.SECONDEXPANSION:
%.a: $(patsubst \%.c,\%.o,$$($$*_SRCS))
ar rcs $# $^

Running Makefile targets

I am trying to 'fire' off the compilation by making all dependencies in a list of items, which are themselves targets.
From the answer (last, posted by Carl..) given in seems to suggest that something like this is possible.
Wildcard targets in a Makefile
all: $(OBJECTS)
OBJECTS = foo.o bar.o
bar.o: bar.c
#echo make $#
foo.o: foo.c
#echo make $#
.PHONY: all
My question is, when I run make I get the following, I cannot seem to get it to compile.
make: Nothing to be done for `all'.
Reverse the order of the first two lines, like so:
OBJECTS = foo.o bar.o
all: $(OBJECTS)
In your example, when Make gets to the all rule, OBJECTS has not yet been defined, so it resolves to this:
all:
Make sees a rule with no commands and no prerequisites-- nothing to be done.
You can do something like
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
This means:
To make a .o file, we need a .c file with the same name ( represented by %). The command to make the .o file is the name of the C compiler $(CC), followed by any compiler flags $(CFLAGS), then -c, etc. $< is the name of the first prerequisite ($^ is the names of all prerequisites, if you want that), and $# is the name of the target.

Resources