Build automation

In order to automate a build process, several open source tools are available and a few of them are widely used in different development environments. Make is the standard UNIX tool to automate the steps required to create the required binary images from the sources, checking the dependencies for each component, and executing the steps in the right order. Make is a standard POSIX tool, and it is part of many UNIX-like systems. In a GNU/Linux distribution, it is implemented as a standalone tool, which is part of the GNU project. From this point on, the GNU Make implementation is simply referred to as Make.

Make is designed to execute the default build by simply invoking the make command with no arguments from the command line, provided that a makefile is present in the working directory. A makefile is a special instruction file, containing rules and recipes to build all the files needed until the expected output files are generated. Open source alternatives offering similar solutions for build automation exist, such as Cmake and Scons, but all the examples in this book are built using Make, because it provide a simple and essential enough environment to control the build system, and it is the one standardized by POSIX.

Some integrated development environments use built-in mechanisms to coordinate the building steps, or generate makefiles before invoking the Make automatically when the user requests to build the output files. However, editing makefiles manually gives complete control on the intermediate steps to generate the final images, where the user can customize the recipes and rules used to generate the desired output files.

There is no specific version that needs to be installed in order to cross-compile code for the Cortex-M target, but some extra parameters, such as the location of the toolchain binaries, or the specific flags needed by the compiler, need to be taken care of when writing targets and directives within the makefile. One of the advantages of using a build process is that targets may have implicit dependencies from other intermediate components that are automatically resolved at compile time. If all the dependencies are correctly configured, makefile ensures that the intermediate steps are executed only when needed, reducing the compile time of the whole project when only a few sources are altered, or when single object files have been deleted.

Makefiles have a specific syntax to describe rules. Each rule begins with the expected target files, expected as the output of the rule, a colon, and the list of prerequisites, which are the files necessary to execute the rule. A set of recipe items follow, each one describing the actions that Make will execute to create the desired target:

target: [prerequisites]
recipe
recipe
...

By default, Make will execute the first rule encountered while parsing the file if no rule name is specified from the command line. If any of the prerequisites are not available, Make automatically looks for a rule in the same makefile that can create the required file, recursively until the chain of requirements is satisfied.

Makefiles can assign a custom string of text to internal variables while executing. Variable names can be assigned using the = operator, and referred to by prefixing them with $. For example, the following assignment is used to put the name of two object files into the OBJS variable:

OBJS = hello.o world.o

A few important variables that are assigned automatically within the rules are the following:

Variable
Meaning
$(@)
Name of the target for the currently executing rule
$(^)
List of all the prerequisites for this rule, without duplicates
$(+)
List of all the prerequisites for this rule, with duplicates if any
$(<)
First element in the prerequisites list

 

These variables are handy to use within the recipe action lines. For example, the recipe to generate a helloworld ELF file from the two object files can be written as follows:

helloworld: $(OBJS)
gcc -o $(@) $(^)

Some of the rules are implicitly defined by Make. For example, the rule to create the files hello.o and world.o from their respective source files can be omitted, as Make expects to be able to obtain each one of these object files in the most obvious way, which is by compiling the corresponding C source files with the same name, if present. This means that this minimalist makefile is already able to compile the two objects from the sources, and link them together using the default set of options for the host system.

The linking recipe can also be implicit, if the executable has the same name as one of its prerequisite objects, minus its .o extension. If the final ELF file is called hello, our makefile could simply become the following one-liner:

hello: world.o

This would automatically resolve the hello.o and world.o dependencies, and then link them together using an implicit linker recipe similar to the one we used in the explicit target.

Implicit rules use predefined variables, which are assigned automatically before the rules are executed, but can be modified within makefile. For example, it is possible to change the default compiler by altering the CC variable. Here is a small list of the most important variables that may be used to alter implicit rules and recipes:

Variable Meaning Default value
CC Compiler program cc
LD Linker program ld
CFLAGS Flags passed to the compiler when compiling sources <empty>
LDFLAGS Flags passed to the compiler in the linking step <empty>

 

When linking a bare-metal application for embedded platforms, the makefile must be modified accordingly, and as shown later in this chapter, a number of flags are required to properly cross-compile the sources and instruct the linker to use the desired memory layout to organize the memory sections. Moreover, additional steps are generally needed to manipulate the ELF file and translate it to a format that can be transferred to the target system. However, the syntax of makefile is the same and the simple rules shown here are not too different from those used to build the example. The default variables still need to be adjusted to modify the default behavior, if implicit rules are used.

When all the dependencies are correctly configured in the makefile, Make ensures that the rules are only executed when the target is older than its dependencies, thus reducing the compile time of the whole project when only a few sources are altered, or when single object files have been deleted.

Make is a very powerful tool, and its range of possibilities goes far beyond the few features used to generate the examples in this book. Mastering the automation process of the builds may lead to optimized build processes. The syntax of makefile includes useful features, such as conditionals, which can be used to produce different results by invoking makefile using different targets or environment variables. For a better understanding of the capabilities of Make, please refer to the GNU Make manual available at https://www.gnu.org/software/make/manual.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.172.252