Fawkes Build System
Table of Contents
The Fawkes build system has evolved over time to allow for several typical use cases while leveraging the power and flexibility of GNU Make to allow for more complex scenarios as the arise. Read this to understand how to write a Makefile used with the AllemaniACs' Fawkes build system. The build system is basically meant to make the things done often very easy while making complex task possible.
After a short introduction to the different supported basic build types we will describe the basic Makefile structure and name a few conventions. Then we will give a simple example and show how to influence the build parameters.
If you start reading about the build system you most likely also want to read about the common build system patterns in FawkesBuildSystemPatterns. A a detailed description of the build process is given in FawkesBuildProcess and it is strongly recommended that you read this if want to break out of the standard patterns or if you want to customize the build system. Especially if you cannot use the standard patterns read this!
Build types
There are basically three build types in the Fawkes build system that we are going to introduce now.
- Sub-directories only
- Concrete targets
- Hybrid concrete targets and sub-directories
Sub-directories only
This can be considered to be an infrastructure Makefile. In general it us used on the way to the real targets, for example in the src/Makefile and src/lib/Makefile Makefiles. It contains a list of sub-directories to process and optional ordering constraint targets. See FawkesBuildSystemPatterns for a sub-directory-only Makefile pattern.
Concrete targets
These targets are used to build libs, binaries and plugins and use the basic templates provided by the build system. This is the Makefile type that you will find the most throughout the software and that we are going to show in the example below.
Hybrid Makefile
This is a combination of the two former types. This can be used if you want to build a few subdirectories and then some concrete targets. By default the concrete targets are build first, then the sub-directories are traversed. Use ordering constraints by adding dependencies to the subdirs target.
Basic structure
The basic Makefile structure is pretty simple.
BASEDIR = ../../.. OBJECT definitions CFLAGS LDFLAGS OBJS_all = ... BINS_all = ... include $(BASEDIR)/etc/buildsys/base.mk
The BASEDIR has to be set to the relative path to the top directory of your Fawkes working copy (i.e. up to something like $(HOME)/fawkes).
The last include line is always the same and may not be changed. Now read on how to define objects that should be compiled and what possibilities you have to influence the compiler and linker parameters.
There are a couple of variables involved when defining a new application or plugin that is to be built. The parameters that can be set are discussed below, here we just discuss how to define a new build target.
In general it is a good idea to start with the Makefile from another similar directory (another plugin's Makefile if you write a plugin, another lib's Makefile if you write a lib etc.).
Conventions
There are a few conventions that you shall follow to make the thing work.
Dependency detection
Dependencies shall be detected during build and the module shall be gracefully skipped if a dependency fail.
The one exception are base libraries in src/libs. These libs and only these libs shall cause a critical failure if they cannot be built. See FawkesBuildSystemPatterns for these patterns.
Sub-directory naming
Sub-directories shall be named in all lowercase containing only alpha-numeric characters, underscore, dash and dot.
It is important to use sub-directory names that are compatible with make in the sense that they can be used as make targets. Because of this sub-directories may not be named like any special target make provides (see make manual).
QA application naming
QA applications shall always start with a qa_ prefix, followed by another module-dependent collision-free module_ prefix.
The qa_ prefix is needed to make the QA applications easy separable from the rest. The module_ prefix depends on the module name, for example the utils use utils_. So in the end your name looks like qa_mymodule_testcase.
Basic example
Assume that you want to build a new application called test which consists of two source files main.cpp and regression.cpp. We want this to be built as test in the global Fawkes bin directory. You can accomplish this by defining the objects as
OBJS_test = main.o regression.o OBJS_all = $(OBJS_test) BINS_all = $(BINDIR)/test include $(BASEDIR)/etc/buildsys/base.mk
This will have the desired results.
Mandatory ending
At the end of the Makefile you then define OBJS_all as a concatenation of all objects mentioned in the file. This is needed to avoid erasion of the .o file after successful compilation! If your programs builds fast you may even leave this out which would cause the object files to be rebuilt every single time.
Binary declaration
The BINS_all line defines all applications that you want to build. It MUST be prefixed by $(BINDIR), otherwise the build system will not recognize it and will not know how to build it.
Compiler and linker flags
Now imagine that this application needs some special compiler and linker flags. In a Makefile we distuinguish two different types of parameters. First we have program-specific options and then we have object-specific options. Program-specific options include
- LIBS_
- LIBDIRS_
Object-specific parameters include
- INCS_
For program-specific options you name the variable with the prefix given above and a program-specific suffix. The object-specific options are defined per object. In our example we might want regression.cpp to be compiled with a special include to an external lib that we downloaded and of course we want the program to be link against it. This could be expressed as:
SUPERLIB = $(HOME)/dev/superlib-0.4 LIBS_test = superlib LIBDIRS_test = $(SUPERLIB)/lib INCS_regression = $(SUPERLIB)/include
This is especially handy if you have multiple applications or plugins defined in just one Makefile. There are also global forms of this options which apply to all applications and plugins in the Makefile. Using these global options the superlib inclusion above would look like this:
SUPERLIB = $(HOME)/dev/superlib-0.4 LIBS = superlib LIBDIRS = $(SUPERLIB)/lib INCDIRS = $(SUPERLIB)/includes
Build system variables per module
There are several variables that influence the behavior of the build system for a specific module. These are:
| Variable | Applies to | Description |
| CFLAGS | compilation | global CFLAGS for whole module, added to CFLAGS_BASE in config |
| CFLAGS_Y | compilation of specific file Y | CFLAGS that are used only for compiling file Y |
| INCDIRS | compilation | space-separated list of include directories, added to CFLAGS with -I prepended |
| INCS_Y | compilation of specific file Y | list of include directories only used for compiling file Y |
| LDFLAGS | linking | linker flags used for whole module |
| LDFLAGS_Y | linking of module Y | linker flags used only for linking the module Y |
| LIBS | linking | list of libs used for whole module |
| LIBS_Y | linking of module Y | list of libs to link module Y against (prepended with -l) |
| LIBDIRS | linking | list of directories searched for libraries for whole module |
| LIBDIRS_Y | linking of module Y | list of directories searched for libraries for module Y |
Basic definitions passed
There are a few defines that are passed to the compilation process by default. These are:
| Definition | Meaning |
| BINDIR | set to the directory where the compiled binaries reside (defaults to $(BASEDIR)/bin |
| LIBDIR | set to the directory where the compiled libraries reside (defaults to $(BASEDIR)/lib |
| PLUGINDIR | set to the directory where the compiled plugins reside (defaults to $(BASEDIR)/plugins |
| CONFDIR | set to the directory where the config files reside (defaults to $(BASEDIR)/cfg |

