blob: 586fca09d56a2629d654b69715d1451e7fe4c4c3 [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _docs-module-structure:
Alexei Frolovb3f7fda2020-03-18 14:59:20 -07002
Keir Mierle086ef1c2020-03-19 02:03:51 -07003----------------
4Module Structure
5----------------
Alexei Frolovb3f7fda2020-03-18 14:59:20 -07006The 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
Wyatt Heplere0575f72020-10-16 10:47:03 -070030 # Exposed private headers go under internal/
Alexei Frolovb3f7fda2020-03-18 14:59:20 -070031 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/setup.py # All Python must be a Python module with setup.py
59 py/foo_test.py # Tests go in py/ but outside of the Python module
60 py/bar_test.py
61 py/pw_foo/__init__.py
62 py/pw_foo/__main__.py
63 py/pw_foo/bar.py
64
65 # Go files go into 'go/...'
66 go/...
67
68 # Examples go in examples/, mixing different languages
69 examples/demo.py
70 examples/demo.cc
71 examples/demo.go
72 examples/BUILD.gn
73 examples/BUILD
74
75 # Size reports go under size_report/
76 size_report/BUILD.gn
77 size_report/base.cc
78 size_report/use_case_a.cc
79 size_report/use_case_b.cc
80
81 # Protobuf definition files go into <module>_protos/...
82 pw_foo_protos/foo.proto
83 pw_foo_protos/internal/zap.proto
84
85 # Other directories are fine, but should be private.
86 data/...
87 graphics/...
88 collection_of_tests/...
89 code_relating_to_subfeature/...
90
91Module name
Ewout van Bekkume072fab2020-07-17 16:34:00 -070092-----------
Alexei Frolovb3f7fda2020-03-18 14:59:20 -070093Pigweed upstream modules are always named with a prefix ``pw_`` to enforce
94namespacing. Projects using Pigweed that wish to make their own modules can use
95whatever name they like, but we suggest picking a short prefix to namespace
96your product (e.g. for an Internet of Toast project, perhaps the prefix could
97be ``it_``).
98
Keir Mierle16f86f42020-08-09 01:01:20 -070099C++ module structure
100--------------------
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700101
102C++ public headers
Ewout van Bekkume072fab2020-07-17 16:34:00 -0700103~~~~~~~~~~~~~~~~~~
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700104Located ``{pw_module_dir}/public/<module>``. These are headers that must be
105exposed due to C++ limitations (i.e. are included from the public interface,
106but are not intended for public use).
107
108**Public headers** should take the form:
109
110``{pw_module_dir}/public/<module>/*.h``
111
112**Exposed private headers** should take the form:
113
114``{pw_module_dir}/public/<module>/internal/*.h``
115
116Examples:
117
118.. code-block::
119
120 pw_foo/...
121 public/pw_foo/foo.h
122 public/pw_foo/a_header.h
123 public/pw_foo/baz.h
124
125For headers that must be exposed due to C++ limitations (i.e. are included from
126the public interface, but are not intended for use), place the headers in a
127``internal`` subfolder under the public headers directory; as
128``{pw_module_dir}/public/<module>/internal/*.h``. For example:
129
130.. code-block::
131
132 pw_foo/...
133 public/pw_foo/internal/secret.h
134 public/pw_foo/internal/business.h
135
136.. note::
137
138 These headers must not override headers from other modules. For
139 that, there is the ``public_overrides/`` directory.
140
Keir Mierle16f86f42020-08-09 01:01:20 -0700141C++ public override headers
142~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700143Located ``{pw_module_dir}/public_overrides/<module>``. In general, the Pigweed
144philosophy is to avoid having "things hiding under rocks", and having header
145files with the same name that can override each other is considered a rock
146where surprising things can hide. Additionally, a design goal of the Pigweed
147module structure is to make it so there is ideally exactly one obvious place
148to find a header based on an ``#include``.
149
150However, in some cases header overrides are necessary to enable flexibly
151combining modules. To make this as explicit as possible, headers which override
152other headers must go in
153
154``{pw_module_dir}/public_overrides/...```
155
156For example, the ``pw_unit_test`` module provides a header override for
157``gtest/gtest.h``. The structure of the module is (omitting some files):
158
159.. code-block::
160
161 pw_unit_test/...
162
163 public_overrides/gtest
164 public_overrides/gtest/gtest.h
165
166 public/pw_unit_test
167 public/pw_unit_test/framework.h
168 public/pw_unit_test/simple_printing_event_handler.h
169 public/pw_unit_test/event_handler.h
170
171Note that the overrides are in a separate directory ``public_overrides``.
172
173C++ implementation files
Ewout van Bekkume072fab2020-07-17 16:34:00 -0700174~~~~~~~~~~~~~~~~~~~~~~~~
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700175Located ``{pw_module_dir}/``. C++ implementation files go at the top level of
176the module. Implementation files must always use "" style includes.
177
178Example:
179
180.. code-block::
181
182 pw_unit_test/...
183 main.cc
184 framework.cc
185 test.gni
186 BUILD.gn
187 README.md
188
Wyatt Heplere0575f72020-10-16 10:47:03 -0700189Compile-time configuration
190~~~~~~~~~~~~~~~~~~~~~~~~~~
191Pigweed modules are intended to be used in a wide variety of environments.
192In support of this, some modules expose compile-time configuration options.
193Pigweed has an established pattern for declaring and overriding module
194configuration.
195
196.. tip::
197
198 Compile-time configuration provides flexibility, but also imposes
199 restrictions. A module can only have one configuration in a given build.
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700200 This makes testing modules with compile-time configuration more difficult.
201 Where appropriate, consider alternatives such as C++ templates or runtime
Wyatt Heplere0575f72020-10-16 10:47:03 -0700202 configuration.
203
204Declaring configuration
205^^^^^^^^^^^^^^^^^^^^^^^
206Configuration values are declared in a header file with macros. If the macro
207value is not already defined, a default definition is provided. Otherwise,
208nothing is done. Configuration headers may include ``static_assert`` statements
209to validate configuration values.
210
211.. code-block:: c++
212
213 // Example configuration header
214
215 #ifndef PW_FOO_INPUT_BUFFER_SIZE_BYTES
216 #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 128
217 #endif // PW_FOO_INPUT_BUFFER_SIZE_BYTES
218
219 static_assert(PW_FOO_INPUT_BUFFER_SIZE_BYTES >= 64);
220
221The configuration header may go in one of three places in the module, depending
222on whether the header should be exposed by the module or not.
223
224.. code-block::
225
226 pw_foo/...
227
228 # Publicly accessible configuration header
229 public/pw_foo/config.h
230
231 # Internal configuration header that is included by other module headers
232 public/pw_foo/internal/config.h
233
234 # Internal configuration header
235 pw_foo_private/config.h
236
237The configuration header is provided by a build system library. This library
238acts as a :ref:`facade<docs-module-structure-facades>`. The facade uses a
239variable such as ``pw_foo_CONFIG``. In upstream Pigweed, all config facades
240default to the ``pw_build_DEFAULT_MODULE_CONFIG`` backend. In the GN build
241system, the config facade is declared as follows:
242
243.. code-block::
244
245 declare_args() {
246 # The build target that overrides the default configuration options for this
247 # module. This should point to a source set that provides defines through a
248 # public config (which may -include a file or add defines directly).
249 pw_foo_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
250 }
251
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700252 # An example source set for each potential config header location follows.
253
Wyatt Heplere0575f72020-10-16 10:47:03 -0700254 # Publicly accessible configuration header (most common)
255 pw_source_set("config") {
256 public = [ "public/pw_foo/config.h" ]
257 public_configs = [ ":public_include_path" ]
258 public_deps = [ pw_foo_CONFIG ]
259 }
260
261 # Internal configuration header that is included by other module headers
262 pw_source_set("config") {
263 sources = [ "public/pw_foo/internal/config.h" ]
264 public_configs = [ ":public_include_path" ]
265 public_deps = [ pw_foo_CONFIG ]
266 visibility = [":*"] # Only allow this module to depend on ":config"
267 friend = [":*"] # Allow this module to access the config.h header.
268 }
269
270 # Internal configuration header
271 pw_source_set("config") {
272 public = [ "pw_foo_private/config.h" ]
273 public_deps = [ pw_foo_CONFIG ]
274 visibility = [":*"] # Only allow this module to depend on ":config"
275 }
276
277Overriding configuration
278^^^^^^^^^^^^^^^^^^^^^^^^
279As noted above, all module configuration facades default to the same backend
280(``pw_build_DEFAULT_MODULE_CONFIG``). This allows projects to override
281configuration values for multiple modules from a single configuration backend,
282if desired. The configuration values may also be overridden individually by
283setting backends for the individual module configurations (e.g. in GN,
284``pw_foo_CONFIG = "//configuration:my_foo_config"``).
285
286Configurations are overridden by setting compilation options in the config
287backend. These options could be set through macro definitions, such as
288``-DPW_FOO_INPUT_BUFFER_SIZE_BYTES=256``, or in a header file included with the
289``-include`` option.
290
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700291This example shows two ways to configure a module in the GN build system.
Wyatt Heplere0575f72020-10-16 10:47:03 -0700292
293.. code-block::
294
295 # In the toolchain, set either pw_build_DEFAULT_MODULE_CONFIG or pw_foo_CONFIG
296 pw_build_DEFAULT_MODULE_CONFIG = get_path_info(":define_overrides", "abspath")
297
298 # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES using the -D macro.
299 pw_source_set("define_overrides") {
300 public_configs = [ ":define_options" ]
301 }
302
303 config("define_options") {
304 defines = [ "-DPW_FOO_INPUT_BUFFER_SIZE_BYTES=256" ]
305 }
306
307 # This configuration sets PW_FOO_INPUT_BUFFER_SIZE_BYTES with a header file.
308 pw_source_set("include_overrides") {
309 public_configs = [ ":header_options" ]
310
311 # Header file with #define PW_FOO_INPUT_BUFFER_SIZE_BYTES 256
312 sources = [ "my_config_overrides.h" ]
313 }
314
315 config("header_options") {
316 cflags = [
317 "-include",
318 "my_config_overrides.h",
319 ]
320 }
321
322.. _docs-module-structure-facades:
323
324Facades
325-------
326In Pigweed, facades represent a dependency that can be swapped at compile time.
327Facades are similar in concept to a virtual interface, but the implementation is
328set by the build system. Runtime polymorphism with facades is not
329possible, and each facade may only have one implementation (backend) per
330toolchain compilation.
331
332In the simplest sense, a facade is just a dependency represented by a variable.
333For example, the ``pw_log`` facade is represented by the ``pw_log_BACKEND``
334build variable. Facades typically are bundled with a build system library that
335depends on the backend.
336
Wyatt Heplere0575f72020-10-16 10:47:03 -0700337Facades are essential in some circumstances:
338
339* Low-level, platform-specific features (:ref:`module-pw_cpu_exception`).
340* Features that require a macro or non-virtual function interface
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700341 (:ref:`module-pw_log`, :ref:`module-pw_assert`).
Wyatt Heplere0575f72020-10-16 10:47:03 -0700342* Highly leveraged code where a virtual interface or callback is too costly or
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700343 cumbersome (:ref:`module-pw_tokenizer`).
344
345.. caution::
346
347 Modules should only use facades when necessary. Facades are permanently locked
348 to a particular implementation at compile time. Multpile backends cannot be
349 used in one build, and runtime dependency injection is not possible, which
350 makes testing difficult. Where appropriate, modules should use other
351 mechanisms, such as virtual interfaces, callbacks, or templates, in place of
352 facades.
Wyatt Heplere0575f72020-10-16 10:47:03 -0700353
354The GN build system provides the
355:ref:`pw_facade template<module-pw_build-facade>` as a convenient way to declare
356facades.
357
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700358Documentation
Keir Mierle16f86f42020-08-09 01:01:20 -0700359-------------
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700360Documentation should go in the root module folder, typically in the
361``docs.rst`` file. There must be a docgen entry for the documentation in the
362``BUILD.gn`` file with the target name ``docs``; so the full target for the
363docs would be ``<module>:docs``.
364
365.. code-block::
366
367 pw_example_module/...
368
369 docs.rst
370
371For modules with more involved documentation, create a separate directory
372called ``docs/`` under the module root, and put the ``.rst`` files and other
373related files (like images and diagrams) there.
374
375.. code-block::
376
377 pw_example_module/...
378
379 docs/docs.rst
380 docs/bar.rst
381 docs/foo.rst
382 docs/image/screenshot.png
383 docs/image/diagram.svg
384
Keir Mierle16f86f42020-08-09 01:01:20 -0700385Creating a new Pigweed module
386-----------------------------
387To create a new Pigweed module, follow the below steps.
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700388
Keir Mierle16f86f42020-08-09 01:01:20 -0700389.. tip::
390
391 Connect with the Pigweed community (by `mailing the Pigweed list
392 <https://groups.google.com/forum/#!forum/pigweed>`_ or `raising your idea
393 in the Pigweed chat <https://discord.gg/M9NSeTA>`_) to discuss your module
394 idea before getting too far into the implementation. This can prevent
395 accidentally duplicating work, or avoiding writing code that won't get
396 accepted.
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700397
3981. Create module folder following `Module name`_ guidelines
3992. Add `C++ public headers`_ files in
400 ``{pw_module_dir}/public/{pw_module_name}/``
4013. Add `C++ implementation files`_ files in ``{pw_module_dir}/``
4024. Add module documentation
403
404 - Add ``{pw_module_dir}/README.md`` that has a module summary
405 - Add ``{pw_module_dir}/docs.rst`` that contains the main module
406 documentation
407
4085. Add build support inside of new module
409
410 - Add GN with ``{pw_module_dir}/BUILD.gn``
411 - Add Bazel with ``{pw_module_dir}/BUILD``
412 - Add CMake with ``{pw_module_dir}/CMakeLists.txt``
413
4146. Add folder alias for new module variable in ``/modules.gni``
415
Wyatt Hepler2fb36b92020-10-29 13:15:34 -0700416 - ``dir_pw_new = get_path_info("pw_new", "abspath")``
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700417
4187. Add new module to main GN build
419
420 - in ``/BUILD.gn`` to ``group("pw_modules")`` using folder alias variable
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700421
4228. Add test target for new module in ``/BUILD.gn`` to
423 ``pw_test_group("pw_module_tests")``
4249. Add new module to CMake build
425
426 - In ``/CMakeLists.txt`` add ``add_subdirectory(pw_new)``
427
42810. Add the new module to docs module
429
430 - Add in ``docs/BUILD.gn`` to ``pw_doc_gen("docs")``
431
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070043211. Run :ref:`module-pw_module-module-check`
Alexei Frolovb3f7fda2020-03-18 14:59:20 -0700433
434 - ``$ pw module-check {pw_module_dir}``
435
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070043612. Contribute your module to upstream Pigweed (optional but encouraged!)