gnu make "leaf" prerequisites - makefile

Is it possible to learn about leaf prerequisites of a target?
In this case, the leaf prerequisites of t1 are t3 and foo.txt, while t2 is intermediary:
t1: t2 t3
cat t2 t3 > t1
t2: foo.txt
cat $< > $#
In this example, t2 is strictly an intermediate repository, and t3 already exists (it is not generate-able).
How can I obtain t3 and foo.txt, as leaf-targets of t1, from make? Is it reasonable to expect that such facility exists?
Use case: I am compiling a LaTeX document to PDF, and would like to pdfattach all the leaf prerequisites without listing them explicitly in the Makefile.

You could do
make --dry-run --debug=a t1 | grep "Considering target"
and write a convoluted script to parse the output (the last line, and any entry with a greater indent than it's subsequent line would be leaves). It sounds like you want to do access this list from within the make invocation though, but I don't think there is any built in support for this.
(you could do a recursive make call from within a recipe -- not efficient, but may work).

Inside the rules the prerequisites can be accessed via automatic variables, such as $< and $^.
However, make must know the prerequisites in order to rebuild correctly, so there must be a list of files in a make variable. That list of files can be specified explicitly or be read from a file or another command.

Related

What is the canonical way to simulate grouped targets in older versions of Make?

What is the best way to simulate Make's grouped targets feature for older versions of Make that do not have the feature?
UPDATE
It looks like a static pattern rule might work:
"Pattern rules are always treated as grouped targets ... regardless of whether they use the : or &: separator"
Grouped targets for pattern rules have been around forever (you don't need &: for that, it's the default way they work). Grouped targets with &: for non-pattern rules was introduced in GNU make 4.3 which was released in Jan 2020.
There is no GNU make 3.0. If you have the default GNU make on MacOS you have something like GNU make 3.81 or something like that. You'll never get a newer version because Apple will never add a version of GNU software that is under GPLv3 so on MacOS you're stuck with old bash, old make, etc. etc. You can use homebrew or macports to install newer versions on your own of course (or build them yourself).
Using static pattern rules won't work. The naming is unfortunate but static pattern rules are actually just a shortcut for writing explicit rules, not pattern rules, and they don't support grouped targets.
If you can't use real pattern rules (your outputs and inputs are not related through some common portion of their filenames) and you can't use GNU make 4.3, then you can use this idiom; if your rule is like this:
t1 t2 t3 ... : p1 p2 p3 ...
command that builds all of t1 t2 t3 ...
then you can change it to this:
t1 t2 t3 ... : .sentinel ;
.sentinel: p1 p2 p3 ...
command that builds all of t1 t2 t3 ...
#touch $#
Don't forget the semicolon after the .sentinel. Or if you prefer you can do something like this for that rule: it's equivalent:
t1 t2 t3 ... : .sentinel
#: do nothing
(: is the shell's "do nothing" operator) Of course you still need the second rule as well.

keep intermediate files defined by wildcards in makefile

I've defined a series of data-processing steps with a Makefile but find that the files belonging to the intermediate steps are deleted by Make. In the following example, the files processed_%.txt are always deleted.
#make some simple data
#echo "test data X" > test_x.txt
#echo "test data y" > test_y.txt
x = test_x.txt
y = test_y.txt
#these are deleted
processed_%.txt: ${x} ${y}
cat $< > $#
#these remain in the directory
processed_again_%.txt: processed_%.txt
cat $< > $#
all: processed_again_x.txt processed_again_y.txt
Can anyone explain what is happening and how to disable/control this behavior?
thanks,
zachcp
This is how chains of implicit rules work.
The second difference is that if make does create b in order to update something else, it deletes b later on after it is no longer needed. Therefore, an intermediate file which did not exist before make also does not exist after make. make reports the deletion to you by printing a ‘rm -f’ command showing which file it is deleting.
and you can control this behavior by marking the file as .SECONDARY
You can prevent automatic deletion of an intermediate file by marking it as a secondary file. To do this, list it as a prerequisite of the special target .SECONDARY. When a file is secondary, make will not create the file merely because it does not already exist, but make does not automatically delete the file. Marking a file as secondary also marks it as intermediate.

forcing order of prerequisites in Makefiles

I have a third party makefile, and I'd like one of the targets (T1) to not be built until another, custom target (T2) is built first. Normally, this would be accomplished by making T2 a prerequisite of T1. BUT, T1 uses the $^ in one of its rules.. so, by adding the prerequisite, I end up breaking the build... What I have is this:
T1: x y z T2
$(MAKE) -j $^;
# fails because T2 should not be passed to the make!!!
.PHONY: T2
T2:
#do some linking and prep for T1
Is there a good way to ensure that T2 is run before T1? (Note: the above example is actually simplified by a bit. T1 is actually the vmlinux target within the Linux kernel makefile, so rewriting it is not only difficult, it makes the code non-portable. Also, I can't run T2 before calling make on the kernel due to some other dependencies).
Have T2 as an order-only prerequisite:
T1: x y z | T2
$(MAKE) -j $^;
# Make will run the T2 rule before this one, but T2 will not appear in $^
Could you just call Make in your build script with the two targets in the proper order, e.g.
make T2 T1
That way you don't need to make any modifications to T1.

Generating list of generated sources (a la foreach) in automake

I am currently working on a project using templates quite extensively, and running into memory constraints during instantiation. I have split up the instantiation into a number of very simple files, which are all three-liners consisting of includes only.
I let these be generated by a rule in Makefile.am. Now I have a quite long list of files that should be generated in my Makefile, and would like to refactor this list with a foreach-like expression. In more specific terms: I have a line like
libfoo_la_SOURCES = a_0.cpp a_1.cpp ... b_0.cpp b_1.cpp ... c_0.cpp c_1.cpp ...
which could be more concisely expressed as
libfoo_la_SOURCES = $(foreach i,a b ...,$(foreach j,0 1 ...,$i_$j.cpp))
However, the second construct is not only warned against by automake, but also does not work: The files given in this manner are neither compiled nor cleaned.
My current workaround is generating this file list by a shell script.
Any ideas how to implement this iteration?
I would forget about making loops: the GNU extension is not standard, and not understood by Automake. One standard (and portable) make construction you can use here is the macro expansion with substitution: $(var:subst1=subst2) will expand to the value of $(var) after replacing any suffix subst1 of a word by subst2. Automake understands this.
If subst1 is empty, as in $(var:=subst2), you are appending subst2 to all files in $(var). You can use this to construct your list of files as follows:
f = a b c d e f
g = $(f:=_0) $(f:=_1) $(f:=_2) $(f:=_3)
all_files = $(g:=.cpp)
echo:
#echo $(all_files)
Running make echo with the above Makefile will display all files from a_0.cpp to f_3.cpp.
Like you, I discovered that the GNU make foreach function will not work like this because
the sources need to be there at the time the Makefile is generated. So, I use GNU Autogen (also here) to generate a makefile fragment which is subsequently included in Makefile.am. So it's probably not that different than your shell script approach.

How can I ignore command line variable assignment in a recursive build?

I'm trying to glue two build systems together. Both are recursive (rules in the makefile use make to call other makefiles to build components of the project).
I'll call them 'A' and 'B' where 'A' builds the application and 'B' builds libraries used by 'A'.
The top level makefile in A calls 'make TARGET=whatever' which means that all the recursively-invoked bits of the build inherit the value of TARGET as a read-only variable, including the build system from B, which is called as part of the recursive build.
I don't want this to happen in the build system for 'B' (which come from a different project) as the makefiles there use TARGET for their own purposes and the build fails since TARGET has the wrong value and is read-only.
I can only see two solutions to this, neither of which is palettable;
1) Rename TARGET to something else in the makefile in A that sets it and in the makefiles in A that use it, to avoid the clash with the lower levels of the build system.
2) Use the 'override' directive everywhere in the makefiles in B where the TARGET variable is set, to override its read-only status.
Anyone got any better ideas? - ideally, I want nothing to be inherited by the B's build system from A's, except those options I explicitly pass to the B build system from A.
Incidentally, I'm using GNU Make v3.80.
You could set MAKEOVERRIDES to nothing in the second-level makefile in A.
callb:
cd subdir && $(MAKE) MAKEOVERRIDES=
This passes down the normal commandline parameters like -k and -s but not commandline variable definitions.
Or you use the historical MFLAGS which is the same as MAKEFLAGS except MFLAGS doesn't contain the commandline variable definitions.
callb:
cd subdir && $(MAKE) $(MFLAGS)
Details about this two options can be read here: The GNU Make Manual
Perhaps you can use the "unexport" directive to prevent TARGET from being propagated to B's makefile?
At the point where build system A invokes build system B, do not use '${MAKE}' directly; invoke a shell script that invokes build system B (possibly after sanitizing the environment).
To achieve the behaviour where the commands are executed by 'make -n', prefix the command line in the makefile with '+' (similar to prefixing the line with '#' or '-').
It sounds like you have modified the A makefile to recursively invoke the B makefile, and thus your problem. Why not instead introduce a new toplevel makefile which recursively invokes the B makefile, and then recursively invokes the A makefile? For example, combined.mk:
all:
$(MAKE) -f Makefile.B
$(MAKE) -f Makefile.A
That way the B makefile inherits nothing from the A makefile.

Resources