How to Override Prerequisites of a Target in Later part of Makefile? - makefile

Is there a way to override the prerequisites of a target in later part of Makefile?
A common Makefile is used by multiple users. To accommodate individual unique usage, an include directive is added at the end.
E.g. in common.make
SomeVar1 =
all: step5
step1:
recipe1
step2: step1
recipe2
step3: step2
recipe3
step4: step3
recipe4
step5: step4
recipe5
-include local.make
Whenever necessary, individual user can override variables or even recipes from common.make in his/her own local.make.
However, it seems prerequisite from common.make can't be overridden in local.make.
E.g. if a user needs to skip step3 & step4, adding the following in local.make doesn't remove step4 as prerequisite for step5 target.
step5: step2
Does GNU make have some syntax to force override prerequisites of targets defined in earlier part of Makefile?
Perhaps, something like:
.OVERRIDE: step5: step2
The only way I can think of is to define prerequisites of each rule in common.make as variables. But it will be unwieldy for large numbers of steps -- lots of variables to define and association/mapping of variable names to their steps requires separate documentations.
I would like to avoid the prerequisite-variable approach if possible. Hopefully, GNU make has some syntax that can do so better.
Thank you for your help.
HCN

I just thought of a workaround that is not quite elegant as the desired hypothetical .OVERRIDE: but it doesn't require defining variable for each prerequisite in common.make.
Using the same common.gmake example, if a user needs to skip step3 & step4, but needs to add step1b & step1c between step1 & step2, his/her local.make will look as followed.
step1b: step1
recipe1b
step1c: step1b
recipe1c
step2: step1c
step3:
:
step4:
:
In addition, GNU make will also display warning messages about step3 & step4 targets being overridden, which is helpful.
local.make:10: warning: overriding commands for target `step3'
local.make:13: warning: overriding commands for target `step4'
common.make:12: warning: ignoring old commands for target `step3'
common.make:15: warning: ignoring old commands for target `step4'
Nevertheless, since this doesn't actually override dependencies, user has to force no-op to all dependencies to be skipped. In this case, no-op is necessary for both step3 & step4 targets, where in real common.make, each skipped target may have multiple prerequisites.
This is the only gotcha I can think of. I believe it'll work even with -j option for parallel execution.
Any potential pitfall in such local.make override usage that I haven't thought of, since I can't predict all possible GNU make options users may combine with local.make override?
HCN

One way to get that override-like behavior is exploiting the pattern matching preferring "the most exact match". Making the original targets just a little ambiguous by a little pattern allows local.make to override with an exact match.
SomeVar1 =
all: step5
s%ep1:
recipe1
s%ep2: step1
recipe2
s%ep3: step2
recipe3
s%ep4: step3
recipe4
s%ep5: step4
recipe5
-include local.make
If having step5 in local.make it is more exact than s%ep5.
(I do not actually like this solution, but you do you, I just bring the tech.)

Make has ?= for overriding behavior:
-include local.make
# local.make definition of STEPX_PREREQS "overrides" below defaults
STEP1_PREREQS ?=
STEP2_PREREQS ?= step1
STEP3_PREREQS ?= step2
step1: $(STEP1_PREREQS)
recipe1
step2: $(STEP2_PREREQS)
recipe2
step3: $(STEP3_PREREQS)
recipe3

If you want you can just add:
step1b: step1
touch "$#"
step1c: step1b
touch "$#"
step2: step1c
in local.make and call like this:
$ make step2; make -t step4; make step5
touch "step1"
touch "step1b"
touch "step1c"
touch "step2"
touch step3
touch step4
touch "step5"
(here to test the makefile I replace recipeN with touch "$#" so touch with quote is my recipe and touch without is printed by GNU Make.)
I know it works because it is very linear. If it doesn't I might just capture target list with make -n or something similar and do a command line like this.
What if instead of overriding you defined: step1.make step2.make
and you include them all. If you generate them by script then it is fine: you modify stepN.make but not the global one that include everybody. And the user could then choose to override precisely which step he wants.
So Makefile will have this shape:
# WARNING feel free to modify this file but don't modify stepN.make
#
# If you have missing target double check it isn't defined
# in stepN.make directly or with pattern (%) rule
include step1.make
include step2.make
include step3.make

Related

what the following makefile means?

Here is Makefile as following, I want to ask what step1 and 2 will do seperately? thanks
DIRS = modules utils library
BUILDDIRS = $(DIRS:%=build-%) step1
all: $(BUILDDIRS)
$(BUILDDIRS):
$(MAKE) -C $(#:build-%=%) step2
This looks like a dispatching Makefile. Its job is to expose the build targets build-modules, build-utils and build-library. If it is not given any target, it builds the all target which depends on all of these. We can also think about, for instance, make build-utils being directly invoked.
Each build-<whatever> corresponds to a <whatever> dir without the build- prefix.
For instance to update the target build-utils, the action is to recursively invoke make with -C utils step2 as the arguments: change to the utils directory and look for a Makefile there, invoking its step2 target.
This Makefile has a flaw: the targets all and the build-<dir> targets are all phony, but there is no .PHONY: declaration for them. This means that make will probe the filesystem, looking for files named all, and build-modules, etc. If you create files with these names, such as by touch build-modules or touch all, the Makefile will then malfunction.

Global prerequisite in GNU make - is it possible

I have a Makefile with tons of targets and would like for a certain script to get executed first, irrespective of what target is being called. I like to call it a global prerequisite.
I do not want to create a target for the script and set it as a prerequisite for all existing targets (which, as I said aren't few). Besides, someone else could add a target in future and not add my script as a prerequisite for their target, so the global prerequisite would take care of that.
Does GNU-make provide for a means to achieve this?
Another approach:
-include dummy
.PHONY: dummy
dummy:
run-the-script
Make will always attempt to rebuild any file which the makefile attempts to include (if it is out of date or does not exist). In this case there is no such file, and the rule to build it runs the script and does nothing else.
There is a solution without modifying your existing Makefile (main difference with the answers pointed to by tripleee). Just create a makefile containing:
.PHONY: all
all:
pre-script
#$(MAKE) -f Makefile --no-print-directory $(MAKECMDGOALS) MAKE='$(MAKE) -f Makefile'
post-script
$(MAKECMDGOALS): all ;
The only drawback is that the pre- and post- scripts will always be run, even if there is nothing else to do. But they will not be run if you invoke make with one of the --dry-run options (other difference with the answers pointed to by tripleee).

How to create a generic Makefile that checks sources for any given target name?

I want to have a generic Makefile that takes any target name and for that target name, checks to see if certain sources exist and then executes some commands. So for example I want to be able to enter:
make mytarget
then make should check to see if mytarget.src1 and mytarget.src2 exist, and if so execute some commands.
I have the following makefile:
%:
$(MYCOMMANDS) $*.scr1 $*.scr2
the only problem with this is that it doesn't check to see if $.scr1 and $.scr2 exist before running $(MYCOMMANDS). This is understandable because I haven't specified any dependencies. However when I try:
%: $*.src1 $*.src2
$(MYCOMMANDS) $*.scr1 $*.scr2
it now doesn't ever run $(MYCOMMAND) and says no rule to make the specified target.
Can someone please explain why in my second code make cannot find the target? Also, how can I achieve the behavior that I want?
The correct way to write a pattern rule is to use the pattern (%) in both the target and the prerequisites:
%: %.src1 %.src2
$(MYCOMMANDS) $^
See Pattern Rules in the GNU make manual. Also see Automatic Variables. By the way, the third paragraph in the second link will explain why your second attempt, using $* in the prerequisites, cannot work.
I was able to get the behavior I want using the MAKECMDGOALS variable. So:
$(MAKECMDGOALS): $(MAKECMDGOALS).src1 $(MAKECMDGOALS).src2
$(MYCOMMANDS) $(MAKECMDGOALS).scr1 $(MAKECMDGOALS).scr2
does what I am looking for. It checks to make sure .src1 and .src2 exist. If they don't make will report an error and if they do it will run $(MYCOMMANDS).

Makefile to execute a sequence of steps

I use make to execute a series of process steps. Each step depends on the success of the previous one. Once completed a step, I touch a file with the name of the step into a separate directory.
Here is one example to explain the concept:
VPATH=steps
step1:
#echo "do some actions with $#"
#touch $(VAPTH)/$#
step2: step1
#echo "do some actions with $#"
#touch $(VPATH)/$#
step3: step2
#echo "do some actions with $#"
#touch $(VPATH)/$#
It basically works, however there is a weakness: it checks for targets either in "." and in VPATH. If you erroneously touch ./step1 in the working directory "." make gets confused. I'd like to know if I can avoid any ambiguity on checking the targets/prerequisites, but I'd like to keep using
make step3
and not
make steps/step3
Any other Makefile example to get the same objective is welcome. Thanks in advance for the help!
A fundamental rule of makefiles is that you cannot create targets that are different from what makes thinks they should be. Make puts the name of the target that it wants you to build in the $# variable. Your rule must create a target with that name, or make will not work properly. In your example you're creating a target with the name $(VPATH)/$# which is not the same as $#, so that's not right.
Another rule of makefiles is that VPATH cannot be used (correctly) to find derived targets. It can only be used to find source files.
I recommend you change the variable name from VPATH to something like STEPDIR, just to avoid confusion. Then you can write a makefile like this (note this is untested and may need to be tweaked). Look up Static Pattern Rules in the GNU make manual to understand what I'm doing in the commented part:
STEPDIR := steps
STEPS := step1 step2 step3
# Translate from local to subdirectory
.PHONY: $(STEPS)
$(STEPS): %: $(STEPDIR)/%
$(STEPDIR)/step1:
#...
#touch $#
$(STEPDIR)/step2: $(STEPDIR)/step1
#...
#touch $#
$(STEPDIR)/step1: $(STEPDIR)/step2
#...
#touch $#

Code generation and make rule expansion

Assume I have a make rule:
.PHONY:gen
gen: auto.template
generate-sources auto.template
that creates a bunch of files, for example auto1.src, auto2.src, auto3.src and so on.
If I now have rules to build targets from *.src files, like this:
$(patsubst %.src,%.target,$(wildcard *.src)): %.target: %.src
build $< > $#
How can I tell make to first execute the gen rule and then expand the preconditions for the second rule template? GNU extensions are welcome.
Note: I would like to keep it in one make invocation; A trivial solution to this would be to put the second rule in a secondary Makefile.secondrun and call $(MAKE) -f Makefile.secondrun after gen was processed. But I was wondering if there is a better option.
Building off Beta's answer, here's how you can do it using makefile remaking in GNU make, which is not the same thing as recursive make. Rather, it updates an included makefile using a rule in the main makefile, then restarts the original make instance. This is how *.d dependency files are typically generated and used.
# Get the list of auto-generated sources. If this file doesn't exist, or if it is older
# than auto.template, it will get built using the rule defined below, according to the
# standard behavior of GNU make. If autosrcs.mk is rebuilt, GNU make will automatically
# restart itself after autosrcs.mk is updated.
include autosrcs.mk
# Once we have the list of auto-generated sources, getting the list of targets to build
# from them is a simple pattern substitution.
TARGETS=$(patsubst %.src,%.target,$(AUTO_SRCS))
all: $(TARGETS)
# Rule describing how to build autosrcs.mk. This generates the sources, then computes
# the list of autogenerated sources and writes that to autosrcs.mk in the form of a
# make variable. Note that we use *shell* constructs to get the list of sources, not
# make constructs like $(wildcard), which could be expanded at the wrong time relative
# to when the source files are actually created.
autosrcs.mk: auto.template
./generate-sources auto.template
echo "AUTO_SRCS=`echo *.src`" > autosrcs.mk
# How to build *.target files from *.src files.
%.target: %.src
#echo 'build $< > $#'
Short answer: you can't. Make determines all of the rules it will have to execute before it executes any rule.
Longer answer: maybe you can. As you say, you can use recursive Make explicitly, or surreptitiously by, say, building a file which your makefile will include (I'm looking at you, Jack Kelly). Or if you could somehow obtain a list of the files which gen will build, you could write a rule around that. Or you could take a leap of faith like this:
%.target: %.src
build $< > $#
%.src: gen;

Resources