wiki:FawkesBuildSystemPatterns
Last modified 11 years ago Last modified on 07.04.2008 00:17:23

Fawkes Build System Patterns

There are a few things that you may come across when using the build system where a solution does not seem obvious but that we have well known recipes for. To avoid searching around in the software to find out if someone already did what you are about to do we have collected some of these recipes on this page.

Dependency check

Many modules have one or more extra dependencies. This may be an external library or a SDK from a vendor of a piece of hardware that the module uses. As already mentioned in the conventions these dependencies shall be checked during build and handled gracefully. This means that no module besides the main libraries shall cause a fatal error that stops the build process. This is because many modules do not have to be built by a developer on his machine because he is working on something completely different.

In general the dependency check shall work as follows: First HAVE_ variables are set depending on the requirements. These may be given to the source files during complete by providing -DHAVE_... in CFLAGS if and only if the dependency is met. Extra warning_* and error_* targets are used to provide an informative message about unmet requirements and bailing out gracefully (warning_*) or causing a fatal error (error_*).

Basic check

How to check if a dependency is met of course depends on the dependency. Here are a few ideas what you can do to check for the dependency.

pkg-config

If the dependency is a library and provides support for pkg-config checking the dependency is very easy. This is the preferred method whenever pkg-config is available for the library. Use a pattern like the following (replace LIBTOCHECK with a descriptive name and library-to-check with the pkg-config name of the library).

ifneq ($(PKGCONFIG),)
  HAVE_LIBTOCHECK = $(if $(shell $(PKGCONFIG) --exists 'library-to-check'; echo $${?/1/}),1,0)
endif

header file

A simple method to check for the existence of a library and its header files is to check for on central header file of the library. This is especially helpful for vendor-supplied SDK.

ifneq ($(wildcard /usr/include/LIBHEADER.h),)
  HAVE_LIBTOCHECK    = 1
endif

Non-fatal dependencies (warning)

A missing non-fatal dependency can have two causes. Either the module cannot be built at all (then the dependency was critical for the module, but the module not for the rest of the software) or it can be built with limited functionality (the dependency provided non-critical extra functionality).

For the module-non-fatal dependency where you just want to show a warning but otherwise process with the compilation use the following pattern (replace libNAME with a short descriptive name of the library. Replace LIBTOCHECK with the name that you used in the check).

ifneq ($(HAVE_LIBTOCHECK),1)
WARN_TARGETS += warning_libNAME
endif
ifeq ($(OBJSSUBMAKE),1)
all: $(WARN_TARGETS)

.PHONY: warning_libNAME
warning_libNAME:
        $(SILENT)echo -e "$(INDENT_PRINT)--- $(TRED)No XYZ support$(TNORMAL) (install package/SDK)";
endif

The the module-fatal dependency failure that will only build the module if the dependency is fulfilled use the following pattern (replace appropriately as mentioned above).

ifneq ($(HAVE_LIBTOCHECK),1)
  WARN_TARGETS += warning_libNAME
else
  PLUGINS_all = $(PLUGINDIR)/myfancy_plugin.so
endif

ifeq ($(OBJSSUBMAKE),1)
all: $(WARN_TARGETS)

.PHONY: warning_libNAME
warning_libNAME:
        $(SILENT)echo -e "$(INDENT_PRINT)--- $(TRED)Omitting my fancy pluginNAME$(TNORMAL) (install package/SDK)"
endif

Fatal dependencies (error)

Such dependencies are only allowed in base libraries in src/libs! Use a pattern like the following.

ifneq ($(HAVE_LIBTOCHECK),1)
  ERROR_TARGETS += error_libNAME
endif# BuildSys
# Fawkes
# Intro

ifneq ($(SRCDIR),)
all: $(ERROR_TARGETS)
.PHONY: error_libNAME
error_libNAME:
        $(SILENT)echo -e "$(INDENT_PRINT)--- $(TRED)LibXYZ cannot be built$(TNORMAL) (install package/SDK)"
        $(SILENT)exit 1
endif

Sub-directory with ordering constraints

Sub-directory-only Makefiles (build type 1) are usually used in infrastructure-critical locations. If the sub-directories happen to depend on each other (in a non-circular fashion, of course!) then for error detection and parallel build it is necessary to tell these inter-dependencies to the build system. This is done with ordering constraints in the Makefile. As an example here is an excerpt from the libs Makefile:

SUBDIRS = core utils interface blackboard netcomm config aspect

include $(BASEDIR)/etc/buildsys/config.mk
include $(BASEDIR)/etc/buildsys/rules.mk

# Explicit dependencies, this is needed to have make bail out if there is any
# error. This is also necessary for working parallel build (i.e. for dual core)
utils: core
interface: core
blackboard: core utils interface
netcomm: core utils
config: core netcomm utils
aspect: core utils blackboard netcomm config

Here the ordering is absolutely crucial or it won't work. First you have to mention the subdirs, then you need to include the config and the rules and only then you may add the ordering constraints. Changing the order of the items will make the whole build fail.

The ordering constraints show the dependencies of the sub-directories. For example the core directory is unconstrained (does not show on a left side of the colon). The utils depend only on core while aspect depend on almost all other libraries. This will tell the build system that it is necessary to build core before any other library, then utils and interface can be built in parallel. netcomm can start as soon as the utils are done, the blackboard has to wait no only until the utils are build but also until the interfaces have been built. I think you get the idea. The general form is

SUBDIR: dependencies

where SUBDIR is one of the mentioned sub-directories (set in SUBDIRS) and the dependencies a list of space-separated directories.

Downloading files off the web

Sometimes code or resources that cannot be maintained within the Fawkes tree have to be downloaded from the Internet. To make that an easy task there is a helper to make that as easy as possible for you. An example in your Makefile may look like this:

include $(BASEDIR)/etc/buildsys/download.mk

URLS_sift = http://robocup.rwth-aachen.de/sourcecode/sift-latest.tar.gz

.PHONY: download-sift
download-sift:
        $(SILENT)$(download-files)

That's all there is to it. The script will try to find an application to download files like curl or wget and use it to download the file. The URLs to download is derived from the name of the target, for a pattern download-someaddon it will look for a variable named URLS_someaddon (where someaddon is the descriptive entity as you probably already guessed).