How do I make an easy-to-update makefile? - makefile

My makefile looks something like this:
FOO_OBJECT_FILES := $(OBJDIR)/Foo.cpp.o
BAR_OBJECT_FILES := $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
ALL_OBJECT_FILES := $(FOO_OBJECT_FILES) $(BAR_OBJECT_FILES)
$(BINDIR)/Foo.a: $(FOO_OBJECT_FILES)
# Rules for making a static library out of Foo's object files go here.
$(BINDIR)/Bar.a: $(BAR_OBJECT_FILES)
# This uses the exact same command sequence as the previous rule.
$(BINDIR)/All.a: $(ALL_OBJECT_FILES)
# Ditto.
# ...
When (not if) more targets are added to the project, the developer will have to update at least three things:
The list of the new target's object files
The list of all object files
Targets for making the new target, even if it uses the same rules as the others
Is there a way to simplify this process, or am I stuck with it?
I tried using wildcard rules, but it doesn't look like they work with macros.
$(BINDIR)/%.a: $(%_OBJECT_FILES)
# ...
You could treat the lists of object files as rules, but then the final target rules can't access them directly.
OBJECT_FILES_Foo: $(OBJDIR)/Foo.cpp.o
OBJECT_FILES_Bar: $(OBJDIR)/Bar.cpp.o $(OBJDIR)Bar.c.o
OBJECT_FILES_All: FOO_OBJECT_FILES BAR_OBJECT_FILES
$(BINDIR)/%.a: OBJECT_FILES_%
# This rule can't see into the object file lists to use them to build.
Is there no better way?

There are probably plenty of ways to do this. One such way is the following. All that needs to be done for a new target is add its name to the list of modules, and give the list of dependencies for it.
BINDIR := bin
OBJDIR := obj
MODULES := Foo Bar
Foo_OBJS := $(OBJDIR)/Foo.cpp.o
Bar_OBJS := $(OBJDIR)/Bar.cpp.o $(OBJDIR)/Bar.c.o
#####################################################
# #
# Nothing below here should need to be altered. #
# #
#####################################################
All_OBJS := $(foreach mod, $(MODULES),$($(mod)_OBJS))
define rule
$(BINDIR)/$(1).a: $($(1)_OBJS)
#echo
#echo 'Target: $$#'
#echo 'Deps : $$^'
endef
$(foreach lib, All $(MODULES), $(eval $(call rule,$(lib))))
###########################################
# #
# The next part is just here for testing. #
# #
###########################################
.PHONY: all
all: $(foreach lib, All $(MODULES),$(BINDIR)/$(lib).a)
%.o:
#echo Making $#

You can't do much about 1 and 2, those are arbitrary things that Make cannot possibly deduce. You can improve 3 slightly:
$(BINDIR)/%.a:
# commands for making a static library
# adding a new target:
QUARTZ_OBJECT_FILES := $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o
ALL_OBJECT_FILES += $(QUARTZ_OBJECT_FILES)
$(BINDIR)/Quartz.a: $(QUARTZ_OBJECT_FILES)
You could use a template to reduce those three lines to one:
$(eval $(call template, QUARTZ_OBJECT_FILES, $(OBJDIR)/Quartz.cpp.o $(OBJDIR)Arbitrary.o))
but is it really worth it?

While the other answers have provided good solutions for manual makefile writing, you could simply use automake to ease the build process.

Related

Universal make-based build system design

I am aware of tools like CMake and GNU Autotools but I'm trying to write a universal build system myself, to use for my C and C++ projects. I'll briefly explain how it works and hopefully, someone can suggest either improvements or a better design altogether.
The build system proper lives in one of the project's subdirectories (I import it as a Git submodule). The project's root directory has a wrapper makefile that defines a couple of macros and includes the main makefile from said subdirectory. That does most of the work: it follows the directory organization scheme (i.e., it outputs libraries in lib, binaries in bin, etc.), it handles automatic dependencies for the source code and the DocBook documentation, and provides the de facto standard targets: all, test, clean, install, as well as others.
Here's what a wrapper makefile that builds two binaries, foo and bar, might look like:
# foo-specific macros
FOO_SRC_FILES = foo1.c foo2.c foo3.c
FOO_OBJ_FILES = $(FOO_SRC_FILES:.c=.o)
FOO_BIN_FILE = foo
# bar-specific macros
BAR_SRC_FILES = bar1.c bar2.c
BAR_OBJ_FILES = $(BAR_SRC_FILES:.c=.o)
BAR_BIN_FILE = bar
# Inform the build system about them
SRC_FILES = $(FOO_SRC_FILES) $(BAR_SRC_FILES)
OBJ_FILES = R(BAR_OBJ_FILES) $(BAR_OBJ_FILES)
BIN_FILES = $(FOO_BIN_FILE) $(BAR_BIN_FILE)
# Only install the binaries. If I were building a library, I would instead
# select the "lib" and perhaps "include" directories.
INSTALL = bin
INSTALL_DIR = /usr/share
# Use the build system
include build/build.mk
Now here's the problem. While build.mk can use pattern rules to create dependency and object files, there's only one OBJ_FILES and only one BIN_FILES. So if I put a pattern rule like the following in the build system that looks like this:
$(BIN_DIR)/$(BIN_FILES): $(OBJ_FILES:%=$(OBJ_DIR)/%) $(LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(LIB_FILES:lib%.a=-l %)
then foo would depend on and link with everything that bar does and vice versa. So what I end up doing is asking the user to put these rules in the wrapper makefile, even though they feel like they belong in build.mk:
$(BIN_DIR)/$(FOO_BIN_FILE): $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) $(FOO_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(FOO_LIB_FILES:lib%.a=-l %)
$(BIN_DIR)/$(BAR_BIN_FILE): $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) $(BAR_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(BAR_LIB_FILES:lib%.a=-l %)
The same issue applies to libraries as well, of course. The upside is that these rules can be copied and pasted almost verbatim; only the prefixes need to be changed (e.g., FOO or BAR).
Ideas to fix this include:
Asking the user to have separate wrapper makefiles for separate things (e.g., one for foo and another for bar) but that is just terrible.
Changing things up a little bit and then using m4 to do some preprocessing but I don't want to go through that unless a more elegant solution doesn't exist.
I would really appreciate some ideas.
PS: I know that the pattern matching expressions in the last two code samples can be replaced with text functions but those are GNU Make-specific. The style I used is more portable and is in fact on the list of additions for the next version of the POSIX standard.
I have begin to develop a similar system for my own C projects, but the logic I use does rely on some features which I believe are specific to GNU Make.
The main idea is to use a combinaison of $(eval) and $(call), by defining the logic of the build system, and then applying to the project tree.
To do so, I have in each of my directories and subdirectories a piece of Makefile of the following form, which I name Srcs.mk:
SRC := foo.c foo_bar.c bar.c
TARGET := foo_bar
SRC_DIR := src
OBJ_DIR := obj
I define a variable, which is in fact a macro, which is expanded with $(call) and then passed to $(eval). It's defined this way:
define get_local_variables
include Srcs.mk
$1SRC := $(SRC)
$1SRC_DIR := $(SRC_DIR)
$1OBJ_DIR := $(OBJ_DIR)
$1TARGET := $(TARGET)
TARGET :=
SRC :=
SRC_DIR :=
OBJ_DIR :=
$(call get_local_variables, $(DIR)) will expand to the above, with $1 replaced by the content of $(DIR). Then it will be treated as a Makefile fragment by $(eval)
This way, I fill per-directory variables for each of my directory.
I have then a handful or other rules which use this variables, using the same principles.
### Macros ###
obj = $(patsubst %.c,$($1OBJ_DIR)/%.o,$($1SRC))
define standard_rules
$($1TARGET): $(obj)
$$(LINK)
$(obj): $($1OBJ_DIR)/%.o:$($1SRC_DIR)/%.c | $($1OBJ_DIR)
$$(COMPILE)
endef
The variable are computed $(call), then expanded and read as makefile fragments by $(eval).
(I use static pattern rules but that it not intrinsic to the idea).
The whole idea is basically to define directories as a kind of namespace, with data attached to them, and then run function over them.
My actual system is a bit more complicated, but that the whole idea.
If you have a way to emulate $(eval) and $(call) (I think these are specific to GNU make, but not sure), you could try that approach.
You can also implement non recursive make this way, by adding a SUBDIRS variables in each directory and running recursively the same macro which is run on the current one. But it should been done carefully, not to mess it up with the order of expansion and evaluation in make.
So get_local_variables need to be evaluated before the rest of the macros are expanded.
(My project is visible on my Github account if you want to take a look, under make-build-system. But it is far from be complete enough^).
Be aware, though, that this is quite painful to debug when things go wrong. Make (at least, GNU) basically catch the error (when there is one) on the higher $(call) or $(eval) expansion.
I have developed my own non-recursive build system for GNU make, called prorab, where I solved the problem you described as follows.
The approach to solve your problem is somewhat similar to what #VannTen described in his answer, except that I use a macro to clean all state variables before defining build rules for the next binary.
For example, a makefile which builds two binaries could look like this:
include prorab.mk
this_name := AppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main1.cpp MyClass1.cpp
$(eval $(prorab-build-app))
$(eval $(prorab-clear-this-vars))
this_name := AnotherppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main2.cpp MyClass2.cpp
$(eval $(prorab-build-app))
So, in this example it will build two binaries: AppName and AnotherppName.
As you can see the build is configured by setting a number of this_-prefixed variables and the calling the $(eval $(prorab-build-app)) which expands to defining all the build, install, clean etc. rules.
Then a call to $(eval $(prorab-clear-this-vars)) clears all this_-prefixed variables, so that those can be defined again from scratch for the next binary, and so on.
Also, the very first line which includes the prorab.mk also cleans all this_-prefixed variables of course, so that the makefiles can be safely included into each other.
You can read more about that build system concepts here https://github.com/cppfw/prorab/blob/master/wiki/HomePage.adoc

Make not reevaluating dependency variable

I have a Makefile designed to build a few different programmes. My SOURCES and OBJECTS depend on PRODUCT, which should be the product being built. At the moment, I set PRODUCT before listing the dependency on OBJECTS on each rule:
CORE_LIB := libcore
SCANNER := scanner
FLETCHER := fletcher
PRODUCT := $(SCANNER)
SOURCE_DIR := ./src
BUILD_DIR := ./build
OBJECTS_DIR := $(BUILD_DIR)/intermediate
PRODUCT_DIR := $(BUILD_DIR)/product
SOURCES = $(wildcard $(SOURCE_DIR)/$(PRODUCT)/*.c) $(wildcard $(SOURCE_DIR)/$(PRODUCT)/*/*.c)
OBJECTS = $(SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# change PRODUCT based on rule being ran
$(SCANNER): PRODUCT := $(SCANNER)
$(SCANNER): $(OBJECTS)
# link stuff
$(CORE_LIB): PRODUCT := $(CORE_LIB)
$(CORE_LIB): $(OBJECTS)
# link stuff
However, OBJECTS keep being evaluated before PRODUCT is set, and never changes based on the rule. Is there anything I'm missing?
You can't do that. The GNU make manual states, regarding target-specific variables:
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
This means that you can't use target-specific variable values during the expansion of prerequisites, like $(OBJECTS). Or rather, you can use any variable there but the target-specific value is not available. Since you set the value of PRODUCT globally to be $(SCANNER), which is scanner, that's the value that will be used in all prerequisites.
You have multiple options. You can just define multiple variables, like this:
$(SCANNER)_SOURCES = $(wildcard $(SOURCE_DIR)/$(SCANNER)/*.c) $(wildcard $(SOURCE_DIR)/$(SCANNER)/*/*.c)
$(CORE_LIB)_SOURCES = $(wildcard $(SOURCE_DIR)/$(CORE_LIB)/*.c) $(wildcard $(SOURCE_DIR)/$(CORE_LIB)/*/*.c)
Then create rules like this:
$(SCANNER): $($(SCANNER)_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# link stuff
$(CORE_LIB): $($(CORE_LIB)_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
# link stuff
If you have more of these, you could create a macro and use eval but I wouldn't bother with the complexity unless you really are going to have a bunch of them.
You can simplify the target definitions a bit using Secondary Expansion, like this:
OBJECTS = $$($$#_SOURCES:$(SOURCE_DIR)/%.c=$(OBJECTS_DIR)/%.o)
.SECONDEXPANSION:
$(SCANNER): $(OBJECTS)
# link stuff
$(CORE_LIB): $(OBJECTS)
# link stuff

nested defines in makefile

I'm very new to creating makefiles, so this question might be trivial. If I'm using bad coding practices, I would appreciate to be pointed at it.
I am trying to create a generic makefile that creates rules based on input variables. At the end of my posting is a "minimal" example.
I got 2 problems.
In line 35 I instantiate the meta_template. I would expect the variable $(Dirs) to be expanded and then the meta_template to be called with each entry of $(Dirs). But it only expands till the second last, ie. the result of make terminates with:
make: *** No rule to make target 'Dir03Target01Tag01', needed by 'Dir03'. Stop.
In line 21 I want to create a shortcut rule, a rule that prerequisites all tags to a given example.
Eg: Dir01Target02: Dir01Target02Tag04 Dir01Target02Tag05
First I was puzzled that I couldn't use $($$(1)Tags) but had to use $$($$(1)Tags) to access the according Tags. (stored in Target01Tags for example).
Using $(addprefix) does not seem to expand as I expected, ie the result of make Dir01Target01 is:
Create Rule Dir01Target01 with Dir01Target01Tag01 Tag02 Tag03
where I expected it to be:
Create Rule Dir01Target01 with Dir01Target01Tag01 Dir01Target01Tag02 Dir01Target01Tag03
edit: I found the answer to question 2. I had $(addprefix $(1)$$(1),$$($$(1)Tags))alter to $$(addprefix $(1)$$(1),$$($$(1)Tags)). I think it's because this needs to be expanded in 2 runs. So after the first run $(addprefix Dirxx$(1),$($(1)Tags)) remains.
Thanks in advance.
# Targets to compile
Targets = Target01 Target02
# Tags to add to Targets
Target01Tags = Tag01 Tag02 Tag03
Target02Tags = Tag04 Tag05
# Some List (in the actual makefile a list of directories $(wildcard NumeratedDir*))
Dirs = Dir01 Dir02 Dir03
all: $(Dirs)
define meta_template
$(1): $(foreach target,$(Targets),$(foreach tag,$($(target)Tags),$(1)$(target)$(tag)))
define compile_meta
# $(1) 1st argument of meta_compile (<Dirs>xx, eg Dir03)
# $$(1) 1st argument of compile_template (<Targets>, eg Target01)
$(1)$$(1): #$(addprefix $(1)$$(1),$($$(1)Tags))
#echo Create Rule $(1)$$(1) with $(addprefix $(1)$$(1),$$($$(1)Tags)) <---- Line21
endef
$(foreach target,$(Targets),$(eval $(call compile_meta,$(target))))
define compile_template
# $(1) 1st argument of meta_compile (<Dirs>xx, eg Dir03)
# $$(1) 1st argument of compile_template (<Targets>, eg Target01)
# $$(2) 2nd argument of compile_template (<Targets>Tags, eg Tag01)
# create rule using arguments (eg Dir03Target01Tag01)
$(1)$$(1)$$(2):
#echo Create Rule $(1)$$(1)$$(2)
endef
$(foreach target,$(Targets),$(foreach tag,$($(target)Tags),$(eval $(call compile_template,$(target),$(tag))))) <--- Line35
endef
$(foreach prog,$(Dirs),$(eval $(call meta_template,$(prog))))

Generic make rules using define

I'm trying to create a common template to generate rules build a set of test cases and place them in unique target locations but have hit a bit of snag with the define directive. The following is the relevant section of my Makefile:
ROOT=../..
PLATFORM=akyboard_gcc
# include all the test cases for the current platform
# They add to the TEST_CASES variable
TEST_CASES=A B
A_FILES = a1.c a2.c
B_FILES = b1.c b2.c
check: $(TEST_CASES:%=check_%)
define CHECK_template
# build artifact directories
$(1)_BLDDIR=$(ROOT)/build/$(PLATFORM)/$(1)
$(1)_OBJDIR=$$($(1)_BLDDIR)/obj
$(1)_EXEDIR=$$($(1)_BLDDIR)/exe
# prepend src/ to all the files that are part of the test case
$(1)_FILES := $($(1:%=%_FILES):%=src/%)
# add the test runner as one of the files to be compiled
$(1)_FILES += test_runner/$(PLATFORM)/main.c
# construct list of objects generated by sources
$(1)_OBJ = $($(1)_FILES:%.c=$$($(1)_OBJDIR)/%.o)
# This creates a rule such as check_{test_case}:
check_$(1): $$($(1)_OBJ)
#echo 1 $(1)
#echo 2 $$($(1)_FILES)
#echo 3 $$($(1)_OBJ)
#echo 5 $$($(1)_OBJDIR)
#echo 4 $$($(1)_BLDDIR)
#echo 6 $$($(1)_EXEDIR)
$$($(1)_OBJDIR)/%.o: $(ROOT)/%.c
#echo coconut
endef
$(foreach testcase, $(TEST_CASES), \
$(eval $(call CHECK_template,$(testcase))) \
)
Issuing "make check" gives the following error
*** No rule to make target `../../build/akyboard_gcc/A/obj/a1.o', needed by `check_A'. Stop.
If I manually create the target rules below it builds without errors
../../build/akyboard_gcc/A/obj/a1.o:
../../build/akyboard_gcc/A/obj/a2.o:
../../build/akyboard_gcc/B/obj/b1.o:
../../build/akyboard_gcc/B/obj/b2.o:
But changing rules like below causes the build error:
../../build/akyboard_gcc/A/obj/%.o:
Would be grateful for any help.
You can find what wrong with your template by replacing eval with info call.
$(foreach testcase, $(TEST_CASES), $(info $(call CHECK_template,$(testcase))))
And your template is fine. But you probably have a problem with pattern rules.
From GNU make manual:
A pattern rule can be used to build a given file only if there is a
target pattern that matches the file name, and all prerequisites in
that rule either exist or can be built.
Perhaps you don't have required sources in $(ROOT) directory. And make just can't create a rule for object file from pattern.

GNU make: build all sources in a different directory

Given:
programs := apps/prog1 apps/prog2 # the actual list is quite long
sources := src/prog1.cpp src/prog2.cpp # showing only 2 files
Make file has 2 targets release and debug. Each target should build every program in bin/ directory and appends target name to the file name.
For example, building release should create bin/prog1_release and bin/prog2_release.
How to write static pattern rule to do it?
Thanks.
This will do it (in GNUMake 3.81):
BINS := $(patsubst apps/%,bin/%,$(programs)) # bin/prog1 bin/prog2 ...
release_bins := $(addsuffix _release,$(BINS)) # bin/prog1_release ...
debug_bins := $(addsuffix _debug,$(BINS)) # bin/prog1_debug ...
$(release_bins): bin/%_release: src/%.cpp
#build the binaries according to the release rule
$(debug_bins): bin/%_debug: src/%.cpp
#build the binaries according to the debug rule
release: $(release_bins)
debug: $(debug_bins)
.PHONY: release debug
# If it turns out that one of the progs needs something else too:
bin/prog20_debug: somethingElse.cpp
(There are ways to make this slightly more concise, but at the cost of clarity.)

Resources