Makefile: match multiple pattern rules - makefile

I have a makefile like this:
EXT = .cc
BLD = .build
all: bin/analyses/test
bin/analyses/test: $(BLD)/object.o
.SECONDEXPANSION:
$(DEPS): $(BLD)/%.d: src/%$(EXT) | $(BLD)/$$(dir %)
$(CXX) $(CPPFLAGS) -MM -MT '$(#:.d=.o)' $< -MF $#
$(BLD)/%.o: | $(BLD)
$(CXX) $(CXXFLAGS) -c $(filter %$(EXT),$^) -o $#
bin/%: $(BLD)/%.o | $$(dir bin/%)
$(CXX) $(LDFLAGS) $(filter %.o,$^) -o $# $(LDLIBS)
bin/%/ $(BLD)/%/:
mkdir -p $#
If line 6 looks the way it is, then everything works. Both bin/analyses/test: and bin/%: rules are used. But if I change line 6 to
bin/analyses/%: $(BLD)/object.o
only the bin/%: rule gets picked up.
How can I make multiple pattern rules match for the same target?

First, Make sometimes removes trailing slashes from targets, which can cause some confusion. In this case it takes your rule bin/%/ $(BLD)/%/: ..., which you clearly intended for directories, and uses it for files, at least sometimes. It is easy enough to do without an explicit rule for directories, by using mkdir -p in other rules.
Second, Make doesn't combine pattern rules the way it does ordinary rules. It finds one pattern rule and uses that. In a relatively simple case like this, we can write a rule that will do what we want:
all: bin/analyses/test
$(BLD)/%.o:
mkdir -p $(dir $#)
$(CXX) $(CXXFLAGS) -c $^ -o $#
bin/analyses/%: $(BLD)/analyses/%.o $(BLD)/object.o
mkdir -p $(dir $#)
$(CXX) $(LDFLAGS) $^ -o $# $(LDLIBS)
bin/%: $(BLD)/%.o
mkdir -p $(dir $#)
$(CXX) $(LDFLAGS) $^ -o $# $(LDLIBS)
(There is some obvious redundancy in those last two rules which I don't see how to iron out without making the makefile less readable, but it works as intended.)

Related

Collapsing Makefile rules for compilation

Lets say I had the following two rules:
# C++ source file compilation
$(BIN)/%.o: $(SRC)/%.cpp
#$(MKDIR) $(BIN)
#printf "Compiling ${CYAN}$<${NC}\r\n"
#$(CPP) $(CPPFLAGS) -I$(INC) -o $# $<
$(BIN)/%.o: $(SRC)/*/%.cpp
#$(MKDIR) $(BIN)
#printf "Compiling ${CYAN}$<${NC}\r\n"
#$(CPP) $(CPPFLAGS) -I$(INC) -o $# $<
They only vary by the additional directory wildcard in the prerequisites. How do I collapse them into a single rule that handles the whole directory subtree at $(SRC)/
The simplest thing to do is use VPATH for this:
VPATH = $(SRC) $(wildcard $(SRC)/*/.)
$(BIN)/%.o: %.cpp
#$(MKDIR) $(BIN)
#printf "Compiling ${CYAN}$<${NC}\r\n"
#$(CPP) $(CPPFLAGS) -I$(INC) -o $# $<
I urge you to avoid adding # before all your recipe lines; it leads to a real inability to debug issues.

Makefile multiple targets from same source file, with different flags

I have a binary that I need to build multiple times with different compiler flags. Therefore, I have a Makefile that states something like:
OBJECTS_A := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.a.o))
OBJECTS_B := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.b.o))
OBJECTS_C := $(addprefix $(OBJFOLDER)/, $(SOURCES:.cpp=.c.o))
I also define a rule to change the flags for each OBJECTS_x:
$(OBJECTS_B): DEFINES+=-D_B
$(OBJECTS_C): DEFINES+=-D_C
And this is where the problem happens: If I state the targets separately, as:
$(OBJFOLDER)/%.a.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
$(OBJFOLDER)/%.b.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
$(OBJFOLDER)/%.c.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
All works. However, if I merge all rules into one, only the first is evaluated:
$(OBJFOLDER)/%.a.o $(OBJFOLDER)/%.b.o $(OBJFOLDER)/%.c.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
What I get on a dry run is that only $(OBJFOLDER)/%.a.o objects are build, but on the linking rule each binary requires its objects (and b and c binaries fail to build, therefore).
Any ideas?
Thank you!
You can achieve this using secondary expansion :
.SECONDEXPANSION:
$(OBJFOLDER)/%.o: $$(basename $$*).cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c $< -o $#
Note that this is not a very idiomatic way of doing this, a more usual define / call / eval combo can be used to generate rules as in your first solution :
VARIANTS=a b c
DEFINES_FOR_a=
DEFINES_FOR_b=-D_B
DEFINES_FOR_c=-D_C
define make_target =
$$(OBJFOLDER)/%.$(1).o: %.cpp
$$(COMPILER) $$(CFLAGS) $$(INCFOLDER) $$(DEFINES_FOR_$(1)) -c $$< -o $$#
endef
$(eval $(foreach variant,$(VARIANTS),$(call make_target,$(variant))))
Another way is to create symlinks to your source files and compile those with different flags. This way the same one generic pattern rule (OBJFOLDER)/%.o: %.cpp can build all of your targets:
OBJECTS_A := $(SOURCES:%.cpp=$(OBJFOLDER)/%.a.o)
OBJECTS_B := $(SOURCES:%.cpp=$(OBJFOLDER)/%.b.o)
OBJECTS_B := $(SOURCES:%.cpp=$(OBJFOLDER)/%.c.o)
$(OBJECTS_B): DEFINES+=-D_B
$(OBJECTS_C): DEFINES+=-D_C
%.a.cpp : %.cpp
ln -s $< $#
%.b.cpp : %.cpp
ln -s $< $#
%.c.cpp : %.cpp
ln -s $< $#
$(OBJFOLDER)/%.o: %.cpp
$(COMPILER) $(CFLAGS) $(INCFOLDER) $(DEFINES) -c -o $# $<

Understanding deeply using a specific case how makefiles are interpreted

I'm trying to understand deeply how makefiles work.
For example, I've the following one:
CC = gcc
CFLAGS = -I.
DEPS = int_array.h
OBJS = int_array.o test_int_array.o
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
test_int_array: $(OBJS)
$(CC) -o $# $^ $(CFLAGS)
clean:
rm -rf *.o test_int_array *.dSYM
The part that I really don't understand fully is :
...
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
test_int_array: $(OBJS)
$(CC) -o $# $^ $(CFLAGS)
...
I know that the option -c basically indicates just to run the preprocessor, compiling and assembling steps (i.e. without producing executables, I guess).
-o means to write the output to the specified file. Which file in this case?
I understood that $# (and $^ for right) is apparently referring to a "left" side, but which one? Is it referring, in the first case, to the left side of :, that is %.o?
What does $< mean?
Could you please explain step by step how the make tool would interpret those two statements?
I think I understood this part more or less:
...
test_int_array: $(OBJS)
$(CC) -o $# $^ $(CFLAGS)
...
which should mean produce an executable called "test_int_array" (which basically is indicated by these options -o $# from the $(OBJS) files on the right (stated using the option $^).
Is $(CFLAGS) needed in both cases? Does the order matter?
In the example:
test_int_array: $(OBJS)
$(CC) -o $# $^ $(CFLAGS)
$# is the filename of the target for this rule: test_int_array.
$^ is the names of all prerequisites.
This would be whatever is contained in OBJS, so: int_array.o test_int_array.o
In the example:
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
$< is the name of the first prerequisite: %.c
$# is the filename of the target for this rule: %.o
$(CFLAGS) is not needed for linking, since it only includes the flag -I. Also the CFLAGS indicates that the flags are used for compiling only, hence C FLAGS.
In a Makefile, each rule follows this format:
resulting_file : source_files
steps to get resulting_file from source_files
What is called respectively lefthand and righthand in a rule is the resulting_file and the source_files.
%.ext : %.ext2
is a pattern rule. It allows your Makefile to automatically create any .ext file it needs if it can find a file at the same path with .ext2.
%.c : %.o
is a pattern rule to obtain your .o files (int_array.o test_int_array.o) from their equivalent .c files (int_array.c test_int_array.c)
This is invoked when you specify that $(OBJS) is needed to build the test_int_array file.
Pattern rules automatically use certain variables, such as $(CFLAGS) so you do not need to manually add it in that rule. You can find a full list of implicitly used variables in pattern rules here: https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_10.html#SEC96
You can find out about $#, $< and $^ and similar here: https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_10.html#SEC101
$#: the entire lefthand
$<: the first file in the righthand
$^: the entire righthand list of files, space separated.

makefile - define dependency using variable with objects when building many executables

I'm following great tutorial about ffmpeg (http://dranger.com/ffmpeg) and I'm trying to build a generic makefile for it.
My problem is that I cannot define a generic rule for executables to be depenent on an object of the same name but with ".o" suffix.
Example: when invoked make all I want to build 2 executables tutorial01 and tutorial02 out of 2 files tutorial01.cpp and tutorial02.cpp, but first I want to compile them into *.o and then link them.
My whole Makefile is like so:
CC=g++
CXXFLAGS="-std=c++11"
CXXFLAGS+=`sdl-config --cflags`
LDFLAGS=-L/usr/lib/x86_64-linux-gnu/
LDFLAGS+=-L/lib/x86_64-linux-gnu/
LDFLAGS+=-lavutil-ffmpeg -lavcodec-ffmpeg -lavformat-ffmpeg -lswscale-ffmpeg
LDFLAGS+=`sdl-config --libs`
SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLES=$(SOURCES:.cpp=)
all : $(EXECUTABLES)
# Not working:
#%$(EXECUTABLES) : $(OBJECTS)
# $(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)
#
# Not working (always substitutes the first found):
#$(EXECUTABLES) : $(OBJECTS)
# $(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)
#
# Not working:
#for exec in $(EXECUTABLES) ; do \
#$(exec) : $(exec).o ; \
#done
#
# Working:
#tutorial01:tutorial01.o
#tutorial02:tutorial02.o
#tutorial03:tutorial03.o
%: %.o
$(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)
%.o : %.cpp
$(CC) $(CXXFLAGS) -c $< -o $#
clean:
rm -rf $(OBJECTS) $(EXECUTABLES)
I tried what is stated above as "not working" and also gave an example of what is working but not generic.
# Not working (always substitutes the first found):
$(EXECUTABLES) : $(OBJECTS)
$(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)
This fails because $(OBJECTS) expands to something like tutorial01.o tutorial02.o tutorial03.o for all targets, and $< expands to the first prerequisite, which is the same (tutorial01.o) for all targets.
# Not working:
for exec in $(EXECUTABLES) ; do \
$(exec) : $(exec).o ; \
done
This fails because it is for-loop written in shell syntax. You can write a for-loop in Make syntax, but it is not needed here.
I would use a pattern rule:
tutorial%: tutorial%.o
$(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)
or a static pattern rule:
$(EXECUTABLES): %: %.o
$(CC) $< -o $# $(LDFLAGS) $(LD_LIBS)

multiple targets build in makefile

Any idea why something like this wouldnt work in makefile ?
all : $(GOAL_DB) $(GOAL)
%.d: %.cpp
$(CC) $(CPPFLAGS_DB) $< > $#
%.o : %.cpp
$(CC) $(FLAGS_DB) $< -o $#
$(GOAL_DB) : $(OFILES)
$(CC) $(LFLAGS_DB) -o $# $^ $(LIBS_DB)
strip $(GOAL_DB)
rm -f *.o *.d
%.d: %.cpp
$(CC) $(CPPFLAGS) $< > $#
%.o : %.cpp
$(CC) $(FLAGS) $< -o $#
$(GOAL) : $(OFILES)
$(CC) $(LFLAGS) -o $# $^ $(LIBS)
strip $(GOAL)
rm -f *.o *.d
I'm just trying to build two different targets using make all , GNU make.
The first target builds fine , but it not creating new objects files for another target.
A makefile is not like a standard program that is executed sequentially. It seems you've made the assumption that new rules appearing before a new target will apply to that target. This is not the case. The makefile is fully evaluated for variables, targets, dependencies, and more, before it starts applying rules.
Make is going to match those $(OFILES) against only one of those %.d: targets, probably the first target pattern it finds.
The reason you are not getting new objects for the other target is that to make it looks like you are building the same set of files twice, thus skipping the second build because it's already complete.
A solution is to use 'target specific variables':
all : $(GOAL_DB) $(GOAL)
$(GOAL): BUILD_FLAGS=$(FLAGS)
$(GOAL): BUILD_CPPFLAGS=$(CPPFLAGS)
$(GOAL): BUILD_OUTDIR=./outdir
$(GOAL): $(OFILES)
$(GOAL_DB): BUILD_FLAGS=$(FLAGS_DB)
$(GOAL_DB): BUILD_CPPFLAGS=$(CPPFLAGS_DB)
$(GOAL_DB): BUILD_OUTDIR=./outdir_db
$(GOAL_DB): $(OFILES)
%.d: %.cpp
mkdir -p $(BUILD_OUTDIR)
$(CC) $(BUILD_CPPFLAGS) $< > $(BUILD_OUTDIR)/$#
%.o : %.cpp
mkdir -p $(BUILD_OUTDIR)
$(CC) $(BUILD_FLAGS) $< -o $(BUILD_OUTDIR)/$#

Resources