Interaction of include commands and target specific variables in make - makefile

Here is my makefile, essentially trying to work around the fact that target-specific variables aren't available in prereqs:
horse: shoe
$(info Making horse)
clean:
#rm -f horse nail shoe shoe.d
# Target-specific variable
shoe: DEPS := nail
shoe: shoe.d
$(info Making shoe)
#touch shoe
shoe.d:
$(info Making shoe.d)
#echo shoe: $(DEPS) > shoe.d
nail:
$(info Making nail)
#touch nail
-include shoe.d
If I run this:
$ make
Making shoe.d
Making shoe
Making horse
It forgot the horseshoe nail! Turns out that shoe.d was created as if $DEPS wasn't set when the rule was run
$ cat shoe.d
shoe:
I found two different tweaks that make things work (while destroying the point) that prove I really don't understand what's going on.
1) If I edit the Makefile to remove the last (include) line it still forgets the nail, of course, but now shoe.d is created correctly:
$ make clean
$ make
Making shoe.d
Making shoe
Making horse
$ cat shoe.d
shoe: nail
Putting the include line back and relying on the contents of shoe.d from the last invocation everything then works as expected:
$ make
Making nail
Making shoe
Making horse
Similarly, if I replace the include line with the rule written out explicitly, things always work as expected.
2) If I remove the target-specific nature of the DEPS variable, ie just write
DEPS := nail
then things work even with the include in place.
Can someone explain what's going on here?

shoe.d is being created as a result of make remaking the makefiles before making the targets, not as a result of considering the dependencies of shoe, so the recipes for shoe.d during this preliminary invocation don't inherit shoe's target-specific variables (i.e. it's as if you invoked make shoe.d).
Make won't know that shoe.d is a makefile in the case if 1) and won't try to remake it before considering the targets, so it's created as a result of the normal dependency flow and inherits DEPS from shoe, for 2) on the other hand DEPS' value is set for the entire makefile.

Related

How can I add a directory to the search path of GNU Make?

I have a makefile that looks something like this:
include anotherFile.mk
all:
someStuff
The file anotherFile.mk is like this:
include yetAnotherFile.mk
export SOME_VAR = 93
The problem is that anotherFile.mk and yetAnotherFile.mk are in a different directory from my Makefile. So my makefile can't just be changed to this:
include $(OTHER_PROJECT_PATH)/anotherFile.mk
all:
someStuff
The problem with this approach is that the include statement in anotherFile.mk will fail because it will be searching in the current directory.
A partial solution that I found is to pass the --include-dir=$OTHER_PROJECT_PATH flag to the invocation of make, but that's a bit user-unfriendly.
So my question is: Is there something I can put inside my makefile that will add to the directories that make searches for when executing an include? Something like MAKE_INCLUDE_DIRS += $(OTHER_PROJECT_PATH)
Surprisingly there doesn't seem to be a good answer to that question. Forcing .INCLUDE_DIR doesn't help and there doesn't seem to be any way around invoking make with --include-dir=$OTHER_PROJECT_PATH.
It is however possible to put the appropriate recursive make invocation inside the makefile but, in order to get it to work for all reasonable cases it quickly becomes too complicated to be worth it. In summary it requires:
a top level condition to check if the OTHER_PROJECT_PATH is in .INCLUDE_DIR
the appropriate target with the recipe invoking make recursively
possibly additional targets if there are multiple command goals
the real make file enclosed in the else part of the conditional
You Makefile would look like this:
OTHER_PROJECT_PATH := other
ifeq (,$(filter $(OTHER_PROJECT_PATH), $(.INCLUDE_DIRS)))
# this is the mechanism to add the include dir in a recursive make
$(or $(firstword $(MAKECMDGOALS)),all):
$(MAKE) -I$(OTHER_PROJECT_PATH) $(MAKECMDGOALS)
# add empty targets for additional goals if needed
ifneq (,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))
$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)):
endif
else
# this is where the real makefile starts
all more:
echo $#: $< $^
include a.mak
endif
It still does not seem possible from a makefile, but if you have a script that sets up environment variables, you can use MAKEFLAGS (e.g. export MAKEFLAGS=I/your/path ordentlich on Linux, or SET on Windows)

GNU make - reference targets in another makefile

Preface:
Yes, my makefiles are written badly.
No, I/we didn't write them; we inherited this code base from another company.
I want to know if it's possible to fix my problem WITHOUT rewriting them.
Question
Is there a way to reference targets from another makefile and use those as prerequisites?
Say you have:
all: libs binary
binary: # I need to add prereqs here
blah
blah2
blah3
For binary, I need to targets in other makefiles as prereqs.
I cannot just include those makefiles, and therefore those targets, because those makefiles define identical variables but with different values.
Is it possible to do something like:
binary: C:/mk1:foo C:/mk2:bar
blah
blah2
blah3
UPDATE
In case it's not clear, makefilesC:/mk1 and C:/mk2are part of the same makefile project that is being executed via some top level makefile with make --jobs=X so in theory all makefiles could be being made in parallel.
Sometimes Recursive Make [duhn-duhn-duhnnnn!] is the right tool for the job:
binary: foo bar
blah
blah2
blah3
.PHONY: foo bar
foo:
$(MAKE) -f mk1 $#
bar:
$(MAKE) -f mk2 $#
The PHONY forces Make to execute those rules and invoke the other makefiles to (perhaps) rebuild foo and bar even if they already exist (because this makefile doesn't know what prerequisites they may have).
What about using the include makefile (or sinclude) mechanism to incorporate the inherited makefile? This should work as long as your own targets have different names.
You can also concatenate makefiles by specifying multiple -f makefile options. They are concatenated in order.

Makefile: Declaring dependencies between included files

Using make's ''Remaking Makefiles'' feature I am generating parts of my makefile with include directives (see Makefile: defining rules and prerequisites in recipes). Now I'm stuck with being unable to see how I can express dependencies between included makefiles. They seem to be all evaluated at once.
Consider the following minimal makefile that illustrates my problem:
all:
-include foo.make
-include bar.make
foo.make: Makefile
echo FOO:=blub bla baz > foo.make
bar.make: Makefile foo.make
echo BAR:=$(FOO) > bar.make
If I now run make I will get:
$ cat foo.make
FOO:=blub bla baz
$ cat bar.make
BAR:=
Why? Since bar.make depends on foo.make, shouldn't the evaluation of bar.make wait until it successfully included foo.make?
And how do I fix this problem and make sure that bar.make is either re-evaluated later or only evaluated once foo.make exists, is included and can define the variable BAR?
The reason I cannot combine foo.make and bar.make into a single makefile and rule is two-fold:
Firstly, in my real setup, bar.make depends on more intermediate targets which in turn transitively depend on foo.make. So at the time foo.make can be created, the content of bar.make cannot yet be made.
Secondly, in my real setup, foo.make and bar.make do not just define variables but also eval() define/endef blocks. So I have to write:
-include makefile_with_prerequisite_variables
define MYDEF
sometarget-$1: $(TARGET_$1_PREREQUISITES)
[...]
endf
-include makefile_with_eval_call_statements
The content of makefile_with_prerequisite_variables and makefile_with_eval_call_statements cannot go into a single makefile snippet:
If I would put makefile_with_eval_call_statements above MYDEF together with makefile_with_prerequisite_variables then the $eval( $call( MYDEF)) statements in it would not work because MYDEF is only declared afterward.
If I would put makefile_with_prerequisite_variables below MYDEF together with makefile_with_eval_call_statements then the recipes defined in MYDEF would not have proper prerequisits because the $(TARGET_$1_PREREQUISITES) variables would then be declared afterward by makefile_with_prerequisite_variables.
In summary, I need to include two different makefiles where one depends upon the other. I do not know how I can express this relationship such that the content of one makefile would only be created after the other makefile is up-to-date and included into the main makefile.
First, your makefile creation in this simple example can easily be fixed by escaping the value of $(FOO) so that it's not expanded when bar.make is created but rather deferred until it's read in. So:
bar.make: Makefile foo.make
echo 'BAR:=$$(FOO)' > $#
However, that might not be sufficient in your more complex real-life makefiles.
GNU make works like this: first parse all the makefiles. Then for every included makefile, treat it as a goal and try to build it (e.g., act as if the user invoked make include1.mk include2.mk include3.mk ...). Then at the end of that, if any of the included makefiles was rebuilt, re-exec ourselves and start the entire process over from scratch.
GNU make does NOT work like this: parse makefiles, try to rebuild the first included makefile and if it's rebuilt, re-exec; if it's not rebuilt go on to the next included makefile, etc.
A simple trick you can use if you have to have this type of order is to put the include of bar.make into foo.make:
all:
-include foo.make
foo.make: Makefile
printf -- '-include bar.make' > $#
echo FOO:=blub bla baz >> $#
bar.make: Makefile foo.make
echo 'BAR:=$$(FOO)' > $#
By doing this you ensure that if foo.make doesn't exist, make can't see the include of bar.make and so it won't try to build it. Only after the first re-exec will make see the include of bar.make and try to build it.
One thing: if you get the latest version of GNU make you no longer need to use the -include trick. You can just use include even with generated makefiles.

using the same make file in a separate directory

I have a makefile in directory foo and would like to use the same makefile in a subdirectory bar. I have been doing the following:
all:
<do work in foo>
cd bar;
make -f ../Makefile <target to make in bar>
This gets very messy when I try to do target specific variable values as I need to pass them on the command line when calling make in bar. Is there a cleaner way to do this?
I cannot tell from the question whether the following solution suites your needs, it might - or might not - work for you.
If your situation is that you simply want the same Makefile features available, include could be a solution. You can create a Makefile in directory bar in which you do everything you need specific to bar, and besides that, you do:
include ../foo/Makefile
Caveat! This doesn't work straight-forward. There cannot be two recipes with the same name. For example, if you want foo/Makefile to do recipeBar for all, and you want foo/Makefile to do recipeFoo and recipeBar for all, the following does not work:
foo/Makefile:
.PHONY: all
all:
recipeFoo
bar/Makefile:
.PHONY: all
all:
reciveBar
include foo/Makefile
Instead, the recipes have to be separated into unique names. However, dependency rules can be there multiple times, so it's not really a challenge to workaround this caveat. So, the following would work:
foo/Makefile:
.PHONY: all
all: allFoo
.PHONY: allFoo
allFoo:
recipeFoo
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar:
recipeBar
include foo/Makefile
Now, if you run make in bar, it would run recipeFoo and recipeBar.
If the sequence matters to you and recipeFoo must run before recipeBar, make allBar dependent on allFoo, like this:
bar/Makefile:
.PHONY: all
all: allBar
.PHONY: allBar
allBar: allFoo
recipeBar
include foo/Makefile
If you want your target-specific variables available when you call another make (for which I recommend to use $(MAKE) not make), you can export your variables - with the corresponding consequences (environment space overflow risk on some Windows versions, .
For example, if you have a target-specific variable FOO for target all in Makefile, and you want that when calling Submake.mak that variable is known, it works like this:
Makefile:
all: export FOO:=bar
.PHONY: all
all:
$(MAKE) -f Submake.mak
Submake.mak:
.PHONY: all
all:
echo $(FOO)
Create a link (hard or symbolic, your choice) in bar to ../Makefile. Then, as Carl points out in his comment, you can make -C bar and everything should work. (As of gmake 3.81, at least, make switches to the new directory first, then does its thing. I cannot speak for gmake 4.0.)

How to have variables of one included makefile available in another makefile included later?

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.

Resources