Compiling and building
Content
- gcc.gnu.org the Gnu compiler collection - supporting various programming languages, hardware architectures and operating systems
- First released in 1987 by Richard Stallman
- 2012: GCC now uses C++ as its implementation language.
- Languages: C, C++, Objective-C, Fortran, Ada, Go, and D
- Libraries for these languages (libstdc++,...)
- Linkers, ld, combines a number of object and archive files, relocates their data and ties up symbol references,
- accepts Linker Command Language files as input
- if the linker is being invoked indirectly, via a compiler driver e.g. gcc then all the linker command-line options should be prefixed by -Wl
- GCC was originally written as the compiler for the GNU operating system
- gcc - Wikipedia
- gcc online documentation
GCC's external interface follows Unix conventions. Users invoke a language-specific driver program (gcc for C, g++ for C++, etc.), which interprets command arguments, calls the actual compiler, runs the assembler on the output, and then optionally runs the linker to produce a complete executable binary.
Each of the language compilers is a separate program that reads source code and outputs machine code. All have a common internal structure. A per-language front end parses the source code in that language and produces an abstract syntax tree ("tree" for short).
These are, if necessary, converted to the middle end's input representation, called GENERIC form; the middle end then gradually transforms the program towards its final form. Compiler optimizations and static code analysis techniques (such as FORTIFY_SOURCE, a compiler directive to discover buffer overflows) are applied to the code. These work on multiple representations, mostly the architecture-independent GIMPLE representation and the architecture-dependent RTL representation. Finally, machine code is produced using architecture-specific pattern matching originally based on an algorithm of Jack Davidson and Chris Fraser.
The make ecosystem
See also https://thoughtbot.com/blog/the-magic-behind-configure-make-make-install
Overview
Usage:
- ./configure // script that prepares the makefile (possibly from a makefile.in template), resides at overall level, e.g. /home/OPTEE/qemu
- you may try ./configure --help
- make // makefile containing commands to compile etc, typically resides in build subdir, e.g. /home/OPTEE/qemu/build
- make install // copies the built program, its libraries and documentation, to the correct locations, which usually means that the program’s binary will be copied to a directory on your PATH, the program’s manual page will be copied to a directory on your MANPATH, and any other files it depends on will be safely stored in the appropriate place.
Since the install step is also defined in the Makefile, where the software is installed can change based on options passed to the configure script, or things the configure script discovered about your system.
Manual for make: https://www.gnu.org/software/make/manual/
A simple makefile consists of “rules” with the following shape:
target … : prerequisites …
recipe
A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.
If you write a rule whose recipe will not create the target file, the recipe will be executed every time the target comes up for remaking. Here is an example:
clean:
rm *.o temp
Because the rm command does not create a file named clean, probably no such file will ever exist. Therefore, the rm command will be executed every time you say ‘make clean’.
In this example, the clean target will not work properly if a file named clean is ever created in this directory. Since it has no prerequisites, clean would always be considered up to date and its recipe would not be executed. To avoid this problem you can explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY
By default, make starts with the first target (not targets whose names start with ‘.’ unless they also contain one or more ‘/’). This is called the default goal.
As configure scripts are fairly large and complex, they are not written by hand but generated by a suite of programs called the autotools, e.g. autoconf and automake.
See
- https://www.gnu.org/software/automake/
On the maintainer’s system:
aclocal # Set up an m4 environment
autoconf # Generate configure from configure.ac
automake --add-missing # Generate Makefile.in from Makefile.am
./configure # Generate Makefile from Makefile.in
make distcheck # Use Makefile to build and test a tarball to distribute
On the end-user’s system:
./configure # Generate Makefile from Makefile.in
make # Use Makefile to build the program
make install # Use Makefile to install the program
Make
Make is a build automation tool that builds executable programs and libraries from source code by reading files called makefiles which specify how to derive the target program.
Telling make what to do
- selection of the desired makefile for invocation
- careful, there can be GNUmakefile, makefile, Makefile, ...
- Use -f to specify, e.g. run $ make -f myspecialmakefile, see https://www.gnu.org/software/make/manual/make.html#Makefile-Arguments
- makefiles can include other makefiles
- by providing the desired target(s), e.g. make all, see https://www.gnu.org/software/make/manual/make.html#Goals, default is first target
- by providing options (sometimes also called flags), e.g. make -n all
- by providing variables (environment or make variables)
- you can set verbosity with -d flag, ref debugging below
- you can set verbosity with V=1 or similar (?)
- Unless specified, make uses the default value for variables such as CFLAGS, CXXFLAGS, or TARGET_ARCH, which is nothing
- by setting variables (e.g. CFLAGS) as environment parameters and exporting them, prior to invocation
- by providing variables as parameters at the moment of invocation
Make invocation
- varibles through environment parameters might be set by export prior to invocation
- from a terminal, in the terminal's working directory
- with one or more targets in form of command-line arguments: make [TARGET ...]
- with one or more options (sometimes also called flags), e.g. make -n all, see list at https://www.gnu.org/software/make/manual/make.html#Instead-of-Execution
- with a verbosity setting, V=1 (warning and errors only), V=99 (stdout+stderr), e.g. make V=1 all
- with one or more variables (environment or make variables)
- already set through environment parameters
- provided at the moment of invocation
Make searches the current directory for a makefile, e.g., GNU Make searches files in order for a file named one of GNUmakefile, makefile, or Makefile. and then invokes the specified (or default) target(s) from that file.
The various commands invoked by make when building the target need to be on the PATH.
Make without arguments
Without arguments, Make
- builds the first target that appears in its makefile, which is traditionally a symbolic "phony" target named all
- uses the default value for variables such as CFLAGS, CXXFLAGS, or TARGET_ARCH, which is nothing
Make targets
Make decides whether a target needs to be regenerated by comparing file modification times. This solves the problem of avoiding the building of files which are already up to date, but it fails when a file changes but its modification time stays in the past. Such changes could be caused by restoring an older version of a source file, or when a network filesystem is a source of files and its clock or time zone is not synchronized with the machine running Make. The user must handle this situation by forcing a complete build. Conversely, if a source file's modification time is in the future, it triggers unnecessary rebuilding, which may inconvenience users.
Makefiles are traditionally used for compiling code (*.c, *.cc, *.C, etc.), but they can also be used for providing commands to automate common tasks. One such makefile is called from the command line:
- make # Without argument runs first TARGET
- make help # Show available TARGETS
- make dist # Make a release archive from current dir
- make check # Unit testing without installation
The makefile language is similar to declarative programming.
Makefiles may contain five types of constructs:
- An explicit rule says when and how to remake one or more files, called the rule's targets. It lists the other files that the targets depend on, called the prerequisites of the target, and may also give a recipe to use to create or update the targets.
- An implicit rule says when and how to remake a class of files based on their names. It describes how a target may depend on a file with a name similar to the target and gives a recipe to create or update such a target.
- A variable definition is a line that specifies a text string value for a variable that can be substituted into the text later.
- A directive is an instruction for make to do something special while reading the makefile such as reading another makefile.
- Lines starting with # are used for comments.
A makefile consists of rules. Each rule begins with a textual dependency line which defines a target followed by a colon (:) and optionally an enumeration of components (files or other targets) on which the target depends. The dependency line is arranged so that the target (left hand of the colon) depends on components (right hand of the colon). It is common to refer to components as prerequisites of the target.
target [target ...]: [component ...]
Tab ↹[command 1]
.
.
.
Tab ↹[command n]
Usually each rule has a single unique target, rather than multiple targets.
For example, a C .o object file is created from .c files, so .c files come first (i.e. specific object file target depends on a C source file and header files). Because Make itself does not understand, recognize or distinguish different kinds of files, this opens up a possibility for human error.
Each dependency line may be followed by a series of TAB indented command lines which define how to transform the components (usually source files) into the target (usually the "output"). If any of the prerequisites has a more recent modification time than the target, the command lines are run. The GNU Make documentation refers to the commands associated with a rule as a "recipe".
The first command may appear on the same line after the prerequisites, separated by a semicolon,
targets: prerequisites ; command
for example, hello: ; @echo "hello"
Make can decide where to start through topological sorting.
Each command line must begin with a tab character to be recognized as a command. The tab is a whitespace character, but the space character does not have the same special meaning. This is problematic, since there may be no visual difference between a tab and a series of space characters. This aspect of the syntax of makefiles is often subject to criticism.
Each command is executed by a separate shell or command-line interpreter instance. Since operating systems use different command-line interpreters this can lead to unportable makefiles. For example, GNU Make (all POSIX Makes) executes commands with /bin/sh by default, where Unix commands like cp are normally used. In contrast to that, Microsoft's nmake executes commands with cmd.exe where batch commands like copy are available but not necessarily cp.
A rule may omit the recipe. The dependency line can consist solely of components that refer to other targets, for example: realclean: clean distclean
The command lines of a rule are usually arranged so that they generate the target. An example: if file.html is newer, it is converted to text. The contents of the makefile:
file.txt: file.html
lynx -dump file.html > file.txt
The rule above would be triggered when Make updates "file.txt". In the following invocation, Make would typically use this rule to update the "file.txt" target if "file.html" were newer.
Command lines can have one or more of the following three prefixes:
- a hyphen-minus (-), specifying that errors are ignored
- an at sign (@), specifying that the command is not printed to standard output before it is executed
- a plus sign (+), the command is executed even if Make is invoked in a "do not execute" mode
Make listing targets
Approach 1
Run '$ make help'.
Approach 2
Use . However, in more advanced files this misses special targets such as .PHONY, lines containing URLs, variable definitions that use :=, lines defining target-specific variables, and more.
Approach 3
Include the following in your Makefile
list:
@grep '^[^#[:space:]].*:' Makefile
Then run '$ make list'.
Approach 4
Define an alias (in .bashrc): alias makefile-targets='grep "^[^#[:space:]].*:" Makefile' . Bash completion expands the alias.
Make macros
A makefile can contain definitions of macros. Macros are usually referred to as variables when they hold simple string definitions, like CC=clang. Macros in makefiles may be overridden in the command-line arguments passed to the Make utility. Environment variables are also available as macros.
Macros allow users to specify the programs invoked and other custom behavior during the build process. For example, the macro CC is frequently used in makefiles to refer to the location of a C compiler, and the user may wish to specify a particular compiler to use.
Some directives in makefiles can include other makefiles.
Line continuation is indicated with a backslash \ character at the end of a line.
Make uses variables through flags
Unless specified, make uses the default value for variables such as CFLAGS, CXXFLAGS, or TARGET_ARCH, which is nothing.
List of possible flags is at https://www.gnu.org/software/make/manual/make.html#index-ARFLAGS .
Make other features
Make supports creating basic functions. You "define" the function just by creating a variable, but use the parameters $(0), $(1), etc. You then call the function with the special call builtin function. The syntax is $(call variable,param,param). $(0) is the variable, while $(1), $(2), etc. are the params.
sweet_new_fn = Variable Name: $(0) First: $(1) Second: $(2) Empty Variable: $(3)
all:
# Outputs "Variable Name: sweet_new_fn First: go Second: tigers Empty Variable:"
@echo $(call sweet_new_fn, go, tigers)
Contents of makefiles
Contents of makefiles
Contents is essentially variable definitions and commands. Some pre-defined variables exist (automatic variables, such as $@). At first sight there's no easy way to identify all possible variables used by the makefile. Some systems use configuration files for this, see e.g. OPTEE's approach for configuring makefile variables.
Using and setting variables
- using variables $(VARIABLE)
- Variable $(MAKE) must be used instead of plainly calling make when calling make recursively, i.e. calling make from within a makefile. See https://www.gnu.org/software/make/manual/make.html#MAKE-Variable
- setting variables
- To set a variable from the makefile, write a line starting with the variable name followed by one of the assignment operators ‘=’, ‘:=’, ‘::=’, or ‘:::=’. Whatever follows the operator and any initial whitespace on the line becomes the value.
- If you’d like a variable to be set to a value only if it’s not already set, then you can use the shorthand operator ‘?=’
Automatic variables
- automatic variables
- $@ - The file name of the target of the rule. If the target is an archive member, then ‘$@’ is the name of the archive file. In a pattern rule that has multiple targets (see Introduction to Pattern Rules), ‘$@’ is the name of whichever target caused the rule’s recipe to be run.
- $% The target member name, when the target is an archive member. For example, if the target is foo.a(bar.o) then ‘$%’ is bar.o and ‘$@’ is foo.a. ‘$%’ is empty when the target is not an archive member.
- $< - The name of the first prerequisite. If the target got its recipe from an implicit rule, this will be the first prerequisite added by the implicit rule
- $? - The names of all the prerequisites that are newer than the target, with spaces between them. If the target does not exist, all prerequisites will be included. For prerequisites which are archive members, only the named member is used
- $^ - The names of all the prerequisites, with spaces between them.
- $+ - This is like ‘$^’, but prerequisites listed more than once are duplicated in the order they were listed in the makefile. This is primarily useful for use in linking commands where it is meaningful to repeat library file names in a particular order.
- $| - The names of all the order-only prerequisites, with spaces between them.
- $* - The stem with which an implicit rule matches (see How Patterns Match). If the target is dir/a.foo.b and the target pattern is a.%.b then the stem is dir/foo. The stem is useful for constructing names of related files.
Debugging makefiles
See chapter 12 of https://www.oreilly.com/openbook/make3/book/
The make warning function
Use as
$(warning A top-level warning)
FOO := $(warning Right-hand side of a simple variable)bar
Command line options
- -d or --debug // the most extensive debug info
- --debug[=FLAGS] modifiers:
- b for basic debugging // e.g. make --debug=b
- v for verbose basic debugging
- i for showing implicit rules
- j for invocation of commands and jobs
- all (default)
- m for debugging during remaking of makefiles
- --just-print or -n
- --jobs or -j2 -j3 .... jn with n number of parallel tasks you want make to perform
- --print-data-base or -p // print the data base (rules and variable values) that results from reading the makefiles; then execute as usual or as otherwise specified
- --version or -v // print version
- --warn-undefined-variables
Buildroot makefiles
Buildroot is a set of makefiles and patches that simplifies and automates the process of building a complete and bootable Linux environment for an embedded system, while using cross-compilation to allow building for multiple target platforms on a single Linux-based development system. Buildroot can automatically build the required cross-compilation toolchain, create a root file system, compile a Linux kernel image, and generate a boot loader for the targeted embedded system, or it can perform any independent combination of these steps.
Meson and Ninja
In the Meson view, there are three phases to getting a development environment running:
- Installing a compiler toolchain - Debian, Ubuntu and derivatives: sudo apt install build-essential // gets you gcc and friends
- Installing Meson - Debian, Ubuntu and derivatives: sudo apt install meson ninja-build
- Creating a project and building it
- mkdir testproject
- cd testproject
- meson init --name testproject --build // creates a project skeleton and compiles it, result is put in the build subdirectory and can be run directly from there
- build/testproject
- The project is now ready for development. You can edit the code with any editor and it is rebuilt by going in the build subdirectory and executing the meson compile command. If your version of Meson is too old, you can compile the project by running the command ninja instead.
Info:
- Ant - Apache - Java building tool
- Apache Ant is a Java library and command-line tool whose mission is to drive processes described in build files as targets and extension points dependent upon each other. The main known usage of Ant is the build of Java applications. Ant supplies a number of built-in tasks allowing to compile, assemble, test and run Java applications. Ant can also be used to build non Java applications, for instance C or C++ applications. More generally, Ant can be used to pilot any type of process which can be described in terms of targets and tasks.