Here's a simple Makefile with 4 targets (a, b, c and all). Target b can fail (represented here with exit 1).
a:
echo "a"
b:
exit 1
c:
echo "c"
all: a b c
When running make all, c is never printed as b fails and target c is consequently not run. But in my particular case, I want c to be run, even if b fails.
I'm wondering if there is a way to define the "continue if error" policy directly inside the dependencies of target all.
I know that the desired behaviour can be reached by :
running make -i all (--ignore-errors) or make -k all (--keep-going)
using a "recursive" make
prefixing failing command in b with - (like -exit 1)
running tasks separately with make a; make b || make c
but all of these options implies to modify targets a, b or c, or modify the way make all is called.
Is there a way to have the expected behaviour by just modifying the all target dependencies (something like all: a -b c, but that definition does not work, obviously)?
Additional requirement : make all should return with exit code 1 if b fails, even if c target succeeds.
If you want to run all recipes of a, -b, c even if the -<something> ones fail you can use a pattern rule for -<something> targets:
a c:
#echo "$#"
b:
#echo "$#"; exit 1
all: a -b c
-%:
-#$(MAKE) $*
Demo (with --no-print-directory for simpler output):
$ make --no-print-directory all
a
b
Makefile:5: recipe for target 'b' failed
make[1]: *** [b] Error 1
Makefile:10: recipe for target '-b' failed
make: [-b] Error 2 (ignored)
c
But if you also want to "remember" their exit status, things are a bit more difficult. We need to store the exit status somewhere, for instance in a file and reuse it for the all recipe:
a c:
#echo "$#"
b:
#echo "$#"; exit 1
all: a -b c
#exit_status=`cat b_exit_status`; exit $$exit_status
-%:
-#$(MAKE) $*; echo "$$?" > $*_exit_status
Demo:
$ make --no-print-directory all
a
b
Makefile:5: recipe for target 'b' failed
make[1]: *** [b] Error 1
c
Makefile:8: recipe for target 'all' failed
make: *** [all] Error 2
Hard-wiring the name of the potentially failing target in the recipe of all is not very elegant. But it should be quite easy to solve:
a b c:
#echo "$#"
d:
#echo "$#"; exit 1
all: a -b c -d
#for f in $(patsubst -%,%_exit_status,$(filter -%,$^)); do \
tmp=`cat $$f`; \
printf '%s: %s\n' "$$f" "$$tmp"; \
if [ "$$tmp" -ne 0 ]; then exit $$tmp; fi; \
done
-%:
-#$(MAKE) $*; echo "$$?" > $*_exit_status
.PHONY: clean
clean:
rm -f *_exit_status
Demo:
$ make --no-print-directory all
a
b
c
d
Makefile:5: recipe for target 'd' failed
make[1]: *** [d] Error 1
b_exit_status: 0
d_exit_status: 2
Makefile:8: recipe for target 'all' failed
make: *** [all] Error 2
While you can't transmit parameters through prerequisite names (or at least only if you change the prerequisites completely), you could employ target-specific variables. But the solution isn't exactly pretty, with an additional variable leading every recipe line:
CIE_DASH = $(if $(filter $#,$(CONTINUE_SET)),-)
all: a b c
all: CONTINUE_SET += b
a:
$(CIE_DASH)echo "a"
b: CONTINUE_SET += e
b: e
$(CIE_DASH)exit 1
e:
$(CIE_DASH)exit 1
c:
$(CIE_DASH)echo "c"
Related
I have a Makefile with targets that have associated dependencies. So I use lookup table like:
APPS = a b c
dependency.lookup.a := x
dependency.lookup.b := y
dependency.lookup.c := z
$(APPS): %: path/$(dependency.lookup.%).datafile
do something with $(dependency.lookup.$#)
This makefile gives me error.
*** No rule to make target 'path/.datafile'
Constraints: Only MinGW. can't use shell/MSYS. Also support FreeBSD.
This requires using Secondary Expansion feature:
.SECONDEXPANSION:
$(APPS): %: path/$$(dependency.loopup.$$*).datafile
#echo "$# depends on $^"
%.datafile : # Create one on demand for testing.
mkdir -p ${#D}
touch $#
Outputs:
mkdir -p path/
touch path/x.datafile
a depends on path/x.datafile
Alternatively, use regular dependencies:
a : path/x.datafile
b : path/y.datafile
c : path/z.datafile
$(APPS): % :
#echo "$# depends on $^"
The top 3 lines only add dependencies, the rule is specified separately. The output is the same.
I had the same problem but with rather long names. I didn't want to repeat them (in APPS = ...), so I used a generated auxiliary makefile.
Makefile:
all: build
include deps.mk
deps.mk: deps.txt deps.sh
./deps.sh <$< >$#
build: $(DEPS)
echo "DEPS are up-to-date." # or whatever
deps.sh:
#!/bin/bash
echo "# This file is generated by $0"
echo "DEPS ="
while read -r dst src; do
echo
echo "DEPS += $dst"
echo "$dst: $src"
echo -e '\t''cp $< $#' # or whatever
done
deps.txt (now only this file needs to be updated for new dependencies):
a x
b y
c z
If a file exists, I want to add a target to build. If the file does not exist, I want the target to be skipped.
an example:
FILENAME = f
TARGETS := normal
ifneq($(shell stat test_$(FILENAME).c), "")
TARGETS += test
endif
all: $(TARGETS)
normal:
#echo normal
test:
#echo test
I'm not sure the $(shell stat ...) part even works, but the bigger problem is that make with any file test_f.c in the current folder gives:
Makefile:4: *** multiple target patterns. Stop.
Removing the ifneq ... endif block makes the target normal. How can I only run the target test if test_f.c exists?
What you can do is generate a string variable (let's call it OPTIONAL) such that when 'test_f.c' exists, OPTIONAL=test; otherwise, OPTIONAL=_nothing_. And then add OPTIONAL as a prerequisite of all. e.g.:
FILENAME = f
TARGETS = normal
OPTIONAL = $(if $(wildcard test_f.c), test, )
all: $(TARGETS) $(OPTIONAL)
normal:
#echo normal
test:
#echo test
You can also iterate over targets with for loop
.PHONY: all
RECIPES = one
all: RECIPES += $(if $(wildcard test_f.c), two, )
all:
for RECIPE in ${RECIPES} ; do \
$(MAKE) $${RECIPE} ; \
done
one:
$(warning "One")
two:
$(warning "Two")
> make
for RECIPE in one ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
> touch test_f.c
> make
for RECIPE in one two ; do \
/Applications/Xcode.app/Contents/Developer/usr/bin/make ${RECIPE} ; \
done
makefile:11: "One"
make[1]: `one' is up to date.
makefile:14: "Two"
make[1]: `two' is up to date.
I am new to make. I have a very basic Make question and have searched in vain about it.I have different arguments to be given to different prerequisites.
a b:c d
prog -i c -j d -out1 a -out2 b
I know that that $+ will list all preqs but how do I supply the arguments. I could retype a b c and d in the recipe and not use automatic variables at all but then typing needs to be done twice. A pattern rule could be made where a and b could be linked to c and d respectively but still how do I supply the arguments?
A Million Thanks
Let's try some simpler rules first:
a b c:
#echo the target is $#
(Test this with "make a" or "make b c".)
a: c d
#echo the prerequisites are $^
#echo the first prerequisite is $<
a: c d
#echo the second prerequisite is $(word 2,$^)
(Crude but effective.)
Your rule:
a b : c d
prog -i c -j d -out1 a -out2 b
is a little odd in that it can be invoked with either target (a or b) and it will do exactly the same thing. This is perfectly legal, but although Make has access to the target name with $#, it cannot easily scan the names of targets it isn't trying to build. But there's a way you can reduce the redundant typing:
TARG_1 := a
TARG_2 := b
TARGS := $(TARG_1) $(TARG_2)
$(TARGS) : c d
prog -i $< -j $(word 2,$^) -out1 $(TARG_1) -out2 $(TARG_2)
With credit to jml, here is a makefile that will run forever:
# We want to create a directory 'D'. Therefore, remove a "regular" file 'D'.
$(shell rm -rf D)
# 'x' will not exist, So Make may "search" for it in the VPATH directory
$(shell rm -rf x)
$(shell mkdir D)
# Finally, create 'D/x', that - hopefully - Make will "associate" with 'x'.
$(shell touch D/x)
VPATH = D
# 'x' is a target, with some ( - could be any! - ) recipe to execute!
x :: ; : blah blah
.SILENT : D/x
# A target-specific assignment, for target 'x'
x: foo =
Running, you will get a freezing process that will never finish, unless, abruptly aborted by a fatal signal.
Why?
I would like to create a makefile for LaTeX documents (in this minimal
example). When there is no file "makeindexstyle.ist", it should be created (by
running make makeindexstyle.ist) and used for formatting the index. The rule for
%.pdf reflects this. However, it is not working yet, I receive the error
ifneq (, ) {
/bin/sh: 1: Syntax error: word unexpected (expecting ")")
make: *** [master.pdf] Error 2
What's wrong?
Parts from Makefile:
MASTER = master
TEX = slave
TEXI = texi2dvi -p
all: $(MASTER:=.pdf)
%.pdf: %.tex $(TEX:=.tex)
ifneq ($(wildcard makeindexstyle.ist), ) { # if makeindexstyle.ist exists, compile and build index
$(TEXI) $<
make makeindexstyle.ist
makeindex -c -s makeindexstyle.ist $(MASTER:=.idx)
}
endif
$(TEXI) $<
makeindexstyle.ist:
#(echo "...") > makeindexstyle.ist
UPDATE:
I tried to make it as simple as possible to see where the error comes from. Among other things (like quoting), I tried this:
%.pdf: %.tex $(TEX:=.tex)
exist := $(wildcard "absolute-path-to-makeindexstyle.ist")
ifneq ($strip $(exist)),)
echo "foo"
endif
$(TEXI) $<
but the result is
exists :=
make: exists: Command not found
make: *** [book.pdf] Error 127
In the meanwhile, I could solve it on the shell side:
IDX = "makeindexstyle.ist"
%.pdf: %.tex $(TEX:=.tex)
#if test -f $(IDX); then \
echo "foo"; \
fi
$(TEXI) $<
This sounds like a duplicate of How do I check if file exists in Makefile? or How to conditional set up a Makefile variable by testing if a file exists.
Try
ifneq ($(wildcard makeindexstyle.ist),)
without the space. Alternatively, throw "" around the arguments?