| .. _docs-module-structure: |
| |
| ---------------- |
| Module Structure |
| ---------------- |
| The Pigweed module structure is designed to keep as much code as possible for a |
| particular slice of functionality in one place. That means including the code |
| from multiple languages, as well as all the related documentation and tests. |
| |
| Additionally, the structure is designed to limit the number of places a file |
| could go, so that when reading callsites it is obvious where a header is from. |
| That is where the duplicated ``<module>`` occurrences in file paths comes from. |
| |
| Example module structure |
| ------------------------ |
| .. code-block:: python |
| |
| pw_foo/... |
| |
| docs.rst # If there is just 1 docs file, call it docs.rst |
| README.md # All modules must have a short README for gittiles |
| |
| BUILD.gn # GN build required |
| BUILD # Bazel build required |
| |
| # C++ public headers; the repeated module name is required |
| public/pw_foo/foo.h |
| public/pw_foo/baz.h |
| |
| # Exposed private headers go under internal/ |
| public/pw_foo/internal/bar.h |
| public/pw_foo/internal/qux.h |
| |
| # Public override headers must go in 'public_overrides' |
| public_overrides/gtest/gtest.h |
| public_overrides/string.h |
| |
| # Private headers go into <module>_*/... |
| pw_foo_internal/zap.h |
| pw_foo_private/zip.h |
| pw_foo_secret/alxx.h |
| |
| # C++ implementations go in the root |
| foo_impl.cc |
| foo.cc |
| baz.cc |
| bar.cc |
| zap.cc |
| zip.cc |
| alxx.cc |
| |
| # C++ tests also go in the root |
| foo_test.cc |
| bar_test.cc |
| zip_test.cc |
| |
| # Python files go into 'py/<module>/...' |
| py/BUILD.gn # Python packages are declared in GN using pw_python_package |
| py/setup.py # Python files are structured as standard Python packages |
| py/foo_test.py # Tests go in py/ but outside of the Python package |
| py/bar_test.py |
| py/pw_foo/__init__.py |
| py/pw_foo/__main__.py |
| py/pw_foo/bar.py |
| py/pw_foo/py.typed # Indicates that this package has type annotations |
| |
| # Go files go into 'go/...' |
| go/... |
| |
| # Examples go in examples/, mixing different languages |
| examples/demo.py |
| examples/demo.cc |
| examples/demo.go |
| examples/BUILD.gn |
| examples/BUILD |
| |
| # Size reports go under size_report/ |
| size_report/BUILD.gn |
| size_report/base.cc |
| size_report/use_case_a.cc |
| size_report/use_case_b.cc |
| |
| # Protobuf definition files go into <module>_protos/... |
| pw_foo_protos/foo.proto |
| pw_foo_protos/internal/zap.proto |
| |
| # Other directories are fine, but should be private. |
| data/... |
| graphics/... |
| collection_of_tests/... |
| code_relating_to_subfeature/... |
| |
| Module name |
| ----------- |
| Pigweed upstream modules are always named with a prefix ``pw_`` to enforce |
| namespacing. Projects using Pigweed that wish to make their own modules can use |
| whatever name they like, but we suggest picking a short prefix to namespace |
| your product (e.g. for an Internet of Toast project, perhaps the prefix could |
| be ``it_``). |
| |
| C++ module structure |
| -------------------- |
| |
| C++ public headers |
| ~~~~~~~~~~~~~~~~~~ |
| Located ``{pw_module_dir}/public/<module>``. These are headers that must be |
| exposed due to C++ limitations (i.e. are included from the public interface, |
| but are not intended for public use). |
| |
| **Public headers** should take the form: |
| |
| ``{pw_module_dir}/public/<module>/*.h`` |
| |
| **Exposed private headers** should take the form: |
| |
| ``{pw_module_dir}/public/<module>/internal/*.h`` |
| |
| Examples: |
| |
| .. code-block:: |
| |
| pw_foo/... |
| public/pw_foo/foo.h |
| public/pw_foo/a_header.h |
| public/pw_foo/baz.h |
| |
| For headers that must be exposed due to C++ limitations (i.e. are included from |
| the public interface, but are not intended for use), place the headers in a |
| ``internal`` subfolder under the public headers directory; as |
| ``{pw_module_dir}/public/<module>/internal/*.h``. For example: |
| |
| .. code-block:: |
| |
| pw_foo/... |
| public/pw_foo/internal/secret.h |
| public/pw_foo/internal/business.h |
| |
| .. note:: |
| |
| These headers must not override headers from other modules. For |
| that, there is the ``public_overrides/`` directory. |
| |
| C++ public override headers |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| Located ``{pw_module_dir}/public_overrides/<module>``. In general, the Pigweed |
| philosophy is to avoid having "things hiding under rocks", and having header |
| files with the same name that can override each other is considered a rock |
| where surprising things can hide. Additionally, a design goal of the Pigweed |
| module structure is to make it so there is ideally exactly one obvious place |
| to find a header based on an ``#include``. |
| |
| However, in some cases header overrides are necessary to enable flexibly |
| combining modules. To make this as explicit as possible, headers which override |
| other headers must go in |
| |
| ``{pw_module_dir}/public_overrides/...``` |
| |
| For example, the ``pw_unit_test`` module provides a header override for |
| ``gtest/gtest.h``. The structure of the module is (omitting some files): |
| |
| .. code-block:: |
| |
| pw_unit_test/... |
| |
| public_overrides/gtest |
| public_overrides/gtest/gtest.h |
| |
| public/pw_unit_test |
| public/pw_unit_test/framework.h |
| public/pw_unit_test/simple_printing_event_handler.h |
| public/pw_unit_test/event_handler.h |
| |
| Note that the overrides are in a separate directory ``public_overrides``. |
| |
| C++ implementation files |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| Located ``{pw_module_dir}/``. C++ implementation files go at the top level of |
| the module. Implementation files must always use "" style includes. |
| |
| Example: |
| |
| .. code-block:: |
| |
| pw_unit_test/... |
| main.cc |
| framework.cc |
| test.gni |
| BUILD.gn |
| README.md |
| |
| .. _module-structure-compile-time-configuration: |
| |
| Compile-time configuration |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| Pigweed modules are intended to be used in a wide variety of environments. |
| In support of this, some modules expose compile-time configuration options. |
| Pigweed has an established pattern for declaring and overriding module |
| configuration. |
| |
| .. tip:: |
| |
| Compile-time configuration provides flexibility, but also imposes |
| restrictions. A module can only have one configuration in a given build. |
| This makes testing modules with compile-time configuration more difficult. |
| Where appropriate, consider alternatives such as C++ templates or runtime |
| configuration. |
| |
| Declaring configuration |
| ^^^^^^^^^^^^^^^^^^^^^^^ |
| Configuration options are declared in a header file as macros. If the macro is |
| not already defined, a default definition is provided. Otherwise, nothing is |
| done. Configuration headers may include ``static_assert`` statements to validate |
| configuration values. |
| |
| .. code-block:: c++ |
| |
| // Example configuration header |
| |
| #ifndef PW_FOO_INPUT_BUFFER_SIZE_BYTES |
| #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 128 |
| #endif // PW_FOO_INPUT_BUFFER_SIZE_BYTES |
| |
| static_assert(PW_FOO_INPUT_BUFFER_SIZE_BYTES >= 64); |
| |
| The configuration header may go in one of three places in the module, depending |
| on whether the header should be exposed by the module or not. |
| |
| .. code-block:: |
| |
| pw_foo/... |
| |
| # Publicly accessible configuration header |
| public/pw_foo/config.h |
| |
| # Internal configuration header that is included by other module headers |
| public/pw_foo/internal/config.h |
| |
| # Internal configuration header |
| pw_foo_private/config.h |
| |
| The configuration header is provided by a build system library. This library |
| acts as a :ref:`facade<docs-module-structure-facades>`. The facade uses a |
| variable such as ``pw_foo_CONFIG``. In upstream Pigweed, all config facades |
| default to the ``pw_build_DEFAULT_MODULE_CONFIG`` backend. In the GN build |
| system, the config facade is declared as follows: |
| |
| .. code-block:: |
| |
| declare_args() { |
| # The build target that overrides the default configuration options for this |
| # module. This should point to a source set that provides defines through a |
| # public config (which may -include a file or add defines directly). |
| pw_foo_CONFIG = pw_build_DEFAULT_MODULE_CONFIG |
| } |
| |
| # An example source set for each potential config header location follows. |
| |
| # Publicly accessible configuration header (most common) |
| pw_source_set("config") { |
| public = [ "public/pw_foo/config.h" ] |
| public_configs = [ ":public_include_path" ] |
| public_deps = [ pw_foo_CONFIG ] |
| } |
| |
| # Internal configuration header that is included by other module headers |
| pw_source_set("config") { |
| sources = [ "public/pw_foo/internal/config.h" ] |
| public_configs = [ ":public_include_path" ] |
| public_deps = [ pw_foo_CONFIG ] |
| visibility = [":*"] # Only allow this module to depend on ":config" |
| friend = [":*"] # Allow this module to access the config.h header. |
| } |
| |
| # Internal configuration header |
| pw_source_set("config") { |
| public = [ "pw_foo_private/config.h" ] |
| public_deps = [ pw_foo_CONFIG ] |
| visibility = [":*"] # Only allow this module to depend on ":config" |
| } |
| |
| Overriding configuration |
| ^^^^^^^^^^^^^^^^^^^^^^^^ |
| As noted above, all module configuration facades default to the same backend |
| (``pw_build_DEFAULT_MODULE_CONFIG``). This allows projects to override |
| configuration values for multiple modules from a single configuration backend, |
| if desired. The configuration values may also be overridden individually by |
| setting backends for the individual module configurations (e.g. in GN, |
| ``pw_foo_CONFIG = "//configuration:my_foo_config"``). |
| |
| Configurations options are overridden by setting macros in the config backend. |
| These macro definitions can be provided through compilation options, such as |
| ``-DPW_FOO_INPUT_BUFFER_SIZE_BYTES=256``. Configuration macro definitions may |
| also be set in a header file. The header file is included using the ``-include`` |
| compilation option. |
| |
| This example shows two ways to configure a module in the GN build system. |
| |
| .. code-block:: |
| |
| # In the toolchain, set either pw_build_DEFAULT_MODULE_CONFIG or pw_foo_CONFIG |
| pw_build_DEFAULT_MODULE_CONFIG = get_path_info(":define_overrides", "abspath") |
| |
| # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES using the -D flag. |
| pw_source_set("define_overrides") { |
| public_configs = [ ":define_options" ] |
| } |
| |
| config("define_options") { |
| defines = [ "PW_FOO_INPUT_BUFFER_SIZE_BYTES=256" ] |
| } |
| |
| # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES in a header file. |
| pw_source_set("include_overrides") { |
| public_configs = [ ":set_options_in_header_file" ] |
| |
| # Header file with #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 256 |
| sources = [ "my_config_overrides.h" ] |
| } |
| |
| config("set_options_in_header_file") { |
| cflags = [ |
| "-include", |
| rebase_path("my_config_overrides.h", root_build_dir), |
| ] |
| } |
| |
| .. admonition:: Why this config pattern is preferred |
| |
| Alternate patterns for configuring a module include overriding the module's |
| config header or having that header optionally include a header at a known |
| path (e.g. ``pw_foo/config_overrides.h``). There are a few downsides to these |
| approaches: |
| |
| * The module needs its own config header that defines, provides defaults for, |
| and validates the configuration options. Replacing this header with a |
| user-defined header would require defining all options in the user's header, |
| which is cumbersome and brittle, and would bypass validation in the module's |
| header. |
| * Including a config override header at a particular path would prevent |
| multiple modules from sharing the same configuration file. Multiple headers |
| could redirect to the same configuration file, but this would still require |
| creating a separate header and setting the config backend variable for each |
| module. |
| * Optionally including a config override header requires boilerplate code that |
| would have to be duplicated in every configurable module. |
| * An optional config override header file would silently be excluded if the |
| file path were accidentally misspelled. |
| |
| Python module structure |
| ----------------------- |
| Python code is structured as described in the :ref:`docs-python-build-structure` |
| section of :ref:`docs-python-build`. |
| |
| .. _docs-module-structure-facades: |
| |
| Facades |
| ------- |
| In Pigweed, facades represent a dependency that can be swapped at compile time. |
| Facades are similar in concept to a virtual interface, but the implementation is |
| set by the build system. Runtime polymorphism with facades is not |
| possible, and each facade may only have one implementation (backend) per |
| toolchain compilation. |
| |
| In the simplest sense, a facade is just a dependency represented by a variable. |
| For example, the ``pw_log`` facade is represented by the ``pw_log_BACKEND`` |
| build variable. Facades typically are bundled with a build system library that |
| depends on the backend. |
| |
| Facades are essential in some circumstances: |
| |
| * Low-level, platform-specific features (:ref:`module-pw_cpu_exception`). |
| * Features that require a macro or non-virtual function interface |
| (:ref:`module-pw_log`, :ref:`module-pw_assert`). |
| * Highly leveraged code where a virtual interface or callback is too costly or |
| cumbersome (:ref:`module-pw_tokenizer`). |
| |
| .. caution:: |
| |
| Modules should only use facades when necessary. Facades are permanently locked |
| to a particular implementation at compile time. Multiple backends cannot be |
| used in one build, and runtime dependency injection is not possible, which |
| makes testing difficult. Where appropriate, modules should use other |
| mechanisms, such as virtual interfaces, callbacks, or templates, in place of |
| facades. |
| |
| The GN build system provides the |
| :ref:`pw_facade template<module-pw_build-facade>` as a convenient way to declare |
| facades. |
| |
| Documentation |
| ------------- |
| Documentation should go in the root module folder, typically in the |
| ``docs.rst`` file. There must be a docgen entry for the documentation in the |
| ``BUILD.gn`` file with the target name ``docs``; so the full target for the |
| docs would be ``<module>:docs``. |
| |
| .. code-block:: |
| |
| pw_example_module/... |
| |
| docs.rst |
| |
| For modules with more involved documentation, create a separate directory |
| called ``docs/`` under the module root, and put the ``.rst`` files and other |
| related files (like images and diagrams) there. |
| |
| .. code-block:: |
| |
| pw_example_module/... |
| |
| docs/docs.rst |
| docs/bar.rst |
| docs/foo.rst |
| docs/image/screenshot.png |
| docs/image/diagram.svg |
| |
| Creating a new Pigweed module |
| ----------------------------- |
| To create a new Pigweed module, follow the below steps. |
| |
| .. tip:: |
| |
| Connect with the Pigweed community (by `mailing the Pigweed list |
| <https://groups.google.com/forum/#!forum/pigweed>`_ or `raising your idea |
| in the Pigweed chat <https://discord.gg/M9NSeTA>`_) to discuss your module |
| idea before getting too far into the implementation. This can prevent |
| accidentally duplicating work, or avoiding writing code that won't get |
| accepted. |
| |
| 1. Create module folder following `Module name`_ guidelines |
| 2. Add `C++ public headers`_ files in |
| ``{pw_module_dir}/public/{pw_module_name}/`` |
| 3. Add `C++ implementation files`_ files in ``{pw_module_dir}/`` |
| 4. Add module documentation |
| |
| - Add ``{pw_module_dir}/README.md`` that has a module summary |
| - Add ``{pw_module_dir}/docs.rst`` that contains the main module |
| documentation |
| |
| 5. Add build support inside of new module |
| |
| - Add GN with ``{pw_module_dir}/BUILD.gn`` |
| - Add Bazel with ``{pw_module_dir}/BUILD`` |
| - Add CMake with ``{pw_module_dir}/CMakeLists.txt`` |
| |
| 6. Add folder alias for new module variable in ``/modules.gni`` |
| |
| - ``dir_pw_new = get_path_info("pw_new", "abspath")`` |
| |
| 7. Add new module to main GN build |
| |
| - in ``/BUILD.gn`` to ``group("pw_modules")`` using folder alias variable |
| |
| 8. Add test target for new module in ``/BUILD.gn`` to |
| ``pw_test_group("pw_module_tests")`` |
| 9. Add new module to CMake build |
| |
| - In ``/CMakeLists.txt`` add ``add_subdirectory(pw_new)`` |
| |
| 10. Add the new module to docs module |
| |
| - Add in ``docs/BUILD.gn`` to ``group("module_docs")`` |
| |
| 11. Run :ref:`module-pw_module-module-check` |
| |
| - ``$ pw module-check {pw_module_dir}`` |
| |
| 12. Contribute your module to upstream Pigweed (optional but encouraged!) |