123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- .. 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.
|