Override static pattern rule in Makefile (without it giving a warning) - makefile

I would like to use a static pattern rule to specify a default recipe for a class of targets, but override that recipe for a few specific targets.
Here's a trivial example that illustrates what I'm trying to do. For each directory that contains a file "test.py" I want to invoke the command "run_test.py", except when the directory is named "one" I want to invoke a different set of commands:
TESTS := $(shell find * -name "test.py" | xargs -I {} dirname {})
.PHONY: $(TESTS)
all: $(TESTS)
$(TESTS): %:
python run_test.py $#
one:
python run_test.py $# mode=1
python run_test.py $# mode=2
python check_results.py $#
This works, but gives a warning:
$ make
Makefile:12: warning: overriding commands for target `one'
Makefile:9: warning: ignoring old commands for target `one'
Is there another way to do this, or eliminate the warning?

No, you can't do that. A static pattern rule is really not a pattern rule: instead it's just a shorthand for writing a lot of explicit rules.
If you want to have most targets use one recipe but a few use another recipe, you should define a real pattern rule for "most" targets, not a static pattern rule, then use explicit rules to override:
%.o: %.cc
g++ -c $< -o $#
test.o: test.cc
g++ -Wall -c $< -o $#
Why do you want to use a static pattern rule instead of a regular pattern rule?
ETA You could also use target-specific variables:
CFLAGS =
test.o: CFLAGS = -Wall
$(OBJS): %.o: %.cc
g++ $(CFLAGS) -c $< -o $#
ETA2 OK, that's much different than your original example.
You have two obvious choices that I can see. The first is to remove the special targets from the list; something like:
SPECIAL_TESTS := one
TESTS := $(patsubst %/test.py,%,$(shell find * -name "test.py"))
.PHONY: all $(TESTS)
all: $(TESTS)
$(filter-out $(SPECIAL_TESTS),$(TESTS)):
python run_test.py $#
$(SPECIAL_TESTS):
python run_test.py $# mode=1
python run_test.py $# mode=2
python check_results.py $#
The other way to do it is with target-specific variables that define the entire recipe, something like this:
TEST_RECIPE = python run_test.py $#
one: TEST_RECIPE = python run_test.py $# mode=1 \
&& python run_test.py $# mode=2 \
&& python check_results.py $#
TESTS := $(patsubst %/test.py,%,$(shell find * -name "test.py"))
.PHONY: all $(TESTS)
all: $(TESTS)
$(TESTS):
$(TEST_RECIPE)

Related

GNU make: several targets in one pattern rule

With explicit targets I can combine several rules like
foo.o bar.o: $(SOURCES)
cc $< -o $#
This is equivalent of
foo.o: $(SOURCES)
cc $< -o $#
bar.o: $(SOURCES)
cc $< -o $#
But I want to use pattern rules.
I have several troff documents (man, README) and I want to generate .html and .ascii files.
Naive approach is
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.html %.ascii: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it doesn't work, because make believes that all files are created with one command (much like & in modern make: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html)
Obviously I can do
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.ascii: %.doc
$(CALL_GROFF)
%.html: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it is a kind of copy-paste.
Could it be solved with GNU make?
This is exactly how this works; it's a long-standing feature. From the documentation:
Pattern rules may have more than one target; however, every target must contain a % character. Pattern rules are always treated as grouped targets (see Multiple Targets in a Rule) regardless of whether they use the : or &: separator.
As example states, it was meant to deal with programs that generate more than one output in one invocation, like bison. You can either update your recipe to generate both files in one shot, or keep the rules separated as you do now.

Add rule to generate specific headers if missing

I have a Makefile that mixes Java and C++ with JNI and I would like to generate the JNI header automatically when needed.
Currently, I have the following:
$(OBJDIR)/org_some_package_%.cpp: $(INCDIR)/jni/org_some_package_%.h
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(OBJDIR)
$(CC) -c -o $# $(CFLAGS) \
-I$(INCDIR) -I../build/include -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/linux" \
-MD -MF $(patsubst %.o, %.d, $#) $<
.SECONDEXPANSION:
$(INCDIR)/jni/%.h: $(JAVA_TEST_BIN)/$$(subst _,/,%).class
#mkdir -p $(INCDIR)/jni
$(JAVAH) -d inc/jni -classpath TestJNI/bin/tests:$(JUNIT_JAR):$(HAMCREST_JAR) $(basename $(subst _,.,$(subst $(INCDIR)/jni/,,$#)))
The problem is that when a .o file is needed, the first rule is never applied:
$(OBJDIR)/org_some_package_%.cpp: $(INCDIR)/jni/org_some_package_%.h
If I replace this rule with a specific one (by replacing % by the name of a class), it works.
How can I make this generic rule work?
I don't want to do this:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(INCDIR)/jni/%.h
...because I may have .cpp files that do not correspond to JNI header.
You cannot create simple prerequisites using patterns. A pattern rule without any recipe doesn't define a prerequisite, it cancels a previously-existing pattern rule. The GNU make manual says:
You can cancel a built-in implicit rule by defining a pattern rule with the same target and prerequisites, but no recipe.
The only way to create a prerequisite is to make it explicit.
So, your $(INCDIR)/jni/a_b_c.h depend on $(JAVA_TEST_BIN)/a/b/c.class? You will have to use advanced make features. I assume that all *.class file under $(JAVA_TEST_BIN) correspond to a built header file. This (not tested) should do what you show in your question:
CLASSES := $(patsubst $(JAVA_TEST_BIN)/%.class,%,$(shell find $(JAVA_TEST_BIN) -type f -name '*.class'))
BUILT_HEADERS :=
# $(1): class
define BUILD_HEADER_rule
header := $$(INCDIR)/jni/$$(subst /,_,$(1)).h
$$(header): $$(JAVA_TEST_BIN)/$(1).class
BUILT_HEADERS += $$(header)
endef
$(foreach c,$(CLASSES),$(eval $(call BUILD_HEADER_rule,$(c))))
$(BUILT_HEADERS):
#mkdir -p $(dir $#)
$(JAVAH) -d inc/jni -classpath TestJNI/bin/tests:$(JUNIT_JAR):$(HAMCREST_JAR) $(basename $(subst _,.,$(subst $(INCDIR)/jni/,,$#)))

A make rule for verbosity

Typically we have this in a Makefile
%.o:%.c
$(cc) $(flags) -o $# -c $<
When the amount of flags is huge, I feel better to write this instead
%.o:%.c
$(info $(cc): $< --> $#)
#$(cc) $(flags) -o $# -c $<
However it can be useful to sometime see everything. So I defined a variable for that:
at=#
%.o:%.c
$(info $(cc): $< --> $#)
$(at)$(cc) $(flags) -o $# -c $<
My question is how to properly, easily set or unset $(at) from the command line. I see two solutions:
$ make verbose all
$ make verbose=1 all
With the first solution I would do this
ifeq (,$(filter verbose,$(MAKECMDGOALS)))
at=#
dummy:=$(filter-out verbose,$(MAKECMDGOALS)))
endif
With the second I might do this
ifeq (,$(filter 1,$(verbose)))
at=#
endif
Is both solutions acceptable or can I do better?
I generally set up my makefiles like this:
Recipe lines that I know I will never want to see the commands reported begin with # (typically this is just any echo statements or other similar meta-statements).
All other recipe lines do NOT prefix with #.
I add the .SILENT: psuedo-target to make the output silent by default.
I prefix (or suffix) the .SILENT: psuedo-target with a variable reference, like $V.
So, something like this:
%.o: %.c
#echo '$(cc): $< --> $#'
$(cc) $(flags) -o $# -c $<
$V.SILENT:
Now by default V is not set, so the last line expands to the .SILENT special target and no recipe commands are shown. If I run make V=1 (or any other value) then the target expands to 1.SILENT: which is nothing special to make and so is essentially ignored, and all my commands that are NOT prefixed with # are printed.

Makefile target matching

I'm having troubles with my Makefile :-(
I have a mix of assembly and C sourcecode that I need to link together. I need different build-instructions for those two types. Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
This what I'm trying now:
Get a list of all C files and their resulting output files:
C_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.c")
C_OBJFILES := $(patsub %.c,%.o,$(C_SRCFILES))
Get a list of all asm files and their resulting output files:
A_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.asm")
A_OBJFILES := $(patsub %.asm,%.o,$(A_SRCFILES))
When I echo those vars to the screen, they seem to be correct, but how I do define my targets now?
I tried something like this
$(A_OBJFILES): ($A_SRCFILES)
$(AS) $(AFLAGS) -o $# $*
$(C_OBJFILES): ($C_SRCFILES)
$(CC) $(CFLAGS) -c -o $# $*
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $(A_OBJFILES) $(C_OBJFILES) -o $(TARGET_OUTPUT)
but ofcourse, this doesn't work...
Any suggestions?
First problem: a misplaced parenthesis or two.
$(A_OBJFILES): ($A_SRCFILES)
Notice that you have the $ inside the ( in ($A_SRCFILES). Make expands $A, which is nothing, and things go downhill. I think you meant $(A_SRCFILES), and the same thing in the other rule.
Second problem: I don't know the syntax of the assembler, but the syntax of the compiler command is wrong:
$(CC) $(CFLAGS) -c -o $# $*
The variable $* is nothing if we're not in a pattern rule, which we're not (yet). And anyway, if we were in a pattern rule and you were trying to build foo.o, this command would look for the source file foo, and there's no such file. Do it this way:
$(CC) $(CFLAGS) -c -o $# $<
Third problem: each object file depends on all source files (in each rule). Try this instead:
$(A_OBJFILES): %.o : %.asm
...
$(C_OBJFILES): %.o : %.c
...
(Now it's a pattern rule.)
Fourth problem: a lot of redundancy in the last rule. Change it to this:
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $(TARGET_OUTPUT)
or better still:
all: $(TARGET_OUTPUT)
$(TARGET_OUTPUT): $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $#
Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
Sure you can:
%.o : %.c
# commands to make .o from a corresponding .c
%.o : %.asm
# commands to make .o from a corresponding .asm

Makefile adds itself as target

I have a Makefile for a C++ program that uses automatic dependency generation. The %.d recipe is taken from the GNU Make manual.
The problem is that somehow "Makefile" is being added as a target and then an implicit rule is causing it to assume it's an executable and using my src/%.cpp rule to try to compile src/Makefile.cpp. When looking at the debug info, this always happens right after the include is executed.
No need to remake target `build/Sprite.d'.
Considering target file `Makefile'.
Looking for an implicit rule for `Makefile'.
...
Trying pattern rule with stem `Makefile'.
Trying implicit prerequisite `Makefile.o'.
Looking for a rule with intermediate file `Makefile.o'.
I know include causes the given Makefiles to be rebuilt if necessary. Does it also try to rebuild the current Makefile? If so how do I stop it, and if not, then why is "Makefile" being added as a target?
Also, the include is executed, causing the .d files to be remade even if I specify a target on the command line, such as make clean. Is there any way to stop that from happening?
# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))
# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))
MAIN = main
SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/
SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))
CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)
.PHONY: all
all: $(MAIN)
$(MAIN): $(OBJECTS)
$(CXX) $(LIBS) $^ -o $(MAIN)
include $(DEPENDENCIES)
%.o: $(BUILD_DIR)stamp
$(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$#) -o $#
$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
# echo Generate dependencies for $ $#.$$$$; \
sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $# : ,g' $#; \
rm -f $#.$$$$
$(BUILD_DIR)stamp:
mkdir -p $(BUILD_DIR)
touch $#
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
.PHONY: printvars
printvars:
# echo $(SOURCES)
# echo $(OBJECTS)
# echo $(DEPENDENCIES)
Make will always try to remake the Makefile before executing the Makefile. To do so, make will look for rules which can be used to recreate the Makefile. Make will look for quite a few implicit rules and other obscure methods to (re)create the Makefile.
In your case, make somehow decided that the pattern rule %.o: $(BUILD_DIR)/stamp should be used to recreate the Makefile, which failed.
To prevent make from remaking the Makefile you can write a rule with an empty recipe:
Makefile: ;
Read the chapter Remaking Makefiles in the make manual for more explanation.
About the included Makefiles: Included Makefiles will always be included, regardless of the target. If the included makefiles are missing (or older than their prerequisites) then they will first be (re)created. That means a make clean will first generate the .d Makefiles, only to delete them again.
You can prevent the including for specific goals by wraping the include directive in a conditional:
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif
Here is your entire Makefile with some fixes. I marked the places where I changed something.
# Makefile
# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))
# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))
MAIN = main
SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build
SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))
CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)
.PHONY: all
all: $(MAIN)
$(MAIN): $(OBJECTS)
$(CXX) $(LIBS) $^ -o $(MAIN)
# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif
# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
$(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$#) -o $#
# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
# echo Generate dependencies for $#;
#set -e; rm -f $#; \
$(CC) -M $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
$(BUILD_DIR)/stamp:
mkdir -p $(BUILD_DIR)
touch $#
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
.PHONY: printvars
printvars:
# echo $(SOURCES)
# echo $(OBJECTS)
# echo $(DEPENDENCIES)

Resources