I've gone over a number of other posts about recursive makefiles. Either I'm doing something wrong or there's a missing feature that I want. With a typical makefile I can check to see if it's out of date with -q, for example:
make -q || echo "out of date"
However, setting up a mechanism to force make to process dependent makefiles causes the root makefile to always be out of date. Here's a working example:
#./Makefile
all: myfile
myfile: dir
cp dir/myfile .
.PHONY: dir
dir:
$(MAKE) -C dir/
#./dir/Makefile
all: myfile
myfile:
touch myfile
#testing...
>>> make
make -C dir/
make[1]: Entering directory `dir'
touch myfile
make[1]: Leaving directory `dir'
cp dir/myfile .
>>> make
make -C dir/
make[1]: Entering directory `dir'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `dir'
cp dir/myfile . #<-- this shouldn't be necessary
>>> make -q || echo "out of date"
make -C dir/
out of date
Is there a mechanism that allows the recursive make call to return and say "actually everything's up to date here, no need to continue"?
There's an hacky way of doing it, maybe.
You could invoke $(MAKE) -q ... && touch something from inside the makefile, and have the something file as requisite for some rule in the outer makefile.
No, there's no way to do that. A recursive invocation of make is just another command that make invokes in a shell: the only result of that command available to make is the exit code. If we could always be sure that the command invoked was a make command, and that we were using the -q flag, then we could assume that if it exited with a 0 then nothing was done. But make does not assume those things.
You will need to rewrite your makefiles to be non-recursive to get the behavior you want.
#Dacav gave me the idea of calling make explicitly. Though without using a temporary files, target dependencies can be set dynamically using a shell-populated variable. I'm not sure the following is a good solution because even when calling make without -q it recursively calls make -q to populate OLD_CHILDREN. Moreover this will happen again unnecessarily for the child makefiles too (assuming multiple levels of recursion).
#./Makefile
CHILDREN=dir/
OLD_CHILDREN=$(foreach c, $(CHILDREN), $(shell make -q -C $(c) || echo $(c)))
all: myfile
myfile: $(OLD_CHILDREN)
cp dir/myfile .
.PHONY: $(CHILDREN)
$(CHILDREN):
$(MAKE) -C $#
Testing...
>>> make -q || echo out of date
make -C dir/
make: *** [dir/] Error 1 #huh??? whatever. still works, returning out of date
out of date
>>> make
make -C dir/
make[1]: Entering directory `dir'
touch myfile
make[1]: Leaving directory `dir'
cp dir/myfile .
>>> make
make: Nothing to be done for `all'.
>>> make -q || echo out of date
>>> #nothing. success :D
Related
I have a makefile that loops over all child directories that have files called Makefile in them, and runs a new make in that directory with my current target. It works fine when it works.
However, if the child make process finds an error it stops... but my calling make doesn't. The calling make continues to run, giving a false impression it is working. How can I make the calling make halt in this case?
# Make the same target in all subdirectories with a Makefile.
SubMakefiles = $(shell find -L . -maxdepth 2 -type f -name Makefile)
SubMakeDirs = $(filter-out ./,$(dir $(SubMakefiles)))
clean depend all::
#for dir in $(SubMakeDirs); do \
make -C $$dir $#
done
Without changing your targeting, you can capture the return value of one or more commands in sh with parenthesis, and exit with their error code should there be an error code, as follows:
# Make the same target in all subdirectories with a Makefile.
SubMakefiles = $(shell find -L . -maxdepth 2 -type f -name Makefile)
SubMakeDirs = $(filter-out ./,$(dir $(SubMakefiles)))
clean depend all::
#for dir in $(SubMakeDirs); do \
( make -C $$dir $# ) || exit $$?; \
done
Ultimately I settled on the following approach. Since the recursive make is run as a command directly by make, any error code produced will cause the master make to abort as well.
Walkthrough: we recurse to any sub-directory with a Makefile in it.
MAKECMDGOALS is a gmake-managed variable holding the command goal. A little counterintuitively, this top-level makefile says that whatever command you're trying to run depends on all the subdirectories with makefiles, and the rule to "create" those subdirectories (of course they already exist, but gmake isn't specifically aware of that) it recursively runs the same command in the subdirectory (specified with -C).
SUBDIRS = ${sort ${dir ${wildcard */Makefile}}}
all $(MAKECMDGOALS):: $(SUBDIRS)
.PHONY: $(SUBDIRS)
$(SUBDIRS)::
$(MAKE) --no-print-directory -C $# $(MAKECMDGOALS)
I'm trying to write a makefile to produce several output files for each of several sources, using pattern rules.
I have the following Makefile (GNU Make 3.8.1):
all : foo.all bar.all
%.all : %.pdf %.svg
#echo Made $*
%.pdf :
touch $#
%.svg :
touch $#
.PHONY: foo.all bar.all
Since *.all do not represent real output files, I tried marking them as .PHONY. However, running make then doesn't work:
$ ls
Makefile
$ make
make: Nothing to be done for `all'.
According to make -d:
No implicit rule found for `all'.
Considering target file `foo.all'.
File `foo.all' does not exist.
Finished prerequisites of target file `foo.all'.
Must remake target `foo.all'.
Successfully remade target file `foo.all'.
Considering target file `bar.all'.
File `bar.all' does not exist.
Finished prerequisites of target file `bar.all'.
Must remake target `bar.all'.
Successfully remade target file `bar.all'.
Finished prerequisites of target file `all'.
Must remake target `all'.
Successfully remade target file `all'.
make: Nothing to be done for `all'.
which seems to be pretending to run the %.all rules, but skipping the bodies.
But with the .PHONY line commented out, Make runs the targets, but then spontaneously decides to delete the output files:
$ make
touch foo.pdf
touch foo.svg
Made foo
touch bar.pdf
touch bar.svg
Made bar
rm foo.pdf foo.svg bar.pdf bar.svg
According to make -d, it says:
Removing intermediate files...
Minimal example
A minimal example giving anomalous behavior:
%.all: %.out
#echo Made $*
%.out:
touch $#
I expect running make somefile.all to cause it to create the file somefile.out, but it gets deleted:
$ make somefile.all
touch somefile.out
Made somefile
rm somefile.out
Keeping make from deleting intermediary files
I recommend against using .PRECIOUS (see below as to why). Using .SECONDARY would preserve the .out files:
TARGETS=foo bar
all: $(TARGETS:=.all)
%.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
$(TARGETS:=.all) just appends .all to all names in TARGETS. $(TARGETS:=.out) appends .out. We apparently cannot use %.out as a target of .SECONDARY. These just save having to relist all targets individually.
I prefer to not use .PRECIOUS for this because the documentation says
if make is killed or interrupted during the execution of their recipes, the target is not deleted.
This can leave corrupted files in the file system. Here's an example.
all: foo.all bar.all
%.all: %.out
#echo Made $*
%.out:
sh -e -c 'echo "{1, 2, 3" > $#; FAIL!; echo "}" >> $#'
.PRECIOUS: %.out
The FAIL! command simulates a tool that crashes in the middle of its work. Here's a shell session working with the Makefile above:
$ ls
Makefile
$ make
sh -e -c 'echo "{1, 2, 3" > foo.out; FAIL!; echo "}" >> foo.out'
sh: 1: FAIL!: not found
make: *** [foo.out] Error 127
$ cat foo.out
{1, 2, 3
Yikes... my foo.out file is incomplete. Let's try making again:
$ make
Made foo
sh -e -c 'echo "{1, 2, 3" > bar.out; FAIL!; echo "}" >> bar.out'
sh: 1: FAIL!: not found
make: *** [bar.out] Error 127
$ cat *.out
{1, 2, 3
{1, 2, 3
Make is none the wiser about files left around by earlier runs so when you run make again, it will take the corrupted files at face value. foo.out was not remade (despite the "Made foo" message) because it already exists and the Makefile went straight to trying to make bar.
.SECONDARY makes it so that:
The targets which .SECONDARY depends on are treated as intermediate files, except that they are never automatically deleted.
This means they are never automatically deleted just because they are intermediate files. The default make behavior of deleting targets that were being rebuilt if the tool rebuilding them crashed is not affected.
Using .PHONY with pattern rules
It seems though that .PHONY works only for targets that are explicit, not inferred. I've not found documentation confirming this. However, this works:
TARGETS:=foo bar
TARGETS_all:=$(TARGETS:=.all)
.PHONY: all
all: $(TARGETS_all)
.PHONY: $(TARGETS_all)
$(TARGETS_all): %.all: %.out
#echo Made $*
%.out:
touch $#
.SECONDARY: $(TARGETS:=.out)
In this rule $(TARGETS_all): %.all: %.out $(TARGETS_all): gives the list of targets to which the pattern can be applied. It makes foo.all and bar.all explicit targets. Without this, they would be inferred targets.
You can test that it works by creating file called foo.all in your directory and run make over and over. The foo.all file has no effect on make.
Your somefile.out files are considered intermediate by GNU make, which is why they are automatically deleted in your example. You can instruct GNU make to preserve these files by use the of .PRECIOUS special target, like this:
%.all: %.out
#echo Made $*
%.out:
touch $#
.PRECIOUS: %.out
I use 'for' loop to make subdirectories,but when some errors occur when make in subdirectory,it continues to make the next directory,can i stop when errors occur 'make' in subdirectory?
all :
for i in $(SUBDIRS); do make -C $$i dll; done;
||
make[1]: *** [bd_snmp.o] error 1
make[1]: Leaving directory `/home/ping/work/svnsocserv/src/bd_snmp'
make[1]: Entering directory `/home/ping/work/svnsocserv/src/bd_snmp_proxy'
Sure, just add something to your shell script that checks:
all:
for i in $(SUBDIRS); do $(MAKE) -C $$i dll || exit 1; done
(note ALWAYS use $(MAKE) to recursively invoke sub-makes, never make).
This is not a great method though, because while it does exit immediately when a sub-make fails it doesn't obey the make -k option to keep going.
Is there any way to cause a certain target to be rebuilt based on the value of a variable?
I have a Makefile with a command-line setting called DEBUG. I want certain object files to be rebuilt (to contain debug information) when I call "make DEBUG=yes" after haveing executed "make DEBUG=no".
Write the value of that variable into a file if the file doesn't exist or if it has a different value. Make all your object files depend on that file, so that whenever the file changes all object files get recompiled. E.g.:
BUILD := debug
SHELL := /bin/bash
$(shell [[ `cat .build 2>/dev/null` == ${BUILD} ]] || echo ${BUILD} > .build)
all : prog
prog : .build
touch $#
clean :
rm prog .build
.PHONY: all clean
Example run:
$ make
touch prog
$ make
make: Nothing to be done for `all'.
$ make BUILD=release
touch prog
$ make BUILD=release
make: Nothing to be done for `all'.
A better way is to build debug and release builds into different directories like MSVS does, but that requires a bit more advanced makefiles.
I have a working make, I have platform code and like several makes for each os in the folder. Right now I have one makefile which works. I renamed it to Makefile.ws and wrote this in Makefile
all:
make -f Makefile.w32
clean:
make -f Makefile.w32 clean
I ran it and got this error
> "make"
make -f Makefile.w32
make[1]: Entering directory `/c/nightly/test'
make -f Makefile.w32
make[3]: Makefile.w32: No such file or directory
make[3]: *** No rule to make target `Makefile.w32'. Stop.
make[2]: *** [all] Error 2
make[1]: *** [build] Error 2
make[1]: Leaving directory `/c/nightly/test'
"make": *** [all] Error 2
Oddly enough the clean works perfectly. Then I decided to write "make -f Makefile.w32 mingw32" and that did not work correctly. In fact it made a folder called mingw32 which I thought was very strange.
As for the mingw32 rule I just copy build which I suspect is the main/normal rule that is used to build
$(BUILD):
#[ -d $# ] || mkdir -p $#
#make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
mingw32:
#[ -d $# ] || mkdir -p $#
#make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
full .w32 source is here http://pastie.org/320035
First, what make are you running? Cygwin or MinGW, or something else?
make -f Makefile.w32
make[1]: Entering directory `/c/nightly/test'
make -f Makefile.w32
make[3]: Makefile.w32: No such file or directory
"Entering directory" is a hint. Why is it entering /c/nightly/test? Is there a Makefile.w32 there?
As to creating the directory "mingw32", the rule
mingw32:
#[ -d $# ] || mkdir -p $#
...
does exactly that. If "mingw32" does not exist, it creates it.
It would be easier to help you if you had a shorter example and clearly explain what you want to accomplish and what you expect to happen.
I think I see the problem in your second example. The mingw32 line should be changed so that it does not include the $(BUILD) variable:
mingw32:
#[ -d $# ] || mkdir -p $#
#make --no-print-directory -C mingw32 -f $(CURDIR)/Makefile
It is clear that he created the directory,
your first command in your given 2 rules, include a mkdir with the object name, i.e. either build or mingw32
Afterwards he changes into the current directory (i.e. no cd at all) and execute Makefile. But as you wrote you renamed Makefile into Makefile.ws, so offcourse you are getting File-Not-Found-error.
From the questions I can only recommend you to have a look at an short introduction or even better the manual for make.
Have you tried calling the secondary makefile using
$(MAKE) -f ...
instead of
make -f ...?