diff options
Diffstat (limited to 'documentation/dev-manual/multiconfig.rst')
-rw-r--r-- | documentation/dev-manual/multiconfig.rst | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/documentation/dev-manual/multiconfig.rst b/documentation/dev-manual/multiconfig.rst new file mode 100644 index 0000000000..71fe542efb --- /dev/null +++ b/documentation/dev-manual/multiconfig.rst | |||
@@ -0,0 +1,312 @@ | |||
1 | .. SPDX-License-Identifier: CC-BY-SA-2.0-UK | ||
2 | |||
3 | Building Images for Multiple Targets With Multiconfig | ||
4 | ***************************************************** | ||
5 | |||
6 | You can use a single ``bitbake`` command to build multiple images or | ||
7 | packages for different targets where each image or package requires a | ||
8 | different configuration (multiple configuration builds). The builds, in | ||
9 | this scenario, are sometimes referred to as "multiconfigs", and this | ||
10 | section uses that term throughout. | ||
11 | |||
12 | This section describes how to set up for multiple configuration builds | ||
13 | and how to account for cross-build dependencies between the | ||
14 | multiconfigs. | ||
15 | |||
16 | Setting Up and Running a Multiple Configuration Build | ||
17 | ===================================================== | ||
18 | |||
19 | To accomplish a multiple configuration build, you must define each | ||
20 | target's configuration separately using a parallel :term:`configuration file` in | ||
21 | the :term:`Build Directory` or configuration directory within a layer, and you | ||
22 | must follow a required file hierarchy. Additionally, you must enable the | ||
23 | multiple configuration builds in your ``local.conf`` file. | ||
24 | |||
25 | Follow these steps to set up and execute multiple configuration builds: | ||
26 | |||
27 | - *Create Separate Configuration Files*: You need to create a single | ||
28 | :term:`Configuration File` for each build target (each multiconfig). | ||
29 | The configuration definitions are implementation dependent but often | ||
30 | each configuration file will define the :term:`MACHINE` and the | ||
31 | temporary directory (:term:`TMPDIR`) BitBake uses for the build. | ||
32 | |||
33 | .. note:: | ||
34 | |||
35 | Whether the same temporary directory (:term:`TMPDIR`) can be shared will | ||
36 | depend on what is similar and what is different between the | ||
37 | configurations. Multiple :term:`MACHINE` targets can share the same | ||
38 | :term:`TMPDIR` as long as the rest of the configuration is the same, | ||
39 | multiple :term:`DISTRO` settings would need separate :term:`TMPDIR` | ||
40 | directories. | ||
41 | |||
42 | For example, consider a scenario with two different multiconfigs for the same | ||
43 | :term:`MACHINE`: "qemux86" built for two distributions such as "poky" and | ||
44 | "poky-lsb". In this case, you would need to use two different :term:`TMPDIR`. | ||
45 | |||
46 | In the general case, using separate :term:`TMPDIR` for the different | ||
47 | multiconfigs is strongly recommended. | ||
48 | |||
49 | The location for these multiconfig configuration files is specific. | ||
50 | They must reside in the current :term:`Build Directory` in a sub-directory of | ||
51 | ``conf`` named ``multiconfig`` or within a :term:`Layer`'s ``conf`` directory | ||
52 | under a directory named ``multiconfig``. Here is an example that defines | ||
53 | two configuration files for the "x86" and "arm" multiconfigs: | ||
54 | |||
55 | .. image:: figures/multiconfig_files.png | ||
56 | :align: center | ||
57 | :width: 50% | ||
58 | |||
59 | The usual :term:`BBPATH` search path is used to locate multiconfig files in | ||
60 | a similar way to other configuration files. | ||
61 | |||
62 | Here is an example showing the minimal statements needed in a | ||
63 | :term:`configuration file` named ``qemux86.conf`` for a ``qemux86`` target | ||
64 | whose temporary build directory is ``tmp-qemux86``:: | ||
65 | |||
66 | MACHINE = "qemux86" | ||
67 | TMPDIR .= "-${BB_CURRENT_MC}" | ||
68 | |||
69 | BitBake will expand the :term:`BB_CURRENT_MC` variable to the value of the | ||
70 | current multiconfig in use. We append this value to :term:`TMPDIR` so that | ||
71 | any change on the definition of :term:`TMPDIR` will automatically affect the | ||
72 | value of :term:`TMPDIR` for each multiconfig. | ||
73 | |||
74 | - *Add the BitBake Multi-configuration Variable to the Local | ||
75 | Configuration File*: Use the | ||
76 | :term:`BBMULTICONFIG` | ||
77 | variable in your ``conf/local.conf`` configuration file to specify | ||
78 | each multiconfig. Continuing with the example from the previous | ||
79 | figure, the :term:`BBMULTICONFIG` variable needs to enable two | ||
80 | multiconfigs: "x86" and "arm" by specifying each configuration file:: | ||
81 | |||
82 | BBMULTICONFIG = "x86 arm" | ||
83 | |||
84 | .. note:: | ||
85 | |||
86 | A "default" configuration already exists by definition. This | ||
87 | configuration is named: "" (i.e. empty string) and is defined by | ||
88 | the variables coming from your ``local.conf`` | ||
89 | file. Consequently, the previous example actually adds two | ||
90 | additional configurations to your build: "arm" and "x86" along | ||
91 | with "". | ||
92 | |||
93 | - *Launch BitBake*: Use the following BitBake command form to launch | ||
94 | the multiple configuration build:: | ||
95 | |||
96 | $ bitbake [mc:multiconfigname:]target [[[mc:multiconfigname:]target] ... ] | ||
97 | |||
98 | For the example in this section, the following command applies:: | ||
99 | |||
100 | $ bitbake mc:x86:core-image-minimal mc:arm:core-image-sato mc::core-image-base | ||
101 | |||
102 | The previous BitBake command builds several components: | ||
103 | |||
104 | - A ``core-image-minimal`` image that is configured through the ``x86.conf`` | ||
105 | configuration file | ||
106 | |||
107 | - A ``core-image-sato`` image that is configured through the ``arm.conf`` | ||
108 | configuration file | ||
109 | |||
110 | - A ``core-image-base`` that is configured through your ``local.conf`` | ||
111 | configuration file | ||
112 | |||
113 | .. note:: | ||
114 | |||
115 | Support for multiple configuration builds in the Yocto Project &DISTRO; | ||
116 | (&DISTRO_NAME;) Release does not include Shared State (sstate) | ||
117 | optimizations. Consequently, if a build uses the same object twice | ||
118 | in, for example, two different :term:`TMPDIR` | ||
119 | directories, the build either loads from an existing sstate cache for | ||
120 | that build at the start or builds the object fresh. | ||
121 | |||
122 | Enabling Multiple Configuration Build Dependencies | ||
123 | ================================================== | ||
124 | |||
125 | Sometimes dependencies can exist between targets (multiconfigs) in a | ||
126 | multiple configuration build. For example, suppose that in order to | ||
127 | build a ``core-image-sato`` image for an "x86" multiconfig, the root | ||
128 | filesystem of an "arm" multiconfig must exist. This dependency is | ||
129 | essentially that the | ||
130 | :ref:`ref-tasks-image` task in the | ||
131 | ``core-image-sato`` recipe depends on the completion of the | ||
132 | :ref:`ref-tasks-rootfs` task of the | ||
133 | ``core-image-minimal`` recipe. | ||
134 | |||
135 | To enable dependencies in a multiple configuration build, you must | ||
136 | declare the dependencies in the recipe using the following statement | ||
137 | form:: | ||
138 | |||
139 | task_or_package[mcdepends] = "mc:from_multiconfig:to_multiconfig:recipe_name:task_on_which_to_depend" | ||
140 | |||
141 | To better show how to use this statement, consider the example scenario | ||
142 | from the first paragraph of this section. The following statement needs | ||
143 | to be added to the recipe that builds the ``core-image-sato`` image:: | ||
144 | |||
145 | do_image[mcdepends] = "mc:x86:arm:core-image-minimal:do_rootfs" | ||
146 | |||
147 | In this example, the `from_multiconfig` is "x86". The `to_multiconfig` is "arm". The | ||
148 | task on which the :ref:`ref-tasks-image` task in the recipe depends is the | ||
149 | :ref:`ref-tasks-rootfs` task from the ``core-image-minimal`` recipe associated | ||
150 | with the "arm" multiconfig. | ||
151 | |||
152 | Once you set up this dependency, you can build the "x86" multiconfig | ||
153 | using a BitBake command as follows:: | ||
154 | |||
155 | $ bitbake mc:x86:core-image-sato | ||
156 | |||
157 | This command executes all the tasks needed to create the | ||
158 | ``core-image-sato`` image for the "x86" multiconfig. Because of the | ||
159 | dependency, BitBake also executes through the :ref:`ref-tasks-rootfs` task for the | ||
160 | "arm" multiconfig build. | ||
161 | |||
162 | Having a recipe depend on the root filesystem of another build might not | ||
163 | seem that useful. Consider this change to the statement in the | ||
164 | ``core-image-sato`` recipe:: | ||
165 | |||
166 | do_image[mcdepends] = "mc:x86:arm:core-image-minimal:do_image" | ||
167 | |||
168 | In this case, BitBake must | ||
169 | create the ``core-image-minimal`` image for the "arm" build since the | ||
170 | "x86" build depends on it. | ||
171 | |||
172 | Because "x86" and "arm" are enabled for multiple configuration builds | ||
173 | and have separate configuration files, BitBake places the artifacts for | ||
174 | each build in the respective temporary build directories (i.e. | ||
175 | :term:`TMPDIR`). | ||
176 | |||
177 | Suggested best practices | ||
178 | ======================== | ||
179 | |||
180 | - :term:`TMPDIR` (other than the default set in bitbake.conf) is only set in | ||
181 | ``local.conf`` by the user. This means that we should **not** manipulate | ||
182 | :term:`TMPDIR` in any way within the Machine or Distro :term:`configuration | ||
183 | file`. | ||
184 | |||
185 | - A multiconfig should specify a :term:`TMPDIR`, and should specify it by | ||
186 | appending the multiconfig name with :term:`BB_CURRENT_MC`. | ||
187 | |||
188 | - Recipes that are used to transfer the output from a multiconfig build to | ||
189 | another should use ``do_task[mcdepends]`` to trigger the build of the | ||
190 | component, and then transfer the item to the current configuration in | ||
191 | :ref:`ref-tasks-install` and :ref:`ref-tasks-deploy`, assuming the value of | ||
192 | the deployed item based on :term:`TMPDIR`. | ||
193 | |||
194 | The :ref:`ref-tasks-install` and :ref:`ref-tasks-deploy` tasks should look | ||
195 | like this:: | ||
196 | |||
197 | do_install() { | ||
198 | install -m 0644 ${TMPDIR}-<multiconfig>/tmp/deploy/images/<machine>/somefile ${D}/some/path | ||
199 | } | ||
200 | |||
201 | do_deploy() { | ||
202 | install -m 0644 ${TMPDIR}-<multiconfig>/tmp/deploy/images/<machine>/somefile ${DEPLOYDIR}/somefile | ||
203 | } | ||
204 | |||
205 | In the example above: | ||
206 | |||
207 | - ``<multiconfig>`` is the multiconfig name as set by the multiconfig | ||
208 | :term:`configuration file` (see the :ref:`dev-manual/multiconfig:Setting | ||
209 | Up and Running a Multiple Configuration Build` section above). | ||
210 | |||
211 | - ``<machine>`` must be the :term:`MACHINE` for which ``somefile`` was built | ||
212 | and deployed. This value may differ from the current :term:`MACHINE` if | ||
213 | the multiconfig :term:`configuration file` overrides it. | ||
214 | |||
215 | - Firmware recipes can set the :term:`INHIBIT_DEFAULT_DEPS` variable to ``1`` | ||
216 | if they don't rely on default dependencies such as the standard C library. | ||
217 | |||
218 | Common use case: building baremetal firmware alongside a Linux build | ||
219 | ==================================================================== | ||
220 | |||
221 | A common use case for multiconfig is to use the default configuration as the | ||
222 | regular Linux build, while one or more multiconfigs can be used to build special | ||
223 | components, such as baremetal firmware. It would also apply to a scenario where | ||
224 | a microcontroller, for example, is companion to a main processor where Linux is | ||
225 | running. This section details how one can achieve these kinds of scenarios with | ||
226 | a multiconfig build. | ||
227 | |||
228 | Adding a multiconfig configuration file and recipe for a baremetal firmware | ||
229 | --------------------------------------------------------------------------- | ||
230 | |||
231 | As described in :ref:`dev-manual/multiconfig:Setting Up and Running a Multiple | ||
232 | Configuration Build`, each multiconfig will require a separate | ||
233 | :term:`Configuration File`. In addition, we will define a separate | ||
234 | :term:`TMPDIR` for our baremetal firmware build configuration. | ||
235 | |||
236 | For example, we will define a new ``conf/multiconfig/baremetal-firmware.conf`` | ||
237 | as follows:: | ||
238 | |||
239 | TMPDIR .= "-${BB_CURRENT_MC}" | ||
240 | TCLIBC = "newlib" | ||
241 | |||
242 | The ``baremetal-firmware.conf`` file configures a separate :term:`TMPDIR` for | ||
243 | holding binaries compiled with the `newlib <https://sourceware.org/newlib/>`__ | ||
244 | toolchain (see :term:`TCLIBC`). | ||
245 | |||
246 | .. note:: | ||
247 | |||
248 | Here, the default :term:`MACHINE` is not overridden by the multiconfig | ||
249 | configuration file. As a consequence, the architecture of the built baremetal | ||
250 | binaries will be the same. In other cases, where the firmware runs on a | ||
251 | completely different architecture, the :term:`MACHINE` must be overridden. | ||
252 | |||
253 | We then create a recipe ``my-firmware.bb`` that defines how the baremetal | ||
254 | firmware is built. The recipe should contain enough information for the | ||
255 | :term:`OpenEmbedded build system` to properly compile the firmware with our | ||
256 | toolchain. The building tasks may vary depending on the nature of the firmware. | ||
257 | However, the recipe should define a :ref:`ref-classes-deploy` task that deploys | ||
258 | the output into the :term:`DEPLOYDIR` directory. We will consider in the | ||
259 | following that the file is named ``my-firmware.elf``. | ||
260 | |||
261 | Building the firmware | ||
262 | --------------------- | ||
263 | |||
264 | The firmware can be built with BitBake with the following command:: | ||
265 | |||
266 | $ bitbake mc:baremetal-firmware:my-firmware | ||
267 | |||
268 | However, we would prefer for ``my-firmware`` to be automatically built when | ||
269 | triggering a normal Linux build. | ||
270 | |||
271 | Using a ``mcdepend``, a recipe belonging to the Linux build can trigger the | ||
272 | build of ``my-firmware``. For example, let's consider that our Linux build needs | ||
273 | to assemble a "special" firmware that uses the output of our ``my-firmware`` | ||
274 | recipe - let's call it ``my-parent-firmware.bb``. Then, we should specify this | ||
275 | dependency in ``my-parent-firmware.bb`` with:: | ||
276 | |||
277 | do_compile[mcdepends] = "mc::baremetal-firmware:my-firmware:do_deploy" | ||
278 | |||
279 | The above will ensure that when the :ref:`ref-tasks-compile` task of | ||
280 | ``my-parent-firmware`` is triggered, the :ref:`ref-tasks-deploy` task of | ||
281 | ``my-firmware`` will already have run successfully. | ||
282 | |||
283 | Using the output of ``my-firmware`` | ||
284 | ----------------------------------- | ||
285 | |||
286 | After ``my-firmware`` recipe has deployed ``my-firmware.elf``, we need to use | ||
287 | the output in some way. We can make a series of assumptions, based on the | ||
288 | default Yocto Project variables in order to get the binary for packaging. | ||
289 | |||
290 | First, we can set the following in ``my-parent-firmware.bb``:: | ||
291 | |||
292 | FIRMWARE_FILE ??= "${TMPDIR}-baremetal-firmware/deploy/images/<machine>/my-firmware.elf" | ||
293 | FIRMWARE_FILE[vardepsexclude] += "TMPDIR" | ||
294 | |||
295 | The first assignment stores the value of the path to the firmware built and | ||
296 | deployed by the ``my-firmware.bb`` recipe. The second assignment excludes the | ||
297 | :term:`TMPDIR` variable from being part of ``FIRMWARE_FILE``'s dependencies --- | ||
298 | meaning that changing the value of :term:`TMPDIR` (for example, changing the | ||
299 | host on which the firmware is built) will not invalidate the :ref:`shared state | ||
300 | cache <overview-manual/concepts:shared state cache>`. | ||
301 | |||
302 | Additionally, ``<machine>`` should be replaced by the :term:`MACHINE` for which | ||
303 | we are building in the baremetal-firmware context. | ||
304 | |||
305 | We can then add a :ref:`ref-tasks-install` task to ``my-parent-firmware``:: | ||
306 | |||
307 | do_install() { | ||
308 | install -Dm 0644 ${FIRMWARE_FILE} ${D}/lib/firmware/my-firmware.elf | ||
309 | } | ||
310 | |||
311 | Doing the above will allow the firmware binary to be transferred and packaged | ||
312 | into the Linux context and rootfs. | ||