Skip to content

Commit

Permalink
dev-manual/multiconfig: add suggested best practices and baremetal se…
Browse files Browse the repository at this point in the history
…ctions

After the suggestions from Mark Hatle on the list
(https://lists.yoctoproject.org/g/docs/topic/110487932), add two
sections to the multiconfig doc:

- Suggested best practices: suggestion for better design of multiconfig
  builds.

- Common use case: baremetal build.

  This section applies the guidelines from the first sections and apply
  it to a real-life example of how to use multiconfig. This one to build
  some baremetal firmware alongside a regular Linux build.

Suggested-by: Mark Hatle <[email protected]>
Reviewed-by: Quentin Schulz <[email protected]>
(From yocto-docs rev: 36fb1e9e5099aa0d858d5478530143e9bac39588)

Signed-off-by: Antonin Godard <[email protected]>
Signed-off-by: Richard Purdie <[email protected]>
  • Loading branch information
antznin authored and rpurdie committed Feb 26, 2025
1 parent 705966c commit dcfbb14
Showing 1 changed file with 136 additions and 0 deletions.
136 changes: 136 additions & 0 deletions documentation/dev-manual/multiconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,139 @@ 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.

0 comments on commit dcfbb14

Please sign in to comment.