blob: d88942e822c695a223afd7f5d7b3bc975a4b2df4 [file] [log] [blame]
Balint Dobszay637f4732019-11-13 12:48:00 +01001TF-A CMake buildsystem
2======================
3
4:Author: Balint Dobszay
5:Organization: Arm Limited
6:Contact: Balint Dobszay <balint.dobszay@arm.com>
7:Status: Accepted
8
9.. contents:: Table of Contents
10
11Abstract
12--------
13This document presents a proposal for a new buildsystem for TF-A using CMake,
14and as part of this a reusable CMake framework for embedded projects. For a
15summary about the proposal, please see the `Phabricator wiki page
16<https://developer.trustedfirmware.org/w/tf_a/cmake-buildsystem-proposal/>`_. As
17mentioned there, the proposal consists of two phases. The subject of this
18document is the first phase only.
19
20Introduction
21------------
22The current Makefile based buildsystem of TF-A has become complicated and hard
23to maintain, there is a need for a new, more flexible solution. The proposal is
24to use CMake language for the new buildsystem. The main reasons of this decision
25are the following:
26
27* It is a well-established, mature tool, widely accepted by open-source
28 projects.
29* TF-M is already using CMake, reducing fragmentation for tf.org projects can be
30 beneficial.
31* CMake has various advantages over Make, e.g.:
32
33 * Host and target system agnostic project.
34 * CMake project is scalable, supports project modularization.
35 * Supports software integration.
36 * Out-of-the-box support for integration with several tools (e.g. project
37 generation for various IDEs, integration with cppcheck, etc).
38
39Of course there are drawbacks too:
40
41* Language is problematic (e.g. variable scope).
42* Not embedded approach.
43
44To overcome these and other problems, we need to create workarounds for some
45tasks, wrap CMake functions, etc. Since this functionality can be useful in
46other embedded projects too, it is beneficial to collect the new code into a
47reusable framework and store this in a separate repository. The following
48diagram provides an overview of the framework structure:
49
50|Framework structure|
51
52Main features
53-------------
54
55Structured configuration description
56^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57In the current Makefile system the build configuration description, validation,
58processing, and the target creation, source file description are mixed and
59spread across several files. One of the goals of the framework is to organize
60this.
61
62The framework provides a solution to describe the input build parameters, flags,
63macros, etc. in a structured way. It contains two utilities for this purpose:
64
65* Map: simple key-value pair implementation.
66* Group: collection of related maps.
67
68The related parameters shall be packed into a group (or "setting group"). The
69setting groups shall be defined and filled with content in config files.
70Currently the config files are created and edited manually, but later a
71configuration management tool (e.g. Kconfig) shall be used to generate these
72files. Therefore, the framework does not contain parameter validation and
73conflict checking, these shall be handled by the configuration tool.
74
75Target description
76^^^^^^^^^^^^^^^^^^
77The framework provides an API called STGT ('simple target') to describe the
78targets, i.e. what is the build output, what source files are used, what
79libraries are linked, etc. The API wraps the CMake target functions, and also
80extends the built-in functionality, it can use the setting groups described in
81the previous section. A group can be applied onto a target, i.e. a collection of
82macros, flags, etc. can be applied onto the given output executable/library.
83This provides a more granular way than the current Makefile system where most of
84these are global and applied onto each target.
85
86Compiler abstraction
87^^^^^^^^^^^^^^^^^^^^
88Apart from the built-in CMake usage of the compiler, there are some common tasks
89that CMake does not solve (e.g. preprocessing a file). For these tasks the
90framework uses wrapper functions instead of direct calls to the compiler. This
91way it is not tied to one specific compiler.
92
93External tools
94^^^^^^^^^^^^^^
95In the TF-A buildsystem some external tools are used, e.g. fiptool for image
96generation or dtc for device tree compilation. These tools have to be found
97and/or built by the framework. For this, the CMake find_package functionality is
98used, any other necessary tools can be added later.
99
100Workflow
101--------
102The following diagram demonstrates the development workflow using the framework:
103
104|Framework workflow|
105
106The process can be split into two main phases:
107
108In the provisioning phase, first we have to obtain the necessary resources, i.e.
109clone the code repository and other dependencies. Next we have to do the
110configuration, preferably using a config tool like KConfig.
111
112In the development phase first we run CMake, which will generate the buildsystem
113using the selected generator backend (currently only the Makefile generator is
114supported). After this we run the selected build tool which in turn calls the
115compiler, linker, packaging tool, etc. Finally we can run and debug the output
116executables.
117
118Usually during development only the steps in this second phase have to be
119repeated, while the provisioning phase needs to be done only once (or rarely).
120
121Example
122-------
123This is a short example for the basic framework usage.
124
125First, we create a setting group called *mem_conf* and fill it with several
126parameters. It is worth noting the difference between *CONFIG* and *DEFINE*
127types: the former is only a CMake domain option, the latter is only a C language
128macro.
129
130Next, we create a target called *fw1* and add the *mem_conf* setting group to
131it. This means that all source and header files used by the target will have all
132the parameters declared in the setting group. Then we set the target type to
133executable, and add some source files. Since the target has the parameters from
134the settings group, we can use it for conditionally adding source files. E.g.
135*dram_controller.c* will only be added if MEM_TYPE equals dram.
136
137.. code-block:: cmake
138
139 group_new(NAME mem_conf)
140 group_add(NAME mem_conf TYPE DEFINE KEY MEM_SIZE VAL 1024)
141 group_add(NAME mem_conf TYPE CONFIG DEFINE KEY MEM_TYPE VAL dram)
142 group_add(NAME mem_conf TYPE CFLAG KEY -Os)
143
144 stgt_create(NAME fw1)
145 stgt_add_setting(NAME fw1 GROUPS mem_conf)
146 stgt_set_target(NAME fw1 TYPE exe)
147
148 stgt_add_src(NAME fw1 SRC
149 ${CMAKE_SOURCE_DIR}/main.c
150 )
151
152 stgt_add_src_cond(NAME fw1 KEY MEM_TYPE VAL dram SRC
153 ${CMAKE_SOURCE_DIR}/dram_controller.c
154 )
155
156.. |Framework structure| image::
157 ../resources/diagrams/cmake_framework_structure.png
158 :width: 75 %
159
160.. |Framework workflow| image::
161 ../resources/diagrams/cmake_framework_workflow.png
162
163--------------
164
165*Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.*