How to generate Makefile rule - makefile

I want to do generate rules in Makefile by this:
# $(call cc-defs, ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
$1: $1.files
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))
but failed with error message:
Makefile:19: *** commands commence before first target. Stop.
Instead that, I can do this by:
# $(call cc-defs, ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))
$(foreach ccfile,$(ccfiles), $(eval $(ccfile):$($(ccfile).files)))
How to make the 1st method works?

Which version of make are you using? $(eval) only appeared in 3.80 (and it only properly works in 3.81 IMHO).
To debug makefiles you'll often have to revert to printf debugging. To see what's going on, replace eval with warning. This shows what you are giving to make:
$ make --warn
Makefile:6: warning: undefined variable `ccfiles'
make: *** No targets. Stop.
(Aside: --warn-undefined-variables is always useful. Undefined variables are untidy.)
O.K., so we need to define $ccfiles. Now we get the for loop firing:
$ make --warn ccfiles=1.cc
Makefile:6: 1.c.files = 1.cc
1.cc: 1.c.files
make: *** No targets. Stop.
Fine. You have given make no recipes, nor a default target. You also have missed out on some variable expansion, and have an extra space in the $(for) invocation (naughty!). Try this:
$ cat Makefile
# $(call cc-defs,ccfiles)
define cc-defs
$1.files = $(patsubst %.cc,%.proto,$1)
$1: $$($1.files) ; echo '[$$#]'
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs,$(ccfile))))
$ make ccfiles=1.cc
make: *** No rule to make target `1.proto', needed by `1.cc'. Stop.

Note that if all you want to do is for all files in a variable to depend on (or be made from) .proto files, you don't need $(eval).
A pattern rule will do (and will work in older versions of GNU Make too):
$(ccfiles): %.cc: %.proto
echo '[$#]'
This does have the side effect of complaining when the ccfiles variable contains any entries not named *.cc (although it still executes the rule in that case).
$ make ccfiles=hello.cc
make: *** No rule to make target `hello.proto', needed by `hello.cc'. Stop.
$ touch hello.proto
$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
Makefile:1: target `hello.c' doesn't match the target pattern
[hello.c]
If the variable can contain many things but you only want to add this processing to .cc files, simply add a filter:
$(filter %.cc,$(ccfiles)): %.cc: %.proto
echo '[$#]'
This then results in:
$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
make: *** No targets. Stop.

Related

GNU Make: Canned recipe which is meant to generate prerequisites for rule causes error "No rule to make target"

I have this simple Makefile:
define some_canned_recipe
find 'foobar' -print
endef
run-something: $(call some_canned_recipe)
#$(info ** [Make] run-something)
#touch $#
I want the 'run-something' rule to be run if and only if one or more files or directories has changed under subdirectory 'foobar'. When I invoke 'make run-something' inside WSL2 however I get this error:
make: *** No rule to make target 'find', needed by 'run-something'. Stop.
Is there a way to achieve what I want (in terms of dynamically generating the prerequisites for the 'run-something' rule)?
PS: I'm aware that a silly solution would be:
define some_canned_recipe
$(shell find 'foobar' -print)
endef
even though this works its not really a good idea because $(shell ...) will run even when the rule 'run-something' is not being targeted.
You can do this using secondary expansion combined with implicit rules:
.SECONDEXPANSION:
run-something:
run-%: $$(shell find 'foobar' -print)
#$(info ** [Make] $#)
#touch $#
(note the $$ before the shell function)
I make no comments on whether I think this is the best way to do it :).

make[1]: Command not found

I am getting below error while building some set of files. Would someone give some pointers on why do I get this error? Am I missing installation of some package?
Snippet of error log:
make[1]: MMD: Command not found
CC drivers/usb/usbhid.libc.o
make[1]: MMD: Command not found
CC drivers/usb/usbmsc.libc.o
make[1]: MMD: Command not found
CC drivers/hid.libc.o
make[1]: MMD: Command not found
AR build/libc.a
make[1]: invalidar: Command not found
Makefile.inc:89: recipe for target 'build/libc.a' failed
Snippet of my makefile:
# macro to define template macros that are used by use_template macro
define create_cc_template
# $1 obj class
# $2 source suffix (c, S)
# $3 additional compiler flags
# $4 additional dependencies
ifn$(EMPTY)def $(1)-objs_$(2)_template
de$(EMPTY)fine $(1)-objs_$(2)_template
$(obj)/$$(1).$(1).o: $$(1).$(2) $(obj)/libpayload-config.h $(4)
#printf " CC $$$$(subst $$$$(obj)/,,$$$$(#))\n"
$(CC) $(3) -MMD $$$$(CFLAGS) -c -o $$$$# $$$$<
en$(EMPTY)def
end$(EMPTY)if
endef
It looks to me as if the CC variable is not defined to anything when you invoke this macro, and the third argument is empty. This means that the recipe make internalizes after the eval is expanded is:
-MMD $$(CFLAGS)...
A quick fix is to escape the variable for CC:
$$(CC) $(3) -MMD ...
I think your expansion model for this is very odd, and probably incorrect in other ways. But, without seeing how this macro is used it's hard to say.
One easy way to debug eval issues is to duplicate the context where the eval appears and replace the eval with the info function. This will print exactly what make will parse, and it should be completely normal and understandable makefile syntax; e.g., change something like:
$(foreach X,$(STUFF),$(eval $(call FOO,$X)))
to:
$(foreach X,$(STUFF),$(info $(call FOO,$X)))
$(foreach X,$(STUFF),$(eval $(call FOO,$X)))

-include directive should ignore errors. But make stops because of an error

From the docs:
If you want make to simply ignore a makefile which does not exist
or cannot be remade, with no error message, use the -include directive
instead of include, like this:
-include FILENAMES...
This acts like include in every way except that there is no error
(not even a warning) if any of the FILENAMES (or any prerequisites of
any of the FILENAMES) do not exist or cannot be remade.
Given the following makefile:
$(shell rm -rf x foo)
-include mkfile
all: ;
mkfile : x ;
x : foo ;
.INTERMEDIATE : x
Running, I get:
make: *** No rule to make target 'foo', needed by 'x'. Stop.
Well, shouldn't Make just ignore this error, as we use a -include (not include) directive, per the documentation above?
make: *** No rule to make target 'foo', needed by 'x'. Stop.
Shouldn't make just ignore this error, as we use a -include (not include) directive, per the documentation above?
No! Your interpretation of the documentation is wrong.
The use of - in -include will ONLY ignore any files that succeed it, i.e. come after it. It will NOT ignore any file!
You have chosen to take part of the documentation out and build your interpretation on it.
there is no error (not even a warning) if any of the FILENAMES (or any prerequisites of any of the FILENAMES) do not exist or cannot be remade.
The above quote ONLY applies to files that succeed -include as in
-include FILENAMES...
it does not apply to every and any files.
Demonstration of - in include
To demonstrate this we can take a simple example as
$(shell rm -rf mkfile)
-include mkfile
all:
echo '$#'
.PHONY: all
In this instance either before, or at least on make's second pass, the file mkfile does not exist. If we were to execute this, the output would be
$ make
echo 'all'
all
Here make has ignored the fact that mkfile does not exist and has continued processing the makefile without warning or error. This is exactly what the documentation states.
If instead we removed the - before include and so had a makefile like
$(shell rm -rf mkfile)
include mkfile
all:
echo '$#'
.PHONY: all
Executing this would produce the output
$ make
makefile:3: mkfile: No such file or directory
make: *** No rule to make target 'mkfile'. Stop.
Now make has stopped because there is an error, the file mkfile does not exist and we haven't used -include.

How can I use a pattern rule to add prerequisites like I can to define variables?

I have the following Makefile:
all: foo/bar/baz
foo/%:
#echo $(VAR)
cp $#.in $#
# This works
foo/bar/%: VAR := Hello world
# This doesn't
foo/bar/%: foo/bar/%.in
foo/bar/baz.in:
touch $#
When I run it, the output is
Hello world
cp foo/bar/baz.in foo/bar/baz
cp: cannot stat ‘foo/bar/baz.in’: No such file or directory
Makefile:4: recipe for target 'foo/bar/baz' failed
make: *** [foo/bar/baz] Error 1
In other words, the pattern-specific variable rule works, but the equivalent syntax to declare an extra prerequisite doesn't. What should I do instead?
The real use case is for copying headers before a build. I wrote
obj/subdir/%.o: CPPFLAGS += -Igen/include
obj/subdir/%.o: | gen/include
gen/include:
# Copy the headers
but the headers don't get copied.
You cannot do this. Pattern rules must define all prerequisite patterns when the rule is created; they cannot be added later.
Writing a pattern rule with no recipe deletes the pattern rule.

make: forcing a rule to be used

Typping make (Gnu Make) with the following Makefile (assuming files foo.x and foo.y are missing)
all: foo.z
foo.z: foo.x foo.y
cat foo.x foo.y > foo.z
produce the error message:
make: *** No rule to make target `foo.x', needed by `foo.z'. Stop.
But this Makefile:
all: foo.z
%.z: %.x %.y
cat %.x %.y > %.z
produce this error message:
make: *** No rule to make target `foo.z', needed by `all'. Stop.
In the first case, the rule is applied, and a dependency if found to be missing.
In the second case, the rule is found to be not appropriate, and that is a rule that is missing.
I want to use the second Makefile (I have a lot a *.z object to be created), but with an error message like with the first Makefile. My make output is logged, and having the expanded name of the missing files will help debugging.
Is it possible? Maybe a way to force the rule to be used?
Of course I'm not catting file, this is just a example...
I just found:
all: foo.z
OBJ=foo.z
$(OBJ): %.z: %.x %.y
cat %.x %.y > %.z
One need Static Pattern rule. Amazing make...

Resources