1.. _docs-module-structure:
2
3----------------
4Module Structure
5----------------
6The Pigweed module structure is designed to keep as much code as possible for a
7particular slice of functionality in one place. That means including the code
8from multiple languages, as well as all the related documentation and tests.
9
10Additionally, the structure is designed to limit the number of places a file
11could go, so that when reading callsites it is obvious where a header is from.
12That is where the duplicated ``<module>`` occurrences in file paths comes from.
13
14Example module structure
15------------------------
16.. code-block:: python
17
18  pw_foo/...
19
20    docs.rst   # If there is just 1 docs file, call it docs.rst
21    README.md  # All modules must have a short README for gittiles
22
23    BUILD.gn   # GN build required
24    BUILD      # Bazel build required
25
26    # C++ public headers; the repeated module name is required
27    public/pw_foo/foo.h
28    public/pw_foo/baz.h
29
30    # Exposed private headers go under internal/
31    public/pw_foo/internal/bar.h
32    public/pw_foo/internal/qux.h
33
34    # Public override headers must go in 'public_overrides'
35    public_overrides/gtest/gtest.h
36    public_overrides/string.h
37
38    # Private headers go into <module>_*/...
39    pw_foo_internal/zap.h
40    pw_foo_private/zip.h
41    pw_foo_secret/alxx.h
42
43    # C++ implementations go in the root
44    foo_impl.cc
45    foo.cc
46    baz.cc
47    bar.cc
48    zap.cc
49    zip.cc
50    alxx.cc
51
52    # C++ tests also go in the root
53    foo_test.cc
54    bar_test.cc
55    zip_test.cc
56
57    # Python files go into 'py/<module>/...'
58    py/BUILD.gn     # Python packages are declared in GN using pw_python_package
59    py/setup.py     # Python files are structured as standard Python packages
60    py/foo_test.py  # Tests go in py/ but outside of the Python package
61    py/bar_test.py
62    py/pw_foo/__init__.py
63    py/pw_foo/__main__.py
64    py/pw_foo/bar.py
65    py/pw_foo/py.typed  # Indicates that this package has type annotations
66
67    # Go files go into 'go/...'
68    go/...
69
70    # Examples go in examples/, mixing different languages
71    examples/demo.py
72    examples/demo.cc
73    examples/demo.go
74    examples/BUILD.gn
75    examples/BUILD
76
77    # Size reports go under size_report/
78    size_report/BUILD.gn
79    size_report/base.cc
80    size_report/use_case_a.cc
81    size_report/use_case_b.cc
82
83    # Protobuf definition files go into <module>_protos/...
84    pw_foo_protos/foo.proto
85    pw_foo_protos/internal/zap.proto
86
87    # Other directories are fine, but should be private.
88    data/...
89    graphics/...
90    collection_of_tests/...
91    code_relating_to_subfeature/...
92
93Module name
94-----------
95Pigweed upstream modules are always named with a prefix ``pw_`` to enforce
96namespacing. Projects using Pigweed that wish to make their own modules can use
97whatever name they like, but we suggest picking a short prefix to namespace
98your product (e.g. for an Internet of Toast project, perhaps the prefix could
99be ``it_``).
100
101C++ module structure
102--------------------
103
104C++ public headers
105~~~~~~~~~~~~~~~~~~
106Located ``{pw_module_dir}/public/<module>``. These are headers that must be
107exposed due to C++ limitations (i.e. are included from the public interface,
108but are not intended for public use).
109
110**Public headers** should take the form:
111
112``{pw_module_dir}/public/<module>/*.h``
113
114**Exposed private headers** should take the form:
115
116``{pw_module_dir}/public/<module>/internal/*.h``
117
118Examples:
119
120.. code-block::
121
122  pw_foo/...
123    public/pw_foo/foo.h
124    public/pw_foo/a_header.h
125    public/pw_foo/baz.h
126
127For headers that must be exposed due to C++ limitations (i.e. are included from
128the public interface, but are not intended for use), place the headers in a
129``internal`` subfolder under the public headers directory; as
130``{pw_module_dir}/public/<module>/internal/*.h``. For example:
131
132.. code-block::
133
134  pw_foo/...
135    public/pw_foo/internal/secret.h
136    public/pw_foo/internal/business.h
137
138.. note::
139
140  These headers must not override headers from other modules. For
141  that, there is the ``public_overrides/`` directory.
142
143C++ public override headers
144~~~~~~~~~~~~~~~~~~~~~~~~~~~
145Located ``{pw_module_dir}/public_overrides/<module>``. In general, the Pigweed
146philosophy is to avoid having "things hiding under rocks", and having header
147files with the same name that can override each other is considered a rock
148where surprising things can hide. Additionally, a design goal of the Pigweed
149module structure is to make it so there is ideally exactly one obvious place
150to find a header based on an ``#include``.
151
152However, in some cases header overrides are necessary to enable flexibly
153combining modules. To make this as explicit as possible, headers which override
154other headers must go in
155
156``{pw_module_dir}/public_overrides/...```
157
158For example, the ``pw_unit_test`` module provides a header override for
159``gtest/gtest.h``. The structure of the module is (omitting some files):
160
161.. code-block::
162
163  pw_unit_test/...
164
165    public_overrides/gtest
166    public_overrides/gtest/gtest.h
167
168    public/pw_unit_test
169    public/pw_unit_test/framework.h
170    public/pw_unit_test/simple_printing_event_handler.h
171    public/pw_unit_test/event_handler.h
172
173Note that the overrides are in a separate directory ``public_overrides``.
174
175C++ implementation files
176~~~~~~~~~~~~~~~~~~~~~~~~
177Located ``{pw_module_dir}/``. C++ implementation files go at the top level of
178the module. Implementation files must always use "" style includes.
179
180Example:
181
182.. code-block::
183
184  pw_unit_test/...
185    main.cc
186    framework.cc
187    test.gni
188    BUILD.gn
189    README.md
190
191Compile-time configuration
192~~~~~~~~~~~~~~~~~~~~~~~~~~
193Pigweed modules are intended to be used in a wide variety of environments.
194In support of this, some modules expose compile-time configuration options.
195Pigweed has an established pattern for declaring and overriding module
196configuration.
197
198.. tip::
199
200  Compile-time configuration provides flexibility, but also imposes
201  restrictions. A module can only have one configuration in a given build.
202  This makes testing modules with compile-time configuration more difficult.
203  Where appropriate, consider alternatives such as C++ templates or runtime
204  configuration.
205
206Declaring configuration
207^^^^^^^^^^^^^^^^^^^^^^^
208Configuration options are declared in a header file as macros. If the macro is
209not already defined, a default definition is provided. Otherwise, nothing is
210done. Configuration headers may include ``static_assert`` statements to validate
211configuration values.
212
213.. code-block:: c++
214
215  // Example configuration header
216
217  #ifndef PW_FOO_INPUT_BUFFER_SIZE_BYTES
218  #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 128
219  #endif  // PW_FOO_INPUT_BUFFER_SIZE_BYTES
220
221  static_assert(PW_FOO_INPUT_BUFFER_SIZE_BYTES >= 64);
222
223The configuration header may go in one of three places in the module, depending
224on whether the header should be exposed by the module or not.
225
226.. code-block::
227
228  pw_foo/...
229
230    # Publicly accessible configuration header
231    public/pw_foo/config.h
232
233    # Internal configuration header that is included by other module headers
234    public/pw_foo/internal/config.h
235
236    # Internal configuration header
237    pw_foo_private/config.h
238
239The configuration header is provided by a build system library. This library
240acts as a :ref:`facade<docs-module-structure-facades>`. The facade uses a
241variable such as ``pw_foo_CONFIG``. In upstream Pigweed, all config facades
242default to the ``pw_build_DEFAULT_MODULE_CONFIG`` backend. In the GN build
243system, the config facade is declared as follows:
244
245.. code-block::
246
247  declare_args() {
248    # The build target that overrides the default configuration options for this
249    # module. This should point to a source set that provides defines through a
250    # public config (which may -include a file or add defines directly).
251    pw_foo_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
252  }
253
254  # An example source set for each potential config header location follows.
255
256  # Publicly accessible configuration header (most common)
257  pw_source_set("config") {
258    public = [ "public/pw_foo/config.h" ]
259    public_configs = [ ":public_include_path" ]
260    public_deps = [ pw_foo_CONFIG ]
261  }
262
263  # Internal configuration header that is included by other module headers
264  pw_source_set("config") {
265    sources = [ "public/pw_foo/internal/config.h" ]
266    public_configs = [ ":public_include_path" ]
267    public_deps = [ pw_foo_CONFIG ]
268    visibility = [":*"]  # Only allow this module to depend on ":config"
269    friend = [":*"]  # Allow this module to access the config.h header.
270  }
271
272  # Internal configuration header
273  pw_source_set("config") {
274    public = [ "pw_foo_private/config.h" ]
275    public_deps = [ pw_foo_CONFIG ]
276    visibility = [":*"]  # Only allow this module to depend on ":config"
277  }
278
279Overriding configuration
280^^^^^^^^^^^^^^^^^^^^^^^^
281As noted above, all module configuration facades default to the same backend
282(``pw_build_DEFAULT_MODULE_CONFIG``). This allows projects to override
283configuration values for multiple modules from a single configuration backend,
284if desired. The configuration values may also be overridden individually by
285setting backends for the individual module configurations (e.g. in GN,
286``pw_foo_CONFIG = "//configuration:my_foo_config"``).
287
288Configurations options are overridden by setting macros in the config backend.
289These macro definitions can be provided through compilation options, such as
290``-DPW_FOO_INPUT_BUFFER_SIZE_BYTES=256``. Configuration macro definitions may
291also be set in a header file. The header file is included using the ``-include``
292compilation option.
293
294This example shows two ways to configure a module in the GN build system.
295
296.. code-block::
297
298  # In the toolchain, set either pw_build_DEFAULT_MODULE_CONFIG or pw_foo_CONFIG
299  pw_build_DEFAULT_MODULE_CONFIG = get_path_info(":define_overrides", "abspath")
300
301  # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES using the -D flag.
302  pw_source_set("define_overrides") {
303    public_configs = [ ":define_options" ]
304  }
305
306  config("define_options") {
307    defines = [ "PW_FOO_INPUT_BUFFER_SIZE_BYTES=256" ]
308  }
309
310  # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES in a header file.
311  pw_source_set("include_overrides") {
312    public_configs = [ ":set_options_in_header_file" ]
313
314    # Header file with #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 256
315    sources = [ "my_config_overrides.h" ]
316  }
317
318  config("set_options_in_header_file") {
319    cflags = [
320      "-include",
321      rebase_path("my_config_overrides.h"),
322    ]
323  }
324
325.. admonition:: Why this config pattern is preferred
326
327  Alternate patterns for configuring a module include overriding the module's
328  config header or having that header optionally include a header at a known
329  path (e.g. ``pw_foo/config_overrides.h``). There are a few downsides to these
330  approaches:
331
332  * The module needs its own config header that defines, provides defaults for,
333    and validates the configuration options. Replacing this header with a
334    user-defined header would require defining all options in the user's header,
335    which is cumbersome and brittle, and would bypass validation in the module's
336    header.
337  * Including a config override header at a particular path would prevent
338    multiple modules from sharing the same configuration file. Multiple headers
339    could redirect to the same configuration file, but this would still require
340    creating a separate header and setting the config backend variable for each
341    module.
342  * Optionally including a config override header requires boilerplate code that
343    would have to be duplicated in every configurable module.
344  * An optional config override header file would silently be excluded if the
345    file path were accidentally misspelled.
346
347Python module structure
348-----------------------
349Python code is structured as described in the :ref:`docs-python-build-structure`
350section of :ref:`docs-python-build`.
351
352.. _docs-module-structure-facades:
353
354Facades
355-------
356In Pigweed, facades represent a dependency that can be swapped at compile time.
357Facades are similar in concept to a virtual interface, but the implementation is
358set by the build system. Runtime polymorphism with facades is not
359possible, and each facade may only have one implementation (backend) per
360toolchain compilation.
361
362In the simplest sense, a facade is just a dependency represented by a variable.
363For example, the ``pw_log`` facade is represented by the ``pw_log_BACKEND``
364build variable. Facades typically are bundled with a build system library that
365depends on the backend.
366
367Facades are essential in some circumstances:
368
369* Low-level, platform-specific features (:ref:`module-pw_cpu_exception`).
370* Features that require a macro or non-virtual function interface
371  (:ref:`module-pw_log`, :ref:`module-pw_assert`).
372* Highly leveraged code where a virtual interface or callback is too costly or
373  cumbersome (:ref:`module-pw_tokenizer`).
374
375.. caution::
376
377  Modules should only use facades when necessary. Facades are permanently locked
378  to a particular implementation at compile time. Multiple backends cannot be
379  used in one build, and runtime dependency injection is not possible, which
380  makes testing difficult. Where appropriate, modules should use other
381  mechanisms, such as virtual interfaces, callbacks, or templates, in place of
382  facades.
383
384The GN build system provides the
385:ref:`pw_facade template<module-pw_build-facade>` as a convenient way to declare
386facades.
387
388Documentation
389-------------
390Documentation should go in the root module folder, typically in the
391``docs.rst`` file. There must be a docgen entry for the documentation in the
392``BUILD.gn`` file with the target name ``docs``; so the full target for the
393docs would be ``<module>:docs``.
394
395.. code-block::
396
397  pw_example_module/...
398
399    docs.rst
400
401For modules with more involved documentation, create a separate directory
402called ``docs/`` under the module root, and put the ``.rst`` files and other
403related files (like images and diagrams) there.
404
405.. code-block::
406
407  pw_example_module/...
408
409    docs/docs.rst
410    docs/bar.rst
411    docs/foo.rst
412    docs/image/screenshot.png
413    docs/image/diagram.svg
414
415Creating a new Pigweed module
416-----------------------------
417To create a new Pigweed module, follow the below steps.
418
419.. tip::
420
421  Connect with the Pigweed community (by `mailing the Pigweed list
422  <https://groups.google.com/forum/#!forum/pigweed>`_ or `raising your idea
423  in the Pigweed chat <https://discord.gg/M9NSeTA>`_) to discuss your module
424  idea before getting too far into the implementation. This can prevent
425  accidentally duplicating work, or avoiding writing code that won't get
426  accepted.
427
4281. Create module folder following `Module name`_ guidelines
4292. Add `C++ public headers`_ files in
430   ``{pw_module_dir}/public/{pw_module_name}/``
4313. Add `C++ implementation files`_ files in ``{pw_module_dir}/``
4324. Add module documentation
433
434    - Add ``{pw_module_dir}/README.md`` that has a module summary
435    - Add ``{pw_module_dir}/docs.rst`` that contains the main module
436      documentation
437
4385. Add build support inside of new module
439
440    - Add GN with ``{pw_module_dir}/BUILD.gn``
441    - Add Bazel with ``{pw_module_dir}/BUILD``
442    - Add CMake with ``{pw_module_dir}/CMakeLists.txt``
443
4446. Add folder alias for new module variable in ``/modules.gni``
445
446    - ``dir_pw_new = get_path_info("pw_new", "abspath")``
447
4487. Add new module to main GN build
449
450    - in ``/BUILD.gn`` to ``group("pw_modules")`` using folder alias variable
451
4528. Add test target for new module in ``/BUILD.gn`` to
453   ``pw_test_group("pw_module_tests")``
4549. Add new module to CMake build
455
456    - In ``/CMakeLists.txt`` add ``add_subdirectory(pw_new)``
457
45810. Add the new module to docs module
459
460    - Add in ``docs/BUILD.gn`` to ``group("module_docs")``
461
46211. Run :ref:`module-pw_module-module-check`
463
464    - ``$ pw module-check {pw_module_dir}``
465
46612. Contribute your module to upstream Pigweed (optional but encouraged!)
467