Usage notes
Basic setup
First, you need to add the basement layer to your project. To do so, add a layers entry to your config.yaml:
bobMinimumVersion: "1.2"
layers:
- name: basement
scm: git
url: https://github.com/BobBuildTool/basement.git
This will always use the latest version. You probably want to add a specific commit
so that always the same recipes are used. Next, the layer must be fetched. For that,
do a:
bob layers update
To use all facilities of the basement project, you just need to inherit the
basement::rootrecipe class in your root recipe:
inherit: [ "basement::rootrecipe" ]
This will make your recipe a root recipe and already setup the sandbox with a proper host toolchain. See the next chapter what tools and toolchains are readily available.
C/C++ toolchains
Before delving into the details, some definitions are necessary. The basement layer uses the same terms that were establisdhed by the autoconf project. In principle, three different systems are distinguished:
- The build system
This is the system where the build is executed, i.e. where Bob and the compiler is running. The
AUTOCONF_BUILDvariable describes the build system and is always defined.- The host system
The host system is where the compiled binaries are meant to be executed. For embedded build systems, this is sometimes called the “target” system. The
AUTOCONF_HOSTvariable represents this system. If set and different thanAUTOCONF_BUILD, the package is cross compiled.- The target system
This system is only applicable to compilers and their related tools. It is stored in the
AUTOCONF_TARGETvariable. It describes the system for which the compiler produces the object code. For a cross-compiler, the target system is different from the host system where the compiler is executed. Of build and target system are identical, it is called a native compiler.
The different systems are described as a so-called target triplet. Even
though it is used ubiquitously, it is only loosely defined. In fact, it may not
even have exactly three fields as the name suggests. It generally has the
format of arch-vendor-system where system may either be the os
(operating system) or kernel-os. See the autoconf documentation.
In almost all cases, projects will use cross compilation. This is even the case where the build system and the host system have the same architecture and operating system. The rationale is to be as independent from the build system as possible. Using native compiler always has the drawback that the result relies at least on the libc of the build system and is thus not portable across machines.
Selecting a C/C++-toolchain
To select the desired toolchain, add a dependency in the following format early in your project dependency list:
depends:
- name: <toolchain name here>
use: [tools, environment]
forward: True
The following GCC toolchains are predefined for commonly used target systems:
devel::cross-toolchain-aarch64-l4re: ARMv8-A AArch64 L4Re.devel::cross-toolchain-aarch64-linux-gnu: ARMv8-A AArch64 Linux with glibc.devel::cross-toolchain-aarch64-none-elf: ARMv8-A/R AArch64 bare metal toolchain with newlib libc.devel::cross-toolchain-arm-linux-gnueabihf: ARMv7-A Linux with glibc. Hard floating point ABI.devel::cross-toolchain-arm-none-eabi: ARMv7 bare metal toolchain with newlib libc.devel::cross-toolchain-x86_64-linux-gnu: x86_64 toolchain for Linux with glibc.devel::cross-toolchain-x86_64-l4re: x86_64 toolchain for L4Re.devel::cross-toolchain-riscv64-linux-gnu: RISC-V toolchain targeting the GC profile.
Read on to learn how to switch to different toolchains for selected dependencies or how to define your own if the standard ones are not sufficient.
All toolchains compile for a reasonable default architecture model that is supposed to be widely supported. To tweak the standard compile flags, the following variables may be optionally set when pulling in the toolchain.
BASEMENT_OPTIMIZECompiler optimization level (
-O). Defaults to2to optimize for speed.BASEMENT_DEBUGMay be
0or1and controls the generation of debugging information. Defaults to1.CROSS_TOOLCHAIN_CPUIf set, adds an
-mcpu=option to the compiler flags with the value. It overrides the default CPU selection of the toolchain.CROSS_TOOLCHAIN_ARCHIf set, adds an
-march=option to the compiler flags with the value. It overrides the default architecture selection of the toolchain.
Example:
depends:
- name: devel::cross-toolchain-aarch64-linux-gnu
use: [tools, environment]
forward: True
environment:
CROSS_TOOLCHAIN_CPU: neoverse-n1
Using clang instead of GCC
The same toolchains are additionally available as LLVM/clang variants. Just
prepend a clang- prefix before the target triplet, e.g.:
devel::cross-toolchain-clang-aarch64-linux-gnudevel::cross-toolchain-clang-x86_64-linux-gnu
By default, the clang toolchains use the LLVM lld linker. This can be
controlled for the whole toolchain with the CROSS_TOOLCHAIN_CLANG_LLD
variable:
depends:
- name: devel::cross-toolchain-clang-aarch64-linux-gnu
use: [tools, environment]
forward: True
environment:
CROSS_TOOLCHAIN_CLANG_LLD: "0"
Note that some recipes may not compile with clang toolchains. Such problems
should be handled in the affected recipe directly. There are a number of
options to deal with clang incompatibilities. In the best case, the sources can
be patched to work with clang. If this is not feasible, the recipe may choose
to explicitly request GCC as compiler. Override the CC and/or CXX
variable in this case:
privateEnvironment:
CC: "$GNU_CC"
CXX: "$GNU_CXX"
There is also GNU_CPP (C preprocessor) and GNU_LD (linker) for packages
that use these tools directly. In case the linker is called implicitly by
clang, its behaviour must be controlled through LDFLAGS. The default is
selected by CROSS_TOOLCHAIN_CLANG_LLD. The recipe must append to
LDFLAGS to override this default consistently:
privateEnvironment:
LDFLAGS: "${LDFLAGS}$(if-then-else,$(eq,$TOOLCHAIN_FLAVOUR,clang), -fuse-ld=ld,)"
Likewise, additional compiler options can be passed if this fixes compile problems with clang. Example:
privateEnvironment:
# LLVM assembler is not fully compatible
CFLAGS: "${CFLAGS}$(if-then-else,$(eq,$TOOLCHAIN_FLAVOUR,clang), -no-integrated-as,)"
Standard tools
There are two tools that are meant to be used by recipes that compile C/C++ code.
target-toolchainThis is the main toolchain. Every C/C++ package uses it. It represents the compiler that builds for the target system where the package should run in the end. Usually, but not necessarily, this is a cross compiler even on the same architecture.
A recipe should make no assumption about which compiler this is and for which architecture or operating system it compiles. This is the key ingredient for making Bob projects flexible because the
target-toolchainmay be replaced anywhere in the dependency tree and all dependencies beneath it will automatically be compiled for the configured target.host-toolchainThis toolchain represents the native host machine compiler. Even though it builds host executables, it does never fingerprint the results. Instead, it is intended to be used in the
buildScriptif the package also needs the host compiler during build time where none of the host build object code is part of the result. Points to the host gcc or the gcc of the sandbox. Only selected packages need it when being built in the sandbox.
Given the above definitions, practically all recipes that build C/C++ code will do a:
buildTool: [target-toolchain]
to use the currently selected C/C++ compiler. Only if the build requires the
native compiler too (e.g. to build some intermediate build tool),
host-toolchain may be added to buildTool.
There are two other tools that are always defined. They are intended to be used
at special places where they replace the target-toolchain for selected
dependencies.
host-compat-toolchainA toolchain that builds portable host executables that should be able to run on the oldest supported Ubuntu LTS. Even though it builds for the host architecture and operating system, it is a cross compiler with a backwards compatible glibc version. When using the
basement::rootrecipeclass, this is the defaulttarget-toolchain. It is defined as a dedicated name to be able to compile specifically for the host when needed:depends: - ... - name: some::package tools: target-toolchain: host-compat-toolchain
This will build
some::packagefor the host regardless of the currently defined target toolchain. It comes in handy if some special tool is needed to compile a package.host-native-toolchainThis toolchain represents the native host machine compiler. In contrast to
host-toolchainit does fingerprint the system. This implies that binary artifacts of such packages are not exchangeable between systems! It is used if a package needs to be compiled natively and the object code is part of the package result. Like in thehost-compat-toolchainexample above, it is usually supplied astarget-toolchainfor selected dependencies.An example for the necessity of the
host-native-toolchainis for example Python. To cross-compile python, the same version is required on the build system. Therefore, Python needs to be first compiled natively. Then Python can be cross compiled by whatevertarget-toolchainis configured. See the following excerpt from thebasement::rootrecipeclass where this is already done for you:depends: - name: python::python3-minimal use: [tools] forward: True tools: # To build python3 a working python interpreter is required. Build # a bootstrap python3 interpreter with the native host toolchain. # The real interpreter is then built with the # host-compat-toolchain. target-toolchain: host-native-toolchain - python::python3
Switching cross-compilers
Once a cross-compiling toolchain has been selected, all following dependencies are built by this compiler. As this applies to all packages, selecting a different cross compiler requires some special care. Suppose a root recipe has the following (intentionally incorrect!) dependency list:
inherit: ["basement::rootrecipe"]
depends:
- name: devel::cross-toolchain-aarch64-linux-gnu
use: [tools, environment]
forward: True
- utils::bash
- name: devel::cross-toolchain-arm-none-eabi
use: [tools, environment]
forward: True
- some::firmware
Warning
The example above does not work but is used as an illustration what needs to be considered.
The above example will unfortunately not work as expected. The reason is that after
the devel::cross-toolchain-aarch64-linux-gnu dependency, everything will be
compiled for Linux AArch64. This includes the devel::cross-toolchain-arm-none-eabi
dependency too! But this compiler needs to be executed on the build system. Therefore,
the target-toolchain used for the compiler needs to be replaced with the
host-compat-toolchain:
depends:
...
- name: devel::cross-toolchain-arm-none-eabi
use: [tools, environment]
forward: True
tools:
target-toolchain: host-compat-toolchain
- some::firmware
As you can see, the devel::cross-toolchain-arm-none-eabi is built
explicitly with the host-compat-toolchain, regardless of which other
toolchain is configured at this point.
Installing a compiler in the target system
Sometimes, the toolchain should be installed on the target system. This works
like for any other package. The only difference is that the use list does
not have the tools key because the compiler should be installed rather than
used at build time:
inherit: ["basement::rootrecipe"]
depends:
# The toolchain for the target system
- name: devel::cross-toolchain-aarch64-linux-gnu
use: [tools, environment]
forward: True
# The native compiler and binutils for the target system
- devel::binutils
- devel::gcc-native
The above example installs a native compiler into the target system. That is, this compiler will produce binaries for the same system. Similarly, a cross-compiler could be installed as well:
inherit: ["basement::rootrecipe"]
depends:
# The toolchain for the target system
- name: devel::cross-toolchain-aarch64-linux-gnu
use: [tools, environment]
forward: True
- devel::cross-toolchain-arm-none-eabi
The toolchain will be compiled for the AArch64 Linux system and will produce
object code for AArch32 bare-metal systems. Note the absence of the use:
[tools, environment] and forward: True lines from the
devel::cross-toolchain-arm-none-eabi dependency.
Advanced toolchain selection
If the pre-configured toolchains are not sufficient, it is possible to compile
almost any custom GNU gcc/binutils based Linux toolchain. Starting point is
the generic devel::cross-toolchain recipe. This unconfigured cross-compilation
toolchain needs to be configured. At least the following variables need to be
defined for it:
ARCHThe target architecture. This is based on the architectures as defined by the Linux kernel. Valid choices are, among others,
arm,arm64,i386,x86_64orriscv. See the Linux kernel documentation for all possible values.AUTOCONF_TARGETThe autoconf target triplet that describes the system. This is the primary variable that affects the toolchain and needs to be aligned with the other switches. See below for some rough guidelines.
GCC_LIBCThe C-library that should be used by the toolchain. Valid choices are
glibc,newlibanduclibc-ng.
The following, additional variables are available to tweak the toolchain:
GCC_TARGET_ARCHThis is passed as
--with-arch=to the gcc configure script and provides the default value for the-march=gcc option. As such, it sets the default target architecture that the compiler is using. It is recommended to pass this switch to choose the right architectural features. See the GCC machine dependent options for the supported values of the-march=option.GCC_TARGET_ABIPassed as
--with-abi=to the gcc configure script and provides the default value for the-mabi=option. This is used for example for RISC-V to choose between the different possible ABIs.GCC_TARGET_FLOAT_ABIMay be either
hardorsoft.GCC_TARGET_FPUPassed as
--with-fpu=to the gcc configure script and provides the default value for the-mfpu=option. Again, the acceptable values depend on the chosen target.GCC_MULTILIBIf set, provides the comma separated set of multilibs to build. The permissible values depend on the target architecture. Currently, the basement layer only supports
m32,m64onx86_64.GCC_ENABLE_LANGUAGESComma separated list of languages that gcc should support. Defaults to
c,c++.GCC_EXTRA_OPTIONSIf set, it is passed verbatim to the gcc configure script.
TODO: Explain target triplet choices.
Standard variables for C/C++ packages
When using the target-toolchain, the following variables are available. The
variables have the same name as the executable that is normally available on
the build system.
AR: The archiver to create/modify static libraries.AS: The assembler.CC: The C-compiler.CPP: The C preprocessor.CXX: The C++-compiler.LD: The linker.NM: Tool to inspect object symbol tables.OBJCOPY: Tool to copy and translate object files.OBJDUMP: Print object file contents.RANLIB: Tool to (re-)generate symbol index of a static library.READELF: Display information about ELF files.STRIP: Tool for stripping unneeded sections and symbols from object files.
Other meta information variables that are not directly linked to a particular executable are:
AUTOCONF_HOST: Set for cross-compiler to the host system target triplet.CROSS_COMPILE: Cross compile prefix for standard tool of a cross-compiling toolchain, e.g.,riscv64-linux-gnu-for a RISC-V Linux cross toolchain. Some build systems use this method to find the right tools instead of the individual variables above (AR, …).TOOLCHAIN_FLAVOUR: Basically the compiler vendor. Can begccwhich is the basement layer main compiler,clangfor LLVM clang andmsvcfor Windows builds with the Microsoft Visual C++ compiler.
Attention
The above variables are defined by target-toolchain only. If it is
missing from buildTools, they will be undefined!
The following variables are not defined by target-toolchain but are part of
the normal environment variables. The reason is that recipes should be able to
amend or replace them at any place.
CPPFLAGS: Preprocessor options, e.g.,-DMACRO=definition.CFLAGS: Compiler options that are used when compiling C-code.CXXFLAGS: Compiler options that are used when compiling C++-code.LDFLAGS: Options used when linking. Note that they are passed to the compiler driver (e.g.,gccorclang) and therefore need to be wrapped appropriately (e.g.,-Wl,<option>in case ofgccorclang).
Feature variables
For some architectures, the cross compilation toolchains provide variables that
indicate the available features of the selected target architecture. This
information is derived from the toolchain defaults and any
CROSS_TOOLCHAIN_ARCH and CROSS_TOOLCHAIN_CPU settings made.
Arm:
CPU_HAS_VFPV2,CPU_HAS_VFPV3,CPU_HAS_VFPV4,CPU_HAS_NEONArm64:
CPU_HAS_SVE,CPU_HAS_SVE2,CPU_HAS_SMEx86_64:
CPU_HAS_SSE3,CPU_HAS_SSSE3,CPU_HAS_SSE41,CPU_HAS_SSE42,CPU_HAS_AVX,CPU_HAS_AVX2,CPU_HAS_AVX512. All CPU features before and including SSE2 are implicitly assumed to be present.
Standard build systems
The following build tools are supported by the basement layer. See the respective section below for the particular usage notes.
CMake
Python 3
Perl
Ocaml / opam / dune
Ocaml is available for building ocaml host tools only. ATM there is no cross compiling support.
See tests/linux/recipes/ocaml/hello.yaml for a hello world example using dune.
Rust
Available development tools
The following tools can be used by naming them in {checkout,build,package}Tools:
autotools (comprised of autoconf, automake and libtool)
bison
clang
cmake
flex
gettext
help2man
llvm
m4
make
meson
ninja
pkg-config
python3
texinfo
util-linux
They are provided by default by the basement::rootrecipe class. Usually,
these tools are picked up automatically by the respective classes and it is not
necessary to name them explicitly.