Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 1 | .. _chapter-module: |
| 2 | |
| 3 | .. default-domain:: cpp |
| 4 | |
| 5 | .. highlight:: sh |
| 6 | |
| 7 | --------- |
| 8 | pw_module |
| 9 | --------- |
| 10 | |
| 11 | The ``pw_module`` module is the "meta-module" for Pigweed, containing |
| 12 | documentation for Pigweed's module structure as well as tools to detect when |
| 13 | modules are in or out of conformance to the module style. |
| 14 | |
| 15 | The Pigweed module structure |
| 16 | ---------------------------- |
| 17 | |
| 18 | The Pigweed module structure is designed to keep as much code as possible for a |
| 19 | particular slice of functionality in one place. That means including the code |
| 20 | from multiple languages, as well as all the related documentation and tests. |
| 21 | |
Alexei Frolov | 44d5473 | 2020-01-10 14:45:43 -0800 | [diff] [blame] | 22 | Additionally, the structure is designed to limit the number of places a file |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 23 | could go, so that when reading callsites it is obvious where a header is from. |
Alexei Frolov | 44d5473 | 2020-01-10 14:45:43 -0800 | [diff] [blame] | 24 | That is where the duplicated ``<module>`` occurrences in file paths comes from. |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 25 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 26 | Steps to create a new module Pigweed project |
| 27 | -------------------------------------------- |
| 28 | |
| 29 | These instructions are for creating a new module for contribution to the |
| 30 | Pigweed project. See below for an `example`__ of what the new module folder |
| 31 | might look like. |
| 32 | |
| 33 | __ `Example module structure`_ |
| 34 | |
| 35 | 1. Create module folder following `Module name`_ guidelines |
| 36 | 2. Add `C++ public headers`_ files in |
| 37 | ``{pw_module_dir}/public/{pw_module_name}/`` |
| 38 | 3. Add `C++ implementation files`_ files in ``{pw_module_dir}/`` |
| 39 | 4. Add module documentation |
| 40 | |
| 41 | - Add ``{pw_module_dir}/README.md`` that has a module summary |
| 42 | - Add ``{pw_module_dir}/docs.rst`` that contains the main module |
| 43 | documentation |
| 44 | |
| 45 | 5. Add build support inside of new module |
| 46 | |
| 47 | - Add GN with ``{pw_module_dir}/BUILD.gn`` |
| 48 | - Add Bazel with ``{pw_module_dir}/BUILD`` |
| 49 | - Add CMake with ``{pw_module_dir}/CMakeLists.txt`` |
| 50 | |
| 51 | 6. Add folder alias for new module variable in ``/modules.gni`` |
| 52 | |
| 53 | - dir_pw_new = "$dir_pigweed/pw_new" |
| 54 | |
| 55 | 7. Add new module to main GN build |
| 56 | |
| 57 | - in ``/BUILD.gn`` to ``group("pw_modules")`` using folder alias variable |
| 58 | - General modules and backends of facades go in ``pw_modules``, facades go |
| 59 | in ``pw_facades`` |
| 60 | |
| 61 | 8. Add test target for new module in ``/BUILD.gn`` to |
| 62 | ``pw_test_group("pw_module_tests")`` |
| 63 | 9. Add new module to CMake build |
| 64 | |
| 65 | - In ``/CMakeLists.txt`` add ``add_subdirectory(pw_new)`` |
| 66 | |
| 67 | 10. Add the new module to docs module |
| 68 | |
| 69 | - Add in ``docs/BUILD.gn`` to ``pw_doc_gen("docs")`` |
| 70 | |
| 71 | 11. Run `module check`__ |
| 72 | |
| 73 | - ``$ pw module-check {pw_module_dir}`` |
| 74 | |
| 75 | __ `Command: pw module-check`_ |
| 76 | |
| 77 | Example module structure |
| 78 | ------------------------ |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 79 | |
| 80 | .. code-block:: python |
| 81 | |
| 82 | pw_foo/... |
| 83 | |
| 84 | docs.rst # If there is just 1 docs file, call it docs.rst |
| 85 | README.md # All modules must have a short README for gittiles |
| 86 | |
| 87 | BUILD.gn # GN build required |
| 88 | BUILD # Bazel build required |
| 89 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 90 | # C++ public headers; the repeated module name is required |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 91 | public/pw_foo/foo.h |
| 92 | public/pw_foo/baz.h |
| 93 | |
| 94 | # Exposed public headers go under internal/ |
| 95 | public/pw_foo/internal/bar.h |
| 96 | public/pw_foo/internal/qux.h |
| 97 | |
| 98 | # Public override headers must go in 'public_overrides' |
| 99 | public_overrides/gtest/gtest.h |
| 100 | public_overrides/string.h |
| 101 | |
| 102 | # Private headers go into <module>_*/... |
| 103 | pw_foo_internal/zap.h |
| 104 | pw_foo_private/zip.h |
| 105 | pw_foo_secret/alxx.h |
| 106 | |
| 107 | # C++ implementations go in the root |
| 108 | foo_impl.cc |
| 109 | foo.cc |
| 110 | baz.cc |
| 111 | bar.cc |
| 112 | zap.cc |
| 113 | zip.cc |
| 114 | alxx.cc |
| 115 | |
| 116 | # C++ tests also go in the root |
| 117 | foo_test.cc |
| 118 | bar_test.cc |
| 119 | zip_test.cc |
| 120 | |
| 121 | # Python files go into 'py/<module>/...' |
| 122 | py/setup.py # All Python must be a Python module with setup.py |
| 123 | py/foo_test.py # Tests go in py/ but outside of the Python module |
| 124 | py/bar_test.py |
| 125 | py/pw_foo/__init__.py |
| 126 | py/pw_foo/__main__.py |
| 127 | py/pw_foo/bar.py |
| 128 | |
| 129 | # Go files go into 'go/...' |
| 130 | go/... |
| 131 | |
| 132 | # Examples go in examples/, mixing different languages |
| 133 | examples/demo.py |
| 134 | examples/demo.cc |
| 135 | examples/demo.go |
| 136 | examples/BUILD.gn |
| 137 | examples/BUILD |
| 138 | |
| 139 | # Size reports go under size_report/ |
| 140 | size_report/BUILD.gn |
| 141 | size_report/base.cc |
| 142 | size_report/use_case_a.cc |
| 143 | size_report/use_case_b.cc |
| 144 | |
Alexei Frolov | 942adf0 | 2019-12-11 17:07:28 -0800 | [diff] [blame] | 145 | # Protobuf definition files go into <module>_protos/... |
| 146 | pw_foo_protos/foo.proto |
| 147 | pw_foo_protos/internal/zap.proto |
| 148 | |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 149 | # Other directories are fine, but should be private. |
| 150 | data/... |
| 151 | graphics/... |
| 152 | collection_of_tests/... |
| 153 | code_relating_to_subfeature/... |
| 154 | |
| 155 | Module name |
| 156 | ----------- |
| 157 | Pigweed upstream modules are always named with a prefix ``pw_`` to enforce |
| 158 | namespacing. Projects using Pigweed that wish to make their own modules can use |
Alexei Frolov | 44d5473 | 2020-01-10 14:45:43 -0800 | [diff] [blame] | 159 | whatever name they like, but we suggest picking a short prefix to namespace |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 160 | your product (e.g. for an Internet of Toast project, perhaps the prefix could |
Alexei Frolov | 44d5473 | 2020-01-10 14:45:43 -0800 | [diff] [blame] | 161 | be ``it_``). |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 162 | |
| 163 | C++ file and directory locations |
| 164 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 165 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 166 | C++ public headers |
| 167 | ^^^^^^^^^^^^^^^^^^ |
| 168 | Located ``{pw_module_dir}/public/<module>``. These are headers that must be |
| 169 | exposed due to C++ limitations (i.e. are included from the public interface, |
| 170 | but are not intended for public use). |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 171 | |
| 172 | **Public headers** should take the form: |
| 173 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 174 | ``{pw_module_dir}/public/<module>/*.h`` |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 175 | |
| 176 | **Exposed private headers** should take the form: |
| 177 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 178 | ``{pw_module_dir}/public/<module>/internal/*.h`` |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 179 | |
| 180 | Examples: |
| 181 | |
| 182 | .. code-block:: |
| 183 | |
| 184 | pw_foo/... |
| 185 | public/pw_foo/foo.h |
| 186 | public/pw_foo/a_header.h |
| 187 | public/pw_foo/baz.h |
| 188 | |
| 189 | For headers that must be exposed due to C++ limitations (i.e. are included from |
| 190 | the public interface, but are not intended for use), place the headers in a |
| 191 | ``internal`` subfolder under the public headers directory; as |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 192 | ``{pw_module_dir}/public/<module>/internal/*.h``. For example: |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 193 | |
| 194 | .. code-block:: |
| 195 | |
| 196 | pw_foo/... |
| 197 | public/pw_foo/internal/secret.h |
| 198 | public/pw_foo/internal/business.h |
| 199 | |
| 200 | .. note:: |
| 201 | |
| 202 | These headers must not override headers from other modules. For |
| 203 | that, there is the ``public_overrides/`` directory. |
| 204 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 205 | Public override headers |
| 206 | ^^^^^^^^^^^^^^^^^^^^^^^ |
| 207 | Located ``{pw_module_dir}/public_overrides/<module>``. In general, the Pigweed |
| 208 | philosophy is to avoid having "things hiding under rocks", and having header |
| 209 | files with the same name that can override each other is considered a rock |
| 210 | where surprising things can hide. Additionally, a design goal of the Pigweed |
| 211 | module structure is to make it so there is ideally exactly one obvious place |
| 212 | to find a header based on an ``#include``. |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 213 | |
| 214 | However, in some cases header overrides are necessary to enable flexibly |
| 215 | combining modules. To make this as explicit as possible, headers which override |
| 216 | other headers must go in |
| 217 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 218 | ``{pw_module_dir}/public_overrides/...``` |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 219 | |
| 220 | For example, the ``pw_unit_test`` module provides a header override for |
| 221 | ``gtest/gtest.h``. The structure of the module is (omitting some files): |
| 222 | |
| 223 | .. code-block:: |
| 224 | |
| 225 | pw_unit_test/... |
| 226 | |
| 227 | public_overrides/gtest |
| 228 | public_overrides/gtest/gtest.h |
| 229 | |
| 230 | public/pw_unit_test |
| 231 | public/pw_unit_test/framework.h |
| 232 | public/pw_unit_test/simple_printing_event_handler.h |
| 233 | public/pw_unit_test/event_handler.h |
| 234 | |
| 235 | Note that the overrides are in a separate directory ``public_overrides``. |
| 236 | |
David Rogers | 582c190 | 2020-01-16 18:17:09 -0800 | [diff] [blame] | 237 | C++ implementation files |
| 238 | ^^^^^^^^^^^^^^^^^^^^^^^^ |
| 239 | Located ``{pw_module_dir}/``. C++ implementation files go at the top level of |
| 240 | the module. Implementation files must always use "" style includes. |
Keir Mierle | c34f71f | 2019-12-06 12:44:47 -0800 | [diff] [blame] | 241 | |
| 242 | Example: |
| 243 | |
| 244 | .. code-block:: |
| 245 | |
| 246 | pw_unit_test/... |
| 247 | main.cc |
| 248 | framework.cc |
| 249 | test.gni |
| 250 | BUILD.gn |
| 251 | README.md |
| 252 | |
| 253 | Documentation |
| 254 | ~~~~~~~~~~~~~ |
| 255 | Documentation should go in the root module folder, typically in the |
| 256 | ``docs.rst`` file. There must be a docgen entry for the documentation in the |
| 257 | ``BUILD.gn`` file with the target name ``docs``; so the full target for the |
| 258 | docs would be ``<module>:docs``. |
| 259 | |
| 260 | .. code-block:: |
| 261 | |
| 262 | pw_example_module/... |
| 263 | |
| 264 | docs.rst |
| 265 | |
| 266 | For modules with more involved documentation, create a separate directory |
| 267 | called ``docs/`` under the module root, and put the ``.rst`` files and other |
| 268 | related files (like images and diagrams) there. |
| 269 | |
| 270 | .. code-block:: |
| 271 | |
| 272 | pw_example_module/... |
| 273 | |
| 274 | docs/docs.rst |
| 275 | docs/bar.rst |
| 276 | docs/foo.rst |
| 277 | docs/image/screenshot.png |
| 278 | docs/image/diagram.svg |
| 279 | |
| 280 | Command: ``pw module-check`` |
| 281 | ---------------------------- |
| 282 | |
| 283 | The ``pw module-check`` command exists to ensure that your module conforms to |
| 284 | the Pigweed module norms. |
| 285 | |
| 286 | For example, at time of writing ``pw module-check pw_module`` is not passing |
| 287 | its own lint: |
| 288 | |
| 289 | .. code-block:: none |
| 290 | |
| 291 | $ pw module-check pw_module |
| 292 | |
| 293 | ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ |
| 294 | ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ |
| 295 | ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ |
| 296 | ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ |
| 297 | ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ |
| 298 | |
| 299 | 20191205 17:05:19 INF Checking module: pw_module |
| 300 | 20191205 17:05:19 ERR PWCK005: Missing ReST documentation; need at least e.g. "docs.rst" |
| 301 | 20191205 17:05:19 ERR FAIL: Found errors when checking module pw_module |
| 302 | |
| 303 | |