Suppress make warning from not-yet-built include without suppressing error - makefile

If I have a makefile
include foo
foo: Makefile
#echo 'bar::' > foo
#echo "\t#echo 'bar'" >> foo
bar::
#echo 'baz'
then when I run make bar, I get
Makefile:1: foo: No such file or directory
bar
baz
I want to suppress this warning. If I have a makefile
-include foo
foo: Makefile
#echo 'bar::' > foo
#echo "\t#echo 'bar'" >> foo
bar::
#echo 'baz'
then I get, as expected,
bar
baz
However, if I have a makefile
-include foo
bar::
#echo 'baz'
and I run make bar, then I get
baz
while I would instead like to get something like
Makefile:1: foo: No such file or directory
make: *** No rule to make target 'foo'. Stop.
or at least
make: *** No rule to make target 'foo'. Stop.
How do I suppress the warning without suppressing the error, so that I get a failure if the file cannot be created / if the target does not exist, but no spew when it is successfully created?

This is a hackish solution: You could split your makefile into two like so:
# Makefile
foo.mak:
#echo running $#
#echo 'bar::' > foo.mak
#echo -e "\t#echo 'bar'" >> foo.mak
bar bar2 bar3: foo.mak
#$(MAKE) --file Makefile.2 $#
and then
#Makefile.2:
include foo.mak
bar::
#echo baz
The problem with this is that it has to invoke a new copy of make for each top-level target. I seem to remember using a match-anything rule instead of a list of explicit targets, but I just tried it, and it seems to be giving me problems, so I listed the targets explicitly.

I have discovered an extremely hacky solution, that involves emitting the error message manually:
-include foo
ifeq (,$(filter-out foo,$(MAKECMDGOALS)))
ifeq (,$(wildcard foo))
$(shell $(MAKE) foo)
ifeq (,$(wildcard foo))
$(error No rule to make target 'foo')
endif
endif
endif
foo: Makefile
#echo 'bar::' > foo
#echo "\t#echo 'bar'" >> foo
bar::
#echo 'baz'
Perhaps an ever-so-slightly-less-hacky solution would be:
ifeq (,$(filter-out foo,$(MAKECMDGOALS)))
ifeq (,$(wildcard foo))
$(shell $(MAKE) foo)
endif
include foo
else
-include foo
endif
foo: Makefile
#echo 'bar::' > foo
#echo "\t#echo 'bar'" >> foo
bar::
#echo 'baz'

Related

Dollar in conditional variable assignment operator in Makefile

Is it possible to pass value with single dollar from shell to Makefile, or I it is only way to put double dollar in bash and then to call make?
Makefile is:
HASH ?= $$6$$salt$$val
.PHONY: tst
tst:
echo '$(HASH)'
Command to run:
> make HASH='$6$salt$val'
echo 'altal'
altal
If I use double quotes, all is fine:
> make HASH='$$6$$salt$$val'
echo '$6$salt$val'
$6$salt$val
But is it possible do not make substitution $ to $$ in bash?
How about writing the initialisation within the file identical to the one coming from the command line? The below script demonstrates how to rewrite a variable with the override directive:
quote-one-level = $(eval override $1=$(subst $,$$$$,$(value $1)))
var-info = $(info $1=$(value $1) flavour=$(flavor $1) origin=$(origin $1))
A ?= $abc
$(call var-info,A)
$(call quote-one-level,A)
$(call var-info,A)
$(call var-info,B)
$(call quote-one-level,B)
$(call var-info,B)
export A
export B
all:
#echo A = '$(A)'
#echo B = '$(B)'
ifeq ($(MAKELEVEL),0)
$(MAKE)
endif
Inflating one $ to $$$$ (and not just $$) is necessary because the eval command literally generates make code, thereby obviously reducing the quoting level by one. Resulting output:
$ make B='$abc'
A=$abc flavour=recursive origin=file
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make
make[1]: Entering directory
A=$abc flavour=recursive origin=environment
A=$$abc flavour=recursive origin=override
B=$abc flavour=recursive origin=command line
B=$$abc flavour=recursive origin=override
A = $abc
B = $abc
make[1]: Leaving directory
Try this:
In console:
export HASH='$6$salt$val'; make
in Makefile:
.PHONY: tst
tst:
#echo "$$HASH"
Result:
$6$salt$val

Conditional Dependencies in GNU Make

I would like to have my target have conditional dependencies. Below is an example that doesn't work
everything: foo bar \
ifndef EXTRA
biz baz
endif
recipe_to_do_stuff
So if I run make it will make everything with all the dependencies foo bar biz baz. But if I ran make EXTRA=true it would make everything with only foo bar.
Is this a possibility? I could have conditionals that run two separate commands but my target has lots of possible dependencies and I don't want to have two places to change if they need updates. Thanks in advance.
This was my eventual answer to have an inline solution.
everything: foo bar $(if $(EXTRA), biz baz)
recipe_to_do_stuff
You can define dependencies in multiple lines by repeating the target name at the beginning of the line:
# Makefile
baz:
#echo baz
bar:
#echo bar
foo: bar
foo: baz
foo:
#echo foo
$ make foo
bar
baz
foo
Hence, you can ifdef around one or more of these dependency lines:
# Makefile
baz:
#echo baz
bar:
#echo bar
foo: bar
ifdef DO_BAZ
foo: baz
endif
foo:
#echo foo
And define the variable if you want the extra dependencies:
$ make foo
bar
foo
$ make foo DO_BAZ=1
bar
baz
foo
This should work.
ifndef EXTRA
REQ=biz baz
endif
everything: foo bar ${REQ}
recipe_to_do_stuff

Makefile appending to variable for previous dependencies

I have a command foo that accepts a list of parameters. I want to alter the parameters I pass to foo depending on the target I am building. I have tried appending to target-specific variables, but that doesn't quite do what I want...
So, for example take this Makefile (which doesn't work since foo is built by target1):
all: target1 target2
foo:
echo foo $(foo_args)
target1: foo_args += abc
target1: foo
echo Target1
target2: foo_args += def
target2: foo
echo Target2
.PHONY: foo target1 target2
What happens:
> make all
foo abc
target1
target2
> make target1
foo abc
target1
> make target2
foo def
target2
What I want:
> make all
foo abc def
target1
target2
> make target1
foo abc
target1
> make target2
foo def
target2
Makefile syntax can be specific to GNU make. I'd also like to keep the parallelism so that target1 and target2 can be built in parallel.
Scalable variant of John's answer:
define add_target
foo_args_all += $2
foo_args_$(strip $1) := $2
$1: foo; #echo $$#
foo_targets += $1
endef
all:
$(eval $(call add_target, target1, abc))
$(eval $(call add_target, target2, def))
all: $(foo_targets)
foo:; #echo foo $(sort $(foreach g,$(or $(MAKECMDGOALS), all),$(foo_args_$g)))
.PHONY: all $(foo_targets)
Output:
$ make -f sample.gmk
foo abc def
target1
target2
$ make -f sample.gmk all
foo abc def
target1
target2
$ make -f sample.gmk target1
foo abc
target1
$ make -f sample.gmk target2
foo def
target2
$ make -f sample.gmk target2 target1
foo abc def
target2
target1
$ make -f sample.gmk target1 target2
foo abc def
target1
target2
Your example is missing a number of details and there may be a better way of handing things (e.g. why are the targets PHONY? How does foo depend on target1?), a complete example that shows exactly how the files are generated will get you a better answer.
That said, something like the following should work in this case
.PHONY: all foo target1 target2
all: foo_args += abc def
all: target1 target2
target1: foo_args += abc
target1: foo
#echo Target1
target2: foo_args += def
target2: foo
#echo Target2
foo:
#echo foo $(sort $(foo_args))
The problem with making target-specific variables is that they are only available in the scope of that target. In particular, if you were building a target with multiple dependencies, you would only go down one dependency at a time, and foo_args would only reflect the targets on the side of the tree you happen to be invoking foo from.
Another solution might be to use something like the following at the top of your makefile:
foo_args_target1 := abc
foo_args_target2 := def
foo_args_all := abc def
foo_args := $(sort $(foreach goal,$(MAKECMDGOALS),$(foo_args_$(goal))))
$(info foo_args is $(foo_args))
This has the advantage that foo_args is available globaly. This still has a scalability issue though -- if you were to create a new target all2 : target1 target2, then you would have to add a foo_args_all2 := ... into the makefile (you couldn't automatically detect that all2 was dependent on target1 or target2, and update foo_args automatically).

Makefile expanding variable inside define

define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
endef
INCLUDES := a b c
$(foreach dir, $(INCLUDES), $(eval $(call func1, $(dir)) ))
all : $(objs)
Contents of each makefile:
cat test/a/component.mk
NAME := AA
cat test/b/component.mk
NAME := BB
cat test/c/component.mk
NAME := CC
Output is
::::::: :::::::::::::::
:::::::AA :::::::::::::::
:::::::BB :::::::::::::::
It looks like first time NAME is empty.
Let's look at the expansion of $(foreach dir, ${INCLUDES}, $(eval $(call func1, ${dir}) )) in painful detail.
${INCLUDES} is expanded, giving $(foreach dir,a b c,$(eval $(call func1,${dir})))
Next dir is set to a
$(call func1,a) is expanded
1 is set to a
func1 is expanded:
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
$(shell pwd) becomes HERE, say (N.B. Use ${CURDIR} instead)
$(strip $1) becomes $(strip a) becomes a
${NAME} expands to nothing
$(info ::::::: ::::::::::::::: ) expands to nothing
As a side effect ::::::: ::::::::::::::: appears on stdout
$(eval $(call func1,a)) expands to $(eval include HERE/test/a/component.mk), expands to nothing
As a side effect, the include is processed by make
Presumably HERE/test/a/component.mk exists and contains valid make syntax,
and the variable NAME gets a value.
1 is set to b. Lather, rinse, repeat.
Tip
To get a hint of problems in code like this, always run make with --warn:
$ make --warn -Rr
Makefile:8: warning: undefined variable 'NAME'
::::::: :::::::::::::::
⋮
Fix
To get some insight, replace the $(eval stuff) with $(error [stuff])
$ make
::::::: :::::::::::::::
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
]. Stop.
Here we see the $(info …) has disappeared even before it has got to the eval.
The naive fix is pretty horrible.
define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$$(info :::::::$${NAME} ::::::::::::::: )
endef
Running this with the $(error …) in place gives
$ make
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )]. Stop.
That stuff between the [ and ] is valid make syntax.
Tidied up it looks like:
include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )
Job done. There are cleaner ways, but you need to understand the pain first!

Dynamically generating a list of targets

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.

Resources