Skip to content

Commit c009f29

Browse files
committed
doc: shed some more light on C/C++ toolchains
1 parent 0bda26d commit c009f29

1 file changed

Lines changed: 376 additions & 0 deletions

File tree

doc/usage.rst

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,382 @@ available.
3232
C/C++ toolchains
3333
----------------
3434

35+
Before delving into the details, some definitions are necessary. The basement layer
36+
uses the same terms that were establisdhed by the autoconf project. In principle, three
37+
different systems are distinguished:
38+
39+
The *build* system
40+
This is the system where the build is executed, i.e. where Bob and the
41+
compiler is running. The ``AUTOCONF_BUILD`` variable describes the build
42+
system and is always defined.
43+
44+
The *host* system
45+
The host system is where the compiled binaries are meant to be executed.
46+
For embedded build systems, this is sometimes called the "target" system.
47+
The ``AUTOCONF_HOST`` variable represents this system. If set and different
48+
than ``AUTOCONF_BUILD``, the package is cross compiled.
49+
50+
The *target* system
51+
This system is only applicable to compilers and their related tools. It is
52+
stored in the ``AUTOCONF_TARGET`` variable. It describes the system for
53+
which the compiler produces the object code. For a cross-compiler, the
54+
*target* system is different from the *host* system where the compiler is
55+
executed. Of *build* and *target* system are identical, it is called a
56+
native compiler.
57+
58+
The different systems are described as a so-called *target triplet*. Even
59+
though it is used ubiquitously, it is only loosely defined. In fact, it may not
60+
even have exactly three fields as the name suggests. It generally has the
61+
format of ``arch-vendor-system`` where ``system`` may either be the ``os``
62+
(operating system) or ``kernel-os``. See the `autoconf documentation
63+
<https://autotools.info/autoconf/canonical.html>`_.
64+
65+
In almost all cases, projects will use cross compilation. This is even the case
66+
where the build system and the host system have the same architecture and
67+
operating system. The rationale is to be as independent from the build system
68+
as possible. Using native compiler always has the drawback that the result
69+
relies at least on the libc of the build system and is thus not portable across
70+
machines.
71+
72+
Selecting a C/C++-toolchain
73+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
74+
75+
To select the desired toolchain, add a dependency in the following format early
76+
in your project dependency list::
77+
78+
depends:
79+
- name: <toolchain name here>
80+
use: [tools, environment]
81+
forward: True
82+
83+
The following toolchains are predefined for commonly used target systems:
84+
85+
* ``devel::cross-toolchain-aarch64-linux-gnu``: ARMv8-A AArch64 Linux with glibc.
86+
* ``devel::cross-toolchain-aarch64-none-elf``: ARMv8-A/R AArch64 bare metal
87+
toolchain with newlib libc.
88+
* ``devel::cross-toolchain-arm-linux-gnueabihf``: ARMv7-A Linux with glibc. Hard
89+
floating point ABI.
90+
* ``devel::cross-toolchain-arm-none-eabi``: ARMv7 bare metal toolchain with
91+
newlib libc.
92+
* ``devel::cross-toolchain-x86_64-linux-gnu``: x86_64 toolchain for Linux with glibc.
93+
* ``devel::cross-toolchain-riscv64-linux-gnu``: RISC-V toolchain targeting the GC
94+
profile.
95+
96+
Read on to learn how to switch to different toolchains for selected
97+
dependencies or how to define your own if the standard ones are not sufficient.
98+
99+
All toolchains compile for a reasonable default architecture model that is
100+
supposed to be widely supported. To tweak the standard compile flags, the
101+
following variables may be optionally set when pulling in the toolchain.
102+
103+
``BASEMENT_OPTIMIZE``
104+
Compiler optimization level (``-O``). Defaults to ``s`` to optimize for
105+
small binaries.
106+
107+
``BASEMENT_DEBUG``
108+
May be ``0`` or ``1`` and controls the generation of debugging information.
109+
Defaults to ``1``.
110+
111+
``CROSS_TOOLCHAIN_CPU``
112+
If set, adds an ``-mcpu=`` option to the compiler flags with the value. It
113+
overrides the default CPU selection of the toolchain.
114+
115+
``CROSS_TOOLCHAIN_ARCH``
116+
If set, adds an ``-march=`` option to the compiler flags with the value. It
117+
overrides the default architecture selection of the toolchain.
118+
119+
Standard tools
120+
~~~~~~~~~~~~~~
121+
122+
There are two tools that are meant to be used by recipes that compile C/C++
123+
code.
124+
125+
``target-toolchain``
126+
This is the main toolchain. Every C/C++ package uses it. It represents the
127+
compiler that builds for the target system where the package should run in
128+
the end. Usually, but not necessarily, this is a cross compiler even on the
129+
same architecture.
130+
131+
A recipe should make no assumption about which compiler this is and for
132+
which architecture or operating system it compiles. This is the key
133+
ingredient for making Bob projects flexible because the
134+
``target-toolchain`` may be replaced anywhere in the dependency tree and
135+
all dependencies beneath it will automatically be compiled for the
136+
configured target.
137+
138+
``host-toolchain``
139+
This toolchain represents the native host machine compiler. Even though it
140+
builds host executables, it does never :external:ref:`fingerprint
141+
<configuration-principle-fingerprinting>` the results. Instead, it is
142+
intended to be used in the ``buildScript`` if the package *also* needs the
143+
host compiler during build time where none of the host build object code is
144+
part of the result. Points to the host gcc or the gcc of the sandbox. Only
145+
selected packages need it when being built in the sandbox.
146+
147+
Given the above definitions, practically all recipes that build C/C++ code will do
148+
a::
149+
150+
buildTool: [target-toolchain]
151+
152+
to use the currently selected C/C++ compiler. Only if the build requires the
153+
native compiler too (e.g. to build some intermediate build tool),
154+
``host-toolchain`` may be added to ``buildTool``.
155+
156+
There are two other tools that are always defined. They are intended to be used
157+
at special places where they replace the ``target-toolchain`` for selected
158+
dependencies.
159+
160+
``host-compat-toolchain``
161+
A toolchain that builds portable host executables that should be able to
162+
run on the oldest supported Ubuntu LTS. Even though it builds for the host
163+
architecture and operating system, it is a cross compiler with a backwards
164+
compatible glibc version. When using the ``basement::rootrecipe`` class,
165+
this is the default ``target-toolchain``. It is defined as a dedicated name
166+
to be able to compile specifically for the host when needed::
167+
168+
depends:
169+
- ...
170+
- name: some::package
171+
tools:
172+
target-toolchain: host-compat-toolchain
173+
174+
This will build ``some::package`` for the host regardless of the currently
175+
defined target toolchain. It comes in handy if some special tool is needed
176+
to compile a package.
177+
178+
``host-native-toolchain``
179+
This toolchain represents the native host machine compiler. In contrast to
180+
``host-toolchain`` it *does* fingerprint the system. This implies that
181+
binary artifacts of such packages are not exchangeable between systems! It
182+
is used if a package needs to be compiled natively and the object code is
183+
part of the package result. Like in the ``host-compat-toolchain`` example
184+
above, it is usually supplied as ``target-toolchain`` for selected
185+
dependencies.
186+
187+
An example for the necessity of the ``host-native-toolchain`` is for
188+
example Python. To cross-compile python, the same version is required on
189+
the build system. Therefore, Python needs to be first compiled natively.
190+
Then Python can be cross compiled by whatever ``target-toolchain`` is
191+
configured. See the following excerpt from the ``basement::rootrecipe``
192+
class where this is already done for you::
193+
194+
depends:
195+
- name: python::python3-minimal
196+
use: [tools]
197+
forward: True
198+
tools:
199+
# To build python3 a working python interpreter is required. Build
200+
# a bootstrap python3 interpreter with the native host toolchain.
201+
# The real interpreter is then built with the
202+
# host-compat-toolchain.
203+
target-toolchain: host-native-toolchain
204+
205+
- python::python3
206+
207+
Switching cross-compilers
208+
~~~~~~~~~~~~~~~~~~~~~~~~~
209+
210+
Once a cross-compiling toolchain has been selected, all following dependencies
211+
are built by this compiler. As this applies to all packages, selecting a
212+
different cross compiler requires some special care. Suppose a root recipe has
213+
the following (intentionally incorrect!) dependency list::
214+
215+
inherit: ["basement::rootrecipe"]
216+
depends:
217+
- name: devel::cross-toolchain-aarch64-linux-gnu
218+
use: [tools, environment]
219+
forward: True
220+
221+
- utils::bash
222+
223+
- name: devel::cross-toolchain-arm-none-eabi
224+
use: [tools, environment]
225+
forward: True
226+
227+
- some::firmware
228+
229+
.. warning::
230+
The example above does *not* work but is used as an illustration what needs
231+
to be considered.
232+
233+
The above example will unfortunately not work as expected. The reason is that after
234+
the ``devel::cross-toolchain-aarch64-linux-gnu`` dependency, *everything* will be
235+
compiled for Linux AArch64. This includes the ``devel::cross-toolchain-arm-none-eabi``
236+
dependency too! But this compiler needs to be executed on the build system. Therefore,
237+
the ``target-toolchain`` used for the compiler needs to be replaced with the
238+
``host-compat-toolchain``::
239+
240+
depends:
241+
...
242+
- name: devel::cross-toolchain-arm-none-eabi
243+
use: [tools, environment]
244+
forward: True
245+
tools:
246+
target-toolchain: host-compat-toolchain
247+
248+
- some::firmware
249+
250+
As you can see, the ``devel::cross-toolchain-arm-none-eabi`` is built
251+
explicitly with the ``host-compat-toolchain``, regardless of which other
252+
toolchain is configured at this point.
253+
254+
Installing a compiler in the target system
255+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256+
257+
Sometimes, the toolchain should be installed on the target system. This works
258+
like for any other package. The only difference is that the ``use`` list does
259+
not have the ``tools`` key because the compiler should be installed rather than
260+
used at build time::
261+
262+
inherit: ["basement::rootrecipe"]
263+
depends:
264+
# The toolchain for the target system
265+
- name: devel::cross-toolchain-aarch64-linux-gnu
266+
use: [tools, environment]
267+
forward: True
268+
269+
# The native compiler and binutils for the target system
270+
- devel::binutils
271+
- devel::gcc-native
272+
273+
The above example installs a native compiler into the target system. That is, this compiler
274+
will produce binaries for the same system. Similarly, a cross-compiler could be installed
275+
as well::
276+
277+
inherit: ["basement::rootrecipe"]
278+
depends:
279+
# The toolchain for the target system
280+
- name: devel::cross-toolchain-aarch64-linux-gnu
281+
use: [tools, environment]
282+
forward: True
283+
284+
- devel::cross-toolchain-arm-none-eabi
285+
286+
The toolchain will be compiled for the AArch64 Linux system and will produce
287+
object code for AArch32 bare-metal systems. Note the absence of the ``use:
288+
[tools, environment]`` and ``forward: True`` lines from the
289+
``devel::cross-toolchain-arm-none-eabi`` dependency.
290+
291+
Advanced toolchain selection
292+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293+
294+
If the pre-configured toolchains are not sufficient, it is possible to compile
295+
almost any custom GNU gcc/binutils based Linux toolchain. Starting point is
296+
the generic ``devel::cross-toolchain`` recipe. This unconfigured cross-compilation
297+
toolchain needs to be configured. At least the following variables need to be
298+
defined for it:
299+
300+
``ARCH``
301+
The target architecture. This is based on the architectures as defined by
302+
the Linux kernel. Valid choices are, among others, ``arm``, ``arm64``,
303+
``i386``, ``x86_64`` or ``riscv``. See the Linux kernel documentation for
304+
all possible values.
305+
306+
``AUTOCONF_TARGET``
307+
The autoconf target triplet that describes the system. This is the primary
308+
variable that affects the toolchain and needs to be aligned with the other
309+
switches. See below for some rough guidelines.
310+
311+
``GCC_LIBC``
312+
The C-library that should be used by the toolchain. Valid choices are
313+
``glibc``, ``newlib`` and ``uclibc-ng``.
314+
315+
The following, additional variables are available to tweak the toolchain:
316+
317+
``GCC_TARGET_ARCH``
318+
This is passed as ``--with-arch=`` to the gcc configure script and provides
319+
the default value for the ``-march=`` gcc option. As such, it sets the
320+
default target architecture that the compiler is using. It is recommended
321+
to pass this switch to choose the right architectural features. See the
322+
`GCC machine dependent options
323+
<https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Submodel-Options.html>`_ for
324+
the supported values of the ``-march=`` option.
325+
326+
``GCC_TARGET_ABI``
327+
Passed as ``--with-abi=`` to the gcc configure script and provides the
328+
default value for the ``-mabi=`` option. This is used for example for
329+
RISC-V to choose between the different possible ABIs.
330+
331+
``GCC_TARGET_FLOAT_ABI``
332+
May be either ``hard`` or ``soft``.
333+
334+
``GCC_TARGET_FPU``
335+
Passed as ``--with-fpu=`` to the gcc configure script and provides the
336+
default value for the ``-mfpu=`` option. Again, the acceptable values
337+
depend on the chosen target.
338+
339+
``GCC_MULTILIB``
340+
If set, provides the comma separated set of multilibs to build. The
341+
permissible values depend on the target architecture. Currently, the
342+
basement layer only supports ``m32,m64`` on ``x86_64``.
343+
344+
``GCC_ENABLE_LANGUAGES``
345+
Comma separated list of languages that gcc should support. Defaults to
346+
``c,c++``.
347+
348+
``GCC_EXTRA_OPTIONS``
349+
If set, it is passed verbatim to the gcc configure script.
350+
351+
TODO: Explain target triplet choices.
352+
353+
Standard variables for C/C++ packages
354+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
355+
356+
When using the ``target-toolchain``, the following variables are available. The
357+
variables have the same name as the executable that is normally available on
358+
the build system.
359+
360+
* ``AR``: The archiver to create/modify static libraries.
361+
* ``AS``: The assembler.
362+
* ``CC``: The C-compiler.
363+
* ``CPP``: The C preprocessor.
364+
* ``CXX``: The C++-compiler.
365+
* ``LD``: The linker.
366+
* ``NM``: Tool to inspect object symbol tables.
367+
* ``OBJCOPY``: Tool to copy and translate object files.
368+
* ``OBJDUMP``: Print object file contents.
369+
* ``RANLIB``: Tool to (re-)generate symbol index of a static library.
370+
* ``READELF``: Display information about ELF files.
371+
* ``STRIP``: Tool for stripping unneeded sections and symbols from object files.
372+
373+
Other meta information variables that are not directly linked to a particular
374+
executable are:
375+
376+
* ``AUTOCONF_HOST``: Set for cross-compiler to the *host* system target triplet.
377+
* ``CROSS_COMPILE``: Cross compile prefix for standard tool of a
378+
cross-compiling toolchain, e.g., ``riscv64-linux-gnu-`` for a RISC-V Linux
379+
cross toolchain. Some build systems use this method to find the right tools
380+
instead of the individual variables above (``AR``, ...).
381+
382+
.. attention::
383+
The above variables are defined by ``target-toolchain`` only. If it is
384+
missing from ``buildTools``, they will be undefined!
385+
386+
The following variables are not defined by ``target-toolchain`` but are part of
387+
the normal environment variables. The reason is that recipes should be able to
388+
amend or replace them at any place.
389+
390+
* ``CPPFLAGS``: Preprocessor options, e.g., ``-DMACRO=definition``.
391+
* ``CFLAGS``: Compiler options that are used when compiling C-code.
392+
* ``CXXFLAGS``: Compiler options that are used when compiling C++-code.
393+
* ``LDFLAGS``: Options used when linking. Note that they are passed to the
394+
compiler driver (e.g., ``gcc`` or ``clang``) and therefore need to be wrapped
395+
appropriately (e.g., ``-Wl,<option>`` in case of ``gcc`` or ``clang``).
396+
397+
Feature variables
398+
~~~~~~~~~~~~~~~~~
399+
400+
For some architectures, the cross compilation toolchains provide variables that
401+
indicate the available features of the selected target architecture. This
402+
information is derived from the toolchain defaults and any
403+
``CROSS_TOOLCHAIN_ARCH`` and ``CROSS_TOOLCHAIN_CPU`` settings made.
404+
405+
* Arm: ``CPU_HAS_VFPV2``, ``CPU_HAS_VFPV3``, ``CPU_HAS_VFPV4``, ``CPU_HAS_NEON``
406+
* Arm64: ``CPU_HAS_SVE``, ``CPU_HAS_SVE2``, ``CPU_HAS_SME``
407+
* x86_64: ``CPU_HAS_SSE3``, ``CPU_HAS_SSSE3``, ``CPU_HAS_SSE41``,
408+
``CPU_HAS_SSE42``, ``CPU_HAS_AVX``, ``CPU_HAS_AVX2``, ``CPU_HAS_AVX512``. All
409+
CPU features before and including SSE2 are implicitly assumed to be present.
410+
35411
Standard build systems
36412
----------------------
37413

0 commit comments

Comments
 (0)