cmake_framework.rst 6.5 KB

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