Accessing parent Makefile targets from subdirectory - makefile

Say my directory structure is this:
foo
foo/Makefile
foo/bar
Now say foo/Makefile has a make target baz.
I want to call make baz from foo/bar without creating another Makefile in the bar subdirectory. Is this possible?

GNU make has two important options for your situation: -f FILE is used to tell make which makefile(s) to use instead of the defaults and -C DIR tells make to change to directory DIR before reading the makefiles. How to use one or the other or both depends on your specific case. Note that -f is compliant with the POSIX standard while -C is an extension supported by GNU make. If -C is not supported by your own version of make and has no equivalent you will have to change the current directory yourself before invoking make, e.g. ( cd some/where; make...; ).
If you can build baz from foo/bar/, as suggested by Oo.oO, you can simply
make -f ../Makefile baz
make will run from foo/bar/ and build baz as indicated in ../Makefile.
If you must be in foo/ to build baz you should use:
make -C .. baz
make will change to .., that is, foo before reading the makefiles and as it will find here one of the defaults (Makefile) it will use it to discover how to build baz.
If you must be in another directory, e.g. the parent of foo/, you need both options and type:
make -C ../.. -f foo/Makefile baz
make will first change to ../.. (parent directory of foo/) and from here it will use foo/Makefile to discover how to build baz.

Related

Running another target after existing one

Imagine we have an existing (untouchable) Makefile with target "foo", and another included Makefile which I can modify. I would like to add a new target called "runafter" which shall be executed after "foo" was run. So the user keeps calling "foo" and some additional code shall be run afterwards.
The usual way to achieve this would be to rename the original ones and do something like:
foo_old:
...
foo: foo_old
# run some code or call another target explicitly
$(MAKE) runafter
But that only works if you can rename foo. If not, how could I extend the behavior of the existing target? Everything I tried to do with foo: ... apparently causes overriding of the old foo target (with warning). But I just want to run some code afterwards!
I do not see how to do this from the included makefile but if you use GNU make then you can add a makefile named makefile instead of Makefile:
$ cat makefile
foo:
$(MAKE) -f Makefile $#
$(MAKE) runafter
runafter:
...
From the GNU make man page:
If no -f option is present, make will look for the makefiles GNUmakefile, makefile, and Makefile, in that order.
So you can also name it GNUmakefile if you wish. With one or the other running make foo should do what you want.

How do I recursively use make with sub receipes?

I have to Make files like follows:
Makefile
foo/
Makefile
Top Make looks like:
make-sample:
cd foo && make all
Bottom Make looks like:
all:
./builder.sh
The sub make works as designed and prints out some stuff to the terminal. When running it from the top make file: make foo it doesn't return the same cli output and it makes me think something is not correct. I was looking at recursion man pages for make https://www.gnu.org/software/make/manual/html_node/Recursion.html#Recursion and I was understanding it somewhat. It looks like i am close, though i don't understand the use of $(MAKE) without giving a recipe.
it looks like it wants me to say something like:
make-sample:
cd foo && $(MAKE)
# or
make-sample2:
$(MAKE) -C foo
Can someone better explain to me how to run a sub make with a recipe such that it properly logs the same info to stdout?
On your top directory, foo is a directory and you need to explicitly tell make that your target name foo is not a file, nor a directory by adding this line:
.PHONY: foo
Otherwise, make might do nothing when you do make foo from the top directory.

Ordering in makefiles

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).

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.

multiple makefiles in one directory

I have a makefile in a directory of mine which builds scripts with certain environment variables set. What if I want to create another makefile in the same directory with different environment variables set? How should I name the two make files? Does makefile.1 and makefile.2 work? How do I call them?
You can give sensible names to the files like makefile.win and makefile.nix and use them:
make -f makefile.win
make -f makefile.nix
or have a Makefile that contains:
win:
make -f makefile.win
nix:
make -f makefile.nix
and use make win or make nix
You can name makefile whatever you want. I usually name it like somename.mk. To use it later you need to tell make what makefile you want. Use -f option for this:
make -f somename.mk
Actually you can have two set of environment variables in the same make file. for example
COMPILER = gcc
CCFLAGS1 = -g
CCFLAGS2 = -Wall
a: main.c
${COMPILER} ${CCFLAGS1} main.c
b: test.c
${COMPILER} ${CCFLAGS2} test.c
then you can just say make a or make b. Depending on what you want.
Also it is possible with -f flag to call which makefile you want to call.
You can do something like this rather than using multiple makefiles for the same purpose. You can pass the environment or set a flag to the same makefile. For eg:
ifeq ($(ENV),ENV1)
ENV_VAR = THIS
else
ENV_VAR = THAT
endif
default : test
.PHONY : test
test:
#echo $(ENV_VAR)
Then you can simply run the make command with arguments
make ENV=ENV1
I have two makefiles in the same directory. Many of the recipes have identical names and here are two solutions:
1. Prefix in make
proja_hello:
#echo "hello A"
projb_hello:
#echo "hello N"
2. Keep two separate files
Project A has makefile. Type make hello.
Project B has a separate make file called projb.mk. Type bmake hello.
This works since I've added alias bmake ='make -f projb.mk to my .bashrc. Note! This command can be called anywhere but only works where projb.mk exists.
Note! You lose autocompletion of make with the alias and typing make -f projb.mk hello is not better than typing make projb_hello.

Resources