diff options
Diffstat (limited to 'docs/build_system.rst')
-rw-r--r-- | docs/build_system.rst | 695 |
1 files changed, 343 insertions, 352 deletions
diff --git a/docs/build_system.rst b/docs/build_system.rst index a7a5c8e8e..981952832 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -163,6 +163,8 @@ and finally Bazel. To avoid confusing the two, we refer to the former as "GN/Bazel targets" and the latter as "Pigweed targets". +.. _docs-build-system-gn: + GN -- A perhaps unfamiliar name, `GN (Generate Ninja)`_ is a meta-build system that @@ -200,15 +202,15 @@ Pigweed's build arguments, which apply across all Pigweed targets. For example, a project could configure the protobuf libraries that it uses. This is done by defining a ``default_args`` scope containing the overrides. -.. code:: +.. code-block:: - # The location of the BUILDCONFIG file. - buildconfig = "//BUILDCONFIG.gn" + # The location of the BUILDCONFIG file. + buildconfig = "//BUILDCONFIG.gn" - # Build arguments set across all Pigweed targets. - default_args = { - dir_pw_third_party_nanopb = "//third_party/nanopb-0.4.2" - } + # Build arguments set across all Pigweed targets. + default_args = { + dir_pw_third_party_nanopb = "//third_party/nanopb-0.4.2" + } Configuration: BUILDCONFIG.gn ----------------------------- @@ -271,22 +273,22 @@ therefore should not evaluate any other GN files. The pattern that Pigweed uses to achieve this is to wrap all dependencies within a condition checking the toolchain. -.. code:: +.. code-block:: - group("my_application_images") { - deps = [] # Empty in the default toolchain. + group("my_application_images") { + deps = [] # Empty in the default toolchain. - if (current_toolchain != default_toolchain) { - # This is only evaluated by Pigweed target toolchains, which configure - # all of the required options to build Pigweed code. - deps += [ "//images:evt" ] - } - } + if (current_toolchain != default_toolchain) { + # This is only evaluated by Pigweed target toolchains, which configure + # all of the required options to build Pigweed code. + deps += [ "//images:evt" ] + } + } - # The images group is instantiated for each of the project's Pigweed targets. - group("my_pigweed_target") { - deps = [ ":my_application_images(//toolchains:my_pigweed_target)" ] - } + # The images group is instantiated for each of the project's Pigweed targets. + group("my_pigweed_target") { + deps = [ ":my_application_images(//toolchains:my_pigweed_target)" ] + } .. warning:: Pigweed's default toolchain is never used, so it is set to an empty toolchain @@ -359,10 +361,10 @@ Next runtime sanitizers supported: * ``ubsan_heuristic`` -- `UndefinedBehaviorSanitizer`_ with the following additional checks enabled: - * ``integer``: Checks for undefined or suspicious integer behavior. - * ``float-divide-by-zero``: Checks for floating point division by zero. - * ``implicit-conversion``: Checks for suspicious behavior of implicit conversions. - * ``nullability``: Checks for null as function arg, lvalue and return type. + * ``integer``: Checks for undefined or suspicious integer behavior. + * ``float-divide-by-zero``: Checks for floating point division by zero. + * ``implicit-conversion``: Checks for suspicious behavior of implicit conversions. + * ``nullability``: Checks for null as function arg, lvalue and return type. These additional checks are heuristic and may not correspond to undefined behavior. @@ -376,6 +378,10 @@ with ``pw_module_tests`` per supported sanitizer. .. _UndefinedBehaviorSanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html .. _ThreadSanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html +coverage +~~~~~~~~~~ +This group defines host-side build target for Clang source-based code coverage. + pw_modules ~~~~~~~~~~ This group lists the main libraries for all of Pigweed's modules. @@ -427,13 +433,13 @@ Pigweed's modules, set relative to a project-specific ``dir_pigweed``. To depend on Pigweed modules from GN code, import Pigweed's overrides file and reference these module variables. -.. code:: +.. code-block:: - # This must be imported before .gni files from any other Pigweed modules. To - # prevent gn format from reordering this import, it must be separated by a - # blank line from other imports. + # This must be imported before .gni files from any other Pigweed modules. To + # prevent gn format from reordering this import, it must be separated by a + # blank line from other imports. - import("//build_overrides/pigweed.gni") + import("//build_overrides/pigweed.gni") GN target type wrappers ----------------------- @@ -488,34 +494,34 @@ Building a custom executable/app image 1. Define your executable GN target using the ``pw_executable`` template. - .. code:: + .. code-block:: - # //foo/BUILD.gn - pw_executable("foo") { - sources = [ "main.cc" ] - deps = [ ":libfoo" ] - } + # //foo/BUILD.gn + pw_executable("foo") { + sources = [ "main.cc" ] + deps = [ ":libfoo" ] + } 2. In the root ``BUILD.gn`` file, add the executable's GN target to the ``apps`` group. - .. code:: + .. code-block:: - # //BUILD.gn - group("apps") { - deps = [ - # ... - "//foo", # Shorthand for //foo:foo - ] - } + # //BUILD.gn + group("apps") { + deps = [ + # ... + "//foo", # Shorthand for //foo:foo + ] + } 3. Run the ninja build to compile your executable. The apps group is built by default, so there's no need to provide a target. The executable will be compiled for every supported Pigweed target. - .. code:: + .. code-block:: - ninja -C out + ninja -C out Alternatively, build your executable by itself by specifying its path to Ninja. When building a GN target manually, the Pigweed target for which it @@ -523,9 +529,9 @@ Building a custom executable/app image For example, to build for the Pigweed target ``host_gcc_debug``: - .. code:: + .. code-block:: - ninja -C out host_gcc_debug/obj/foo/bin/foo + ninja -C out host_gcc_debug/obj/foo/bin/foo .. note:: @@ -535,9 +541,9 @@ Building a custom executable/app image 4. Retrieve your compiled binary from the out directory. It is located at the path - .. code:: + .. code-block:: - out/<pw_target>/obj/<gn_path>/{bin,test}/<executable> + out/<pw_target>/obj/<gn_path>/{bin,test}/<executable> where ``pw_target`` is the Pigweed target for which the binary was built, ``gn_path`` is the GN path to the BUILD.gn file defining the executable, @@ -548,27 +554,18 @@ Building a custom executable/app image For example, the ``foo`` executable defined above and compiled for the Pigweed target stm32f429i_disc1_debug is found at: - .. code:: + .. code-block:: - out/stm32f429i_disc1_debug/obj/foo/bin/foo + out/stm32f429i_disc1_debug/obj/foo/bin/foo -CMake ------ +The CMake build +=============== A well-known name in C/C++ development, `CMake`_ is widely used by all kinds of projects, including embedded devices. Pigweed's CMake support is provided primarily for projects that have an existing CMake build and wish to integrate Pigweed modules. -Bazel ------ -The open source version of Google's internal build system. `Bazel`_ has been -growing in popularity within the open source world, as well as being adopted by -various proprietary projects. Its modular structure makes it a great fit for -à la carte usage. - -.. note:: - Bazel support is experimental and only for the brave for now. If you are - looking for stable set of build API's please use GN. +.. _docs-build-system-bazel: The Bazel build =============== @@ -586,7 +583,7 @@ While described in more detail in the Bazel docs there a few Bazel features that are of particular importance when targeting embedded platforms. The most commonly used commands used in bazel are; -.. code:: sh +.. code-block:: sh bazel build //your:target bazel test //your:target @@ -596,17 +593,17 @@ commonly used commands used in bazel are; Building ^^^^^^^^ -When it comes to building/testing your Bazel target for a specific Pigweed -target (e.g. stm32f429i-discovery) a slight variation is required. +When it comes to building/testing your build target for a specific target +platform (e.g. stm32f429i-discovery) a slight variation is required. -.. code:: sh +.. code-block:: sh bazel build //your:target \ - --platforms=@pigweed//pw_build/platforms:stm32f429i-disc1 + --platforms=@pigweed//pw_build/platforms:lm3s6965evb For more information on how to create your own platforms refer to the official `Bazel platforms reference`_. You may also find helpful examples of constraints -and platforms in the '//pw_build/platforms' and '//pw_build/constraints' +and platforms in the ``//pw_build/platforms`` and ``//pw_build/constraints`` directories. .. _Bazel platforms reference: https://docs.bazel.build/versions/main/platforms.html @@ -615,8 +612,8 @@ Testing ^^^^^^^ Running tests on an embedded target with Bazel is possible although support for this is experimental. The easiest way of achieving this at the moment is to use -Bazel's '--run_under' flag. To make this work create a Bazel target -('//your_handler') that; +Bazel's ``--run_under`` flag. To make this work create a Bazel target +(``//your_handler``) that: 1. Takes a single argument (the path to the elf) and uploads the elf to your Pigweed target. @@ -625,35 +622,53 @@ Bazel's '--run_under' flag. To make this work create a Bazel target and returns (0, 1) respectively if one of the keywords is intercepted. (This step assumes you are using the pw_unit_test package and it is configured for your target). -4. Run; - .. code:: sh +Then, run: + + .. code-block:: sh bazel test //your:test --platforms=//your/platform --run_under=//your_handler +Test tag conventions +~~~~~~~~~~~~~~~~~~~~ +Pigweed observes the standard Bazel test `tag conventions +<https://bazel.build/reference/test-encyclopedia#tag-conventions>`_. We also +use the following additional tags: + +* ``integration``: large, slow integration tests in upstream Pigweed are given + the ``integration`` tag. You can skip running these tests using + `--test_tag_filters + <https://bazel.build/docs/user-manual#test-tag-filters>`_. For example, + + .. code-block:: sh + + bazel test --test_tag_filters=-integration //... + + will run all tests *except* for these integration tests. + Code Coverage ^^^^^^^^^^^^^ Making use of the code coverage functionality in Bazel is straightforward. 1. Add the following lines to your '.bazelrc'. - .. code:: sh + .. code-block:: sh - coverage --experimental_generate_llvm_lcov - coverage --combined_report=lcov + coverage --experimental_generate_llvm_lcov + coverage --combined_report=lcov 2. Generate a combined lcov coverage report. This will produce a combined lcov coverage report at the path 'bazel-out/_coverage/_coverage_report.dat'. e.g. - .. code:: sh + .. code-block:: sh - bazel coverage //pw_log/... + bazel coverage //pw_log/... 3. View the results using the command line utility 'lcov'. - .. code:: sh + .. code-block:: sh - lcov --list bazel-out/_coverage/_coverage_report.dat + lcov --list bazel-out/_coverage/_coverage_report.dat Configuration ------------- @@ -670,7 +685,7 @@ Selects are useful for specifying different dependencies/source depending on the platform that is currently being targeted. For more information on this please see the `Bazel selects reference`_. e.g. -.. code:: py +.. code-block:: py pw_cc_library( name = "some_platform_dependant_library", @@ -686,7 +701,7 @@ Compatibility lists allow you to specify which platforms your targets are compatible with. Consider an example where you want to specify that a target is compatible with only a host os; -.. code:: py +.. code-block:: py pw_cc_library( name = "some_host_only_lib", @@ -705,15 +720,17 @@ other OS's will fail if this target is explicitly depended on. However if building with a wild card for a non-host platform this target will be skipped and the build will continue. e.g. -.. code:: sh +.. code-block:: sh - bazel build //... --platforms=@pigweed//pw_build/platforms:cortex_m0 + bazel build //... --platforms=@pigweed//pw_build/platforms:lm3s6965evb This allows for you to easily create compatibility matricies without adversely affecting your ability build your entire repo for a given Pigweed target. For more detailed information on how to use the target_compatible_with attribute please see `Bazel target_compatible_with reference`_. +.. _docs-build_system-bazel_flags: + Flags/build settings ^^^^^^^^^^^^^^^^^^^^ Flags/build settings are particularly useful in scenarios where you may want to @@ -729,7 +746,7 @@ select statement. A simple example of when it is useful to use a label_flag is when you want to swap out a single dependency from the command line. e.g. -.. code:: py +.. code-block:: py pw_cc_library( name = "some_default_io", @@ -754,7 +771,7 @@ swap out a single dependency from the command line. e.g. From here the label_flag by default redirects to the target ":some_default_io", however it is possible to override this from the command line. e.g. -.. code:: sh +.. code-block:: sh bazel build //:some_target_that_needs_io --//:io=//:some_other_io @@ -768,312 +785,286 @@ however it is possible to override this from the command line. e.g. .. _docs-build_system-bazel_configuration: -Pigweed's Bazel configuration +Facades and backends tutorial ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Pigweeds Bazel configuration API is designed to be distributed across the -Pigweed repository and/or your downstream repository. If you are coming from -GN's centralized configuration API it might be useful to think about -Pigweed+Bazel's configuration as the transpose of GN's configuration. The -configuration interface that is supported by Pigweed is designed to start simple -and then grow with your project. +This section walks you through an example of configuring :ref:`facade +<module-pw_build-bazel-pw_cc_facade>` backends in a Pigweed Bazel project. -.. note:: - There are plans to extend the configuration API for Bazel. However, - currently the only configurations that are available under the Bazel+Pigweed - configuration API is the ability to switch facade backends. For more - information on what this is please see the - :ref:`docs-module-structure-facades` section of :ref:`docs-module-structure`. +Consider a scenario that you are building a flight controller for a spacecraft. +But you have very little experience with Pigweed and have just landed here. +First things first, you would set up your WORKSPACE to fetch Pigweed +repository. Then, add the dependencies that you need from Pigweed's WORKSPACE. -Consider a scenario that you are building a flight controller for a -spacecraft. But have very little experience with Pigweed and you have just -landed here. First things first you would; +Maybe you want to try using the :ref:`pw_chrono <module-pw_chrono>` module. So +you create a target in your repository like so: -1. Set up your WORKSPACE to fetch the Pigweeds repository. Then add the - dependencies that you need from Pigweeds WORKSPACE. +.. code-block:: python -2. Add a pigweed_config rule to your WORKSPACE, using Pigweed's default - configuration. + # BUILD.bazel + pw_cc_library( + name = "time_is_relative", + srcs = ["relative_time_on_earth.cc"], + deps = ["@pigweed//pw_chrono:system_clock"], + ) - .. code:: py +This should work out of the box for any host operating system. E.g., running, - # WORKSPACE ... - load("//pw_build:target_config.bzl", "pigweed_config") +.. code-block:: console - # Configure Pigweeds backend. - pigweed_config( - name = "pigweed_config", - build_file = "@pigweed//targets:default_config.BUILD", - ) + bazel build //:time_is_relative -.. note:: - We are aware, that the experience of setting up your WORKSPACE file to work - with pigweed is less than ideal. This API is under construction, but we are - working on this! +will produce a working library for your host OS. -.. - TODO: Add in a better way to pull down WORKSPACE dependencies in Bazel then - add docs in here. +Using Pigweed-provided backends +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +But you're probably here because Pigweed offers a set of embedded libraries, +and might be interested in running your code on some micro-controller/FPGA +combined with an RTOS. For now let's assume you are using FreeRTOS and are +happy to make use of our default ``//pw_chrono`` backend for FreeRTOS. You +could build your library with: -Now to explain what is going on here. Housed under the "pigweed_config" remote -repository is a set of configuration flags. These can be used to inject -dependencies into facades to override the default backend. +.. code-block:: console -Continuing on with our scenario, consider that you maybe want to try using the -'//pw_chrono' module. So you create a target in your repository like so; + bazel build \ + --@pigweed//targets:pw_chrono_system_clock_backend=@pigweed//pw_chrono_freertos:system_clock_backend \ + //:time_is_relative -.. code:: +Then, ``//pw_chrono:system_clock`` will use the FreeRTOS backend +``//pw_chrono_freertos:system_clock_backend``. - # BUILD - pw_cc_library( - name = "time_is_relative", - srcs = ["relative_time_on_earth.cc"], - deps = ["@pigweed//pw_chrono"], - ) +How does this work? Here's the relevant part of the dependency tree for your +target: -Now this should work out of the box for any host operating system. e.g. Running; +.. code-block:: -.. code:: + //:time_is_relative + | + v + @pigweed//pw_chrono:system_clock -------> @pigweed//targets:pw_chrono_system_clock_backend + | (Injectable) + | | + | v + | @pigweed//pw_chrono_freertos:system_clock + | (Actual backend) + v | + @pigweed//pw_chrono:system_clock_facade <------------------. - bazel build //:time_is_relative +When building ``//:time_is_relative``, Bazel checks the dependencies of +``@pigweed//pw_chrono:system_clock`` and finds that it depends on +``@pigweed//targets:pw_chrono_system_clock_backend``, which looks like this: -will produce a working library. But as your probably here because Pigweed offers -a set of embedded libraries you might be interested in running your code on some -random micro-controller/FPGA combined with an RTOS. For now let's assume that by -some coincidence you are using FreeRTOS and are happy to make use -of our default '//pw_chrono' backend for FreeRTOS. You could build the following -with; +.. code-block:: python -.. code:: sh + # @pigweed//targets/BUILD.bazel - bazel build //:time_is_relative \ - --platforms=@pigweed//pw_build/platforms:freertos + label_flag( + name = "pw_chrono_system_clock_backend", + build_setting_default = "@pigweed//pw_chrono:system_clock_backend_multiplexer", + ) -There is a fair bit to unpack here in terms of how our configuration system -is determining which dependencies to choose for your build. The dependency -tree (that is important for configuration) in a project such as this would -look like. +This is a `label_flag +<https://bazel.build/extending/config#label-typed-build-settings>`_: a +dependency edge in the build graph that can be overridden by command line +flags. By setting -.. code:: +.. code-block:: console - @pigweed//pw_chrono:pw_chrono_facade <-----------. - ^ | - | @pigweed//pw_chrono_freertos:system_clock - | (Actual backend) - | ^ - | | - | @pigweed//pw_chrono:system_clock_backend_multiplexer - | Select backend based on OS: - | [FreeRTOS (X), Embos ( ), STL ( ), Threadx ( )] - | ^ - | | - @pigweed//pw_chrono -------> @pigweed_config//:pw_chrono_system_clock_backend - ^ (Injectable) - | - //:time_is_relative + --@pigweed//targets:pw_chrono_system_clock_backend=\ + @pigweed//pw_chrono_freertos:system_clock_backend -So when evaluating this setup Bazel checks the dependencies for '//pw_chrono' -and finds that it depends on "@pigweed_config//:pw_chrono_system_clock_backend" which looks -like this; +on the command line, you told Bazel to override the default and use the +FreeRTOS backend. -.. code:: py +Defining a custom backend +~~~~~~~~~~~~~~~~~~~~~~~~~ +Continuing with our scenario, let's say that you have read +:ref:`docs-module-structure` and now want to implement your own backend for +``//pw_chrono:system_clock`` using a hardware RTC. In this case you would +create a new directory ``pw_chrono_my_hardware_rtc``, containing some header +files and a BUILD file like, + +.. code-block:: python + + # //pw_chrono_my_hardware_rtc/BUILD.bazel + + pw_cc_library( + name = "system_clock", + hdrs = [ + "public/pw_chrono_stl/system_clock_config.h", + "public/pw_chrono_stl/system_clock_inline.h", + "public_overrides/pw_chrono_backend/system_clock_config.h", + "public_overrides/pw_chrono_backend/system_clock_inline.h", + ], + includes = [ + "public", + "public_overrides", + ], + deps = [ + "//pw_chrono:system_clock_facade", + ], + ) - # pw_chrono config. - label_flag( - name = "pw_chrono_system_clock_backend", - build_setting_default = "@pigweed//pw_chrono:system_clock_backend_multiplexer", - ) +To build your ``//:time_is_relative`` target using this backend, you'd run, -Looking at the 'build_setting_default' we can see that by default it depends -back on the target "@pigweed//pw_chrono:system_clock_backend_multiplexer". If -you only had one backend you could actually just change the -'build_setting_default' to point directly to your backend. However because we -have four different backends we have to use the select semantics to choose the -right one. In this case it looks like; +.. code-block:: console -.. code:: py + bazel build //:time_is_relative \ + --@pigweed//targets:pw_chrono_system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock - pw_cc_library( - name = "system_clock_backend_multiplexer", - visibility = ["@pigweed_config//:__pkg__"], - deps = select({ - "@pigweed//pw_build/constraints/rtos:freertos": - ["//pw_chrono_freertos:system_clock"], - "@pigweed//pw_build/constraints/rtos:embos": - ["//pw_chrono_embos:system_clock"], - "@pigweed//pw_build/constraints/rtos:threadx": - ["//pw_chrono_threadx:system_clock"], - "//conditions:default": ["//pw_chrono_stl:system_clock"], - }), - ) +This modifies the build graph to look something like this: -Intuitively you can see that the first option was selected, which terminates -the configuration chain. +.. code-block:: -Continuing on with our scenario let's say that you have read -:ref:`docs-module-structure` and now want to implement your own backend for -'//pw_chrono' using a hardware RTC. In this case you would create a new -directory 'pw_chrono_my_hardware_rtc'. To ensure that your new backend compiles -with the facade an easy and temporary way to override the dependency tree is -to override the label flag in '@pigweed_config'. For example; - -.. code:: sh - - bazel build //:time_is_relative \ - --@pigweed_config//pw_chrono_system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock - -This temporarily modifies the build graph to look something like this; - -.. code:: - - @pigweed//pw_chrono:pw_chrono_facade <-----. - ^ | - | @your_workspace//pw_chrono_my_hardware_rtc:system_clock - | (Actual backend) - | ^ - | | - @pigweed//pw_chrono -> @pigweed_config//:pw_chrono_system_clock_backend - ^ (Injectable) - | - //:time_is_relative - -Now while this is a nice temporary change, but you might find yourself in need -of a more permanent configuration. Particularly if you want to override multiple -different backends. In other words if you had several backends to override, that -would translate to several different command line flags (one for each override). -This problem further compounds as you have multiple Pigweed targets all -requiring different combinations of different backends as you can't even reuse -your command line entries. Instead you would have to memorize the correct -combination of backends for each of your targets. - -So continuing on with our scenario, let's say we add a backup micro-controller, + //:time_is_relative + | + v + @pigweed//pw_chrono:system_clock -------> @pigweed//targets:pw_chrono_system_clock_backend + | (Injectable) + | | + | v + | //pw_chrono_my_hardware_rtc:system_clock + | (Actual backend) + v | + @pigweed//pw_chrono:system_clock_facade <------------------. + +Associating backends with platforms through bazelrc +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +As your project grows, you will want to select backends for an increasing +number of facades. The right backend to choose will depend on the target +platform (host vs embedded, with potentially multiple target embedded +platforms). Managing this through command-line flags would be pretty arduous! + +What we recommend you do instead is group these flags into configs in your +`bazelrc <https://bazel.build/run/bazelrc>`_. Eventually, your bazelrc may look +something like this: + +.. code-block:: sh + + # The Cortex M7 config + build:m7 --@pigweed//tagrets:pw_chrono_system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock + build:m7 --@pigweed//targets:pw_sys_io_backend=//cortex-m7:sys_io + + # The Cortex M4 config + build:m4 --@pigweed//tagrets:pw_chrono_system_clock_backend=//pw_chrono_my_hardware_rtc:system_clock + build:m4 --@pigweed//targets:pw_sys_io_backend=//cortex-m4:sys_io + build:m4 --@pigweed//targets:pw_log_backend=@pigweed//pw_log_string + build:m4 --@pigweed//targets:pw_log_string_handler_backend=@pigweed//pw_system:log_backend + +Then, to build your library for a particular configuration, on the command line +you just specify the ``--config`` on the command line: + +.. code-block:: console + + bazel build --config=m4 //:time_is_relative + +Multiplexer-based backend selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TODO(`b/272090220 <https://issues.pigweed.dev/issues/272090220>`_): Not all +facades and backends expose this interface yet. + +As an alternative to directly switching backends using label flags, Pigweed +supports backend selection based on the target `platform +<https://bazel.build/extending/platforms>`_. That is, on the command line you +build with, + +.. code-block:: console + + bazel build --platforms-//platforms:primary_computer //:time_is_relative + +and backend selection is done by Bazel based on the platform definition. Let's +discuss how to set this up. + +Continuing with our scenario, let's say we add a backup microcontroller to our spacecraft. But this backup computer doesn't have a hardware RTC. We still want to share the bulk of the code between the two computers but now we need two separate implementations for our pw_chrono facade. Let's say we choose -to keep the primary flight computer using the hardware RTC and switch the backup -computer over to use Pigweeds default FreeRTOS backend. In this case we might, -want to do something similar to -'@pigweed//pw_chrono:system_clock_backend_multiplexer' and create selectable -dependencies for the two different computers. Now because there are no default -constraint_setting's that meet our requirements we are going to have to; - -1. Create a constraint_setting and a set of constraint_value's for the flight - computer. For example; - - .. code:: py - - # //platforms/flight_computer/BUILD - constraint_setting( - name = "flight_computer", - ) - - constraint_value( - name = "primary", - constraint_setting = ":flight_computer", - ) - - constraint_value( - name = "backup", - constraint_setting = ":flight_computer", - ) - -2. Create a set of platforms that can be used to switch constraint_value's. - For example; - - .. code:: py - - # //platforms/BUILD - platform( - name = "primary_computer", - constraint_values = ["//platforms/flight_computer:primary"], - ) - - platform( - name = "backup_computer", - constraint_values = ["//platforms/flight_computer:backup"], - ) - -3. Create a target multiplexer that will select the right backend depending on - which computer you are using. For example; - - .. code:: py - - # //pw_chrono/BUILD - load("//pw_build:pigweed.bzl", "pw_cc_library") - - pw_cc_library( - name = "system_clock_backend_multiplexer", - deps = select({ - "//platforms/flight_computer:primary": [ - "//pw_chrono_my_hardware_rtc:system_clock", - ], - "//platforms/flight_computer:backup": [ - "@pigweed//pw_chrono_freertos:system_clock", - ], - "//conditions:default": [ - "@pigweed//pw_chrono_stl:system_clock", - ], - }), - ) - -4. Copy and paste across the target/default_config.BUILD across from the - Pigweed repository and modifying the build_setting_default for the target - 'pw_chrono_system_clock_backend' to point to your new system_clock_backend_multiplexer - target. For example; - - This; - - .. code:: py - - # @pigweed//target:default_config.BUILD - label_flag( - name = "pw_chrono_system_clock_backend", - build_setting_default = "@pigweed//pw_chrono:system_clock_backend_multiplexer", - ) - - Becomes this; - - .. code:: py - - # @your_workspace//target:your_config.BUILD - label_flag( - name = "pw_chrono_system_clock_backend", - build_setting_default = - "@your_workspace//pw_chrono:system_clock_backend_multiplexer", - ) - -5. Switch your workspace 'pigweed_config' rule over to use your custom config. - - .. code:: py - - # WORKSPACE - pigweed_config( - name = "pigweed_config", - build_file = "//target/your_config.BUILD", - ) +to keep the primary flight computer using the hardware RTC and switch the +backup computer over to use Pigweed's default FreeRTOS backend: + +#. Create a constraint value corresponding to your custom backend: + + .. code-block:: python + + # //pw_chrono_my_hardware_rtc/BUILD.bazel + constraint_value( + name = "system_clock_backend", + constraint_setting = "//pw_chrono:system_clock_constraint_setting", + ) + +#. Create a set of platforms that can be used to switch constraint values. + For example: + + .. code-block:: python + + # //platforms/BUILD.bazel + platform( + name = "primary_computer", + constraint_values = ["//pw_chrono_my_hardware_rtc:system_clock_backend"], + ) + + platform( + name = "backup_computer", + constraint_values = ["@pigweed//pw_chrono_freertos:system_clock_backend"], + ) + + If you already have platform definitions for the primary and backup + computers, just add these constraint values to them. + +#. Create a target multiplexer that will select the right backend depending on + which computer you are using. For example: + + .. code-block:: python + + # //targets/BUILD.bazel + load("//pw_build:pigweed.bzl", "pw_cc_library") + + pw_cc_library( + name = "system_clock_backend_multiplexer", + deps = select({ + "//pw_chrono_my_hardware_rtc:system_clock_backend": [ + "//pw_chrono_my_hardware_rtc:system_clock", + ], + "@pigweed//pw_chrono_freertos:system_clock_backend": [ + "@pigweed//pw_chrono_freertos:system_clock", + ], + "//conditions:default": [ + "@pigweed//pw_chrono_stl:system_clock", + ], + }), + ) + +#. Add a build setting override for the ``pw_chrono_system_clock_backend`` label + flag to your ``.bazelrc`` file that points to your new target multiplexer. + + .. code-block:: bash + + # //.bazelrc + build --@pigweed//targets:pw_chrono_system_clock_backend=//targets:system_clock_backend_multiplexer Building your target now will result in slightly different build graph. For example, running; -.. code:: sh +.. code-block:: sh bazel build //:time_is_relative --platforms=//platforms:primary_computer Will result in a build graph that looks like; -.. code:: - - @pigweed//pw_chrono:pw_chrono_facade <---. - ^ | - | @your_workspace//pw_chrono_my_hardware_rtc:system_clock - | (Actual backend) - | ^ - | | - | @your_workspace//pw_chrono:system_clock_backend_multiplexer - | Select backend based on OS: - | [Primary (X), Backup ( ), Host only default ( )] - | ^ - | | - @pigweed//pw_chrono -> @pigweed_config//:pw_chrono_system_clock_backend - ^ (Injectable) - | - //:time_is_relative +.. code-block:: + + //:time_is_relative + | + @pigweed//pw_chrono -> @pigweed//targets:pw_chrono_system_clock_backend + | (Injectable) + | | + | v + | //targets:system_clock_backend_multiplexer + | Select backend based on OS: + | [Primary (X), Backup ( ), Host only default ( )] + | | + | v + | //pw_chrono_my_hardware_rtc:system_clock + | (Actual backend) + v | + @pigweed//pw_chrono:pw_chrono_facade <---. + |