I've two targets foo and bar. Neither depend on the other, but if bar has to be rebuilt, it has to be done before foo. They are what gnu-make calls phony targets, their rules have always to be executed when they are specified.
Currently, we express a main target which depends on both like this:
# user level targets
all: bar
#$(MAKE) foo
#echo all
alt: foo
#echo alt
# internal targets
foo:
#echo foo
bar: qux
#echo bar
qux:
#echo qux
#touch qux
and we have the required behavior: if qux is not up-to-date: make bar outputs qux bar foo all (in that order) and make alt outputs foo alt; if qux is up-to-date, make bar output bar foo all and make alt outputs foo alt.
This is increasingly uncomfortable as foo has to be handled specifically (all targets which depend on both have to be handled that way, foo can't be put in a variable describing dependencies if bar is also there, the submake is itself an issue and the command line has to be maintained to pass additional variables). We now have another target which has to be handled in the same way and I'm looking for other, more convenient, ways to handle the structure.
Note 1 : In practice, I'm currently using only gnu-make but the only known dependency on a gnu-make extension over POSIX is the possibility to include files (which is quite widely available). I'd prefer something which keep the current state (i.e. widely supported constructs), but if it is not possible, the use of a gnu-make only extension is acceptable.
Note 2: gnu-make has a notion of order-only-prerequisites, but it apparently doesn't provide what we need. With
# user level targets
all: bar foo
#echo all
alt: foo
#echo alt
# internal targets
foo: | bar
#echo foo
bar:
#echo bar
make alt also build bar (if a file bar exist, its date doesn't influence the decision of rebuilding foo, which is the documented behavior).
Note 3: The more I think about it, the less I think it is possible to solve this problem with make without using a recursive call. It seems to me that it need two passes on the dependency graph, one to determine what has to be built, one to determine the ordering and I know nothing in make behavior which can't be done with a one pass algorithm.
Hmmm, how about this hack (for a hack it undoubtedly is :-)).
Basically, you could run make -d -n plus your command arguments. The output will contain several lines like Must remake target 'clean'. This information tells you whether this run of make will attempt to build both foo and bar. If this turns out to be the case, just add a rule to cause the serialisation you want.
A sketch:
this := $(lastword ${MAKEFILE_LIST})
ifndef DONTRECURSE
targets-that-will-get-remade := $(patsubst %',%,$(shell ${MAKE} -f ${this} ${MAKECMDGOALS} --debug=b -n DONTRECURSE=nosiree | grep -Po "Must remake target '\K.*'"))
endif
ifeq (bar foo,$(sort $(filter bar foo,${targets-that-will-get-remade})))
foo: bar
endif
.PHONY: foo bar
foo bar:
sleep 3
: $#
So, you run make. DONTRECURSE is not set so the $(shell …) runs. That runs make a second time with the same makefile and goals, but adds the -d (debug) and -n (don't actually run the recipes) flags. DONTRECURSE is set to prevent a third copy of make running.
The expansion of all that is a list of the targets this run of make will attempt to build on this run. (Extracting the target names is pretty tiresome—there is probably a cleaner way.)
If this list of targets includes both foo and bar, simply add a foo: bar dependency. Job done. The sleep 3 lines show this serialisation working when you use -j4 (say).
Related
Make is a beautiful and powerful tool for wide and sundry use. I love it, and this is only a detail.
Suppose I have a recursive make recipe defined as follows:
submake-cmd:
make ${SUBMAKEGOALS} -C foo
# where I have derived ${SUBMAKEGOALS} earlier
And here is the crux: I can either set the make interface up like so...
$: make submake-cmd SUBMAKEGOALS="foo bar"
-- OR --
# preferably
make submake-cmd foo bar biz baz
The only problem is that if I calculate the ${SUBMAKEGOALS}, then I am forced to write the submake-cmd rule like so in order to avoid unpredictable behavior:
submake-cmd:
make ${SUBMAKEGOALS} -C foo
exit 1
Such that make will exit, and I will see make exit with an error.
There must be some way to clear out the make targets or indicate that make has completed successfully when using make recursion...
Does anyone have any ideas?
preferably
make submake-cmd foo bar biz baz
You're probably overthinking something. If you want to pass an unknown number of targets to submake then it could be done like this:
ifneq ($(sub),)
.DEFAULT:
$(MAKE) -C $(sub) $#
endif
And then: make sub=dir1 foo; make sub=dir2 bar biz bazetc. (I assume the root makefile does not contain any of foo/bar/biz/baz of its own; if it's not so, then you have to patch these rules appropriately).
I have a couple of rules which are parametrised as follows:
.SECONDEXPANSION:
NAME=default
foo: x-$$(NAME)
bar: NAME=bar
bar: foo
baz: NAME=baz
baz: foo
x-%:
#echo building $#
I would expect the following output when running make bar:
$ make bar
building x-bar
But I see:
$ make bar
building x-default
Is there a way to delay expansion in a rules' prerequisites until after it is being invoked so I can parametrize the rules like this? I would like to avoid using define ... endef etc. because my rules are quite complicated and having another level of $$ in there would really hurt readability.
This was a bug in make. Fixed in make 4.4. It yields the output you expected:
# make bar
building x-bar
.PHONY: foo bar baz
all: foo bar baz foo bar
# #=$#
# ?=$?
# <=$<
# ^=$^
# +=$+
Here is the output of this:
# #=all
# ?=foo bar baz
# <=foo
# ^=foo bar baz
# +=foo bar baz foo bar
If if comment first line as:
#.PHONY: foo bar baz
Then output is:
make: *** No rule to make target `foo', needed by `all'. Stop.
i have two question:
1) Why make does not complain for a rule in first case when "foo bar and baz" are declared PHONY.
2) I started commands with a pound(#). Why these commands are not treated as a comment.
Etan has the right info in his comments. Just to be a little more verbose:
1) Declaring a target PHONY creates it as a target inside make. So thereafter, if you list it as a prerequisite, make knows about it just as if you'd written foo: as a target. It could be argued this is a bug in make; I'm not sure, but that's how it works.
2) The important detail from Etan's answer is that if there's a line in your makefile [1] that starts with a TAB, make will send it to the shell. Make doesn't try to interpret the line, even to see if it's a comment or not (other than expanding variables/functions of course). Whatever you write, is sent to the shell.
[1] "in a target context", which is hard to describe concretely... best bet is to never use TAB unless you're trying to write a recipe line.
I have the Makefile below,
include settings.mk
include main.mk
where settings.mk has the following content,
FOO=foo
BAR=bar
and main.mk is as follows:
THIS_MAKEFILE:=$(lastword $(MAKEFILE_LIST))
.PHONY: all
all:
$(MAKE) -f $(THIS_MAKEFILE) display
.PHONY: display
display:
#echo "FOO=$(FOO)"
#echo "BAR=$(BAR)"
The problem is that make all results in the following output
FOO=
BAR=
instead of
FOO=foo
BAR=bar
How to have the variables FOO and BAR available in main.mk?
When you execute just make -f main.mk (in the all target), the values are not set because you've just reread main.mk without pre-reading settings.mk.
In the Makefile, the variables are set. If you add a rule such as:
check:
#echo "FOO=$(FOO); BAR=$(BAR)"
to the Makefile, and then run make check, you'll see that FOO and BAR are indeed set.
So, the question becomes: why on earth are you doing what you are doing — and why are you expecting just make -f main.mk to know about stuff set in a makefile that the second invocation of make hasn't read? I think this is probably an XY Problem.
You could add include settings.mk to main.mk; that might make sense (but then the main Makefile would only need to contain include main.mk, leaving open the question of why you have both).
They are available in main.mk. They aren't available in the sub-make that you are spawning because you haven't exported them.
Use $(info FOO:$(FOO))/etc. in main.mk and you'll see them print out correctly.
SUBDIRS = foo bar baz
.PHONY: dirs $(SUBDIRS)
dirs: $(SUBDIRS)
$(SUBDIRS):
#echo $#
#ls $#
Anybody can just help me out to understand this make-file?
If possible explain me each statement (why do we need it?, what is the purpose? etc.)
And how exactly this make-file works?
It have wrong formatting, so i can only guess what it was before... Well, so it is:
first line assignes list "foo bar baz" to variable named SUBDIRS
second line is special command that makes all specified targets 'phonetical' - you can invoke "make dirs" or "make foo", and it will find target with that name and execute it, but it's no actual file with this name (like usual non-phony targets)
third one - creates target named 'dirs' which depends on value of SUBDIRS variable. space-separated list. this target have no real actions
fourth line creates rules for SUBRIDS variable contents, with no dependencies. The rest of text is actions that have to be performed to 'make' this target (so, in your case - if you just call "make", it will call "make dirs" (because it's the first target), which depends on foo, bar and baz - so these targets will be invoked. to perform each of these targets, make will call echo and ls - so eventually you'll get these three directory names and list of their files)