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