You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
420 lines
14 KiB
420 lines
14 KiB
.. _cmake-details: |
|
|
|
Build System (CMake) |
|
******************** |
|
|
|
|
|
CMake is used to build your application together with the Zephyr kernel. A |
|
CMake build is done in two stages. The first stage is called |
|
**configuration**. During configuration, the CMakeLists.txt build scripts are |
|
executed. After configuration is finished, CMake has an internal model of the |
|
Zephyr build, and can generate build scripts that are native to the host |
|
platform. |
|
|
|
CMake supports generating scripts for several build systems, but only Ninja and |
|
Make are tested and supported by Zephyr. After configuration, you begin the |
|
**build** stage by executing the generated build scripts. These build scripts |
|
can recompile the application without involving CMake following |
|
most code changes. However, after certain changes, the configuration step must |
|
be executed again before building. The build scripts can detect some of these |
|
situations and reconfigure automatically, but there are cases when this must be |
|
done manually. |
|
|
|
Zephyr uses CMake's concept of a 'target' to organize the build. A |
|
target can be an executable, a library, or a generated file. For |
|
application developers, the library target is the most important to |
|
understand. All source code that goes into a Zephyr build does so by |
|
being included in a library target, even application code. |
|
|
|
Library targets have source code, that is added through CMakeLists.txt |
|
build scripts like this: |
|
|
|
.. code-block:: cmake |
|
|
|
target_sources(app PRIVATE src/main.c) |
|
|
|
In the above :file:`CMakeLists.txt`, an existing library target named ``app`` |
|
is configured to include the source file :file:`src/main.c`. The ``PRIVATE`` |
|
keyword indicates that we are modifying the internals of how the library is |
|
being built. Using the keyword ``PUBLIC`` would modify how other |
|
libraries that link with app are built. In this case, using ``PUBLIC`` |
|
would cause libraries that link with ``app`` to also include the |
|
source file :file:`src/main.c`, behavior that we surely do not want. The |
|
``PUBLIC`` keyword could however be useful when modifying the include |
|
paths of a target library. |
|
|
|
|
|
Build and Configuration Phases |
|
============================== |
|
|
|
The Zephyr build process can be divided into two main phases: a configuration |
|
phase (driven by CMake) and a build phase (driven by Make or Ninja). |
|
|
|
.. _build_configuration_phase: |
|
|
|
Configuration Phase |
|
------------------- |
|
|
|
The configuration phase begins when the user invokes *CMake* to generate a |
|
build system, specifying a source application directory and a board target. |
|
|
|
.. figure:: build-config-phase.svg |
|
:align: center |
|
:alt: Zephyr's build configuration phase |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
CMake begins by processing the :file:`CMakeLists.txt` file in the application |
|
directory, which refers to the :file:`CMakeLists.txt` file in the Zephyr |
|
top-level directory, which in turn refers to :file:`CMakeLists.txt` files |
|
throughout the build tree (directly and indirectly). Its primary output is a |
|
set of Makefiles or Ninja files to drive the build process, but the CMake |
|
scripts also do some processing of their own, which is explained here. |
|
|
|
Note that paths beginning with :file:`build/` below refer to the build |
|
directory you create when running CMake. |
|
|
|
Devicetree |
|
:file:`*.dts` (*devicetree source*) and :file:`*.dtsi` (*devicetree source |
|
include*) files are collected from the target's architecture, SoC, board, |
|
and application directories. |
|
|
|
:file:`*.dtsi` files are included by :file:`*.dts` files via the C |
|
preprocessor (often abbreviated *cpp*, which should not be confused with |
|
C++). The C preprocessor is also used to merge in any devicetree |
|
:file:`*.overlay` files, and to expand macros in :file:`*.dts`, |
|
:file:`*.dtsi`, and :file:`*.overlay` files. The preprocessor output is |
|
placed in :file:`build/zephyr/zephyr.dts.pre`. |
|
|
|
The preprocessed devicetree sources are parsed by |
|
:zephyr_file:`gen_defines.py <scripts/dts/gen_defines.py>` to generate a |
|
:file:`build/zephyr/include/generated/zephyr/devicetree_generated.h` header with |
|
preprocessor macros. |
|
|
|
Source code should access preprocessor macros generated from devicetree by |
|
including the :zephyr_file:`devicetree.h <include/zephyr/devicetree.h>` header, |
|
which includes :file:`devicetree_generated.h`. |
|
|
|
:file:`gen_defines.py` also writes the final devicetree to |
|
:file:`build/zephyr/zephyr.dts` in the build directory. This file's contents |
|
may be useful for debugging. |
|
|
|
If the devicetree compiler ``dtc`` is installed, it is run on |
|
:file:`build/zephyr/zephyr.dts` to catch any extra warnings and errors |
|
generated by this tool. The output from ``dtc`` is unused otherwise, and |
|
this step is skipped if ``dtc`` is not installed. |
|
|
|
The above is just a brief overview. For more information on devicetree, see |
|
:ref:`dt-guide`. |
|
|
|
Kconfig |
|
:file:`Kconfig` files define available configuration options for the |
|
target architecture, SoC, board, and application, as well as dependencies |
|
between options. |
|
|
|
Kconfig configurations are stored in *configuration files*. The initial |
|
configuration is generated by merging configuration fragments from the board |
|
and application (e.g. :file:`prj.conf`). |
|
|
|
The output from Kconfig is an :file:`autoconf.h` header with preprocessor |
|
assignments, and a :file:`.config` file that acts both as a saved |
|
configuration and as configuration output (used by CMake). The definitions in |
|
:file:`autoconf.h` are automatically exposed at compile time, so there is no |
|
need to include this header. |
|
|
|
Information from devicetree is available to Kconfig, through the functions |
|
defined in :zephyr_file:`kconfigfunctions.py |
|
<scripts/kconfig/kconfigfunctions.py>`. |
|
|
|
See :ref:`the Kconfig section of the manual <kconfig>` for more information. |
|
|
|
Build Phase |
|
----------- |
|
|
|
The build phase begins when the user invokes ``make`` or ``ninja``. Its |
|
ultimate output is a complete Zephyr application in a format suitable for |
|
loading/flashing on the desired target board (:file:`zephyr.elf`, |
|
:file:`zephyr.hex`, etc.) The build phase can be broken down, conceptually, |
|
into four stages: the pre-build, first-pass binary, final binary, and |
|
post-processing. |
|
|
|
Pre-build |
|
+++++++++ |
|
|
|
Pre-build occurs before any source files are compiled, because during |
|
this phase header files used by the source files are generated. |
|
|
|
Offset generation |
|
Access to high-level data structures and members is sometimes |
|
required when the definitions of those structures is not |
|
immediately accessible (e.g., assembly language). The generation of |
|
*offsets.h* (by *gen_offset_header.py*) facilitates this. |
|
|
|
System call boilerplate |
|
The *gen_syscall.py* and *parse_syscalls.py* scripts work |
|
together to bind potential system call functions with their |
|
implementations. |
|
|
|
.. figure:: build-build-phase-1.svg |
|
:align: center |
|
:alt: Zephyr's build stage I |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
Intermediate binaries |
|
+++++++++++++++++++++ |
|
|
|
Compilation proper begins with the first intermediate binary. Source files (C |
|
and assembly) are collected from various subsystems (which ones is |
|
decided during the configuration phase), and compiled into archives |
|
(with reference to header files in the tree, as well as those |
|
generated during the configuration phase and the pre-build stage(s)). |
|
|
|
.. figure:: build-build-phase-2.svg |
|
:align: center |
|
:alt: Zephyr's build stage II |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
The exact number of intermediate binaries is decided during the configuration |
|
phase. |
|
|
|
If memory protection is enabled, then: |
|
|
|
Partition grouping |
|
The *gen_app_partitions.py* script scans all the |
|
generated archives and outputs linker scripts to ensure that |
|
application partitions are properly grouped and aligned for the |
|
target’s memory protection hardware. |
|
|
|
Then *cpp* is used to combine linker script fragments from the target’s |
|
architecture/SoC, the kernel tree, optionally the partition output if |
|
memory protection is enabled, and any other fragments selected during |
|
the configuration process, into a *linker.cmd* file. The compiled |
|
archives are then linked with *ld* as specified in the |
|
*linker.cmd*. |
|
|
|
Unfixed size binary |
|
The unfixed size intermediate binary is produced when :ref:`usermode_api` |
|
is enabled or :ref:`devicetree` is in use. |
|
It produces a binary where sizes are not fixed and thus it may be used |
|
by post-process steps that will impact the size of the final binary. |
|
|
|
.. figure:: build-build-phase-3.svg |
|
:align: center |
|
:alt: Zephyr's build stage III |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
Fixed size binary |
|
The fixed size intermediate binary is produced when :ref:`usermode_api` |
|
is enabled or when generated IRQ tables are used, |
|
:kconfig:option:`CONFIG_GEN_ISR_TABLES` |
|
It produces a binary where sizes are fixed and thus the size must not change |
|
between the intermediate binary and the final binary. |
|
|
|
.. figure:: build-build-phase-4.svg |
|
:align: center |
|
:alt: Zephyr's build stage IV |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
Intermediate binaries post-processing |
|
+++++++++++++++++++++++++++++++++++++ |
|
|
|
The binaries from the previous stage are incomplete, with empty and/or |
|
placeholder sections that must be filled in by, essentially, reflection. |
|
|
|
To complete the build procedure the following scripts are executed on the |
|
intermediate binaries to produce the missing pieces needed for the final |
|
binary. |
|
|
|
When :ref:`usermode_api` is enabled: |
|
|
|
Partition alignment |
|
The *gen_app_partitions.py* script scans the unfixed size binary and |
|
generates an app shared memory aligned linker script snippet where the |
|
partitions are sorted in descending order. |
|
|
|
.. figure:: build-postprocess-1.svg |
|
:align: center |
|
:alt: Zephyr's intermediate binary post-process I |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
When :ref:`devicetree` is used: |
|
|
|
Device dependencies |
|
The *gen_device_deps.py* script scans the unfixed size binary to determine |
|
relationships between devices that were recorded from devicetree data, |
|
and replaces the encoded relationships with values that are optimized to |
|
locate the devices actually present in the application. |
|
|
|
.. figure:: build-postprocess-2.svg |
|
:align: center |
|
:alt: Zephyr's intermediate binary post-process II |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
When :kconfig:option:`CONFIG_GEN_ISR_TABLES` is enabled: |
|
The *gen_isr_tables.py* script scans the fixed size binary and creates |
|
an isr_tables.c source file with a hardware vector table and/or software |
|
IRQ table. |
|
|
|
.. figure:: build-postprocess-3.svg |
|
:align: center |
|
:alt: Zephyr's intermediate binary post-process III |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
When :ref:`usermode_api` is enabled: |
|
|
|
Kernel object hashing |
|
The *gen_kobject_list.py* scans the *ELF DWARF* |
|
debug data to find the address of the all kernel objects. This |
|
list is passed to *gperf*, which generates a perfect hash function and |
|
table of those addresses, then that output is optimized by |
|
*process_gperf.py*, using known properties of our special case. |
|
|
|
.. figure:: build-postprocess-4.svg |
|
:align: center |
|
:alt: Zephyr's intermediate binary post-process IV |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
When no intermediate binary post-processing is required then the first |
|
intermediate binary will be directly used as the final binary. |
|
|
|
Final binary |
|
++++++++++++ |
|
|
|
The binary from the previous stage is incomplete, with empty and/or |
|
placeholder sections that must be filled in by, essentially, reflection. |
|
|
|
The link from the previous stage is repeated, this time with the missing |
|
pieces populated. |
|
|
|
.. figure:: build-build-phase-5.svg |
|
:align: center |
|
:alt: Zephyr's build final stage |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
Post processing |
|
+++++++++++++++ |
|
|
|
Finally, if necessary, the completed kernel is converted from *ELF* to |
|
the format expected by the loader and/or flash tool required by the |
|
target. This is accomplished in a straightforward manner with *objdump*. |
|
|
|
.. figure:: build-build-phase-6.svg |
|
:align: center |
|
:alt: Zephyr's build final stage post-process |
|
:figclass: align-center |
|
:width: 80% |
|
|
|
|
|
.. _build_system_scripts: |
|
|
|
Supporting Scripts and Tools |
|
============================ |
|
|
|
The following is a detailed description of the scripts used during the build process. |
|
|
|
.. _gen_syscalls.py: |
|
|
|
:zephyr_file:`scripts/build/gen_syscalls.py` |
|
-------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/gen_syscalls.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_device_deps.py: |
|
|
|
:zephyr_file:`scripts/build/gen_device_deps.py` |
|
----------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/gen_device_deps.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_kobject_list.py: |
|
|
|
:zephyr_file:`scripts/build/gen_kobject_list.py` |
|
------------------------------------------------ |
|
|
|
.. include:: ../../../scripts/build/gen_kobject_list.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_offset_header.py: |
|
|
|
:zephyr_file:`scripts/build/gen_offset_header.py` |
|
------------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/gen_offset_header.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _parse_syscalls.py: |
|
|
|
:zephyr_file:`scripts/build/parse_syscalls.py` |
|
---------------------------------------------- |
|
|
|
|
|
.. include:: ../../../scripts/build/parse_syscalls.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_idt.py: |
|
|
|
:zephyr_file:`arch/x86/gen_idt.py` |
|
---------------------------------- |
|
|
|
.. include:: ../../../arch/x86/gen_idt.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_gdt.py: |
|
|
|
:zephyr_file:`arch/x86/gen_gdt.py` |
|
---------------------------------- |
|
|
|
.. include:: ../../../arch/x86/gen_gdt.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _gen_relocate_app.py: |
|
|
|
:zephyr_file:`scripts/build/gen_relocate_app.py` |
|
------------------------------------------------ |
|
|
|
.. include:: ../../../scripts/build/gen_relocate_app.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _process_gperf.py: |
|
|
|
:zephyr_file:`scripts/build/process_gperf.py` |
|
--------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/process_gperf.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
:zephyr_file:`scripts/build/gen_app_partitions.py` |
|
-------------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/gen_app_partitions.py |
|
:start-after: """ |
|
:end-before: """ |
|
|
|
.. _check_init_priorities.py: |
|
|
|
:zephyr_file:`scripts/build/check_init_priorities.py` |
|
----------------------------------------------------- |
|
|
|
.. include:: ../../../scripts/build/check_init_priorities.py |
|
:start-after: """ |
|
:end-before: """
|
|
|