In a BSD Makefile, is it possible to define a catch-all target? I'm looking for the GNU equivalent of:
%:
#echo caught target $#
I was hoping that the preprocessor possesses enough magic to define a target on the fly, but couldn't figure out how to do so. All local variables, such as .TARGET, only work within a target but not at global scope.
I don't know if BSD make qualifies, but every POSIX-compliant version of make must support the .DEFAULT: special target which does this, even in GNU make, without the overhead of using a catch-all pattern like %:
.DEFAULT:
#echo caught target $#
Related
Background:
We have a Makefile that sits in the middle of a dev-ops pipeline that does things in unusual ways, for legacy reasons not worth describing. This has worked fine for years, but upon upgrading to GNU Make 4.4, it started generating a new warning of the form:
Makefile:X: warning: pattern recipe did not update peer target 'Y'.
I'm 99% sure this warning is harmless for our use case, but the new warning output is tripping CI failures in our pipeline. This Makefile is invoked by an automated script and the output is also parsed by an automated script, neither of which can easily be changed.
Minimal reproducer:
Makefile:
%-seq %-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
do_complicated_stuff:
#echo doing complicated stuff with SRC=$(SRC) TGT=$(TGT)
touch $(TGT)
%-seq :: %.c force
echo Error: this rule should not be run in this MRE
exit 1
.PHONY: force
Command:
$ rm -f *-{seq,par} ; touch foo.cpp ; make --no-print-directory foo-seq
make do_complicated_stuff SRC=foo.cpp TGT=foo-seq
doing complicated stuff with SRC=foo.cpp TGT=foo-seq
touch foo-seq
Makefile:2: warning: pattern recipe did not update peer target 'foo-par'.
Here make was invoked to build the foo-seq target, but it's complaining about the foo-par target which does not exist and was not even mentioned. A given make command will be invoked to build exactly one target at a time, and the .PHONY dependency ensures the rule will be run (regardless of whether or not Make considers dependencies to be up-to-date). Make will be invoked (by the script) many times in the same directory to build each test.
Question:
Why is GNU Make 4.4 suddenly generating this new warning for an idiom that has always silently worked correctly in the past, and what is the minimal change to silence this harmless warning?
Constraints/Requirements:
The solution probably needs to involve a pattern rule of some kind, because the set of possible source file names cannot be encoded in the Makefile. The name is provided only on the command line (which cannot be changed), but the rule needs to match it against the existence of the source file to ensure the correct rule is selected and executed.
In the real Makefile both of the rules modeled above are much more complicated than shown here, so we'd like to avoid duplicating the first rule for each of the target patterns (which does silence the warning, but causes maintainability problems).
Finally, for portability reasons the solution needs to continue functioning correctly without new warnings for all versions of GNU Make back to v3.80 (possibly negotiable if there's really no better solution).
The current plan is that in the next release of GNU make, this will become an error not a warning. So you should address it now. The reason for the change is that there are other errors in how GNU make handles patterns that cannot be fixed without changing this.
The problem is this rule:
%-seq %-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
I'm not sure what you intend for this rule to actually do, but what it tells make it will do is a single invocation of this recipe will create both targets %-seq and %-par. That's what multiple patterns in a single rule means, and has always meant.
If your recipe does not actually build both of those targets then you will see this issue.
The simplest thing to do is write the rule twice, once for each target, which will work on all versions of GNU make and was always the correct way to write it:
%-seq :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
%-par :: %.cpp force
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
ETA
Wanting to avoid "duplicating the recipe" is trivial enough. Just put it into a variable:
define COMPLEX_RECIPE
$(MAKE) do_complicated_stuff SRC=$< TGT=$#
endef
%-seq :: %.cpp force ; $(COMPLEX_RECIPE)
%-par :: %.cpp force ; $(COMPLEX_RECIPE)
I have these recipes in my Makefile. They generate cross-compiled objects for ARM architecture and link them into an elf binary:
%.ao: %.c
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $# $<
%.elf: %.ao startup_stm32f0xx.ao system_stm32f0xx.ao
$(ARM_CC) $(ARM_FLAGS) $other_arguments -o $# $^
This works fine from a clean build.
Contrary to my expectation, if I then say touch foo.c; make foo.elf, gmake responds with
make: 'foo.elf' is up to date.
If I try to make foo.ao, gmake says that it, too , is up to date.
What am I missing?
Edit after reading the comments:
TLDR: I did have multiple rules matching the same target, as John Bollinger alluded and HardcoreHenry said specifically.
In addition to the rules above, there's a rule for assembly sources so I can use those vendor files:
%.ao: %.s
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $# $<
I had been debugging some macros, and used -save-temps to look at preprocessor output. This option also writes .s files. So after I'd run make foo.elf, I'd have the following in my directory:
foo.c
foo.i
foo.s
foo.ao
foo.elf
I can touch foo.c, but make sees that there's a foo.s which is older than foo.ao, and produces the output that it does. On a clean build, there is no foo.s, so make finds the %.c:%.ao rule and the build proceeds from foo.c.
(BTW, .ao stands for ARM object. In addition to cross-compiling for AMR, I compile many of the sources to run unit tests on the host, using the built-in .o:.c rule)
I'm not a fan of pattern rules.
Make can make very strange decisions on which rules apply depending on whatever is lying around on your hard disks.
It's all a bit arbitrary.
Much better IMHO to tell make exactly what files you need for a target.
It's pretty easy too.
Just prefix your pattern rule with the list of targets you actually want it to apply to.
This makes it a Static Pattern Rule.
objects := main.ao tools.ao devices.ao# etc
${objects}: %.ao: %.c
$(ARM_CC) $(ARM_CPPFLAGS) $(ARM_FLAGS) $(CFLAGS) -c -o $# $<
%.elf: ${objects} startup_stm32f0xx.ao system_stm32f0xx.ao
$(ARM_CC) $(ARM_FLAGS) $other_arguments -o $# $^
As an added bonus, make now won't try to create the pre-existing startup_stm32f0xx.ao and system_stm32f0xx.ao.
Usually I find it nicer to list the source files, but YMMV:
sources := main.c tools.c devices.c
objects := $(patsubst $.c,%.ao,${sources})
(P.S. Using a Static Pattern Rule doesn't really give you any advantage over a normal rule in this noddy case. I just wanted to show a small tweak that would make your makefiles much more consistent in their behaviour.)
I know it's bad form to use an answer to respond to another answer, but I ran out of space in a comment to #bobbogo's answer.
Sorry but I can't agree with your assessment of pattern rules. It's not true that you will get "strange decisions" based on "whatever is lying around on your harddisks", and it's certainly not arbitrary.
There is one advantage of static pattern rules over pattern rules, and that is also its downside: a static pattern rule is a shorthand for creating an explicit rule, so that rule will always be used to build that target. A pattern rule, on the other hand, is just one possible way to build a target: if the prerequisites of a pattern rule don't exist and can't be made, then make keeps going and looks for other pattern rules that might be able to build that target.
So if you have multiple possible ways you can build a target then an explicit rule cannot be used for that.
The problem with pattern rules is that if NO pattern rule applies then make just assumes there is no rule to build that target. If the target exists then make simply says "up to date" (as we see in the question) since there's no rule to build it. That can be confusing to users.
If you use an explicit rule (including a static pattern rule) and some prerequisite doesn't exist and can't be created, then make will exit with an error, which can make it easier to figure out what went wrong.
Consider the following Makefile
foo:
#echo '$#'
test:
#echo '$#'
#echo '---'
# Catch-all target
%: test
#echo '+++'
#echo '$#'
When issuing make bar the following is the console output:
$ make bar
test
---
+++
Makefile
+++
bar
I would like to understand the origin of Makefile which shows it is received as argument at some point, and also how to get rid of it in such a scheme. This is using
GNU Make 4.1
Built for x86_64-apple-darwin13.4.0
GNU make treats the makefile itself as a target that needs to be updated. See How Makefiles Are Remade:
... after reading in all makefiles, make will consider each as a goal target and attempt to update it. If a makefile has a rule which says how to update it (found either in that very makefile or in another one) or if an implicit rule applies to it (see Using Implicit Rules), it will be updated if necessary...
If you know that one or more of your makefiles cannot be remade and you want to keep make from performing an implicit rule search on them, perhaps for efficiency reasons, you can use any normal method of preventing implicit rule look-up to do so. For example, you can write an explicit rule with the makefile as the target, and an empty recipe (see Using Empty Recipes).
Hence, the catch-all-target % is used to update Makefile.
Makefiles often do not have to be updated, so it is customary to add an empty rule for that:
Makefile : ;
I am trying to check a file against a list before I try to compile it in a GNU makefile. Will the conditional ifneq below be evaluated every time the rule is invoked or just once? The condition seems to always evaluate the same way.
If not is the only way to do this to put the conditional in the shell command? I realize it may seem weird that the target list could be "not OK" ... the Make system could certainly be fixed to eliminate that weirdness but the pain will be greater.
Any suggestions?
Eli
OKSRC := realfile1.cpp realfile2.cpp
%.o: %.cpp
ifneq ($(findstring $<,$(OKSRC),),)
... do the compile
else
#skip the file
endif
Quoting from the Make documentation:
Conditional directives are parsed immediately. This means, for example, that automatic variables cannot be used in conditional directives, as automatic variables are not set until the recipe for that rule is invoked. If you need to use automatic variables in a conditional directive you must move the condition into the recipe and use shell conditional syntax instead.
ifneq is a conditional directive, and $< is an automatic variable. So in short, your above code will not work, so you would have to use the shell-based conditional.
But I would strongly suggest that you fix the root cause (i.e. the erroneous dependency generation), rather than trying to hack around it.
I think you can use if function to defer the expansion of automatic variables until the condition is evaluated.
OKSRC = realfile1.cpp realfile2.cpp
.cpp.o:
$(if $(findstring $<,$(OKSRC)),$(CC) $(CFLAGS) -c $<,#echo skip $<)
And the result shows:
$ make realfile1.o realfile2.o realfile3.o
cc -c realfile1.cpp
cc -c realfile2.cpp
skip realfile3.cpp
Is there a mechanism in make to allow for default global implicit rules that are available anywhere, similar to the built-in rules?
Make provides some built-inimplicit rules for compiling C/C++/Fortran files, without even requiring a Makefile for simple cases. However, when compiling other languages (e.g. Go programming language files), a Makefile is always required. I would like to extend my Makeenvironment to have implicit rules available by default.
This is not normally desirable, as it would cause your Makefile to be less portable; it wouldn't work on somebody else's machine if they didn't have it set up that way.
However, if you want to do this, create a "global" Makefile somewhere with your default rules for Go files, then add its path to the MAKEFILES environment variable. This global Makefile will be processed before any Makefile when you run "make", just as if you had included its source at the top of the file.
I'm assuming you're referring to the fact that you can do
make hello.o
and make will automatically know how to make the .o from a .c file (or indeed from a .f or .p, if one exists) - but you want to do this for custom file types (say, building a .bar from a .foo.
The most portable way of doing this is as follows (in your Makefile):
.SUFFIXES: .foo .bar
.foo.bar:
foo2bar -in $> -out $#
The first line (.SUFFIXES) warns make that you'll be treating these as special suffixes; the second line says "here's a recipe for making a .bar from a .foo. The third line gives the command for doing this - $> and $# get changed by make to the input and output filenames.
NOTE: The indent for the third line MUST be a tab character.
A much more flexible method, that only works with GNU make, is to use its support for implicit rules. If you can guarantee you'll be using GNU make then this is probably to be recommended.
While I agree with dmazzoni, I just though I'd add my make recipe for a Go Makefile:
# Include default Golang Make magic
include $(GOROOT)/src/Make.$(GOARCH)
# Hack the following line
your_program: your_program.$O
$(LD) -o $# $^
# Compiles .go-files into architecture-specific binaries
%.$O: %.go
$(GC) -o $# $^
clean:
rm your_program *.$O
(Note: the $O is DOLLAR + UPPERCASE-o - not zero!)
While I haven't tested it on all the machines I have available, i believe it should port fairly well.