Makefile with two targets and separate build folders - makefile

I am trying to use one Makefile with two similar targets and two separate build folders. The only difference between the targets is the addition of a CFLAG define.
Here is a snippet of what I have, however I can't get the build folder to evaluate to something different depending on the target. I used foo and bar to represent the different targets.
...
foo_BUILD_DIR := build_foo/
bar_BUILD_DIR := build_bar/
C_SRCS := main.c
CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1
C_OBJS := $(addprefix $(BUILD_DIR),$(subst .c,.o,$(C_SRCS)))
$(BUILD_DIR)%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
foo:$(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o $#
bar:$(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o $#

You have three problems to solve here.
The first is building object files in the build directories. First we write a pattern rule to build foo objects:
foo_BUILD_DIR := build_foo # Don't put the trailing slash in the dir name, it's a pain.
CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1
$(foo_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(foo_CFLAGS) $^ -o $#
Once that's working perfectly, we write another rule for the bar objects:
$(bar_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
The second problem is tidying up what we've written so far. That foo_CFLAGS variable is now the only thing making these two recipes different, so let's get rid of it with a target-specific variable assignment:
$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1
$(foo_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
Now we can combine the rules into one pattern rule with two targets:
$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1
$(foo_BUILD_DIR)/%.o $(bar_BUILD_DIR)/%.o: %.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) $^ -o $#
The third problem is getting the foo and bar rules to require the right objects. Obviously this rule:
foo:$(OBJS)
...
won't work, we need something specific to foo:
foo: $(addprefix $(foo_BUILD_DIR)/, $(OBJS))
...
This works, but it requires us to write a foo rule that specifies $(foo_BUILD_DIR), a bar rule that specifies $(bar_BUILD_DIR), and so on. Is there a lazier way? All we need is to take the target (e.g. foo) and get it into the prerequisite list. As you know, the automatic variable $# contains the target, but it isn't available in the prerequisite list, because the prerequisite list is expanded before a value is assigned to that variable-- unless we use Secondary Expansion. This is an advanced technique, but it lets us do a second expansion in the later phase (escaping our variables with an extra $ to protect them from the first expansion):
.SECONDEXPANSION:
foo: $(addprefix $$($$#_BUILD_DIR)/, $(OBJS))
...
And once that's working, we can add another target, or as many as we want:
foo bar: $(addprefix $$($$#_BUILD_DIR)/, $(OBJS))
...
There are one or two more refinements possible, but this is enough to start with.

Related

How do you call a rule that involves `%` from another rule in a Makefile?

I have the following makefile snippet to compile my C++ project.
obj/%.o: src/%.cpp
$(CXX) $(CFLAGS) -c $< -o $#
Now I want to link the .o files. But I want to be able to just call the rule name of the link, like make build/main, in order to compile AND link.
Neither this:
build/main: $(wildcard obj/*.o)
$(CXX) $^ -o $#
works, as it only links and does not compile, as I would expect by this answer;
nor this:
build/main: obj/%.o
$(CXX) $^ -o $#
with the error:
No rule to make target 'obj/%.o', needed by 'build/main'. Stop.
even though it is needed.
SOURCES := $(wildcard src/*.cpp)
OBJECTS := $(patsubst src/%.cpp, obj/%.o, $(SOURCES))
build/main: $(OBJECTS)
$(CXX) $^ -o $#

Single make rule for multiple output files in different directories

Lets say I have one directory with c files (.) and I want the object files to end up in two different directories: debug and release. Now I want to make the rule for this. This will end up being something like this:
$(DEBUGDIR)%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
$(RELEASEDIR)%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
These are two rules which are exactly the same. Since all possible differences between those rules are contained in CFLAGS there is no reason to even have two rules: I want to maintain only one. I tried this:
$(DEBUGDIR)%.o $(RELEASEDIR)%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
But now it will not compile the o-files for RELEASE when it already compiled for DEBUG. How can I merge these two rules into one?
You can't do it. Pattern rules with multiple targets define a recipe that creates multiple output files when run one time.
For this I'd just write the rule twice. Alternatively you can put the recipe into a variable and use it twice:
COMPILE = $(CC) $(CFLAGS) -c $< -o $#
$(DEBUGDIR)%.o : %.c
$(COMPILE)
$(RELEASEDIR)%.o : %.c
$(COMPILE)

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 with multiple directories and dependencies

``I am pretty new to Makefile and I am trying to build a project.
This is the structure of my project:
-Project
|-generic
| |-include
| |-src
|-specific
| |-include
| |-src
|-Makefile
|-bin
Here generic contains mainly interface classes (virtual), and files in specific may depends on those.
I want to compile all sources and put their corresponding .o files in bin and also create a static library in bin.
EDIT:
CC=k1-g++
CPPFLAGS=-c -I$(GENERIC_INCLUDE_DIR) -I$(SPECIFIC_INCLUDE_DIR) -Os -std=gnu++11 -mos=nodeos
CXXFLAGS=-c -I$(GENERIC_INCLUDE_DIR) -I$(SPECIFIC_INCLUDE_DIR) -Os -std=gnu++11 -mos=nodeos
LFLAGS=-pthread -lnodeos
GENERIC_INCLUDE_DIR=generic/include
SPECIFIC_INCLUDE_DIR=specific/include
GENERIC_SRC_DIR=generic/src
SPECIFIC_SRC_DIR=specific/src
LIB = libengine.a
BIN_DIR=bin
vpath %.cpp $(GENERIC_SRC_DIR) $(SPECIFIC_SRC_DIR)
SOURCES := $(wildcard $(GENERIC_SRC_DIR)/*.cpp $(SPECIFIC_SRC_DIR)/*.cpp)
#SOURCES := $(wildcard $(GENERIC_SRC_DIR)/*.cpp $(SPECIFIC_SRC_DIR)/*.cpp)
SOURCES := $(notdir $(SOURCES))
OBJECTS := $(patsubst %.cpp,$(BIN_DIR)/%.cpp.o,$(SOURCES))
all: $(OBJECTS) $(LIB)
$(LIB): $(OBJECTS)
ar -cvq $(BIN_DIR)/$# $^
#$(BIN_DIR)/%.cpp.o: $(GENERIC_SRC_DIR)/%.cpp
# $(CC) $(CPPFLAGS) $< $(LFLAGS) -o $#
$(BIN_DIR)/%.cpp.o: %.cpp
$(CC) $(CPPFLAGS) $< $(LFLAGS) -o $#
.PHONY: clean
clean:
rm -f $(BIN_DIR)/*
Any help would be appreciated
Suppose you have generic/src/foo.cpp and specific.src/bar.cpp.
This:
$(BIN_DIR)/%.o: %.cpp
$(CC) $(CPPFLAGS) $*.cpp -o $#
is pretty close to what you need (I've replaced $(BIN_DIR)/$*.o with the automatic variable $#, which expands to the name of the target); the only problem is that it doesn't work. This rule tells Make it can build obj/foo.o from foo.cpp, but there is no foo.cpp. There's a generic/src/foo.cpp, but Make doesn't know that that's what you meant. We could write a rule like this:
$(BIN_DIR)/%.o: $(GENERIC_SRC_DIR)/%.cpp
$(CC) $(CPPFLAGS) $*.cpp -o $#
But a tidier way is to use the vpath directive:
vpath %.cpp $(GENERIC_SRC_DIR) $(SPECIFIC_SRC_DIR)
$(BIN_DIR)/%.o: %.cpp
$(CC) $(CPPFLAGS) $*.cpp -o $#
This will do nicely for building any one object file, but you say you want to compile "all sources". That isn't always a good idea, but if that's what you want it isn't hard, we use wildcard to obtain a list of all the sources, then convert that into a list of corresponding object files, then build them all:
SOURCES := $(wildcard $(GENERIC_SRC_DIR)/*.cpp $(SPECIFIC_SRC_DIR)/*.cpp)
SOURCES := $(notdir $(SOURCES))
OBJECTS := $(patsubst %.cpp,%.o, $(SOURCES))
# We could have done that all in one line, but this way is easier to read.
all: $(OBJECTS)
Now for the library:
vpath %.o $(BIN_DIR)
$(BIN_DIR)/$(LIB): $(OBJECTS)
ar -cvq $# $^
Further refinements are possible, but that should keep busy for a while.

Makefile does not work properly

I've this folder structure
project
|_src
| |_test
| |_main.cpp
|_Makefile
This is my makefile (trying to adapt from this link):
CC = g++
RM = rm
WFLAGS = -c -Wall -W
LDFLAGS =
SRCTESTD = src/test
EXECUTABLE = test
OBJD = .obj
DEPD = .dep
SRCSTEST = $(SRCTESTD)/main.cpp
OBJECTSTEST = $(patsubst %.cpp, $(OBJD)/test/%.o, $(notdir $(SRCSTEST)))
DEPDSTEST = $(patsubst %.cpp, $(DEPD)/test/%.d, $(notdir $(SRCSTEST)))
all: $(SRCSTEST) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTSTEST)
$(CC) $(LDFLAGS) $(OBJECTSTEST) -o $#
.cpp.o:
$(CC) $(WFLAGS) $< -o $#
It does not work, and I've this error
make: *** No rule to make target `.obj/test/main.o', needed by `test'. Stop.
What I'm doing wrong? Sorry for trivial question, but I'm a make newbie.
The link shows outdated methods, such as suffix rules. Making dependencies can also be done during compilation by gcc/g++.
As for the rest, here is it :
EXE := test
SRCDIR := src
OBJDIR := .obj
SRC := $(shell find $(SRCDIR) -name "*.cpp")
OBJ := $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
DEP := $(OBJ:.o=.d)
LDLIBS := # -l flags
LDFLAGS := # -L flags
CPPFLAGS := -MMD -MP # -I flags also
CXXFLAGS := -W -Wall # no -c flag here
.PHONY: all clean fclean re
all: $(EXE)
clean:
$(RM) -r $(OBJDIR)
fclean: clean
$(RM) $(EXE)
re: fclean all
-include $(DEP)
$(EXE): $(OBJ)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
No redefinition of internally defined variables, no suffix rules, correct linking step and dependencies generation.
Update: To avoid calling mkdir for every source file, one should use order-only prerequisites and the special target .SECONDEXPANSION.
Change this block:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
To this:
.SECONDEXPANSION:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $$(#D)/
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $# -c $<
%/:
mkdir $*
The error means make can't find a correct rule to build your object files. Your tree structure lacks some informations: only one file ? Where are the others ? Anyway, here are some hints:
In the last two lines, you are using an obsolete feature of make: suffix rules. I suggest you switch to a pattern rule, which is functionaly equivalent.
Say something like:
%.o: %.cpp
$(CXX) $(CXXFLAGS) $< -o $#
Another thing (that shouldn't be a problem here): you are using the variable CC which is internally defined as the default C compiler. It's okay because you redefine it, but as your sources seem to be C++ files, why not use the variable CXX, that is internally defined as the C++ compiler ?
Lastly, to make sure your set of files are correctly defined, you can print them with a dummy show target, see here.
show:
#echo "OBJECTSTEST=$(OBJECTSTEST)"
...

Resources