aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-11-23 00:09:38 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-11-23 00:09:38 +0000
commitaff67b6bbc28eba4d1f4a4a12e116e63ab4813ea (patch)
treeb7892405559e1714c330ffb8ac9de9104296f4e3
parent13960e09d00c4e8612da0a9084e0e051603f072b (diff)
parentf054515492af5132f685cb23fe11891ee77104c9 (diff)
downloadpigweed-android14-qpr2-s3-release.tar.gz
Change-Id: I018123259cd231d0e74933e492391999e36650e8
-rw-r--r--.bazelrc34
-rw-r--r--.eslintrc.cjs1
-rw-r--r--.gitignore4
-rw-r--r--.vscode/pw_project_extensions.json8
-rw-r--r--BUILD.gn36
-rw-r--r--PIGWEED_MODULES3
-rw-r--r--WORKSPACE44
-rw-r--r--docs/BUILD.gn37
-rw-r--r--docs/_static/css/pigweed.css11
-rw-r--r--docs/_static/js/pigweed.js40
-rw-r--r--docs/changelog.rst659
-rw-r--r--docs/code_reviews.rst11
-rw-r--r--docs/concepts/index.rst146
-rw-r--r--docs/contributing/index.rst (renamed from docs/contributing.rst)93
-rw-r--r--docs/contributing/module_docs.rst304
-rw-r--r--docs/facades.rst153
-rw-r--r--docs/get_started/bazel.rst217
-rw-r--r--docs/get_started/index.rst27
-rw-r--r--docs/get_started/upstream.rst10
-rw-r--r--docs/glossary.rst13
-rw-r--r--docs/images/pw_env_setup_demo.gifbin477189 -> 0 bytes
-rw-r--r--docs/images/pw_status_test.pngbin80866 -> 0 bytes
-rw-r--r--docs/images/pw_watch_build_demo.gifbin294644 -> 0 bytes
-rw-r--r--docs/images/pw_watch_on_device_demo.gifbin344863 -> 0 bytes
-rw-r--r--docs/images/pw_watch_test_demo.gifbin262146 -> 0 bytes
-rw-r--r--docs/images/pw_watch_test_demo2.gifbin864841 -> 0 bytes
-rw-r--r--docs/images/stm32f429i-disc1_connected.jpgbin118263 -> 0 bytes
-rw-r--r--docs/index.rst32
-rw-r--r--docs/infra/ci_cq_intro.rst22
-rw-r--r--docs/mission.rst55
-rw-r--r--docs/overview.rst138
-rw-r--r--docs/python_build.rst2
-rw-r--r--docs/style/commit_message.rst296
-rw-r--r--docs/style/cpp.rst826
-rw-r--r--docs/style/doxygen.rst253
-rw-r--r--docs/style/sphinx.rst632
-rw-r--r--docs/style_guide.rst1928
-rw-r--r--docs/targets.rst4
-rw-r--r--docs/templates/docs/api.rst13
-rw-r--r--docs/templates/docs/cli.rst11
-rw-r--r--docs/templates/docs/concepts.rst9
-rw-r--r--docs/templates/docs/design.rst14
-rw-r--r--docs/templates/docs/docs.rst117
-rw-r--r--docs/templates/docs/gui.rst9
-rw-r--r--docs/templates/docs/guides.rst15
-rw-r--r--docs/templates/docs/tutorials/index.rst13
-rw-r--r--pw_allocator/BUILD.bazel73
-rw-r--r--pw_allocator/BUILD.gn77
-rw-r--r--pw_allocator/CMakeLists.txt70
-rw-r--r--pw_allocator/allocator.cc15
-rw-r--r--pw_allocator/allocator_metric_proxy.cc33
-rw-r--r--pw_allocator/allocator_metric_proxy_test.cc20
-rw-r--r--pw_allocator/allocator_test.cc20
-rw-r--r--pw_allocator/allocator_testing.cc103
-rw-r--r--pw_allocator/block.cc259
-rw-r--r--pw_allocator/block_test.cc1195
-rw-r--r--pw_allocator/docs.rst44
-rw-r--r--pw_allocator/fallback_allocator.cc35
-rw-r--r--pw_allocator/fallback_allocator_test.cc148
-rw-r--r--pw_allocator/freelist_heap.cc31
-rw-r--r--pw_allocator/freelist_heap_test.cc37
-rw-r--r--pw_allocator/libc_allocator.cc19
-rw-r--r--pw_allocator/libc_allocator_test.cc5
-rw-r--r--pw_allocator/null_allocator_test.cc2
-rw-r--r--pw_allocator/public/pw_allocator/allocator.h285
-rw-r--r--pw_allocator/public/pw_allocator/allocator_metric_proxy.h11
-rw-r--r--pw_allocator/public/pw_allocator/allocator_testing.h131
-rw-r--r--pw_allocator/public/pw_allocator/block.h935
-rw-r--r--pw_allocator/public/pw_allocator/fallback_allocator.h11
-rw-r--r--pw_allocator/public/pw_allocator/freelist_heap.h4
-rw-r--r--pw_allocator/public/pw_allocator/libc_allocator.h14
-rw-r--r--pw_allocator/public/pw_allocator/null_allocator.h6
-rw-r--r--pw_allocator/public/pw_allocator/simple_allocator.h87
-rw-r--r--pw_allocator/public/pw_allocator/split_free_list_allocator.h261
-rw-r--r--pw_allocator/pw_allocator_private/allocator_testing.h75
-rw-r--r--pw_allocator/simple_allocator_test.cc66
-rw-r--r--pw_allocator/size_report/BUILD.bazel54
-rw-r--r--pw_allocator/size_report/BUILD.gn46
-rw-r--r--pw_allocator/size_report/split_free_list_allocator.cc131
-rw-r--r--pw_allocator/split_free_list_allocator.cc279
-rw-r--r--pw_allocator/split_free_list_allocator_test.cc446
-rw-r--r--pw_allocator/unique_ptr_test.cc149
-rw-r--r--pw_analog/BUILD.bazel2
-rw-r--r--pw_arduino_build/py/pw_arduino_build/__main__.py14
-rwxr-xr-xpw_arduino_build/py/pw_arduino_build/builder.py26
-rw-r--r--pw_arduino_build/py/pw_arduino_build/core_installer.py15
-rw-r--r--pw_arduino_build/py/setup.cfg1
-rw-r--r--pw_assert/assert_backend_compile_test_c.c1
-rw-r--r--pw_assert/docs.rst2
-rw-r--r--pw_assert/print_and_abort_check_public_overrides/pw_assert_backend/check_backend.h12
-rw-r--r--pw_assert/public/pw_assert/internal/print_and_abort.h27
-rw-r--r--pw_bluetooth/public/pw_bluetooth/hci_commands.emb208
-rw-r--r--pw_bluetooth/public/pw_bluetooth/hci_common.emb21
-rw-r--r--pw_bluetooth/public/pw_bluetooth/hci_events.emb59
-rw-r--r--pw_build/BUILD.bazel14
-rw-r--r--pw_build/BUILD.gn15
-rw-r--r--pw_build/CMakeLists.txt1
-rw-r--r--pw_build/bazel.rst23
-rw-r--r--pw_build/bazel_internal/BUILD.bazel3
-rw-r--r--pw_build/bazel_internal/pigweed_internal.bzl54
-rw-r--r--pw_build/defaults.gni2
-rw-r--r--pw_build/generated_pigweed_modules_lists.gni12
-rw-r--r--pw_build/gn_internal/build_target.gni5
-rw-r--r--pw_build/pigweed.bzl32
-rw-r--r--pw_build/py/generate_cc_blob_library_test.py4
-rw-r--r--pw_build/py/pw_build/generate_cc_blob_library.py5
-rwxr-xr-xpw_build/py/pw_build/python_runner.py4
-rw-r--r--pw_build/py/pw_build/wrap_ninja.py2
-rw-r--r--pw_build/py/setup.cfg1
-rw-r--r--pw_build/python.gni5
-rw-r--r--pw_bytes/BUILD.bazel9
-rw-r--r--pw_bytes/BUILD.gn7
-rw-r--r--pw_bytes/CMakeLists.txt11
-rw-r--r--pw_bytes/docs.rst7
-rw-r--r--pw_bytes/public/pw_bytes/suffix.h33
-rw-r--r--pw_bytes/suffix_test.cc28
-rw-r--r--pw_cli/py/pw_cli/color.py7
-rw-r--r--pw_cli/py/pw_cli/envparse.py5
-rw-r--r--pw_console/docs.rst26
-rw-r--r--pw_console/embedding.rst12
-rw-r--r--pw_console/internals.rst3
-rw-r--r--pw_console/plugins.rst4
-rw-r--r--pw_console/py/BUILD.bazel11
-rw-r--r--pw_console/py/BUILD.gn1
-rw-r--r--pw_console/py/pw_console/docs/user_guide.rst3
-rw-r--r--pw_console/py/pw_console/socket_client.py139
-rw-r--r--pw_console/py/socket_client_test.py181
-rw-r--r--pw_console/testing.rst3
-rw-r--r--pw_containers/docs.rst1
-rw-r--r--pw_containers/public/pw_containers/variable_length_entry_queue.h101
-rw-r--r--pw_containers/pw_containers_private/variable_length_entry_queue_test_oracle.h28
-rw-r--r--pw_containers/py/pw_containers/variable_length_entry_queue.py16
-rw-r--r--pw_containers/variable_length_entry_queue.c39
-rw-r--r--pw_containers/variable_length_entry_queue_test.cc162
-rw-r--r--pw_digital_io_mcuxpresso/digital_io.cc7
-rw-r--r--pw_docgen/docs.rst2
-rw-r--r--pw_docgen/py/pw_docgen/sphinx/pigweed_live.py2
-rwxr-xr-xpw_doctor/py/pw_doctor/doctor.py5
-rw-r--r--pw_emu/config.rst13
-rw-r--r--pw_emu/py/mock_emu.py2
-rw-r--r--pw_emu/py/pw_emu/__main__.py14
-rw-r--r--pw_emu/py/pw_emu/core.py48
-rw-r--r--pw_emu/py/pw_emu/renode.py11
-rw-r--r--pw_emu/py/tests/cli_test.py24
-rw-r--r--pw_emu/py/tests/core_test.py85
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/bazel.json5
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/go.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/host_tools.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/luci.json12
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json8
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/python310.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/python311.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/python38.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/cipd_setup/python39.json2
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list49
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_darwin.list342
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_linux.list342
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_windows.list342
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/python_base_requirements.txt4
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_darwin_lock.txt353
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_linux_lock.txt353
-rw-r--r--pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_windows_lock.txt353
-rw-r--r--pw_format/BUILD.gn34
-rw-r--r--pw_format/docs.rst22
-rw-r--r--pw_format/rust/BUILD.bazel112
-rw-r--r--pw_format/rust/pw_format/lib.rs (renamed from pw_tokenizer/rust/pw_tokenizer_printf/lib.rs)38
-rw-r--r--pw_format/rust/pw_format/macros.rs487
-rw-r--r--pw_format/rust/pw_format/tests.rs (renamed from pw_tokenizer/rust/pw_tokenizer_printf/tests.rs)12
-rw-r--r--pw_format/rust/pw_format_example_macro.rs142
-rw-r--r--pw_format/rust/pw_format_example_macro_test.rs24
-rw-r--r--pw_format/rust/pw_format_test_macros.rs227
-rw-r--r--pw_format/rust/pw_format_test_macros_test.rs168
-rw-r--r--pw_fuzzer/BUILD.gn2
-rw-r--r--pw_fuzzer/docs.rst2
-rw-r--r--pw_fuzzer/examples/libfuzzer/BUILD.bazel1
-rw-r--r--pw_fuzzer/fuzzer.bzl5
-rw-r--r--pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h59
-rw-r--r--pw_fuzzer/public/pw_fuzzer/fuzztest.h5
-rw-r--r--pw_hdlc/api.rst8
-rw-r--r--pw_hdlc/py/pw_hdlc/rpc.py56
-rw-r--r--pw_i2c/BUILD.bazel1
-rw-r--r--pw_ide/py/pw_ide/cli.py8
-rw-r--r--pw_ide/py/pw_ide/commands.py23
-rw-r--r--pw_ide/py/pw_ide/vscode.py4
-rw-r--r--pw_ide/ts/pigweed-vscode/CHANGELOG.md12
-rw-r--r--pw_ide/ts/pigweed-vscode/README.md8
-rw-r--r--pw_ide/ts/pigweed-vscode/package-lock.json4452
-rw-r--r--pw_ide/ts/pigweed-vscode/package.json29
-rw-r--r--pw_ide/ts/pigweed-vscode/pigweed-ide-0.0.1.vsixbin590364 -> 0 bytes
-rw-r--r--pw_ide/ts/pigweed-vscode/src/extension.ts5
-rw-r--r--pw_ide/ts/pigweed-vscode/tsconfig.json4
-rw-r--r--pw_ide/ts/pigweed-vscode/webpack.config.js72
-rw-r--r--pw_libc/BUILD.gn12
-rw-r--r--pw_libcxx/BUILD.bazel (renamed from pw_trace_tokenized/py/pyproject.toml)19
-rw-r--r--pw_libcxx/BUILD.gn33
-rw-r--r--pw_libcxx/__cxa_deleted_virtual.cc15
-rw-r--r--pw_libcxx/__cxa_pure_virtual.cc15
-rw-r--r--pw_libcxx/docs.rst9
-rw-r--r--pw_libcxx/operator_delete.cc37
-rw-r--r--pw_log/Android.bp42
-rw-r--r--pw_log/BUILD.bazel5
-rw-r--r--pw_log/docs.rst14
-rw-r--r--pw_log_basic/BUILD.bazel6
-rw-r--r--pw_log_string/BUILD.bazel5
-rw-r--r--pw_log_string/docs.rst2
-rw-r--r--pw_log_tokenized/BUILD.bazel17
-rw-r--r--pw_minimal_cpp_stdlib/BUILD.gn2
-rw-r--r--pw_minimal_cpp_stdlib/CMakeLists.txt4
-rw-r--r--pw_multibuf/BUILD.bazel76
-rw-r--r--pw_multibuf/BUILD.gn82
-rw-r--r--pw_multibuf/CMakeLists.txt75
-rw-r--r--pw_multibuf/chunk.cc247
-rw-r--r--pw_multibuf/chunk_test.cc430
-rw-r--r--pw_multibuf/docs.rst87
-rw-r--r--pw_multibuf/multibuf.cc129
-rw-r--r--pw_multibuf/multibuf_test.cc214
-rw-r--r--pw_multibuf/public/pw_multibuf/chunk.h372
-rw-r--r--pw_multibuf/public/pw_multibuf/internal/test_utils.h162
-rw-r--r--pw_multibuf/public/pw_multibuf/multibuf.h373
-rw-r--r--pw_package/py/pw_package/packages/stm32cube.py5
-rw-r--r--pw_package/py/pw_package/packages/zephyr.py4
-rw-r--r--pw_perf_test/BUILD.bazel157
-rw-r--r--pw_perf_test/BUILD.gn160
-rw-r--r--pw_perf_test/CMakeLists.txt118
-rw-r--r--pw_perf_test/examples/example_perf_test.cc (renamed from pw_perf_test/performance_test_generic.cc)0
-rw-r--r--pw_perf_test/framework.cc53
-rw-r--r--pw_perf_test/perf_test.cc80
-rw-r--r--pw_perf_test/perf_test_test.cc43
-rw-r--r--pw_perf_test/public/pw_perf_test/internal/framework.h57
-rw-r--r--pw_perf_test/public/pw_perf_test/internal/test_info.h50
-rw-r--r--pw_perf_test/public/pw_perf_test/perf_test.h141
-rw-r--r--pw_perf_test/public/pw_perf_test/state.h87
-rw-r--r--pw_perf_test/state.cc66
-rw-r--r--pw_perf_test/state_test.cc3
-rw-r--r--pw_perf_test/test_info.cc28
-rw-r--r--pw_polyfill/BUILD.bazel39
-rw-r--r--pw_polyfill/BUILD.gn57
-rw-r--r--pw_polyfill/CMakeLists.txt2
-rw-r--r--pw_polyfill/README.md2
-rw-r--r--pw_polyfill/docs.rst29
-rw-r--r--pw_polyfill/standard_library_public/pw_polyfill/standard_library/cstddef.h75
-rw-r--r--pw_polyfill/standard_library_public/pw_polyfill/standard_library/iterator.h55
-rw-r--r--pw_polyfill/test.cc36
-rw-r--r--pw_presubmit/docs.rst9
-rw-r--r--pw_presubmit/py/pw_presubmit/build.py161
-rwxr-xr-xpw_presubmit/py/pw_presubmit/format_code.py9
-rw-r--r--pw_presubmit/py/pw_presubmit/git_repo.py10
-rw-r--r--pw_presubmit/py/pw_presubmit/keep_sorted.py2
-rwxr-xr-xpw_presubmit/py/pw_presubmit/pigweed_presubmit.py23
-rw-r--r--pw_protobuf/Android.bp7
-rw-r--r--pw_protobuf/py/setup.cfg8
-rw-r--r--pw_protobuf_compiler/proto.gni22
-rw-r--r--pw_protobuf_compiler/pw_proto_library.bzl2
-rw-r--r--pw_protobuf_compiler/py/setup.cfg11
-rw-r--r--pw_rpc/java/main/dev/pigweed/pw_rpc/Service.java8
-rw-r--r--pw_rpc/public/pw_rpc/synchronous_call.h4
-rw-r--r--pw_rpc/pwpb/docs.rst3
-rw-r--r--pw_rust/BUILD.bazel2
-rw-r--r--pw_snapshot/py/metadata_test.py16
-rw-r--r--pw_snapshot/py/pw_snapshot_metadata/metadata.py2
-rw-r--r--pw_software_update/docs.rst2
-rw-r--r--pw_span/CMakeLists.txt2
-rw-r--r--pw_span/docs.rst3
-rw-r--r--pw_span/public/pw_span/span.h2
-rw-r--r--pw_stm32cube_build/docs.rst194
-rw-r--r--pw_stream/socket_stream.cc1
-rw-r--r--pw_string/BUILD.gn1
-rw-r--r--pw_string/api.rst5
-rw-r--r--pw_string/code_size.rst40
-rw-r--r--pw_string/design.rst27
-rw-r--r--pw_string/docs.rst65
-rw-r--r--pw_string/guide.rst158
-rw-r--r--pw_sys_io_ambiq_sdk/BUILD.bazel1
-rw-r--r--pw_sys_io_arduino/BUILD.bazel3
-rw-r--r--pw_sys_io_baremetal_lm3s6965evb/BUILD.bazel1
-rw-r--r--pw_sys_io_emcraft_sf2/BUILD.bazel3
-rw-r--r--pw_sys_io_rp2040/BUILD.bazel3
-rw-r--r--pw_sys_io_stm32cube/BUILD.bazel6
-rw-r--r--pw_system/BUILD.bazel126
-rw-r--r--pw_system/BUILD.gn62
-rw-r--r--pw_system/CMakeLists.txt75
-rw-r--r--pw_system/example_user_app_init.cc4
-rw-r--r--pw_system/file_manager.cc47
-rw-r--r--pw_system/file_service.cc32
-rw-r--r--pw_system/freertos_backends.gni1
-rw-r--r--pw_system/freertos_target_hooks.cc18
-rw-r--r--pw_system/hdlc_rpc_server.cc2
-rw-r--r--pw_system/init.cc33
-rw-r--r--pw_system/public/pw_system/config.h15
-rw-r--r--pw_system/public/pw_system/file_manager.h61
-rw-r--r--pw_system/public/pw_system/file_service.h (renamed from pw_polyfill/cstddef_public_overrides/cstddef)12
-rw-r--r--pw_system/public/pw_system/target_hooks.h2
-rw-r--r--pw_system/public/pw_system/trace_service.h (renamed from pw_polyfill/iterator_public_overrides/iterator)15
-rw-r--r--pw_system/public/pw_system/transfer_handlers.h37
-rw-r--r--pw_system/public/pw_system/transfer_service.h27
-rw-r--r--pw_system/py/BUILD.bazel1
-rw-r--r--pw_system/py/BUILD.gn5
-rw-r--r--pw_system/py/pw_system/console.py24
-rw-r--r--pw_system/py/pw_system/device.py22
-rw-r--r--pw_system/py/pw_system/device_tracing.py162
-rw-r--r--pw_system/py/pw_system/trace_client.py211
-rw-r--r--pw_system/stl_backends.gni1
-rw-r--r--pw_system/stl_target_hooks.cc8
-rw-r--r--pw_system/system_target.gni4
-rw-r--r--pw_system/trace_service.cc40
-rw-r--r--pw_system/transfer_handlers.cc39
-rw-r--r--pw_system/transfer_service.cc68
-rw-r--r--pw_thread/py/thread_analyzer_test.py87
-rw-r--r--pw_tls_client/test_server_test.cc3
-rw-r--r--pw_tokenizer/Android.bp9
-rw-r--r--pw_tokenizer/docs.rst4
-rw-r--r--pw_tokenizer/get_started.rst2
-rw-r--r--pw_tokenizer/public/pw_tokenizer/config.h10
-rw-r--r--pw_tokenizer/public/pw_tokenizer/nested_tokenization.h9
-rw-r--r--pw_tokenizer/rust/BUILD.bazel30
-rw-r--r--pw_tokenizer/rust/pw_tokenizer/lib.rs20
-rw-r--r--pw_tokenizer/rust/pw_tokenizer_macro.rs265
-rw-r--r--pw_tokenizer/tokenize.cc3
-rw-r--r--pw_toolchain/BUILD.bazel1
-rw-r--r--pw_toolchain/BUILD.gn4
-rw-r--r--pw_toolchain/arm_clang/toolchains.gni73
-rw-r--r--pw_toolchain/arm_gcc/BUILD.bazel21
-rw-r--r--pw_toolchain/arm_gcc/BUILD.gn14
-rw-r--r--pw_toolchain/arm_gcc/toolchains.gni111
-rw-r--r--pw_toolchain/docs.rst7
-rw-r--r--pw_toolchain/host_clang/BUILD.bazel100
-rw-r--r--pw_toolchain/host_clang/toolchain.cmake2
-rw-r--r--pw_toolchain/host_clang/toolchains.gni15
-rw-r--r--pw_toolchain/host_gcc/toolchain.cmake2
-rw-r--r--pw_toolchain/host_gcc/toolchains.gni11
-rw-r--r--pw_toolchain/traits.gni4
-rw-r--r--pw_toolchain_bazel/BUILD.gn5
-rw-r--r--pw_toolchain_bazel/api.rst298
-rw-r--r--pw_toolchain_bazel/cc_toolchain/defs.bzl25
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl74
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl225
-rw-r--r--pw_toolchain_bazel/cc_toolchain/private/utils.bzl24
-rw-r--r--pw_toolchain_bazel/docs.rst51
-rw-r--r--pw_toolchain_bazel/features/BUILD.bazel16
-rw-r--r--pw_trace_tokenized/BUILD.bazel79
-rw-r--r--pw_trace_tokenized/BUILD.gn49
-rw-r--r--pw_trace_tokenized/CMakeLists.txt26
-rw-r--r--pw_trace_tokenized/base_trace_service.cc76
-rw-r--r--pw_trace_tokenized/public/pw_trace_tokenized/base_trace_service.h43
-rw-r--r--pw_trace_tokenized/public/pw_trace_tokenized/trace_service_pwpb.h38
-rw-r--r--pw_trace_tokenized/pw_trace_protos/trace_rpc.proto4
-rw-r--r--pw_trace_tokenized/pw_trace_protos/trace_service.options13
-rw-r--r--pw_trace_tokenized/pw_trace_protos/trace_service.proto53
-rw-r--r--pw_trace_tokenized/py/BUILD.gn11
-rw-r--r--pw_trace_tokenized/trace_service_pwpb.cc75
-rw-r--r--pw_trace_tokenized/trace_service_pwpb_test.cc130
-rw-r--r--pw_transfer/client_test.cc29
-rw-r--r--pw_transfer/integration_test/cross_language_large_read_test.py2
-rw-r--r--pw_transfer/integration_test/cross_language_large_write_test.py2
-rw-r--r--pw_transfer/integration_test/cross_language_medium_read_test.py2
-rw-r--r--pw_transfer/integration_test/cross_language_medium_write_test.py2
-rw-r--r--pw_transfer/integration_test/cross_language_small_test.py2
-rw-r--r--pw_transfer/integration_test/expected_errors_test.py2
-rw-r--r--pw_transfer/integration_test/legacy_binaries_test.py2
-rw-r--r--pw_transfer/integration_test/multi_transfer_test.py2
-rw-r--r--pw_transfer/public/pw_transfer/internal/config.h6
-rw-r--r--pw_transfer/public/pw_transfer/internal/context.h2
-rw-r--r--pw_transfer/transfer_test.cc15
-rw-r--r--pw_unit_test/BUILD.bazel21
-rw-r--r--pw_unit_test/BUILD.gn19
-rw-r--r--pw_unit_test/CMakeLists.txt21
-rw-r--r--pw_unit_test/docs.rst73
-rw-r--r--pw_unit_test/framework_test.cc40
-rw-r--r--pw_unit_test/googletest_test_matchers_test.cc193
-rw-r--r--pw_unit_test/public/pw_unit_test/googletest_test_matchers.h256
-rw-r--r--pw_unit_test/public/pw_unit_test/internal/framework.h179
-rw-r--r--pw_unit_test/py/pw_unit_test/test_runner.py13
-rw-r--r--pw_varint/docs.rst2
-rw-r--r--pw_watch/BUILD.gn4
l---------pw_watch/doc_resources/pw_watch_on_device_demo.gif1
l---------pw_watch/doc_resources/pw_watch_test_demo2.gif1
-rw-r--r--pw_watch/docs.rst4
-rw-r--r--pw_watch/guide.rst4
-rwxr-xr-xpw_watch/py/pw_watch/watch.py30
-rw-r--r--pw_web/BUILD.gn5
-rw-r--r--pw_web/docs.rst15
-rw-r--r--pw_web/log-viewer/src/components/log-list/log-list.styles.ts21
-rw-r--r--pw_web/log-viewer/src/components/log-list/log-list.ts77
-rw-r--r--pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts4
-rw-r--r--pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts5
-rw-r--r--pw_web/log-viewer/src/components/log-view/log-view.ts62
-rw-r--r--pw_web/log-viewer/src/components/log-viewer.styles.ts4
-rw-r--r--pw_web/log-viewer/src/components/log-viewer.ts7
-rw-r--r--pw_web/log-viewer/src/index.css8
-rw-r--r--pw_web/testing.rst57
-rw-r--r--seed/0000-index.rst8
-rw-r--r--seed/0102-module-docs.rst14
-rw-r--r--seed/0107-communications.rst18
-rw-r--r--seed/0110-memory-allocation-interfaces.rst464
-rw-r--r--seed/0112-async-poll.rst615
-rw-r--r--seed/0113-bazel-cc-toolchain-api.rst724
-rw-r--r--seed/BUILD.gn15
-rw-r--r--targets/BUILD.bazel3
-rw-r--r--targets/arduino/target_toolchains.gni9
-rw-r--r--targets/docs/BUILD.gn4
-rw-r--r--targets/host/target_docs.rst8
-rw-r--r--targets/host/target_toolchains.gni26
-rw-r--r--targets/stm32f429i_disc1_stm32cube/BUILD.bazel47
-rw-r--r--targets/stm32f429i_disc1_stm32cube/BUILD.gn67
-rw-r--r--targets/stm32f429i_disc1_stm32cube/boot.cc1
-rw-r--r--targets/stm32f429i_disc1_stm32cube/config/stm32f4xx_hal_conf.h2
-rw-r--r--third_party/boringssl/BUILD.gn3
-rw-r--r--third_party/llvm_builtins/BUILD.gn260
-rw-r--r--third_party/llvm_libc/llvm_libc.gni2
-rw-r--r--third_party/nanopb/BUILD.gn50
-rw-r--r--third_party/nanopb/CMakeLists.txt27
-rw-r--r--third_party/nanopb/generate_nanopb_proto.py41
-rw-r--r--third_party/nanopb/nanopb.gni9
-rw-r--r--third_party/stm32cube/BUILD.bazel14
-rw-r--r--third_party/stm32cube/BUILD.gn15
-rw-r--r--third_party/stm32cube/cmsis_core.BUILD.bazel (renamed from pw_trace_tokenized/py/setup.cfg)31
-rw-r--r--third_party/stm32cube/cmsis_device.BUILD.bazel46
-rw-r--r--third_party/stm32cube/stm32_hal_driver.BUILD.bazel117
418 files changed, 25486 insertions, 7505 deletions
diff --git a/.bazelrc b/.bazelrc
index 76c01867b..b8d5c6278 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -12,6 +12,22 @@
# License for the specific language governing permissions and limitations under
# the License.
+# Silence all C/C++ warnings in external code.
+#
+# Note that this will not silence warnings from external headers #include'd in
+# first-party code.
+common --per_file_copt=external/.*@-w
+common --host_per_file_copt=external/.*@-w
+
+# Don't automatically create __init__.py files.
+#
+# This prevents spurious package name collisions at import time, and should be
+# the default (https://github.com/bazelbuild/bazel/issues/7386). It's
+# particularly helpful for Pigweed, because we have many potential package name
+# collisions due to a profusion of stuttering paths like
+# pw_transfer/py/pw_transfer.
+common --incompatible_default_to_explicit_init_py
+
# Required for new toolchain resolution API.
build --incompatible_enable_cc_toolchain_resolution
@@ -63,6 +79,24 @@ build:fuzztest \
build:fuzztest \
--@pigweed//targets:pw_unit_test_main=@com_google_fuzztest//fuzztest:fuzztest_gtest_main
+# We use non-default labels for the STM32Cube repositories upstream (to reserve
+# the option of building for more than one MCU family down the road), so need to
+# override the three labels below.
+common --//third_party/stm32cube:hal_driver=@stm32f4xx_hal_driver//:hal_driver
+common --@stm32f4xx_hal_driver//:cmsis_device=@cmsis_device_f4//:cmsis_device
+common --@stm32f4xx_hal_driver//:cmsis_init=@cmsis_device_f4//:default_cmsis_init
+
+# Config for the stm32f429i_disc1_stm32cube platform.
+#
+# TODO: b/301334234 - Make the platform set the flags below.
+build:stm32f429i --platforms=//targets/stm32f429i_disc1_stm32cube:platform
+build:stm32f429i --copt="-DSTM32CUBE_HEADER=\"stm32f4xx.h\""
+build:stm32f429i --copt="-DSTM32F429xx"
+build:stm32f429i --@stm32f4xx_hal_driver//:hal_config=//targets/stm32f429i_disc1_stm32cube:hal_config
+build:stm32f429i --//pw_log:backend_impl=@pigweed//pw_log_tokenized:impl
+build:stm32f429i --//targets:pw_log_backend=@pigweed//pw_log_tokenized
+build:stm32f429i --//targets:pw_log_tokenized_handler_backend=@pigweed//pw_system:log_backend
+
# Specifies desired output mode for running tests.
# Valid values are
# 'summary' to output only test status summary
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 92bb15f21..9ef87ba66 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -31,6 +31,7 @@ module.exports = {
"bazel-pigweed",
"bazel-testlogs",
"node-modules",
+ "pw_ide/ts/pigweed-vscode/webpack.config.js",
"pw_web/log-viewer/src/assets/**",
"pw_web/log-viewer/src/legacy/**/*",
"pw_web/log-viewer/src/models/**",
diff --git a/.gitignore b/.gitignore
index 00e8ebbee..e8b70a59f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,7 @@ yarn-error.log
# Emboss
*.emb.h
+
+# Console Logs
+pw_console-logs.txt
+pw_console-device-logs.txt
diff --git a/.vscode/pw_project_extensions.json b/.vscode/pw_project_extensions.json
new file mode 100644
index 000000000..278d8c519
--- /dev/null
+++ b/.vscode/pw_project_extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "swyddfa.esbonio"
+ ],
+ "unwantedRecommendations": [
+ "trond-snekvik.simple-rst"
+ ]
+}
diff --git a/BUILD.gn b/BUILD.gn
index a96f77d1f..6a9a23145 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -123,7 +123,6 @@ group("default") {
# teensy
group("extended_default") {
deps = [
- ":cpp14_compatibility",
":cpp20_compatibility",
":default",
":host_clang_debug_dynamic_allocation",
@@ -562,14 +561,6 @@ group("pigweed_default") {
}
}
-group("cpp14_compatibility") {
- _cpp14_toolchain = "$_internal_toolchains:pw_strict_host_clang_debug_cpp14"
- deps = [
- ":cpp14_modules($_cpp14_toolchain)",
- ":cpp14_tests.run($_cpp14_toolchain)",
- ]
-}
-
# Build Pigweed with -std=c++20 to ensure compatibility. Compile with
# optimizations since the compiler tends to catch more errors with optimizations
# enabled than without.
@@ -655,7 +646,7 @@ if (current_toolchain != default_toolchain) {
pw_test_group("pw_perf_tests") {
tests = [
"$dir_pw_checksum:perf_tests",
- "$dir_pw_perf_test:perf_test_tests_test",
+ "$dir_pw_perf_test:examples",
"$dir_pw_protobuf:perf_tests",
]
output_metadata = true
@@ -672,31 +663,6 @@ if (current_toolchain != default_toolchain) {
output_metadata = true
}
- # Modules that support C++14.
- # TODO(hepler): pw_kvs is supposed to compile as C++14, but does not.
- group("cpp14_modules") {
- public_deps = [
- dir_pw_polyfill,
- dir_pw_preprocessor,
- dir_pw_tokenizer,
- dir_pw_varint,
- ]
- }
-
- # Tests that support C++14.
- pw_test_group("cpp14_tests") {
- group_deps = [
- "$dir_pw_polyfill:tests",
- "$dir_pw_span:tests",
- ]
- tests = [
- "$dir_pw_tokenizer:simple_tokenize_test",
- "$dir_pw_containers:to_array_test",
- "$dir_pw_string:string_test",
- ]
- output_metadata = true
- }
-
pw_coverage_report("coverage_report") {
filter_paths = []
ignore_filename_patterns = [
diff --git a/PIGWEED_MODULES b/PIGWEED_MODULES
index 5ad40deb3..6064f4d57 100644
--- a/PIGWEED_MODULES
+++ b/PIGWEED_MODULES
@@ -47,6 +47,7 @@ pw_doctor
pw_emu
pw_env_setup
pw_file
+pw_format
pw_function
pw_fuzzer
pw_hdlc
@@ -62,6 +63,7 @@ pw_interrupt_zephyr
pw_intrusive_ptr
pw_kvs
pw_libc
+pw_libcxx
pw_log
pw_log_android
pw_log_basic
@@ -75,6 +77,7 @@ pw_malloc_freelist
pw_metric
pw_minimal_cpp_stdlib
pw_module
+pw_multibuf
pw_multisink
pw_package
pw_perf_test
diff --git a/WORKSPACE b/WORKSPACE
index e5624b8b1..06f1c89d2 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -30,10 +30,10 @@ load(
# Used in modules: //pw_build, (Assorted modules via select statements).
http_archive(
name = "platforms",
- sha256 = "5308fc1d8865406a49427ba24a9ab53087f17f5266a7aabbfc28823f3916e1ca",
+ sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz",
- "https://github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz",
+ "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz",
+ "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz",
],
)
@@ -85,7 +85,11 @@ cipd_repository(
tag = "git_revision:d342388843734b6c5c50fb7e18cd3a76476b93aa",
)
+# Note that the order of registration matters: Bazel will use the first
+# toolchain compatible with the target platform. So, they should be listed from
+# most-restrive to least-restrictive.
register_toolchains(
+ "//pw_toolchain/host_clang:host_cc_toolchain_linux_kythe",
"//pw_toolchain/host_clang:host_cc_toolchain_linux",
"//pw_toolchain/host_clang:host_cc_toolchain_macos",
)
@@ -138,6 +142,8 @@ load("@rules_python//python:repositories.bzl", "python_register_toolchains")
# Use Python 3.10 for bazel Python rules.
python_register_toolchains(
name = "python3_10",
+ # Allows building as root in a docker container. Required by oss-fuzz.
+ ignore_root_user_error = True,
python_version = "3.10",
)
@@ -198,11 +204,11 @@ protobuf_deps()
# Setup Nanopb protoc plugin.
# Required by: Pigweed.
# Used in modules: pw_protobuf.
-git_repository(
+http_archive(
name = "com_github_nanopb_nanopb",
- commit = "ee27d70d329e1718f39eea1f425178e747263173",
- remote = "https://github.com/nanopb/nanopb.git",
- shallow_since = "1641373017 +0800",
+ sha256 = "3f78bf63722a810edb6da5ab5f0e76c7db13a961c2aad4ab49296e3095d0d830",
+ strip_prefix = "nanopb-0.4.8",
+ url = "https://github.com/nanopb/nanopb/archive/refs/tags/0.4.8.tar.gz",
)
load("@com_github_nanopb_nanopb//extra/bazel:nanopb_deps.bzl", "nanopb_deps")
@@ -387,3 +393,27 @@ http_archive(
strip_prefix = "FreeRTOS-Kernel-10.5.1",
urls = ["https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V10.5.1.tar.gz"],
)
+
+http_archive(
+ name = "stm32f4xx_hal_driver",
+ build_file = "//third_party/stm32cube:stm32_hal_driver.BUILD.bazel",
+ sha256 = "c8741e184555abcd153f7bdddc65e4b0103b51470d39ee0056ce2f8296b4e835",
+ strip_prefix = "stm32f4xx_hal_driver-1.8.0",
+ urls = ["https://github.com/STMicroelectronics/stm32f4xx_hal_driver/archive/refs/tags/v1.8.0.tar.gz"],
+)
+
+http_archive(
+ name = "cmsis_device_f4",
+ build_file = "//third_party/stm32cube:cmsis_device.BUILD.bazel",
+ sha256 = "6390baf3ea44aff09d0327a3c112c6ca44418806bfdfe1c5c2803941c391fdce",
+ strip_prefix = "cmsis_device_f4-2.6.8",
+ urls = ["https://github.com/STMicroelectronics/cmsis_device_f4/archive/refs/tags/v2.6.8.tar.gz"],
+)
+
+http_archive(
+ name = "cmsis_core",
+ build_file = "//third_party/stm32cube:cmsis_core.BUILD.bazel",
+ sha256 = "f711074a546bce04426c35e681446d69bc177435cd8f2f1395a52db64f52d100",
+ strip_prefix = "cmsis_core-5.4.0_cm4",
+ urls = ["https://github.com/STMicroelectronics/cmsis_core/archive/refs/tags/v5.4.0_cm4.tar.gz"],
+)
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 95ff3dde1..dc0968b87 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -35,13 +35,6 @@ pw_doc_group("core_docs") {
"$dir_pw_build/py/pw_build/generate_python_wheel_cache.py",
"$dir_pw_build/python.gni",
"$dir_pw_build/python_gn_args.gni",
- "images/pw_env_setup_demo.gif",
- "images/pw_status_test.png",
- "images/pw_watch_build_demo.gif",
- "images/pw_watch_on_device_demo.gif",
- "images/pw_watch_test_demo.gif",
- "images/pw_watch_test_demo2.gif",
- "images/stm32f429i-disc1_connected.jpg",
"layout/page.html",
"run_doxygen.py",
]
@@ -51,37 +44,34 @@ pw_doc_group("core_docs") {
"code_of_conduct.rst",
"code_reviews.rst",
"concepts/index.rst",
- "contributing.rst",
+ "contributing/index.rst",
+ "contributing/module_docs.rst",
"editors.rst",
"embedded_cpp_guide.rst",
+ "facades.rst",
"faq.rst",
+ "get_started/bazel.rst",
"get_started/index.rst",
"get_started/upstream.rst",
+ "glossary.rst",
"infra/ci_cq_intro.rst",
"infra/index.rst",
"infra/rollers.rst",
+ "mission.rst",
"module_structure.rst",
"os/index.rst",
"os/zephyr/index.rst",
"os/zephyr/kconfig.rst",
+ "overview.rst",
"size_optimizations.rst",
+ "style/commit_message.rst",
+ "style/cpp.rst",
+ "style/doxygen.rst",
+ "style/sphinx.rst",
"style_guide.rst",
]
}
-pw_doc_group("templates") {
- sources = [
- "templates/docs/api.rst",
- "templates/docs/cli.rst",
- "templates/docs/concepts.rst",
- "templates/docs/design.rst",
- "templates/docs/docs.rst",
- "templates/docs/gui.rst",
- "templates/docs/guides.rst",
- "templates/docs/tutorials/index.rst",
- ]
-}
-
# Documentation for upstream Pigweed targets.
group("target_docs") {
deps = [
@@ -167,6 +157,8 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_log_tokenized/public/pw_log_tokenized/base64.h",
"$dir_pw_log_tokenized/public/pw_log_tokenized/handler.h",
"$dir_pw_log_tokenized/public/pw_log_tokenized/metadata.h",
+ "$dir_pw_multibuf/public/pw_multibuf/chunk.h",
+ "$dir_pw_multibuf/public/pw_multibuf/multibuf.h",
"$dir_pw_protobuf/public/pw_protobuf/find.h",
"$dir_pw_random/public/pw_random/random.h",
"$dir_pw_random/public/pw_random/xor_shift.h",
@@ -199,6 +191,7 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_tokenizer/public/pw_tokenizer/tokenize.h",
"$dir_pw_toolchain/public/pw_toolchain/no_destructor.h",
"$dir_pw_transfer/public/pw_transfer/atomic_file_transfer_handler.h",
+ "$dir_pw_unit_test/public/pw_unit_test/internal/framework.h",
"$dir_pw_varint/public/pw_varint/stream.h",
"$dir_pw_varint/public/pw_varint/varint.h",
"$dir_pw_work_queue/public/pw_work_queue/work_queue.h",
@@ -235,7 +228,6 @@ pw_doc_gen("docs") {
"automated_analysis.rst",
"build_system.rst",
"changelog.rst",
- "glossary.rst",
"index.rst",
"module_guides.rst",
"python_build.rst",
@@ -249,7 +241,6 @@ pw_doc_gen("docs") {
":module_docs",
":static_assets",
":target_docs",
- ":templates",
":third_party_docs",
"$dir_pigweed/kudzu:docs",
"$dir_pigweed/seed:docs",
diff --git a/docs/_static/css/pigweed.css b/docs/_static/css/pigweed.css
index aec328823..a90cf4f4e 100644
--- a/docs/_static/css/pigweed.css
+++ b/docs/_static/css/pigweed.css
@@ -30,6 +30,17 @@
text-align: center;
}
+/********** Scrolling behavior ***********/
+
+/*
+ * All nodes across the site should scroll instantly. This rule is intentionally
+ * broad and agressive. Context:
+ * https://pigweed.dev/docs/style_guide.html#site-nav-scrolling
+ */
+* {
+ scroll-behavior: auto !important;
+}
+
/********** General document coloring ***********/
/* Code blocks inside block quotes end up getting italicized. */
diff --git a/docs/_static/js/pigweed.js b/docs/_static/js/pigweed.js
index 27bdfb1ed..6553ed336 100644
--- a/docs/_static/js/pigweed.js
+++ b/docs/_static/js/pigweed.js
@@ -15,37 +15,61 @@
window.pigweed = {};
// Scroll the site nav so that the current page is visible.
+// Context: https://pigweed.dev/docs/style_guide.html#site-nav-scrolling
window.pigweed.scrollSiteNav = () => {
const siteNav = document.querySelector('.sidebar-scroll');
// The node within the site nav that represents the page that the user is
// currently looking at.
const currentPage = document.querySelector('.current-page');
- // Determine which site nav node to scroll to.
+ // Determine which site nav node to scroll to. Scroll directly to top-level
+ // (L1) and second-level (L2) nodes. For L3 nodes and deeper, scroll to the
+ // node's L2 ancestor so that the user sees all the docs in the set.
let targetNode;
if (
currentPage.classList.contains('toctree-l1') ||
currentPage.classList.contains('toctree-l2')
) {
- // Scroll directly to top-level (L1) and second-level (L2) nodes.
targetNode = currentPage;
} else {
- // For L3 nodes and deeper, scroll to the node's L2 ancestor so that the
- // user sees all the docs in the set.
targetNode = document.querySelector('li.toctree-l2.current');
}
- var scrollDistance =
+ // Scroll to the node. Note that we also tried scrollIntoView() but
+ // it wasn't reliable enough.
+ const scrollDistance =
targetNode.getBoundingClientRect().top -
siteNav.getBoundingClientRect().top;
siteNav.scrollTop = scrollDistance;
};
+// If the URL has a deep link, make sure the page scrolls to that section.
+// Context: https://pigweed.dev/docs/style_guide.html#site-nav-scrolling
+window.pigweed.scrollMainContent = () => {
+ // Only run this logic if there's a deep link in the URL.
+ if (!window.location.hash) {
+ return;
+ }
+ // Make sure that there's actually a node that matches the deep link before
+ // attempting to jump to it.
+ const targetNode = document.querySelector(window.location.hash);
+ if (!targetNode) {
+ return;
+ }
+ // Scroll to the node. Note that we also tried scrollIntoView() but
+ // it wasn't reliable enough.
+ const mainContent = document.querySelector('div.main');
+ const scrollDistance =
+ targetNode.getBoundingClientRect().top -
+ mainContent.getBoundingClientRect().top;
+ mainContent.scrollTop = scrollDistance;
+};
+
window.addEventListener('DOMContentLoaded', () => {
- // (b/297384789) Start Mermaid diagram rendering as early as possible to
- // prevent a race condition between Furo's scrolling logic and the Mermaid
- // diagram rendering logic.
+ // Manually control when Mermaid diagrams render to prevent scrolling issues.
+ // Context: https://pigweed.dev/docs/style_guide.html#site-nav-scrolling
if (window.mermaid) {
// https://mermaid.js.org/config/usage.html#using-mermaid-run
window.mermaid.run();
}
window.pigweed.scrollSiteNav();
+ window.pigweed.scrollMainContent();
});
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 5e7f5fefb..26465d7b7 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -5,16 +5,645 @@
=====================
What's New In Pigweed
=====================
+
+----------------------------------------
+Discuss what's new with the Pigweed team
+----------------------------------------
+.. pigweed-live::
+
.. _docs-changelog-latest:
-----------
-Oct 6, 2023
+Nov 3, 2023
-----------
.. changelog_highlights_start
+Highlights (Oct 19, 2023 to Nov 3, 2023):
+
+* A lot more of the :cpp:class:`pw::multibuf::Chunk` API was implemented.
+* :ref:`module-pw_format` is a new module dedicated to Rust format string parsing.
+* The tokenizer prefix is now configurable via
+ ``PW_TOKENIZER_NESTED_PREFIX_STR``.
+* References to C++14 have been removed throughout the codebase. Pigweed no
+ longer supports C++14; C++17 or newer is required.
+* The upstream Pigweed GN build is now
+ :ref:`more isolated <docs-changelog-20231103-pw_build>` so that downstream
+ projects have less conflicts when importing Pigweed into their existing GN
+ build.
+* Build configuration is moving away from Bazel macros like ``pw_cc_library``
+ and towards the toolchain configuration so that downstream projects can have
+ :ref:`full control <docs-changelog-20231103-bazel>` over how Pigweed libraries
+ are built.
+* New guidelines for authoring module docs have been published at
+ :ref:`docs-contrib-moduledocs`. :ref:`module-pw_string` is now an example
+ of a "golden" module docs set that follows the new guidelines. Please leave
+ feedback on the new guidelines (and module docs updated to follow the
+ guidelines) in `issue #309123039 <https://issues.pigweed.dev/issues/309123039>`__.
+
+.. changelog_highlights_end
+
+Active SEEDs
+============
+Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
+
+* `SEED-0103: pw_protobuf Object Model <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/133971>`__
+* `SEED-0106: Project Template <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/155430>`__
+* `SEED-0110: Memory Allocation Interfaces <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168772>`__
+* `SEED-0113: Modular Bazel C/C++ Toolchain API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173453>`__
+* `SEED-0114: Channels <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175471>`__
+* `SEED-0115: Sensors <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176760>`__
+* `SEED-0116: Sockets <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177696>`__
+
+Modules
+=======
+
+pw_allocator
+------------
+The docs now have an auto-generated :ref:`module-pw_allocator-size`.
+``pw::allocator::SplitFreeListAllocator`` has a new ``blocks()`` method for getting the
+range of blocks being tracked. The class was also refactored to
+use the existing ``Block`` API. The ``Block`` API itself was refactored to
+encode offsets and flags into fields.
+
+* `Add size reporting <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178370>`__
+* `Return Range from SplitFreeListAllocator <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177807>`__
+* `Refactor SplitFreeListAllocator to use Block <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176579>`__
+* `Refactor Block to use encoded offsets <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176578>`__
+
+pw_arduino_build
+----------------
+* `STM32 Core fixes <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177750>`__
+
+pw_assert
+---------
+* `Update print_and_abort backend formatting <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177845>`__
+
+pw_bluetooth
+------------
+More :ref:`Emboss <module-pw_third_party_emboss>` definitions were added.
+
+.. todo-check: disable
+
+* `Add TODO for issue 308794058 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/151070>`__
+ (issue `#308794058 <https://issues.pigweed.dev/issues/308794058>`__)
+* `Remove anonymous entry in LEPeerAddressTypeNoAnon <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177881>`__
+* `Separate LEAddressType and LEExtendedAddressType <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178010>`__
+* `Define LEExtendedCreateConnectionV1 Emboss structure <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176574>`__
+ (issue `#305976440 <https://issues.pigweed.dev/issues/305976440>`__)
+* `Define LEEnhancedConnectionCompleteSubeventV1 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176576>`__
+ (issue `#305976440 <https://issues.pigweed.dev/issues/305976440>`__)
+* `Remove padding from Emboss command definitions <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176772>`__
+
+.. todo-check: enable
+
+.. _docs-changelog-20231103-pw_build:
+
+pw_build
+--------
+Pigweed used to inject a selection of recommended configs into every ``pw_*``
+C/C++ target in the GN build. These were previously only possible to remove
+with the ``remove_configs`` argument. These configs are now bundled with
+toolchains instead, and if you don't use a Pigweed-style toolchain you'll
+no longer need to find ways to strip the default configs from Pigweed build rules.
+More importantly, this changes makes Pigweed's recommended configs behave
+identically to other toolchain configs, and they're now more clearly part of
+GN toolchain definitions. This change is transparent to most projects, but some
+Pigweed customers have been asking for this for a while.
+
+The :ref:`module-pw_build-bazel-empty_cc_library` Bazel utility was added.
+
+* `Add empty_cc_library <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178555>`__
+* `Remove pw_build_default_configs_in_toolchain <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177894>`__
+* `Apply pigweed_default_configs in toolchain <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/120610>`__
+ (issue `#260111641 <https://issues.pigweed.dev/issues/260111641>`__)
+* `Fix blob attribute ordering <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177458>`__
+* `Only use -Wextra-semi on C++ files with GCC <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177171>`__
+ (issue `#301262374 <https://issues.pigweed.dev/issues/306734552, b/301262374>`__)
+* `Silence Windows-specific warnings <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177172>`__
+
+pw_bytes
+--------
+A new ``_b`` literal was added to make it easier to create bytes for tests
+and constants.
+
+* `Add _b suffix for byte literals <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178134>`__
+
+pw_containers
+-------------
+The reference docs for the variable length entry queue API in C and Python
+were updated.
+
+* `Update VariableLengthEntryQueue size functions; cleanup <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173454>`__
+
+pw_digital_io_mcuxpresso
+------------------------
+* `Remove RT595 size def <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178353>`__
+
+pw_doctor
+---------
+* `Trivial linter fixes <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176939>`__
+
+pw_emu
+------
+* `renode: Show more details when failing to connect <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178563>`__
+ (issue `#307736513 <https://issues.pigweed.dev/issues/307736513>`__)
+
+pw_env_setup
+------------
+``pip`` has been pinned to ``23.2.1`` and ``pip-tools`` to ``7.3.0`` to
+prevent dependency resolution problems.
+
+* `Pin pip and pip-tools <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177834>`__
+* `Update protoc to 2@24.4 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177050>`__
+ (issue `#306461552 <https://issues.pigweed.dev/issues/306461552>`__)
+
+pw_format
+---------
+:ref:`module-pw_format` is a new module dedicated to Rust format string parsing.
+
+* `Correct crate name in docs <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178078>`__
+* `Move Rust format string parsing into its own module <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168362>`__
+
+pw_fuzzer
+---------
+* `Inline NonOkStatus() <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178212>`__
+* `Fix instrumentation config <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178214>`__
+
+.. _docs-changelog-20231103-pw_hdlc:
+
+pw_hdlc
+-------
+Using read callbacks in ``RpcClient`` is no longer accepted and the use of
+``CancellableReader`` is now enforced because it provides a safe and clean
+shutdown process.
+
+* `Enforce use of CancellableReader <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173618>`__
+ (issue `#301496598 <https://issues.pigweed.dev/issues/301496598>`__)
+
+pw_libcxx
+---------
+:ref:`module-pw_libcxx` is a new module that provides ``libcxx`` symbols and
+will eventually facilitate pulling in headers as well.
+
+* `Add pw_libcxx library <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/144970>`__
+
+pw_log
+------
+A :ref:`module-pw_log-bazel-backend_impl` label flag was added to Bazel to
+avoid circular dependencies.
+
+* `Enable sandboxing for pigweed genrules <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178550>`__
+ (issue `#307824623 <https://issues.pigweed.dev/issues/307824623>`__)
+* `Introduce backend_impl label flag <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177842>`__
+ (issue `#234877642 <https://issues.pigweed.dev/issues/234877642>`__)
+
+pw_multibuf
+-----------
+A lot more of the :cpp:class:`pw::multibuf::Chunk` API was implemented.
+
+* `Add basic MultiBuf operations <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178036>`__
+* `Add Chunk::Merge <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177636>`__
+* `Fix TrackingAllocatorWithMemory UAF <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177694>`__
+* `Add module and Chunk implementation <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173951>`__
+
+pw_package
+----------
+* `Use mirror for stm32cube <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/142510>`__
+ (issue `#278914999 <https://issues.pigweed.dev/issues/278914999>`__)
+* `Fix Zephyr URL <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177456>`__
+
+pw_presubmit
+------------
+A CSS formatter was added.
+
+* `Add basic CSS formatter <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178810>`__
+ (issue `#308948504 <https://issues.pigweed.dev/issues/308948504>`__)
+* `Kalypsi-based coverage upload <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175070>`__
+ (issue `#279161371 <https://issues.pigweed.dev/issues/279161371>`__)
+* `Handle missing upstream better <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177038>`__
+ (issue `#282808936 <https://issues.pigweed.dev/issues/282808936>`__)
+* `Trivial linter fixes <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176939>`__
+
+pw_protobuf
+-----------
+* `Enable sandboxing for pigweed genrules <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178550>`__
+ (issue `#307824623 <https://issues.pigweed.dev/issues/307824623>`__)
+
+pw_rpc
+------
+:ref:`pw::rpc::SynchronousCallFor() <module-pw_rpc-client-sync-call-wrappers>`
+now supports :ref:`DynamicClient <module-pw_rpc_pw_protobuf-client>`.
+
+* `Update Java service error with tip <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178812>`__
+ (issue `#293361955 <https://issues.pigweed.dev/issues/293361955>`__)
+* `Support DynamicClient with SynchronousCallFor API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177637>`__
+
+pw_string
+---------
+The docs were updated to match the new :ref:`docs-contrib-moduledocs`.
+
+* `Docs tweaks <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177883>`__
+
+pw_sys_io
+---------
+Backends that depend on ``default_putget_bytes`` were updated to express the
+dependency.
+
+* `Fix Bazel backends <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177656>`__
+
+pw_system
+---------
+See :ref:`docs-changelog-20231103-pw_hdlc` for an explanation of the
+``CancellableReader`` change.
+
+* `Enforce use of CancellableReader <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173618>`__
+ (issue `#301496598 <https://issues.pigweed.dev/issues/301496598>`__)
+
+pw_tls_client
+-------------
+* `Update to new boringssl API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178150>`__
+
+pw_tokenizer
+------------
+The tokenizer prefix is now configurable via ``PW_TOKENIZER_NESTED_PREFIX_STR``.
+
+* `Enable sandboxing for pigweed genrules <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178550>`__
+ (issue `#307824623 <https://issues.pigweed.dev/issues/307824623>`__)
+* `Let tokenizer prefix be configurable <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177575>`__
+
+pw_toolchain
+------------
+You can now set the ``dir_pw_third_party_builtins`` GN var to your
+``compiler-rt/builtins`` checkout to enable buildings LLVM ``builtins`` from
+source instead of relying on a shipped ``libgcc``.
+
+* `Apply pigweed_default_configs in toolchain <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/120610>`__
+ (issue `#260111641 <https://issues.pigweed.dev/issues/260111641>`__)
+* `Build compiler-rt builtins to replace libgcc <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/144050>`__
+
+pw_unit_test
+------------
+* `Pass verbose flag to TestRunner <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177470>`__
+
+pw_web
+------
+* `Limit component rerendering <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177810>`__
+ (issue `#307559191 <https://issues.pigweed.dev/issues/307559191>`__)
+
+Build
+=====
+References to C++14 have been removed throughout the codebase. Pigweed no
+longer supports C++14; C++17 or newer is required.
+
+* `Drop C++14 compatibility from the build and docs <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177610>`__
+
+.. _docs-changelog-20231103-bazel:
+
+Bazel
+-----
+Build configuration is moving away from Bazel macros like ``pw_cc_library``
+and towards the toolchain configuration so that downstream projects can have
+full control over how Pigweed libraries are built.
+
+* `Move Kythe copts to toolchain configuration <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178592>`__
+ (issue `#267498492 <https://issues.pigweed.dev/issues/267498492>`__)
+* `Move warnings to toolchain configuration <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178557>`__
+ (issue `#240466562 <https://issues.pigweed.dev/issues/240466562>`__)
+* `Silence warnings from external code <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178436>`__
+ (issue `#300330623 <https://issues.pigweed.dev/issues/300330623>`__)
+* `stm32cube support <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177134>`__
+* `Remove most copts from pw_cc_library macro <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/170824>`__
+ (issue `#267498492 <https://issues.pigweed.dev/issues/267498492>`__)
+
+Targets
+=======
+``pw_assert_BACKEND`` for :ref:`target-host` was set to
+``print_and_abort_check_backend`` to enable compatibility with GoogleTest death
+tests.
+
+* (``host``) `Change pw_assert_BACKEND <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177835>`__
+
+OS support
+==========
+* (``zephyr``) `Update checkout to v3.5 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177669>`__
+
+Docs
+====
+New guidelines for authoring module docs have been published at
+:ref:`docs-contrib-moduledocs`. :ref:`module-pw_string` is now an example
+of a "golden" module docs set that follows the new guidelines. Please leave
+feedback on the new guidelines (and module docs updated to follow the
+guidelines) in `issue #309123039 <https://issues.pigweed.dev/issues/309123039>`__.
+
+There's now a definition for :ref:`docs-glossary-facade` in the glossary.
+
+* `Update module docs authoring guidelines <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177465>`__
+* `Fix nav and main content scrolling <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178591>`__
+ (issue `#303261476 <https://issues.pigweed.dev/issues/303261476>`__)
+* `Add udev instructions to Bazel Get Started <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178435>`__
+* `Add information on the experimental repo to contributing.rst <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178272>`__
+* `Mention command for updating Py dep hashes <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177799>`__
+* `Define facade in glossary <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177632>`__
+* `Remove symlinks to files that were removed <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177530>`__
+* `Mention upstream development guide in contributor guidelines <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177459>`__
+* `Move all images out of the repo <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176751>`__
+* `Update changelog <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177085>`__
+ (issue `#292247409 <https://issues.pigweed.dev/issues/292247409>`__)
+* `Move CoC to Contributors section of sitenav <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177071>`__
+
+SEEDs
+=====
+* (SEED-0107) `Update SEED references; fix typo <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177698>`__
+* (SEED-0112) `Async Poll Model <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168337>`__
+* (SEED-0115) `Fix link <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177093>`__
+* (SEED-0116) `Claim SEED number <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177697>`__
+
+Third party
+===========
+* (nanopb) `Detect protoc updates <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177650>`__
+
+------------
+Oct 20, 2023
+------------
+Highlights (Oct 5, 2023 to Oct 20, 2023):
+
+* ``pw_emu`` has launched! Check out :ref:`module-pw_emu` to get started.
+ See :ref:`seed-0108` for background.
+* :ref:`module-pw_log-tokenized-args` are now supported. See :ref:`seed-0105`
+ for background.
+* The new :cpp:class:`pw::allocator::UniquePtr` class offers a safer, simpler
+ RAII API for allocating individual values within an allocator.
+* A few SEEDs were accepted: :ref:`seed-0105`, :ref:`seed-0109`, and
+ :ref:`seed-0111`.
+* Lots of new docs, including a guide for
+ :ref:`getting started with Bazel <docs-get-started-bazel>`, a
+ conceptual explanation of :ref:`facades and backends <docs-facades>`,
+ and an eng blog post detailing :ref:`Kudzu <docs-blog-01-kudzu>`, an
+ electronic badge that the Pigweed team made for Maker Faire 2023.
+
+Active SEEDs
+============
+Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
+
+* `SEED-0103: pw_protobuf Object Model <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/133971>`__
+* `SEED-0106: Project Template <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/155430>`__
+* `SEED-0110: Memory Allocation Interfaces <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168772>`__
+* `SEED-0113: Modular Bazel C/C++ Toolchain API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173453>`__
+* `SEED-0114: Channels <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175471>`__
+* `SEED-0115: Sensors <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176760>`__
+
+Modules
+=======
+
+pw_allocator
+------------
+The new :cpp:class:`pw::allocator::UniquePtr` class offers a safer, simpler
+RAII API for allocating individual values within an allocator.
+
+* `Fix SplitFreeListAllocator region alignment <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175232>`__
+* `Add UniquePtr\<T\> <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176781>`__
+
+pw_async
+--------
+* `Add CMake support <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175475>`__
+
+pw_async_basic
+--------------
+* `Add missing include <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175476>`__
+* `Fix build error when using pw_async:heap_dispatcher <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173480>`__
+
+pw_bluetooth
+------------
+* `Define LEChannelSelectionAlgorithmSubevent <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176577>`__
+* `Define LEScanTimeoutSubevent <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176575>`__
+ (issue `#265052417 <https://issues.pigweed.dev/issues/265052417>`__)
+* `Use $size_in_bits instead of hardcoding size <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176573>`__
+* `Switch from parameterized value to determining at run time <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176572>`__
+ (issue `#305975969 <https://issues.pigweed.dev/issues/305975969>`__)
+* `Fix size reports <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173620>`__
+
+pw_build
+--------
+:ref:`module-pw_build-bazel-pw_linker_script` now describes how to work
+with linker scripts.
+
+* `Update pw_linker_script docs <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174848>`__
+* `Move pw_linker_script rule definition <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174872>`__
+
+pw_chre
+-------
+* `Remove TODOs for CHRE MacOS support <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175490>`__
+
+pw_cli
+------
+* `Honor NO_COLOR and CLICOLOR_FORCE <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176860>`__
+* `Use typing.Literal <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176778>`__
+
+pw_digital_io
+-------------
+* `Add Android.bp for proto/rpc <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176270>`__
+
+pw_emu
+------
+The module has launched! Check out :ref:`module-pw_emu` to get started.
+
+* `renode: Increase start timeout to 120s <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176865>`__
+* `Fix pid file race condition <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176782>`__
+* `mock_emu: start listening before making the port available <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176856>`__
+ (issue `#306155313 <https://issues.pigweed.dev/issues/306155313>`__)
+* `qemu: Force using IPv4 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176430>`__
+ (issue `#305810466 <https://issues.pigweed.dev/issues/305810466>`__)
+* `Add renode support <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173613>`__
+* `Add QEMU support <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173612>`__
+* `core: Let the OS terminate foreground emulator processes <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175638>`__
+* `Add user APIs and the command line interface <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173611>`__
+* `Add core components <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173610>`__
+* `Add Emulators Frontend module boilerplate <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/162096>`__
+
+pw_env_setup
+------------
+* `Allow disabling CIPD cache <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176650>`__
+* `Add prpc <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175236>`__
+
+pw_function
+-----------
+* `Move pw_function_CONFIG to .gni <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173652>`__
+
+pw_hdlc
+-------
+:ref:`module-pw_hdlc-api-rpc` now has much more information on how to use
+``pw_hdlc`` for RPC in Python.
+
+* `Update Python RPC documents <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174825>`__
+
+pw_i2c
+------
+* `Fix accidental c++2a <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176511>`__
+* `Add Android.bp for i2c proto/rpc <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176070>`__
+
+pw_kvs
+------
+The new ``FlashPartitionWithLogicalSectors`` variant of ``FlashPartition``
+supports combining multiple physical ``FlashMemory`` sectors into a single
+logical ``FlashPartition`` sector.
+
+* `Add FlashPartitionWithLogicalSectors <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/106917>`__
+
+pw_log_tokenized
+----------------
+:ref:`module-pw_log-tokenized-args` are now supported. See :ref:`seed-0105` for background.
+
+* `Add tokenized string args support to log backend <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/164514>`__
+
+pw_log_zephyr
+-------------
+* `Clean-up unused dependencies from TOKENIZED_LIB <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174813>`__
+
+pw_minimal_cpp_stdlib
+---------------------
+* `Support additional libraries <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173814>`__
+* `Add Zephyr Kconfig to enable include path <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173653>`__
+
+pw_package
+----------
+* `Update boringssl commit & skip clang-tidy <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175016>`__
+* `Update Emboss commit <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173619>`__
+
+pw_presubmit
+------------
+:ref:`module-pw_presubmit-presubmit-checks` has more guidance on when to use
+``--base`` and ``--full``.
+
+* `Add note about --full and --base <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175633>`__
+
+pw_snapshot
+-----------
+* `More detokenization tests <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176759>`__
+
+pw_spi
+------
+* `Fix cmake integration <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175190>`__
+
+pw_sync_zephyr
+--------------
+* `Add TimedThreadNotification::try_acquire_until <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175478>`__
+
+pw_system
+---------
+The ``Device`` class's constructor now accepts a ``logger`` argument
+that enables you to specify which logger should be used.
+
+* `Add option to pass logger to Device <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175075>`__
+
+pw_third_party_freertos
+-----------------------
+* `Add arm_cm7_not_r0p1 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/172382>`__
+
+pw_thread
+---------
+* `More detokenization tests <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176759>`__
+
+pw_thread_freertos
+------------------
+* `Fix extra wakeups when detaching threads <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175310>`__
+ (issue `#303885539 <https://issues.pigweed.dev/issues/303885539>`__)
+
+pw_tokenizer
+------------
+:ref:`module-pw_tokenizer-get-started-integration` has new guidance around
+configuring linker scripts in Bazel.
+
+* `Expose linker_script in BUILD.bazel <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175590>`__
+
+pw_toolchain
+------------
+* `Exclude googletest from static analysis <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173482>`__
+
+pw_transfer
+-----------
+* `Start the API reference <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/170011>`__
+ (issue `#299147635 <https://issues.pigweed.dev/issues/299147635>`__)
+
+pw_web
+------
+* `Reduce table cell padding <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176690>`__
+ (issue `#305022558 <https://issues.pigweed.dev/issues/305022558>`__)
+* `Fix invisible jump button <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175330>`__
+* `Enable manual color scheme setting <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173630>`__
+ (issue `#301498553 <https://issues.pigweed.dev/issues/301498553>`__)
+
+Build
+=====
+* `Fix pw_BUILD_BROKEN_GROUPS <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176114>`__
+* `Update Android.bp <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175631>`__
+ (issue `#277108894 <https://issues.pigweed.dev/issues/277108894>`__)
+
+Bazel
+-----
+* `Don't autodetect C++ toolchain <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175613>`__
+ (issue `#304880653 <https://issues.pigweed.dev/issues/304880653>`__)
+* `Add O2 to arm_gcc toolchain <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175536>`__
+ (issue `#299994234 <https://issues.pigweed.dev/issues/299994234>`__)
+
+Targets
+=======
+* (rp2040_pw_system) `Enable time slicing <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175074>`__
+
+OS support
+==========
+* (zephyr) `Allow direct CMake inclusions <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175477>`__
+
+Docs
+====
+* `Move CoC to Contributors section of sitenav <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177071>`__
+* `Create concepts section in sitenav <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177037>`__
+* `Add facades and backends page <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/170602>`__
+* `Add Bazel getting started tutorial <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176319>`__
+* `Remove css class on Kudzu image captions <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176770>`__
+* `Kudzu photos <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176710>`__
+* `Refactor the getting started section <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176331>`__
+* `Add sitemap <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176492>`__
+* `Add hat tip for pixel doubling technique <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175639>`__
+* `Start eng blog and add Kudzu page <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175619>`__
+* `Add Pigweed Live directive <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174892>`__
+* `Add builder viz to CI/CQ intro <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175414>`__
+ (issue `#302680656 <https://issues.pigweed.dev/issues/302680656>`__)
+* `Fix link <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175415>`__
+ (issue `#302680656 <https://issues.pigweed.dev/issues/302680656>`__)
+* `Add changelog highlight <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175231>`__
+* `Update changelog <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174818>`__
+
+SEEDs
+=====
+A few SEEDs were accepted and a few more started.
+
+* (SEED-0105) `Add nested tokens to pw_tokenizer and pw_log <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/154190>`__
+* (SEED-0109) `Communication Buffers <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168357>`__
+* (SEED-0111) `Update status, add link to SEED-0113 <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176254>`__
+* (SEED-0111) `Make Bazel Pigweed's Primary Build System <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/171695>`__
+* (SEED-0113) `Claim SEED number (Modular Bazel C/C++ Toolchain API) <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175510>`__
+* (SEED-0114) `Claim SEED number (Channels) <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175412>`__
+* (SEED-0115) `Clain SEED number (Sensors) <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176763>`__
+
+Third party
+===========
+* (boringssl) `Remove crypto_sysrand.cc <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175017>`__
+* (fuchsia) `Copybara import <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173651>`__
+* (fuchsia) `Update copybara with fit/defer.h <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173481>`__
+
+Miscellaneous
+=============
+* `Update formatting for new clang version <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175311>`__
+* `Use C++20 everywhere <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/174630>`__
+ (issue `#303371098 <https://issues.pigweed.dev/issues/303371098>`__)
+* (revert) `Use .test convention" <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/171793>`__
+* `Add generated Emboss code <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176571>`__
+
+-----------
+Oct 6, 2023
+-----------
Highlights (Sep 21, 2023 to Oct 6, 2023):
-* ``pw_allocator`` got :ref:`a bunch of new APIs <docs-changelog-20231009-pw_allocator>`!
* We expanded our RP2040 support. See the new :ref:`module-pw_chrono_rp2040`
and :ref:`module-pw_digital_io_rp2040` modules.
* The :ref:`new CancellableReader class in pw_hdlc <docs-changelog-20231009-pw_hdlc>`
@@ -26,13 +655,6 @@ Highlights (Sep 21, 2023 to Oct 6, 2023):
arguments <docs-changelog-20231009-pw_tokenizer>`.
* The ``pigweed_config`` mechanism in Bazel is now officially retired.
-Please join us at the next Pigweed Live on **Monday, Oct 9 1PM PST** to
-discuss these changes and anything else on your mind. Join our
-`Discord <https://discord.gg/M9NSeTA>`_ and head over to the ``#pigweed-live``
-channel to get a link to the video meeting.
-
-.. changelog_highlights_end
-
Active SEEDs
============
Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
@@ -348,11 +970,6 @@ Highlights (Sep 07, 2023 to Sep 22, 2023):
* The new :ref:`docs-code_reviews` document outlines the upstream Pigweed code
review process.
-Please join us at the next Pigweed Live on **Monday, Sep 25 1PM PST** to
-discuss these changes and anything else on your mind. Join our
-`Discord <https://discord.gg/M9NSeTA>`_ and head over to the ``#pigweed-live``
-channel to get a link to the video meeting.
-
Active SEEDs
============
Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
@@ -747,10 +1364,6 @@ Highlights (Aug 25, 2023 to Sep 8, 2023):
* SEED :ref:`seed-0108` has also been approved! Coming soon, the new ``pw_emu``
module will make it easier to work with emulators.
-Please join us at the next `Pigweed Live <https://discord.gg/M9NSeTA>`_ on
-**Mon, Sep 11 1PM PST** to discuss All Things Pigweed. Go to the
-``#pigweed-live`` channel to get a link to the video meeting.
-
Active SEEDs
============
Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
@@ -1042,11 +1655,6 @@ Highlights (Aug 11, 2023 to Aug 25, 2023):
* A new Ambiq Apollo4 target that uses the Ambiq Suite SDK and FreeRTOS
has been added.
-Please join us at the next Pigweed Live on **Monday, Aug 28 1PM PST** to
-discuss these changes and anything else on your mind. Join our
-`Discord <https://discord.gg/M9NSeTA>`_ and head over to the ``#pigweed-live``
-channel to get a link to the video meeting.
-
Active SEEDs
============
Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
@@ -1351,11 +1959,6 @@ Highlights (Jul 27, 2023 to Aug 11, 2023):
fixing `longstanding docs site UI issues <https://issues.pigweed.dev/issues/292273650>`_,
and more.
-Please join us at the next Pigweed Live on Monday, Aug 14 1PM PST to
-discuss these changes and anything else on your mind. Join our
-`Discord <https://discord.gg/M9NSeTA>`_ and head over to the ``#pigweed-live``
-channel to get a link to the video meeting.
-
Active SEEDs
============
Help shape the future of Pigweed! Please leave feedback on the following active RFCs (SEEDs):
diff --git a/docs/code_reviews.rst b/docs/code_reviews.rst
index 742a02b99..fcd42082c 100644
--- a/docs/code_reviews.rst
+++ b/docs/code_reviews.rst
@@ -24,6 +24,17 @@ Small changes
Please follow the guidance in `Google's Eng-Practices Small CLs
<https://google.github.io/eng-practices/review/developer/small-cls.html>`_.
+Complete changes
+================
+Please follow the guidance in :ref:`docs-contributing-contribution-standards`.
+In summary, CLs must be complete, tested, and include documentation and unit
+tests for new code, bug fixes, and any code changes that merit it. However, to
+enable iterative work and small changes, ``TODO`` comments are acceptable. They
+must include an explanation of the problem and an action to take.
+
+We will not take over incomplete changes to avoid shifting our focus. We may
+reject changes that do not meet the criteria above.
+
-------------
For reviewers
-------------
diff --git a/docs/concepts/index.rst b/docs/concepts/index.rst
index 8c0d164d7..1f9c5c2a2 100644
--- a/docs/concepts/index.rst
+++ b/docs/concepts/index.rst
@@ -1,129 +1,39 @@
.. _docs-concepts:
-=============
-About Pigweed
-=============
+========
+Concepts
+========
+.. grid:: 1
-Why Build Pigweed?
-==================
-Our goal is to make embedded software development efficient, robust, and
-heck, even delightful, for projects ranging from weekend Arduino experiements
-to commercial products selling in the millions.
+ .. grid-item-card:: :octicon:`info` Facades & Backends
+ :link: docs-facades
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
-Embedded software development is notoriously arcane. Developers often have to
-track down vendor toolchains specific to the hardware they're targeting, write
-their code against hardware-specfic SDKs/HALs, and limit themselves to a small
-subset of C. Project teams are on their own to figure out how to set up a build
-system, automated testing, serial communication, and many other embedded
-project fundamentals. This is error prone and takes effort away from developing
-the actual product!
+ A key concept in Pigweed's architecture. If you're using Pigweed
+ modules extensively in complex ways or contributing to upstream Pigweed,
+ you'll probably need to understand these ideas.
-There are solutions on the market that promise to solve all of these problems
-with a monolithic framework—just write your code against the framework and use
-hardware the framework supports, and you get an efficient embedded development
-environment. But this approach doesn't work well for existing projects that
-weren't built on the framework from the beginning or for projects that have
-specific needs the framework wasn't designed for. We know from experience that
-this approach alone doesn't meet our goal.
+.. grid:: 2
-So we have set out to build a platform that supports successful embedded
-developers at every scale by allowing them to adopt as much or as little of
-what Pigweed provides as they need, in the way that works best for their
-project.
+ .. grid-item-card:: :octicon:`info` FAQs
+ :link: docs-faq
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
-How Pigweed Works
-=================
-Pigweed provides four foundational pillars to support your embedded development:
+ Answers to general, frequently asked questions about Pigweed.
-* :ref:`A comprehensive set of libraries for embedded development<docs-concepts-embedded-development-libraries>`
-* :ref:`A hermetic and replicable development environment<docs-concepts-development-environment>`
-* :ref:`A system for building, testing, and linting your project<docs-concepts-build-system>`
-* :ref:`A full framework for new projects that want a turn-key solution<docs-concepts-full-framework>`
+ .. grid-item-card:: :octicon:`info` Glossary
+ :link: docs-glossary
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
-.. _docs-concepts-embedded-development-libraries:
+ Concise definitions of key Pigweed terms.
-Embedded Development Libraries
-------------------------------
-Pigweed enables you to use modern C++ and software development best practices in
-your embedded project without compromising performance or increasing memory use
-compared to conventional embedded C.
+.. toctree::
+ :maxdepth: 1
+ :hidden:
-We provide libraries (:ref:`modules <docs-glossary-module>`) for
-:ref:`strings<module-pw_string>`, :ref:`time<module-pw_chrono>`,
-:ref:`assertions<module-pw_assert>`, :ref:`logging<module-pw_log>`,
-:ref:`serial communication<module-pw_spi>`, :ref:`remote procedure calls (RPC)
-<module-pw_rpc>`, and :ref:`much more<docs-module-guides>`.
-
-These modules are designed to work both on your host machine and on a wide
-variety of target devices. We achieve this by writing them in an inherently
-portable way, or through the facade/backend pattern. As a result, you can write
-most or all of your code to run transparently on your host machine and targets.
-
-.. _docs-concepts-development-environment:
-
-Development Environment
------------------------
-Managing toolchains, build systems, and other software needed for a project is
-complex. Pigweed provides all of this out of the box for Linux, Mac, and
-Windows systems in a sealed environment that leaves the rest of your system
-untouched. Getting new developers started is as simple as cloning your project
-repository and activating the Pigweed environment.
-
-.. _docs-concepts-build-system:
-
-Build System
-------------
-Pigweed modules are built to integrate seamlessly into projects using GN. We
-are rapidly expanding our good support for CMake and nascent support for Bazel
-so you can use your build system of choice. For new projects, Pigweed provides a
-build system you can integrate your own code into that works out of the box.
-
-.. _docs-concepts-full-framework:
-
-Full Framework (coming in 2022)
--------------------------------
-For those who want a fully-integrated solution that provides everything Pigweed
-has to offer with an opinionated project structure, we are working diligently
-on a :ref:`Pigweed framework<module-pw_system>`. Stay tuned for more news to
-come! In the meantime, we invite you to discuss this and collaborate with us
-on `Discord <https://discord.gg/M9NSeTA>`_.
-
-.. _docs-concepts-right-for-my-project:
-
-Is Pigweed Right for My Project?
-================================
-Pigweed is still in its early stages, and while we have ambitious plans for it,
-Pigweed might not be the right fit for your project today. Here are some things
-to keep in mind:
-
-* Many individual modules are stable and are running on shipped devices today.
- If any of those modules meet your needs, you should feel safe bringing them
- into your project.
-
-* Some modules are in very early and active stages of development. They likely
- have unstable APIs and may not work on all supported targets. If this is the
- case, it will be indicated in the module's documentation. If you're interested
- in contributing to the development of one of these modules, we encourage you
- to experiment with them. Otherwise they aren't ready for use in most projects.
-
-* Setting up new projects to use Pigweed is currently not very easy, but we are
- working to address that. In the meantime, join the Pigweed community on
- `Discord <https://discord.gg/M9NSeTA>`_ to get help.
-
-Supported language versions
-===========================
-
-C++
----
-Most Pigweed code requires C++17, but a few modules, such as
-:ref:`module-pw_kvs` and :ref:`module-pw_tokenizer`, work with C++14. All
-Pigweed code is compatible with C++20. Pigweed defines GN toolchains for
-building with C++14 and C++20; see :ref:`target-host` target documentation for
-more information. For Bazel, the C++ standard version can be configured using
-the `--cxxopt flag <https://bazel.build/docs/user-manual#cxxopt>`_.
-
-.. _docs-concepts-python-version:
-
-Python
-------
-Pigweed officially supports Python 3.8, 3.9, 3.10, and 3.11.
+ ../facades
+ ../glossary
+ FAQs <../faq>
diff --git a/docs/contributing.rst b/docs/contributing/index.rst
index 749304afb..006b76c69 100644
--- a/docs/contributing.rst
+++ b/docs/contributing/index.rst
@@ -10,6 +10,10 @@ Before participating in our community, please take a moment to review our
:ref:`docs-code-of-conduct`. We expect everyone who interacts with the project
to respect these guidelines.
+Get started
+-----------
+See :ref:`docs-get-started-upstream`.
+
Pigweed contribution overview
-----------------------------
.. note::
@@ -87,6 +91,25 @@ You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
+.. _docs-contributing-contribution-standards:
+
+Contribution Standards
+----------------------
+Contributions, i.e. CLs, are expected to be complete solutions, accompanied by
+documentation and unit tests when merited, e.g. new code, bug fixes, or code
+that changes some behavior. This also means that code changes must be tested.
+Tests will avoid or minimize any negative impact to Pigweed users, while
+documentation will help others learn about the new changes.
+
+We understand that you may have different priorities or not know the best way to
+complete your contribution. In that case, reach out via our `chat room
+<https://discord.gg/M9NSeTA>`_ or on the `mailing list
+<https://groups.google.com/forum/#!forum/pigweed>`_ for help or `File a bug
+<https://issues.pigweed.dev/issues?q=status:open>`_
+requesting fixes describing how this may be blocking you. Otherwise you risk
+working on a CL that does not match our vision and could be rejected. To keep
+our focus, we cannot adopt incomplete CLs.
+
.. _gerrit-commit-hook:
Gerrit Commit Hook
@@ -112,8 +135,8 @@ of your Pigweed repository.
Commit Message
--------------
-See the :ref:`commit message section of the style guide<commit-style>` for how
-commit messages should look.
+See the :ref:`commit message section of the style
+guide<docs-pw-style-commit-message>` for how commit messages should look.
Documentation
-------------
@@ -155,6 +178,39 @@ Code Reviews
------------
See :ref:`docs-code_reviews` for information about the code review process.
+Experimental Repository and Where to Land Code
+----------------------------------------------
+Pigweed's has an `Experimental Repository
+<https://pigweed.googlesource.com/pigweed/experimental>`_ which differs from
+our main repository in a couple key ways:
+
+* Code is not expected to become production grade.
+* Code review standards are relaxed to allow experimentation.
+* In general the value of the code in the repository is the knowledge gained
+ from the experiment, not the code itself.
+
+Good uses of the repo include:
+
+* Experimenting with using an API (ex. C++20 coroutines) with no plans to
+ turn it into production code.
+* One-off test programs to gather data.
+
+We would like to avoid large pieces of code being developed in the experimental
+repository then imported into the Pigweed repository. If large amounts of code
+end up needing to migrate from experimental to main, then it must be landed
+incrementally as a series of reviewable patches, typically no
+`larger than 500 lines each
+<https://google.github.io/eng-practices/review/developer/small-cls.html>`_.
+This creates a large code review burden that often results in poorer reviews.
+Therefore, if the eventual location of the code will be the main Pigweed
+repository, it is **strongly encouraged** that the code be developed in the
+**main repository under an experimental flag**.
+
+.. note::
+ The current organization of the experimental repository does not reflect
+ this policy. This will be re-organized once we have concrete recommendations
+ on its organization.
+
Community Guidelines
--------------------
This project follows `Google's Open Source Community Guidelines
@@ -272,7 +328,7 @@ This will be effectively the same as running the following command before every
$ pw presubmit
-.. image:: ../pw_presubmit/docs/pw_presubmit_demo.gif
+.. image:: ../../pw_presubmit/docs/pw_presubmit_demo.gif
:width: 800
:alt: pw presubmit demo
@@ -341,9 +397,30 @@ Presubmit flags
.. inclusive-language: enable
-.. toctree::
- :maxdepth: 1
+.. _docs-contributing-presubmit-virtualenv-hashes:
- embedded_cpp_guide
- style_guide
- code_reviews
+Updating Python dependencies in the virtualenv_setup directory
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+If you update any of the requirements or constraints files in
+``//pw_env_setup/py/pw_env_setup/virtualenv_setup``, you must run this command
+to ensure that all of the hashes are updated:
+
+.. code-block:: console
+
+ pw presubmit --step update_upstream_python_constraints --full
+
+For Python packages that have native extensions, the command needs to be run 3
+times: once on Linux, once on macOS, and once on Windows. Please run it on the
+OSes that are available to you; a core Pigweed teammate will run it on the rest.
+See the warning about caching Python packages for multiple platforms in
+:ref:`docs-python-build-downloading-packages`.
+
+.. toctree::
+ :maxdepth: 1
+ :hidden:
+
+ ../embedded_cpp_guide
+ ../style_guide
+ ../code_reviews
+ ../code_of_conduct
+ module_docs
diff --git a/docs/contributing/module_docs.rst b/docs/contributing/module_docs.rst
new file mode 100644
index 000000000..63476696f
--- /dev/null
+++ b/docs/contributing/module_docs.rst
@@ -0,0 +1,304 @@
+.. _docs-contrib-moduledocs:
+
+======================
+Module Docs Guidelines
+======================
+This page provides guidelines on how to write documentation for a single
+Pigweed module.
+
+These guidelines are a work-in-progress. Most of the guidelines are
+recommendations, not requirements, because we're still figuring out which
+guidelines are truly universal to all modules and which ones only work for
+certain types of modules.
+
+---------------------
+Choose your adventure
+---------------------
+Our guidance for you changes depending on the nature of the module you're
+documenting. This flowchart summarizes the decision path:
+
+.. mermaid::
+
+ flowchart TD
+ A[Start] --> B{Simple module or complex?}
+ B -->|Complex| C[Talk to the Pigweed team]
+ B -->|Simple| D{A little content or a lot?}
+ D -->|A little| E[Use the single-page approach]
+ D -->|A lot| F[Use the multi-page approach]
+
+.. _docs-contrib-moduledocs-moduletypes:
+
+-------------------------------------
+Simple modules versus complex modules
+-------------------------------------
+We're currently focused on updating the docs for "simple" modules. Here's our
+general definition for a simple module:
+
+* The module's API is end-user-facing or HAL-facing, not
+ infrastructure-facing.
+* The module does not use :ref:`facades <docs-glossary-facade>`.
+* The module's API surface is small or medium in size.
+* The module's API is in a single programming language.
+
+If your module doesn't meet these criteria, it's probably a "complex" module.
+`Create an issue <https://issues.pigweed.dev/issues/new>`_ before attempting
+to refactor a complex module's docs.
+
+.. note::
+
+ Why the focus on simple modules? We tried refactoring complex modules to
+ adhere to :ref:`SEED-0102 <seed-0102>`. The migrations were difficult and
+ the resulting docs weren't an obvious improvement. We learned that it's more
+ effective to focus on simple modules for now and take more time to figure out
+ what works for complex modules.
+
+Examples of simple modules:
+
+* :ref:`module-pw_string`
+* :ref:`module-pw_i2c`
+
+Examples of complex modules:
+
+* :ref:`module-pw_tokenizer`
+
+.. _docs-contrib-moduledocs-simple:
+
+------------------------
+Simple module guidelines
+------------------------
+Follow these guidelines if you're writing docs for a
+:ref:`simple module <docs-contrib-moduledocs-moduletypes>`.
+
+Single-page approach versus multi-page approach
+===============================================
+If your module meets the following criteria then you should *probably* use
+the :ref:`docs-contrib-moduledocs-singlepage`:
+
+* There is less than 1000 words of content in total.
+* The API has 2 classes or less.
+* The API has 10 methods or less.
+
+If your module doesn't meet all these criteria, then you should *probably*
+use the :ref:`docs-contrib-moduledocs-multipage`. As you can tell by our use of
+*probably*, this is just a soft guideline. E.g. if you have 2000 words of
+content but you feel strongly that the single-page approach is better for your
+module, then go for it!
+
+The content that you write mostly stays the same whether you use the single-page
+or multi-page approach. All modules must have
+:ref:`docs-contrib-moduledocs-sales` content for example. The only
+difference is that in the single-page approach this is the first *section* of
+content whereas in the multi-page approach it's the first *page* of content.
+
+.. _docs-contrib-moduledocs-singlepage:
+
+Single-page approach
+====================
+When using the single-page approach, this is the default ordering of
+sections in ``docs.rst``:
+
+* :ref:`docs-contrib-moduledocs-sales`
+* :ref:`docs-contrib-moduledocs-getstarted`
+* :ref:`docs-contrib-moduledocs-guides`
+* :ref:`docs-contrib-moduledocs-reference`
+* :ref:`docs-contrib-moduledocs-design`
+* :ref:`docs-contrib-moduledocs-roadmap`
+* :ref:`docs-contrib-moduledocs-size`
+
+The sales pitch must come first, followed by the getting started instructions.
+Everything else beyond that is optional. The sections can be re-arranged if
+you feel strongly about it, but we've found this is an intuitive ordering.
+
+The file must be located at ``//pw_<name>/docs.rst``, where ``<name>`` is
+replaced with the actual name of your module.
+
+.. _docs-contrib-moduledocs-multipage:
+
+Multi-page approach
+===================
+When using the multi-page approach, this is the default ordering of
+pages:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Page Title
+ - Filename
+ - Description
+ * - ``pw_<name>``
+ - ``docs.rst``
+ - The :ref:`docs-contrib-moduledocs-sales` content.
+ * - ``Get Started & Guides``
+ - ``guides.rst``
+ - The :ref:`docs-contrib-moduledocs-getstarted` content followed by the
+ :ref:`docs-contrib-moduledocs-guides` content. See the note below.
+ * - ``API Reference``
+ - ``api.rst``
+ - The :ref:`docs-contrib-moduledocs-reference` content.
+ * - ``Design & Roadmap``
+ - ``design.rst``
+ - The :ref:`docs-contrib-moduledocs-design` content. See the note below.
+ * - ``Code Size Analysis``
+ - ``size.rst``
+ - The :ref:`docs-contrib-moduledocs-size` content.
+
+The sales pitch and getting started instructions are required. Everything else
+is optional. The sections can be re-arranged if you feel strongly about it,
+but we've found that this is an intuitive ordering.
+
+You can split ``Get Started & Guides`` into 2 docs if that works better for
+your module. The filenames should be ``get_started.rst`` and ``guides.rst``.
+
+``Design & Roadmap`` can also be split into 2 docs. The filenames should be
+``design.rst`` and ``roadmap.rst``.
+
+Nav cards
+---------
+When using the multi-page approach, every page in the set must link to every
+other page in the set. We recommend using the ``grid`` directive to create
+call-to-action buttons on the bottom of every page. See ``//pw_string/docs.rst``
+for an example.
+
+.. note::
+
+ We will eventually automate this. We know it's error-prone and tedious to
+ do this manually.
+
+------------------
+Content guidelines
+------------------
+The following sections provide instructions on how to write each content type.
+
+.. note::
+
+ We call them "content types" because in the
+ :ref:`docs-contrib-moduledocs-singlepage` each of these things represent a
+ section of content on ``docs.rst`` whereas in the
+ :ref:`docs-contrib-moduledocs-multipage` they might be an entire page of
+ content or a section within a page.
+
+.. _docs-contrib-moduledocs-sales:
+
+Sales pitch
+===========
+The sales pitch should:
+
+* Assume that the reader is an embedded developer.
+* Clearly explain how the reader's work as an embedded developer
+ will improve if they adopt the module.
+* Provide a code sample demonstrating one of the most important
+ problems the module solves. (Only required for modules that expose
+ an API.)
+
+Examples:
+
+* :ref:`module-pw_string`
+* :ref:`module-pw_tokenizer`
+
+.. _docs-contrib-moduledocs-getstarted:
+
+Get started
+===========
+The get started instructions should:
+
+* Show how to get set up in Bazel, GN, and CMake.
+* Present Bazel instructions first.
+* Clearly state when a build system isn't supported.
+* Format the instructions with the ``.. tab-set::`` directive. See
+ ``//pw_string/guide.rst`` for an example. The Bazel instructions are
+ presented in the first tab, the GN instructions in the next, and so on.
+* Demonstrate how to complete a common use case. See the next paragraph.
+
+If your get started content is on the same page as your guides, then the get
+started section doesn't need to demonstrate a common use case. The reader can
+just scroll down and see how to complete common tasks. If your get started
+content is a standalone page, it should demonstrate how to complete a common
+task. The reader shouldn't have to dig around multiple docs just to figure out
+how to do something useful with the module.
+
+Examples:
+
+* :ref:`module-pw_string-get-started` (pw_string)
+
+.. _docs-contrib-moduledocs-guides:
+
+Guides
+======
+The guides should:
+
+* Focus on how to solve real-world problems with the module. See
+ `About how-to guides <https://diataxis.fr/how-to-guides/>`_.
+
+Examples:
+
+* :ref:`module-pw_string-guide-stringbuilder`
+
+.. _docs-contrib-moduledocs-reference:
+
+API reference
+=============
+The API reference should:
+
+* Be auto-generated from :ref:`docs-pw-style-doxygen` (for C++ / C APIs) or
+ autodoc (for Python APIs).
+* Provide a code example demonstrating how to use class, at minimum. Consider
+ whether it's also helpful to provide more granular examples demonstrating
+ how to use each method, variable, etc.
+
+The typical approach is to order everything alphabetically. Some module docs
+group classes logically according to the tasks they're related to. We don't
+have a hard guideline here because we're not sure one of these approaches is
+universally better than the other.
+
+Examples:
+
+* :ref:`module-pw_string-api` (pw_string)
+* :ref:`module-pw_tokenizer-api` (pw_tokenizer)
+
+.. _docs-contrib-moduledocs-design:
+
+Design
+======
+The design content should:
+
+* Focus on `theory of operation <https://en.wikipedia.org/wiki/Theory_of_operation>`_
+ or `explanation <https://diataxis.fr/explanation/>`_.
+
+Examples:
+
+* :ref:`module-pw_string-design-inlinestring` (pw_string)
+
+.. _docs-contrib-moduledocs-roadmap:
+
+Roadmap
+=======
+The roadmap should:
+
+* Focus on things known to be missing today that could make sense in the
+ future. The reader should be encouraged to talk to the Pigweed team.
+
+The roadmap should not:
+
+* Make very definite guarantees that a particular feature will ship by a
+ certain date. You can get an exception if you really need to do this, but
+ it should be avoided in most cases.
+
+Examples:
+
+* :ref:`module-pw_string-roadmap` (pw_string)
+
+.. _docs-contrib-moduledocs-size:
+
+Size analysis
+=============
+The size analysis should:
+
+* Be auto-generated. See the ``pw_size_diff`` targets in ``//pw_string/BUILD.gn``
+ for examples.
+
+We elevate the size analysis to its own section or page because it's a very
+important consideration for many embedded developers.
+
+Examples:
+
+* :ref:`module-pw_string-size-reports` (pw_string)
diff --git a/docs/facades.rst b/docs/facades.rst
new file mode 100644
index 000000000..c067e3052
--- /dev/null
+++ b/docs/facades.rst
@@ -0,0 +1,153 @@
+.. _docs-facades:
+
+====================
+Facades and backends
+====================
+This page explains what "facades" and "backends" mean in the context of Pigweed
+and provides guidelines on when to use them.
+
+------------------------------
+What are facades and backends?
+------------------------------
+.. _top-down approach: https://en.wikipedia.org/wiki/Bottom%E2%80%93up_and_top%E2%80%93down_design
+
+Let's take a `top-down approach`_, starting with high-level definitions.
+In the context of a Pigweed module:
+
+* A facade is an API contract of a module that must be satisfied at compile-time,
+ i.e. a swappable dependency that changes the implementation of an API at
+ compile-time.
+* A backend is an implementation of a facade's contract.
+
+Usually, the facade and the backend are in different modules. One module
+exposes the facade contract, and another module is the backend that implements
+the facade contract. The naming pattern that Pigweed follows is
+``{facade_name}_{backend_name}``.
+
+Facades, by design, don't need to be configured until they're actually used.
+This makes it significantly easier to bring up features piece-by-piece.
+
+Example: pw_log and pw_log_string
+=================================
+Here's a step-by-step walkthrough of how a real backend module implements
+another module's facade.
+
+.. _//pw_log/public/pw_log/log.h: https://cs.opensource.google/pigweed/pigweed/+/main:pw_log/public/pw_log/log.h
+.. _//pw_log_string/public_overrides/pw_log_backend/log_backend.h: https://cs.opensource.google/pigweed/pigweed/+/main:pw_log_string/public_overrides/pw_log_backend/log_backend.h
+
+* :ref:`module-pw_log` is a module that exposes a facade. The macros listed in
+ :ref:`module-pw_log-macros` represent the API of the module.
+* The ``#include "pw_log/log_backend.h"`` line in `//pw_log/public/pw_log/log.h`_
+ represents the facade contract of ``pw_log``.
+* :ref:`module-pw_log_string` is a backend module, It implements the
+ ``pw_log/log_backend.h`` facade contract in
+ `//pw_log_string/public_overrides/pw_log_backend/log_backend.h`_.
+* In the build system there is a variable of some sort that specifies the
+ backend. In Bazel there's a `label
+ flag <https://bazel.build/extending/config#label-typed-build-settings>`_
+ at ``//targets:pw_log_backend``. In CMake there's a ``pw_log_BACKEND``
+ variable set to ``pw_log_string``. In GN there's a ``pw_log_BACKEND``
+ variable set to ``dir_pw_log_string``.
+
+.. note::
+
+ There are a few more steps needed to get ``pw_log_string`` hooked up as the
+ backend for ``pw_log`` but they aren't essential for the current discussion.
+ See :ref:`module-pw_log_string-get-started-gn` for the details.
+
+Example: Swappable OS libraries
+===============================
+The facade and backend system is similar to swappable OS libraries: you can
+write code against ``libc`` (for example) and it will work so long as you have
+an object to link against that has the symbols you're depending on. You can
+swap in different versions of ``libc`` and your code doesn't need to know about
+it.
+
+A similar example from Windows is ``d3d9.dll``. Developers often swap this DLL
+with a different library like ReShade to customize shading behavior.
+
+Can a module have multiple facades?
+===================================
+Yes. The module-to-facade relationship is one-to-many. A module can expose
+0, 1, or many facades. There can be (and usually are) multiple backend modules
+implementing the same facade.
+
+Is the module facade the same thing as its API?
+===============================================
+No. It's best to think of them as different concepts. The API is the interface
+that downstream projects interact with. There's always a one-to-one relationship
+between a module and its API. A facade represents the build system "glue" that
+binds a backend to an API.
+
+This is a common point of confusion because there are a few modules that
+blur this distinction. Historically, the facade comprised the API as well as
+the build system concept. We're moving away from this perspective.
+
+Are Pigweed facades the same as the GoF facade design pattern?
+==============================================================
+.. _facade pattern: https://en.wikipedia.org/wiki/Facade_pattern
+
+There are some loose similarities but it's best to think of them as different
+things. The goal of the Gang of Four `facade pattern`_ is to compress some
+ugly, complex API behind a much smaller API that is more aligned with your
+narrow business needs. The motivation behind some Pigweed facades is loosely
+the same: we don't want a device HAL to leak out into your include paths when
+a facade is implemented.
+
+------------------------------
+Why does Pigweed have facades?
+------------------------------
+Pigweed's facades are basically a pattern that builds off the ideas of
+`link-time subsitution <https://bramtertoolen.medium.com/91ffd4ef8687>`_.
+Link-time subsitution only allows you to replace one source file with another,
+whereas facades enable substituting program elements defined in *header files*.
+
+Pigweed facades enable implementation flexibility with zero performance cost.
+There are two ways to look at this. Continuing with the ``pw_log`` example:
+
+* Organizations can reuse more code across projects by adopting the ``pw_log``
+ API as their logging layer. Facades enable each project to customize how
+ its logs are handled.
+* For projects that want to integrate with ``pw_log`` but cannot adopt its
+ API, facades can wrap the existing API which enables Pigweed to be compatible
+ with the existing system.
+
+Two of the major aspects of "implementation flexibility" enabled by facades are:
+
+* Portability. For example, ``pw_sync`` needs platform-specific
+ implementations to work correctly.
+* Customized behavior. For example, you can make a fully portable ``pw_log``
+ implementation, but what makes it special is the ability to tune it to your
+ needs.
+
+Why compile-time?
+=================
+Resolving facades and backends at compile-time enables:
+
+* Call-site control from backends.
+* Static allocation of backend-provided types.
+* Explicit backend includes so it’s visually obvious you’re poking through
+ abstraction.
+
+--------------------------------
+When to use facades and backends
+--------------------------------
+If you're trying to use a Pigweed module, and that module exposes a facade,
+then you've got no choice: you've got to hook up a backend to fulfill that
+facade contract or else the module won't work.
+
+When to roll your own facades and backends
+==========================================
+* You need a global function or macro.
+* You absolutely must avoid the overhead of virtual functions.
+
+When to NOT roll your own facades and backends
+==============================================
+* If you can afford the runtime cost of dependency injection, use that.
+ In all other cases where link-time subsitution will work, use that.
+ Only if the API contract requires a backend to provide a header (which
+ link-time substitution doesn't let you do) should you reach for a facde.
+* You're trying to use globals to avoid dependency injection. Use
+ the dependency injection! It makes testing much easier.
+* Your needs can be served by a standard mechanism like virtual interfaces.
+ Use the standard mechanism.
diff --git a/docs/get_started/bazel.rst b/docs/get_started/bazel.rst
new file mode 100644
index 000000000..68ba18821
--- /dev/null
+++ b/docs/get_started/bazel.rst
@@ -0,0 +1,217 @@
+.. _docs-get-started-bazel:
+
+==================================
+Get Started With Pigweed And Bazel
+==================================
+This guide provides a starting point for using Pigweed in a Bazel-based project.
+Bazel is :ref:`the recommended build system <seed-0111>` for new projects using
+Pigweed.
+
+-----------
+Limitations
+-----------
+.. TODO: b/306393519 - Update the MacOS description once that path is verified.
+
+* **Linux**. Your development host must be running Linux. Bazel-based Pigweed
+ projects are not yet supported on Windows. MacOS probably works but is
+ unverified.
+
+-----
+Setup
+-----
+#. `Install Bazel <https://bazel.build/install>`_.
+
+ .. tip::
+
+ If you want to minimize system-wide installations, first install
+ `Node Version Manager <https://github.com/nvm-sh/nvm>`_ and then
+ install Bazelisk through NPM using ``npm install -g @bazel/bazelisk``.
+ If you use this workflow, remember that Bazel will only be available
+ in the version of Node that's currently activated through NVM.
+
+#. Clone `the project <https://pigweed.googlesource.com/example/echo/+/refs/heads/main>`_:
+
+ .. code-block:: console
+
+ $ git clone --recursive https://pigweed.googlesource.com/example/echo
+
+ .. tip::
+
+ If you forgot the ``--recursive`` flag when cloning the code, run
+ ``git submodule update --init``.
+
+All subsequent commands that you see in this guide should be run from the
+root directory of your new ``echo`` repo.
+
+-----------------
+Build the project
+-----------------
+#. Build the project for :ref:`target-host` and
+ :ref:`target-stm32f429i-disc1`:
+
+ .. code-block:: console
+
+ $ bazel build //...
+
+ You should see output like this:
+
+ .. code-block:: none
+
+ Starting local Bazel server and connecting to it...
+ INFO: Analyzed 7 targets (101 packages loaded, 17371 targets configured).
+ INFO: Found 7 targets...
+ INFO: From Linking src/echo:
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-closer.o): in function `_close_r':
+ closer.c:(.text._close_r+0xc): warning: _close is not implemented and will always fail
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-signalr.o): in function `_getpid_r':
+ signalr.c:(.text._getpid_r+0x0): warning: _getpid is not implemented and will always fail
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-signalr.o): in function `_kill_r':
+ signalr.c:(.text._kill_r+0xe): warning: _kill is not implemented and will always fail
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-lseekr.o): in function `_lseek_r':
+ lseekr.c:(.text._lseek_r+0x10): warning: _lseek is not implemented and will always fail
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-readr.o): in function `_read_r':
+ readr.c:(.text._read_r+0x10): warning: _read is not implemented and will always fail
+ /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/gcc_arm_none_eabi_toolchain/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(libc_a-writer.o): in function `_write_r':
+ writer.c:(.text._write_r+0x10): warning: _write is not implemented and will always fail
+ INFO: Elapsed time: 6.716s, Critical Path: 1.98s
+ INFO: 40 processes: 3 internal, 37 linux-sandbox.
+ INFO: Build completed successfully, 40 total actions
+
+-----------------------
+Run the project locally
+-----------------------
+#. Run the project locally on your Linux development host:
+
+ .. code-block:: console
+
+ bazel run //src:echo
+
+ You should see output like this:
+
+ .. code-block:: none
+
+ INFO: Analyzed target //src:echo (36 packages loaded, 202 targets configured).
+ INFO: Found 1 target...
+ Target //src:echo up-to-date:
+ bazel-bin/src/echo
+ INFO: Elapsed time: 0.899s, Critical Path: 0.03s
+ INFO: 1 process: 1 internal.
+ INFO: Build completed successfully, 1 total action
+ INFO: Running command line: bazel-bin/src/echo
+
+#. Press ``Ctrl`` + ``C`` to stop running the project.
+
+----------------------------------------
+Flash the project onto a Discovery board
+----------------------------------------
+If you have an `STM32F429 Discovery <https://www.st.com/stm32f4-discover>`_
+board, you can run the project on that hardware.
+
+.. note::
+
+ You don't need this hardware to run the project. Because this project
+ supports the :ref:`target-host` target, you can run everything
+ on your Linux development host.
+
+#. Ensure your udev rules are set up to allow the user running the commands
+ below to access the Discovery Board. For example, you may want to add the
+ following rule as ``/etc/udev/rules.d/99-stm32f329i-disc1.rules``:
+
+ .. code-block:: console
+
+ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="664", GROUP="plugdev"
+
+ The user running the commands needs to be in the group ``plugdev``.
+
+#. Connect the Discovery board to your development host with a USB
+ cable. **Use the Mini-B USB port on the Discovery board, not the
+ Micro-B port**.
+
+#. Flash the project to the Discovery board:
+
+ .. code-block:: console
+
+ $ bazel run //tools:flash
+
+ You should see output like this:
+
+ .. code-block:: none
+
+ INFO: Analyzed target //tools:flash (52 packages loaded, 2760 targets configured).
+ INFO: Found 1 target...
+ Target //tools:flash up-to-date:
+ bazel-bin/tools/flash
+ INFO: Elapsed time: 0.559s, Critical Path: 0.04s
+ INFO: 1 process: 1 internal.
+ INFO: Build completed successfully, 1 total action
+ INFO: Running command line: bazel-bin/tools/flash
+ binary Rlocation is: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/execroot/__main__/bazel-out/k8-fastbuild/bin/src/echo.elf
+ openocd Rlocation is: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/openocd/bin/openocd
+ openocd config Rlocation is: /home/xyz/.cache/bazel/_bazel_xyz/8c700b5cf88b83b789ceaf0e4e271fac/external/pigweed/targets/stm32f429i_disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg
+ xPack OpenOCD x86_64 Open On-Chip Debugger 0.11.0+dev (2021-12-07-17:30)
+ Licensed under GNU GPL v2
+ For bug reports, read
+ http://openocd.org/doc/doxygen/bugs.html
+ DEPRECATED! use 'adapter driver' not 'interface'
+ DEPRECATED! use 'adapter serial' not 'hla_serial'
+ Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
+ srst_only separate srst_nogate srst_open_drain connect_deassert_srst
+
+ Info : clock speed 2000 kHz
+ Info : STLINK V2J25M14 (API v2) VID:PID 0483:374B
+ Info : Target voltage: 2.837377
+ Info : stm32f4x.cpu: Cortex-M4 r0p1 processor detected
+ Info : stm32f4x.cpu: target has 6 breakpoints, 4 watchpoints
+ Info : gdb port disabled
+ Info : Unable to match requested speed 2000 kHz, using 1800 kHz
+ Info : Unable to match requested speed 2000 kHz, using 1800 kHz
+ target halted due to debug-request, current mode: Thread
+ xPSR: 0x01000000 pc: 0x08000708 msp: 0x20030000
+ Info : Unable to match requested speed 8000 kHz, using 4000 kHz
+ Info : Unable to match requested speed 8000 kHz, using 4000 kHz
+ ** Programming Started **
+ Info : device id = 0x20016419
+ Info : flash size = 2048 kbytes
+ Info : Dual Bank 2048 kiB STM32F42x/43x/469/479 found
+ Info : Padding image section 0 at 0x08000010 with 496 bytes
+ ** Programming Finished **
+ ** Resetting Target **
+ Info : Unable to match requested speed 2000 kHz, using 1800 kHz
+ Info : Unable to match requested speed 2000 kHz, using 1800 kHz
+ shutdown command invoked
+
+
+Communicate with the project over serial
+========================================
+After you've flashed the project onto your Discovery board, your Linux development
+host can communicate with the project over a serial terminal like ``miniterm``.
+
+#. Transmit and receive characters:
+
+ .. code-block:: console
+
+ $ bazel run //tools:miniterm -- /dev/ttyACM0 --filter=debug
+
+ After typing ``hello`` and pressing ``Ctrl`` + ``]`` to exit you should see output
+ like this:
+
+ .. code-block:: none
+
+ INFO: Analyzed target //tools:miniterm (41 packages loaded, 2612 targets configured).
+ INFO: Found 1 target...
+ Target //tools:miniterm up-to-date:
+ bazel-bin/tools/miniterm
+ INFO: Elapsed time: 0.373s, Critical Path: 0.02s
+ INFO: 1 process: 1 internal.
+ INFO: Build completed successfully, 1 total action
+ INFO: Running command line: bazel-bin/tools/miniterm /dev/ttyACM0 '--filter=debug'
+ --- Miniterm on /dev/ttyACM0 115200,8,N,1 ---
+ --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
+ [TX:'h'] [RX:'h'] h [TX:'e'] [RX:'e'] e [TX:'l'] [RX:'l'] l [TX:'l'] [RX:'l'] l [TX:'o'] [RX:'o'] o
+ --- exit ---
+
+------------------------------
+Questions? Comments? Feedback?
+------------------------------
+Please join `our Discord <https://discord.com/invite/M9NSeTA>`_ and talk to us
+in the ``#bazel-build`` channel or `file a bug <https://issues.pigweed.dev>`_.
diff --git a/docs/get_started/index.rst b/docs/get_started/index.rst
index 3048b47a6..5dc484aef 100644
--- a/docs/get_started/index.rst
+++ b/docs/get_started/index.rst
@@ -3,18 +3,27 @@
===========
Get Started
===========
-Stay tuned for a tutorial on getting started with Pigweed in a Bazel-based project,
-:ref:`Pigweed's primary build system <seed-0111>` going forward.
+.. grid:: 1
+
+ .. grid-item-card:: :octicon:`rocket` Bazel
+ :link: docs-get-started-bazel
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
+
+ Fork our minimal, Bazel-based starter code. Bazel is the recommended
+ build system for new projects using Pigweed.
.. grid:: 2
.. grid-item-card:: :octicon:`code` Sample Project
:link: https://pigweed.googlesource.com/pigweed/sample_project/+/main/README.md
:link-type: url
- :class-item: sales-pitch-cta-primary
+ :class-item: sales-pitch-cta-secondary
Fork the Sample Project, a repository that outlines the recommended way to use
- Pigweed in a broader GN-based project.
+ Pigweed in a broader GN-based project. Note that Bazel is the recommended
+ build system for new projects using Pigweed, whereas this sample project uses
+ GN.
.. grid-item-card:: :octicon:`code` Kudzu
:link: docs-kudzu
@@ -22,10 +31,17 @@ Stay tuned for a tutorial on getting started with Pigweed in a Bazel-based proje
:class-item: sales-pitch-cta-secondary
Study the code of Kudzu, a just-for-fun Maker Faire 2023 project that
- demonstrates complex Pigweed usage.
+ demonstrates complex Pigweed usage. This project also uses GN.
.. grid:: 2
+ .. grid-item-card:: :octicon:`list-ordered` Zephyr
+ :link: docs-os-zephyr-get-started
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ Instructions on how to use Pigweed in a Zephyr-based project.
+
.. grid-item-card:: :octicon:`list-ordered` Upstream Pigweed
:link: docs-get-started-upstream
:link-type: ref
@@ -37,4 +53,5 @@ Stay tuned for a tutorial on getting started with Pigweed in a Bazel-based proje
:maxdepth: 1
:hidden:
+ Bazel <bazel>
Upstream Pigweed <upstream>
diff --git a/docs/get_started/upstream.rst b/docs/get_started/upstream.rst
index 9cf2d74f5..136443048 100644
--- a/docs/get_started/upstream.rst
+++ b/docs/get_started/upstream.rst
@@ -174,7 +174,7 @@ bootstrap may take several minutes to complete, so please be patient.
Below is a real-time demo with roughly what you should expect to see as output:
-.. image:: ../images/pw_env_setup_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_env_setup_demo.gif
:width: 800
:alt: build example using pw watch
@@ -260,7 +260,7 @@ save the file.
See below for a demo of this in action:
-.. image:: ../images/pw_watch_build_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_watch_build_demo.gif
:width: 800
:alt: build example using pw watch
@@ -274,7 +274,7 @@ failure.
To see a test failure, modify ``pw_status/status_test.cc`` to fail by changing
one of the strings in the "KnownString" test.
-.. image:: ../images/pw_watch_test_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_watch_test_demo.gif
:width: 800
:alt: example test failure using pw watch
@@ -330,7 +330,7 @@ updates to ensure you have permissions to use the USB device. For example, on
Linux you may need to update your udev rules and ensure you're in the plugdev
and dialout groups.
-.. image:: ../images/stm32f429i-disc1_connected.jpg
+.. image:: https://storage.googleapis.com/pigweed-media/stm32f429i-disc1_connected.jpg
:width: 800
:alt: development boards connected via USB
@@ -367,7 +367,7 @@ will be run across the attached boards!
See the demo below for an example of what this all looks like put together:
-.. image:: ../images/pw_watch_on_device_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_watch_on_device_demo.gif
:width: 800
:alt: pw watch running on-device tests
diff --git a/docs/glossary.rst b/docs/glossary.rst
index b752e1ca0..a486a9b5e 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -6,6 +6,19 @@ Glossary
This glossary defines terms that have specific meanings in the context of
Pigweed.
+.. _docs-glossary-facade:
+
+------
+Facade
+------
+A facade is an API contract of a module that must be satisfied at compile-time,
+i.e. a swappable dependency that changes the implementation of an API at
+compile-time.
+
+Learn more:
+
+* :ref:`docs-facades`
+
.. _docs-glossary-module:
------
diff --git a/docs/images/pw_env_setup_demo.gif b/docs/images/pw_env_setup_demo.gif
deleted file mode 100644
index c8942ac3b..000000000
--- a/docs/images/pw_env_setup_demo.gif
+++ /dev/null
Binary files differ
diff --git a/docs/images/pw_status_test.png b/docs/images/pw_status_test.png
deleted file mode 100644
index 336abff3e..000000000
--- a/docs/images/pw_status_test.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/pw_watch_build_demo.gif b/docs/images/pw_watch_build_demo.gif
deleted file mode 100644
index 877d84931..000000000
--- a/docs/images/pw_watch_build_demo.gif
+++ /dev/null
Binary files differ
diff --git a/docs/images/pw_watch_on_device_demo.gif b/docs/images/pw_watch_on_device_demo.gif
deleted file mode 100644
index 68fa11898..000000000
--- a/docs/images/pw_watch_on_device_demo.gif
+++ /dev/null
Binary files differ
diff --git a/docs/images/pw_watch_test_demo.gif b/docs/images/pw_watch_test_demo.gif
deleted file mode 100644
index 83a6a0b85..000000000
--- a/docs/images/pw_watch_test_demo.gif
+++ /dev/null
Binary files differ
diff --git a/docs/images/pw_watch_test_demo2.gif b/docs/images/pw_watch_test_demo2.gif
deleted file mode 100644
index e9e9c65bc..000000000
--- a/docs/images/pw_watch_test_demo2.gif
+++ /dev/null
Binary files differ
diff --git a/docs/images/stm32f429i-disc1_connected.jpg b/docs/images/stm32f429i-disc1_connected.jpg
deleted file mode 100644
index 37643b56f..000000000
--- a/docs/images/stm32f429i-disc1_connected.jpg
+++ /dev/null
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
index 95c13dca3..eb3b74644 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,26 +8,24 @@
:hidden:
Home <self>
+ docs/overview
docs/get_started/index
docs/concepts/index
targets
Modules <module_guides>
docs/module_structure
changelog
- glossary
Mailing List <https://groups.google.com/forum/#!forum/pigweed>
Chat Room <https://discord.gg/M9NSeTA>
docs/os/index
docs/size_optimizations
Code Editor Support <docs/editors>
- FAQ <docs/faq>
third_party_support
Source Code <https://cs.pigweed.dev/pigweed>
Code Reviews <https://pigweed-review.googlesource.com>
Issue Tracker <https://issues.pigweed.dev/issues?q=status:open>
- docs/contributing
+ docs/contributing/index
docs/infra/index
- docs/code_of_conduct
Automated Analysis <automated_analysis>
Build System <build_system>
SEEDs <seed/0000-index>
@@ -67,16 +65,10 @@ See :ref:`docs-changelog-latest` in our changelog for details.
---------------
Getting Started
---------------
-Check out `Pigweed Sample Project <https://pigweed.googlesource.com/pigweed/sample_project/+/main/README.md>`_
-to see how to use Pigweed as a library in your broader project.
-
-Visit the :ref:`docs-get-started-upstream` guide to learn how to bootstrap and
-activate a Pigweed environment, build Pigweed for a specific host or device,
-run tests, and more.
-
-Zephyr
-======
-See :ref:`docs-os-zephyr-get-started`.
+Check out our :ref:`docs-get-started` landing page. We've got a guide that
+shows you how to use Pigweed in a new, Bazel-based project (the recommended
+path), sample code for GN-based projects, a tutorial on getting set up to
+contribute to upstream Pigweed, and more.
------------------------
What does Pigweed offer?
@@ -101,7 +93,7 @@ and verifying the test runs as expected. Once this is set up, you can attach
multiple devices to run tests in a distributed manner to reduce the time it
takes to run tests.
-.. image:: docs/images/pw_watch_on_device_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_watch_on_device_demo.gif
:width: 800
:alt: pw watch running on-device tests
@@ -119,7 +111,7 @@ With ``pw format``, you can format C, C++, Python, GN, and Go code according to
configurations defined by your project. ``pw format`` leverages existing tools
like ``clang-format``, and it’s simple to add support for new languages.
-.. image:: pw_presubmit/docs/pw_presubmit_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_presubmit_demo.gif
:width: 800
:alt: pw presubmit demo
@@ -150,7 +142,7 @@ turn inflates a virtual environment. The tooling is installed into your
workspace, and makes no changes to your system. This tooling is designed to be
reused by any project.
-.. image:: docs/images/pw_env_setup_demo.gif
+.. image:: https://storage.googleapis.com/pigweed-media/pw_env_setup_demo.gif
:width: 800
:alt: pw environment setup demo
@@ -170,7 +162,7 @@ build, the result is a flexible and powerful setup that enables easily
developing code on your desktop (with tests), then running the same tests
on-device.
-.. image:: docs/images/pw_status_test.png
+.. image:: https://storage.googleapis.com/pigweed-media/pw_status_test.png
:width: 800
:alt: pw_status test run natively on host
@@ -192,8 +184,8 @@ Here is a selection of interesting modules:
:link: module-pw_polyfill
:link-type: ref
- Similar to JavaScript “polyfill” libraries, this module provides selected
- C++17 standard library components that are compatible with C++14.
+ This module makes it easier to work with different C++ standards in one
+ codebase.
.. grid-item-card:: :octicon:`container` pw_tokenizer
:link: module-pw_tokenizer
diff --git a/docs/infra/ci_cq_intro.rst b/docs/infra/ci_cq_intro.rst
index ad29663c7..bda836a8c 100644
--- a/docs/infra/ci_cq_intro.rst
+++ b/docs/infra/ci_cq_intro.rst
@@ -258,11 +258,23 @@ follow the instructions below.
First, get both changes passing CQ with ``patches.json`` files.
-If one of the codependent changes is a submodule and another is the parent
-project, update the submodule change to no longer include the ``patches.json``
-file. Then directly submit the submodule change, bypassing CQ. This will break
-the roller, but not the source tree, so others on your team are unaffected. Once
-it's submitted do the following:
+Second, if one of the codependent changes is a submodule and another is the
+parent project, update the submodule change to no longer include the
+``patches.json`` file. Then directly submit the change that lives in the child
+submodule, bypassing CQ. This will break the roller, but not the source tree, so
+others on your team are unaffected.
+
+.. admonition:: Note
+
+ For the main Pigweed repository, only core Pigweed team members can force
+ submit, and they must first
+ `request a temporary ACL <http://go/pwi-cookbook#bypass-cq>`_ to do so. This
+ process requires an associated bug, so have one on hand before reaching out
+ with a force submission request.
+
+
+Finally, once the change has merged into the child project, update the submodule
+pointer in the parent project:
.. admonition:: Note
:class: warning
diff --git a/docs/mission.rst b/docs/mission.rst
new file mode 100644
index 000000000..eb85d87aa
--- /dev/null
+++ b/docs/mission.rst
@@ -0,0 +1,55 @@
+.. _docs-mission:
+
+======================
+Mission & Philosophies
+======================
+
+-----------
+Our Mission
+-----------
+.. pull-quote::
+
+ Create both *software* and *process* foundations for *sustained*,
+ *robust*, and *rapid* *embedded* product development with *large teams*.
+
+Each component of our mission has additional meaning:
+
+- **Software** - Our modular components and surrounding tooling are our
+ primary deliverable. The software is the product of our philosophies.
+- **Process** - Developing robust software processes is hard, and not something
+ all teams have time for. Our mission is to create reusable processes that can
+ save time and increase team happiness. Our approach is to offer tooling and
+ standards. For example, our integrated code formatting, module directory
+ structure, style guide, test processes, and more are all designed for reuse.
+- **Sustained** - Products often live a long time or are part of a series of
+ related products; and over time the requirements change. Our mission is to
+ make these sustained development situations easier. Our approach is to create
+ flexible and reusable foundations to gracefully handle changes. For example,
+ our porting abstractions make switching a microcontroller vendor easier.
+- **Robust** - Creating robust products is hard, yet important for finding
+ market and product success. Our mission is to assist teams in creating robust
+ products. Our approach is to:
+
+ #. Invest heavily in software reliability tooling and processes (unit testing,
+ sanitizing, formatting, etc)
+ #. Comprehensively apply said tooling and processes to Pigweed, making Pigweed
+ a robust foundation
+ #. Make said tooling available for downstream use, so teams can ensure code
+ they write is robust
+
+- **Rapid** - Creating products quickly is critical in today's market. Our
+ mission is to make shipping products faster with Pigweed. Our approach is to
+ accelerate product development by offering reliable foundations (less bug
+ hunting), having great onboarding tooling (low engineer churn/setup), offering
+ modules solving common but hard problems (RPC), etc.
+- **Embedded** - Our mission is to support software embedded in products such as
+ earbuds, watches, phones, thermostats, satellites, cars, drones, and related.
+- **Large teams** - Developing software with large teams in the embedded space
+ is increasingly frequent due to more capable embedded processors.
+ Traditionally, teams were 1-5 people, but are increasingly much larger
+ (50-200 person) teams. Our mission is to make developing larger products with
+ correspondingly larger teams easier and faster, with an eye to support rapid
+ prototyping where there is a chance to scale from an idea to production. Our
+ approach is the investment in common abstractions, common practices, common
+ standards, tooling around those standards, and a focus on automation and
+ repeatability.
diff --git a/docs/overview.rst b/docs/overview.rst
new file mode 100644
index 000000000..d79306ea9
--- /dev/null
+++ b/docs/overview.rst
@@ -0,0 +1,138 @@
+.. _docs-overview:
+
+========
+Overview
+========
+
+Why Build Pigweed?
+==================
+.. note::
+
+ See our :ref:`docs-mission` for more details.
+
+Our goal is to make embedded software development efficient, robust, and
+heck, even delightful, for projects ranging from weekend Arduino experiements
+to commercial products selling in the millions.
+
+Embedded software development is notoriously arcane. Developers often have to
+track down vendor toolchains specific to the hardware they're targeting, write
+their code against hardware-specfic SDKs/HALs, and limit themselves to a small
+subset of C. Project teams are on their own to figure out how to set up a build
+system, automated testing, serial communication, and many other embedded
+project fundamentals. This is error prone and takes effort away from developing
+the actual product!
+
+There are solutions on the market that promise to solve all of these problems
+with a monolithic framework—just write your code against the framework and use
+hardware the framework supports, and you get an efficient embedded development
+environment. But this approach doesn't work well for existing projects that
+weren't built on the framework from the beginning or for projects that have
+specific needs the framework wasn't designed for. We know from experience that
+this approach alone doesn't meet our goal.
+
+So we have set out to build a platform that supports successful embedded
+developers at every scale by allowing them to adopt as much or as little of
+what Pigweed provides as they need, in the way that works best for their
+project.
+
+How Pigweed Works
+=================
+Pigweed provides four foundational pillars to support your embedded development:
+
+* :ref:`A comprehensive set of libraries for embedded development<docs-concepts-embedded-development-libraries>`
+* :ref:`A hermetic and replicable development environment<docs-concepts-development-environment>`
+* :ref:`A system for building, testing, and linting your project<docs-concepts-build-system>`
+* :ref:`A full framework for new projects that want a turn-key solution<docs-concepts-full-framework>`
+
+.. _docs-concepts-embedded-development-libraries:
+
+Embedded Development Libraries
+------------------------------
+Pigweed enables you to use modern C++ and software development best practices in
+your embedded project without compromising performance or increasing memory use
+compared to conventional embedded C.
+
+We provide libraries (:ref:`modules <docs-glossary-module>`) for
+:ref:`strings<module-pw_string>`, :ref:`time<module-pw_chrono>`,
+:ref:`assertions<module-pw_assert>`, :ref:`logging<module-pw_log>`,
+:ref:`serial communication<module-pw_spi>`, :ref:`remote procedure calls (RPC)
+<module-pw_rpc>`, and :ref:`much more<docs-module-guides>`.
+
+These modules are designed to work both on your host machine and on a wide
+variety of target devices. We achieve this by writing them in an inherently
+portable way, or through the facade/backend pattern. As a result, you can write
+most or all of your code to run transparently on your host machine and targets.
+
+.. _docs-concepts-development-environment:
+
+Development Environment
+-----------------------
+Managing toolchains, build systems, and other software needed for a project is
+complex. Pigweed provides all of this out of the box for Linux, Mac, and
+Windows systems in a sealed environment that leaves the rest of your system
+untouched. Getting new developers started is as simple as cloning your project
+repository and activating the Pigweed environment.
+
+.. _docs-concepts-build-system:
+
+Build System
+------------
+Pigweed modules are built to integrate seamlessly into projects using GN. We
+are rapidly expanding our good support for CMake and nascent support for Bazel
+so you can use your build system of choice. For new projects, Pigweed provides a
+build system you can integrate your own code into that works out of the box.
+
+.. _docs-concepts-full-framework:
+
+Full Framework (coming in 2022)
+-------------------------------
+For those who want a fully-integrated solution that provides everything Pigweed
+has to offer with an opinionated project structure, we are working diligently
+on a :ref:`Pigweed framework<module-pw_system>`. Stay tuned for more news to
+come! In the meantime, we invite you to discuss this and collaborate with us
+on `Discord <https://discord.gg/M9NSeTA>`_.
+
+.. _docs-concepts-right-for-my-project:
+
+Is Pigweed Right for My Project?
+================================
+Pigweed is still in its early stages, and while we have ambitious plans for it,
+Pigweed might not be the right fit for your project today. Here are some things
+to keep in mind:
+
+* Many individual modules are stable and are running on shipped devices today.
+ If any of those modules meet your needs, you should feel safe bringing them
+ into your project.
+
+* Some modules are in very early and active stages of development. They likely
+ have unstable APIs and may not work on all supported targets. If this is the
+ case, it will be indicated in the module's documentation. If you're interested
+ in contributing to the development of one of these modules, we encourage you
+ to experiment with them. Otherwise they aren't ready for use in most projects.
+
+* Setting up new projects to use Pigweed is currently not very easy, but we are
+ working to address that. In the meantime, join the Pigweed community on
+ `Discord <https://discord.gg/M9NSeTA>`_ to get help.
+
+Supported language versions
+===========================
+
+C++
+---
+All Pigweed code requires C++17 and is fully compatible with C++20. Pigweed
+defines GN toolchains for building with C++17 and C++20; see :ref:`target-host`
+target documentation for more information. For Bazel, the C++ standard version
+can be configured using the `--cxxopt flag
+<https://bazel.build/docs/user-manual#cxxopt>`_.
+
+.. _docs-concepts-python-version:
+
+Python
+------
+Pigweed officially supports Python 3.8, 3.9, 3.10, and 3.11.
+
+.. toctree::
+ :maxdepth: 1
+ :hidden:
+
+ mission
diff --git a/docs/python_build.rst b/docs/python_build.rst
index 2be9cb032..2cce15a9c 100644
--- a/docs/python_build.rst
+++ b/docs/python_build.rst
@@ -325,6 +325,8 @@ template.
Caching Python Packages for Offline Installation
------------------------------------------------
+.. _docs-python-build-downloading-packages:
+
Downloading Packages
^^^^^^^^^^^^^^^^^^^^
The :ref:`module-pw_build-pw_python_venv` target adds an optional sub target
diff --git a/docs/style/commit_message.rst b/docs/style/commit_message.rst
new file mode 100644
index 000000000..5b4dbaff7
--- /dev/null
+++ b/docs/style/commit_message.rst
@@ -0,0 +1,296 @@
+.. _docs-pw-style-commit-message:
+
+====================
+Commit message style
+====================
+Pigweed commit message bodies and summaries are limited to 72 characters wide to
+improve readability, and should be prefixed with the name of the module that the
+commit is affecting. The commits should describe what is changed, and why. When
+writing long commit messages, consider whether the content should go in the
+documentation or code comments instead. For example:
+
+.. code-block:: none
+
+ pw_some_module: Short capitalized description
+
+ Details about the change here. Include a summary of what changed, and a clear
+ description of why the change is needed. Consider what parts of the commit
+ message are better suited for documentation or code.
+
+ - Added foo, to fix issue bar
+ - Improved speed of qux
+ - Refactored and extended qux's test suite
+
+-----------------------------
+Include both "what" and "why"
+-----------------------------
+It is important to include a "why" component in most commits. Sometimes, why is
+evident - for example, reducing memory usage, optimizing, or fixing a bug.
+Otherwise, err on the side of over-explaining why, not under-explaining why.
+
+When adding the "why" to a commit, also consider if that "why" content should go
+into the documentation or code comments.
+
+.. admonition:: **Yes**: Reasoning in commit message
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_sync_xrtos: Invoke deadlock detector
+
+ During locking, run the deadlock detector if there are enough cycles.
+ Though this costs performance, several bugs that went unnoticed would have
+ been caught by turning this on earlier. Take the small hit by default to
+ better catch issues going forward; see extended docs for details.
+
+.. admonition:: **No**: Reasoning omitted
+ :class: error
+
+ .. code-block:: none
+
+ pw_sync_xrtos: Invoke deadlock detector
+
+ During locking, run the deadlock detector if there are enough cycles.
+
+------------------
+Present imperative
+------------------
+Use present imperative style instead of passive descriptive.
+
+.. admonition:: **Yes**: Uses imperative style for subject and text.
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_something: Add foo and bar functions
+
+ This commit correctly uses imperative present-tense style.
+
+.. admonition:: **No**: Uses non-imperative style for subject and text.
+ :class: error
+
+ .. code-block:: none
+
+ pw_something: Adds more things
+
+ Use present tense imperative style for subjects and commit. The above
+ subject has a plural "Adds" which is incorrect; should be "Add".
+
+---------------------------------------
+Documentation instead of commit content
+---------------------------------------
+Consider whether any of the commit message content should go in the
+documentation or code comments and have the commit message reference it.
+Documentation and code comments are durable and discoverable; commit messages
+are rarely read after the change lands.
+
+.. admonition:: **Yes**: Created docs and comments
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_i2c: Add and enforce invariants
+
+ Precisely define the invariants around certain transaction states, and
+ extend the code to enforce them. See the newly extended documentation in
+ this change for details.
+
+.. admonition:: **No**: Important content only in commit message
+ :class: error
+
+ .. code-block:: none
+
+ pw_i2c: Add and enforce invariants
+
+ Add a new invariant such that before a transaction, the line must be high;
+ and after, the line must be low, due to XXX and YYY. Furthermore, users
+ should consider whether they could ever encounter XXX, and in that case
+ should ZZZ instead.
+
+---------------------------
+Lists instead of paragraphs
+---------------------------
+Use bulleted lists when multiple changes are in a single CL. Ideally, try to
+create smaller CLs so this isn't needed, but larger CLs are a practical reality.
+
+.. admonition:: **Yes**: Uses bulleted lists
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_complicated_module: Pre-work for refactor
+
+ Prepare for a bigger refactor by reworking some arguments before the larger
+ change. This change must land in downstream projects before the refactor to
+ enable a smooth transition to the new API.
+
+ - Add arguments to MyImportantClass::MyFunction
+ - Update MyImportantClass to handle precondition Y
+ - Add stub functions to be used during the transition
+
+.. admonition:: **No**: Long paragraph is hard to scan
+ :class: error
+
+ .. code-block:: none
+
+ pw_foo: Many things in a giant BWOT
+
+ This CL does A, B, and C. The commit message is a Big Wall Of Text
+ (BWOT), which we try to discourage in Pigweed. Also changes X and Y,
+ because Z and Q. Furthermore, in some cases, adds a new Foo (with Bar,
+ because we want to). Also refactors qux and quz.
+
+------------
+Subject line
+------------
+The subject line (first line of the commit) should take the form ``pw_<module>:
+Short description``. The module should not be capitalized, but the description
+should (unless the first word is an identifier). See below for the details.
+
+.. admonition:: **No**: Uses a non-standard ``[]`` to indicate module:
+ :class: error
+
+ .. code-block:: none
+
+ [pw_foo]: Do a thing
+
+.. admonition:: **No**: Has a period at the end of the subject
+ :class: error
+
+ .. code-block:: none
+
+ pw_bar: Do something great.
+
+.. admonition:: **No**: Puts extra stuff after the module which isn't a module.
+ :class: error
+
+ .. code-block:: none
+
+ pw_bar/byte_builder: Add more stuff to builder
+
+Multiple modules
+================
+Sometimes it is necessary to change code across multiple modules.
+
+#. **2-5 modules**: Use ``{}`` syntax shown below
+#. **>5 modules changed** - Omit the module names entirely
+#. **Changes mostly in one module** - If the commit mostly changes the
+ code in a single module with some small changes elsewhere, only list the
+ module receiving most of the content
+
+.. admonition:: **Yes**: Small number of modules affected; use {} syntax.
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_{foo, bar, baz}: Change something in a few places
+
+ When changes cross a few modules, include them with the syntax shown
+ above.
+
+.. admonition:: **Yes**: Many modules changed
+ :class: checkmark
+
+ .. code-block:: none
+
+ Change convention for how errors are handled
+
+ When changes cross many modules, skip the module name entirely.
+
+.. admonition:: **No**: Too many modules changed for subject
+ :class: error
+
+ .. code-block:: none
+
+ pw_{a, b, c, d, e, f, g, h, i, j}: Change convention for how errors are handled
+
+ When changes cross many modules, skip the module name entirely.
+
+Non-standard modules
+====================
+Most Pigweed modules follow the format of ``pw_<foo>``; however, some do not,
+such as targets. Targets are effectively modules, even though they're nested, so
+they get a ``/`` character.
+
+.. admonition:: **Yes**:
+ :class: checkmark
+
+ .. code-block:: none
+
+ targets/xyz123: Tweak support for XYZ's PQR
+
+ PQR is needed for reason ZXW; this adds a performant implementation.
+
+Capitalization
+==============
+The text after the ``:`` should be capitalized, provided the first word is not a
+case-sensitive symbol.
+
+.. admonition:: **No**: Doesn't capitalize the subject
+ :class: error
+
+ .. code-block:: none
+
+ pw_foo: do a thing
+
+ Above subject is incorrect, since it is a sentence style subject.
+
+.. admonition:: **Yes**: Doesn't capitalize the subject when subject's first
+ word is a lowercase identifier.
+ :class: checkmark
+
+ .. code-block:: none
+
+ pw_foo: std::unique_lock cleanup
+
+ This commit message demonstrates the subject when the subject has an
+ identifier for the first word. In that case, follow the identifier casing
+ instead of capitalizing.
+
+ However, imperative style subjects often have the identifier elsewhere in
+ the subject; for example:
+
+ .. code-block:: none
+
+ pw_foo: Improve use of std::unique_lock
+
+------
+Footer
+------
+We support a number of `git footers`_ in the commit message, such as ``Bug:
+123`` in the message below:
+
+.. code-block:: none
+
+ pw_something: Add foo and bar functions
+
+ Bug: 123
+
+You are encouraged to use the following footers when appropriate:
+
+* ``Bug``: Associates this commit with a bug (issue in our `bug tracker`_). The
+ bug will be automatically updated when the change is submitted. When a change
+ is relevant to more than one bug, include multiple ``Bug`` lines, like so:
+
+ .. code-block:: none
+
+ pw_something: Add foo and bar functions
+
+ Bug: 123
+ Bug: 456
+
+* ``Fixed`` or ``Fixes``: Like ``Bug``, but automatically closes the bug when
+ submitted.
+
+ .. code-block:: none
+
+ pw_something: Fix incorrect use of foo
+
+ Fixes: 123
+
+In addition, we support all of the `Chromium CQ footers`_, but those are
+relatively rarely useful.
+
+.. _bug tracker: https://bugs.chromium.org/p/pigweed/issues/list
+.. _Chromium CQ footers: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/infra/cq.md#options
+.. _git footers: https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/git-footers.html
diff --git a/docs/style/cpp.rst b/docs/style/cpp.rst
new file mode 100644
index 000000000..73b78f4f1
--- /dev/null
+++ b/docs/style/cpp.rst
@@ -0,0 +1,826 @@
+.. _docs-pw-style-cpp:
+
+=========
+C++ style
+=========
+The Pigweed C++ style guide is closely based on Google's external C++ Style
+Guide, which is found on the web at
+https://google.github.io/styleguide/cppguide.html. The Google C++ Style Guide
+applies to Pigweed except as described in this document.
+
+The Pigweed style guide only applies to Pigweed itself. It does not apply to
+projects that use Pigweed or to the third-party code included with Pigweed.
+Non-Pigweed code is free to use features restricted by Pigweed, such as dynamic
+memory allocation and the entirety of the C++ Standard Library.
+
+Recommendations in the :ref:`docs-embedded-cpp` are considered part of the
+Pigweed style guide, but are separated out since it covers more general
+embedded development beyond just C++ style.
+
+C++ standard
+============
+All Pigweed C++ code must compile with ``-std=c++17`` in Clang and GCC. C++20
+features may be used as long as the code still compiles unmodified with C++17.
+See ``pw_polyfill/language_feature_macros.h`` for macros that provide C++20
+features when supported.
+
+Compiler extensions should not be used unless wrapped in a macro or properly
+guarded in the preprocessor. See ``pw_processor/compiler.h`` for macros that
+wrap compiler-specific features.
+
+Automatic formatting
+====================
+Pigweed uses `clang-format <https://clang.llvm.org/docs/ClangFormat.html>`_ to
+automatically format Pigweed source code. A ``.clang-format`` configuration is
+provided with the Pigweed repository.
+
+Automatic formatting is essential to facilitate large-scale, automated changes
+in Pigweed. Therefore, all code in Pigweed is expected to be formatted with
+``clang-format`` prior to submission. Existing code may be reformatted at any
+time.
+
+If ``clang-format`` formats code in an undesirable or incorrect way, it can be
+disabled for the affected lines by adding ``// clang-format off``.
+``clang-format`` must then be re-enabled with a ``// clang-format on`` comment.
+
+.. code-block:: cpp
+
+ // clang-format off
+ constexpr int kMyMatrix[] = {
+ 100, 23, 0,
+ 0, 542, 38,
+ 1, 2, 201,
+ };
+ // clang-format on
+
+C Standard Library
+==================
+In C++ headers, always use the C++ versions of C Standard Library headers (e.g.
+``<cstdlib>`` instead of ``<stdlib.h>``). If the header is used by both C and
+C++ code, only the C header should be used.
+
+In C++ code, it is preferred to use C functions from the ``std`` namespace. For
+example, use ``std::memcpy`` instead of ``memcpy``. The C++ standard does not
+require the global namespace versions of the functions to be provided. Using
+``std::`` is more consistent with the C++ Standard Library and makes it easier
+to distinguish Pigweed functions from library functions.
+
+Within core Pigweed, do not use C standard library functions that allocate
+memory, such as ``std::malloc``. There are exceptions to this for when dynamic
+allocation is enabled for a system; Pigweed modules are allowed to add extra
+functionality when a heap is present; but this must be optional.
+
+C++ Standard Library
+====================
+Much of the C++ Standard Library is not a good fit for embedded software. Many
+of the classes and functions were not designed with the RAM, flash, and
+performance constraints of a microcontroller in mind. For example, simply
+adding the line ``#include <iostream>`` can increase the binary size by 150 KB!
+This is larger than many microcontrollers' entire internal storage.
+
+However, with appropriate caution, a limited set of standard C++ libraries can
+be used to great effect. Developers can leverage familiar, well-tested
+abstractions instead of writing their own. C++ library algorithms and classes
+can give equivalent or better performance than hand-written C code.
+
+A limited subset of the C++ Standard Library is permitted in Pigweed. To keep
+Pigweed small, flexible, and portable, functions that allocate dynamic memory
+must be avoided. Care must be exercised when using multiple instantiations of a
+template function, which can lead to code bloat.
+
+Permitted Headers
+-----------------
+.. admonition:: The following C++ Standard Library headers are always permitted:
+ :class: checkmark
+
+ * ``<array>``
+ * ``<complex>``
+ * ``<initializer_list>``
+ * ``<iterator>``
+ * ``<limits>``
+ * ``<optional>``
+ * ``<random>``
+ * ``<ratio>``
+ * ``<string_view>``
+ * ``<tuple>``
+ * ``<type_traits>``
+ * ``<utility>``
+ * ``<variant>``
+ * C Standard Library headers (``<c*>``)
+
+.. admonition:: With caution, parts of the following headers can be used:
+ :class: warning
+
+ * ``<algorithm>`` -- be wary of potential memory allocation
+ * ``<atomic>`` -- not all MCUs natively support atomic operations
+ * ``<bitset>`` -- conversions to or from strings are disallowed
+ * ``<functional>`` -- do **not** use ``std::function``; use
+ :ref:`module-pw_function`
+ * ``<mutex>`` -- can use ``std::lock_guard``, use :ref:`module-pw_sync` for
+ mutexes
+ * ``<new>`` -- for placement new
+ * ``<numeric>`` -- be wary of code size with multiple template instantiations
+
+.. admonition:: Never use any of these headers:
+ :class: error
+
+ * Dynamic containers (``<list>``, ``<map>``, ``<set>``, ``<vector>``, etc.)
+ * Streams (``<iostream>``, ``<ostream>``, ``<fstream>``, ``<sstream>`` etc.)
+ -- in some cases :ref:`module-pw_stream` can work instead
+ * ``<span>`` -- use :ref:`module-pw_span` instead. Downstream projects may
+ want to directly use ``std::span`` if it is available, but upstream must
+ use the ``pw::span`` version for compatability
+ * ``<string>`` -- can use :ref:`module-pw_string`
+ * ``<thread>`` -- can use :ref:`module-pw_thread`
+ * ``<future>`` -- eventually :ref:`module-pw_async` will offer this
+ * ``<exception>``, ``<stdexcept>`` -- no exceptions
+ * ``<memory>``, ``<scoped_allocator>`` -- no allocations
+ * ``<regex>``
+ * ``<valarray>``
+
+Headers not listed here should be carefully evaluated before they are used.
+
+These restrictions do not apply to third party code or to projects that use
+Pigweed.
+
+Combining C and C++
+===================
+Prefer to write C++ code over C code, using ``extern "C"`` for symbols that must
+have C linkage. ``extern "C"`` functions should be defined within C++
+namespaces to simplify referring to other code.
+
+C++ functions with no parameters do not include ``void`` in the parameter list.
+C functions with no parameters must include ``void``.
+
+.. code-block:: cpp
+
+ namespace pw {
+
+ bool ThisIsACppFunction() { return true; }
+
+ extern "C" int pw_ThisIsACFunction(void) { return -1; }
+
+ extern "C" {
+
+ int pw_ThisIsAlsoACFunction(void) {
+ return ThisIsACppFunction() ? 100 : 0;
+ }
+
+ } // extern "C"
+
+ } // namespace pw
+
+Comments
+========
+Prefer C++-style (``//``) comments over C-style comments (``/* */``). C-style
+comments should only be used for inline comments.
+
+.. code-block:: cpp
+
+ // Use C++-style comments, except where C-style comments are necessary.
+ // This returns a random number using an algorithm I found on the internet.
+ #define RANDOM_NUMBER() [] { \
+ return 4; /* chosen by fair dice roll */ \
+ }()
+
+Indent code in comments with two additional spaces, making a total of three
+spaces after the ``//``. All code blocks must begin and end with an empty
+comment line, even if the blank comment line is the last line in the block.
+
+.. code-block:: cpp
+
+ // Here is an example of code in comments.
+ //
+ // int indentation_spaces = 2;
+ // int total_spaces = 3;
+ //
+ // engine_1.thrust = RANDOM_NUMBER() * indentation_spaces + total_spaces;
+ //
+ bool SomeFunction();
+
+Control statements
+==================
+
+Loops and conditionals
+----------------------
+All loops and conditional statements must use braces, and be on their own line.
+
+.. admonition:: **Yes**: Always use braces for line conditionals and loops:
+ :class: checkmark
+
+ .. code-block:: cpp
+
+ while (SomeCondition()) {
+ x += 2;
+ }
+ if (OtherCondition()) {
+ DoTheThing();
+ }
+
+
+.. admonition:: **No**: Missing braces
+ :class: error
+
+ .. code-block:: cpp
+
+ while (SomeCondition())
+ x += 2;
+ if (OtherCondition())
+ DoTheThing();
+
+.. admonition:: **No**: Statement on same line as condition
+ :class: error
+
+ .. code-block:: cpp
+
+ while (SomeCondition()) { x += 2; }
+ if (OtherCondition()) { DoTheThing(); }
+
+
+The syntax ``while (true)`` is preferred over ``for (;;)`` for infinite loops.
+
+.. admonition:: **Yes**:
+ :class: checkmark
+
+ .. code-block:: cpp
+
+ while (true) {
+ DoSomethingForever();
+ }
+
+.. admonition:: **No**:
+ :class: error
+
+ .. code-block:: cpp
+
+ for (;;) {
+ DoSomethingForever();
+ }
+
+
+Prefer early exit with ``return`` and ``continue``
+--------------------------------------------------
+Prefer to exit early from functions and loops to simplify code. This is the
+same same conventions as `LLVM
+<https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code>`_.
+We find this approach is superior to the "one return per function" style for a
+multitude of reasons:
+
+* **Visually**, the code is easier to follow, and takes less horizontal screen
+ space.
+* It makes it clear what part of the code is the **"main business" versus "edge
+ case handling"**.
+* For **functions**, parameter checking is in its own section at the top of the
+ function, rather than scattered around in the fuction body.
+* For **loops**, element checking is in its own section at the top of the loop,
+ rather than scattered around in the loop body.
+* Commit **deltas are simpler to follow** in code reviews; since adding a new
+ parameter check or loop element condition doesn't cause an indentation change
+ in the rest of the function.
+
+The guidance applies in two cases:
+
+* **Function early exit** - Early exits are for function parameter checking
+ and edge case checking at the top. The main functionality follows.
+* **Loop early exit** - Early exits in loops are for skipping an iteration
+ due to some edge case with an item getting iterated over. Loops may also
+ contain function exits, which should be structured the same way (see example
+ below).
+
+.. admonition:: **Yes**: Exit early from functions; keeping the main handling
+ at the bottom and de-dentend.
+ :class: checkmark
+
+ .. code-block:: cpp
+
+ Status DoSomething(Parameter parameter) {
+ // Parameter validation first; detecting incoming use errors.
+ PW_CHECK_INT_EQ(parameter.property(), 3, "Programmer error: frobnitz");
+
+ // Error case: Not in correct state.
+ if (parameter.other() == MyEnum::kBrokenState) {
+ LOG_ERROR("Device in strange state: %s", parametr.state_str());
+ return Status::InvalidPrecondition();
+ }
+
+ // Error case: Not in low power mode; shouldn't do anything.
+ if (parameter.power() != MyEnum::kLowPower) {
+ LOG_ERROR("Not in low power mode");
+ return Status::InvalidPrecondition();
+ }
+
+ // Main business for the function here.
+ MainBody();
+ MoreMainBodyStuff();
+ }
+
+.. admonition:: **No**: Main body of function is buried and right creeping.
+ Even though this is shorter than the version preferred by Pigweed due to
+ factoring the return statement, the logical structure is less obvious. A
+ function in Pigweed containing **nested conditionals indicates that
+ something complicated is happening with the flow**; otherwise it would have
+ the early bail structure; so pay close attention.
+ :class: error
+
+ .. code-block:: cpp
+
+ Status DoSomething(Parameter parameter) {
+ // Parameter validation first; detecting incoming use errors.
+ PW_CHECK_INT_EQ(parameter.property(), 3, "Programmer error: frobnitz");
+
+ // Error case: Not in correct state.
+ if (parameter.other() != MyEnum::kBrokenState) {
+ // Error case: Not in low power mode; shouldn't do anything.
+ if (parameter.power() == MyEnum::kLowPower) {
+ // Main business for the function here.
+ MainBody();
+ MoreMainBodyStuff();
+ } else {
+ LOG_ERROR("Not in low power mode");
+ }
+ } else {
+ LOG_ERROR("Device in strange state: %s", parametr.state_str());
+ }
+ return Status::InvalidPrecondition();
+ }
+
+.. admonition:: **Yes**: Bail early from loops; keeping the main handling at
+ the bottom and de-dentend.
+ :class: checkmark
+
+ .. code-block:: cpp
+
+ for (int i = 0; i < LoopSize(); ++i) {
+ // Early skip of item based on edge condition.
+ if (!CommonCase()) {
+ continue;
+ }
+ // Early exit of function based on error case.
+ int my_measurement = GetSomeMeasurement();
+ if (my_measurement < 10) {
+ LOG_ERROR("Found something strange; bailing");
+ return Status::Unknown();
+ }
+
+ // Main body of the loop.
+ ProcessItem(my_items[i], my_measurement);
+ ProcessItemMore(my_items[i], my_measurement, other_details);
+ ...
+ }
+
+.. admonition:: **No**: Right-creeping code with the main body buried inside
+ some nested conditional. This makes it harder to understand what is the
+ main purpose of the loop versus what is edge case handling.
+ :class: error
+
+ .. code-block:: cpp
+
+ for (int i = 0; i < LoopSize(); ++i) {
+ if (CommonCase()) {
+ int my_measurement = GetSomeMeasurement();
+ if (my_measurement >= 10) {
+ // Main body of the loop.
+ ProcessItem(my_items[i], my_measurement);
+ ProcessItemMore(my_items[i], my_measurement, other_details);
+ ...
+ } else {
+ LOG_ERROR("Found something strange; bailing");
+ return Status::Unknown();
+ }
+ }
+ }
+
+There are cases where this structure doesn't work, and in those cases, it is
+fine to structure the code differently.
+
+No ``else`` after ``return`` or ``continue``
+--------------------------------------------
+Do not put unnecessary ``} else {`` blocks after blocks that terminate with a
+return, since this causes unnecessary rightward indentation creep. This
+guidance pairs with the preference for early exits to reduce code duplication
+and standardize loop/function structure.
+
+.. admonition:: **Yes**: No else after return or continue
+ :class: checkmark
+
+ .. code-block:: cpp
+
+ // Note lack of else block due to return.
+ if (Failure()) {
+ DoTheThing();
+ return Status::ResourceExausted();
+ }
+
+ // Note lack of else block due to continue.
+ while (MyCondition()) {
+ if (SomeEarlyBail()) {
+ continue;
+ }
+ // Main handling of item
+ ...
+ }
+
+ DoOtherThing();
+ return OkStatus();
+
+.. admonition:: **No**: Else after return needlessly creeps right
+ :class: error
+
+ .. code-block:: cpp
+
+ if (Failure()) {
+ DoTheThing();
+ return Status::ResourceExausted();
+ } else {
+ while (MyCondition()) {
+ if (SomeEarlyBail()) {
+ continue;
+ } else {
+ // Main handling of item
+ ...
+ }
+ }
+ DoOtherThing();
+ return OkStatus();
+ }
+
+Include guards
+==============
+The first non-comment line of every header file must be ``#pragma once``. Do
+not use traditional macro include guards. The ``#pragma once`` should come
+directly after the Pigweed copyright block, with no blank line, followed by a
+blank, like this:
+
+.. code-block:: cpp
+
+ // Copyright 2021 The Pigweed Authors
+ //
+ // Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ // use this file except in compliance with the License. You may obtain a copy of
+ // the License at
+ //
+ // https://www.apache.org/licenses/LICENSE-2.0
+ //
+ // Unless required by applicable law or agreed to in writing, software
+ // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ // License for the specific language governing permissions and limitations under
+ // the License.
+ #pragma once
+
+ // Header file-level comment goes here...
+
+Memory allocation
+=================
+Dynamic memory allocation can be problematic. Heap allocations and deallocations
+occupy valuable CPU cycles. Memory usage becomes nondeterministic, which can
+result in a system crashing without a clear culprit.
+
+To keep Pigweed portable, core Pigweed code is not permitted to dynamically
+(heap) allocate memory, such as with ``malloc`` or ``new``. All memory should be
+allocated with automatic (stack) or static (global) storage duration. Pigweed
+must not use C++ libraries that use dynamic allocation.
+
+Projects that use Pigweed are free to use dynamic allocation, provided they
+have selected a target that enables the heap.
+
+Naming
+======
+Entities shall be named according to the `Google style guide
+<https://google.github.io/styleguide/cppguide.html>`_, with the following
+additional requirements.
+
+C++ code
+--------
+* All Pigweed C++ code must be in the ``pw`` namespace. Namespaces for modules
+ should be nested under ``pw``. For example, ``pw::string::Format()``.
+* Whenever possible, private code should be in a source (.cc) file and placed in
+ anonymous namespace nested under ``pw``.
+* If private code must be exposed in a header file, it must be in a namespace
+ nested under ``pw``. The namespace may be named for its subsystem or use a
+ name that designates it as private, such as ``internal``.
+* Template arguments for non-type names (e.g. ``template <int kFooBar>``) should
+ follow the constexpr and const variable Google naming convention, which means
+ k prefixed camel case (e.g. ``kCamelCase``). This matches the Google C++
+ style for variable naming, however the wording in the official style guide
+ isn't explicit for template arguments and could be interpreted to use
+ ``foo_bar`` style naming. For consistency with other variables whose value is
+ always fixed for the duration of the program, the naming convention is
+ ``kCamelCase``, and so that is the style we use in Pigweed.
+* Trivial membor accessors should be named with ``snake_case()``. The Google
+ C++ style allows either ``snake_case()`` or ``CapsCase()``, but Pigweed
+ always uses ``snake_case()``.
+* Abstract base classes should be named generically, with derived types named
+ specifically. For example, ``Stream`` is an abstract base, and
+ ``SocketStream`` and ``StdioStream`` are an implementations of that
+ interface. Any prefix or postfix indicating whether something is abstract or
+ concrete is not permitted; for example, ``IStream`` or ``SocketStreamImpl``
+ are both not permitted. These pre-/post-fixes add additional visual noise and
+ are irrelevant to consumers of these interfaces.
+
+C code
+------
+In general, C symbols should be prefixed with the module name. If the symbol is
+not associated with a module, use just ``pw`` as the module name. Facade
+backends may chose to prefix symbols with the facade's name to help reduce the
+length of the prefix.
+
+* Public names used by C code must be prefixed with the module name (e.g.
+ ``pw_tokenizer_*``).
+* If private code must be exposed in a header, private names used by C code must
+ be prefixed with an underscore followed by the module name (e.g.
+ ``_pw_assert_*``).
+* Avoid writing C source (.c) files in Pigweed. Prefer to write C++ code with C
+ linkage using ``extern "C"``. Within C source, private C functions and
+ variables must be named with the ``_pw_my_module_*`` prefix and should be
+ declared ``static`` whenever possible; for example,
+ ``_pw_my_module_MyPrivateFunction``.
+* The C prefix rules apply to
+
+ * C functions (``int pw_foo_FunctionName(void);``),
+ * variables used by C code (``int pw_foo_variable_name;``),
+ * constant variables used by C code (``const int pw_foo_kConstantName;``),
+ * structs used by C code (``typedef struct {} pw_foo_StructName;``), and
+ * all of the above for ``extern "C"`` names in C++ code.
+
+ The prefix does not apply to struct members, which use normal Google style.
+
+Preprocessor macros
+-------------------
+* Public Pigweed macros must be prefixed with the module name (e.g.
+ ``PW_MY_MODULE_*``).
+* Private Pigweed macros must be prefixed with an underscore followed by the
+ module name (e.g. ``_PW_MY_MODULE_*``). (This style may change, see
+ `b/234886184 <https://issuetracker.google.com/issues/234886184>`_).
+
+**Example**
+
+.. code-block:: cpp
+
+ namespace pw::my_module {
+ namespace nested_namespace {
+
+ // C++ names (types, variables, functions) must be in the pw namespace.
+ // They are named according to the Google style guide.
+ constexpr int kGlobalConstant = 123;
+
+ // Prefer using functions over extern global variables.
+ extern int global_variable;
+
+ class Class {};
+
+ void Function();
+
+ extern "C" {
+
+ // Public Pigweed code used from C must be prefixed with pw_.
+ extern const int pw_my_module_kGlobalConstant;
+
+ extern int pw_my_module_global_variable;
+
+ void pw_my_module_Function(void);
+
+ typedef struct {
+ int member_variable;
+ } pw_my_module_Struct;
+
+ // Private Pigweed code used from C must be prefixed with _pw_.
+ extern const int _pw_my_module_kPrivateGlobalConstant;
+
+ extern int _pw_my_module_private_global_variable;
+
+ void _pw_my_module_PrivateFunction(void);
+
+ typedef struct {
+ int member_variable;
+ } _pw_my_module_PrivateStruct;
+
+ } // extern "C"
+
+ // Public macros must be prefixed with PW_.
+ #define PW_MY_MODULE_PUBLIC_MACRO(arg) arg
+
+ // Private macros must be prefixed with _PW_.
+ #define _PW_MY_MODULE_PRIVATE_MACRO(arg) arg
+
+ } // namespace nested_namespace
+ } // namespace pw::my_module
+
+See :ref:`docs-pw-style-macros` for details about macro usage.
+
+Namespace scope formatting
+==========================
+All non-indented blocks (namespaces, ``extern "C"`` blocks, and preprocessor
+conditionals) must have a comment on their closing line with the
+contents of the starting line.
+
+All nested namespaces should be declared together with no blank lines between
+them.
+
+.. code-block:: cpp
+
+ #include "some/header.h"
+
+ namespace pw::nested {
+ namespace {
+
+ constexpr int kAnonConstantGoesHere = 0;
+
+ } // namespace
+
+ namespace other {
+
+ const char* SomeClass::yes = "no";
+
+ bool ThisIsAFunction() {
+ #if PW_CONFIG_IS_SET
+ return true;
+ #else
+ return false;
+ #endif // PW_CONFIG_IS_SET
+ }
+
+ extern "C" {
+
+ const int pw_kSomeConstant = 10;
+ int pw_some_global_variable = 600;
+
+ void pw_CFunction() { ... }
+
+ } // extern "C"
+
+ } // namespace
+ } // namespace pw::nested
+
+Using directives for literals
+=============================
+`Using-directives
+<https://en.cppreference.com/w/cpp/language/namespace#Using-directives>`_ (e.g.
+``using namespace ...``) are permitted in implementation files only for the
+purposes of importing literals such as ``std::chrono_literals`` or
+``pw::bytes::unit_literals``. Namespaces that contain any symbols other than
+literals are not permitted in a using-directive. This guidance also has no
+impact on `using-declarations
+<https://en.cppreference.com/w/cpp/language/namespace#Using-declarations>`_
+(e.g. ``using foo::Bar;``).
+
+Rationale: Literals improve code readability, making units clearer at the point
+of definition.
+
+.. code-block:: cpp
+
+ using namespace std::chrono; // Not allowed
+ using namespace std::literals::chrono_literals; // Allowed
+
+ constexpr std::chrono::duration delay = 250ms;
+
+Pointers and references
+=======================
+For pointer and reference types, place the asterisk or ampersand next to the
+type.
+
+.. code-block:: cpp
+
+ int* const number = &that_thing;
+ constexpr const char* kString = "theory!"
+
+ bool FindTheOneRing(const Region& where_to_look) { ... }
+
+Prefer storing references over storing pointers. Pointers are required when the
+pointer can change its target or may be ``nullptr``. Otherwise, a reference or
+const reference should be used.
+
+.. _docs-pw-style-macros:
+
+Preprocessor macros
+===================
+Macros should only be used when they significantly improve upon the C++ code
+they replace. Macros should make code more readable, robust, and safe, or
+provide features not possible with standard C++, such as stringification, line
+number capturing, or conditional compilation. When possible, use C++ constructs
+like constexpr variables in place of macros. Never use macros as constants,
+except when a string literal is needed or the value must be used by C code.
+
+When macros are needed, the macros should be accompanied with extensive tests
+to ensure the macros are hard to use wrong.
+
+Stand-alone statement macros
+----------------------------
+Macros that are standalone statements must require the caller to terminate the
+macro invocation with a semicolon (see `Swalling the Semicolon
+<https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html>`_). For
+example, the following does *not* conform to Pigweed's macro style:
+
+.. code-block:: cpp
+
+ // BAD! Definition has built-in semicolon.
+ #define PW_LOG_IF_BAD(mj) \
+ CallSomeFunction(mj);
+
+ // BAD! Compiles without error; semicolon is missing.
+ PW_LOG_IF_BAD("foo")
+
+Here's how to do this instead:
+
+.. code-block:: cpp
+
+ // GOOD; requires semicolon to compile.
+ #define PW_LOG_IF_BAD(mj) \
+ CallSomeFunction(mj)
+
+ // GOOD; fails to compile due to lacking semicolon.
+ PW_LOG_IF_BAD("foo")
+
+For macros in function scope that do not already require a semicolon, the
+contents can be placed in a ``do { ... } while (0)`` loop.
+
+.. code-block:: cpp
+
+ #define PW_LOG_IF_BAD(mj) \
+ do { \
+ if (mj.Bad()) { \
+ Log(#mj " is bad") \
+ } \
+ } while (0)
+
+Standalone macros at global scope that do not already require a semicolon can
+add a ``static_assert`` declaration statement as their last line.
+
+.. code-block:: cpp
+
+ #define PW_NEAT_THING(thing) \
+ bool IsNeat_##thing() { return true; } \
+ static_assert(true, "Macros must be terminated with a semicolon")
+
+Private macros in public headers
+--------------------------------
+Private macros in public headers must be prefixed with ``_PW_``, even if they
+are undefined after use; this prevents collisions with downstream users. For
+example:
+
+.. code-block:: cpp
+
+ #define _PW_MY_SPECIAL_MACRO(op) ...
+ ...
+ // Code that uses _PW_MY_SPECIAL_MACRO()
+ ...
+ #undef _PW_MY_SPECIAL_MACRO
+
+Macros in private implementation files (.cc)
+--------------------------------------------
+Macros within .cc files that should only be used within one file should be
+undefined after their last use; for example:
+
+.. code-block:: cpp
+
+ #define DEFINE_OPERATOR(op) \
+ T operator ## op(T x, T y) { return x op y; } \
+ static_assert(true, "Macros must be terminated with a semicolon") \
+
+ DEFINE_OPERATOR(+);
+ DEFINE_OPERATOR(-);
+ DEFINE_OPERATOR(/);
+ DEFINE_OPERATOR(*);
+
+ #undef DEFINE_OPERATOR
+
+Preprocessor conditional statements
+===================================
+When using macros for conditional compilation, prefer to use ``#if`` over
+``#ifdef``. This checks the value of the macro rather than whether it exists.
+
+* ``#if`` handles undefined macros equivalently to ``#ifdef``. Undefined
+ macros expand to 0 in preprocessor conditional statements.
+* ``#if`` evaluates false for macros defined as 0, while ``#ifdef`` evaluates
+ true.
+* Macros defined using compiler flags have a default value of 1 in GCC and
+ Clang, so they work equivalently for ``#if`` and ``#ifdef``.
+* Macros defined to an empty statement cause compile-time errors in ``#if``
+ statements, which avoids ambiguity about how the macro should be used.
+
+All ``#endif`` statements should be commented with the expression from their
+corresponding ``#if``. Do not indent within preprocessor conditional statements.
+
+.. code-block:: cpp
+
+ #if USE_64_BIT_WORD
+ using Word = uint64_t;
+ #else
+ using Word = uint32_t;
+ #endif // USE_64_BIT_WORD
+
+Unsigned integers
+=================
+Unsigned integers are permitted in Pigweed. Aim for consistency with existing
+code and the C++ Standard Library. Be very careful mixing signed and unsigned
+integers.
+
+Features not in the C++ standard
+================================
+Avoid features not available in standard C++. This includes compiler extensions
+and features from other standards like POSIX.
+
+For example, use ``ptrdiff_t`` instead of POSIX's ``ssize_t``, unless
+interacting with a POSIX API in intentionally non-portable code. Never use
+POSIX functions with suitable standard or Pigweed alternatives, such as
+``strnlen`` (use ``pw::string::NullTerminatedLength`` instead).
diff --git a/docs/style/doxygen.rst b/docs/style/doxygen.rst
new file mode 100644
index 000000000..d36bcf119
--- /dev/null
+++ b/docs/style/doxygen.rst
@@ -0,0 +1,253 @@
+.. _docs-pw-style-doxygen:
+
+===========================
+Doxygen documentation style
+===========================
+Doxygen comments in C, C++, and Java are surfaced in Sphinx using `Breathe
+<https://breathe.readthedocs.io/en/latest/index.html>`_.
+
+.. note::
+
+ Sources with doxygen comment blocks must be added to the
+ ``_doxygen_input_files`` list in ``//docs/BUILD.gn`` to be processed.
+
+Breathe provides various `directives
+<https://breathe.readthedocs.io/en/latest/directives.html>`_ for bringing
+Doxygen comments into Sphinx. These include the following:
+
+- `doxygenfile
+ <https://breathe.readthedocs.io/en/latest/directives.html#doxygenfile>`_ --
+ Documents a source file. May limit to specific types of symbols with
+ ``:sections:``.
+
+ .. code-block:: rst
+
+ .. doxygenfile:: pw_rpc/internal/config.h
+ :sections: define, func
+
+- `doxygenclass
+ <https://breathe.readthedocs.io/en/latest/directives.html#doxygenclass>`_ --
+ Documents a class and optionally its members.
+
+ .. code-block:: rst
+
+ .. doxygenclass:: pw::sync::BinarySemaphore
+ :members:
+
+- `doxygentypedef
+ <https://breathe.readthedocs.io/en/latest/directives.html#doxygentypedef>`_ --
+ Documents an alias (``typedef`` or ``using`` statement).
+
+ .. code-block:: rst
+
+ .. doxygentypedef:: pw::Function
+
+- `doxygenfunction
+ <https://breathe.readthedocs.io/en/latest/directives.html#doxygenfunction>`_ --
+ Documents a source file. Can be filtered to limit to specific types of
+ symbols.
+
+ .. code-block:: rst
+
+ .. doxygenfunction:: pw::tokenizer::EncodeArgs
+
+- `doxygendefine
+ <https://breathe.readthedocs.io/en/latest/directives.html#doxygendefine>`_ --
+ Documents a preprocessor macro.
+
+ .. code-block:: rst
+
+ .. doxygendefine:: PW_TOKENIZE_STRING
+
+.. admonition:: See also
+
+ `All Breathe directives for use in RST files <https://breathe.readthedocs.io/en/latest/directives.html>`_
+
+Example Doxygen Comment Block
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Start a Doxygen comment block using ``///`` (three forward slashes).
+
+.. code-block:: cpp
+
+ /// This is the documentation comment for the `PW_LOCK_RETURNED()` macro. It
+ /// describes how to use the macro.
+ ///
+ /// Doxygen comments can refer to other symbols using Sphinx cross
+ /// references. For example, @cpp_class{pw::InlineBasicString}, which is
+ /// shorthand for @crossref{cpp,class,pw::InlineBasicString}, links to a C++
+ /// class. @crossref{py,func,pw_tokenizer.proto.detokenize_fields} links to a
+ /// Python function.
+ ///
+ /// @param[out] dest The memory area to copy to.
+ /// @param[in] src The memory area to copy from.
+ /// @param[in] n The number of bytes to copy
+ ///
+ /// @retval OK KVS successfully initialized.
+ /// @retval DATA_LOSS KVS initialized and is usable, but contains corrupt data.
+ /// @retval UNKNOWN Unknown error. KVS is not initialized.
+ ///
+ /// @rst
+ /// The ``@rst`` and ``@endrst`` commands form a block block of
+ /// reStructuredText that is rendered in Sphinx.
+ ///
+ /// .. warning::
+ /// this is a warning admonition
+ ///
+ /// .. code-block:: cpp
+ ///
+ /// void release(ptrdiff_t update = 1);
+ /// @endrst
+ ///
+ /// Example code block using Doxygen markup below. To override the language
+ /// use `@code{.cpp}`
+ ///
+ /// @code
+ /// class Foo {
+ /// public:
+ /// Mutex* mu() PW_LOCK_RETURNED(mu) { return &mu; }
+ ///
+ /// private:
+ /// Mutex mu;
+ /// };
+ /// @endcode
+ ///
+ /// @b The first word in this sentence is bold (The).
+ ///
+ #define PW_LOCK_RETURNED(x) __attribute__((lock_returned(x)))
+
+Doxygen Syntax
+^^^^^^^^^^^^^^
+Pigweed prefers to use RST wherever possible, but there are a few Doxygen
+syntatic elements that may be needed.
+
+Common Doxygen commands for use within a comment block:
+
+- ``@rst`` To start a reStructuredText block. This is a custom alias for
+ ``\verbatim embed:rst:leading-asterisk``. This must be paired with
+ ``@endrst``.
+- `@code <https://www.doxygen.nl/manual/commands.html#cmdcode>`_ Start a code
+ block. This must be paired with ``@endcode``.
+- `@param <https://www.doxygen.nl/manual/commands.html#cmdparam>`_ Document
+ parameters, this may be repeated.
+- `@pre <https://www.doxygen.nl/manual/commands.html#cmdpre>`_ Starts a
+ paragraph where the precondition of an entity can be described.
+- `@post <https://www.doxygen.nl/manual/commands.html#cmdpost>`_ Starts a
+ paragraph where the postcondition of an entity can be described.
+- `@return <https://www.doxygen.nl/manual/commands.html#cmdreturn>`_ Single
+ paragraph to describe return value(s).
+- `@retval <https://www.doxygen.nl/manual/commands.html#cmdretval>`_ Document
+ return values by name. This is rendered as a definition list.
+- `@note <https://www.doxygen.nl/manual/commands.html#cmdnote>`_ Add a note
+ admonition to the end of documentation.
+- `@b <https://www.doxygen.nl/manual/commands.html#cmdb>`_ To bold one word.
+
+.. tip:
+
+ Did you add Doxygen comments and now your build is failing because Doxygen
+ says it can't find the class you decorated? Make sure your ``@code`` blocks
+ are paired with ``@endcode`` blocks and your ``@rst`` blocks are paired
+ with ``@endrst`` blocks!
+
+Doxygen provides `structural commands
+<https://doxygen.nl/manual/docblocks.html#structuralcommands>`_ that associate a
+comment block with a particular symbol. Example of these include ``@class``,
+``@struct``, ``@def``, ``@fn``, and ``@file``. Do not use these unless
+necessary, since they are redundant with the declarations themselves.
+
+One case where structural commands are necessary is when a single comment block
+describes multiple symbols. To group multiple symbols into a single comment
+block, include a structural commands for each symbol on its own line. For
+example, the following comment documents two macros:
+
+.. code-block:: cpp
+
+ /// @def PW_ASSERT_EXCLUSIVE_LOCK
+ /// @def PW_ASSERT_SHARED_LOCK
+ ///
+ /// Documents functions that dynamically check to see if a lock is held, and
+ /// fail if it is not held.
+
+.. seealso:: `All Doxygen commands <https://www.doxygen.nl/manual/commands.html>`_
+
+Cross-references
+^^^^^^^^^^^^^^^^
+Pigweed provides Doxygen aliases for creating Sphinx cross references within
+Doxygen comments. These should be used when referring to other symbols, such as
+functions, classes, or macros.
+
+.. inclusive-language: disable
+
+The basic alias is ``@crossref``, which supports any `Sphinx domain
+<https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html>`_.
+For readability, aliases for commonnly used types are provided.
+
+.. inclusive-language: enable
+
+- ``@crossref{domain,type,identifier}`` Inserts a cross reference of any type in
+ any Sphinx domain. For example, ``@crossref{c,func,foo}`` is equivalent to
+ ``:c:func:`foo``` and links to the documentation for the C function ``foo``,
+ if it exists.
+- ``@c_macro{identifier}`` Equivalent to ``:c:macro:`identifier```.
+- ``@cpp_func{identifier}`` Equivalent to ``:cpp:func:`identifier```.
+- ``@cpp_class{identifier}`` Equivalent to ``:cpp:class:`identifier```.
+- ``@cpp_type{identifier}`` Equivalent to ``:cpp:type:`identifier```.
+
+.. note::
+
+ Use the `@` aliases described above for all cross references. Doxygen
+ provides other methods for cross references, but Sphinx cross references
+ offer several advantages:
+
+ - Sphinx cross references work for all identifiers known to Sphinx, including
+ those documented with e.g. ``.. cpp:class::`` or extracted from Python.
+ Doxygen references can only refer to identifiers known to Doxygen.
+ - Sphinx cross references always use consistent formatting. Doxygen cross
+ references sometimes render as plain text instead of code-style monospace,
+ or include ``()`` in macros that shouldn't have them.
+ - Sphinx cross references can refer to symbols that have not yet been
+ documented. They will be formatted correctly and become links once the
+ symbols are documented.
+ - Using Sphinx cross references in Doxygen comments makes cross reference
+ syntax more consistent within Doxygen comments and between RST and
+ Doxygen.
+
+Create cross reference links elsewhere in the document to symbols documented
+with Doxygen using standard Sphinx cross references, such as ``:cpp:class:`` and
+``:cpp:func:``.
+
+.. code-block:: rst
+
+ - :cpp:class:`pw::sync::BinarySemaphore::BinarySemaphore`
+ - :cpp:func:`pw::sync::BinarySemaphore::try_acquire`
+
+.. seealso::
+ - `C++ cross reference link syntax`_
+ - `C cross reference link syntax`_
+ - `Python cross reference link syntax`_
+
+.. inclusive-language: disable
+
+.. _C++ cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing
+.. _C cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-c-constructs
+.. _Python cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects
+
+.. inclusive-language: enable
+
+Status codes in Doxygen comments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Use the following syntax when referring to ``pw_status`` codes in Doxygen
+comments:
+
+.. code-block::
+
+ @pw_status{YOUR_STATUS_CODE_HERE}
+
+Replace ``YOUR_STATUS_CODE_HERE`` with a valid ``pw_status`` code.
+
+This syntax ensures that Doxygen links back to the status code's
+reference documentation in :ref:`module-pw_status`.
+
+.. note::
+
+ The guidance in this section only applies to Doxygen comments in C++ header
+ files.
diff --git a/docs/style/sphinx.rst b/docs/style/sphinx.rst
new file mode 100644
index 000000000..06a6a82ed
--- /dev/null
+++ b/docs/style/sphinx.rst
@@ -0,0 +1,632 @@
+.. _docs-pw-style-sphinx:
+
+==========================
+Sphinx documentation style
+==========================
+.. note::
+
+ Pigweed's documentation style guide came after much of the documentation was
+ written, so Pigweed's docs don't yet 100% conform to this style guide. When
+ updating docs, please update them to match the style guide.
+
+Pigweed documentation is written using the `reStructuredText
+<https://docutils.sourceforge.io/rst.html>`_ markup language and processed by
+`Sphinx`_. We use the `Furo theme <https://github.com/pradyunsg/furo>`_ along
+with the `sphinx-design <https://sphinx-design.readthedocs.io/en/furo-theme/>`_
+extension.
+
+.. _Sphinx: https://www.sphinx-doc.org/
+
+Syntax Reference Links
+======================
+.. admonition:: See also
+ :class: seealso
+
+ - `reStructuredText Primer`_
+
+ - `reStructuredText Directives <https://docutils.sourceforge.io/docs/ref/rst/directives.html>`_
+
+ - `Furo Reference <https://pradyunsg.me/furo/reference/>`_
+
+ - `Sphinx-design Reference <https://sphinx-design.readthedocs.io/en/furo-theme/>`_
+
+ReST is flexible, supporting formatting the same logical document in a few ways
+(for example headings, blank lines). Pigweed has the following restrictions to
+make our documentation consistent.
+
+.. inclusive-language: disable
+
+.. _reStructuredText Primer: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
+
+.. inclusive-language: enable
+
+Headings
+========
+Use headings according to the following hierarchy, with the shown characters
+for the ReST heading syntax.
+
+.. code-block:: rst
+
+ ==================================
+ Document Title: Two Bars of Equals
+ ==================================
+ Document titles use equals ("====="), above and below. Capitalize the words
+ in the title, except for 'a', 'of', and 'the'.
+
+ ---------------------------
+ Major Sections Within a Doc
+ ---------------------------
+ Major sections use hyphens ("----"), above and below. Capitalize the words in
+ the title, except for 'a', 'of', and 'the'.
+
+ Heading 1 - For Sections Within a Doc
+ =====================================
+ These should be title cased. Use a single equals bar ("====").
+
+ Heading 2 - for subsections
+ ---------------------------
+ Subsections use hyphens ("----"). In many cases, these headings may be
+ sentence-like. In those cases, only the first letter should be capitalized.
+ For example, FAQ subsections would have a title with "Why does the X do the
+ Y?"; note the sentence capitalization (but not title capitalization).
+
+ Heading 3 - for subsubsections
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ Use the caret symbol ("^^^^") for subsubsections.
+
+ Note: Generally don't go beyond heading 3.
+
+ Heading 4 - for subsubsubsections
+ .................................
+ Don't use this heading level, but if you must, use period characters
+ ("....") for the heading.
+
+Do not put blank lines after headings.
+--------------------------------------
+.. admonition:: **Yes**: No blank after heading
+ :class: checkmark
+
+ .. code-block:: rst
+
+ Here is a heading
+ -----------------
+ Note that there is no blank line after the heading separator!
+
+.. admonition:: **No**: Unnecessary blank line
+ :class: error
+
+ .. code-block:: rst
+
+ Here is a heading
+ -----------------
+
+ There is a totally unnecessary blank line above this one. Don't do this.
+
+Do not put multiple blank lines before a heading.
+-------------------------------------------------
+.. admonition:: **Yes**: Just one blank after section content before the next heading
+ :class: checkmark
+
+ .. code-block:: rst
+
+ There is some text here in the section before the next. It's just here to
+ illustrate the spacing standard. Note that there is just one blank line
+ after this paragraph.
+
+ Just one blank!
+ ---------------
+ There is just one blank line before the heading.
+
+.. admonition:: **No**: Extra blank lines
+ :class: error
+
+ .. code-block:: rst
+
+ There is some text here in the section before the next. It's just here to
+ illustrate the spacing standard. Note that there are too many blank lines
+ after this paragraph; there should be just one.
+
+
+
+ Too many blanks
+ ---------------
+ There are too many blanks before the heading for this section.
+
+Directives
+==========
+Indent directives 3 spaces; and put a blank line between the directive and the
+content. This aligns the directive content with the directive name.
+
+.. admonition:: **Yes**: Three space indent for directives; and nested
+ :class: checkmark
+
+ .. code-block:: none
+
+ Here is a paragraph that has some content. After this content is a
+ directive.
+
+ .. my_directive::
+
+ Note that this line's start aligns with the "m" above. The 3-space
+ alignment accounts for the ".. " prefix for directives, to vertically
+ align the directive name with the content.
+
+ This indentation must continue for nested directives.
+
+ .. nested_directive::
+
+ Here is some nested directive content.
+
+.. admonition:: **No**: One space, two spaces, four spaces, or other indents
+ for directives
+ :class: error
+
+ .. code-block:: none
+
+ Here is a paragraph with some content.
+
+ .. my_directive::
+
+ The indentation here is incorrect! It's one space short; doesn't align
+ with the directive name above.
+
+ .. nested_directive::
+
+ This isn't indented correctly either; it's too much (4 spaces).
+
+.. admonition:: **No**: Missing blank between directive and content.
+ :class: error
+
+ .. code-block:: none
+
+ Here is a paragraph with some content.
+
+ .. my_directive::
+ Note the lack of blank line above here.
+
+Tables
+======
+Consider using ``.. list-table::`` syntax, which is more maintainable and
+easier to edit for complex tables (`details
+<https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table>`_).
+
+Code Snippets
+=============
+Use code blocks from actual source code files wherever possible. This helps keep
+documentation fresh and removes duplicate code examples. There are a few ways to
+do this with Sphinx.
+
+The `literalinclude`_ directive creates a code blocks from source files. Entire
+files can be included or a just a subsection. The best way to do this is with
+the ``:start-after:`` and ``:end-before:`` options.
+
+Example:
+
+.. card::
+
+ Documentation Source (``.rst`` file)
+ ^^^
+
+ .. code-block:: rst
+
+ .. literalinclude:: run_doxygen.py
+ :start-after: [doxygen-environment-variables]
+ :end-before: [doxygen-environment-variables]
+
+.. card::
+
+ Source File
+ ^^^
+
+ .. code-block::
+
+ # DOCSTAG: [doxygen-environment-variables]
+ env = os.environ.copy()
+ env['PW_DOXYGEN_OUTPUT_DIRECTORY'] = str(output_dir.resolve())
+ env['PW_DOXYGEN_INPUT'] = ' '.join(pw_module_list)
+ env['PW_DOXYGEN_PROJECT_NAME'] = 'Pigweed'
+ # DOCSTAG: [doxygen-environment-variables]
+
+.. card::
+
+ Rendered Output
+ ^^^
+
+ .. code-block::
+
+ env = os.environ.copy()
+ env['PW_DOXYGEN_OUTPUT_DIRECTORY'] = str(output_dir.resolve())
+ env['PW_DOXYGEN_INPUT'] = ' '.join(pw_module_list)
+ env['PW_DOXYGEN_PROJECT_NAME'] = 'Pigweed'
+
+.. inclusive-language: disable
+
+.. _literalinclude: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-literalinclude
+
+.. inclusive-language: enable
+
+Generating API documentation from source
+========================================
+Whenever possible, document APIs in the source code and use Sphinx to generate
+documentation for them. This keeps the documentation in sync with the code and
+reduces duplication.
+
+Python
+------
+Include Python API documentation from docstrings with `autodoc directives`_.
+Example:
+
+.. inclusive-language: disable
+
+.. _autodoc directives: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#directives
+
+.. inclusive-language: enable
+
+.. code-block:: rst
+
+ .. automodule:: pw_cli.log
+ :members:
+
+ .. automodule:: pw_console.embed
+ :members: PwConsoleEmbed
+ :undoc-members:
+ :show-inheritance:
+
+ .. autoclass:: pw_console.log_store.LogStore
+ :members: __init__
+ :undoc-members:
+ :show-inheritance:
+
+Include argparse command line help with the `argparse
+<https://sphinx-argparse.readthedocs.io/en/latest/usage.html>`_
+directive. Example:
+
+.. code-block:: rst
+
+ .. argparse::
+ :module: pw_watch.watch
+ :func: get_parser
+ :prog: pw watch
+ :nodefaultconst:
+ :nodescription:
+ :noepilog:
+
+Customize the depth of a page's table of contents
+=================================================
+Put ``:tocdepth: X`` on the first line of the page, where ``X`` equals how many
+levels of section heading you want to show in the page's table of contents. See
+``//docs/changelog.rst`` for an example.
+
+Changelog
+=========
+This section explains how we update the changelog.
+
+#. On the Friday before Pigweed Live, use
+ `changelog <https://kaycebasques.github.io/changelog/>`_ to generate a first
+ draft of the changelog.
+
+#. Copy-paste the reStructuredText output from the changelog tool to the top
+ of ``//docs/changelog.rst``.
+
+#. Delete these lines from the previous update in ``changelog.rst``
+ (which is no longer the latest update):
+
+ * ``.. _docs-changelog-latest:``
+ * ``.. changelog_highlights_start``
+ * ``.. changelog_highlights_end``
+
+#. Polish up the auto-generated first draft into something more readable:
+
+ * Don't change the section headings. The text in each section heading
+ should map to one of the categories that we allow in our commit
+ messages, such as ``bazel``, ``docs``, ``pw_base64``, and so on.
+ * Add a 1-paragraph summary to each section.
+ * Focus on features, important bug fixes, and breaking changes. Delete
+ internal commits that Pigweed customers won't care about.
+
+#. Push your change up to Gerrit and kick off a dry run. After a few minutes
+ the docs will get staged.
+
+#. Copy the rendered content from the staging site into the Pigweed Live
+ Google Doc.
+
+#. Make sure to land the changelog updates the same week as Pigweed Live.
+
+There is no need to update ``//docs/index.rst``. The ``What's new in Pigweed``
+content on the homepage is pulled from the changelog (that's what the
+``docs-changelog-latest``, ``changelog_highlights_start``, and
+``changelog_highlights_end`` labels are for).
+
+Why "changelog" and not "release notes"?
+----------------------------------------
+Because Pigweed doesn't have releases.
+
+Why organize by module / category?
+----------------------------------
+Why is the changelog organized by category / module? Why not the usual
+3 top-level sections: features, fixes, breaking changes?
+
+* Because some Pigweed customers only use a few modules. Organizing by module
+ helps them filter out all the changes that aren't relevant to them faster.
+* If we keep the changelog section heading text fairly structured, we may
+ be able to present the changelog in other interesting ways. For example,
+ it should be possible to collect every ``pw_base64`` section in the changelog
+ and then provide a changelog for only ``pw_base64`` over in the ``pw_base64``
+ docs.
+* The changelog tool is easily able to organize by module / category due to
+ how we annotate our commits. We will not be able to publish changelog updates
+ every 2 weeks if there is too much manual work involved.
+
+.. _docs-site-scroll:
+
+Site nav scrolling
+==================
+We have had recurring issues with scrolling on pigweed.dev. This section
+provides context on the issue and fix(es).
+
+
+The behavior we want:
+
+* The page that you're currently on should be visible in the site nav.
+* URLs with deep links (e.g. ``pigweed.dev/pw_allocator/#size-report``) should
+ instantly jump to the target section (e.g. ``#size-report``).
+* There should be no scrolling animations anywhere on the site. Scrolls should
+ happen instantly.
+
+.. _furo.js: https://github.com/pradyunsg/furo/blob/main/src/furo/assets/scripts/furo.js
+
+A few potential issues at play:
+
+* Our theme (Furo) has non-configurable scrolling logic. See `furo.js`_.
+* There seems to be a race condition between Furo's scrolling behavior and our
+ text-to-diagram tool, Mermaid, which uses JavaScript to render the diagrams
+ on page load. However, we also saw issues on pages that didn't have any
+ diagrams, so that can't be the site-wide root cause.
+
+.. _scrollTop: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop
+.. _scrollIntoView: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
+.. _scroll-behavior: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
+
+Our current fix:
+
+* In ``//docs/_static/js/pigweed.js`` we manually scroll the site nav and main
+ content via `scrollTop`_. Note that we previously tried `scrollIntoView`_
+ but it was not an acceptable fix because the main content would always scroll
+ down about 50 pixels, even when a deep link was not present in the URL.
+ We also manually control when Mermaid renders its diagrams.
+* In ``//docs/_static/css/pigweed.css`` we use an aggressive CSS rule
+ to ensure that `scroll-behavior`_ is set to ``auto`` (i.e. instant scrolling)
+ for all elements on the site.
+
+Background:
+
+* `Tracking issue <https://issues.pigweed.dev/issues/303261476>`_
+* `First fix <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/162410>`_
+* `Second fix <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/162990>`_
+* `Third fix <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168555>`_
+* `Fourth fix <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178591>`_
+
+.. _docs-pw-style-cta:
+
+Call-to-action buttons on sales pitch pages (docs.rst)
+======================================================
+Use the following directive to put call-to-action buttons on a ``docs.rst``
+page:
+
+.. code-block::
+
+ .. grid:: 2
+
+ .. grid-item-card:: :octicon:`zap` Get started & guides
+ :link: <REF>
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
+
+ Learn how to integrate <MODULE> into your project and implement
+ common use cases.
+
+ .. grid-item-card:: :octicon:`info` API reference
+ :link: <REF>
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ Get detailed reference information about the <MODULE> API.
+
+ .. grid:: 2
+
+ .. grid-item-card:: :octicon:`info` CLI reference
+ :link: <REF>
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ Get usage information about <MODULE> command line utilities.
+
+ .. grid-item-card:: :octicon:`table` Design
+ :link: <REF>
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ Read up on how <MODULE> is designed.
+
+* Remove cards for content that does not exist. For example, if the module
+ doesn't have a CLI reference, remove the card for that doc.
+* Replace ``<REF>`` and ``<MODULE>``. Don't change anything else. We want
+ a consistent call-to-action experience across all the modules.
+
+Copy-to-clipboard feature on code blocks
+========================================
+
+.. _sphinx-copybutton: https://sphinx-copybutton.readthedocs.io/en/latest/
+.. _Remove copybuttons using a CSS selector: https://sphinx-copybutton.readthedocs.io/en/latest/use.html#remove-copybuttons-using-a-css-selector
+
+The copy-to-clipboard feature on code blocks is powered by `sphinx-copybutton`_.
+
+``sphinx-copybutton`` recognizes ``$`` as an input prompt and automatically
+removes it.
+
+There is a workflow for manually removing the copy-to-clipboard button for a
+particular code block but it has not been implemented yet. See
+`Remove copybuttons using a CSS selector`_.
+
+Grouping related content with tabs
+==================================
+Use the ``tab-set`` directive to group related content together. This feature is
+powered by `sphinx-design Tabs
+<https://sphinx-design.readthedocs.io/en/furo-theme/tabs.html>`_
+
+Tabs for code-only content
+--------------------------
+Use the ``tabs`` and ``code-tab`` directives together. Example:
+
+.. code-block:: rst
+
+ .. tab-set-code::
+
+ .. code-block:: c++
+
+ // C++ code...
+
+ .. code-block:: python
+
+ # Python code...
+
+Rendered output:
+
+.. tab-set-code::
+
+ .. code-block:: c++
+
+ // C++ code...
+
+ .. code-block:: python
+
+ # Python code...
+
+Tabs for all other content
+--------------------------
+Use the ``tabs`` and ``tab-item`` directives together. Example:
+
+.. code-block:: rst
+
+ .. tab-set::
+
+ .. tab-item:: Linux
+
+ Linux instructions...
+
+ .. tab-item:: Windows
+
+ Windows instructions...
+
+Rendered output:
+
+.. tab-set::
+
+ .. tab-item:: Linux
+
+ Linux instructions...
+
+ .. tab-item:: Windows
+
+ Windows instructions...
+
+Tab synchronization
+-------------------
+Tabs are synchronized in two ways:
+
+1. ``tab-set-code::`` ``code-block`` languages names.
+2. ``tab-item::`` ``:sync:`` values.
+
+For Example:
+
+.. code-block:: rst
+
+ .. tabs-set-code::
+
+ .. code-block:: c++
+
+ // C++ code...
+
+ .. code-block:: py
+
+ # Python code...
+
+ .. tabs-set-code::
+
+ .. code-block:: c++
+
+ // More C++ code...
+
+ .. code-block:: py
+
+ # More Python code...
+
+ .. tab-set::
+
+ .. tab-item:: Linux
+ :sync: key1
+
+ Linux instructions...
+
+ .. tab-item:: Windows
+ :sync: key2
+
+ Windows instructions...
+
+ .. tab-set::
+
+ .. tab-item:: Linux
+ :sync: key1
+
+ More Linux instructions...
+
+ .. tab-item:: Windows
+ :sync: key2
+
+ More Windows instructions...
+
+Rendered output:
+
+.. tab-set-code::
+
+ .. code-block:: c++
+
+ // C++ code...
+
+ .. code-block:: py
+
+ # Python code...
+
+.. tab-set-code::
+
+ .. code-block:: c++
+
+ // More C++ code...
+
+ .. code-block:: py
+
+ # More Python code...
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: key1
+
+ Linux instructions...
+
+ .. tab-item:: Windows
+ :sync: key2
+
+ Windows instructions...
+
+.. tab-set::
+
+ .. tab-item:: Linux
+ :sync: key1
+
+ More Linux instructions...
+
+ .. tab-item:: Windows
+ :sync: key2
+
+ More Windows instructions...
diff --git a/docs/style_guide.rst b/docs/style_guide.rst
index 1637821ec..552f5a1d2 100644
--- a/docs/style_guide.rst
+++ b/docs/style_guide.rst
@@ -3,869 +3,44 @@
===========
Style Guide
===========
-- :ref:`cpp-style`
-- :ref:`owners-style`
-- :ref:`python-style`
-- :ref:`documentation-style`
-- :ref:`commit-style`
+.. grid:: 1
-.. tip::
- Pigweed runs ``pw format`` as part of ``pw presubmit`` to perform some code
- formatting checks. To speed up the review process, consider adding ``pw
- presubmit`` as a git push hook using the following command:
- ``pw presubmit --install``
-
-.. _cpp-style:
-
----------
-C++ style
----------
-The Pigweed C++ style guide is closely based on Google's external C++ Style
-Guide, which is found on the web at
-https://google.github.io/styleguide/cppguide.html. The Google C++ Style Guide
-applies to Pigweed except as described in this document.
-
-The Pigweed style guide only applies to Pigweed itself. It does not apply to
-projects that use Pigweed or to the third-party code included with Pigweed.
-Non-Pigweed code is free to use features restricted by Pigweed, such as dynamic
-memory allocation and the entirety of the C++ Standard Library.
-
-Recommendations in the :doc:`embedded_cpp_guide` are considered part of the
-Pigweed style guide, but are separated out since it covers more general
-embedded development beyond just C++ style.
-
-C++ standard
-============
-All Pigweed C++ code must compile with ``-std=c++17`` in Clang and GCC. C++20
-features may be used as long as the code still compiles unmodified with C++17.
-See ``pw_polyfill/language_feature_macros.h`` for macros that provide C++20
-features when supported.
-
-Compiler extensions should not be used unless wrapped in a macro or properly
-guarded in the preprocessor. See ``pw_processor/compiler.h`` for macros that
-wrap compiler-specific features.
-
-Automatic formatting
-====================
-Pigweed uses `clang-format <https://clang.llvm.org/docs/ClangFormat.html>`_ to
-automatically format Pigweed source code. A ``.clang-format`` configuration is
-provided with the Pigweed repository.
-
-Automatic formatting is essential to facilitate large-scale, automated changes
-in Pigweed. Therefore, all code in Pigweed is expected to be formatted with
-``clang-format`` prior to submission. Existing code may be reformatted at any
-time.
-
-If ``clang-format`` formats code in an undesirable or incorrect way, it can be
-disabled for the affected lines by adding ``// clang-format off``.
-``clang-format`` must then be re-enabled with a ``// clang-format on`` comment.
-
-.. code-block:: cpp
-
- // clang-format off
- constexpr int kMyMatrix[] = {
- 100, 23, 0,
- 0, 542, 38,
- 1, 2, 201,
- };
- // clang-format on
-
-C Standard Library
-==================
-In C++ headers, always use the C++ versions of C Standard Library headers (e.g.
-``<cstdlib>`` instead of ``<stdlib.h>``). If the header is used by both C and
-C++ code, only the C header should be used.
-
-In C++ code, it is preferred to use C functions from the ``std`` namespace. For
-example, use ``std::memcpy`` instead of ``memcpy``. The C++ standard does not
-require the global namespace versions of the functions to be provided. Using
-``std::`` is more consistent with the C++ Standard Library and makes it easier
-to distinguish Pigweed functions from library functions.
-
-Within core Pigweed, do not use C standard library functions that allocate
-memory, such as ``std::malloc``. There are exceptions to this for when dynamic
-allocation is enabled for a system; Pigweed modules are allowed to add extra
-functionality when a heap is present; but this must be optional.
-
-C++ Standard Library
-====================
-Much of the C++ Standard Library is not a good fit for embedded software. Many
-of the classes and functions were not designed with the RAM, flash, and
-performance constraints of a microcontroller in mind. For example, simply
-adding the line ``#include <iostream>`` can increase the binary size by 150 KB!
-This is larger than many microcontrollers' entire internal storage.
-
-However, with appropriate caution, a limited set of standard C++ libraries can
-be used to great effect. Developers can leverage familiar, well-tested
-abstractions instead of writing their own. C++ library algorithms and classes
-can give equivalent or better performance than hand-written C code.
-
-A limited subset of the C++ Standard Library is permitted in Pigweed. To keep
-Pigweed small, flexible, and portable, functions that allocate dynamic memory
-must be avoided. Care must be exercised when using multiple instantiations of a
-template function, which can lead to code bloat.
-
-Permitted Headers
------------------
-.. admonition:: The following C++ Standard Library headers are always permitted:
- :class: checkmark
-
- * ``<array>``
- * ``<complex>``
- * ``<initializer_list>``
- * ``<iterator>``
- * ``<limits>``
- * ``<optional>``
- * ``<random>``
- * ``<ratio>``
- * ``<string_view>``
- * ``<tuple>``
- * ``<type_traits>``
- * ``<utility>``
- * ``<variant>``
- * C Standard Library headers (``<c*>``)
-
-.. admonition:: With caution, parts of the following headers can be used:
- :class: warning
-
- * ``<algorithm>`` -- be wary of potential memory allocation
- * ``<atomic>`` -- not all MCUs natively support atomic operations
- * ``<bitset>`` -- conversions to or from strings are disallowed
- * ``<functional>`` -- do **not** use ``std::function``; use
- :ref:`module-pw_function`
- * ``<mutex>`` -- can use ``std::lock_guard``, use :ref:`module-pw_sync` for
- mutexes
- * ``<new>`` -- for placement new
- * ``<numeric>`` -- be wary of code size with multiple template instantiations
-
-.. admonition:: Never use any of these headers:
- :class: error
-
- * Dynamic containers (``<list>``, ``<map>``, ``<set>``, ``<vector>``, etc.)
- * Streams (``<iostream>``, ``<ostream>``, ``<fstream>``, ``<sstream>`` etc.)
- -- in some cases :ref:`module-pw_stream` can work instead
- * ``<span>`` -- use :ref:`module-pw_span` instead. Downstream projects may
- want to directly use ``std::span`` if it is available, but upstream must
- use the ``pw::span`` version for compatability
- * ``<string>`` -- can use :ref:`module-pw_string`
- * ``<thread>`` -- can use :ref:`module-pw_thread`
- * ``<future>`` -- eventually :ref:`module-pw_async` will offer this
- * ``<exception>``, ``<stdexcept>`` -- no exceptions
- * ``<memory>``, ``<scoped_allocator>`` -- no allocations
- * ``<regex>``
- * ``<valarray>``
-
-Headers not listed here should be carefully evaluated before they are used.
-
-These restrictions do not apply to third party code or to projects that use
-Pigweed.
-
-Combining C and C++
-===================
-Prefer to write C++ code over C code, using ``extern "C"`` for symbols that must
-have C linkage. ``extern "C"`` functions should be defined within C++
-namespaces to simplify referring to other code.
-
-C++ functions with no parameters do not include ``void`` in the parameter list.
-C functions with no parameters must include ``void``.
-
-.. code-block:: cpp
-
- namespace pw {
-
- bool ThisIsACppFunction() { return true; }
-
- extern "C" int pw_ThisIsACFunction(void) { return -1; }
-
- extern "C" {
-
- int pw_ThisIsAlsoACFunction(void) {
- return ThisIsACppFunction() ? 100 : 0;
- }
-
- } // extern "C"
-
- } // namespace pw
-
-Comments
-========
-Prefer C++-style (``//``) comments over C-style comments (``/* */``). C-style
-comments should only be used for inline comments.
-
-.. code-block:: cpp
-
- // Use C++-style comments, except where C-style comments are necessary.
- // This returns a random number using an algorithm I found on the internet.
- #define RANDOM_NUMBER() [] { \
- return 4; /* chosen by fair dice roll */ \
- }()
-
-Indent code in comments with two additional spaces, making a total of three
-spaces after the ``//``. All code blocks must begin and end with an empty
-comment line, even if the blank comment line is the last line in the block.
-
-.. code-block:: cpp
-
- // Here is an example of code in comments.
- //
- // int indentation_spaces = 2;
- // int total_spaces = 3;
- //
- // engine_1.thrust = RANDOM_NUMBER() * indentation_spaces + total_spaces;
- //
- bool SomeFunction();
-
-Control statements
-==================
-
-Loops and conditionals
-----------------------
-All loops and conditional statements must use braces, and be on their own line.
-
-.. admonition:: **Yes**: Always use braces for line conditionals and loops:
- :class: checkmark
-
- .. code-block:: cpp
-
- while (SomeCondition()) {
- x += 2;
- }
- if (OtherCondition()) {
- DoTheThing();
- }
-
-
-.. admonition:: **No**: Missing braces
- :class: error
-
- .. code-block:: cpp
-
- while (SomeCondition())
- x += 2;
- if (OtherCondition())
- DoTheThing();
-
-.. admonition:: **No**: Statement on same line as condition
- :class: error
-
- .. code-block:: cpp
-
- while (SomeCondition()) { x += 2; }
- if (OtherCondition()) { DoTheThing(); }
-
-
-The syntax ``while (true)`` is preferred over ``for (;;)`` for infinite loops.
-
-.. admonition:: **Yes**:
- :class: checkmark
-
- .. code-block:: cpp
-
- while (true) {
- DoSomethingForever();
- }
-
-.. admonition:: **No**:
- :class: error
-
- .. code-block:: cpp
-
- for (;;) {
- DoSomethingForever();
- }
-
-
-Prefer early exit with ``return`` and ``continue``
---------------------------------------------------
-Prefer to exit early from functions and loops to simplify code. This is the
-same same conventions as `LLVM
-<https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code>`_.
-We find this approach is superior to the "one return per function" style for a
-multitude of reasons:
-
-* **Visually**, the code is easier to follow, and takes less horizontal screen
- space.
-* It makes it clear what part of the code is the **"main business" versus "edge
- case handling"**.
-* For **functions**, parameter checking is in its own section at the top of the
- function, rather than scattered around in the fuction body.
-* For **loops**, element checking is in its own section at the top of the loop,
- rather than scattered around in the loop body.
-* Commit **deltas are simpler to follow** in code reviews; since adding a new
- parameter check or loop element condition doesn't cause an indentation change
- in the rest of the function.
-
-The guidance applies in two cases:
-
-* **Function early exit** - Early exits are for function parameter checking
- and edge case checking at the top. The main functionality follows.
-* **Loop early exit** - Early exits in loops are for skipping an iteration
- due to some edge case with an item getting iterated over. Loops may also
- contain function exits, which should be structured the same way (see example
- below).
-
-.. admonition:: **Yes**: Exit early from functions; keeping the main handling
- at the bottom and de-dentend.
- :class: checkmark
-
- .. code-block:: cpp
-
- Status DoSomething(Parameter parameter) {
- // Parameter validation first; detecting incoming use errors.
- PW_CHECK_INT_EQ(parameter.property(), 3, "Programmer error: frobnitz");
-
- // Error case: Not in correct state.
- if (parameter.other() == MyEnum::kBrokenState) {
- LOG_ERROR("Device in strange state: %s", parametr.state_str());
- return Status::InvalidPrecondition();
- }
-
- // Error case: Not in low power mode; shouldn't do anything.
- if (parameter.power() != MyEnum::kLowPower) {
- LOG_ERROR("Not in low power mode");
- return Status::InvalidPrecondition();
- }
-
- // Main business for the function here.
- MainBody();
- MoreMainBodyStuff();
- }
-
-.. admonition:: **No**: Main body of function is buried and right creeping.
- Even though this is shorter than the version preferred by Pigweed due to
- factoring the return statement, the logical structure is less obvious. A
- function in Pigweed containing **nested conditionals indicates that
- something complicated is happening with the flow**; otherwise it would have
- the early bail structure; so pay close attention.
- :class: error
-
- .. code-block:: cpp
-
- Status DoSomething(Parameter parameter) {
- // Parameter validation first; detecting incoming use errors.
- PW_CHECK_INT_EQ(parameter.property(), 3, "Programmer error: frobnitz");
-
- // Error case: Not in correct state.
- if (parameter.other() != MyEnum::kBrokenState) {
- // Error case: Not in low power mode; shouldn't do anything.
- if (parameter.power() == MyEnum::kLowPower) {
- // Main business for the function here.
- MainBody();
- MoreMainBodyStuff();
- } else {
- LOG_ERROR("Not in low power mode");
- }
- } else {
- LOG_ERROR("Device in strange state: %s", parametr.state_str());
- }
- return Status::InvalidPrecondition();
- }
-
-.. admonition:: **Yes**: Bail early from loops; keeping the main handling at
- the bottom and de-dentend.
- :class: checkmark
-
- .. code-block:: cpp
-
- for (int i = 0; i < LoopSize(); ++i) {
- // Early skip of item based on edge condition.
- if (!CommonCase()) {
- continue;
- }
- // Early exit of function based on error case.
- int my_measurement = GetSomeMeasurement();
- if (my_measurement < 10) {
- LOG_ERROR("Found something strange; bailing");
- return Status::Unknown();
- }
-
- // Main body of the loop.
- ProcessItem(my_items[i], my_measurement);
- ProcessItemMore(my_items[i], my_measurement, other_details);
- ...
- }
-
-.. admonition:: **No**: Right-creeping code with the main body buried inside
- some nested conditional. This makes it harder to understand what is the
- main purpose of the loop versus what is edge case handling.
- :class: error
-
- .. code-block:: cpp
-
- for (int i = 0; i < LoopSize(); ++i) {
- if (CommonCase()) {
- int my_measurement = GetSomeMeasurement();
- if (my_measurement >= 10) {
- // Main body of the loop.
- ProcessItem(my_items[i], my_measurement);
- ProcessItemMore(my_items[i], my_measurement, other_details);
- ...
- } else {
- LOG_ERROR("Found something strange; bailing");
- return Status::Unknown();
- }
- }
- }
-
-There are cases where this structure doesn't work, and in those cases, it is
-fine to structure the code differently.
-
-No ``else`` after ``return`` or ``continue``
---------------------------------------------
-Do not put unnecessary ``} else {`` blocks after blocks that terminate with a
-return, since this causes unnecessary rightward indentation creep. This
-guidance pairs with the preference for early exits to reduce code duplication
-and standardize loop/function structure.
-
-.. admonition:: **Yes**: No else after return or continue
- :class: checkmark
-
- .. code-block:: cpp
-
- // Note lack of else block due to return.
- if (Failure()) {
- DoTheThing();
- return Status::ResourceExausted();
- }
-
- // Note lack of else block due to continue.
- while (MyCondition()) {
- if (SomeEarlyBail()) {
- continue;
- }
- // Main handling of item
- ...
- }
-
- DoOtherThing();
- return OkStatus();
-
-.. admonition:: **No**: Else after return needlessly creeps right
- :class: error
-
- .. code-block:: cpp
-
- if (Failure()) {
- DoTheThing();
- return Status::ResourceExausted();
- } else {
- while (MyCondition()) {
- if (SomeEarlyBail()) {
- continue;
- } else {
- // Main handling of item
- ...
- }
- }
- DoOtherThing();
- return OkStatus();
- }
-
-Include guards
-==============
-The first non-comment line of every header file must be ``#pragma once``. Do
-not use traditional macro include guards. The ``#pragma once`` should come
-directly after the Pigweed copyright block, with no blank line, followed by a
-blank, like this:
-
-.. code-block:: cpp
-
- // Copyright 2021 The Pigweed Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not
- // use this file except in compliance with the License. You may obtain a copy of
- // the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations under
- // the License.
- #pragma once
-
- // Header file-level comment goes here...
-
-Memory allocation
-=================
-Dynamic memory allocation can be problematic. Heap allocations and deallocations
-occupy valuable CPU cycles. Memory usage becomes nondeterministic, which can
-result in a system crashing without a clear culprit.
-
-To keep Pigweed portable, core Pigweed code is not permitted to dynamically
-(heap) allocate memory, such as with ``malloc`` or ``new``. All memory should be
-allocated with automatic (stack) or static (global) storage duration. Pigweed
-must not use C++ libraries that use dynamic allocation.
-
-Projects that use Pigweed are free to use dynamic allocation, provided they
-have selected a target that enables the heap.
-
-Naming
-======
-Entities shall be named according to the `Google style guide
-<https://google.github.io/styleguide/cppguide.html>`_, with the following
-additional requirements.
-
-C++ code
---------
-* All Pigweed C++ code must be in the ``pw`` namespace. Namespaces for modules
- should be nested under ``pw``. For example, ``pw::string::Format()``.
-* Whenever possible, private code should be in a source (.cc) file and placed in
- anonymous namespace nested under ``pw``.
-* If private code must be exposed in a header file, it must be in a namespace
- nested under ``pw``. The namespace may be named for its subsystem or use a
- name that designates it as private, such as ``internal``.
-* Template arguments for non-type names (e.g. ``template <int kFooBar>``) should
- follow the constexpr and const variable Google naming convention, which means
- k prefixed camel case (e.g. ``kCamelCase``). This matches the Google C++
- style for variable naming, however the wording in the official style guide
- isn't explicit for template arguments and could be interpreted to use
- ``foo_bar`` style naming. For consistency with other variables whose value is
- always fixed for the duration of the program, the naming convention is
- ``kCamelCase``, and so that is the style we use in Pigweed.
-* Trivial membor accessors should be named with ``snake_case()``. The Google
- C++ style allows either ``snake_case()`` or ``CapsCase()``, but Pigweed
- always uses ``snake_case()``.
-* Abstract base classes should be named generically, with derived types named
- specifically. For example, ``Stream`` is an abstract base, and
- ``SocketStream`` and ``StdioStream`` are an implementations of that
- interface. Any prefix or postfix indicating whether something is abstract or
- concrete is not permitted; for example, ``IStream`` or ``SocketStreamImpl``
- are both not permitted. These pre-/post-fixes add additional visual noise and
- are irrelevant to consumers of these interfaces.
-
-C code
-------
-In general, C symbols should be prefixed with the module name. If the symbol is
-not associated with a module, use just ``pw`` as the module name. Facade
-backends may chose to prefix symbols with the facade's name to help reduce the
-length of the prefix.
-
-* Public names used by C code must be prefixed with the module name (e.g.
- ``pw_tokenizer_*``).
-* If private code must be exposed in a header, private names used by C code must
- be prefixed with an underscore followed by the module name (e.g.
- ``_pw_assert_*``).
-* Avoid writing C source (.c) files in Pigweed. Prefer to write C++ code with C
- linkage using ``extern "C"``. Within C source, private C functions and
- variables must be named with the ``_pw_my_module_*`` prefix and should be
- declared ``static`` whenever possible; for example,
- ``_pw_my_module_MyPrivateFunction``.
-* The C prefix rules apply to
-
- * C functions (``int pw_foo_FunctionName(void);``),
- * variables used by C code (``int pw_foo_variable_name;``),
- * constant variables used by C code (``const int pw_foo_kConstantName;``),
- * structs used by C code (``typedef struct {} pw_foo_StructName;``), and
- * all of the above for ``extern "C"`` names in C++ code.
-
- The prefix does not apply to struct members, which use normal Google style.
-
-Preprocessor macros
--------------------
-* Public Pigweed macros must be prefixed with the module name (e.g.
- ``PW_MY_MODULE_*``).
-* Private Pigweed macros must be prefixed with an underscore followed by the
- module name (e.g. ``_PW_MY_MODULE_*``). (This style may change, see
- `b/234886184 <https://issuetracker.google.com/issues/234886184>`_).
-
-**Example**
-
-.. code-block:: cpp
-
- namespace pw::my_module {
- namespace nested_namespace {
-
- // C++ names (types, variables, functions) must be in the pw namespace.
- // They are named according to the Google style guide.
- constexpr int kGlobalConstant = 123;
-
- // Prefer using functions over extern global variables.
- extern int global_variable;
-
- class Class {};
-
- void Function();
-
- extern "C" {
-
- // Public Pigweed code used from C must be prefixed with pw_.
- extern const int pw_my_module_kGlobalConstant;
-
- extern int pw_my_module_global_variable;
-
- void pw_my_module_Function(void);
-
- typedef struct {
- int member_variable;
- } pw_my_module_Struct;
-
- // Private Pigweed code used from C must be prefixed with _pw_.
- extern const int _pw_my_module_kPrivateGlobalConstant;
-
- extern int _pw_my_module_private_global_variable;
-
- void _pw_my_module_PrivateFunction(void);
-
- typedef struct {
- int member_variable;
- } _pw_my_module_PrivateStruct;
-
- } // extern "C"
-
- // Public macros must be prefixed with PW_.
- #define PW_MY_MODULE_PUBLIC_MACRO(arg) arg
-
- // Private macros must be prefixed with _PW_.
- #define _PW_MY_MODULE_PRIVATE_MACRO(arg) arg
-
- } // namespace nested_namespace
- } // namespace pw::my_module
-
-See :ref:`docs-pw-style-macros` for details about macro usage.
-
-Namespace scope formatting
-==========================
-All non-indented blocks (namespaces, ``extern "C"`` blocks, and preprocessor
-conditionals) must have a comment on their closing line with the
-contents of the starting line.
+ .. grid-item-card:: :octicon:`diff-added` C++ style
+ :link: docs-pw-style-cpp
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
-All nested namespaces should be declared together with no blank lines between
-them.
+ Our C++ style guide: an extension of the Google C++ style with further
+ restrictions and guidance for embedded
-.. code-block:: cpp
-
- #include "some/header.h"
-
- namespace pw::nested {
- namespace {
-
- constexpr int kAnonConstantGoesHere = 0;
-
- } // namespace
-
- namespace other {
-
- const char* SomeClass::yes = "no";
-
- bool ThisIsAFunction() {
- #if PW_CONFIG_IS_SET
- return true;
- #else
- return false;
- #endif // PW_CONFIG_IS_SET
- }
-
- extern "C" {
-
- const int pw_kSomeConstant = 10;
- int pw_some_global_variable = 600;
-
- void pw_CFunction() { ... }
-
- } // extern "C"
-
- } // namespace
- } // namespace pw::nested
-
-Using directives for literals
-=============================
-`Using-directives
-<https://en.cppreference.com/w/cpp/language/namespace#Using-directives>`_ (e.g.
-``using namespace ...``) are permitted in implementation files only for the
-purposes of importing literals such as ``std::chrono_literals`` or
-``pw::bytes::unit_literals``. Namespaces that contain any symbols other than
-literals are not permitted in a using-directive. This guidance also has no
-impact on `using-declarations
-<https://en.cppreference.com/w/cpp/language/namespace#Using-declarations>`_
-(e.g. ``using foo::Bar;``).
-
-Rationale: Literals improve code readability, making units clearer at the point
-of definition.
-
-.. code-block:: cpp
+.. grid:: 3
- using namespace std::chrono; // Not allowed
- using namespace std::literals::chrono_literals; // Allowed
+ .. grid-item-card:: :octicon:`rocket` Commit messages
+ :link: docs-pw-style-commit-message
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
- constexpr std::chrono::duration delay = 250ms;
+ How to format commit messages for Pigweed
-Pointers and references
-=======================
-For pointer and reference types, place the asterisk or ampersand next to the
-type.
+ .. grid-item-card:: :octicon:`code-square` Sphinx
+ :link: docs-pw-style-sphinx
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
-.. code-block:: cpp
-
- int* const number = &that_thing;
- constexpr const char* kString = "theory!"
-
- bool FindTheOneRing(const Region& where_to_look) { ... }
-
-Prefer storing references over storing pointers. Pointers are required when the
-pointer can change its target or may be ``nullptr``. Otherwise, a reference or
-const reference should be used.
-
-.. _docs-pw-style-macros:
-
-Preprocessor macros
-===================
-Macros should only be used when they significantly improve upon the C++ code
-they replace. Macros should make code more readable, robust, and safe, or
-provide features not possible with standard C++, such as stringification, line
-number capturing, or conditional compilation. When possible, use C++ constructs
-like constexpr variables in place of macros. Never use macros as constants,
-except when a string literal is needed or the value must be used by C code.
-
-When macros are needed, the macros should be accompanied with extensive tests
-to ensure the macros are hard to use wrong.
-
-Stand-alone statement macros
-----------------------------
-Macros that are standalone statements must require the caller to terminate the
-macro invocation with a semicolon (see `Swalling the Semicolon
-<https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html>`_). For
-example, the following does *not* conform to Pigweed's macro style:
-
-.. code-block:: cpp
-
- // BAD! Definition has built-in semicolon.
- #define PW_LOG_IF_BAD(mj) \
- CallSomeFunction(mj);
-
- // BAD! Compiles without error; semicolon is missing.
- PW_LOG_IF_BAD("foo")
+ Our website and module documentation is built with Sphinx
-Here's how to do this instead:
-
-.. code-block:: cpp
+ .. grid-item-card:: :octicon:`code-square` Doxygen
+ :link: docs-pw-style-doxygen
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
- // GOOD; requires semicolon to compile.
- #define PW_LOG_IF_BAD(mj) \
- CallSomeFunction(mj)
-
- // GOOD; fails to compile due to lacking semicolon.
- PW_LOG_IF_BAD("foo")
-
-For macros in function scope that do not already require a semicolon, the
-contents can be placed in a ``do { ... } while (0)`` loop.
-
-.. code-block:: cpp
+ How to structure reference documentation for C++ code
- #define PW_LOG_IF_BAD(mj) \
- do { \
- if (mj.Bad()) { \
- Log(#mj " is bad") \
- } \
- } while (0)
-
-Standalone macros at global scope that do not already require a semicolon can
-add a ``static_assert`` declaration statement as their last line.
-
-.. code-block:: cpp
-
- #define PW_NEAT_THING(thing) \
- bool IsNeat_##thing() { return true; } \
- static_assert(true, "Macros must be terminated with a semicolon")
-
-Private macros in public headers
---------------------------------
-Private macros in public headers must be prefixed with ``_PW_``, even if they
-are undefined after use; this prevents collisions with downstream users. For
-example:
-
-.. code-block:: cpp
-
- #define _PW_MY_SPECIAL_MACRO(op) ...
- ...
- // Code that uses _PW_MY_SPECIAL_MACRO()
- ...
- #undef _PW_MY_SPECIAL_MACRO
-
-Macros in private implementation files (.cc)
---------------------------------------------
-Macros within .cc files that should only be used within one file should be
-undefined after their last use; for example:
-
-.. code-block:: cpp
-
- #define DEFINE_OPERATOR(op) \
- T operator ## op(T x, T y) { return x op y; } \
- static_assert(true, "Macros must be terminated with a semicolon") \
-
- DEFINE_OPERATOR(+);
- DEFINE_OPERATOR(-);
- DEFINE_OPERATOR(/);
- DEFINE_OPERATOR(*);
-
- #undef DEFINE_OPERATOR
-
-Preprocessor conditional statements
-===================================
-When using macros for conditional compilation, prefer to use ``#if`` over
-``#ifdef``. This checks the value of the macro rather than whether it exists.
-
-* ``#if`` handles undefined macros equivalently to ``#ifdef``. Undefined
- macros expand to 0 in preprocessor conditional statements.
-* ``#if`` evaluates false for macros defined as 0, while ``#ifdef`` evaluates
- true.
-* Macros defined using compiler flags have a default value of 1 in GCC and
- Clang, so they work equivalently for ``#if`` and ``#ifdef``.
-* Macros defined to an empty statement cause compile-time errors in ``#if``
- statements, which avoids ambiguity about how the macro should be used.
-
-All ``#endif`` statements should be commented with the expression from their
-corresponding ``#if``. Do not indent within preprocessor conditional statements.
-
-.. code-block:: cpp
-
- #if USE_64_BIT_WORD
- using Word = uint64_t;
- #else
- using Word = uint32_t;
- #endif // USE_64_BIT_WORD
-
-Unsigned integers
-=================
-Unsigned integers are permitted in Pigweed. Aim for consistency with existing
-code and the C++ Standard Library. Be very careful mixing signed and unsigned
-integers.
-
-Features not in the C++ standard
-================================
-Avoid features not available in standard C++. This includes compiler extensions
-and features from other standards like POSIX.
-
-For example, use ``ptrdiff_t`` instead of POSIX's ``ssize_t``, unless
-interacting with a POSIX API in intentionally non-portable code. Never use
-POSIX functions with suitable standard or Pigweed alternatives, such as
-``strnlen`` (use ``pw::string::NullTerminatedLength`` instead).
-
-.. _owners-style:
-
---------------------
-Code Owners (OWNERS)
---------------------
-If you use Gerrit for source code hosting and have the
-`code-owners <https://android-review.googlesource.com/plugins/code-owners/Documentation/backend-find-owners.html>`__
-plug-in enabled Pigweed can help you enforce consistent styling on those files
-and also detects some errors.
-
-The styling follows these rules.
-
-#. Content is grouped by type of line (Access grant, include, etc).
-#. Each grouping is sorted alphabetically.
-#. Groups are placed the following order with a blank line separating each
- grouping.
-
- * "set noparent" line
- * "include" lines
- * "file:" lines
- * user grants (some examples: "*", "foo@example.com")
- * "per-file:" lines
-
-This plugin will, by default, act upon any file named "OWNERS".
+.. tip::
+ Pigweed runs ``pw format`` as part of ``pw presubmit`` to perform some code
+ formatting checks. To speed up the review process, consider adding ``pw
+ presubmit`` as a git push hook using the following command:
+ ``pw presubmit --install``
.. _python-style:
@@ -950,1032 +125,35 @@ ambiguity with other build systems or tooling.
Pigweed's Bazel files follow the `Bazel style guide
<https://docs.bazel.build/versions/main/skylark/build-style.html>`_.
-.. _documentation-style:
-
--------------
-Documentation
--------------
-.. note::
-
- Pigweed's documentation style guide came after much of the documentation was
- written, so Pigweed's docs don't yet 100% conform to this style guide. When
- updating docs, please update them to match the style guide.
-
-Pigweed documentation is written using the `reStructuredText
-<https://docutils.sourceforge.io/rst.html>`_ markup language and processed by
-`Sphinx`_. We use the `Furo theme <https://github.com/pradyunsg/furo>`_ along
-with the `sphinx-design <https://sphinx-design.readthedocs.io/en/furo-theme/>`_
-extension.
-
-Syntax Reference Links
-======================
-.. admonition:: See also
- :class: seealso
-
- - `reStructuredText Primer`_
-
- - `reStructuredText Directives <https://docutils.sourceforge.io/docs/ref/rst/directives.html>`_
-
- - `Furo Reference <https://pradyunsg.me/furo/reference/>`_
-
- - `Sphinx-design Reference <https://sphinx-design.readthedocs.io/en/furo-theme/>`_
-
-ReST is flexible, supporting formatting the same logical document in a few ways
-(for example headings, blank lines). Pigweed has the following restrictions to
-make our documentation consistent.
-
-Headings
-========
-Use headings according to the following hierarchy, with the shown characters
-for the ReST heading syntax.
-
-.. code-block:: rst
-
- ==================================
- Document Title: Two Bars of Equals
- ==================================
- Document titles use equals ("====="), above and below. Capitalize the words
- in the title, except for 'a', 'of', and 'the'.
-
- ---------------------------
- Major Sections Within a Doc
- ---------------------------
- Major sections use hyphens ("----"), above and below. Capitalize the words in
- the title, except for 'a', 'of', and 'the'.
-
- Heading 1 - For Sections Within a Doc
- =====================================
- These should be title cased. Use a single equals bar ("====").
-
- Heading 2 - for subsections
- ---------------------------
- Subsections use hyphens ("----"). In many cases, these headings may be
- sentence-like. In those cases, only the first letter should be capitalized.
- For example, FAQ subsections would have a title with "Why does the X do the
- Y?"; note the sentence capitalization (but not title capitalization).
-
- Heading 3 - for subsubsections
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Use the caret symbol ("^^^^") for subsubsections.
-
- Note: Generally don't go beyond heading 3.
-
- Heading 4 - for subsubsubsections
- .................................
- Don't use this heading level, but if you must, use period characters
- ("....") for the heading.
-
-Do not put blank lines after headings.
---------------------------------------
-.. admonition:: **Yes**: No blank after heading
- :class: checkmark
-
- .. code-block:: rst
-
- Here is a heading
- -----------------
- Note that there is no blank line after the heading separator!
-
-.. admonition:: **No**: Unnecessary blank line
- :class: error
-
- .. code-block:: rst
-
- Here is a heading
- -----------------
-
- There is a totally unnecessary blank line above this one. Don't do this.
-
-Do not put multiple blank lines before a heading.
--------------------------------------------------
-.. admonition:: **Yes**: Just one blank after section content before the next heading
- :class: checkmark
-
- .. code-block:: rst
-
- There is some text here in the section before the next. It's just here to
- illustrate the spacing standard. Note that there is just one blank line
- after this paragraph.
-
- Just one blank!
- ---------------
- There is just one blank line before the heading.
-
-.. admonition:: **No**: Extra blank lines
- :class: error
-
- .. code-block:: rst
-
- There is some text here in the section before the next. It's just here to
- illustrate the spacing standard. Note that there are too many blank lines
- after this paragraph; there should be just one.
-
-
-
- Too many blanks
- ---------------
- There are too many blanks before the heading for this section.
-
-Directives
-==========
-Indent directives 3 spaces; and put a blank line between the directive and the
-content. This aligns the directive content with the directive name.
-
-.. admonition:: **Yes**: Three space indent for directives; and nested
- :class: checkmark
-
- .. code-block:: none
-
- Here is a paragraph that has some content. After this content is a
- directive.
-
- .. my_directive::
-
- Note that this line's start aligns with the "m" above. The 3-space
- alignment accounts for the ".. " prefix for directives, to vertically
- align the directive name with the content.
-
- This indentation must continue for nested directives.
-
- .. nested_directive::
-
- Here is some nested directive content.
-
-.. admonition:: **No**: One space, two spaces, four spaces, or other indents
- for directives
- :class: error
-
- .. code-block:: none
-
- Here is a paragraph with some content.
-
- .. my_directive::
-
- The indentation here is incorrect! It's one space short; doesn't align
- with the directive name above.
-
- .. nested_directive::
-
- This isn't indented correctly either; it's too much (4 spaces).
-
-.. admonition:: **No**: Missing blank between directive and content.
- :class: error
-
- .. code-block:: none
-
- Here is a paragraph with some content.
-
- .. my_directive::
- Note the lack of blank line above here.
-
-Tables
-======
-Consider using ``.. list-table::`` syntax, which is more maintainable and
-easier to edit for complex tables (`details
-<https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table>`_).
-
-Code Snippets
-=============
-Use code blocks from actual source code files wherever possible. This helps keep
-documentation fresh and removes duplicate code examples. There are a few ways to
-do this with Sphinx.
-
-The `literalinclude`_ directive creates a code blocks from source files. Entire
-files can be included or a just a subsection. The best way to do this is with
-the ``:start-after:`` and ``:end-before:`` options.
-
-Example:
-
-.. card::
-
- Documentation Source (``.rst`` file)
- ^^^
-
- .. code-block:: rst
-
- .. literalinclude:: run_doxygen.py
- :start-after: [doxygen-environment-variables]
- :end-before: [doxygen-environment-variables]
-
-.. card::
-
- Source File
- ^^^
-
- .. code-block::
-
- # DOCSTAG: [doxygen-environment-variables]
- env = os.environ.copy()
- env['PW_DOXYGEN_OUTPUT_DIRECTORY'] = str(output_dir.resolve())
- env['PW_DOXYGEN_INPUT'] = ' '.join(pw_module_list)
- env['PW_DOXYGEN_PROJECT_NAME'] = 'Pigweed'
- # DOCSTAG: [doxygen-environment-variables]
-
-.. card::
-
- Rendered Output
- ^^^
-
- .. code-block::
-
- env = os.environ.copy()
- env['PW_DOXYGEN_OUTPUT_DIRECTORY'] = str(output_dir.resolve())
- env['PW_DOXYGEN_INPUT'] = ' '.join(pw_module_list)
- env['PW_DOXYGEN_PROJECT_NAME'] = 'Pigweed'
-
-Generating API documentation from source
-========================================
-Whenever possible, document APIs in the source code and use Sphinx to generate
-documentation for them. This keeps the documentation in sync with the code and
-reduces duplication.
-
-Python
-------
-Include Python API documentation from docstrings with `autodoc directives`_.
-Example:
-
-.. code-block:: rst
-
- .. automodule:: pw_cli.log
- :members:
-
- .. automodule:: pw_console.embed
- :members: PwConsoleEmbed
- :undoc-members:
- :show-inheritance:
-
- .. autoclass:: pw_console.log_store.LogStore
- :members: __init__
- :undoc-members:
- :show-inheritance:
-
-Include argparse command line help with the `argparse
-<https://sphinx-argparse.readthedocs.io/en/latest/usage.html>`_
-directive. Example:
-
-.. code-block:: rst
-
- .. argparse::
- :module: pw_watch.watch
- :func: get_parser
- :prog: pw watch
- :nodefaultconst:
- :nodescription:
- :noepilog:
-
-
-Doxygen
--------
-Doxygen comments in C, C++, and Java are surfaced in Sphinx using `Breathe
-<https://breathe.readthedocs.io/en/latest/index.html>`_.
-
-.. note::
-
- Sources with doxygen comment blocks must be added to the
- ``_doxygen_input_files`` list in ``//docs/BUILD.gn`` to be processed.
-
-Breathe provides various `directives
-<https://breathe.readthedocs.io/en/latest/directives.html>`_ for bringing
-Doxygen comments into Sphinx. These include the following:
-
-- `doxygenfile
- <https://breathe.readthedocs.io/en/latest/directives.html#doxygenfile>`_ --
- Documents a source file. May limit to specific types of symbols with
- ``:sections:``.
-
- .. code-block:: rst
-
- .. doxygenfile:: pw_rpc/internal/config.h
- :sections: define, func
-
-- `doxygenclass
- <https://breathe.readthedocs.io/en/latest/directives.html#doxygenclass>`_ --
- Documents a class and optionally its members.
-
- .. code-block:: rst
-
- .. doxygenclass:: pw::sync::BinarySemaphore
- :members:
-
-- `doxygentypedef
- <https://breathe.readthedocs.io/en/latest/directives.html#doxygentypedef>`_ --
- Documents an alias (``typedef`` or ``using`` statement).
-
- .. code-block:: rst
-
- .. doxygentypedef:: pw::Function
-
-- `doxygenfunction
- <https://breathe.readthedocs.io/en/latest/directives.html#doxygenfunction>`_ --
- Documents a source file. Can be filtered to limit to specific types of
- symbols.
-
- .. code-block:: rst
-
- .. doxygenfunction:: pw::tokenizer::EncodeArgs
-
-- `doxygendefine
- <https://breathe.readthedocs.io/en/latest/directives.html#doxygendefine>`_ --
- Documents a preprocessor macro.
-
- .. code-block:: rst
-
- .. doxygendefine:: PW_TOKENIZE_STRING
-
-.. admonition:: See also
-
- `All Breathe directives for use in RST files <https://breathe.readthedocs.io/en/latest/directives.html>`_
-
-Example Doxygen Comment Block
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Start a Doxygen comment block using ``///`` (three forward slashes).
-
-.. code-block:: cpp
-
- /// This is the documentation comment for the `PW_LOCK_RETURNED()` macro. It
- /// describes how to use the macro.
- ///
- /// Doxygen comments can refer to other symbols using Sphinx cross
- /// references. For example, @cpp_class{pw::InlineBasicString}, which is
- /// shorthand for @crossref{cpp,class,pw::InlineBasicString}, links to a C++
- /// class. @crossref{py,func,pw_tokenizer.proto.detokenize_fields} links to a
- /// Python function.
- ///
- /// @param[out] dest The memory area to copy to.
- /// @param[in] src The memory area to copy from.
- /// @param[in] n The number of bytes to copy
- ///
- /// @retval OK KVS successfully initialized.
- /// @retval DATA_LOSS KVS initialized and is usable, but contains corrupt data.
- /// @retval UNKNOWN Unknown error. KVS is not initialized.
- ///
- /// @rst
- /// The ``@rst`` and ``@endrst`` commands form a block block of
- /// reStructuredText that is rendered in Sphinx.
- ///
- /// .. warning::
- /// this is a warning admonition
- ///
- /// .. code-block:: cpp
- ///
- /// void release(ptrdiff_t update = 1);
- /// @endrst
- ///
- /// Example code block using Doxygen markup below. To override the language
- /// use `@code{.cpp}`
- ///
- /// @code
- /// class Foo {
- /// public:
- /// Mutex* mu() PW_LOCK_RETURNED(mu) { return &mu; }
- ///
- /// private:
- /// Mutex mu;
- /// };
- /// @endcode
- ///
- /// @b The first word in this sentence is bold (The).
- ///
- #define PW_LOCK_RETURNED(x) __attribute__((lock_returned(x)))
-
-Doxygen Syntax
-^^^^^^^^^^^^^^
-Pigweed prefers to use RST wherever possible, but there are a few Doxygen
-syntatic elements that may be needed.
-
-Common Doxygen commands for use within a comment block:
-
-- ``@rst`` To start a reStructuredText block. This is a custom alias for
- ``\verbatim embed:rst:leading-asterisk``. This must be paired with
- ``@endrst``.
-- `@code <https://www.doxygen.nl/manual/commands.html#cmdcode>`_ Start a code
- block. This must be paired with ``@endcode``.
-- `@param <https://www.doxygen.nl/manual/commands.html#cmdparam>`_ Document
- parameters, this may be repeated.
-- `@pre <https://www.doxygen.nl/manual/commands.html#cmdpre>`_ Starts a
- paragraph where the precondition of an entity can be described.
-- `@post <https://www.doxygen.nl/manual/commands.html#cmdpost>`_ Starts a
- paragraph where the postcondition of an entity can be described.
-- `@return <https://www.doxygen.nl/manual/commands.html#cmdreturn>`_ Single
- paragraph to describe return value(s).
-- `@retval <https://www.doxygen.nl/manual/commands.html#cmdretval>`_ Document
- return values by name. This is rendered as a definition list.
-- `@note <https://www.doxygen.nl/manual/commands.html#cmdnote>`_ Add a note
- admonition to the end of documentation.
-- `@b <https://www.doxygen.nl/manual/commands.html#cmdb>`_ To bold one word.
-
-.. tip:
-
- Did you add Doxygen comments and now your build is failing because Doxygen
- says it can't find the class you decorated? Make sure your ``@code`` blocks
- are paired with ``@endcode`` blocks and your ``@rst`` blocks are paired
- with ``@endrst`` blocks!
-
-Doxygen provides `structural commands
-<https://doxygen.nl/manual/docblocks.html#structuralcommands>`_ that associate a
-comment block with a particular symbol. Example of these include ``@class``,
-``@struct``, ``@def``, ``@fn``, and ``@file``. Do not use these unless
-necessary, since they are redundant with the declarations themselves.
-
-One case where structural commands are necessary is when a single comment block
-describes multiple symbols. To group multiple symbols into a single comment
-block, include a structural commands for each symbol on its own line. For
-example, the following comment documents two macros:
-
-.. code-block:: cpp
-
- /// @def PW_ASSERT_EXCLUSIVE_LOCK
- /// @def PW_ASSERT_SHARED_LOCK
- ///
- /// Documents functions that dynamically check to see if a lock is held, and
- /// fail if it is not held.
-
-.. seealso:: `All Doxygen commands <https://www.doxygen.nl/manual/commands.html>`_
-
-Cross-references
-^^^^^^^^^^^^^^^^
-Pigweed provides Doxygen aliases for creating Sphinx cross references within
-Doxygen comments. These should be used when referring to other symbols, such as
-functions, classes, or macros.
-
-.. inclusive-language: disable
-
-The basic alias is ``@crossref``, which supports any `Sphinx domain
-<https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html>`_.
-For readability, aliases for commonnly used types are provided.
-
-.. inclusive-language: enable
-
-- ``@crossref{domain,type,identifier}`` Inserts a cross reference of any type in
- any Sphinx domain. For example, ``@crossref{c,func,foo}`` is equivalent to
- ``:c:func:`foo``` and links to the documentation for the C function ``foo``,
- if it exists.
-- ``@c_macro{identifier}`` Equivalent to ``:c:macro:`identifier```.
-- ``@cpp_func{identifier}`` Equivalent to ``:cpp:func:`identifier```.
-- ``@cpp_class{identifier}`` Equivalent to ``:cpp:class:`identifier```.
-- ``@cpp_type{identifier}`` Equivalent to ``:cpp:type:`identifier```.
-
-.. note::
-
- Use the `@` aliases described above for all cross references. Doxygen
- provides other methods for cross references, but Sphinx cross references
- offer several advantages:
-
- - Sphinx cross references work for all identifiers known to Sphinx, including
- those documented with e.g. ``.. cpp:class::`` or extracted from Python.
- Doxygen references can only refer to identifiers known to Doxygen.
- - Sphinx cross references always use consistent formatting. Doxygen cross
- references sometimes render as plain text instead of code-style monospace,
- or include ``()`` in macros that shouldn't have them.
- - Sphinx cross references can refer to symbols that have not yet been
- documented. They will be formatted correctly and become links once the
- symbols are documented.
- - Using Sphinx cross references in Doxygen comments makes cross reference
- syntax more consistent within Doxygen comments and between RST and
- Doxygen.
-
-Create cross reference links elsewhere in the document to symbols documented
-with Doxygen using standard Sphinx cross references, such as ``:cpp:class:`` and
-``:cpp:func:``.
-
-.. code-block:: rst
-
- - :cpp:class:`pw::sync::BinarySemaphore::BinarySemaphore`
- - :cpp:func:`pw::sync::BinarySemaphore::try_acquire`
-
-.. seealso::
- - `C++ cross reference link syntax`_
- - `C cross reference link syntax`_
- - `Python cross reference link syntax`_
-
-.. _Sphinx: https://www.sphinx-doc.org/
-
-.. inclusive-language: disable
-
-.. _reStructuredText Primer: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
-.. _literalinclude: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-literalinclude
-.. _C++ cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing
-.. _C cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-c-constructs
-.. _Python cross reference link syntax: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects
-.. _autodoc directives: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#directives
-
-.. inclusive-language: enable
-
-Status codes in Doxygen comments
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Use the following syntax when referring to ``pw_status`` codes in Doxygen
-comments:
-
-.. code-block::
-
- @pw_status{YOUR_STATUS_CODE_HERE}
-
-Replace ``YOUR_STATUS_CODE_HERE`` with a valid ``pw_status`` code.
-
-This syntax ensures that Doxygen links back to the status code's
-reference documentation in :ref:`module-pw_status`.
-
-.. note::
-
- The guidance in this section only applies to Doxygen comments in C++ header
- files.
-
-Customize the depth of a page's table of contents
-=================================================
-Put ``:tocdepth: X`` on the first line of the page, where ``X`` equals how many
-levels of section heading you want to show in the page's table of contents. See
-``//docs/changelog.rst`` for an example.
-
-Changelog
-=========
-
-This section explains how we update the changelog.
-
-#. On the Friday before Pigweed Live, use
- `changelog <https://kaycebasques.github.io/changelog/>`_ to generate a first
- draft of the changelog.
-
-#. Copy-paste the reStructuredText output from the changelog tool to the top
- of ``//docs/changelog.rst``.
-
-#. Delete these lines from the previous update in ``changelog.rst``
- (which is no longer the latest update):
-
- * ``.. _docs-changelog-latest:``
- * ``.. changelog_highlights_start``
- * ``.. changelog_highlights_end``
-
-#. Polish up the auto-generated first draft into something more readable:
-
- * Don't change the section headings. The text in each section heading
- should map to one of the categories that we allow in our commit
- messages, such as ``bazel``, ``docs``, ``pw_base64``, and so on.
- * Add a 1-paragraph summary to each section.
- * Focus on features, important bug fixes, and breaking changes. Delete
- internal commits that Pigweed customers won't care about.
-
-#. Push your change up to Gerrit and kick off a dry run. After a few minutes
- the docs will get staged.
-
-#. Copy the rendered content from the staging site into the Pigweed Live
- Google Doc.
-
-#. Make sure to land the changelog updates the same week as Pigweed Live.
-
-There is no need to update ``//docs/index.rst``. The ``What's new in Pigweed``
-content on the homepage is pulled from the changelog (that's what the
-``docs-changelog-latest``, ``changelog_highlights_start``, and
-``changelog_highlights_end`` labels are for).
-
-Why "changelog" and not "release notes"?
-----------------------------------------
-Because Pigweed doesn't have releases.
-
-Why organize by module / category?
-----------------------------------
-Why is the changelog organized by category / module? Why not the usual
-3 top-level sections: features, fixes, breaking changes?
-
-* Because some Pigweed customers only use a few modules. Organizing by module
- helps them filter out all the changes that aren't relevant to them faster.
-* If we keep the changelog section heading text fairly structured, we may
- be able to present the changelog in other interesting ways. For example,
- it should be possible to collect every ``pw_base64`` section in the changelog
- and then provide a changelog for only ``pw_base64`` over in the ``pw_base64``
- docs.
-* The changelog tool is easily able to organize by module / category due to
- how we annotate our commits. We will not be able to publish changelog updates
- every 2 weeks if there is too much manual work involved.
-
-Site nav scrolling
-==================
-The site nav was customized (`change #162410`_) to scroll on initial page load
-so that the current page is visible in the site nav. The scrolling logic is
-handled in ``//docs/_static/js/pigweed.js``.
-
-The site nav scrolling logic runs on a 1-second delay after the ``load`` event
-to protect against a race condition between our scrolling logic and Sphinx's
-scrolling logic. For example, if the user visits
-``https://pigweed.dev/pw_tokenizer/design.html#bit-tokenization``, the page
-should first scroll to the ``#bit-tokenization`` section and then the site
-nav can scroll to the current page.
-
-.. _change #162410: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/162410
-
-Call-to-action buttons on sales pitch pages (docs.rst)
-======================================================
-Use the following directive to put call-to-action buttons on a ``docs.rst``
-page:
-
-.. code-block::
-
- .. grid:: 2
-
- .. grid-item-card:: :octicon:`zap` Get started & guides
- :link: <REF>
- :link-type: ref
- :class-item: sales-pitch-cta-primary
-
- Learn how to integrate <MODULE> into your project and implement
- common use cases.
-
- .. grid-item-card:: :octicon:`info` API reference
- :link: <REF>
- :link-type: ref
- :class-item: sales-pitch-cta-secondary
-
- Get detailed reference information about the <MODULE> API.
-
- .. grid:: 2
-
- .. grid-item-card:: :octicon:`info` CLI reference
- :link: <REF>
- :link-type: ref
- :class-item: sales-pitch-cta-secondary
-
- Get usage information about <MODULE> command line utilities.
-
- .. grid-item-card:: :octicon:`table` Design
- :link: <REF>
- :link-type: ref
- :class-item: sales-pitch-cta-secondary
-
- Read up on how <MODULE> is designed.
-
-* Remove cards for content that does not exist. For example, if the module
- doesn't have a CLI reference, remove the card for that doc.
-* Replace ``<REF>`` and ``<MODULE>``. Don't change anything else. We want
- a consistent call-to-action experience across all the modules.
-
-.. _commit-style:
-
---------------
-Commit message
---------------
-Pigweed commit message bodies and summaries are limited to 72 characters wide
-to improve readability. Commit summaries should also be prefixed with the name
-of the module that the commit is affecting. :ref:`Examples
-<docs-contributing-commit-message-examples>` of well and ill-formed commit
-messages are provided below.
-
-Consider the following when writing a commit message:
-
-#. **Documentation and comments are better** - Consider whether the commit
- message contents would be better expressed in the documentation or code
- comments. Docs and code comments are durable and readable later; commit
- messages are rarely read after the change lands.
-#. **Include why the change is made, not just what the change is** - It is
- important to include a "why" component in most commits. Sometimes, why is
- evident - for example, reducing memory usage, or optimizing. But it is often
- not. Err on the side of over-explaining why, not under-explaining why.
-
-.. _docs-contributing-commit-message-examples:
-
-Pigweed commit messages should conform to the following style:
-
-.. admonition:: **Yes**:
- :class: checkmark
-
- .. code-block:: none
-
- pw_some_module: Short capitalized description
-
- Details about the change here. Include a summary of the what, and a clear
- description of why the change is needed for future maintainers.
-
- Consider what parts of the commit message are better suited for
- documentation.
-
-.. admonition:: **Yes**: Small number of modules affected; use {} syntax.
- :class: checkmark
-
- .. code-block:: none
-
- pw_{foo, bar, baz}: Change something in a few places
-
- When changes cross a few modules, include them with the syntax shown
- above.
-
-.. admonition:: **Yes**: Targets are effectively modules, even though they're
- nested, so they get a ``/`` character.
- :class: checkmark
-
- .. code-block:: none
-
- targets/xyz123: Tweak support for XYZ's PQR
-
-.. admonition:: **Yes**: Uses imperative style for subject and text.
- :class: checkmark
-
- .. code-block:: none
-
- pw_something: Add foo and bar functions
-
- This commit correctly uses imperative present-tense style.
-
-.. admonition:: **No**: Uses non-imperative style for subject and text.
- :class: error
-
- .. code-block:: none
-
- pw_something: Adds more things
-
- Use present tense imperative style for subjects and commit. The above
- subject has a plural "Adds" which is incorrect; should be "Add".
-
-.. admonition:: **Yes**: Use bulleted lists when multiple changes are in a
- single CL. Prefer smaller CLs, but larger CLs are a practical reality.
- :class: checkmark
-
- .. code-block:: none
-
- pw_complicated_module: Pre-work for refactor
-
- Prepare for a bigger refactor by reworking some arguments before the larger
- change. This change must land in downstream projects before the refactor to
- enable a smooth transition to the new API.
-
- - Add arguments to MyImportantClass::MyFunction
- - Update MyImportantClass to handle precondition Y
- - Add stub functions to be used during the transition
-
-.. admonition:: **No**: Run on paragraph instead of bulleted list
- :class: error
-
- .. code-block:: none
-
- pw_foo: Many things in a giant BWOT
-
- This CL does A, B, and C. The commit message is a Big Wall Of Text
- (BWOT), which we try to discourage in Pigweed. Also changes X and Y,
- because Z and Q. Furthermore, in some cases, adds a new Foo (with Bar,
- because we want to). Also refactors qux and quz.
-
-.. admonition:: **No**: Doesn't capitalize the subject
- :class: error
-
- .. code-block:: none
-
- pw_foo: do a thing
-
- Above subject is incorrect, since it is a sentence style subject.
-
-.. admonition:: **Yes**: Doesn't capitalize the subject when subject's first
- word is a lowercase identifier.
- :class: checkmark
-
- .. code-block:: none
-
- pw_foo: std::unique_lock cleanup
-
- This commit message demonstrates the subject when the subject has an
- identifier for the first word. In that case, follow the identifier casing
- instead of capitalizing.
-
- However, imperative style subjects often have the identifier elsewhere in
- the subject; for example:
-
- .. code-block:: none
-
- pw_foo: Improve use of std::unique_lock
-
-.. admonition:: **No**: Uses a non-standard ``[]`` to indicate module:
- :class: error
-
- .. code-block:: none
-
- [pw_foo]: Do a thing
-
-.. admonition:: **No**: Has a period at the end of the subject
- :class: error
-
- .. code-block:: none
-
- pw_bar: Do something great.
-
-.. admonition:: **No**: Puts extra stuff after the module which isn't a module.
- :class: error
-
- .. code-block:: none
-
- pw_bar/byte_builder: Add more stuff to builder
-
-Footer
-======
-We support a number of `git footers`_ in the commit message, such as ``Bug:
-123`` in the message below:
-
-.. code-block:: none
-
- pw_something: Add foo and bar functions
-
- Bug: 123
-
-You are encouraged to use the following footers when appropriate:
-
-* ``Bug``: Associates this commit with a bug (issue in our `bug tracker`_). The
- bug will be automatically updated when the change is submitted. When a change
- is relevant to more than one bug, include multiple ``Bug`` lines, like so:
-
- .. code-block:: none
-
- pw_something: Add foo and bar functions
-
- Bug: 123
- Bug: 456
-
-* ``Fixed`` or ``Fixes``: Like ``Bug``, but automatically closes the bug when
- submitted.
-
- .. code-block:: none
-
- pw_something: Fix incorrect use of foo
-
- Fixes: 123
-
-In addition, we support all of the `Chromium CQ footers`_, but those are
-relatively rarely useful.
-
-.. _bug tracker: https://bugs.chromium.org/p/pigweed/issues/list
-.. _Chromium CQ footers: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/infra/cq.md#options
-.. _git footers: https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/git-footers.html
-
-Copy-to-clipboard feature on code blocks
-========================================
-
-.. _sphinx-copybutton: https://sphinx-copybutton.readthedocs.io/en/latest/
-.. _Remove copybuttons using a CSS selector: https://sphinx-copybutton.readthedocs.io/en/latest/use.html#remove-copybuttons-using-a-css-selector
-
-The copy-to-clipboard feature on code blocks is powered by `sphinx-copybutton`_.
-
-``sphinx-copybutton`` recognizes ``$`` as an input prompt and automatically
-removes it.
-
-There is a workflow for manually removing the copy-to-clipboard button for a
-particular code block but it has not been implemented yet. See
-`Remove copybuttons using a CSS selector`_.
-
-Grouping related content with tabs
-==================================
-Use the ``tab-set`` directive to group related content together. This feature is
-powered by `sphinx-design Tabs
-<https://sphinx-design.readthedocs.io/en/furo-theme/tabs.html>`_
-
-Tabs for code-only content
---------------------------
-Use the ``tabs`` and ``code-tab`` directives together. Example:
-
-.. code-block:: rst
-
- .. tab-set-code::
-
- .. code-block:: c++
-
- // C++ code...
-
- .. code-block:: python
-
- # Python code...
-
-Rendered output:
-
-.. tab-set-code::
-
- .. code-block:: c++
-
- // C++ code...
-
- .. code-block:: python
-
- # Python code...
-
-Tabs for all other content
---------------------------
-Use the ``tabs`` and ``tab-item`` directives together. Example:
-
-.. code-block:: rst
-
- .. tab-set::
-
- .. tab-item:: Linux
-
- Linux instructions...
-
- .. tab-item:: Windows
-
- Windows instructions...
-
-Rendered output:
-
-.. tab-set::
-
- .. tab-item:: Linux
-
- Linux instructions...
-
- .. tab-item:: Windows
-
- Windows instructions...
-
-Tab synchronization
--------------------
-Tabs are synchronized in two ways:
-
-1. ``tab-set-code::`` ``code-block`` languages names.
-2. ``tab-item::`` ``:sync:`` values.
-
-For Example:
-
-.. code-block:: rst
-
- .. tabs-set-code::
-
- .. code-block:: c++
-
- // C++ code...
-
- .. code-block:: py
-
- # Python code...
-
- .. tabs-set-code::
-
- .. code-block:: c++
-
- // More C++ code...
-
- .. code-block:: py
-
- # More Python code...
-
- .. tab-set::
-
- .. tab-item:: Linux
- :sync: key1
-
- Linux instructions...
-
- .. tab-item:: Windows
- :sync: key2
-
- Windows instructions...
-
- .. tab-set::
-
- .. tab-item:: Linux
- :sync: key1
-
- More Linux instructions...
-
- .. tab-item:: Windows
- :sync: key2
-
- More Windows instructions...
-
-Rendered output:
-
-.. tab-set-code::
-
- .. code-block:: c++
-
- // C++ code...
-
- .. code-block:: py
-
- # Python code...
-
-.. tab-set-code::
-
- .. code-block:: c++
-
- // More C++ code...
-
- .. code-block:: py
-
- # More Python code...
-
-.. tab-set::
-
- .. tab-item:: Linux
- :sync: key1
-
- Linux instructions...
-
- .. tab-item:: Windows
- :sync: key2
-
- Windows instructions...
+.. _owners-style:
-.. tab-set::
+--------------------
+Code Owners (OWNERS)
+--------------------
+If you use Gerrit for source code hosting and have the
+`code-owners <https://android-review.googlesource.com/plugins/code-owners/Documentation/backend-find-owners.html>`__
+plug-in enabled Pigweed can help you enforce consistent styling on those files
+and also detects some errors.
- .. tab-item:: Linux
- :sync: key1
+The styling follows these rules.
- More Linux instructions...
+#. Content is grouped by type of line (Access grant, include, etc).
+#. Each grouping is sorted alphabetically.
+#. Groups are placed the following order with a blank line separating each
+ grouping.
- .. tab-item:: Windows
- :sync: key2
+ * "set noparent" line
+ * "include" lines
+ * "file:" lines
+ * user grants (some examples: "*", "foo@example.com")
+ * "per-file:" lines
- More Windows instructions...
+This plugin will, by default, act upon any file named "OWNERS".
+.. toctree::
+ :hidden:
+ C++ <style/cpp>
+ Commit message <style/commit_message>
+ Doxygen documentation <style/doxygen>
+ Sphinx documentation <style/sphinx>
diff --git a/docs/targets.rst b/docs/targets.rst
index 6e8dba1b7..47aca6dad 100644
--- a/docs/targets.rst
+++ b/docs/targets.rst
@@ -49,9 +49,7 @@ build argument defined in Pigweed. Some notable arguments include:
template. This is typically used to set compiler flags, optimization levels,
global #defines, etc.
* ``default_public_deps``: List of GN targets which are added as a dependency
- to all ``pw_*`` GN targets. This is used to add global module dependencies;
- for example, in upstream, ``pw_polyfill`` is added here to provide C++17
- features in C++14 code.
+ to all ``pw_*`` GN targets. This is used to add global module dependencies.
* Facade backends: Pigweed defines facades to provide a common interface for
core system features such as logging without assuming an implementation.
When building a Pigweed target, the implementations for each of these must be
diff --git a/docs/templates/docs/api.rst b/docs/templates/docs/api.rst
deleted file mode 100644
index 7ce13f745..000000000
--- a/docs/templates/docs/api.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-.. _module-MODULE_NAME-api:
-
-=============
-API Reference
-=============
-..
- If a module includes a developer-facing API, this is the place to include API
- documentation. This doc may be omitted for experimental modules that are not
- yet ready for public use.
-
- A module may have APIs for multiple languages. If so, replace this file with
- an `api` directory and an `index.rst` file that links to separate docs for
- each supported language.
diff --git a/docs/templates/docs/cli.rst b/docs/templates/docs/cli.rst
deleted file mode 100644
index a3afc0185..000000000
--- a/docs/templates/docs/cli.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-.. _module-MODULE_NAME-cli:
-
-=============
-CLI Reference
-=============
-..
- If the module has a command-line interface, document that here. For Python
- CLIs, the `sphinx-argparse package
- <https://sphinx-argparse.readthedocs.io/en/stable/usage.html>`_ makes this
- very simple and is already included in Pigweed. This doc may be omitted for
- experimental modules that are not yet ready for public use.
diff --git a/docs/templates/docs/concepts.rst b/docs/templates/docs/concepts.rst
deleted file mode 100644
index 9dde7063b..000000000
--- a/docs/templates/docs/concepts.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. _module-MODULE_NAME-concepts:
-
-========
-Concepts
-========
-..
- This doc describes fundamental concepts related to the problem the module
- solves which are independent of the module's solution. Topics related to this
- module's solution and specific design should be in ``design.rst``.
diff --git a/docs/templates/docs/design.rst b/docs/templates/docs/design.rst
deleted file mode 100644
index b214229c3..000000000
--- a/docs/templates/docs/design.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-.. _module-MODULE_NAME-design:
-
-======
-Design
-======
-..
- This doc provides background on how a module works internally, the assumptions
- inherent in its design, why this particular design was chosen over others, and
- other topics of that nature.
-
- This doc is only required when the "Design considerations" section in the
- index becomes large enough that a separate doc is needed to maintain
- readability. If *this* doc becomes too large, it can be replaced with a
- ``design`` subdirectory.
diff --git a/docs/templates/docs/docs.rst b/docs/templates/docs/docs.rst
deleted file mode 100644
index f223d3fef..000000000
--- a/docs/templates/docs/docs.rst
+++ /dev/null
@@ -1,117 +0,0 @@
-.. _module-MODULE_NAME:
-
-===========
-MODULE_NAME
-===========
-..
- A short description of the module. Aim for three sentences at most that
- clearly convey what the module does.
-
-.. card::
-
- :octicon:`comment-discussion` Status:
- :bdg-primary:`Experimental`
- :octicon:`chevron-right`
- :bdg-secondary-line:`Unstable`
- :octicon:`chevron-right`
- :bdg-secondary-line:`Stable`
- :octicon:`kebab-horizontal`
- :bdg-primary:`Current`
- :octicon:`chevron-right`
- :bdg-secondary-line:`Deprecated`
-
- :octicon:`multi-select` Backends: :ref:`module-pw_rpc`
-
- :octicon:`copy` Facade: :ref:`module-pw_rpc`
-
-----------
-Background
-----------
-..
- Describe *what* problem this module solves. You can make references to other
- existing solutions to highlight why their approaches were not suitable, but
- avoid describing *how* this module solves the problem in this section.
-
- For some modules, this section may need to include fundamental concepts about
- the problem being solved. Those should be mentioned briefly here, but covered
- more extensively in ``concepts.rst``.
-
-------------
-Our solution
-------------
-..
- Describe *how* this module addresses the problems highlighted in section
- above. This is a great place to include artifacts other than text to more
- effectively deliver the message. For example, consider including images or
- animations for UI-related modules, or code samples illustrating a superior API
- in libraries.
-
-Design considerations
----------------------
-..
- Briefly explain any pertinent design considerations and trade-offs. For
- example, perhaps this module optimizes for very small RAM and Flash usage at
- the expense of additional CPU time, where other solutions might make a
- different choice.
-
- Many modules will have more extensive documentation on design and
- implementation details. If so, that additional content should be in
- ``design.rst`` and linked to from this section.
-
-Size report
------------
-..
- If this module includes code that runs on target, include the size report(s)
- here.
-
-Roadmap
--------
-..
- What are the broad future plans for this module? What remains to be built, and
- what are some known issues? Are there opportunities for contribution?
-
----------------
-Who this is for
----------------
-..
- Highlight the use cases that are appropriate for this module. This is the
- place to make the sales pitch-why should developers use this module, and what
- makes this module stand out from alternative solutions that try to address
- this problem.
-
---------------------
-Is it right for you?
---------------------
-..
- This module may not solve all problems or be appropriate for all
- circumstances. This is the place to add those caveats. Highly-experimental
- modules that are under very active development might be a poor fit for any
- developer right now. If so, mention that here.
-
----------------
-Getting started
----------------
-..
- Explain how developers use this module in their project, including any
- prerequisites and conditions. This section should cover the fastest and
- simplest ways to get started (for example, "add this dependency in GN",
- "include this header"). More complex and specific use cases should be covered
- in guides that are linked in this section.
-
---------
-Contents
---------
-.. toctree::
- :maxdepth: 1
-
- concepts
- design
- guides
- api
- cli
- gui
-
-.. toctree::
- :maxdepth: 2
-
- tutorials/index
diff --git a/docs/templates/docs/gui.rst b/docs/templates/docs/gui.rst
deleted file mode 100644
index fa63ff7d6..000000000
--- a/docs/templates/docs/gui.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-.. _module-MODULE_NAME-gui:
-
-=============
-GUI Reference
-=============
-..
- If the module has a graphic interface of any kind (including text-mode
- interfaces and web applications), document its usage here. This doc may be
- omitted for experimental modules that are not yet ready for public use.
diff --git a/docs/templates/docs/guides.rst b/docs/templates/docs/guides.rst
deleted file mode 100644
index 50348dd3f..000000000
--- a/docs/templates/docs/guides.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-.. _module-MODULE_NAME-guides:
-
-======
-Guides
-======
-..
- Guides are task-oriented. They are essentially steps or recipe a user should
- follow to complete a particular goal.
-
- Each module should have a minimum of two guides: one guide on how to integrate
- the module with the user's project, and one guide on the most common use case
- for the module. Most modules ought to have many more guides than this.
-
- If this document becomes to large and difficult to navigate, replace it with a
- ``guides`` subdirectory.
diff --git a/docs/templates/docs/tutorials/index.rst b/docs/templates/docs/tutorials/index.rst
deleted file mode 100644
index 037bec8bb..000000000
--- a/docs/templates/docs/tutorials/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-.. _module-MODULE_NAME-codelabs:
-
-=========
-Tutorials
-=========
-..
- Tutorials are primarily learning-oriented; they are designed to teach the user
- about a concept or feature so that they can achieve basic competence.
-
- If your module has tutorials, this index should link to each of them so that
- they're populated in the table of contents. Also consider adding narrative
- instead of simply adding a bulleted list. Guide the user through the
- curriculum they should follow to become knowledgable about the module.
diff --git a/pw_allocator/BUILD.bazel b/pw_allocator/BUILD.bazel
index 6fb346750..c7c7d9ae3 100644
--- a/pw_allocator/BUILD.bazel
+++ b/pw_allocator/BUILD.bazel
@@ -57,17 +57,18 @@ pw_cc_library(
pw_cc_library(
name = "block",
- srcs = [
- "block.cc",
- ],
+ srcs = ["block.cc"],
hdrs = [
"public/pw_allocator/block.h",
],
includes = ["public"],
deps = [
"//pw_assert",
+ "//pw_bytes",
+ "//pw_result",
"//pw_span",
"//pw_status",
+ "//third_party/fuchsia:stdcompat",
],
)
@@ -120,12 +121,12 @@ pw_cc_library(
)
pw_cc_library(
- name = "split_free_list_allocator",
+ name = "libc_allocator",
srcs = [
- "split_free_list_allocator.cc",
+ "libc_allocator.cc",
],
hdrs = [
- "public/pw_allocator/split_free_list_allocator.h",
+ "public/pw_allocator/libc_allocator.h",
],
includes = ["public"],
deps = [
@@ -137,30 +138,45 @@ pw_cc_library(
)
pw_cc_library(
- name = "libc_allocator",
- srcs = [
- "libc_allocator.cc",
+ name = "null_allocator",
+ hdrs = [
+ "public/pw_allocator/null_allocator.h",
],
+ includes = ["public"],
+ deps = [
+ ":allocator",
+ ],
+)
+
+pw_cc_library(
+ name = "simple_allocator",
hdrs = [
- "public/pw_allocator/libc_allocator.h",
+ "public/pw_allocator/simple_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
- "//pw_assert",
+ ":block",
"//pw_bytes",
- "//pw_status",
],
)
pw_cc_library(
- name = "null_allocator",
+ name = "split_free_list_allocator",
+ srcs = [
+ "split_free_list_allocator.cc",
+ ],
hdrs = [
- "public/pw_allocator/null_allocator.h",
+ "public/pw_allocator/split_free_list_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
+ ":block",
+ "//pw_assert",
+ "//pw_bytes",
+ "//pw_result",
+ "//pw_status",
],
)
@@ -170,13 +186,16 @@ pw_cc_library(
"allocator_testing.cc",
],
hdrs = [
- "pw_allocator_private/allocator_testing.h",
+ "public/pw_allocator/allocator_testing.h",
],
+ includes = ["public"],
deps = [
":allocator",
":block",
+ ":simple_allocator",
"//pw_assert",
"//pw_bytes",
+ "//pw_unit_test",
],
)
@@ -276,14 +295,38 @@ pw_cc_test(
)
pw_cc_test(
+ name = "simple_allocator_test",
+ srcs = [
+ "simple_allocator_test.cc",
+ ],
+ deps = [
+ ":allocator_testing",
+ ":simple_allocator",
+ ],
+)
+
+pw_cc_test(
name = "split_free_list_allocator_test",
srcs = [
"split_free_list_allocator_test.cc",
],
deps = [
+ ":allocator_testing",
":split_free_list_allocator",
"//pw_bytes",
"//pw_containers:vector",
"//pw_unit_test",
],
)
+
+pw_cc_test(
+ name = "unique_ptr_test",
+ srcs = [
+ "unique_ptr_test.cc",
+ ],
+ deps = [
+ ":allocator",
+ ":allocator_testing",
+ "//pw_unit_test",
+ ],
+)
diff --git a/pw_allocator/BUILD.gn b/pw_allocator/BUILD.gn
index 2c74c3106..e7039f0a1 100644
--- a/pw_allocator/BUILD.gn
+++ b/pw_allocator/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2020 The Pigweed Authors
+# Copyright 2023 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -14,6 +14,7 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_bloat/bloat.gni")
import("$dir_pw_build/target_types.gni")
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_unit_test/test.gni")
@@ -69,10 +70,13 @@ pw_source_set("block") {
configs = [ ":enable_heap_poison" ]
public = [ "public/pw_allocator/block.h" ]
public_deps = [
- dir_pw_assert,
+ "$dir_pw_third_party/fuchsia:stdcompat",
+ dir_pw_bytes,
+ dir_pw_result,
dir_pw_span,
dir_pw_status,
]
+ deps = [ dir_pw_assert ]
sources = [ "block.cc" ]
}
@@ -132,20 +136,51 @@ pw_source_set("null_allocator") {
public_deps = [ ":allocator" ]
}
+pw_source_set("simple_allocator") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/pw_allocator/simple_allocator.h" ]
+ public_deps = [
+ ":allocator",
+ ":block",
+ dir_pw_bytes,
+ ]
+}
+
pw_source_set("split_free_list_allocator") {
public_configs = [ ":default_config" ]
public = [ "public/pw_allocator/split_free_list_allocator.h" ]
public_deps = [
":allocator",
- dir_pw_status,
- ]
- deps = [
- dir_pw_assert,
+ ":block",
dir_pw_bytes,
+ dir_pw_result,
+ dir_pw_status,
]
+ deps = [ dir_pw_assert ]
sources = [ "split_free_list_allocator.cc" ]
}
+pw_size_diff("allocator_size_report") {
+ title = "Sizes of various pw_allocator implementations"
+ binaries = [
+ {
+ target = "size_report:split_free_list_allocator"
+ base = "$dir_pw_bloat:bloat_base"
+ label = "SplitFreeListAllocator"
+ },
+ {
+ target = "size_report:split_free_list_allocator_with_unique_ptr"
+ base = "size_report:split_free_list_allocator"
+ label = "Allocator::MakeUnique and UniquePtr"
+ },
+ {
+ target = "size_report:split_free_list_allocator_with_metric_proxy"
+ base = "size_report:split_free_list_allocator"
+ label = "AllocatorMetricProxy wrapping another allocator"
+ },
+ ]
+}
+
pw_test_group("tests") {
tests = [
":allocator_test",
@@ -156,18 +191,22 @@ pw_test_group("tests") {
":freelist_heap_test",
":libc_allocator_test",
":null_allocator_test",
+ ":simple_allocator_test",
":split_free_list_allocator_test",
+ ":unique_ptr_test",
]
}
pw_source_set("allocator_testing") {
- public = [ "pw_allocator_private/allocator_testing.h" ]
+ public = [ "public/pw_allocator/allocator_testing.h" ]
public_deps = [
":allocator",
":block",
- dir_pw_assert,
+ ":simple_allocator",
dir_pw_bytes,
+ dir_pw_unit_test,
]
+ deps = [ dir_pw_assert ]
sources = [ "allocator_testing.cc" ]
}
@@ -232,8 +271,19 @@ pw_test("null_allocator_test") {
sources = [ "null_allocator_test.cc" ]
}
+pw_test("simple_allocator_test") {
+ configs = [ ":enable_heap_poison" ]
+ deps = [
+ ":allocator_testing",
+ ":simple_allocator",
+ ]
+ sources = [ "simple_allocator_test.cc" ]
+}
+
pw_test("split_free_list_allocator_test") {
+ configs = [ ":enable_heap_poison" ]
deps = [
+ ":allocator_testing",
":split_free_list_allocator",
"$dir_pw_containers:vector",
dir_pw_bytes,
@@ -241,7 +291,16 @@ pw_test("split_free_list_allocator_test") {
sources = [ "split_free_list_allocator_test.cc" ]
}
+pw_test("unique_ptr_test") {
+ deps = [ ":allocator_testing" ]
+ sources = [ "unique_ptr_test.cc" ]
+}
+
pw_doc_group("docs") {
- inputs = [ "doc_resources/pw_allocator_heap_visualizer_demo.png" ]
+ inputs = [
+ "doc_resources/pw_allocator_heap_visualizer_demo.png",
+ "public/pw_allocator/simple_allocator.h",
+ ]
sources = [ "docs.rst" ]
+ report_deps = [ ":allocator_size_report" ]
}
diff --git a/pw_allocator/CMakeLists.txt b/pw_allocator/CMakeLists.txt
index 0b2ca8263..6f923edc7 100644
--- a/pw_allocator/CMakeLists.txt
+++ b/pw_allocator/CMakeLists.txt
@@ -49,9 +49,13 @@ pw_add_library(pw_allocator.block STATIC
PUBLIC_INCLUDES
public
PUBLIC_DEPS
- pw_assert
+ pw_bytes
+ pw_result
pw_span
pw_status
+ pw_third_party.fuchsia.stdcompat
+ PRIVATE_DEPS
+ pw_assert
SOURCES
block.cc
)
@@ -98,11 +102,11 @@ pw_add_library(pw_allocator.freelist_heap STATIC
freelist_heap.cc
)
-pw_add_library(pw_allocator.split_free_list_allocator STATIC
+pw_add_library(pw_allocator.libc_allocator STATIC
SOURCES
- split_free_list_allocator.cc
+ libc_allocator.cc
HEADERS
- public/pw_allocator/split_free_list_allocator.h
+ public/pw_allocator/libc_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
@@ -113,38 +117,54 @@ pw_add_library(pw_allocator.split_free_list_allocator STATIC
pw_bytes
)
-pw_add_library(pw_allocator.libc_allocator STATIC
- SOURCES
- libc_allocator.cc
+pw_add_library(pw_allocator.null_allocator INTERFACE
HEADERS
- public/pw_allocator/libc_allocator.h
+ public/pw_allocator/null_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
- pw_status
- PRIVATE_DEPS
- pw_assert
+)
+
+pw_add_library(pw_allocator.simple_allocator INTERFACE
+ HEADERS
+ public/pw_allocator/simple_allocator.h
+ PUBLIC_DEPS
+ pw_allocator.allocator
+ pw_allocator.block
pw_bytes
)
-pw_add_library(pw_allocator.null_allocator INTERFACE
+pw_add_library(pw_allocator.split_free_list_allocator STATIC
HEADERS
- public/pw_allocator/null_allocator.h
+ public/pw_allocator/split_free_list_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
+ pw_allocator.block
+ pw_bytes
+ pw_result
+ pw_status
+ PRIVATE_DEPS
+ pw_assert
+ SOURCES
+ split_free_list_allocator.cc
)
pw_add_library(pw_allocator.allocator_testing STATIC
HEADERS
- pw_allocator_private/allocator_testing.h
+ public/pw_allocator/allocator_testing.h
+ PUBLIC_INCLUDES
+ public
PUBLIC_DEPS
pw_allocator.allocator
pw_allocator.block
- pw_assert
+ pw_allocator.simple_allocator
pw_bytes
+ PRIVATE_DEPS
+ pw_assert
+ pw_unit_test
SOURCES
allocator_testing.cc
)
@@ -239,10 +259,19 @@ pw_add_test(pw_allocator.null_allocator_test
pw_allocator
)
+pw_add_test(pw_allocator.simple_allocator_test
+ SOURCES
+ simple_allocator_test.cc
+ PRIVATE_DEPS
+ pw_allocator.allocator_testing
+ pw_allocator.simple_allocator
+)
+
pw_add_test(pw_allocator.split_free_list_allocator_test
SOURCES
split_free_list_allocator_test.cc
PRIVATE_DEPS
+ pw_allocator.allocator_testing
pw_allocator.split_free_list_allocator
pw_containers.vector
pw_bytes
@@ -251,3 +280,14 @@ pw_add_test(pw_allocator.split_free_list_allocator_test
modules
pw_allocator
)
+
+pw_add_test(pw_allocator.unique_ptr_test
+ SOURCES
+ unique_ptr_test.cc
+ PRIVATE_DEPS
+ pw_allocator.allocator
+ pw_allocator.allocator_testing
+ GROUPS
+ modules
+ pw_allocator
+)
diff --git a/pw_allocator/allocator.cc b/pw_allocator/allocator.cc
index 15cca58bc..f63bacd69 100644
--- a/pw_allocator/allocator.cc
+++ b/pw_allocator/allocator.cc
@@ -22,23 +22,20 @@
namespace pw::allocator {
-void* Allocator::DoReallocate(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
+void* Allocator::DoReallocate(void* ptr, Layout layout, size_t new_size) {
if (new_size == 0) {
return nullptr;
}
- if (DoResize(ptr, old_size, old_alignment, new_size)) {
+ if (Resize(ptr, layout, new_size)) {
return ptr;
}
- void* new_ptr = DoAllocate(new_size, old_alignment);
+ void* new_ptr = DoAllocate(Layout(new_size, layout.alignment()));
if (new_ptr == nullptr) {
return nullptr;
}
- if (ptr != nullptr && old_size != 0) {
- std::memcpy(new_ptr, ptr, old_size);
- DoDeallocate(ptr, old_size, old_alignment);
+ if (ptr != nullptr && layout.size() != 0) {
+ std::memcpy(new_ptr, ptr, layout.size());
+ DoDeallocate(ptr, layout);
}
return new_ptr;
}
diff --git a/pw_allocator/allocator_metric_proxy.cc b/pw_allocator/allocator_metric_proxy.cc
index 98f3bc930..10ca68d3a 100644
--- a/pw_allocator/allocator_metric_proxy.cc
+++ b/pw_allocator/allocator_metric_proxy.cc
@@ -31,20 +31,18 @@ void AllocatorMetricProxy::Initialize(Allocator& allocator) {
memusage_.Add(count_);
}
-Status AllocatorMetricProxy::DoQuery(const void* ptr,
- size_t size,
- size_t alignment) const {
+Status AllocatorMetricProxy::DoQuery(const void* ptr, Layout layout) const {
PW_DCHECK_NOTNULL(allocator_);
- return allocator_->QueryUnchecked(ptr, size, alignment);
+ return allocator_->Query(ptr, layout);
}
-void* AllocatorMetricProxy::DoAllocate(size_t size, size_t alignment) {
+void* AllocatorMetricProxy::DoAllocate(Layout layout) {
PW_DCHECK_NOTNULL(allocator_);
- void* ptr = allocator_->AllocateUnchecked(size, alignment);
+ void* ptr = allocator_->Allocate(layout);
if (ptr == nullptr) {
return nullptr;
}
- used_.Increment(size);
+ used_.Increment(layout.size());
if (used_.value() > peak_.value()) {
peak_.Set(used_.value());
}
@@ -52,30 +50,25 @@ void* AllocatorMetricProxy::DoAllocate(size_t size, size_t alignment) {
return ptr;
}
-void AllocatorMetricProxy::DoDeallocate(void* ptr,
- size_t size,
- size_t alignment) {
+void AllocatorMetricProxy::DoDeallocate(void* ptr, Layout layout) {
PW_DCHECK_NOTNULL(allocator_);
- allocator_->DeallocateUnchecked(ptr, size, alignment);
+ allocator_->Deallocate(ptr, layout);
if (ptr == nullptr) {
return;
}
- PW_DCHECK_UINT_GE(used_.value(), size);
+ PW_DCHECK_UINT_GE(used_.value(), layout.size());
PW_DCHECK_UINT_NE(count_.value(), 0);
- used_.Set(used_.value() - size);
+ used_.Set(used_.value() - layout.size());
count_.Set(count_.value() - 1);
}
-bool AllocatorMetricProxy::DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
+bool AllocatorMetricProxy::DoResize(void* ptr, Layout layout, size_t new_size) {
PW_DCHECK_NOTNULL(allocator_);
- if (!allocator_->ResizeUnchecked(ptr, old_size, old_alignment, new_size)) {
+ if (!allocator_->Resize(ptr, layout, new_size)) {
return false;
}
- PW_DCHECK_UINT_GE(used_.value(), old_size);
- used_.Set(used_.value() - old_size + new_size);
+ PW_DCHECK_UINT_GE(used_.value(), layout.size());
+ used_.Set(used_.value() - layout.size() + new_size);
if (used_.value() > peak_.value()) {
peak_.Set(used_.value());
}
diff --git a/pw_allocator/allocator_metric_proxy_test.cc b/pw_allocator/allocator_metric_proxy_test.cc
index e426349a9..cfc94b2f0 100644
--- a/pw_allocator/allocator_metric_proxy_test.cc
+++ b/pw_allocator/allocator_metric_proxy_test.cc
@@ -15,27 +15,23 @@
#include "pw_allocator/allocator_metric_proxy.h"
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
namespace pw::allocator {
namespace {
// Test fixtures.
-struct AllocatorMetricProxyTest : ::testing::Test {
- private:
- std::array<std::byte, 256> buffer = {};
- test::FakeAllocator wrapped;
+class AllocatorMetricProxyTest : public ::testing::Test {
+ protected:
+ AllocatorMetricProxyTest() : allocator(0) {}
- public:
- AllocatorMetricProxy allocator;
+ void SetUp() override { allocator.Initialize(*wrapped); }
- AllocatorMetricProxyTest() : allocator(0) {}
+ AllocatorMetricProxy allocator;
- void SetUp() override {
- EXPECT_TRUE(wrapped.Initialize(buffer).ok());
- allocator.Initialize(wrapped);
- }
+ private:
+ test::AllocatorForTestWithBuffer<256> wrapped;
};
// Unit tests.
diff --git a/pw_allocator/allocator_test.cc b/pw_allocator/allocator_test.cc
index bbd3f6c77..c41356ca8 100644
--- a/pw_allocator/allocator_test.cc
+++ b/pw_allocator/allocator_test.cc
@@ -17,7 +17,7 @@
#include <cstddef>
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_bytes/alignment.h"
namespace pw::allocator {
@@ -25,14 +25,15 @@ namespace {
// Test fixtures.
-struct AllocatorTest : ::testing::Test {
- private:
- std::array<std::byte, 256> buffer = {};
+class AllocatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override { EXPECT_EQ(allocator.Init(buffer), OkStatus()); }
+ void TearDown() override { allocator.DeallocateAll(); }
- public:
- test::FakeAllocator allocator;
+ test::AllocatorForTest allocator;
- void SetUp() override { EXPECT_EQ(allocator.Initialize(buffer), OkStatus()); }
+ private:
+ std::array<std::byte, 256> buffer = {};
};
// Unit tests
@@ -42,11 +43,6 @@ TEST_F(AllocatorTest, ReallocateNull) {
size_t new_size = old_layout.size();
void* new_ptr = allocator.Reallocate(nullptr, old_layout, new_size);
- // Reallocate should call Resize.
- EXPECT_EQ(allocator.resize_ptr(), nullptr);
- EXPECT_EQ(allocator.resize_old_size(), old_layout.size());
- EXPECT_EQ(allocator.resize_new_size(), new_size);
-
// Resize should fail and Reallocate should call Allocate.
EXPECT_EQ(allocator.allocate_size(), new_size);
diff --git a/pw_allocator/allocator_testing.cc b/pw_allocator/allocator_testing.cc
index 337b89d20..269c47d1d 100644
--- a/pw_allocator/allocator_testing.cc
+++ b/pw_allocator/allocator_testing.cc
@@ -12,32 +12,35 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_assert/check.h"
-#include "pw_bytes/alignment.h"
namespace pw::allocator::test {
-static Block* NextBlock(Block* block) {
- return block->Last() ? nullptr : block->Next();
+AllocatorForTest::~AllocatorForTest() {
+ for (auto* block : allocator_.blocks()) {
+ PW_DCHECK(
+ !block->Used(),
+ "The block at %p was still in use when its allocator was "
+ "destroyed. All memory allocated by an allocator must be released "
+ "before the allocator goes out of scope.",
+ static_cast<void*>(block));
+ }
}
-Status FakeAllocator::Initialize(ByteSpan buffer) {
- if (auto status = Block::Init(buffer, &head_); !status.ok()) {
- return status;
- }
+Status AllocatorForTest::Init(ByteSpan bytes) {
ResetParameters();
- return OkStatus();
+ return allocator_.Init(bytes);
}
-void FakeAllocator::Exhaust() {
- for (Block* block = head_; block != nullptr; block = NextBlock(block)) {
+void AllocatorForTest::Exhaust() {
+ for (auto* block : allocator_.blocks()) {
block->MarkUsed();
}
}
-void FakeAllocator::ResetParameters() {
+void AllocatorForTest::ResetParameters() {
allocate_size_ = 0;
deallocate_ptr_ = nullptr;
deallocate_size_ = 0;
@@ -46,75 +49,33 @@ void FakeAllocator::ResetParameters() {
resize_new_size_ = 0;
}
-Status FakeAllocator::DoQuery(const void* ptr, size_t size, size_t) const {
- PW_CHECK(head_ != nullptr);
- if (size == 0 || ptr == nullptr) {
- return Status::OutOfRange();
- }
- const auto* bytes = static_cast<const std::byte*>(ptr);
- Block* block = Block::FromUsableSpace(const_cast<std::byte*>(bytes));
- if (!block->IsValid()) {
- return Status::OutOfRange();
+void AllocatorForTest::DeallocateAll() {
+ for (auto* block : allocator_.blocks()) {
+ BlockType::Free(block);
}
- size = AlignUp(size, alignof(Block));
- for (Block* curr = head_; curr != nullptr; curr = NextBlock(curr)) {
- if (curr == block && curr->InnerSize() == size) {
- return OkStatus();
- }
- }
- return Status::OutOfRange();
+ ResetParameters();
}
-void* FakeAllocator::DoAllocate(size_t size, size_t) {
- PW_CHECK(head_ != nullptr);
- allocate_size_ = size;
- for (Block* block = head_; block != nullptr; block = NextBlock(block)) {
- Block* fragment = nullptr;
- if (!block->Used() && block->Split(size, &fragment).ok()) {
- block->MarkUsed();
- return block->UsableSpace();
- }
- }
- return nullptr;
+Status AllocatorForTest::DoQuery(const void* ptr, Layout layout) const {
+ return allocator_.Query(ptr, layout);
}
-void FakeAllocator::DoDeallocate(void* ptr, size_t size, size_t alignment) {
- PW_CHECK(head_ != nullptr);
+void* AllocatorForTest::DoAllocate(Layout layout) {
+ allocate_size_ = layout.size();
+ return allocator_.Allocate(layout);
+}
+
+void AllocatorForTest::DoDeallocate(void* ptr, Layout layout) {
deallocate_ptr_ = ptr;
- deallocate_size_ = size;
- if (!DoQuery(ptr, size, alignment).ok()) {
- return;
- }
- Block* block = Block::FromUsableSpace(static_cast<std::byte*>(ptr));
- block->MarkFree();
- block->MergeNext().IgnoreError();
- block->MergePrev().IgnoreError();
+ deallocate_size_ = layout.size();
+ return allocator_.Deallocate(ptr, layout);
}
-bool FakeAllocator::DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
- PW_CHECK(head_ != nullptr);
+bool AllocatorForTest::DoResize(void* ptr, Layout layout, size_t new_size) {
resize_ptr_ = ptr;
- resize_old_size_ = old_size;
+ resize_old_size_ = layout.size();
resize_new_size_ = new_size;
- if (!DoQuery(ptr, old_size, old_alignment).ok() || old_size == 0 ||
- new_size == 0) {
- return false;
- }
- if (old_size == new_size) {
- return true;
- }
- Block* block = Block::FromUsableSpace(static_cast<std::byte*>(ptr));
- block->MarkFree();
- block->MergeNext().IgnoreError();
- Block* fragment = nullptr;
- if (block->Split(new_size, &fragment) == Status::OutOfRange()) {
- block->Split(old_size, &fragment).IgnoreError();
- }
- block->MarkUsed();
- return block->InnerSize() >= new_size;
+ return allocator_.Resize(ptr, layout, new_size);
}
} // namespace pw::allocator::test
diff --git a/pw_allocator/block.cc b/pw_allocator/block.cc
index d2349f157..f09876fde 100644
--- a/pw_allocator/block.cc
+++ b/pw_allocator/block.cc
@@ -14,242 +14,69 @@
#include "pw_allocator/block.h"
-#include <cstring>
-
#include "pw_assert/check.h"
-#include "pw_span/span.h"
namespace pw::allocator {
-Status Block::Init(const span<std::byte> region, Block** block) {
- // Ensure the region we're given is aligned and sized accordingly
- if (reinterpret_cast<uintptr_t>(region.data()) % alignof(Block) != 0) {
- return Status::InvalidArgument();
- }
-
- if (region.size() < sizeof(Block)) {
- return Status::InvalidArgument();
- }
-
- union {
- Block* block;
- std::byte* bytes;
- } aliased;
- aliased.bytes = region.data();
-
- // Make "next" point just past the end of this block; forming a linked list
- // with the following storage. Since the space between this block and the
- // next are implicitly part of the raw data, size can be computed by
- // subtracting the pointers.
- aliased.block->next_ =
- reinterpret_cast<Block*>(region.data() + region.size_bytes());
- aliased.block->MarkLast();
-
- aliased.block->prev_ = nullptr;
- *block = aliased.block;
-#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- (*block)->PoisonBlock();
-#endif // PW_ALLOCATOR_POISON_ENABLE
- return OkStatus();
-}
-
-Status Block::Split(size_t head_block_inner_size, Block** new_block) {
- if (new_block == nullptr) {
- return Status::InvalidArgument();
- }
-
- // Don't split used blocks.
- // TODO(jgarside): Relax this restriction? Flag to enable/disable this check?
- if (Used()) {
- return Status::FailedPrecondition();
- }
-
- // First round the head_block_inner_size up to a alignof(Block) bounary.
- // This ensures that the next block header is aligned accordingly.
- // Alignment must be a power of two, hence align()-1 will return the
- // remainder.
- auto align_bit_mask = alignof(Block) - 1;
- size_t aligned_head_block_inner_size = head_block_inner_size;
- if ((head_block_inner_size & align_bit_mask) != 0) {
- aligned_head_block_inner_size =
- (head_block_inner_size & ~align_bit_mask) + alignof(Block);
- }
-
- // (1) Are we trying to allocate a head block larger than the current head
- // block? This may happen because of the alignment above.
- if (aligned_head_block_inner_size > InnerSize()) {
- return Status::OutOfRange();
- }
-
- // (2) Does the resulting block have enough space to store the header?
- // TODO(jgarside): What to do if the returned section is empty (i.e. remaining
- // size == sizeof(Block))?
- if (InnerSize() - aligned_head_block_inner_size <
- sizeof(Block) + 2 * PW_ALLOCATOR_POISON_OFFSET) {
- return Status::ResourceExhausted();
- }
-
- // Create the new block inside the current one.
- Block* new_next = reinterpret_cast<Block*>(
- // From the current position...
- reinterpret_cast<intptr_t>(this) +
- // skip past the current header...
- sizeof(*this) +
- // add the poison bytes before usable space ...
- PW_ALLOCATOR_POISON_OFFSET +
- // into the usable bytes by the new inner size...
- aligned_head_block_inner_size +
- // add the poison bytes after the usable space ...
- PW_ALLOCATOR_POISON_OFFSET);
-
- // If we're inserting in the middle, we need to update the current next
- // block to point to what we're inserting
- if (!Last()) {
- Next()->prev_ = new_next;
- }
-
- // Copy next verbatim so the next block also gets the "last"-ness
- new_next->next_ = next_;
- new_next->prev_ = this;
-
- // Update the current block to point to the new head.
- next_ = new_next;
-
- *new_block = next_;
-
#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- PoisonBlock();
- (*new_block)->PoisonBlock();
-#endif // PW_ALLOCATOR_POISON_ENABLE
- return OkStatus();
+void BaseBlock::Poison(void* block, size_t header_size, size_t outer_size) {
+ auto* start = reinterpret_cast<std::byte*>(block);
+ std::memcpy(
+ start + header_size - kPoisonOffset, kPoisonPattern, kPoisonOffset);
+ std::memcpy(
+ start + outer_size - kPoisonOffset, kPoisonPattern, kPoisonOffset);
}
-Status Block::MergeNext() {
- // Anything to merge with?
- if (Last()) {
- return Status::OutOfRange();
- }
-
- // Is this or the next block in use?
- if (Used() || Next()->Used()) {
- return Status::FailedPrecondition();
- }
-
- // Simply enough, this block's next pointer becomes the next block's
- // next pointer. We then need to re-wire the "next next" block's prev
- // pointer to point back to us though.
- next_ = Next()->next_;
-
- // Copying the pointer also copies the "last" status, so this is safe.
- if (!Last()) {
- Next()->prev_ = this;
- }
-
- return OkStatus();
+bool BaseBlock::CheckPoison(const void* block,
+ size_t header_size,
+ size_t outer_size) {
+ const auto* start = reinterpret_cast<const std::byte*>(block);
+ return std::memcmp(start + header_size - kPoisonOffset,
+ kPoisonPattern,
+ kPoisonOffset) == 0 &&
+ std::memcmp(start + outer_size - kPoisonOffset,
+ kPoisonPattern,
+ kPoisonOffset) == 0;
}
-Status Block::MergePrev() {
- // We can't merge if we have no previous. After that though, merging with
- // the previous block is just MergeNext from the previous block.
- if (prev_ == nullptr) {
- return Status::OutOfRange();
- }
-
- // WARNING: This class instance will still exist, but technically be invalid
- // after this has been invoked. Be careful when doing anything with `this`
- // After doing the below.
- return prev_->MergeNext();
-}
+#else // PW_ALLOCATOR_POISON_ENABLE
-// TODO: b/234875269 - Add stack tracing to locate which call to the heap
-// operation caused the corruption.
-// TODO(jgarside): Add detailed information to log report and leave succinct
-// messages in the crash message.
-void Block::CrashIfInvalid() {
- switch (CheckStatus()) {
- case VALID:
- break;
- case MISALIGNED:
- PW_DCHECK(false,
- "The block at address %p is not aligned.",
- static_cast<void*>(this));
- break;
- case NEXT_MISMATCHED:
- PW_DCHECK(false,
- "The 'prev' field in the next block (%p) does not match the "
- "address of the current block (%p).",
- static_cast<void*>(Next()->Prev()),
- static_cast<void*>(this));
- break;
- case PREV_MISMATCHED:
- PW_DCHECK(false,
- "The 'next' field in the previous block (%p) does not match "
- "the address of the current block (%p).",
- static_cast<void*>(Prev()->Next()),
- static_cast<void*>(this));
- break;
- case POISON_CORRUPTED:
- PW_DCHECK(false,
- "The poisoned pattern in the block at %p is corrupted.",
- static_cast<void*>(this));
- break;
- }
-}
+void BaseBlock::Poison(void*, size_t, size_t) {}
-// This function will return a Block::BlockStatus that is either VALID or
-// indicates the reason why the Block is invalid. If the Block is invalid at
-// multiple points, this function will only return one of the reasons.
-Block::BlockStatus Block::CheckStatus() const {
- // Make sure the Block is aligned.
- if (reinterpret_cast<uintptr_t>(this) % alignof(Block) != 0) {
- return BlockStatus::MISALIGNED;
- }
+bool BaseBlock::CheckPoison(const void*, size_t, size_t) { return true; }
- // Test if the prev/next pointer for this Block matches.
- if (!Last() && (this >= Next() || this != Next()->Prev())) {
- return BlockStatus::NEXT_MISMATCHED;
- }
+#endif // PW_ALLOCATOR_POISON_ENABLE
- if (Prev() && (this <= Prev() || this != Prev()->Next())) {
- return BlockStatus::PREV_MISMATCHED;
- }
+// TODO: b/234875269 - Add stack tracing to locate which call to the heap
+// operation caused the corruption in the methods below.
-#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- if (!this->CheckPoisonBytes()) {
- return BlockStatus::POISON_CORRUPTED;
- }
-#endif // PW_ALLOCATOR_POISON_ENABLE
- return BlockStatus::VALID;
+void BaseBlock::CrashMisaligned(uintptr_t addr) {
+ PW_DCHECK(false,
+ "The block at address %p is not aligned.",
+ reinterpret_cast<void*>(addr));
}
-// Paint sizeof(void*) bytes before and after the usable space in Block as the
-// randomized function pattern.
-void Block::PoisonBlock() {
-#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- std::byte* front_region = reinterpret_cast<std::byte*>(this) + sizeof(*this);
- memcpy(front_region, POISON_PATTERN, PW_ALLOCATOR_POISON_OFFSET);
+void BaseBlock::CrashNextMismatched(uintptr_t addr, uintptr_t next_prev) {
+ PW_DCHECK(false,
+ "The 'prev' field in the next block (%p) does not match the "
+ "address of the current block (%p).",
+ reinterpret_cast<void*>(next_prev),
+ reinterpret_cast<void*>(addr));
+}
- std::byte* end_region =
- reinterpret_cast<std::byte*>(Next()) - PW_ALLOCATOR_POISON_OFFSET;
- memcpy(end_region, POISON_PATTERN, PW_ALLOCATOR_POISON_OFFSET);
-#endif // PW_ALLOCATOR_POISON_ENABLE
+void BaseBlock::CrashPrevMismatched(uintptr_t addr, uintptr_t prev_next) {
+ PW_DCHECK(false,
+ "The 'next' field in the previous block (%p) does not match "
+ "the address of the current block (%p).",
+ reinterpret_cast<void*>(prev_next),
+ reinterpret_cast<void*>(addr));
}
-bool Block::CheckPoisonBytes() const {
-#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- std::byte* front_region = reinterpret_cast<std::byte*>(
- reinterpret_cast<intptr_t>(this) + sizeof(*this));
- if (std::memcmp(front_region, POISON_PATTERN, PW_ALLOCATOR_POISON_OFFSET)) {
- return false;
- }
- std::byte* end_region = reinterpret_cast<std::byte*>(
- reinterpret_cast<intptr_t>(this->Next()) - PW_ALLOCATOR_POISON_OFFSET);
- if (std::memcmp(end_region, POISON_PATTERN, PW_ALLOCATOR_POISON_OFFSET)) {
- return false;
- }
-#endif // PW_ALLOCATOR_POISON_ENABLE
- return true;
+void BaseBlock::CrashPoisonCorrupted(uintptr_t addr) {
+ PW_DCHECK(false,
+ "The poisoned pattern in the block at %p is corrupted.",
+ reinterpret_cast<void*>(addr));
}
} // namespace pw::allocator
diff --git a/pw_allocator/block_test.cc b/pw_allocator/block_test.cc
index 06772bd99..fd2906d3f 100644
--- a/pw_allocator/block_test.cc
+++ b/pw_allocator/block_test.cc
@@ -14,6 +14,7 @@
#include "pw_allocator/block.h"
+#include <cstdint>
#include <cstring>
#include "gtest/gtest.h"
@@ -23,101 +24,143 @@ using std::byte;
namespace pw::allocator {
-TEST(Block, CanCreateSingleBlock) {
+const size_t kCapacity = 0x20000;
+
+template <typename BlockType>
+void CanCreateSingleBlock() {
constexpr size_t kN = 200;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- auto status = Block::Init(span(bytes, kN), &block);
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- ASSERT_EQ(status, OkStatus());
EXPECT_EQ(block->OuterSize(), kN);
- EXPECT_EQ(block->InnerSize(),
- kN - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET);
+ EXPECT_EQ(block->InnerSize(), kN - BlockType::kBlockOverhead);
EXPECT_EQ(block->Prev(), nullptr);
- EXPECT_EQ(block->Next(), (Block*)((uintptr_t)block + kN));
- EXPECT_EQ(block->Used(), false);
- EXPECT_EQ(block->Last(), true);
+ EXPECT_EQ(block->Next(), nullptr);
+ EXPECT_FALSE(block->Used());
+ EXPECT_TRUE(block->Last());
+}
+TEST(GenericBlockTest, CanCreateSingleBlock) {
+ CanCreateSingleBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanCreateSingleBlock) {
+ CanCreateSingleBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotCreateUnalignedSingleBlock) {
+template <typename BlockType>
+void CannotCreateUnalignedSingleBlock() {
constexpr size_t kN = 1024;
// Force alignment, so we can un-force it below
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
byte* byte_ptr = bytes;
- Block* block = nullptr;
- auto status = Block::Init(span(byte_ptr + 1, kN - 1), &block);
-
- EXPECT_EQ(status, Status::InvalidArgument());
+ Result<BlockType*> result = BlockType::Init(span(byte_ptr + 1, kN - 1));
+ EXPECT_FALSE(result.ok());
+ EXPECT_EQ(result.status(), Status::InvalidArgument());
+}
+TEST(GenericBlockTest, CannotCreateUnalignedSingleBlock) {
+ CannotCreateUnalignedSingleBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotCreateUnalignedSingleBlock) {
+ CannotCreateUnalignedSingleBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotCreateTooSmallBlock) {
+template <typename BlockType>
+void CannotCreateTooSmallBlock() {
constexpr size_t kN = 2;
- alignas(Block*) byte bytes[kN];
- Block* block = nullptr;
- auto status = Block::Init(span(bytes, kN), &block);
+ alignas(BlockType*) byte bytes[kN];
- EXPECT_EQ(status, Status::InvalidArgument());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ EXPECT_FALSE(result.ok());
+ EXPECT_EQ(result.status(), Status::ResourceExhausted());
+}
+TEST(GenericBlockTest, CannotCreateTooSmallBlock) {
+ CannotCreateTooSmallBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotCreateTooSmallBlock) {
+ CannotCreateTooSmallBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanSplitBlock) {
+TEST(CustomBlockTest, CannotCreateTooLargeBlock) {
+ using BlockType = Block<uint16_t, 512>;
+ constexpr size_t kN = 1024;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ EXPECT_FALSE(result.ok());
+ EXPECT_EQ(result.status(), Status::OutOfRange());
+}
+
+template <typename BlockType>
+void CanSplitBlock() {
constexpr size_t kN = 1024;
constexpr size_t kSplitN = 512;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* next_block = nullptr;
- auto status = block->Split(kSplitN, &next_block);
+ result = BlockType::Split(block1, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- ASSERT_EQ(status, OkStatus());
- EXPECT_EQ(block->InnerSize(), kSplitN);
- EXPECT_EQ(block->OuterSize(),
- kSplitN + sizeof(Block) + 2 * PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(block->Last(), false);
+ EXPECT_EQ(block1->InnerSize(), kSplitN);
+ EXPECT_EQ(block1->OuterSize(), kSplitN + BlockType::kBlockOverhead);
+ EXPECT_FALSE(block1->Last());
- EXPECT_EQ(next_block->OuterSize(),
- kN - kSplitN - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(next_block->Used(), false);
- EXPECT_EQ(next_block->Last(), true);
+ EXPECT_EQ(block2->OuterSize(), kN - kSplitN - BlockType::kBlockOverhead);
+ EXPECT_FALSE(block2->Used());
+ EXPECT_TRUE(block2->Last());
- EXPECT_EQ(block->Next(), next_block);
- EXPECT_EQ(next_block->Prev(), block);
+ EXPECT_EQ(block1->Next(), block2);
+ EXPECT_EQ(block2->Prev(), block1);
+}
+TEST(GenericBlockTest, CanSplitBlock) { CanSplitBlock<Block<>>(); }
+TEST(CustomBlockTest, CanSplitBlock) {
+ CanSplitBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanSplitBlockUnaligned) {
+template <typename BlockType>
+void CanSplitBlockUnaligned() {
constexpr size_t kN = 1024;
constexpr size_t kSplitN = 513;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- // We should split at sizeof(Block) + kSplitN bytes. Then
- // we need to round that up to an alignof(Block*) boundary.
+ // We should split at sizeof(BlockType) + kSplitN bytes. Then
+ // we need to round that up to an alignof(BlockType*) boundary.
uintptr_t split_addr = ((uintptr_t)&bytes) + kSplitN;
- split_addr += alignof(Block*) - (split_addr % alignof(Block*));
+ split_addr += alignof(BlockType*) - (split_addr % alignof(BlockType*));
uintptr_t split_len = split_addr - (uintptr_t)&bytes;
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ EXPECT_EQ(block1->InnerSize(), split_len);
+ EXPECT_EQ(block1->OuterSize(), split_len + BlockType::kBlockOverhead);
- Block* next_block = nullptr;
- auto status = block->Split(kSplitN, &next_block);
+ EXPECT_EQ(block2->OuterSize(), kN - block1->OuterSize());
+ EXPECT_FALSE(block2->Used());
- ASSERT_EQ(status, OkStatus());
- EXPECT_EQ(block->InnerSize(), split_len);
- EXPECT_EQ(block->OuterSize(),
- split_len + sizeof(Block) + 2 * PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(next_block->OuterSize(),
- kN - split_len - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(next_block->Used(), false);
- EXPECT_EQ(block->Next(), next_block);
- EXPECT_EQ(next_block->Prev(), block);
+ EXPECT_EQ(block1->Next(), block2);
+ EXPECT_EQ(block2->Prev(), block1);
+}
+TEST(GenericBlockTest, CanSplitBlockUnaligned) { CanSplitBlock<Block<>>(); }
+TEST(CustomBlockTest, CanSplitBlockUnaligned) {
+ CanSplitBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanSplitMidBlock) {
+template <typename BlockType>
+void CanSplitMidBlock() {
// Split once, then split the original block again to ensure that the
// pointers get rewired properly.
// I.e.
@@ -130,282 +173,990 @@ TEST(Block, CanSplitMidBlock) {
constexpr size_t kN = 1024;
constexpr size_t kSplit1 = 512;
constexpr size_t kSplit2 = 256;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* block2 = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(kSplit1, &block2));
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- Block* block3 = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(kSplit2, &block3));
+ result = BlockType::Split(block1, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
- EXPECT_EQ(block->Next(), block3);
+ EXPECT_EQ(block1->Next(), block3);
+ EXPECT_EQ(block3->Prev(), block1);
EXPECT_EQ(block3->Next(), block2);
EXPECT_EQ(block2->Prev(), block3);
- EXPECT_EQ(block3->Prev(), block);
+}
+TEST(GenericBlockTest, CanSplitMidBlock) { CanSplitMidBlock<Block<>>(); }
+TEST(CustomBlockTest, CanSplitMidBlock) {
+ CanSplitMidBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotSplitBlockWithoutHeaderSpace) {
- constexpr size_t kN = 1024;
- constexpr size_t kSplitN =
- kN - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET - 1;
- alignas(Block*) byte bytes[kN];
-
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+template <typename BlockType>
+void CannotSplitTooSmallBlock() {
+ constexpr size_t kN = 64;
+ constexpr size_t kSplitN = kN + 1;
+ alignas(BlockType*) byte bytes[kN];
- Block* next_block = nullptr;
- auto status = block->Split(kSplitN, &next_block);
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- EXPECT_EQ(status, Status::ResourceExhausted());
- EXPECT_EQ(next_block, nullptr);
+ result = BlockType::Split(block, kSplitN);
+ EXPECT_EQ(result.status(), Status::OutOfRange());
}
-TEST(Block, MustProvideNextBlockPointer) {
+template <typename BlockType>
+void CannotSplitBlockWithoutHeaderSpace() {
constexpr size_t kN = 1024;
- constexpr size_t kSplitN = kN - sizeof(Block) - 1;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplitN = kN - BlockType::kBlockOverhead - 1;
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- auto status = block->Split(kSplitN, nullptr);
- EXPECT_EQ(status, Status::InvalidArgument());
+ result = BlockType::Split(block, kSplitN);
+ EXPECT_EQ(result.status(), Status::ResourceExhausted());
+}
+TEST(GenericBlockTest, CannotSplitBlockWithoutHeaderSpace) {
+ CannotSplitBlockWithoutHeaderSpace<Block<>>();
+}
+TEST(CustomBlockTest, CannotSplitBlockWithoutHeaderSpace) {
+ CannotSplitBlockWithoutHeaderSpace<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotMakeBlockLargerInSplit) {
+template <typename BlockType>
+void CannotMakeBlockLargerInSplit() {
// Ensure that we can't ask for more space than the block actually has...
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
-
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ alignas(BlockType*) byte bytes[kN];
- Block* next_block = nullptr;
- auto status = block->Split(block->InnerSize() + 1, &next_block);
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- EXPECT_EQ(status, Status::OutOfRange());
+ result = BlockType::Split(block, block->InnerSize() + 1);
+ EXPECT_EQ(result.status(), Status::OutOfRange());
+}
+TEST(GenericBlockTest, CannotMakeBlockLargerInSplit) {
+ CannotMakeBlockLargerInSplit<Block<>>();
+}
+TEST(CustomBlockTest, CannotMakeBlockLargerInSplit) {
+ CannotMakeBlockLargerInSplit<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotMakeSecondBlockLargerInSplit) {
+template <typename BlockType>
+void CannotMakeSecondBlockLargerInSplit() {
// Ensure that the second block in split is at least of the size of header.
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- Block* next_block = nullptr;
- auto status = block->Split(
- block->InnerSize() - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET + 1,
- &next_block);
-
- ASSERT_EQ(status, Status::ResourceExhausted());
- EXPECT_EQ(next_block, nullptr);
+ result = BlockType::Split(block,
+ block->InnerSize() - BlockType::kBlockOverhead + 1);
+ EXPECT_EQ(result.status(), Status::ResourceExhausted());
+}
+TEST(GenericBlockTest, CannotMakeSecondBlockLargerInSplit) {
+ CannotMakeSecondBlockLargerInSplit<Block<>>();
+}
+TEST(CustomBlockTest, CannotMakeSecondBlockLargerInSplit) {
+ CannotMakeSecondBlockLargerInSplit<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanMakeZeroSizeFirstBlock) {
+template <typename BlockType>
+void CanMakeZeroSizeFirstBlock() {
// This block does support splitting with zero payload size.
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
-
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ alignas(BlockType*) byte bytes[kN];
- Block* next_block = nullptr;
- auto status = block->Split(0, &next_block);
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- ASSERT_EQ(status, OkStatus());
+ result = BlockType::Split(block, 0);
+ ASSERT_EQ(result.status(), OkStatus());
EXPECT_EQ(block->InnerSize(), static_cast<size_t>(0));
}
+TEST(GenericBlockTest, CanMakeZeroSizeFirstBlock) {
+ CanMakeZeroSizeFirstBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanMakeZeroSizeFirstBlock) {
+ CanMakeZeroSizeFirstBlock<Block<uint32_t, kCapacity>>();
+}
-TEST(Block, CanMakeZeroSizeSecondBlock) {
+template <typename BlockType>
+void CanMakeZeroSizeSecondBlock() {
// Likewise, the split block can be zero-width.
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* next_block = nullptr;
- auto status = block->Split(
- block->InnerSize() - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET,
- &next_block);
+ result =
+ BlockType::Split(block1, block1->InnerSize() - BlockType::kBlockOverhead);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- ASSERT_EQ(status, OkStatus());
- EXPECT_EQ(next_block->InnerSize(), static_cast<size_t>(0));
+ EXPECT_EQ(block2->InnerSize(), static_cast<size_t>(0));
+}
+TEST(GenericBlockTest, CanMakeZeroSizeSecondBlock) {
+ CanMakeZeroSizeSecondBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanMakeZeroSizeSecondBlock) {
+ CanMakeZeroSizeSecondBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanMarkBlockUsed) {
+template <typename BlockType>
+void CanMarkBlockUsed() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
block->MarkUsed();
- EXPECT_EQ(block->Used(), true);
+ EXPECT_TRUE(block->Used());
- // Mark used packs that data into the next pointer. Check that it's still
- // valid
- EXPECT_EQ(block->Next(), (Block*)((uintptr_t)block + kN));
+ // Size should be unaffected.
+ EXPECT_EQ(block->OuterSize(), kN);
block->MarkFree();
- EXPECT_EQ(block->Used(), false);
+ EXPECT_FALSE(block->Used());
+}
+TEST(GenericBlockTest, CanMarkBlockUsed) { CanMarkBlockUsed<Block<>>(); }
+TEST(CustomBlockTest, CanMarkBlockUsed) {
+ CanMarkBlockUsed<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotSplitUsedBlock) {
+template <typename BlockType>
+void CannotSplitUsedBlock() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplitN = 512;
+ alignas(BlockType*) byte bytes[kN];
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
block->MarkUsed();
+ result = BlockType::Split(block, kSplitN);
+ EXPECT_EQ(result.status(), Status::FailedPrecondition());
+}
+TEST(GenericBlockTest, CannotSplitUsedBlock) {
+ CannotSplitUsedBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotSplitUsedBlock) {
+ CannotSplitUsedBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanAllocFirstFromAlignedBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSize = 256;
+ constexpr size_t kAlign = 32;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // Make sure the block's usable space is aligned.
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ size_t pad_inner_size = AlignUp(addr, kAlign) - addr;
+ if (pad_inner_size != 0) {
+ if (pad_inner_size < BlockType::kHeaderSize) {
+ pad_inner_size += kAlign;
+ }
+ pad_inner_size -= BlockType::kHeaderSize;
+ result = BlockType::Split(block, pad_inner_size);
+ EXPECT_EQ(result.status(), OkStatus());
+ block = *result;
+ }
+
+ // Allocate from the front of the block.
+ BlockType* prev = block->Prev();
+ EXPECT_EQ(BlockType::AllocFirst(block, kSize, kAlign), OkStatus());
+ EXPECT_EQ(block->InnerSize(), kSize);
+ addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ EXPECT_EQ(addr % kAlign, 0U);
+ EXPECT_TRUE(block->Used());
+
+ // No new padding block was allocated.
+ EXPECT_EQ(prev, block->Prev());
+
+ // Extra was split from the end of the block.
+ EXPECT_FALSE(block->Last());
+}
+TEST(GenericBlockTest, CanAllocFirstFromAlignedBlock) {
+ CanAllocFirstFromAlignedBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanAllocFirstFromAlignedBlock) {
+ CanAllocFirstFromAlignedBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanAllocFirstFromUnalignedBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSize = 256;
+ constexpr size_t kAlign = 32;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // Make sure the block's usable space is not aligned.
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ size_t pad_inner_size = AlignUp(addr, kAlign) - addr + (kAlign / 2);
+ if (pad_inner_size < BlockType::kHeaderSize) {
+ pad_inner_size += kAlign;
+ }
+ pad_inner_size -= BlockType::kHeaderSize;
+ result = BlockType::Split(block, pad_inner_size);
+ EXPECT_EQ(result.status(), OkStatus());
+ block = *result;
+
+ // Allocate from the front of the block.
+ BlockType* prev = block->Prev();
+ EXPECT_EQ(BlockType::AllocFirst(block, kSize, kAlign), OkStatus());
+ EXPECT_EQ(block->InnerSize(), kSize);
+ addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ EXPECT_EQ(addr % kAlign, 0U);
+ EXPECT_TRUE(block->Used());
+
+ // A new padding block was allocated.
+ EXPECT_LT(prev, block->Prev());
+
+ // Extra was split from the end of the block.
+ EXPECT_FALSE(block->Last());
+}
+TEST(GenericBlockTest, CanAllocFirstFromUnalignedBlock) {
+ CanAllocFirstFromUnalignedBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanAllocFirstFromUnalignedBlock) {
+ CanAllocFirstFromUnalignedBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CannotAllocFirstTooSmallBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kAlign = 32;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // Make sure the block's usable space is not aligned.
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ size_t pad_inner_size = AlignUp(addr, kAlign) - addr + (kAlign / 2);
+ if (pad_inner_size < BlockType::kHeaderSize) {
+ pad_inner_size += kAlign;
+ }
+ pad_inner_size -= BlockType::kHeaderSize;
+ result = BlockType::Split(block, pad_inner_size);
+ EXPECT_EQ(result.status(), OkStatus());
+ block = *result;
+
+ // Cannot allocate without room to a split a block for alignment.
+ EXPECT_EQ(BlockType::AllocFirst(block, block->InnerSize(), kAlign),
+ Status::OutOfRange());
+}
+TEST(GenericBlockTest, CannotAllocFirstTooSmallBlock) {
+ CannotAllocFirstTooSmallBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotAllocFirstTooSmallBlock) {
+ CannotAllocFirstTooSmallBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanAllocLast() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSize = 256;
+ constexpr size_t kAlign = 32;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // Allocate from the back of the block.
+ EXPECT_EQ(BlockType::AllocLast(block, kSize, kAlign), OkStatus());
+ EXPECT_GE(block->InnerSize(), kSize);
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ EXPECT_EQ(addr % kAlign, 0U);
+ EXPECT_TRUE(block->Used());
+
+ // Extra was split from the front of the block.
+ EXPECT_FALSE(block->Prev()->Used());
+ EXPECT_TRUE(block->Last());
+}
+TEST(GenericBlockTest, CanAllocLast) { CanAllocLast<Block<>>(); }
+TEST(CustomBlockTest, CanAllocLast) {
+ CanAllocLast<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CannotAllocLastFromTooSmallBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kAlign = 32;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // Make sure the block's usable space is not aligned.
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ size_t pad_inner_size = AlignUp(addr, kAlign) - addr + (kAlign / 2);
+ if (pad_inner_size < BlockType::kHeaderSize) {
+ pad_inner_size += kAlign;
+ }
+ pad_inner_size -= BlockType::kHeaderSize;
+ result = BlockType::Split(block, pad_inner_size);
+ EXPECT_EQ(result.status(), OkStatus());
+ block = *result;
+
+ // Cannot allocate without room to a split a block for alignment.
+ EXPECT_EQ(BlockType::AllocLast(block, block->InnerSize(), kAlign),
+ Status::ResourceExhausted());
+}
- Block* next_block = nullptr;
- auto status = block->Split(512, &next_block);
- EXPECT_EQ(status, Status::FailedPrecondition());
+TEST(GenericBlockTest, CannotAllocLastFromTooSmallBlock) {
+ CannotAllocLastFromTooSmallBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotAllocLastFromTooSmallBlock) {
+ CannotAllocLastFromTooSmallBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanMergeWithNextBlock) {
+template <typename BlockType>
+void CanMergeWithNextBlock() {
// Do the three way merge from "CanSplitMidBlock", and let's
// merge block 3 and 2
constexpr size_t kN = 1024;
constexpr size_t kSplit1 = 512;
constexpr size_t kSplit2 = 256;
- alignas(Block*) byte bytes[kN];
-
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ alignas(BlockType*) byte bytes[kN];
- Block* block2 = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(kSplit1, &block2));
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* block3 = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(kSplit2, &block3));
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
- EXPECT_EQ(block3->MergeNext(), OkStatus());
+ result = BlockType::Split(block1, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
- EXPECT_EQ(block->Next(), block3);
- EXPECT_EQ(block3->Prev(), block);
- EXPECT_EQ(block->InnerSize(), kSplit2);
+ EXPECT_EQ(BlockType::MergeNext(block3), OkStatus());
- // The resulting "right hand" block should have an outer size of 1024 - 256 -
- // sizeof(Block) - 2*PW_ALLOCATOR_POISON_OFFSET, which accounts for the first
- // block.
- EXPECT_EQ(block3->OuterSize(),
- kN - kSplit2 - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET);
+ EXPECT_EQ(block1->Next(), block3);
+ EXPECT_EQ(block3->Prev(), block1);
+ EXPECT_EQ(block1->InnerSize(), kSplit2);
+ EXPECT_EQ(block3->OuterSize(), kN - block1->OuterSize());
+}
+TEST(GenericBlockTest, CanMergeWithNextBlock) {
+ CanMergeWithNextBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanMergeWithNextBlock) {
+ CanMergeWithNextBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotMergeWithFirstOrLastBlock) {
+template <typename BlockType>
+void CannotMergeWithFirstOrLastBlock() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplitN = 512;
+ alignas(BlockType*) byte bytes[kN];
// Do a split, just to check that the checks on Next/Prev are
// different...
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- Block* next_block = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(512, &next_block));
+ EXPECT_EQ(BlockType::MergeNext(block2), Status::OutOfRange());
- EXPECT_EQ(next_block->MergeNext(), Status::OutOfRange());
- EXPECT_EQ(block->MergePrev(), Status::OutOfRange());
+ BlockType* block0 = block1->Prev();
+ EXPECT_EQ(BlockType::MergeNext(block0), Status::OutOfRange());
+}
+TEST(GenericBlockTest, CannotMergeWithFirstOrLastBlock) {
+ CannotMergeWithFirstOrLastBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotMergeWithFirstOrLastBlock) {
+ CannotMergeWithFirstOrLastBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CannotMergeUsedBlock) {
+template <typename BlockType>
+void CannotMergeUsedBlock() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplitN = 512;
+ alignas(BlockType*) byte bytes[kN];
// Do a split, just to check that the checks on Next/Prev are
// different...
- Block* block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &block), OkStatus());
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ result = BlockType::Split(block, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+
+ block->MarkUsed();
+ EXPECT_EQ(BlockType::MergeNext(block), Status::FailedPrecondition());
+}
+TEST(GenericBlockTest, CannotMergeUsedBlock) {
+ CannotMergeUsedBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotMergeUsedBlock) {
+ CannotMergeUsedBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanFreeSingleBlock() {
+ constexpr size_t kN = 1024;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ block->MarkUsed();
+ BlockType::Free(block);
+ EXPECT_FALSE(block->Used());
+ EXPECT_EQ(block->OuterSize(), kN);
+}
+TEST(GenericBlockTest, CanFreeSingleBlock) { CanFreeSingleBlock<Block<>>(); }
+TEST(CustomBlockTest, CanFreeSingleBlock) {
+ CanFreeSingleBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanFreeBlockWithoutMerging() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
+
+ block1->MarkUsed();
+ block2->MarkUsed();
+ block3->MarkUsed();
+
+ BlockType::Free(block2);
+ EXPECT_FALSE(block2->Used());
+ EXPECT_NE(block2->Prev(), nullptr);
+ EXPECT_FALSE(block2->Last());
+}
+TEST(GenericBlockTest, CanFreeBlockWithoutMerging) {
+ CanFreeBlockWithoutMerging<Block<>>();
+}
+TEST(CustomBlockTest, CanFreeBlockWithoutMerging) {
+ CanFreeBlockWithoutMerging<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanFreeBlockAndMergeWithPrev() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
+
+ block2->MarkUsed();
+ block3->MarkUsed();
+
+ BlockType::Free(block2);
+ EXPECT_FALSE(block2->Used());
+ EXPECT_EQ(block2->Prev(), nullptr);
+ EXPECT_FALSE(block2->Last());
+}
+TEST(GenericBlockTest, CanFreeBlockAndMergeWithPrev) {
+ CanFreeBlockAndMergeWithPrev<Block<>>();
+}
+TEST(CustomBlockTest, CanFreeBlockAndMergeWithPrev) {
+ CanFreeBlockAndMergeWithPrev<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanFreeBlockAndMergeWithNext() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+
+ block1->MarkUsed();
+ block2->MarkUsed();
+
+ BlockType::Free(block2);
+ EXPECT_FALSE(block2->Used());
+ EXPECT_NE(block2->Prev(), nullptr);
+ EXPECT_TRUE(block2->Last());
+}
+TEST(GenericBlockTest, CanFreeBlockAndMergeWithNext) {
+ CanFreeBlockAndMergeWithNext<Block<>>();
+}
+TEST(CustomBlockTest, CanFreeBlockAndMergeWithNext) {
+ CanFreeBlockAndMergeWithNext<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanFreeUsedBlockAndMergeWithBoth() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* next_block = nullptr;
- ASSERT_EQ(OkStatus(), block->Split(512, &next_block));
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+
+ block2->MarkUsed();
+
+ BlockType::Free(block2);
+ EXPECT_FALSE(block2->Used());
+ EXPECT_EQ(block2->Prev(), nullptr);
+ EXPECT_TRUE(block2->Last());
+}
+TEST(GenericBlockTest, CanFreeUsedBlockAndMergeWithBoth) {
+ CanFreeUsedBlockAndMergeWithBoth<Block<>>();
+}
+TEST(CustomBlockTest, CanFreeUsedBlockAndMergeWithBoth) {
+ CanFreeUsedBlockAndMergeWithBoth<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanResizeBlockSameSize() {
+ constexpr size_t kN = 1024;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
block->MarkUsed();
- EXPECT_EQ(block->MergeNext(), Status::FailedPrecondition());
- EXPECT_EQ(next_block->MergePrev(), Status::FailedPrecondition());
+ EXPECT_EQ(BlockType::Resize(block, block->InnerSize()), OkStatus());
+}
+TEST(GenericBlockTest, CanResizeBlockSameSize) {
+ CanResizeBlockSameSize<Block<>>();
+}
+TEST(CustomBlockTest, CanResizeBlockSameSize) {
+ CanResizeBlockSameSize<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CannotResizeFreeBlock() {
+ constexpr size_t kN = 1024;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ EXPECT_EQ(BlockType::Resize(block, block->InnerSize()),
+ Status::FailedPrecondition());
+}
+TEST(GenericBlockTest, CannotResizeFreeBlock) {
+ CannotResizeFreeBlock<Block<>>();
+}
+TEST(CustomBlockTest, CannotResizeFreeBlock) {
+ CannotResizeFreeBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanResizeBlockSmallerWithNextFree() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ block1->MarkUsed();
+ size_t block2_inner_size = block2->InnerSize();
+
+ // Shrink by less than the minimum needed for a block. The extra should be
+ // added to the subsequent block.
+ size_t delta = BlockType::kBlockOverhead / 2;
+ size_t new_inner_size = block1->InnerSize() - delta;
+ EXPECT_EQ(BlockType::Resize(block1, new_inner_size), OkStatus());
+ EXPECT_EQ(block1->InnerSize(), new_inner_size);
+
+ block2 = block1->Next();
+ EXPECT_GE(block2->InnerSize(), block2_inner_size + delta);
+}
+TEST(GenericBlockTest, CanResizeBlockSmallerWithNextFree) {
+ CanResizeBlockSmallerWithNextFree<Block<>>();
+}
+TEST(CustomBlockTest, CanResizeBlockSmallerWithNextFree) {
+ CanResizeBlockSmallerWithNextFree<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanResizeBlockLargerWithNextFree() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ block1->MarkUsed();
+ size_t block2_inner_size = block2->InnerSize();
+
+ // Grow by less than the minimum needed for a block. The extra should be
+ // added to the subsequent block.
+ size_t delta = BlockType::kBlockOverhead / 2;
+ size_t new_inner_size = block1->InnerSize() + delta;
+ EXPECT_EQ(BlockType::Resize(block1, new_inner_size), OkStatus());
+ EXPECT_EQ(block1->InnerSize(), new_inner_size);
+
+ block2 = block1->Next();
+ EXPECT_GE(block2->InnerSize(), block2_inner_size - delta);
+}
+TEST(GenericBlockTest, CanResizeBlockLargerWithNextFree) {
+ CanResizeBlockLargerWithNextFree<Block<>>();
+}
+TEST(CustomBlockTest, CanResizeBlockLargerWithNextFree) {
+ CanResizeBlockLargerWithNextFree<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CannotResizeBlockMuchLargerWithNextFree() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
+
+ block1->MarkUsed();
+ block3->MarkUsed();
+
+ size_t new_inner_size = block1->InnerSize() + block2->OuterSize() + 1;
+ EXPECT_EQ(BlockType::Resize(block1, new_inner_size), Status::OutOfRange());
+}
+TEST(GenericBlockTest, CannotResizeBlockMuchLargerWithNextFree) {
+ CannotResizeBlockMuchLargerWithNextFree<Block<>>();
+}
+TEST(CustomBlockTest, CannotResizeBlockMuchLargerWithNextFree) {
+ CannotResizeBlockMuchLargerWithNextFree<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanCheckValidBlock) {
+template <typename BlockType>
+void CanResizeBlockSmallerWithNextUsed() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplit1 = 512;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* first_block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &first_block), OkStatus());
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- Block* second_block = nullptr;
- ASSERT_EQ(OkStatus(), first_block->Split(512, &second_block));
+ block1->MarkUsed();
+ block2->MarkUsed();
- Block* third_block = nullptr;
- ASSERT_EQ(OkStatus(), second_block->Split(256, &third_block));
+ // Shrink the block.
+ size_t delta = kSplit1 / 2;
+ size_t new_inner_size = block1->InnerSize() - delta;
+ EXPECT_EQ(BlockType::Resize(block1, new_inner_size), OkStatus());
+ EXPECT_EQ(block1->InnerSize(), new_inner_size);
- EXPECT_EQ(first_block->IsValid(), true);
- EXPECT_EQ(second_block->IsValid(), true);
- EXPECT_EQ(third_block->IsValid(), true);
+ block2 = block1->Next();
+ EXPECT_EQ(block2->OuterSize(), delta);
+}
+TEST(GenericBlockTest, CanResizeBlockSmallerWithNextUsed) {
+ CanResizeBlockSmallerWithNextUsed<Block<>>();
+}
+TEST(CustomBlockTest, CanResizeBlockSmallerWithNextUsed) {
+ CanResizeBlockSmallerWithNextUsed<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanCheckInalidBlock) {
+template <typename BlockType>
+void CannotResizeBlockLargerWithNextUsed() {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ constexpr size_t kSplit1 = 512;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- Block* first_block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &first_block), OkStatus());
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
- Block* second_block = nullptr;
- ASSERT_EQ(OkStatus(), first_block->Split(512, &second_block));
+ block1->MarkUsed();
+ block2->MarkUsed();
- Block* third_block = nullptr;
- ASSERT_EQ(OkStatus(), second_block->Split(256, &third_block));
+ size_t delta = BlockType::kBlockOverhead / 2;
+ size_t new_inner_size = block1->InnerSize() + delta;
+ EXPECT_EQ(BlockType::Resize(block1, new_inner_size), Status::OutOfRange());
+}
+TEST(GenericBlockTest, CannotResizeBlockLargerWithNextUsed) {
+ CannotResizeBlockLargerWithNextUsed<Block<>>();
+}
+TEST(CustomBlockTest, CannotResizeBlockLargerWithNextUsed) {
+ CannotResizeBlockLargerWithNextUsed<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanCheckValidBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 256;
+ alignas(BlockType*) byte bytes[kN];
- Block* fourth_block = nullptr;
- ASSERT_EQ(OkStatus(), third_block->Split(128, &fourth_block));
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
- std::byte* next_ptr = reinterpret_cast<std::byte*>(first_block);
- memcpy(next_ptr, second_block, sizeof(void*));
- EXPECT_EQ(first_block->IsValid(), false);
- EXPECT_EQ(second_block->IsValid(), false);
- EXPECT_EQ(third_block->IsValid(), true);
- EXPECT_EQ(fourth_block->IsValid(), true);
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
+
+ EXPECT_TRUE(block1->IsValid());
+ block1->CrashIfInvalid();
+
+ EXPECT_TRUE(block2->IsValid());
+ block2->CrashIfInvalid();
+
+ EXPECT_TRUE(block3->IsValid());
+ block3->CrashIfInvalid();
+}
+TEST(GenericBlockTest, CanCheckValidBlock) { CanCheckValidBlock<Block<>>(); }
+TEST(CustomBlockTest, CanCheckValidBlock) {
+ CanCheckValidBlock<Block<uint32_t, kCapacity>>();
+}
+
+template <typename BlockType>
+void CanCheckInvalidBlock() {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplit1 = 512;
+ constexpr size_t kSplit2 = 128;
+ constexpr size_t kSplit3 = 256;
+ alignas(BlockType*) byte bytes[kN];
+ memset(bytes, 0, sizeof(bytes));
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+
+ result = BlockType::Split(block1, kSplit1);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ result = BlockType::Split(block2, kSplit2);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block3 = *result;
+
+ result = BlockType::Split(block3, kSplit3);
+ ASSERT_EQ(result.status(), OkStatus());
#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
- std::byte fault_poison[PW_ALLOCATOR_POISON_OFFSET] = {std::byte(0)};
- std::byte* front_poison =
- reinterpret_cast<std::byte*>(third_block) + sizeof(*third_block);
- memcpy(front_poison, fault_poison, PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(third_block->IsValid(), false);
-
- std::byte* end_poison =
- reinterpret_cast<std::byte*>(fourth_block) + sizeof(*fourth_block);
- memcpy(end_poison, fault_poison, PW_ALLOCATOR_POISON_OFFSET);
- EXPECT_EQ(fourth_block->IsValid(), false);
+ // Corrupt a byte in the poisoned header.
+ EXPECT_TRUE(block1->IsValid());
+ bytes[BlockType::kHeaderSize - 1] = std::byte(0xFF);
+ EXPECT_FALSE(block1->IsValid());
+
+ // Corrupt a byte in the poisoned footer.
+ EXPECT_TRUE(block2->IsValid());
+ bytes[block1->OuterSize() + block2->OuterSize() - 1] = std::byte(0xFF);
+ EXPECT_FALSE(block2->IsValid());
#endif // PW_ALLOCATOR_POISON_ENABLE
+
+ // Corrupt a Block header.
+ // This must not touch memory outside the original region, or the test may
+ // (correctly) abort when run with address sanitizer.
+ // To remain as agostic to the internals of `Block` as possible, the test
+ // copies a smaller block's header to a larger block.
+ EXPECT_TRUE(block3->IsValid());
+ auto* src = reinterpret_cast<std::byte*>(block2);
+ auto* dst = reinterpret_cast<std::byte*>(block3);
+ std::memcpy(dst, src, sizeof(BlockType));
+ EXPECT_FALSE(block3->IsValid());
+}
+TEST(GenericBlockTest, CanCheckInvalidBlock) {
+ CanCheckInvalidBlock<Block<>>();
+}
+TEST(CustomBlockTest, CanCheckInvalidBlock) {
+ CanCheckInvalidBlock<Block<uint32_t, kCapacity>>();
}
-TEST(Block, CanPoisonBlock) {
-#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
+TEST(CustomBlockTest, CustomFlagsInitiallyZero) {
constexpr size_t kN = 1024;
- alignas(Block*) byte bytes[kN];
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- Block* first_block = nullptr;
- EXPECT_EQ(Block::Init(span(bytes, kN), &first_block), OkStatus());
+ EXPECT_EQ(block->GetFlags(), 0U);
+}
- Block* second_block = nullptr;
- ASSERT_EQ(OkStatus(), first_block->Split(512, &second_block));
+TEST(CustomBlockTest, SetCustomFlags) {
+ constexpr size_t kN = 1024;
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
- Block* third_block = nullptr;
- ASSERT_EQ(OkStatus(), second_block->Split(256, &third_block));
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
- EXPECT_EQ(first_block->IsValid(), true);
- EXPECT_EQ(second_block->IsValid(), true);
- EXPECT_EQ(third_block->IsValid(), true);
-#endif // PW_ALLOCATOR_POISON_ENABLE
+ block->SetFlags(1);
+ EXPECT_EQ(block->GetFlags(), 1U);
+}
+
+TEST(CustomBlockTest, SetAllCustomFlags) {
+ constexpr size_t kN = 1024;
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ // `1024/alignof(uint16_t)` is `0x200`, which leaves 6 bits available for
+ // flags per offset field. After 1 builtin field, this leaves 2*5 available
+ // for custom flags.
+ block->SetFlags((uint16_t(1) << 10) - 1);
+ EXPECT_EQ(block->GetFlags(), 0x3FFU);
+}
+
+TEST(CustomBlockTest, ClearCustomFlags) {
+ constexpr size_t kN = 1024;
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ block->SetFlags(0x155);
+ block->SetFlags(0x2AA, 0x333);
+ EXPECT_EQ(block->GetFlags(), 0x2EEU);
+}
+
+TEST(CustomBlockTest, FlagsNotCopiedOnSplit) {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplitN = 512;
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block1 = *result;
+ block1->SetFlags(0x137);
+
+ result = BlockType::Split(block1, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block2 = *result;
+
+ EXPECT_EQ(block1->GetFlags(), 0x137U);
+ EXPECT_EQ(block2->GetFlags(), 0U);
+}
+
+TEST(CustomBlockTest, FlagsPreservedByMergeNext) {
+ constexpr size_t kN = 1024;
+ constexpr size_t kSplitN = 512;
+ using BlockType = Block<uint16_t, kN>;
+ alignas(BlockType*) byte bytes[kN];
+
+ Result<BlockType*> result = BlockType::Init(span(bytes, kN));
+ ASSERT_EQ(result.status(), OkStatus());
+ BlockType* block = *result;
+
+ result = BlockType::Split(block, kSplitN);
+ ASSERT_EQ(result.status(), OkStatus());
+
+ block->SetFlags(0x137);
+ EXPECT_EQ(BlockType::MergeNext(block), OkStatus());
+ EXPECT_EQ(block->GetFlags(), 0x137U);
}
} // namespace pw::allocator
diff --git a/pw_allocator/docs.rst b/pw_allocator/docs.rst
index 45f19b98f..0d5545ccf 100644
--- a/pw_allocator/docs.rst
+++ b/pw_allocator/docs.rst
@@ -7,17 +7,15 @@ pw_allocator
This module provides various building blocks
for a dynamic allocator. This is composed of the following parts:
-- ``block``: An implementation of a linked list of memory blocks, supporting
- splitting and merging of blocks.
+- ``block``: An implementation of a doubly-linked list of memory blocks,
+ supporting splitting and merging of blocks.
- ``freelist``: A freelist, suitable for fast lookups of available memory chunks
(i.e. ``block`` s).
- ``allocator``: An interface for memory allocators. Several concrete
implementations are also provided.
-Heap Integrity Check
-====================
-The ``Block`` class provides two check functions:
-
+Block
+=====
.. doxygenclass:: pw::allocator::Block
:members:
@@ -31,6 +29,19 @@ Allocator
.. doxygenclass:: pw::allocator::Allocator
:members:
+Example
+-------
+As an example, the following implements a simple allocator that tracks memory
+using ``Block``.
+
+.. literalinclude:: public/pw_allocator/simple_allocator.h
+ :language: cpp
+ :linenos:
+ :start-after: [pw_allocator_examples_simple_allocator]
+ :end-before: [pw_allocator_examples_simple_allocator]
+
+Other Implemetations
+--------------------
Provided implementations of the ``Allocator`` interface include:
- ``AllocatorMetricProxy``: Wraps another allocator and records its usage.
@@ -40,9 +51,14 @@ Provided implementations of the ``Allocator`` interface include:
only be used if the ``libc`` in use provides those functions.
- ``NullAllocator``: Always fails. This may be useful if allocations should be
disallowed under specific circumstances.
-- ``SplitFreeListAllocator``: Tracks free blocks using a free list, and splits
- large and small allocations between the front and back, respectively, of its
- memory region in order to reduce fragmentation.
+- ``SplitFreeListAllocator``: Tracks memory using ``Block``, and splits large
+ and small allocations between the front and back, respectively, of it memory
+ region in order to reduce fragmentation.
+
+UniquePtr
+=========
+.. doxygenclass:: pw::allocator::UniquePtr
+ :members:
Heap Poisoning
==============
@@ -124,5 +140,11 @@ Options include the following:
- ``--pointer-size <integer of pointer size>``: The size of a pointer on the
machine where ``malloc/free`` is called. The default value is ``4``.
-Note, this module, and its documentation, is currently incomplete and
-experimental.
+.. _module-pw_allocator-size:
+
+Size report
+===========
+``pw_allocator`` provides some of its own implementations of the ``Allocator``
+interface, whos costs are shown below.
+
+.. include:: allocator_size_report
diff --git a/pw_allocator/fallback_allocator.cc b/pw_allocator/fallback_allocator.cc
index cfa7bd120..1e3faf666 100644
--- a/pw_allocator/fallback_allocator.cc
+++ b/pw_allocator/fallback_allocator.cc
@@ -24,39 +24,32 @@ void FallbackAllocator::Initialize(Allocator& primary, Allocator& secondary) {
secondary_ = &secondary;
}
-Status FallbackAllocator::DoQuery(const void* ptr,
- size_t size,
- size_t alignment) const {
+Status FallbackAllocator::DoQuery(const void* ptr, Layout layout) const {
PW_DCHECK(primary_ != nullptr && secondary_ != nullptr);
- auto status = primary_->QueryUnchecked(ptr, size, alignment);
- return status.ok() ? status
- : secondary_->QueryUnchecked(ptr, size, alignment);
+ auto status = primary_->Query(ptr, layout);
+ return status.ok() ? status : secondary_->Query(ptr, layout);
}
-void* FallbackAllocator::DoAllocate(size_t size, size_t alignment) {
+void* FallbackAllocator::DoAllocate(Layout layout) {
PW_DCHECK(primary_ != nullptr && secondary_ != nullptr);
- void* ptr = primary_->AllocateUnchecked(size, alignment);
- return ptr != nullptr ? ptr : secondary_->AllocateUnchecked(size, alignment);
+ void* ptr = primary_->Allocate(layout);
+ return ptr != nullptr ? ptr : secondary_->Allocate(layout);
}
-void FallbackAllocator::DoDeallocate(void* ptr, size_t size, size_t alignment) {
+void FallbackAllocator::DoDeallocate(void* ptr, Layout layout) {
PW_DCHECK(primary_ != nullptr && secondary_ != nullptr);
- if (primary_->QueryUnchecked(ptr, size, alignment).ok()) {
- primary_->DeallocateUnchecked(ptr, size, alignment);
+ if (primary_->Query(ptr, layout).ok()) {
+ primary_->Deallocate(ptr, layout);
} else {
- secondary_->DeallocateUnchecked(ptr, size, alignment);
+ secondary_->Deallocate(ptr, layout);
}
}
-bool FallbackAllocator::DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
+bool FallbackAllocator::DoResize(void* ptr, Layout layout, size_t new_size) {
PW_DCHECK(primary_ != nullptr && secondary_ != nullptr);
- return primary_->QueryUnchecked(ptr, old_size, old_alignment).ok()
- ? primary_->ResizeUnchecked(ptr, old_size, old_alignment, new_size)
- : secondary_->ResizeUnchecked(
- ptr, old_size, old_alignment, new_size);
+ return primary_->Query(ptr, layout).ok()
+ ? primary_->Resize(ptr, layout, new_size)
+ : secondary_->Resize(ptr, layout, new_size);
}
} // namespace pw::allocator
diff --git a/pw_allocator/fallback_allocator_test.cc b/pw_allocator/fallback_allocator_test.cc
index 982c6cc7d..c623fa652 100644
--- a/pw_allocator/fallback_allocator_test.cc
+++ b/pw_allocator/fallback_allocator_test.cc
@@ -15,7 +15,7 @@
#include "pw_allocator/fallback_allocator.h"
#include "gtest/gtest.h"
-#include "pw_allocator_private/allocator_testing.h"
+#include "pw_allocator/allocator_testing.h"
#include "pw_status/status.h"
namespace pw::allocator {
@@ -23,75 +23,73 @@ namespace {
// Test fixtures.
-struct FallbackAllocatorTest : ::testing::Test {
- private:
- std::array<std::byte, 128> buffer1 = {};
- std::array<std::byte, 128> buffer2 = {};
+class FallbackAllocatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override { allocator.Initialize(*primary, *secondary); }
- public:
- test::FakeAllocator primary;
- test::FakeAllocator secondary;
- FallbackAllocator allocator;
-
- void SetUp() override {
- EXPECT_EQ(primary.Initialize(buffer1), OkStatus());
- EXPECT_EQ(secondary.Initialize(buffer2), OkStatus());
- allocator.Initialize(primary, secondary);
+ void TearDown() override {
+ primary->DeallocateAll();
+ secondary->DeallocateAll();
}
+
+ test::AllocatorForTestWithBuffer<128> primary;
+ test::AllocatorForTestWithBuffer<128> secondary;
+ FallbackAllocator allocator;
};
// Unit tests.
TEST_F(FallbackAllocatorTest, QueryValidPrimary) {
Layout layout = Layout::Of<uint32_t>();
- void* ptr = primary.Allocate(layout);
- EXPECT_TRUE(primary.Query(ptr, layout).ok());
- EXPECT_EQ(secondary.Query(ptr, layout), Status::OutOfRange());
+ void* ptr = primary->Allocate(layout);
+ EXPECT_TRUE(primary->Query(ptr, layout).ok());
+ EXPECT_EQ(secondary->Query(ptr, layout), Status::OutOfRange());
EXPECT_TRUE(allocator.Query(ptr, layout).ok());
}
TEST_F(FallbackAllocatorTest, QueryValidSecondary) {
Layout layout = Layout::Of<uint32_t>();
- void* ptr = secondary.Allocate(layout);
- EXPECT_FALSE(primary.Query(ptr, layout).ok());
- EXPECT_TRUE(secondary.Query(ptr, layout).ok());
+ void* ptr = secondary->Allocate(layout);
+ EXPECT_FALSE(primary->Query(ptr, layout).ok());
+ EXPECT_TRUE(secondary->Query(ptr, layout).ok());
EXPECT_TRUE(allocator.Query(ptr, layout).ok());
}
TEST_F(FallbackAllocatorTest, QueryInvalidPtr) {
std::array<std::byte, 128> buffer = {};
- test::FakeAllocator other;
- ASSERT_TRUE(other.Initialize(buffer).ok());
+ test::AllocatorForTest other;
+ ASSERT_EQ(other.Init(buffer), OkStatus());
Layout layout = Layout::Of<uint32_t>();
void* ptr = other.Allocate(layout);
- EXPECT_FALSE(primary.Query(ptr, layout).ok());
- EXPECT_FALSE(secondary.Query(ptr, layout).ok());
+ EXPECT_FALSE(primary->Query(ptr, layout).ok());
+ EXPECT_FALSE(secondary->Query(ptr, layout).ok());
EXPECT_FALSE(allocator.Query(ptr, layout).ok());
+ other.DeallocateAll();
}
TEST_F(FallbackAllocatorTest, AllocateFromPrimary) {
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), 0U);
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), 0U);
}
TEST_F(FallbackAllocatorTest, AllocateFromSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), layout.size());
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, AllocateFailure) {
Layout layout = Layout::Of<uint32_t[0x10000]>();
void* ptr = allocator.Allocate(layout);
EXPECT_EQ(ptr, nullptr);
- EXPECT_EQ(primary.allocate_size(), layout.size());
- EXPECT_EQ(secondary.allocate_size(), layout.size());
+ EXPECT_EQ(primary->allocate_size(), layout.size());
+ EXPECT_EQ(secondary->allocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, DeallocateUsingPrimary) {
@@ -99,22 +97,22 @@ TEST_F(FallbackAllocatorTest, DeallocateUsingPrimary) {
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
allocator.Deallocate(ptr, layout);
- EXPECT_EQ(primary.deallocate_ptr(), ptr);
- EXPECT_EQ(primary.deallocate_size(), layout.size());
- EXPECT_EQ(secondary.deallocate_ptr(), nullptr);
- EXPECT_EQ(secondary.deallocate_size(), 0U);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr);
+ EXPECT_EQ(primary->deallocate_size(), layout.size());
+ EXPECT_EQ(secondary->deallocate_ptr(), nullptr);
+ EXPECT_EQ(secondary->deallocate_size(), 0U);
}
TEST_F(FallbackAllocatorTest, DeallocateUsingSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
allocator.Deallocate(ptr, layout);
- EXPECT_EQ(primary.deallocate_ptr(), nullptr);
- EXPECT_EQ(primary.deallocate_size(), 0U);
- EXPECT_EQ(secondary.deallocate_ptr(), ptr);
- EXPECT_EQ(secondary.deallocate_size(), layout.size());
+ EXPECT_EQ(primary->deallocate_ptr(), nullptr);
+ EXPECT_EQ(primary->deallocate_size(), 0U);
+ EXPECT_EQ(secondary->deallocate_ptr(), ptr);
+ EXPECT_EQ(secondary->deallocate_size(), layout.size());
}
TEST_F(FallbackAllocatorTest, ResizePrimary) {
@@ -124,69 +122,69 @@ TEST_F(FallbackAllocatorTest, ResizePrimary) {
size_t new_size = sizeof(uint32_t[3]);
EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(primary.resize_ptr(), ptr);
- EXPECT_EQ(primary.resize_old_size(), old_layout.size());
- EXPECT_EQ(primary.resize_new_size(), new_size);
+ EXPECT_EQ(primary->resize_ptr(), ptr);
+ EXPECT_EQ(primary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(primary->resize_new_size(), new_size);
// Secondary should not be touched.
- EXPECT_EQ(secondary.resize_ptr(), nullptr);
- EXPECT_EQ(secondary.resize_old_size(), 0U);
- EXPECT_EQ(secondary.resize_new_size(), 0U);
+ EXPECT_EQ(secondary->resize_ptr(), nullptr);
+ EXPECT_EQ(secondary->resize_old_size(), 0U);
+ EXPECT_EQ(secondary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizePrimaryFailure) {
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
- primary.Exhaust();
+ primary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(primary.resize_ptr(), ptr);
- EXPECT_EQ(primary.resize_old_size(), old_layout.size());
- EXPECT_EQ(primary.resize_new_size(), new_size);
+ EXPECT_EQ(primary->resize_ptr(), ptr);
+ EXPECT_EQ(primary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(primary->resize_new_size(), new_size);
// Secondary should not be touched.
- EXPECT_EQ(secondary.resize_ptr(), nullptr);
- EXPECT_EQ(secondary.resize_old_size(), 0U);
- EXPECT_EQ(secondary.resize_new_size(), 0U);
+ EXPECT_EQ(secondary->resize_ptr(), nullptr);
+ EXPECT_EQ(secondary->resize_old_size(), 0U);
+ EXPECT_EQ(secondary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizeSecondary) {
- primary.Exhaust();
+ primary->Exhaust();
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
size_t new_size = sizeof(uint32_t[3]);
EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(secondary.resize_ptr(), ptr);
- EXPECT_EQ(secondary.resize_old_size(), old_layout.size());
- EXPECT_EQ(secondary.resize_new_size(), new_size);
+ EXPECT_EQ(secondary->resize_ptr(), ptr);
+ EXPECT_EQ(secondary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(secondary->resize_new_size(), new_size);
// Primary should not be touched.
- EXPECT_EQ(primary.resize_ptr(), nullptr);
- EXPECT_EQ(primary.resize_old_size(), 0U);
- EXPECT_EQ(primary.resize_new_size(), 0U);
+ EXPECT_EQ(primary->resize_ptr(), nullptr);
+ EXPECT_EQ(primary->resize_old_size(), 0U);
+ EXPECT_EQ(primary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ResizeSecondaryFailure) {
- primary.Exhaust();
+ primary->Exhaust();
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
- secondary.Exhaust();
+ secondary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_size));
- EXPECT_EQ(secondary.resize_ptr(), ptr);
- EXPECT_EQ(secondary.resize_old_size(), old_layout.size());
- EXPECT_EQ(secondary.resize_new_size(), new_size);
+ EXPECT_EQ(secondary->resize_ptr(), ptr);
+ EXPECT_EQ(secondary->resize_old_size(), old_layout.size());
+ EXPECT_EQ(secondary->resize_new_size(), new_size);
// Primary should not be touched.
- EXPECT_EQ(primary.resize_ptr(), nullptr);
- EXPECT_EQ(primary.resize_old_size(), 0U);
- EXPECT_EQ(primary.resize_new_size(), 0U);
+ EXPECT_EQ(primary->resize_ptr(), nullptr);
+ EXPECT_EQ(primary->resize_old_size(), 0U);
+ EXPECT_EQ(primary->resize_new_size(), 0U);
}
TEST_F(FallbackAllocatorTest, ReallocateSameAllocator) {
@@ -201,22 +199,22 @@ TEST_F(FallbackAllocatorTest, ReallocateSameAllocator) {
size_t new_size = sizeof(uint32_t[3]);
void* new_ptr = allocator.Reallocate(ptr1, old_layout, new_size);
EXPECT_NE(new_ptr, nullptr);
- EXPECT_EQ(primary.deallocate_ptr(), ptr1);
- EXPECT_EQ(primary.deallocate_size(), old_layout.size());
- EXPECT_EQ(primary.allocate_size(), new_size);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr1);
+ EXPECT_EQ(primary->deallocate_size(), old_layout.size());
+ EXPECT_EQ(primary->allocate_size(), new_size);
}
TEST_F(FallbackAllocatorTest, ReallocateDifferentAllocator) {
Layout old_layout = Layout::Of<uint32_t>();
void* ptr = allocator.Allocate(old_layout);
- primary.Exhaust();
+ primary->Exhaust();
size_t new_size = sizeof(uint32_t[3]);
void* new_ptr = allocator.Reallocate(ptr, old_layout, new_size);
EXPECT_NE(new_ptr, nullptr);
- EXPECT_EQ(primary.deallocate_ptr(), ptr);
- EXPECT_EQ(primary.deallocate_size(), old_layout.size());
- EXPECT_EQ(secondary.allocate_size(), new_size);
+ EXPECT_EQ(primary->deallocate_ptr(), ptr);
+ EXPECT_EQ(primary->deallocate_size(), old_layout.size());
+ EXPECT_EQ(secondary->allocate_size(), new_size);
}
} // namespace
diff --git a/pw_allocator/freelist_heap.cc b/pw_allocator/freelist_heap.cc
index 1558278e4..c57eeba6a 100644
--- a/pw_allocator/freelist_heap.cc
+++ b/pw_allocator/freelist_heap.cc
@@ -23,10 +23,12 @@ namespace pw::allocator {
FreeListHeap::FreeListHeap(span<std::byte> region, FreeList& freelist)
: freelist_(freelist), heap_stats_() {
- Block* block;
+ auto result = BlockType::Init(region);
PW_CHECK_OK(
- Block::Init(region, &block),
+ result.status(),
"Failed to initialize FreeListHeap region; misaligned or too small");
+ BlockType* block = *result;
+ block->CrashIfInvalid();
freelist_.AddChunk(BlockToSpan(block))
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
@@ -46,15 +48,14 @@ void* FreeListHeap::Allocate(size_t size) {
freelist_.RemoveChunk(chunk)
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
- Block* chunk_block = Block::FromUsableSpace(chunk.data());
+ BlockType* chunk_block = BlockType::FromUsableSpace(chunk.data());
chunk_block->CrashIfInvalid();
// Split that chunk. If there's a leftover chunk, add it to the freelist
- Block* leftover;
- auto status = chunk_block->Split(size, &leftover);
- if (status == PW_STATUS_OK) {
- freelist_.AddChunk(BlockToSpan(leftover))
+ Result<BlockType*> result = BlockType::Split(chunk_block, size);
+ if (result.ok()) {
+ freelist_.AddChunk(BlockToSpan(*result))
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
}
@@ -75,7 +76,7 @@ void FreeListHeap::Free(void* ptr) {
return;
}
- Block* chunk_block = Block::FromUsableSpace(bytes);
+ BlockType* chunk_block = BlockType::FromUsableSpace(bytes);
chunk_block->CrashIfInvalid();
size_t size_freed = chunk_block->InnerSize();
@@ -86,8 +87,8 @@ void FreeListHeap::Free(void* ptr) {
}
chunk_block->MarkFree();
// Can we combine with the left or right blocks?
- Block* prev = chunk_block->Prev();
- Block* next = nullptr;
+ BlockType* prev = chunk_block->Prev();
+ BlockType* next = nullptr;
if (!chunk_block->Last()) {
next = chunk_block->Next();
@@ -97,17 +98,15 @@ void FreeListHeap::Free(void* ptr) {
// Remove from freelist and merge
freelist_.RemoveChunk(BlockToSpan(prev))
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
- chunk_block->MergePrev()
+ chunk_block = chunk_block->Prev();
+ BlockType::MergeNext(chunk_block)
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
-
- // chunk_block is now invalid; prev now encompasses it.
- chunk_block = prev;
}
if (next != nullptr && !next->Used()) {
freelist_.RemoveChunk(BlockToSpan(next))
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
- chunk_block->MergeNext()
+ BlockType::MergeNext(chunk_block)
.IgnoreError(); // TODO: b/242598609 - Handle Status properly
}
// Add back to the freelist
@@ -139,7 +138,7 @@ void* FreeListHeap::Realloc(void* ptr, size_t size) {
return nullptr;
}
- Block* chunk_block = Block::FromUsableSpace(bytes);
+ BlockType* chunk_block = BlockType::FromUsableSpace(bytes);
if (!chunk_block->Used()) {
return nullptr;
}
diff --git a/pw_allocator/freelist_heap_test.cc b/pw_allocator/freelist_heap_test.cc
index 0d3353c3b..de8f2047e 100644
--- a/pw_allocator/freelist_heap_test.cc
+++ b/pw_allocator/freelist_heap_test.cc
@@ -22,7 +22,7 @@ namespace pw::allocator {
TEST(FreeListHeap, CanAllocate) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -30,13 +30,13 @@ TEST(FreeListHeap, CanAllocate) {
ASSERT_NE(ptr, nullptr);
// In this case, the allocator should be returning us the start of the chunk.
- EXPECT_EQ(ptr, &buf[0] + sizeof(Block) + PW_ALLOCATOR_POISON_OFFSET);
+ EXPECT_EQ(ptr, &buf[0] + FreeListHeap::BlockType::kHeaderSize);
}
TEST(FreeListHeap, AllocationsDontOverlap) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -58,7 +58,7 @@ TEST(FreeListHeap, CanFreeAndRealloc) {
// and get that value back again.
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -71,7 +71,7 @@ TEST(FreeListHeap, CanFreeAndRealloc) {
TEST(FreeListHeap, ReturnsNullWhenAllocationTooLarge) {
constexpr size_t N = 2048;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -80,19 +80,18 @@ TEST(FreeListHeap, ReturnsNullWhenAllocationTooLarge) {
TEST(FreeListHeap, ReturnsNullWhenFull) {
constexpr size_t N = 2048;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
- EXPECT_NE(
- allocator.Allocate(N - sizeof(Block) - 2 * PW_ALLOCATOR_POISON_OFFSET),
- nullptr);
+ EXPECT_NE(allocator.Allocate(N - FreeListHeap::BlockType::kBlockOverhead),
+ nullptr);
EXPECT_EQ(allocator.Allocate(1), nullptr);
}
TEST(FreeListHeap, ReturnedPointersAreAligned) {
constexpr size_t N = 2048;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -118,7 +117,7 @@ TEST(FreeListHeap, CannotFreeNonOwnedPointer) {
// We can cheat; create a heap, allocate it all, and try and return something
// random to it. Try allocating again, and check that we get nullptr back.
constexpr size_t N = 2048;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -145,7 +144,7 @@ TEST(FreeListHeap, CanRealloc) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
constexpr size_t kNewAllocSize = 768;
- alignas(Block) std::byte buf[N] = {std::byte(1)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(1)};
FreeListHeapBuffer allocator(buf);
@@ -160,7 +159,7 @@ TEST(FreeListHeap, ReallocHasSameContent) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = sizeof(int);
constexpr size_t kNewAllocSize = sizeof(int) * 2;
- alignas(Block) std::byte buf[N] = {std::byte(1)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(1)};
// Data inside the allocated block.
std::byte data1[kAllocSize];
// Data inside the reallocated block.
@@ -184,7 +183,7 @@ TEST(FreeListHeap, ReturnsNullReallocFreedPointer) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
constexpr size_t kNewAllocSize = 256;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -199,7 +198,7 @@ TEST(FreeListHeap, ReallocSmallerSize) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
constexpr size_t kNewAllocSize = 256;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -214,7 +213,7 @@ TEST(FreeListHeap, ReallocTooLarge) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 512;
constexpr size_t kNewAllocSize = 4096;
- alignas(Block) std::byte buf[N] = {std::byte(0)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(0)};
FreeListHeapBuffer allocator(buf);
@@ -231,7 +230,7 @@ TEST(FreeListHeap, CanCalloc) {
constexpr size_t kAllocSize = 128;
constexpr size_t kNum = 4;
constexpr int size = kNum * kAllocSize;
- alignas(Block) std::byte buf[N] = {std::byte(1)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(1)};
constexpr std::byte zero{0};
FreeListHeapBuffer allocator(buf);
@@ -250,7 +249,7 @@ TEST(FreeListHeap, CanCallocWeirdSize) {
constexpr size_t kAllocSize = 143;
constexpr size_t kNum = 3;
constexpr int size = kNum * kAllocSize;
- alignas(Block) std::byte buf[N] = {std::byte(132)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(132)};
constexpr std::byte zero{0};
FreeListHeapBuffer allocator(buf);
@@ -267,7 +266,7 @@ TEST(FreeListHeap, CanCallocWeirdSize) {
TEST(FreeListHeap, CallocTooLarge) {
constexpr size_t N = 2048;
constexpr size_t kAllocSize = 2049;
- alignas(Block) std::byte buf[N] = {std::byte(1)};
+ alignas(FreeListHeap::BlockType) std::byte buf[N] = {std::byte(1)};
FreeListHeapBuffer allocator(buf);
diff --git a/pw_allocator/libc_allocator.cc b/pw_allocator/libc_allocator.cc
index c848d6c9f..8469bcdae 100644
--- a/pw_allocator/libc_allocator.cc
+++ b/pw_allocator/libc_allocator.cc
@@ -24,27 +24,26 @@
namespace pw::allocator {
-void* LibCAllocator::DoAllocate(size_t size, size_t alignment) {
+void* LibCAllocator::DoAllocate(Layout layout) {
// TODO: b/301930507 - `aligned_alloc` is not portable. Return null for larger
// allocations for now.
- return alignment <= alignof(std::max_align_t) ? std::malloc(size) : nullptr;
+ return layout.alignment() <= alignof(std::max_align_t)
+ ? std::malloc(layout.size())
+ : nullptr;
}
-void LibCAllocator::DoDeallocate(void* ptr, size_t, size_t) { std::free(ptr); }
+void LibCAllocator::DoDeallocate(void* ptr, Layout) { std::free(ptr); }
-bool LibCAllocator::DoResize(void*, size_t old_size, size_t, size_t new_size) {
+bool LibCAllocator::DoResize(void*, Layout layout, size_t new_size) {
// `realloc` may move memory, even when shrinking. Only return true if no
// change is needed.
- return old_size == new_size;
+ return layout.size() == new_size;
}
-void* LibCAllocator::DoReallocate(void* ptr,
- size_t,
- size_t old_alignment,
- size_t new_size) {
+void* LibCAllocator::DoReallocate(void* ptr, Layout layout, size_t new_size) {
// TODO: b/301930507 - `aligned_alloc` is not portable. Return null for larger
// allocations for now.
- return old_alignment <= alignof(std::max_align_t)
+ return layout.alignment() <= alignof(std::max_align_t)
? std::realloc(ptr, new_size)
: nullptr;
}
diff --git a/pw_allocator/libc_allocator_test.cc b/pw_allocator/libc_allocator_test.cc
index d69dcb072..15eaac212 100644
--- a/pw_allocator/libc_allocator_test.cc
+++ b/pw_allocator/libc_allocator_test.cc
@@ -22,7 +22,8 @@ namespace pw::allocator {
// Test fixtures.
-struct LibCAllocatorTest : ::testing::Test {
+class LibCAllocatorTest : public ::testing::Test {
+ protected:
LibCAllocator allocator;
};
@@ -42,7 +43,7 @@ TEST_F(LibCAllocatorTest, AllocateLargeAlignment) {
/// allocator has a maximum alignment of `std::align_max_t`.
size_t size = 16;
size_t alignment = alignof(std::max_align_t) * 2;
- void* ptr = allocator.AllocateUnchecked(size, alignment);
+ void* ptr = allocator.Allocate(Layout(size, alignment));
EXPECT_EQ(ptr, nullptr);
}
diff --git a/pw_allocator/null_allocator_test.cc b/pw_allocator/null_allocator_test.cc
index a257e2658..0b319646a 100644
--- a/pw_allocator/null_allocator_test.cc
+++ b/pw_allocator/null_allocator_test.cc
@@ -23,7 +23,7 @@ TEST(NullAllocatorTest, Allocate) {
// Allocate should fail, regardless of size and alignment.
for (size_t size = 1; size < 0x100; size <<= 1) {
for (size_t alignment = 1; alignment < 0x100; alignment <<= 1) {
- EXPECT_EQ(allocator.AllocateUnchecked(size, alignment), nullptr);
+ EXPECT_EQ(allocator.Allocate(Layout(size, alignment)), nullptr);
}
}
}
diff --git a/pw_allocator/public/pw_allocator/allocator.h b/pw_allocator/public/pw_allocator/allocator.h
index 63ec8e5a2..a36d3ea3e 100644
--- a/pw_allocator/public/pw_allocator/allocator.h
+++ b/pw_allocator/public/pw_allocator/allocator.h
@@ -15,6 +15,7 @@
#include <cstddef>
#include <optional>
+#include <utility>
#include "pw_status/status.h"
@@ -35,23 +36,30 @@ namespace pw::allocator {
/// @endcode
class Layout {
public:
+ constexpr Layout(size_t size, size_t alignment = alignof(std::max_align_t))
+ : size_(size), alignment_(alignment) {}
+
/// Creates a Layout for the given type.
template <typename T>
static constexpr Layout Of() {
return Layout(sizeof(T), alignof(T));
}
+ constexpr Layout Extend(size_t size) {
+ return Layout(size_ + size, alignment_);
+ }
+
size_t size() const { return size_; }
size_t alignment() const { return alignment_; }
private:
- constexpr Layout(size_t size, size_t alignment)
- : size_(size), alignment_(alignment) {}
-
size_t size_;
size_t alignment_;
};
+template <typename T>
+class UniquePtr;
+
/// Abstract interface for memory allocation.
///
/// This is the most generic and fundamental interface provided by the
@@ -93,15 +101,7 @@ class Allocator {
/// object.
/// @retval OK This object can re/deallocate the pointer.
Status Query(const void* ptr, Layout layout) const {
- return DoQuery(ptr, layout.size(), layout.alignment());
- }
-
- /// Like `Query`, but takes its parameters directly instead of as a `Layout`.
- ///
- /// Callers should almost always prefer `Query`. This method is meant for use
- /// by tests and other allocators implementing the virtual functions below.
- Status QueryUnchecked(const void* ptr, size_t size, size_t alignment) const {
- return DoQuery(ptr, size, alignment);
+ return DoQuery(ptr, layout);
}
/// Allocates a block of memory with the specified size and alignment.
@@ -110,18 +110,18 @@ class Allocator {
/// size of 0.
///
/// @param[in] layout Describes the memory to be allocated.
- void* Allocate(Layout layout) {
- return DoAllocate(layout.size(), layout.alignment());
- }
+ void* Allocate(Layout layout) { return DoAllocate(layout); }
- /// Like `Allocate`, but takes its parameters directly instead of as a
- /// `Layout`.
- ///
- /// Callers should almost always prefer `Allocate`. This method is meant for
- /// use by tests and other allocators implementing the virtual functions
- /// below.
- void* AllocateUnchecked(size_t size, size_t alignment) {
- return DoAllocate(size, alignment);
+ template <typename T, typename... Args>
+ std::optional<UniquePtr<T>> MakeUnique(Args&&... args) {
+ static constexpr Layout kStaticLayout = Layout::Of<T>();
+ void* void_ptr = Allocate(kStaticLayout);
+ if (void_ptr == nullptr) {
+ return std::nullopt;
+ }
+ T* ptr = new (void_ptr) T(std::forward<Args>(args)...);
+ return std::make_optional<UniquePtr<T>>(
+ UniquePtr<T>::kPrivateConstructor, ptr, &kStaticLayout, this);
}
/// Releases a previously-allocated block of memory.
@@ -132,17 +132,7 @@ class Allocator {
/// @param[in] ptr Pointer to previously-allocated memory.
/// @param[in] layout Describes the memory to be deallocated.
void Deallocate(void* ptr, Layout layout) {
- return DoDeallocate(ptr, layout.size(), layout.alignment());
- }
-
- /// Like `Deallocate`, but takes its parameters directly instead of as a
- /// `Layout`.
- ///
- /// Callers should almost always prefer `Deallocate`. This method is meant for
- /// use by tests and other allocators implementing the virtual functions
- /// below.
- void DeallocateUnchecked(void* ptr, size_t size, size_t alignment) {
- return DoDeallocate(ptr, size, alignment);
+ return DoDeallocate(ptr, layout);
}
/// Modifies the size of an previously-allocated block of memory without
@@ -152,25 +142,17 @@ class Allocator {
/// allocation; otherwise returns false.
///
/// In particular, it always returns true if the `old_layout.size()` equals
- /// `new_szie`, and always returns false if the given pointer is null, the
+ /// `new_size`, and always returns false if the given pointer is null, the
/// `old_layout.size()` is 0, or the `new_size` is 0.
///
/// @param[in] ptr Pointer to previously-allocated memory.
/// @param[in] old_layout Describes the previously-allocated memory.
/// @param[in] new_size Requested new size for the memory allocation.
- bool Resize(void* ptr, Layout old_layout, size_t new_size) {
- return DoResize(ptr, old_layout.size(), old_layout.alignment(), new_size);
- }
-
- /// Like `Resize`, but takes its parameters directly instead of as a `Layout`.
- ///
- /// Callers should almost always prefer `Resize`. This method is meant for use
- /// by tests and other allocators implementing the virtual functions below.
- bool ResizeUnchecked(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
- return DoResize(ptr, old_size, old_alignment, new_size);
+ bool Resize(void* ptr, Layout layout, size_t new_size) {
+ if (ptr == nullptr || layout.size() == 0 || new_size == 0) {
+ return false;
+ }
+ return DoResize(ptr, layout, new_size);
}
/// Modifies the size of a previously-allocated block of memory.
@@ -190,24 +172,10 @@ class Allocator {
/// 0 will return a new allocation.
///
/// @param[in] ptr Pointer to previously-allocated memory.
- /// @param[in] old_layout Describes the previously-allocated memory.
+ /// @param[in] layout Describes the previously-allocated memory.
/// @param[in] new_size Requested new size for the memory allocation.
- void* Reallocate(void* ptr, Layout old_layout, size_t new_size) {
- return DoReallocate(
- ptr, old_layout.size(), old_layout.alignment(), new_size);
- }
-
- /// Like `Reallocate`, but takes its parameters directly instead of as a
- /// `Layout`.
- ///
- /// Callers should almost always prefer `Reallocate`. This method is meant for
- /// use by tests and other allocators implementing the virtual functions
- /// below.
- void* ReallocateUnchecked(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
- return DoReallocate(ptr, old_size, old_alignment, new_size);
+ void* Reallocate(void* ptr, Layout layout, size_t new_size) {
+ return DoReallocate(ptr, layout, new_size);
}
private:
@@ -217,31 +185,196 @@ class Allocator {
/// Allocators which dispatch to other allocators need to override this method
/// in order to be able to direct reallocations and deallocations to
/// appropriate allocator.
- virtual Status DoQuery(const void*, size_t, size_t) const {
+ virtual Status DoQuery(const void*, Layout) const {
return Status::Unimplemented();
}
/// Virtual `Allocate` function implemented by derived classes.
- virtual void* DoAllocate(size_t size, size_t alignment) = 0;
+ virtual void* DoAllocate(Layout layout) = 0;
/// Virtual `Deallocate` function implemented by derived classes.
- virtual void DoDeallocate(void* ptr, size_t size, size_t alignment) = 0;
+ virtual void DoDeallocate(void* ptr, Layout layout) = 0;
/// Virtual `Resize` function implemented by derived classes.
- virtual bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) = 0;
+ ///
+ /// The default implementation simply returns `false`, indicating that
+ /// resizing is not supported.
+ virtual bool DoResize(void* /*ptr*/, Layout /*layout*/, size_t /*new_size*/) {
+ return false;
+ }
/// Virtual `Reallocate` function that can be overridden by derived classes.
///
/// The default implementation will first try to `Resize` the data. If that is
/// unsuccessful, it will allocate an entirely new block, copy existing data,
/// and deallocate the given block.
- virtual void* DoReallocate(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size);
+ virtual void* DoReallocate(void* ptr, Layout layout, size_t new_size);
+};
+
+/// An RAII pointer to a value of type ``T`` stored within an ``Allocator``.
+///
+/// This is analogous to ``std::unique_ptr``, but includes a few differences
+/// in order to support ``Allocator`` and encourage safe usage. Most notably,
+/// ``UniquePtr<T>`` cannot be constructed from a ``T*``.
+template <typename T>
+class UniquePtr {
+ public:
+ /// Creates an empty (``nullptr``) instance.
+ ///
+ /// NOTE: Instances of this type are most commonly constructed using
+ /// ``Allocator::MakeUnique``.
+ constexpr UniquePtr()
+ : value_(nullptr), layout_(nullptr), allocator_(nullptr) {}
+
+ /// Creates an empty (``nullptr``) instance.
+ ///
+ /// NOTE: Instances of this type are most commonly constructed using
+ /// ``Allocator::MakeUnique``.
+ constexpr UniquePtr(std::nullptr_t)
+ : value_(nullptr), layout_(nullptr), allocator_(nullptr) {}
+
+ /// Move-constructs a ``UniquePtr<T>`` from a ``UniquePtr<U>``.
+ ///
+ /// This allows not only pure move construction where ``T == U``, but also
+ /// converting construction where ``T`` is a base class of ``U``, like
+ /// ``UniquePtr<Base> base(allocator.MakeUnique<Child>());``.
+ template <typename U>
+ UniquePtr(UniquePtr<U>&& other) noexcept
+ : value_(other.value_),
+ layout_(other.layout_),
+ allocator_(other.allocator_) {
+ static_assert(
+ std::is_assignable_v<T*&, U*>,
+ "Attempted to construct a UniquePtr<T> from a UniquePtr<U> where "
+ "U* is not assignable to T*.");
+ other.Release();
+ }
+
+ /// Move-assigns a ``UniquePtr<T>`` from a ``UniquePtr<U>``.
+ ///
+ /// This operation destructs and deallocates any value currently stored in
+ /// ``this``.
+ ///
+ /// This allows not only pure move assignment where ``T == U``, but also
+ /// converting assignment where ``T`` is a base class of ``U``, like
+ /// ``UniquePtr<Base> base = allocator.MakeUnique<Child>();``.
+ template <typename U>
+ UniquePtr& operator=(UniquePtr<U>&& other) noexcept {
+ static_assert(std::is_assignable_v<T*&, U*>,
+ "Attempted to assign a UniquePtr<U> to a UniquePtr<T> where "
+ "U* is not assignable to T*.");
+ Reset();
+ value_ = other.value_;
+ layout_ = other.layout_;
+ allocator_ = other.allocator_;
+ other.Release();
+ }
+
+ /// Sets this ``UniquePtr`` to null, destructing and deallocating any
+ /// currently-held value.
+ ///
+ /// After this function returns, this ``UniquePtr`` will be in an "empty"
+ /// (``nullptr``) state until a new value is assigned.
+ UniquePtr& operator=(std::nullptr_t) { Reset(); }
+
+ /// Destructs and deallocates any currently-held value.
+ ~UniquePtr() { Reset(); }
+
+ /// Sets this ``UniquePtr`` to an "empty" (``nullptr``) value without
+ /// destructing any currently-held value or deallocating any underlying
+ /// memory.
+ void Release() {
+ value_ = nullptr;
+ layout_ = nullptr;
+ allocator_ = nullptr;
+ }
+
+ /// Destructs and deallocates any currently-held value.
+ ///
+ /// After this function returns, this ``UniquePtr`` will be in an "empty"
+ /// (``nullptr``) state until a new value is assigned.
+ void Reset() {
+ if (value_ != nullptr) {
+ value_->~T();
+ allocator_->Deallocate(value_, *layout_);
+ Release();
+ }
+ }
+
+ /// ``operator bool`` is not provided in order to ensure that there is no
+ /// confusion surrounding ``if (foo)`` vs. ``if (*foo)``.
+ ///
+ /// ``nullptr`` checking should instead use ``if (foo == nullptr)``.
+ explicit operator bool() const = delete;
+
+ /// Returns whether this ``UniquePtr`` is in an "empty" (``nullptr``) state.
+ bool operator==(std::nullptr_t) const { return value_ == nullptr; }
+
+ /// Returns whether this ``UniquePtr`` is not in an "empty" (``nullptr``)
+ /// state.
+ bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
+
+ /// Returns the underlying (possibly null) pointer.
+ T* get() { return value_; }
+ /// Returns the underlying (possibly null) pointer.
+ const T* get() const { return value_; }
+
+ /// Permits accesses to members of ``T`` via ``my_unique_ptr->Member``.
+ ///
+ /// The behavior of this operation is undefined if this ``UniquePtr`` is in an
+ /// "empty" (``nullptr``) state.
+ T* operator->() { return value_; }
+ const T* operator->() const { return value_; }
+
+ /// Returns a reference to any underlying value.
+ ///
+ /// The behavior of this operation is undefined if this ``UniquePtr`` is in an
+ /// "empty" (``nullptr``) state.
+ T& operator*() { return *value_; }
+ const T& operator*() const { return *value_; }
+
+ private:
+ /// A pointer to the contained value.
+ T* value_;
+
+ /// The ``layout_` with which ``value_``'s allocation was initially created.
+ ///
+ /// Unfortunately this is not simply ``Layout::Of<T>()`` since ``T`` may be
+ /// a base class of the original allocated type.
+ const Layout* layout_;
+
+ /// The ``allocator_`` in which ``value_`` is stored.
+ /// This must be tracked in order to deallocate the memory upon destruction.
+ Allocator* allocator_;
+
+ /// Allow converting move constructor and assignment to access fields of
+ /// this class.
+ ///
+ /// Without this, ``UniquePtr<U>`` would not be able to access fields of
+ /// ``UniquePtr<T>``.
+ template <typename U>
+ friend class UniquePtr;
+
+ class PrivateConstructorType {};
+ static constexpr PrivateConstructorType kPrivateConstructor{};
+
+ public:
+ /// Private constructor that is public only for use with `emplace` and
+ /// other in-place construction functions.
+ ///
+ /// Constructs a ``UniquePtr`` from an already-allocated value.
+ ///
+ /// NOTE: Instances of this type are most commonly constructed using
+ /// ``Allocator::MakeUnique``.
+ UniquePtr(PrivateConstructorType,
+ T* value,
+ const Layout* layout,
+ Allocator* allocator)
+ : value_(value), layout_(layout), allocator_(allocator) {}
+
+ // Allow construction with ``kPrivateConstructor`` to the implementation
+ // of ``MakeUnique``.
+ friend class Allocator;
};
} // namespace pw::allocator
diff --git a/pw_allocator/public/pw_allocator/allocator_metric_proxy.h b/pw_allocator/public/pw_allocator/allocator_metric_proxy.h
index 045d89545..b4cc3a561 100644
--- a/pw_allocator/public/pw_allocator/allocator_metric_proxy.h
+++ b/pw_allocator/public/pw_allocator/allocator_metric_proxy.h
@@ -50,19 +50,16 @@ class AllocatorMetricProxy : public Allocator {
private:
/// @copydoc Allocator::Query
- Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
+ Status DoQuery(const void* ptr, Layout layout) const override;
/// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
+ void* DoAllocate(Layout layout) override;
/// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
+ void DoDeallocate(void* ptr, Layout layout) override;
/// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
+ bool DoResize(void* ptr, Layout layout, size_t new_size) override;
metric::Group memusage_;
Allocator* allocator_ = nullptr;
diff --git a/pw_allocator/public/pw_allocator/allocator_testing.h b/pw_allocator/public/pw_allocator/allocator_testing.h
new file mode 100644
index 000000000..f5c84c9ee
--- /dev/null
+++ b/pw_allocator/public/pw_allocator/allocator_testing.h
@@ -0,0 +1,131 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <array>
+#include <cstddef>
+
+#include "gtest/gtest.h"
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/block.h"
+#include "pw_allocator/simple_allocator.h"
+#include "pw_bytes/span.h"
+
+namespace pw::allocator::test {
+
+/// Simple memory allocator for testing.
+///
+/// This allocator records the most recent parameters passed to the `Allocator`
+/// interface methods, and returns them via accessors.
+class AllocatorForTest : public Allocator {
+ public:
+ constexpr AllocatorForTest() = default;
+ ~AllocatorForTest() override;
+
+ size_t allocate_size() const { return allocate_size_; }
+ void* deallocate_ptr() const { return deallocate_ptr_; }
+ size_t deallocate_size() const { return deallocate_size_; }
+ void* resize_ptr() const { return resize_ptr_; }
+ size_t resize_old_size() const { return resize_old_size_; }
+ size_t resize_new_size() const { return resize_new_size_; }
+
+ /// Provides memory for the allocator to allocate from.
+ Status Init(ByteSpan bytes);
+
+ /// Allocates all the memory from this object.
+ void Exhaust();
+
+ /// Resets the recorded parameters to an initial state.
+ void ResetParameters();
+
+ /// This frees all memory associated with this allocator.
+ void DeallocateAll();
+
+ private:
+ using BlockType = Block<>;
+
+ /// @copydoc Allocator::Query
+ Status DoQuery(const void* ptr, Layout layout) const override;
+
+ /// @copydoc Allocator::Allocate
+ void* DoAllocate(Layout layout) override;
+
+ /// @copydoc Allocator::Deallocate
+ void DoDeallocate(void* ptr, Layout layout) override;
+
+ /// @copydoc Allocator::Resize
+ bool DoResize(void* ptr, Layout layout, size_t new_size) override;
+
+ SimpleAllocator allocator_;
+ size_t allocate_size_ = 0;
+ void* deallocate_ptr_ = nullptr;
+ size_t deallocate_size_ = 0;
+ void* resize_ptr_ = nullptr;
+ size_t resize_old_size_ = 0;
+ size_t resize_new_size_ = 0;
+};
+
+/// Wraps a default-constructed type a buffer holding a region of memory.
+///
+/// Although the type is arbitrary, the intended purpose of of this class is to
+/// provide allocators with memory to use when testing.
+///
+/// This class uses composition instead of inheritance in order to allow the
+/// wrapped type's destructor to reference the memory without risk of a
+/// use-after-free. As a result, the specific methods of the wrapped type
+/// are not directly accesible. Instead, they can be accessed using the `*` and
+/// `->` operators, e.g.
+///
+/// @code{.cpp}
+/// WithBuffer<MyAllocator, 256> allocator;
+/// allocator->MethodSpecificToMyAllocator();
+/// @endcode
+///
+/// Note that this class does NOT initialize the allocator, since initialization
+/// is not specified as part of the `Allocator` interface and may vary from
+/// allocator to allocator. As a result, typical usgae includes deriving a class
+/// that initializes the wrapped allocator with the buffer in a constructor. See
+/// `AllocatorForTestWithBuffer` below for an example.
+///
+/// @tparam T The wrapped object.
+/// @tparam kBufferSize The size of the backing memory, in bytes.
+/// @tparam AlignType Buffer memory will be aligned to this type's
+/// alignment boundary.
+template <typename T, size_t kBufferSize, typename AlignType = uint8_t>
+class WithBuffer {
+ public:
+ static constexpr size_t kCapacity = kBufferSize;
+
+ std::byte* data() { return buffer_.data(); }
+ size_t size() const { return buffer_.size(); }
+
+ T& operator*() { return obj_; }
+ T* operator->() { return &obj_; }
+
+ private:
+ alignas(AlignType) std::array<std::byte, kBufferSize> buffer_;
+ T obj_;
+};
+
+/// An `AllocatorForTest` that is automatically initialized on construction.
+template <size_t kBufferSize>
+class AllocatorForTestWithBuffer
+ : public WithBuffer<AllocatorForTest, kBufferSize> {
+ public:
+ AllocatorForTestWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size())), OkStatus());
+ }
+};
+
+} // namespace pw::allocator::test
diff --git a/pw_allocator/public/pw_allocator/block.h b/pw_allocator/public/pw_allocator/block.h
index 35e9c01aa..4b5e7e594 100644
--- a/pw_allocator/public/pw_allocator/block.h
+++ b/pw_allocator/public/pw_allocator/block.h
@@ -11,265 +11,848 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-
-// WARNING: This code is a experimental WIP & exploration only, and is far from
-// usable.
#pragma once
#include <cstdint>
+#include <cstring>
+#include "lib/stdcompat/bit.h"
+#include "pw_bytes/alignment.h"
+#include "pw_bytes/span.h"
+#include "pw_result/result.h"
#include "pw_span/span.h"
#include "pw_status/status.h"
namespace pw::allocator {
+/// Representation-independent base class of Block.
+///
+/// This class contains static methods which do not depend on the template
+/// parameters of ``Block`` that are used to encode block information. This
+/// reduces the amount of code generated for ``Block``s with different
+/// parameters.
+///
+/// This class should not be used directly. Instead, see ``Block``.
+class BaseBlock {
+ public:
#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
-// Add poison offset of sizeof(void*) bytes before and after usable space in all
-// Blocks.
-#define PW_ALLOCATOR_POISON_OFFSET sizeof(void*)
+ // Add poison offset of 8 bytes before and after usable space in all
+ // Blocks.
+ static constexpr size_t kPoisonOffset = 8;
#else
-// Set the poison offset to 0 bytes; will not add poisson space before and
-// after usable space in all Blocks.
-#define PW_ALLOCATOR_POISON_OFFSET static_cast<size_t>(0)
+ // Set the poison offset to 0 bytes; will not add poison space before and
+ // after usable space in all Blocks.
+ static constexpr size_t kPoisonOffset = 0;
#endif // PW_ALLOCATOR_POISON_ENABLE
-/// @brief The `Block` type is intended to be a building block component for
-/// allocators.
+ // No copy/move
+ BaseBlock(const BaseBlock& other) = delete;
+ BaseBlock& operator=(const BaseBlock& other) = delete;
+ BaseBlock(BaseBlock&& other) = delete;
+ BaseBlock& operator=(BaseBlock&& other) = delete;
+
+ protected:
+ enum BlockStatus {
+ kValid,
+ kMisaligned,
+ kPrevMismatched,
+ kNextMismatched,
+ kPoisonCorrupted,
+ };
+
+#if defined(PW_ALLOCATOR_POISON_ENABLE) && PW_ALLOCATOR_POISON_ENABLE
+ static constexpr std::byte kPoisonPattern[kPoisonOffset] = {
+ std::byte{0x92},
+ std::byte{0x88},
+ std::byte{0x0a},
+ std::byte{0x00},
+ std::byte{0xec},
+ std::byte{0xdc},
+ std::byte{0xae},
+ std::byte{0x4e},
+ };
+#endif // PW_ALLOCATOR_POISON_ENABLE
+
+ BaseBlock() = default;
+
+ /// Poisons the block's guard regions, if poisoning is enabled.
+ ///
+ /// Does nothing if poisoning is disabled.
+ static void Poison(void* block, size_t header_size, size_t outer_size);
+
+ /// Returns whether the block's guard regions are untouched, if poisoning is
+ /// enabled.
+ ///
+ /// Trivially returns true if poisoning is disabled.
+ static bool CheckPoison(const void* block,
+ size_t header_size,
+ size_t outer_size);
+
+ static void CrashMisaligned(uintptr_t addr);
+ static void CrashNextMismatched(uintptr_t addr, uintptr_t next_prev);
+ static void CrashPrevMismatched(uintptr_t addr, uintptr_t prev_next);
+ static void CrashPoisonCorrupted(uintptr_t addr);
+
+ // Associated types
+
+ /// Iterator for a list of blocks.
+ ///
+ /// This class is templated both on the concrete block type, as well as on a
+ /// function that can advance the iterator to the next element. This class
+ /// cannot be instantiated directly. Instead, use the `begin` and `end`
+ /// methods of `Block::Range` or `Block::ReverseRange`.
+ template <typename BlockType, BlockType* (*Advance)(const BlockType*)>
+ class BaseIterator {
+ public:
+ BaseIterator& operator++() {
+ if (block_ != nullptr) {
+ block_ = Advance(block_);
+ }
+ return *this;
+ }
+
+ bool operator!=(const BaseIterator& other) {
+ return block_ != other.block_;
+ }
+
+ BlockType* operator*() { return block_; }
+
+ protected:
+ BaseIterator(BlockType* block) : block_(block) {}
+
+ private:
+ BlockType* block_;
+ };
+
+ /// Represents a range of blocks in a list.
+ ///
+ /// This class is templated both on the concrete block and iterator types.
+ /// This class cannot be instantiated directly. Instead, use `Block::Range` or
+ /// `Block::ReverseRange`.
+ template <typename BlockType, typename IteratorType>
+ class BaseRange {
+ public:
+ IteratorType& begin() { return begin_; }
+ IteratorType& end() { return end_; }
+
+ protected:
+ BaseRange(BlockType* begin_inclusive, BlockType* end_exclusive)
+ : begin_(begin_inclusive), end_(end_exclusive) {}
+
+ private:
+ IteratorType begin_;
+ IteratorType end_;
+ };
+};
+
+/// @brief Represents a region of memory as an element of a doubly linked list.
///
-/// In this design, there is an explicit pointer to `Next` and
-/// `Prev` from the block header; the size is not encoded. The below diagram
-/// shows what this would look like for two blocks.
+/// Typically, an application will start with a single block representing a
+/// contiguous region of memory returned from a call to `Init`. This block can
+/// be split into smaller blocks that refer to their neighbors. Neighboring
+/// blocks can be merged. These behaviors allows ``Allocator``s to track
+/// allocated memory with a small amount of overhead. See
+/// pw_allocator_private/simple_allocator.h for an example.
///
-/// @code{.unparsed}
-/// .------+---------------------------------.-----------------------------
-/// | Block A (first) | Block B (second)
+/// Blocks will always be aligned to a `kAlignment boundary. Block sizes will
+/// always be rounded up to a multiple of `kAlignment`.
///
-/// +------+------+--------------------------+------+------+---------------
-/// | Next | Prev | usable space | Next | Prev | usable space..
-/// +------+------+--------------------------+------+--+---+---------------
-/// ^ | ^ |
-/// | '-------------------------------------' |
-/// | |
-/// '----------- Block B's prev points to Block A -----'
-/// @endcode
+/// The blocks do not encode their size. Instead, they encode the offsets to the
+/// next and previous blocks. These offsets are encoded using the type given by
+/// the template parameter `T`. The encoded offsets are simply the offsets
+/// divded by the minimum alignment.
///
-/// One use for these blocks is to use them as allocations, where each block
-/// represents an allocation handed out by `malloc()`. These blocks could also
-/// be used as part of a slab or buddy allocator.
+/// Optionally, callers may add guard regions to block by defining
+/// `PW_ALLOCATOR_POISON_ENABLE`. These guard regions will be set to a known
+/// whenever a block is created and checked when that block is merged. This can
+/// catch heap overflows where consumers write beyond the end of the usable
+/// space.
///
-/// Each block also contains flags for whether it is the last block (i.e.
-/// whether the `Next` pointer points to a valid block, or just denotes the end
-/// of this block), and whether the block is in use. These are encoded into the
-/// last two bits of the `Next` pointer, as follows:
+/// As an example, the diagram below represents two contiguous
+/// `Block<uint32_t, ...>`s with heap poisoning enabled and
+/// `alignof(uint32_t) == 4`. The indices indicate byte offsets.
///
/// @code{.unparsed}
-/// .-----------------------------------------------------------------------.
-/// | Block |
-/// +-----------------------------------------------------------------------+
-/// | Next | Prev | usable space |
-/// +----------------+------+------+ + |
-/// | Ptr[N..2] | Last | Used | | |
-/// +----------------+------+------+------+---------------------------------+
-/// ^
-/// |
-/// '----------- Next() = Next & ~0x3 --------------------------------->
+/// Block 1:
+/// +--------------------------------------+----------------+----------------+
+/// | Header | <Usable space> | Footer |
+/// +----------+----------+----------------+----------------+----------------+
+/// | Prev | Next | | | |
+/// | 0....3 | 4......7 | 8...........15 | 16.........271 | 272........280 |
+/// | 00000000 | 00000046 | kPoisonPattern | <Usable space> | kPoisonPattern |
+/// +----------+----------+----------------+----------------+----------------+
+///
+/// Block 2:
+/// +--------------------------------------+----------------+----------------+
+/// | Header | <Usable space> | Footer |
+/// +----------+----------+----------------+----------------+----------------+
+/// | Prev | Next | | | |
+/// | 0....3 | 4......7 | 8...........15 | 16........1039 | 1040......1056 |
+/// | 00000046 | 00000106 | kPoisonPattern | <Usable space> | kPoisonPattern |
+/// +----------+----------+----------------+----------------+----------------+
/// @endcode
///
-/// The first block in a chain is denoted by a nullptr `Prev` field, and the
-/// last block is denoted by the `Last` bit being set.
+/// The overall size of the block (e.g. 280 bytes) is given by its next offset
+/// multiplied by the alignment (e.g. 0x106 * 4). Also, the next offset of a
+/// block matches the previous offset of its next block. The first block in a
+/// list is denoted by having a previous offset of `0`.
+///
+/// Each block also encodes flags. Builtin flags indicate whether the block is
+/// in use and whether it is the last block in the list. The last block will
+/// still have a next offset that denotes its size.
///
-/// Note, this block class requires that the given block is aligned to an
-/// `alignof(Block*)` boundary. Because of this alignment requirement, each
-/// returned block will also be aligned to an `alignof(Block*)` boundary, and
-/// the size will always be rounded up to a multiple of `alignof(Block*)`.
+/// Depending on `kMaxSize`, some bits of type `T` may not be needed to
+/// encode an offset. Additional bits of both the previous and next offsets may
+/// be used for setting custom flags.
///
-/// This class must be constructed using the static `Init` call.
-class Block final {
+/// For example, for a `Block<uint32_t, 0x10000>`, on a platform where
+/// `alignof(uint32_t) == 4`, the fully encoded bits would be:
+///
+/// @code{.unparsed}
+/// +-------------------------------------------------------------------------+
+/// | block: |
+/// +------------------------------------+------------------------------------+
+/// | .prev_ | .next_: |
+/// +---------------+------+-------------+---------------+------+-------------+
+/// | MSB | | LSB | MSB | | LSB |
+/// | 31.........16 | 15 | 14........0 | 31.........16 | 15 | 14........0 |
+/// | custom_flags1 | used | prev_offset | custom_flags2 | last | next_offset |
+/// +---------------+------+-------------+---------------+------+-------------+
+/// @endcode
+///
+/// @tparam UintType Unsigned integral type used to encode offsets and flags.
+/// @tparam kMaxSize Largest offset that can be addressed by this block. Bits
+/// of `UintType` not needed for offsets are available as
+/// flags.
+template <typename UintType = uintptr_t,
+ size_t kMaxSize = std::numeric_limits<uintptr_t>::max()>
+class Block final : public BaseBlock {
public:
- // No copy/move
- Block(const Block& other) = delete;
- Block& operator=(const Block& other) = delete;
- Block(Block&& other) = delete;
- Block& operator=(Block&& other) = delete;
+ static_assert(std::is_unsigned_v<UintType>);
+ static_assert(kMaxSize <= std::numeric_limits<UintType>::max());
+
+ static constexpr size_t kCapacity = kMaxSize;
+ static constexpr size_t kHeaderSize = sizeof(Block) + kPoisonOffset;
+ static constexpr size_t kFooterSize = kPoisonOffset;
+ static constexpr size_t kBlockOverhead = kHeaderSize + kFooterSize;
+ static constexpr size_t kAlignment = alignof(Block);
/// @brief Creates the first block for a given memory region.
///
/// @pre The start of the given memory region must be aligned to an
- /// `alignof(Block)` boundary.
+ /// `kAlignment` boundary.
///
- /// @returns `INVALID_ARGUMENT` if the given region is unaligned to too small,
- /// or `OK` otherwise.
- static Status Init(const span<std::byte> region, Block** block);
+ /// @retval OK Returns a block representing the region.
+ /// @retval INVALID_ARGUMENT The region is unaligned.
+ /// @retval RESOURCE_EXHAUSTED The region is too small for a block.
+ /// @retval OUT_OF_RANGE The region is larger than `kMaxSize`.
+ static Result<Block*> Init(ByteSpan region);
- /// @returns A pointer to a `Block`, given a pointer to the start of the
- /// usable space inside the block. In other words, this operation is the
- /// opposite of `UsableSpace()`. In reality, this method just subtracts the
- /// appropriate amount from `usable_space` to point to the start of the owning
- /// block.
+ /// @returns A pointer to a `Block`, given a pointer to the start of the
+ /// usable space inside the block.
+ ///
+ /// This is the inverse of `UsableSpace()`.
///
- /// @warning This method does not do any checking; passing a random
- /// pointer will return a non-null pointer.
+ /// @warning This method does not do any checking; passing a random
+ /// pointer will return a non-null pointer.
static Block* FromUsableSpace(std::byte* usable_space) {
- return reinterpret_cast<Block*>(usable_space - sizeof(Block) -
- PW_ALLOCATOR_POISON_OFFSET);
+ // Perform memory laundering to prevent the compiler from tracing the memory
+ // used to store the block and to avoid optimaztions that may be invalidated
+ // by the use of placement-new to create blocks in `Init` and `Split`.
+ return std::launder(reinterpret_cast<Block*>(usable_space - kHeaderSize));
}
/// @returns The total size of the block in bytes, including the header.
- size_t OuterSize() const {
- return reinterpret_cast<intptr_t>(Next()) -
- reinterpret_cast<intptr_t>(this);
- }
+ size_t OuterSize() const { return GetOffset(next_); }
/// @returns The number of usable bytes inside the block.
- size_t InnerSize() const {
- return OuterSize() - sizeof(*this) - 2 * PW_ALLOCATOR_POISON_OFFSET;
- }
+ size_t InnerSize() const { return OuterSize() - kBlockOverhead; }
/// @returns A pointer to the usable space inside this block.
std::byte* UsableSpace() {
- return reinterpret_cast<std::byte*>(this) + sizeof(*this) +
- PW_ALLOCATOR_POISON_OFFSET;
+ // Accessing a dynamic type through a glvalue of std::byte is always well-
+ // defined to allow for object representation.
+ return reinterpret_cast<std::byte*>(this) + kHeaderSize;
}
- /// Split this block, such that this block has an inner size of
- /// `head_block_inner_size`, and return a new block in the remainder of the
- /// space in `new_block`.
- ///
- /// The `remainder` block will be aligned to an `alignof(Block*)` boundary
- /// (and `head_block_inner_size` will be rounded up). If the remaining space
- /// is not large enough to store a new `Block` after rounding, no splitting
- /// will occur.
- ///
- /// @returns One of the the following:
- /// * `OK`: The split completed successfully.
- /// * `INVALID_ARGUMENT`: `new_block` is `null`.
- /// * `FAILED_PRECONDITION`: This block is in use and cannot be split.
- /// * `OUT_OF_RANGE`: The requested size for this block is greater than the
- /// current `inner_size`.
- /// * `RESOURCE_EXHAUSTED`: The split cannot occur because the `remainder`
- /// block
- /// would not be large enough to store a block header.
- Status Split(size_t head_block_inner_size, Block** new_block);
+ /// Splits an aligned block from the start of the block, and marks it as used.
+ ///
+ /// If successful, `block` will be replaced by a block that has an inner
+ /// size of at least `inner_size`, and whose starting address is aligned to an
+ /// `alignment` boundary. If unsuccessful, `block` will be unmodified.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer with a pointer to the new, smaller block. In total, up to two
+ /// additional blocks may be created: one to pad the returned block to an
+ /// alignment boundary and one for the trailing space.
+ ///
+ /// @pre The block must not be in use.
+ ///
+ /// @retval OK The split completed successfully.
+ /// @retval FAILED_PRECONDITION This block is in use and cannot be split.
+ /// @retval OUT_OF_RANGE The requested size plus padding needed for
+ /// alignment is greater than the current size.
+ static Status AllocFirst(Block*& block, size_t inner_size, size_t alignment);
- /// Merges this block with the one that comes after it.
+ /// Splits an aligned block from the end of the block, and marks it as used.
///
- /// @pre The blocks must not be in use.
+ /// If successful, `block` will be replaced by a block that has an inner
+ /// size of at least `inner_size`, and whose starting address is aligned to an
+ /// `alignment` boundary. If unsuccessful, `block` will be unmodified.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer with a pointer to the new, smaller block. An additional block may
+ /// be created for the leading space.
///
- /// @returns One of the following:
- /// * `OK`: The merge was successful.
- /// * `OUT_OF_RANGE`: Attempting to merge the last block failed.
- /// * `FAILED_PRECONDITION`: The blocks could not be merged
- /// because one of them was in use.
- Status MergeNext();
+ /// @pre The block must not be in use.v
+ ///
+ /// @retval OK The split completed successfully.
+ /// @retval FAILED_PRECONDITION This block is in use and cannot be split.
+ /// @retval OUT_OF_RANGE The requested size is greater than the
+ /// current size.
+ /// @retval RESOURCE_EXHAUSTED The remaining space is too small to hold a
+ /// new block.
+ static Status AllocLast(Block*& block, size_t inner_size, size_t alignment);
+
+ /// Marks the block as free and merges it with any free neighbors.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer. If neither member is free, the returned pointer will point to the
+ /// original block. Otherwise, it will point to the new, larger block created
+ /// by merging adjacent free blocks together.
+ static void Free(Block*& block);
+
+ /// Grows or shrinks the block.
+ ///
+ /// If successful, `block` may be merged with the block after it in order to
+ /// provide additional memory (when growing) or to merge released memory (when
+ /// shrinking). If unsuccessful, `block` will be unmodified.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer with a pointer to the new, smaller block.
+ ///
+ /// @pre The block must be in use.
+ ///
+ /// @retval OK The resize completed successfully.
+ /// @retval FAILED_PRECONDITION This block is not in use.
+ /// @retval OUT_OF_RANGE The requested size is greater than the
+ /// available space.
+ static Status Resize(Block*& block, size_t new_inner_size);
- /// Merges this block with the one that comes before it.
+ /// Attempts to split this block.
+ ///
+ /// If successful, the block will have an inner size of `new_inner_size`,
+ /// rounded up to a `kAlignment` boundary. The remaining space will be
+ /// returned as a new block.
+ ///
+ /// This method may fail if the remaining space is too small to hold a new
+ /// block. If this method fails for any reason, the original block is
+ /// unmodified.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer with a pointer to the new, smaller block.
+ ///
+ /// @pre The block must not be in use.
+ ///
+ /// @retval OK The split completed successfully.
+ /// @retval FAILED_PRECONDITION This block is in use and cannot be split.
+ /// @retval OUT_OF_RANGE The requested size for this block is greater
+ /// than the current `inner_size`.
+ /// @retval RESOURCE_EXHAUSTED The remaining space is too small to hold a
+ /// new block.
+ static Result<Block*> Split(Block*& block, size_t new_inner_size);
+
+ /// Merges this block with the one that comes after it.
+ ///
+ /// This method is static in order to consume and replace the given block
+ /// pointer with a pointer to the new, larger block.
///
/// @pre The blocks must not be in use.
///
- /// @warning Merging with a previous block invalidates this block instance.
- /// Do not perform any operations on this instance after merging.
+ /// @retval OK The merge was successful.
+ /// @retval OUT_OF_RANGE The given block is the last block.
+ /// @retval FAILED_PRECONDITION One or more of the blocks is in use.
+ static Status MergeNext(Block*& block);
+
+ /// Fetches the block immediately after this one.
///
- /// @returns One of the following:
- /// * `OK`: The merge was successful.
- /// * `OUT_OF_RANGE`: Attempting to merge the first block failed.
- /// * `FAILED_PRECONDITION`: The blocks could not be merged because
- /// one of them was in use.
- Status MergePrev();
+ /// For performance, this always returns a block pointer, even if the returned
+ /// pointer is invalid. The pointer is valid if and only if `Last()` is false.
+ ///
+ /// Typically, after calling `Init` callers may save a pointer past the end of
+ /// the list using `Next()`. This makes it easy to subsequently iterate over
+ /// the list:
+ /// @code{.cpp}
+ /// auto result = Block<>::Init(byte_span);
+ /// Block<>* begin = *result;
+ /// Block<>* end = begin->Next();
+ /// ...
+ /// for (auto* block = begin; block != end; block = block->Next()) {
+ /// // Do something which each block.
+ /// }
+ /// @endcode
+ Block* Next() const;
+
+ /// @copydoc `Next`.
+ static Block* NextBlock(const Block* block) { return block->Next(); }
+
+ /// @returns The block immediately before this one, or a null pointer if this
+ /// is the first block.
+ Block* Prev() const;
+
+ /// @copydoc `Prev`.
+ static Block* PrevBlock(const Block* block) { return block->Prev(); }
/// Indicates whether the block is in use.
///
/// @returns `true` if the block is in use or `false` if not.
- bool Used() const { return (NextAsUIntPtr() & kInUseFlag) == kInUseFlag; }
+ bool Used() const { return (prev_ & kBuiltinFlag) != 0; }
/// Indicates whether this block is the last block or not (i.e. whether
- /// `NextBlock()` points to a valid block or not). This is needed because
- /// `NextBlock()` points to the end of this block, whether there is a valid
+ /// `Next()` points to a valid block or not). This is needed because
+ /// `Next()` points to the end of this block, whether there is a valid
/// block there or not.
///
/// @returns `true` is this is the last block or `false` if not.
- bool Last() const { return (NextAsUIntPtr() & kLastFlag) == kLastFlag; }
+ bool Last() const { return (next_ & kBuiltinFlag) != 0; }
/// Marks this block as in use.
- void MarkUsed() {
- next_ = reinterpret_cast<Block*>((NextAsUIntPtr() | kInUseFlag));
- }
+ void MarkUsed() { prev_ |= kBuiltinFlag; }
/// Marks this block as free.
- void MarkFree() {
- next_ = reinterpret_cast<Block*>((NextAsUIntPtr() & ~kInUseFlag));
- }
+ void MarkFree() { prev_ &= ~kBuiltinFlag; }
/// Marks this block as the last one in the chain.
- void MarkLast() {
- next_ = reinterpret_cast<Block*>((NextAsUIntPtr() | kLastFlag));
- }
+ void MarkLast() { next_ |= kBuiltinFlag; }
/// Clears the last bit from this block.
- void ClearLast() {
- next_ = reinterpret_cast<Block*>((NextAsUIntPtr() & ~kLastFlag));
- }
+ void ClearLast() { next_ &= ~kBuiltinFlag; }
- /// Fetches the block immediately after this one.
+ /// Sets (and clears) custom flags for this block.
///
- /// @note You should also check `Last()`. `Next()` may return a valid
- /// block, even if one does not exist.
- Block* Next() const {
- return reinterpret_cast<Block*>(
- (NextAsUIntPtr() & ~(kInUseFlag | kLastFlag)));
- }
+ /// The number of bits available for custom flags depends on the capacity of
+ /// the block, and is given by `kCustomFlagBits`. Only this many of the least
+ /// significant bits of `flags_to_set` and `flags_to_clear` are considered;
+ /// any others are ignored. Refer to the class level documentation for the
+ /// exact bit layout.
+ ///
+ /// Custom flags are not copied when a block is split, and are unchanged when
+ /// merging for the block that remains valid after the merge.
+ ///
+ /// If `flags_to_clear` are provided, these bits will be cleared before
+ /// setting the `flags_to_set`. As a consequence, if a bit is set in both
+ /// `flags_to_set` and `flags_to_clear`, it will be set upon return.
+ ///
+ /// @param[in] flags_to_set Bit flags to enable.
+ /// @param[in] flags_to_clear Bit flags to disable.
+ void SetFlags(UintType flags_to_set, UintType flags_to_clear = 0);
- /// @returns The block immediately before this one. Returns a null pointer
- /// if this is the first block.
- Block* Prev() const { return prev_; }
+ /// Returns the custom flags previously set on this block.
+ UintType GetFlags();
/// @brief Checks if a block is valid.
///
- /// @returns `false` if a block is corrupted. Returns `true` if the following
- /// conditions are all true:
- /// * The block is aligned
- /// * The prev/next fields match with the previous and next blocks
- /// * The poisoned bytes are not damaged
- bool IsValid() const { return CheckStatus() == BlockStatus::VALID; }
+ /// @returns `true` if and only if the following conditions are met:
+ /// * The block is aligned.
+ /// * The prev/next fields match with the previous and next blocks.
+ /// * The poisoned bytes are not damaged (if poisoning is enabled).
+ bool IsValid() const { return CheckStatus() == BlockStatus::kValid; }
- /// @brief Crashes if a block is invalid. Uses `PW_DCHECK` to log information
- /// about why the block is invalid. Does nothing if the block is valid.
+ /// @brief Crashes with an informtaional message if a block is invalid.
+ ///
+ /// Does nothing if the block is valid.
void CrashIfInvalid();
private:
- static constexpr uintptr_t kInUseFlag = 0x1;
- static constexpr uintptr_t kLastFlag = 0x2;
- static constexpr std::byte POISON_PATTERN[8] = {std::byte{0x92},
- std::byte{0x88},
- std::byte{0x0a},
- std::byte{0x00},
- std::byte{0xec},
- std::byte{0xdc},
- std::byte{0xae},
- std::byte{0x4e}};
- enum BlockStatus {
- VALID,
- MISALIGNED,
- PREV_MISMATCHED,
- NEXT_MISMATCHED,
- POISON_CORRUPTED
- };
+ static constexpr UintType kMaxOffset = UintType(kMaxSize / kAlignment);
+ static constexpr size_t kCustomFlagBitsPerField =
+ cpp20::countl_zero(kMaxOffset) - 1;
+ static constexpr size_t kCustomFlagBits = kCustomFlagBitsPerField * 2;
+ static constexpr size_t kOffsetBits = cpp20::bit_width(kMaxOffset);
+ static constexpr UintType kBuiltinFlag = UintType(1) << kOffsetBits;
+ static constexpr UintType kOffsetMask = kBuiltinFlag - 1;
+ static constexpr size_t kCustomFlagShift = kOffsetBits + 1;
+ static constexpr UintType kCustomFlagMask = ~(kOffsetMask | kBuiltinFlag);
+
+ Block(size_t prev_offset, size_t next_offset);
- Block() = default;
+ /// Consumes the block and returns as a span of bytes.
+ static ByteSpan AsBytes(Block*&& block);
- // Helper to reduce some of the casting nesting in the block management
- // functions.
- uintptr_t NextAsUIntPtr() const { return reinterpret_cast<uintptr_t>(next_); }
+ /// Consumes the span of bytes and uses it to construct and return a block.
+ static Block* AsBlock(size_t prev_offset, ByteSpan bytes);
- void PoisonBlock();
- bool CheckPoisonBytes() const;
+ /// Returns a `BlockStatus` that is either kValid or indicates the reason why
+ /// the block is invalid.
+ ///
+ /// If the block is invalid at multiple points, this function will only return
+ /// one of the reasons.
BlockStatus CheckStatus() const;
- // Note: Consider instead making these next/prev offsets from the current
- // block, with templated type for the offset size. There are some interesting
- // tradeoffs here; perhaps a pool of small allocations could use 1-byte
- // next/prev offsets to reduce size further.
- Block* next_;
- Block* prev_;
+ /// Extracts the offset portion from `next_` or `prev_`.
+ static size_t GetOffset(UintType packed) {
+ return static_cast<size_t>(packed & kOffsetMask) * kAlignment;
+ }
+
+ /// Overwrites the offset portion of `next_` or `prev_`.
+ static void SetOffset(UintType& field, size_t offset) {
+ field = (field & ~kOffsetMask) | static_cast<UintType>(offset) / kAlignment;
+ }
+
+ UintType next_ = 0;
+ UintType prev_ = 0;
+
+ public:
+ // Associated types.
+
+ /// Represents an iterator that moves forward through a list of blocks.
+ ///
+ /// This class is not typically instantiated directly, but rather using a
+ /// range-based for-loop using `Block::Range`.
+ class Iterator : public BaseIterator<Block, NextBlock> {
+ public:
+ Iterator(Block* block) : BaseIterator<Block, NextBlock>(block) {}
+ };
+
+ /// Represents an iterator that moves forward through a list of blocks.
+ ///
+ /// This class is not typically instantiated directly, but rather using a
+ /// range-based for-loop using `Block::ReverseRange`.
+ class ReverseIterator : public BaseIterator<Block, PrevBlock> {
+ public:
+ ReverseIterator(Block* block) : BaseIterator<Block, PrevBlock>(block) {}
+ };
+
+ /// Represents a range of blocks that can be iterated over.
+ ///
+ /// The typical usage of this class is in a range-based for-loop, e.g.
+ /// @code{.cpp}
+ /// for (auto* block : Range(first, last)) { ... }
+ /// @endcode
+ class Range : public BaseRange<Block, Iterator> {
+ public:
+ /// Constructs a range including `begin` and all valid following blocks.
+ explicit Range(Block* begin) : BaseRange<Block, Iterator>(begin, nullptr) {}
+
+ /// Constructs a range of blocks from `begin` to `end`, inclusively.
+ Range(Block* begin_inclusive, Block* end_inclusive)
+ : BaseRange<Block, Iterator>(begin_inclusive, end_inclusive->Next()) {}
+ };
+
+ /// Represents a range of blocks that can be iterated over in the reverse
+ /// direction.
+ ///
+ /// The typical usage of this class is in a range-based for-loop, e.g.
+ /// @code{.cpp}
+ /// for (auto* block : ReverseRange(last, first)) { ... }
+ /// @endcode
+ class ReverseRange : public BaseRange<Block, ReverseIterator> {
+ public:
+ /// Constructs a range including `rbegin` and all valid preceding blocks.
+ explicit ReverseRange(Block* rbegin)
+ : BaseRange<Block, ReverseIterator>(rbegin, nullptr) {}
+
+ /// Constructs a range of blocks from `rbegin` to `rend`, inclusively.
+ ReverseRange(Block* rbegin_inclusive, Block* rend_inclusive)
+ : BaseRange<Block, ReverseIterator>(rbegin_inclusive,
+ rend_inclusive->Prev()) {}
+ };
};
+// Public template method implementations.
+
+template <typename UintType, size_t kMaxSize>
+Result<Block<UintType, kMaxSize>*> Block<UintType, kMaxSize>::Init(
+ ByteSpan region) {
+ if (reinterpret_cast<uintptr_t>(region.data()) % kAlignment != 0) {
+ return Status::InvalidArgument();
+ }
+ if (region.size() < kBlockOverhead) {
+ return Status::ResourceExhausted();
+ }
+ if (kMaxSize < region.size()) {
+ return Status::OutOfRange();
+ }
+ Block* block = AsBlock(0, region);
+ block->MarkLast();
+ BaseBlock::Poison(block, kHeaderSize, block->OuterSize());
+ return block;
+}
+
+template <typename UintType, size_t kMaxSize>
+Status Block<UintType, kMaxSize>::AllocFirst(Block*& block,
+ size_t inner_size,
+ size_t alignment) {
+ if (block->Used()) {
+ return Status::FailedPrecondition();
+ }
+ // Check if padding will be needed at the front to align the usable space.
+ size_t pad_outer_size = 0;
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ if (addr % alignment != 0) {
+ pad_outer_size = AlignUp(addr + kBlockOverhead, alignment) - addr;
+ inner_size += pad_outer_size;
+ }
+
+ // Split the block to get the requested usable space. It is not an error if
+ // the block is too small to split off a new trailing block.
+ Result<Block*> result = Block::Split(block, inner_size);
+ if (!result.ok() && result.status() != Status::ResourceExhausted()) {
+ return result.status();
+ }
+
+ // If present, split the padding off the front. Since this space was included
+ // in the previous split, it should always succeed.
+ if (pad_outer_size != 0) {
+ result = Block::Split(block, pad_outer_size - kBlockOverhead);
+ block = *result;
+ }
+
+ block->MarkUsed();
+ return OkStatus();
+}
+
+template <typename UintType, size_t kMaxSize>
+Status Block<UintType, kMaxSize>::AllocLast(Block*& block,
+ size_t inner_size,
+ size_t alignment) {
+ if (block->Used()) {
+ return Status::FailedPrecondition();
+ }
+ // Find the last address that is aligned and is followed by enough space for
+ // block overhead and the requested size.
+ if (block->InnerSize() < inner_size) {
+ return Status::OutOfRange();
+ }
+ alignment = std::max(alignment, kAlignment);
+ auto addr = reinterpret_cast<uintptr_t>(block->UsableSpace());
+ uintptr_t next =
+ AlignDown(addr + (block->InnerSize() - inner_size), alignment);
+ if (next != addr) {
+ if (next < addr + kBlockOverhead) {
+ // A split is needed, but no block will fit.
+ return Status::ResourceExhausted();
+ }
+ size_t pad_inner_size = next - (addr + kBlockOverhead);
+ Result<Block*> result = Block::Split(block, pad_inner_size);
+ if (!result.ok()) {
+ return result.status();
+ }
+ block = *result;
+ }
+ block->MarkUsed();
+ return OkStatus();
+}
+
+template <typename UintType, size_t kMaxSize>
+void Block<UintType, kMaxSize>::Free(Block*& block) {
+ block->MarkFree();
+ Block* prev = block->Prev();
+ if (Block::MergeNext(prev).ok()) {
+ block = prev;
+ }
+ Block::MergeNext(block).IgnoreError();
+}
+
+template <typename UintType, size_t kMaxSize>
+Status Block<UintType, kMaxSize>::Resize(Block*& block, size_t new_inner_size) {
+ if (!block->Used()) {
+ return Status::FailedPrecondition();
+ }
+ size_t old_inner_size = block->InnerSize();
+ size_t aligned_inner_size = AlignUp(new_inner_size, kAlignment);
+ if (old_inner_size == aligned_inner_size) {
+ return OkStatus();
+ }
+
+ // Treat the block as free and try to combine it with the next block. At most
+ // one free block is expecte to follow this block.
+ block->MarkFree();
+ MergeNext(block).IgnoreError();
+
+ // Try to split off a block of the requested size.
+ Status status = Block::Split(block, aligned_inner_size).status();
+
+ // It is not an error if the split fails because the remainder is too small
+ // for a block.
+ if (status == Status::ResourceExhausted()) {
+ status = OkStatus();
+ }
+
+ // Otherwise, restore the original block on failure.
+ if (!status.ok()) {
+ Split(block, old_inner_size).IgnoreError();
+ }
+ block->MarkUsed();
+ return status;
+}
+
+template <typename UintType, size_t kMaxSize>
+Result<Block<UintType, kMaxSize>*> Block<UintType, kMaxSize>::Split(
+ Block*& block, size_t new_inner_size) {
+ if (block->Used()) {
+ return Status::FailedPrecondition();
+ }
+ size_t old_inner_size = block->InnerSize();
+ size_t aligned_inner_size = AlignUp(new_inner_size, kAlignment);
+ if (old_inner_size < new_inner_size || old_inner_size < aligned_inner_size) {
+ return Status::OutOfRange();
+ }
+ if (old_inner_size - aligned_inner_size < kBlockOverhead) {
+ return Status::ResourceExhausted();
+ }
+ size_t prev_offset = GetOffset(block->prev_);
+ size_t outer_size1 = aligned_inner_size + kBlockOverhead;
+ bool is_last = block->Last();
+ UintType flags = block->GetFlags();
+ ByteSpan bytes = AsBytes(std::move(block));
+ Block* block1 = AsBlock(prev_offset, bytes.subspan(0, outer_size1));
+ Block* block2 = AsBlock(outer_size1, bytes.subspan(outer_size1));
+ size_t outer_size2 = block2->OuterSize();
+ if (is_last) {
+ block2->MarkLast();
+ } else {
+ SetOffset(block2->Next()->prev_, outer_size2);
+ }
+ block1->SetFlags(flags);
+ BaseBlock::Poison(block1, kHeaderSize, outer_size1);
+ BaseBlock::Poison(block2, kHeaderSize, outer_size2);
+ block = std::move(block1);
+ return block2;
+}
+
+template <typename UintType, size_t kMaxSize>
+Status Block<UintType, kMaxSize>::MergeNext(Block*& block) {
+ if (block == nullptr || block->Last()) {
+ return Status::OutOfRange();
+ }
+ Block* next = block->Next();
+ if (block->Used() || next->Used()) {
+ return Status::FailedPrecondition();
+ }
+ size_t prev_offset = GetOffset(block->prev_);
+ bool is_last = next->Last();
+ UintType flags = block->GetFlags();
+ ByteSpan prev_bytes = AsBytes(std::move(block));
+ ByteSpan next_bytes = AsBytes(std::move(next));
+ size_t next_offset = prev_bytes.size() + next_bytes.size();
+ std::byte* merged = ::new (prev_bytes.data()) std::byte[next_offset];
+ block = AsBlock(prev_offset, ByteSpan(merged, next_offset));
+ if (is_last) {
+ block->MarkLast();
+ } else {
+ SetOffset(block->Next()->prev_, GetOffset(block->next_));
+ }
+ block->SetFlags(flags);
+ return OkStatus();
+}
+
+template <typename UintType, size_t kMaxSize>
+Block<UintType, kMaxSize>* Block<UintType, kMaxSize>::Next() const {
+ size_t offset = GetOffset(next_);
+ uintptr_t addr = Last() ? 0 : reinterpret_cast<uintptr_t>(this) + offset;
+ // See the note in `FromUsableSpace` about memory laundering.
+ return std::launder(reinterpret_cast<Block*>(addr));
+}
+
+template <typename UintType, size_t kMaxSize>
+Block<UintType, kMaxSize>* Block<UintType, kMaxSize>::Prev() const {
+ size_t offset = GetOffset(prev_);
+ uintptr_t addr =
+ (offset == 0) ? 0 : reinterpret_cast<uintptr_t>(this) - offset;
+ // See the note in `FromUsableSpace` about memory laundering.
+ return std::launder(reinterpret_cast<Block*>(addr));
+}
+
+template <typename UintType, size_t kMaxSize>
+void Block<UintType, kMaxSize>::SetFlags(UintType flags_to_set,
+ UintType flags_to_clear) {
+ UintType hi_flags_to_set = flags_to_set >> kCustomFlagBitsPerField;
+ hi_flags_to_set <<= kCustomFlagShift;
+ UintType hi_flags_to_clear = (flags_to_clear >> kCustomFlagBitsPerField)
+ << kCustomFlagShift;
+ UintType lo_flags_to_set =
+ (flags_to_set & ((UintType(1) << kCustomFlagBitsPerField) - 1))
+ << kCustomFlagShift;
+ UintType lo_flags_to_clear =
+ (flags_to_clear & ((UintType(1) << kCustomFlagBitsPerField) - 1))
+ << kCustomFlagShift;
+ prev_ = (prev_ & ~hi_flags_to_clear) | hi_flags_to_set;
+ next_ = (next_ & ~lo_flags_to_clear) | lo_flags_to_set;
+}
+
+template <typename UintType, size_t kMaxSize>
+UintType Block<UintType, kMaxSize>::GetFlags() {
+ UintType hi_flags = (prev_ & kCustomFlagMask) >> kCustomFlagShift;
+ UintType lo_flags = (next_ & kCustomFlagMask) >> kCustomFlagShift;
+ return (hi_flags << kCustomFlagBitsPerField) | lo_flags;
+}
+
+// Private template method implementations.
+
+template <typename UintType, size_t kMaxSize>
+Block<UintType, kMaxSize>::Block(size_t prev_offset, size_t next_offset)
+ : BaseBlock() {
+ SetOffset(prev_, prev_offset);
+ SetOffset(next_, next_offset);
+}
+
+template <typename UintType, size_t kMaxSize>
+ByteSpan Block<UintType, kMaxSize>::AsBytes(Block*&& block) {
+ size_t block_size = block->OuterSize();
+ std::byte* bytes = ::new (std::move(block)) std::byte[block_size];
+ return {bytes, block_size};
+}
+
+template <typename UintType, size_t kMaxSize>
+Block<UintType, kMaxSize>* Block<UintType, kMaxSize>::AsBlock(
+ size_t prev_offset, ByteSpan bytes) {
+ return ::new (bytes.data()) Block(prev_offset, bytes.size());
+}
+
+template <typename UintType, size_t kMaxSize>
+typename Block<UintType, kMaxSize>::BlockStatus
+Block<UintType, kMaxSize>::CheckStatus() const {
+ // Make sure the Block is aligned.
+ if (reinterpret_cast<uintptr_t>(this) % kAlignment != 0) {
+ return BlockStatus::kMisaligned;
+ }
+
+ // Test if the prev/next pointer for this Block matches.
+ if (!Last() && (this >= Next() || this != Next()->Prev())) {
+ return BlockStatus::kNextMismatched;
+ }
+
+ if (Prev() && (this <= Prev() || this != Prev()->Next())) {
+ return BlockStatus::kPrevMismatched;
+ }
+
+ if (!CheckPoison(this, kHeaderSize, OuterSize())) {
+ return BlockStatus::kPoisonCorrupted;
+ }
+
+ return BlockStatus::kValid;
+}
+
+template <typename UintType, size_t kMaxSize>
+void Block<UintType, kMaxSize>::CrashIfInvalid() {
+ uintptr_t addr = reinterpret_cast<uintptr_t>(this);
+ switch (CheckStatus()) {
+ case kValid:
+ break;
+ case kMisaligned:
+ CrashMisaligned(addr);
+ break;
+ case kNextMismatched:
+ CrashNextMismatched(addr, reinterpret_cast<uintptr_t>(Next()->Prev()));
+ break;
+ case kPrevMismatched:
+ CrashPrevMismatched(addr, reinterpret_cast<uintptr_t>(Prev()->Next()));
+ break;
+ case kPoisonCorrupted:
+ CrashPoisonCorrupted(addr);
+ break;
+ }
+}
+
} // namespace pw::allocator
diff --git a/pw_allocator/public/pw_allocator/fallback_allocator.h b/pw_allocator/public/pw_allocator/fallback_allocator.h
index 61c20fd2b..5707c1aee 100644
--- a/pw_allocator/public/pw_allocator/fallback_allocator.h
+++ b/pw_allocator/public/pw_allocator/fallback_allocator.h
@@ -33,19 +33,16 @@ class FallbackAllocator : public Allocator {
private:
/// @copydoc Allocator::Query
- Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
+ Status DoQuery(const void* ptr, Layout layout) const override;
/// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
+ void* DoAllocate(Layout layout) override;
/// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
+ void DoDeallocate(void* ptr, Layout layout) override;
/// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t alignment,
- size_t new_size) override;
+ bool DoResize(void* ptr, Layout layout, size_t new_size) override;
Allocator* primary_ = nullptr;
Allocator* secondary_ = nullptr;
diff --git a/pw_allocator/public/pw_allocator/freelist_heap.h b/pw_allocator/public/pw_allocator/freelist_heap.h
index 673302627..5c6b12b74 100644
--- a/pw_allocator/public/pw_allocator/freelist_heap.h
+++ b/pw_allocator/public/pw_allocator/freelist_heap.h
@@ -24,6 +24,8 @@ namespace pw::allocator {
class FreeListHeap {
public:
+ using BlockType = Block<>;
+
template <size_t kNumBuckets>
friend class FreeListHeapBuffer;
struct HeapStats {
@@ -44,7 +46,7 @@ class FreeListHeap {
void LogHeapStats();
private:
- span<std::byte> BlockToSpan(Block* block) {
+ span<std::byte> BlockToSpan(BlockType* block) {
return span<std::byte>(block->UsableSpace(), block->InnerSize());
}
diff --git a/pw_allocator/public/pw_allocator/libc_allocator.h b/pw_allocator/public/pw_allocator/libc_allocator.h
index 9f2fcd6da..70f67f286 100644
--- a/pw_allocator/public/pw_allocator/libc_allocator.h
+++ b/pw_allocator/public/pw_allocator/libc_allocator.h
@@ -27,22 +27,16 @@ class LibCAllocator : public Allocator {
private:
/// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
+ void* DoAllocate(Layout layout) override;
/// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
+ void DoDeallocate(void* ptr, Layout layout) override;
/// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
+ bool DoResize(void* ptr, Layout layout, size_t new_size) override;
/// @copydoc Allocator::Reallocate
- void* DoReallocate(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
+ void* DoReallocate(void* ptr, Layout layout, size_t new_size) override;
};
} // namespace pw::allocator
diff --git a/pw_allocator/public/pw_allocator/null_allocator.h b/pw_allocator/public/pw_allocator/null_allocator.h
index 55a7f62f4..b4c3ba6aa 100644
--- a/pw_allocator/public/pw_allocator/null_allocator.h
+++ b/pw_allocator/public/pw_allocator/null_allocator.h
@@ -29,13 +29,13 @@ class NullAllocator : public Allocator {
private:
/// @copydoc Allocator::Allocate
- void* DoAllocate(size_t, size_t) override { return nullptr; }
+ void* DoAllocate(Layout) override { return nullptr; }
/// @copydoc Allocator::Deallocate
- void DoDeallocate(void*, size_t, size_t) override {}
+ void DoDeallocate(void*, Layout) override {}
/// @copydoc Allocator::Resize
- bool DoResize(void*, size_t, size_t, size_t) override { return false; }
+ bool DoResize(void*, Layout, size_t) override { return false; }
};
} // namespace pw::allocator
diff --git a/pw_allocator/public/pw_allocator/simple_allocator.h b/pw_allocator/public/pw_allocator/simple_allocator.h
new file mode 100644
index 000000000..73662d452
--- /dev/null
+++ b/pw_allocator/public/pw_allocator/simple_allocator.h
@@ -0,0 +1,87 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/block.h"
+
+namespace pw::allocator {
+
+// DOCSTAG: [pw_allocator_examples_simple_allocator]
+/// Simple allocator that uses a list of `Block`s.
+class SimpleAllocator : public Allocator {
+ public:
+ using Block = pw::allocator::Block<>;
+ using Range = typename Block::Range;
+
+ constexpr SimpleAllocator() = default;
+
+ /// Initialize this allocator to allocate memory from `region`.
+ Status Init(ByteSpan region) {
+ auto result = Block::Init(region);
+ if (result.ok()) {
+ blocks_ = *result;
+ }
+ return result.status();
+ }
+
+ /// Return the range of blocks for this allocator.
+ Range blocks() { return Range(blocks_); }
+
+ private:
+ /// @copydoc Allocator::Query
+ Status DoQuery(const void* ptr, Layout) const override {
+ for (auto* block : Range(blocks_)) {
+ if (block->UsableSpace() == ptr) {
+ return OkStatus();
+ }
+ }
+ return Status::OutOfRange();
+ }
+
+ /// @copydoc Allocator::Allocate
+ void* DoAllocate(Layout layout) override {
+ for (auto* block : Range(blocks_)) {
+ if (Block::AllocFirst(block, layout.size(), layout.alignment()).ok()) {
+ return block->UsableSpace();
+ }
+ }
+ return nullptr;
+ }
+
+ /// @copydoc Allocator::Deallocate
+ void DoDeallocate(void* ptr, Layout) override {
+ if (ptr == nullptr) {
+ return;
+ }
+ auto* bytes = static_cast<std::byte*>(ptr);
+ Block* block = Block::FromUsableSpace(bytes);
+ Block::Free(block);
+ }
+
+ /// @copydoc Allocator::Resize
+ bool DoResize(void* ptr, Layout, size_t new_size) override {
+ if (ptr == nullptr) {
+ return false;
+ }
+ auto* bytes = static_cast<std::byte*>(ptr);
+ Block* block = Block::FromUsableSpace(bytes);
+ return Block::Resize(block, new_size).ok();
+ }
+
+ Block* blocks_ = nullptr;
+};
+// DOCSTAG: [pw_allocator_examples_simple_allocator]
+
+} // namespace pw::allocator
diff --git a/pw_allocator/public/pw_allocator/split_free_list_allocator.h b/pw_allocator/public/pw_allocator/split_free_list_allocator.h
index 5cd21d51a..46fd9d971 100644
--- a/pw_allocator/public/pw_allocator/split_free_list_allocator.h
+++ b/pw_allocator/public/pw_allocator/split_free_list_allocator.h
@@ -13,15 +13,41 @@
// the License.
#pragma once
+#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <optional>
#include "pw_allocator/allocator.h"
+#include "pw_allocator/block.h"
+#include "pw_bytes/alignment.h"
+#include "pw_bytes/span.h"
+#include "pw_result/result.h"
#include "pw_status/status.h"
namespace pw::allocator {
+/// Block-independent base class of SplitFreeListAllocator.
+///
+/// This class contains static methods which do not depend on the template
+/// parameters of ``SplitFreeListAllocator`` that are used to determine block
+/// type. This allows the methods to be defined in a separate source file and
+/// use macros that cannot be used in headers, e.g. PW_CHECK.
+///
+/// This class should not be used directly. Instead, see
+/// ``SplitFreeListAllocator``.
+class BaseSplitFreeListAllocator : public Allocator {
+ protected:
+ constexpr BaseSplitFreeListAllocator() = default;
+
+ /// Crashes with an informational method that the given block is allocated.
+ ///
+ /// This method is meant to be called by ``SplitFreeListAllocator``s
+ /// destructor. There must not be any outstanding allocations from an
+ /// when it is destroyed.
+ static void CrashOnAllocated(void* allocated);
+};
+
/// This memory allocator uses a free list to track unallocated blocks, with a
/// twist: Allocations above or below a given threshold are taken from
/// respectively lower or higher addresses from within the allocator's memory
@@ -32,17 +58,10 @@ namespace pw::allocator {
/// another allocator. If this is done, the `Query` method will incorrectly
/// think pointers returned by that alloator were created by this one, and
/// report that this allocator can de/reallocate them.
-class SplitFreeListAllocator : public Allocator {
+template <typename BlockType = Block<>>
+class SplitFreeListAllocator : public BaseSplitFreeListAllocator {
public:
- /// Free memory blocks are tracked using a singly linked list. The free memory
- /// itself is used to for these structs, so the minimum size and alignment
- /// supported by this allocator is `sizeof(FreeBlock)`.
- ///
- /// Allocator callers should not need to access this type directly.
- struct FreeBlock {
- FreeBlock* next;
- size_t size;
- };
+ using Range = typename BlockType::Range;
constexpr SplitFreeListAllocator() = default;
~SplitFreeListAllocator() override;
@@ -51,45 +70,219 @@ class SplitFreeListAllocator : public Allocator {
SplitFreeListAllocator(const SplitFreeListAllocator&) = delete;
SplitFreeListAllocator& operator=(const SplitFreeListAllocator&) = delete;
- uintptr_t addr() const { return addr_; }
- uintptr_t size() const { return size_; }
-
/// Sets the memory region to be used by this allocator, and the threshold at
/// which allocations are considerd "large" or "small". Large and small
/// allocations return lower and higher addresses, respectively.
///
- /// @param[in] base Start of the memory region for this allocator.
- /// @param[in] size Length of the memory region for this allocator.
- /// @param[in] threshold Allocations of this size of larger are considered
- /// "large" and come from lower addresses.
- void Initialize(void* base, size_t size, size_t threshold);
+ /// @param[in] region The memory region for this allocator.
+ /// @param[in] threshold Allocations of this size of larger are
+ /// "large" and come from lower addresses.
+ /// @retval OK The allocator is initialized.
+ /// @retval INVALID_ARGUMENT The memory region is null.
+ /// @retval RESOURCE_EXHAUSTED The region is too small for `BlockType`.
+ /// @retval OUT_OF_RANGE The region too large for `BlockType`.
+ Status Init(ByteSpan region, size_t threshold);
- private:
- /// Adds the given block to the free list. The block must not be null.
- void AddBlock(FreeBlock* block);
+ /// Returns an iterable range of blocks tracking the memory of this allocator.
+ Range blocks() const;
- /// Removes the given block from the free list. The block must not be null.
- FreeBlock* RemoveBlock(FreeBlock* prev, FreeBlock* block);
+ private:
+ using ReverseRange = typename BlockType::ReverseRange;
/// @copydoc Allocator::Dispatch
- Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
+ Status DoQuery(const void* ptr, Layout layout) const override;
/// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
+ void* DoAllocate(Layout layout) override;
+
+ /// Allocate a large chunk of memory.
+ ///
+ /// Allocations larger than the threshold will be allocated from lower
+ /// addresses. If a block needs to be fragmented, the returned pointer will be
+ /// from the lower-addressed part of the block.
+ ///
+ /// @param[in] layout Describes the memory to be allocated.
+ void* DoAllocateLarge(Layout layout);
+
+ /// Allocate a small chunk of memory.
+ ///
+ /// Allocations smaller than the threshold will be allocated from higher
+ /// addresses. If a block needs to be fragmented, the returned pointer will be
+ /// from the higher-addressed part of the block.
+ ///
+ /// @param[in] layout Describes the memory to be allocated.
+ void* DoAllocateSmall(Layout layout);
/// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
+ void DoDeallocate(void* ptr, Layout layout) override;
/// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
-
- uintptr_t addr_ = 0;
- size_t size_ = 0;
- FreeBlock* head_ = nullptr;
+ bool DoResize(void* ptr, Layout layout, size_t new_size) override;
+
+ // Represents the entire memory region for this allocator.
+ void* begin_ = nullptr;
+ void* end_ = nullptr;
+
+ // Represents the range of blocks that include free blocks. Blocks outside
+ // this range are guaranteed to be in use. These are effectively cached values
+ // used to speed up searching for free blocks.
+ BlockType* first_free_ = nullptr;
+ BlockType* last_free_ = nullptr;
+
+ // The boundary between what are consider "small" and "large" allocations.
size_t threshold_ = 0;
};
+// Template method implementations
+
+template <typename BlockType>
+SplitFreeListAllocator<BlockType>::~SplitFreeListAllocator() {
+ auto* begin = BlockType::FromUsableSpace(static_cast<std::byte*>(begin_));
+ for (auto* block : Range(begin)) {
+ if (block->Used()) {
+ CrashOnAllocated(block);
+ }
+ }
+}
+
+template <typename BlockType>
+typename BlockType::Range SplitFreeListAllocator<BlockType>::blocks() const {
+ auto* begin = BlockType::FromUsableSpace(static_cast<std::byte*>(begin_));
+ return Range(begin);
+}
+
+template <typename BlockType>
+Status SplitFreeListAllocator<BlockType>::Init(ByteSpan region,
+ size_t threshold) {
+ if (region.data() == nullptr) {
+ return Status::InvalidArgument();
+ }
+ if (BlockType::kCapacity < region.size()) {
+ return Status::OutOfRange();
+ }
+
+ // Blocks need to be aligned. Find the first aligned address, and use as much
+ // of the memory region as possible.
+ auto addr = reinterpret_cast<uintptr_t>(region.data());
+ auto aligned = AlignUp(addr, BlockType::kAlignment);
+ Result<BlockType*> result = BlockType::Init(region.subspan(aligned - addr));
+ if (!result.ok()) {
+ return result.status();
+ }
+
+ // Initially, the block list is a single free block.
+ BlockType* block = *result;
+ begin_ = block->UsableSpace();
+ end_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(begin_) +
+ block->InnerSize());
+ first_free_ = block;
+ last_free_ = block;
+
+ threshold_ = threshold;
+ return OkStatus();
+}
+
+template <typename BlockType>
+Status SplitFreeListAllocator<BlockType>::DoQuery(const void* ptr,
+ Layout) const {
+ return (ptr < begin_ || end_ <= ptr) ? Status::OutOfRange() : OkStatus();
+}
+
+template <typename BlockType>
+void* SplitFreeListAllocator<BlockType>::DoAllocate(Layout layout) {
+ if (begin_ == nullptr || layout.size() == 0) {
+ return nullptr;
+ }
+ size_t size = layout.size();
+ size_t alignment = std::max(layout.alignment(), BlockType::kAlignment);
+ layout = Layout(size, alignment);
+ return size < threshold_ ? DoAllocateSmall(layout) : DoAllocateLarge(layout);
+}
+
+template <typename BlockType>
+void* SplitFreeListAllocator<BlockType>::DoAllocateSmall(Layout layout) {
+ // Update the cached last free block.
+ while (last_free_->Used() && first_free_ != last_free_) {
+ last_free_ = last_free_->Prev();
+ }
+ // Search backwards for the first block that can hold this allocation.
+ for (auto* block : ReverseRange(last_free_, first_free_)) {
+ if (BlockType::AllocLast(block, layout.size(), layout.alignment()).ok()) {
+ return block->UsableSpace();
+ }
+ }
+ // No valid block found.
+ return nullptr;
+}
+
+template <typename BlockType>
+void* SplitFreeListAllocator<BlockType>::DoAllocateLarge(Layout layout) {
+ // Update the cached first free block.
+ while (first_free_->Used() && first_free_ != last_free_) {
+ first_free_ = first_free_->Next();
+ }
+ // Search forwards for the first block that can hold this allocation.
+ for (auto* block : Range(first_free_, last_free_)) {
+ if (BlockType::AllocFirst(block, layout.size(), layout.alignment()).ok()) {
+ // A new last free block may be split off the end of the allocated block.
+ if (last_free_ <= block) {
+ last_free_ = block->Last() ? block : block->Next();
+ }
+ return block->UsableSpace();
+ }
+ }
+ // No valid block found.
+ return nullptr;
+}
+
+template <typename BlockType>
+void SplitFreeListAllocator<BlockType>::DoDeallocate(void* ptr, Layout) {
+ // Do nothing if uninitialized or no memory block pointer.
+ if (begin_ == nullptr || ptr < begin_ || end_ <= ptr) {
+ return;
+ }
+ auto* block = BlockType::FromUsableSpace(static_cast<std::byte*>(ptr));
+ block->CrashIfInvalid();
+
+ // Free the block and merge it with its neighbors, if possible.
+ BlockType::Free(block);
+
+ // Update the first and/or last free block pointers.
+ if (block < first_free_) {
+ first_free_ = block;
+ }
+ if (block->Last() || last_free_ < block->Next()) {
+ last_free_ = block;
+ }
+}
+
+template <typename BlockType>
+bool SplitFreeListAllocator<BlockType>::DoResize(void* ptr,
+ Layout layout,
+ size_t new_size) {
+ // Fail to resize is uninitialized or invalid parameters.
+ if (begin_ == nullptr || !DoQuery(ptr, layout).ok()) {
+ return false;
+ }
+
+ // Ensure that this allocation came from this object.
+ auto* block = BlockType::FromUsableSpace(static_cast<std::byte*>(ptr));
+ block->CrashIfInvalid();
+
+ bool next_is_first_free = !block->Last() && block->Next() == first_free_;
+ bool next_is_last_free = !block->Last() && block->Next() == last_free_;
+ if (!BlockType::Resize(block, new_size).ok()) {
+ return false;
+ }
+
+ // The block after this one may have grown or shrunk.
+ if (next_is_first_free) {
+ first_free_ = block->Last() ? block : block->Next();
+ }
+ if (next_is_last_free) {
+ last_free_ = block->Last() ? block : block->Next();
+ }
+ return true;
+}
+
} // namespace pw::allocator
diff --git a/pw_allocator/pw_allocator_private/allocator_testing.h b/pw_allocator/pw_allocator_private/allocator_testing.h
deleted file mode 100644
index 03bf07365..000000000
--- a/pw_allocator/pw_allocator_private/allocator_testing.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2023 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-#pragma once
-
-#include <array>
-#include <cstddef>
-
-#include "pw_allocator/allocator.h"
-#include "pw_allocator/block.h"
-#include "pw_bytes/span.h"
-
-namespace pw::allocator::test {
-
-/// Fake memory allocator for testing.
-///
-/// This allocator can return a fixed number of allocations made using an
-/// internal buffer. It records the most recent parameters passed to the
-/// `Allocator` interface methods, and returns them via accessors.
-class FakeAllocator : public Allocator {
- public:
- constexpr FakeAllocator() = default;
-
- size_t allocate_size() const { return allocate_size_; }
- void* deallocate_ptr() const { return deallocate_ptr_; }
- size_t deallocate_size() const { return deallocate_size_; }
- void* resize_ptr() const { return resize_ptr_; }
- size_t resize_old_size() const { return resize_old_size_; }
- size_t resize_new_size() const { return resize_new_size_; }
-
- /// Provides memory for the allocator to allocate from.
- Status Initialize(ByteSpan buffer);
-
- /// Allocates all the memory from this object.
- void Exhaust();
-
- /// Resets the recorded parameters to an initial state.
- void ResetParameters();
-
- private:
- /// @copydoc Allocator::Query
- Status DoQuery(const void* ptr, size_t size, size_t alignment) const override;
-
- /// @copydoc Allocator::Allocate
- void* DoAllocate(size_t size, size_t alignment) override;
-
- /// @copydoc Allocator::Deallocate
- void DoDeallocate(void* ptr, size_t size, size_t alignment) override;
-
- /// @copydoc Allocator::Resize
- bool DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) override;
-
- Block* head_ = nullptr;
- size_t allocate_size_ = 0;
- void* deallocate_ptr_ = nullptr;
- size_t deallocate_size_ = 0;
- void* resize_ptr_ = nullptr;
- size_t resize_old_size_ = 0;
- size_t resize_new_size_ = 0;
-};
-
-} // namespace pw::allocator::test
diff --git a/pw_allocator/simple_allocator_test.cc b/pw_allocator/simple_allocator_test.cc
new file mode 100644
index 000000000..4904bfe3b
--- /dev/null
+++ b/pw_allocator/simple_allocator_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_allocator/simple_allocator.h"
+
+#include "gtest/gtest.h"
+#include "pw_allocator/allocator_testing.h"
+
+namespace pw::allocator {
+
+// Size of the memory region to use in the tests below.
+constexpr size_t kCapacity = 256;
+
+// An `SimpleAllocator` that is automatically initialized on construction.
+class SimpleAllocatorWithBuffer
+ : public test::WithBuffer<SimpleAllocator, kCapacity> {
+ public:
+ SimpleAllocatorWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size())), OkStatus());
+ }
+};
+
+// This is not meant to be a rigorous unit test of individual behaviors, as much
+// as simply a way to demonstrate and exercise the simple allocator.
+TEST(SimpleAllocatorTest, AllocateResizeDeallocate) {
+ SimpleAllocatorWithBuffer allocator;
+
+ // Can allocate usable memory.
+ constexpr size_t kSize1 = kCapacity / 4;
+ constexpr Layout layout1 = Layout::Of<std::byte[kSize1]>();
+ auto* ptr = static_cast<std::byte*>(allocator->Allocate(layout1));
+ ASSERT_NE(ptr, nullptr);
+ memset(ptr, 0x5A, kSize1);
+
+ // Can shrink memory. Contents are preserved.
+ constexpr size_t kSize2 = kCapacity / 8;
+ constexpr Layout layout2 = Layout::Of<std::byte[kSize2]>();
+ EXPECT_TRUE(allocator->Resize(ptr, layout1, layout2.size()));
+ for (size_t i = 0; i < kSize2; ++i) {
+ EXPECT_EQ(size_t(ptr[i]), 0x5Au);
+ }
+
+ // Can grow memory. Contents are preserved.
+ constexpr size_t kSize3 = kCapacity / 2;
+ constexpr Layout layout3 = Layout::Of<std::byte[kSize3]>();
+ EXPECT_TRUE(allocator->Resize(ptr, layout2, layout3.size()));
+ for (size_t i = 0; i < kSize2; ++i) {
+ EXPECT_EQ(size_t(ptr[i]), 0x5Au);
+ }
+
+ // Can free memory.
+ allocator->Deallocate(ptr, layout3);
+}
+
+} // namespace pw::allocator
diff --git a/pw_allocator/size_report/BUILD.bazel b/pw_allocator/size_report/BUILD.bazel
new file mode 100644
index 000000000..3f1e5345b
--- /dev/null
+++ b/pw_allocator/size_report/BUILD.bazel
@@ -0,0 +1,54 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_binary",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+pw_cc_binary(
+ name = "split_free_list_allocator",
+ srcs = ["split_free_list_allocator.cc"],
+ deps = [
+ "//pw_allocator:allocator_metric_proxy",
+ "//pw_allocator:split_free_list_allocator",
+ "//pw_bloat:bloat_this_binary",
+ ],
+)
+
+pw_cc_binary(
+ name = "split_free_list_allocator_with_unique_ptr",
+ srcs = ["split_free_list_allocator.cc"],
+ copts = ["-DSIZE_REPORT_UNIQUE_PTR=1"],
+ deps = [
+ "//pw_allocator:allocator_metric_proxy",
+ "//pw_allocator:split_free_list_allocator",
+ "//pw_bloat:bloat_this_binary",
+ ],
+)
+
+pw_cc_binary(
+ name = "split_free_list_allocator_with_metric_proxy",
+ srcs = ["split_free_list_allocator.cc"],
+ copts = ["-DSIZE_REPORT_METRIC_PROXY=1"],
+ deps = [
+ "//pw_allocator:allocator_metric_proxy",
+ "//pw_allocator:split_free_list_allocator",
+ "//pw_bloat:bloat_this_binary",
+ ],
+)
diff --git a/pw_allocator/size_report/BUILD.gn b/pw_allocator/size_report/BUILD.gn
new file mode 100644
index 000000000..1f73b0a68
--- /dev/null
+++ b/pw_allocator/size_report/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_executable("split_free_list_allocator") {
+ sources = [ "split_free_list_allocator.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..:allocator_metric_proxy",
+ "..:split_free_list_allocator",
+ ]
+}
+
+pw_executable("split_free_list_allocator_with_unique_ptr") {
+ sources = [ "split_free_list_allocator.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..:allocator_metric_proxy",
+ "..:split_free_list_allocator",
+ ]
+ defines = [ "SIZE_REPORT_UNIQUE_PTR=1" ]
+}
+
+pw_executable("split_free_list_allocator_with_metric_proxy") {
+ sources = [ "split_free_list_allocator.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..:allocator_metric_proxy",
+ "..:split_free_list_allocator",
+ ]
+ defines = [ "SIZE_REPORT_METRIC_PROXY=1" ]
+}
diff --git a/pw_allocator/size_report/split_free_list_allocator.cc b/pw_allocator/size_report/split_free_list_allocator.cc
new file mode 100644
index 000000000..42a51017c
--- /dev/null
+++ b/pw_allocator/size_report/split_free_list_allocator.cc
@@ -0,0 +1,131 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// This size report uses pw::string::Format and std::snprintf to write a single
+// printf-style string to a buffer. The number of bytes written is returned.
+//
+// This compares the overhead of using pw::string::Format to directly calling
+// std::snprintf and determining the number of bytes written. It demonstrates
+// that the code for using pw::string::Format is much simpler.
+
+#include "pw_allocator/split_free_list_allocator.h"
+
+#include "pw_bloat/bloat_this_binary.h"
+
+#ifdef SIZE_REPORT_METRIC_PROXY
+#include "pw_allocator/allocator_metric_proxy.h"
+#endif // SIZE_REPORT_METRIC_PROXY
+
+namespace {
+
+pw::allocator::SplitFreeListAllocator allocator;
+
+#ifdef SIZE_REPORT_METRIC_PROXY
+pw::allocator::AllocatorMetricProxy proxy(0);
+#endif // SIZE_REPORT_METRIC_PROXY
+
+constexpr void* kFakeMemoryRegionStart = &allocator;
+constexpr size_t kFakeMemoryRegionSize = 4096;
+
+constexpr size_t kSplitFreeListThreshold = 128;
+
+} // namespace
+
+int main() {
+ pw::bloat::BloatThisBinary();
+
+ allocator.Init(
+ pw::ByteSpan(reinterpret_cast<std::byte*>(kFakeMemoryRegionStart),
+ kFakeMemoryRegionSize),
+ kSplitFreeListThreshold);
+
+ struct Foo {
+ char name[16];
+ };
+ struct Bar : public Foo {
+ int number;
+ };
+
+ // Small allocation.
+ Foo* foo =
+ static_cast<Foo*>(allocator.Allocate(pw::allocator::Layout::Of<Foo>()));
+ if (foo == nullptr) {
+ return 1;
+ }
+
+ foo->name[0] = '\0';
+
+ // Reallocate.
+ Bar* bar = static_cast<Bar*>(
+ allocator.Reallocate(foo, pw::allocator::Layout::Of<Foo>(), sizeof(Bar)));
+ if (bar == nullptr) {
+ return 1;
+ }
+
+ bar->number = 4;
+
+ // Large allocation.
+ struct Baz {
+ std::byte data[kSplitFreeListThreshold * 2];
+ };
+ Baz* baz =
+ static_cast<Baz*>(allocator.Allocate(pw::allocator::Layout::Of<Baz>()));
+ if (baz == nullptr) {
+ return 1;
+ }
+
+ baz->data[kSplitFreeListThreshold] = std::byte(0xf1);
+
+ // Deallocate.
+ allocator.Deallocate(bar, pw::allocator::Layout::Of<Bar>());
+ allocator.Deallocate(baz, pw::allocator::Layout::Of<Baz>());
+
+#ifdef SIZE_REPORT_UNIQUE_PTR
+
+ struct Point {
+ int x;
+ int y;
+
+ Point(int xx, int yy) : x(xx), y(yy) {}
+ };
+
+ {
+ std::optional<pw::allocator::UniquePtr<Point>> maybe_point =
+ allocator.MakeUnique<Point>(3, 4);
+ if (!maybe_point.has_value()) {
+ return 1;
+ }
+
+ pw::allocator::UniquePtr<Point> point = *maybe_point;
+ point->x = point->y * 2;
+ }
+
+#endif // SIZE_REPORT_UNIQUE_PTR
+
+#ifdef SIZE_REPORT_METRIC_PROXY
+ proxy.Initialize(allocator);
+
+ Foo* foo2 =
+ static_cast<Foo*>(proxy.Allocate(pw::allocator::Layout::Of<Foo>()));
+ if (foo2 == nullptr) {
+ return 1;
+ }
+
+ foo2->name[1] = 'a';
+
+ proxy.Deallocate(foo2, pw::allocator::Layout::Of<Foo>());
+#endif // SIZE_REPORT_METRIC_PROXY
+
+ return 0;
+}
diff --git a/pw_allocator/split_free_list_allocator.cc b/pw_allocator/split_free_list_allocator.cc
index 848c4572e..adae4da15 100644
--- a/pw_allocator/split_free_list_allocator.cc
+++ b/pw_allocator/split_free_list_allocator.cc
@@ -14,283 +14,16 @@
#include "pw_allocator/split_free_list_allocator.h"
-#include <algorithm>
-
#include "pw_assert/check.h"
-#include "pw_bytes/alignment.h"
namespace pw::allocator {
-static_assert(sizeof(size_t) == sizeof(uintptr_t), "platform not supported");
-
-using FreeBlock = SplitFreeListAllocator::FreeBlock;
-
-// Public methods.
-
-SplitFreeListAllocator::~SplitFreeListAllocator() {
- // All memory must be returned before the allocator goes out of scope.
- if (addr_ != 0) {
- PW_CHECK(addr_ == reinterpret_cast<uintptr_t>(head_));
- PW_CHECK(head_->next == nullptr);
- PW_CHECK(head_->size == size_);
- }
-}
-
-void SplitFreeListAllocator::Initialize(void* base,
- size_t size,
- size_t threshold) {
- PW_CHECK(base != nullptr);
- auto addr = reinterpret_cast<uintptr_t>(base);
-
- // See `Normalize` below. All addresses, including the start and end of the
- // overall memory region, must be a multiple of and aligned to
- // `sizeof(FreeBlock)`.
- addr_ = AlignUp(addr, sizeof(FreeBlock));
- PW_CHECK(sizeof(FreeBlock) <= size, "size underflow on alignment");
- size_ = AlignDown(size - (addr_ - addr), sizeof(FreeBlock));
- PW_CHECK(sizeof(FreeBlock) <= size_, "region is smaller than a single block");
-
- head_ = reinterpret_cast<FreeBlock*>(addr_);
- head_->next = nullptr;
- head_->size = size_;
- threshold_ = threshold;
-}
-
-// Private methods.
-
-namespace {
-
-/// Adjust the layout if necessary to match `SplitFreeListAllocator`'s minimums.
-///
-/// This functions will modify `size` and `alignment` to represent a memory
-/// region that is a multiple of `sizeof(FreeBlock)`, aligned on
-/// `sizeof(FreeBlock)` boundaries.
-///
-/// The use of such minimum sizes and alignments eliminates several conditions
-/// and edge cases that would need to checked and addressed if more granular
-/// sizes and alignments were used. It also greatly simplifies ensuring that any
-/// fragments can hold a `FreeBlock` as well as reconstructing the `FreeBlock`
-/// from a pointer and `Layout` in `Deallocate`.
-///
-/// These simplifications allow de/allocation to be quicker, at the potential
-/// cost of a few bytes wasted for small and/or less strictly aligned
-/// allocations.
-void Normalize(size_t& size, size_t& alignment) {
- alignment = std::max(alignment, sizeof(FreeBlock));
- size = AlignUp(std::max(size, sizeof(FreeBlock)), alignment);
-}
-
-/// Stores a `FreeBlock` representing a block of the given `size` at
-/// `ptr` + `offset`, and returns it.
-FreeBlock* CreateBlock(void* ptr, size_t size, size_t offset = 0) {
- auto addr = reinterpret_cast<uintptr_t>(ptr) + offset;
- auto* block = reinterpret_cast<FreeBlock*>(addr);
- block->next = nullptr;
- block->size = size;
- return block;
-}
-
-/// Returns true if `prev` + `offset` equals `next`.
-bool IsAdjacent(void* prev, size_t offset, void* next) {
- return reinterpret_cast<uintptr_t>(prev) + offset ==
- reinterpret_cast<uintptr_t>(next);
-}
-
-/// Reduces the size of a block and creates and returns a new block representing
-/// the difference.
-///
-/// The original block must have room for both resulting `FreeBlock`s.
-///
-/// This function assumes `prev` IS on a free list.
-FreeBlock* SplitBlock(FreeBlock* prev, size_t offset) {
- PW_DCHECK(sizeof(FreeBlock) <= offset);
- PW_DCHECK(offset + sizeof(FreeBlock) <= prev->size);
- FreeBlock* next = CreateBlock(prev, prev->size - offset, offset);
- next->next = prev->next;
- prev->size = offset;
- prev->next = next;
- return next;
-}
-
-/// Combines two blocks into one and returns it.
-///
-/// `prev` and `next` MUJST NOT be null.
-
-/// This function assumes `prev` and `next` ARE NOT on a free list.
-FreeBlock* MergeBlocks(FreeBlock* prev, FreeBlock* next) {
- PW_DCHECK(prev != nullptr);
- PW_DCHECK(next != nullptr);
- prev->size += next->size;
- return prev;
-}
-
-} // namespace
-
-void SplitFreeListAllocator::AddBlock(FreeBlock* block) {
- PW_DCHECK(addr_ != 0);
- PW_DCHECK(block != nullptr);
- block->next = head_;
- head_ = block;
-}
-
-SplitFreeListAllocator::FreeBlock* SplitFreeListAllocator::RemoveBlock(
- FreeBlock* prev, FreeBlock* block) {
- PW_DCHECK(addr_ != 0);
- PW_DCHECK(block != nullptr);
- if (block == head_) {
- head_ = block->next;
- } else {
- prev->next = block->next;
- }
- return block;
-}
-
-Status SplitFreeListAllocator::DoQuery(const void* ptr,
- size_t size,
- size_t alignment) const {
- PW_DCHECK(addr_ != 0);
- if (ptr == nullptr || size == 0) {
- return Status::OutOfRange();
- }
- Normalize(size, alignment);
- auto addr = reinterpret_cast<uintptr_t>(ptr);
- if (addr + size <= addr || addr < addr_ || addr_ + size_ < addr + size) {
- return Status::OutOfRange();
- }
- return OkStatus();
-}
-
-void* SplitFreeListAllocator::DoAllocate(size_t size, size_t alignment) {
- PW_DCHECK(addr_ != 0);
- if (head_ == nullptr || size == 0 || size_ < size) {
- return nullptr;
- }
- Normalize(size, alignment);
-
- // Blocks over and under the threshold are allocated from lower and higher
- // addresses, respectively.
- bool from_lower = threshold_ <= size;
- FreeBlock* prev = nullptr;
- FreeBlock* block = nullptr;
- size_t offset = 0;
- for (FreeBlock *previous = nullptr, *current = head_; current != nullptr;
- previous = current, current = current->next) {
- if (current->size < size) {
- continue;
- }
- // Fragment large requests from the start of the block, and small requests
- // from the back. Verify the aligned offsets are still within the block.
- uintptr_t current_start = reinterpret_cast<uintptr_t>(current);
- uintptr_t current_end = current_start + current->size;
- uintptr_t addr = from_lower ? AlignUp(current_start, alignment)
- : AlignDown(current_end - size, alignment);
- if (addr < current_start || current_end < addr + size) {
- continue;
- }
- // Update `prev` and `block` if the current block is earlier or later and we
- // want blocks with lower or higher address, respectively.
- if (block == nullptr || (current < block) == from_lower) {
- prev = previous;
- block = current;
- offset = addr - current_start;
- }
- }
- if (block == nullptr) {
- return nullptr;
- }
- if (offset != 0) {
- prev = block;
- block = SplitBlock(block, offset);
- }
- if (size < block->size) {
- SplitBlock(block, size);
- }
- return RemoveBlock(prev, block);
-}
-
-void SplitFreeListAllocator::DoDeallocate(void* ptr,
- size_t size,
- size_t alignment) {
- PW_DCHECK(addr_ != 0);
-
- // Do nothing if no memory block pointer.
- if (ptr == nullptr) {
- return;
- }
-
- // Ensure that this allocation came from this object.
- PW_DCHECK(DoQuery(ptr, size, alignment).ok());
-
- Normalize(size, alignment);
- FreeBlock* block = CreateBlock(ptr, size);
- for (FreeBlock *previous = nullptr, *current = head_; current != nullptr;
- current = current->next) {
- if (IsAdjacent(current, current->size, block)) {
- // Block precedes block being freed. Remove from list and merge.
- block = MergeBlocks(RemoveBlock(previous, current), block);
- } else if (IsAdjacent(block, block->size, current)) {
- // Block follows block being freed. Remove from list and merge.
- block = MergeBlocks(block, RemoveBlock(previous, current));
- } else {
- previous = current;
- }
- }
-
- // Add released block to the free list.
- AddBlock(block);
-}
-
-bool SplitFreeListAllocator::DoResize(void* ptr,
- size_t old_size,
- size_t old_alignment,
- size_t new_size) {
- PW_DCHECK(addr_ != 0);
-
- if (ptr == nullptr || old_size == 0 || new_size == 0) {
- return false;
- }
-
- // Ensure that this allocation came from this object.
- PW_DCHECK(DoQuery(ptr, old_size, old_alignment).ok());
-
- // Do nothing if new size equals current size.
- Normalize(old_size, old_alignment);
- Normalize(new_size, old_alignment);
- if (old_size == new_size) {
- return true;
- }
- bool growing = old_size < new_size;
- size_t diff = growing ? new_size - old_size : old_size - new_size;
- // Try to find a free block that follows this one.
- FreeBlock* prev = nullptr;
- FreeBlock* next = head_;
- while (next != nullptr && !IsAdjacent(ptr, old_size, next)) {
- prev = next;
- next = next->next;
- }
- if (growing) {
- if (next == nullptr || next->size < diff) {
- // No free neighbor that is large enough. Must reallocate.
- return false;
- }
- // Split the next block and remove the portion to be returned.
- if (diff != next->size) {
- SplitBlock(next, diff);
- }
- RemoveBlock(prev, next);
- } else /* !growing*/ {
- if (next == nullptr) {
- // Create a new block for the extra space and add it.
- next = CreateBlock(ptr, diff, new_size);
- } else {
- // Merge the extra space with the next block.
- RemoveBlock(prev, next);
- prev = CreateBlock(ptr, diff, new_size);
- next = MergeBlocks(prev, next);
- }
- AddBlock(next);
- }
- return true;
+void BaseSplitFreeListAllocator::CrashOnAllocated(void* allocated) {
+ PW_DCHECK(false,
+ "The block at %p was still in use when its allocator was "
+ "destroyed. All memory allocated by an allocator must be released "
+ "before the allocator goes out of scope.",
+ allocated);
}
} // namespace pw::allocator
diff --git a/pw_allocator/split_free_list_allocator_test.cc b/pw_allocator/split_free_list_allocator_test.cc
index 4551ec98e..c519849d6 100644
--- a/pw_allocator/split_free_list_allocator_test.cc
+++ b/pw_allocator/split_free_list_allocator_test.cc
@@ -15,305 +15,405 @@
#include "pw_allocator/split_free_list_allocator.h"
#include "gtest/gtest.h"
+#include "pw_allocator/allocator_testing.h"
+#include "pw_allocator/block.h"
#include "pw_bytes/alignment.h"
+#include "pw_bytes/span.h"
#include "pw_containers/vector.h"
namespace pw::allocator {
namespace {
-// Test fixture.
+// Test fixtures.
+
+// Size of the memory region to use in the tests below.
+static constexpr size_t kCapacity = 256;
+
+// Minimum size of a "large" allocation; allocation less than this size are
+// considered "small".
+static constexpr size_t kThreshold = 64;
+
+// An `SplitFreeListAllocator` that is automatically initialized on
+// construction.
+using BlockType = Block<uint16_t, kCapacity>;
+class SplitFreeListAllocatorWithBuffer
+ : public test::
+ WithBuffer<SplitFreeListAllocator<BlockType>, kCapacity, BlockType> {
+ public:
+ SplitFreeListAllocatorWithBuffer() {
+ EXPECT_EQ((*this)->Init(ByteSpan(this->data(), this->size()), kThreshold),
+ OkStatus());
+ }
+};
-struct SplitFreeListAllocatorTest : ::testing::Test {
- alignas(16) std::array<std::byte, 256> buffer;
- SplitFreeListAllocator allocator;
+// Test case fixture that allows individual tests to cache allocations and
+// release them automatically on tear-down.
+class SplitFreeListAllocatorTest : public ::testing::Test {
+ protected:
+ static constexpr size_t kMaxSize = kCapacity - BlockType::kBlockOverhead;
+ static constexpr size_t kNumPtrs = 16;
void SetUp() override {
- allocator.Initialize(buffer.data(), buffer.size(), 64);
+ for (size_t i = 0; i < kNumPtrs; ++i) {
+ ptrs_[i] = nullptr;
+ }
+ }
+
+ // This method simply ensures the memory is usable by writing to it.
+ void UseMemory(void* ptr, size_t size) { memset(ptr, 0x5a, size); }
+
+ void TearDown() override {
+ for (size_t i = 0; i < kNumPtrs; ++i) {
+ if (ptrs_[i] != nullptr) {
+ // `SplitFreeListAllocator::Deallocate` doesn't actually use the layout,
+ // as the information it needs is encoded in the blocks.
+ allocator_->Deallocate(ptrs_[i], Layout::Of<void*>());
+ }
+ }
}
+
+ SplitFreeListAllocatorWithBuffer allocator_;
+
+ // Tests can store allocations in this array to have them automatically
+ // freed in `TearDown`, including on ASSERT failure. If pointers are manually
+ // deallocated, they should be set to null in the array.
+ void* ptrs_[kNumPtrs];
};
// Unit tests.
-TEST_F(SplitFreeListAllocatorTest, InitializeUnaligned) {
+TEST_F(SplitFreeListAllocatorTest, InitUnaligned) {
// The test fixture uses aligned memory to make it easier to reason about
- // allocations, but that isn't strictly required. Simply verify that a call to
- // `Initialize` with unaligned memory does not crash.
- alignas(16) std::array<std::byte, 256> buf;
- SplitFreeListAllocator unaligned;
- unaligned.Initialize(buf.data() + 1, buf.size() - 1, 64);
+ // allocations, but that isn't strictly required.
+ SplitFreeListAllocator<Block<>> unaligned;
+ ByteSpan bytes(allocator_.data(), allocator_.size());
+ EXPECT_EQ(unaligned.Init(bytes.subspan(1), kThreshold), OkStatus());
}
-TEST_F(SplitFreeListAllocatorTest, AllocateLargeDeallocate) {
- constexpr Layout layout = Layout::Of<std::byte[64]>();
- void* ptr = allocator.Allocate(layout);
- // Returned pointer should be from the beginning.
- EXPECT_EQ(ptr, buffer.data());
- allocator.Deallocate(ptr, layout);
+TEST_F(SplitFreeListAllocatorTest, AllocateLarge) {
+ constexpr Layout layout = Layout::Of<std::byte[kThreshold]>();
+ ptrs_[0] = allocator_->Allocate(layout);
+ ASSERT_NE(ptrs_[0], nullptr);
+ EXPECT_GE(ptrs_[0], allocator_.data());
+ EXPECT_LT(ptrs_[0], allocator_.data() + allocator_.size());
+ UseMemory(ptrs_[0], layout.size());
}
-TEST_F(SplitFreeListAllocatorTest, AllocateSmallDeallocate) {
+TEST_F(SplitFreeListAllocatorTest, AllocateSmall) {
// Returned pointer should not be from the beginning, but should still be in
// range. Exact pointer depends on allocator's minimum allocation size.
constexpr Layout layout = Layout::Of<uint8_t>();
- void* ptr = allocator.Allocate(layout);
- EXPECT_GT(ptr, buffer.data());
- EXPECT_LT(ptr, buffer.data() + buffer.size());
- allocator.Deallocate(ptr, layout);
+ ptrs_[0] = allocator_->Allocate(layout);
+ ASSERT_NE(ptrs_[0], nullptr);
+ EXPECT_GT(ptrs_[0], allocator_.data());
+ EXPECT_LT(ptrs_[0], allocator_.data() + allocator_.size());
+ UseMemory(ptrs_[0], layout.size());
}
TEST_F(SplitFreeListAllocatorTest, AllocateTooLarge) {
- void* ptr = allocator.Allocate(Layout::Of<std::byte[512]>());
- EXPECT_EQ(ptr, nullptr);
+ ptrs_[0] = allocator_->Allocate(Layout::Of<std::byte[kCapacity * 2]>());
+ EXPECT_EQ(ptrs_[0], nullptr);
+}
+
+TEST_F(SplitFreeListAllocatorTest, AllocateLargeAlignment) {
+ constexpr size_t kSize = sizeof(uint32_t);
+ constexpr size_t kAlignment = 64;
+ ptrs_[0] = allocator_->Allocate(Layout(kSize, kAlignment));
+ ASSERT_NE(ptrs_[0], nullptr);
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(ptrs_[0]) % kAlignment, 0U);
+ UseMemory(ptrs_[0], kSize);
+
+ ptrs_[1] = allocator_->Allocate(Layout(kSize, kAlignment));
+ ASSERT_NE(ptrs_[1], nullptr);
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(ptrs_[1]) % kAlignment, 0U);
+ UseMemory(ptrs_[1], kSize);
+}
+
+TEST_F(SplitFreeListAllocatorTest, AllocateFromUnaligned) {
+ SplitFreeListAllocator<Block<>> unaligned;
+ ByteSpan bytes(allocator_.data(), allocator_.size());
+ ASSERT_EQ(unaligned.Init(bytes.subspan(1), kThreshold), OkStatus());
+
+ constexpr Layout layout = Layout::Of<std::byte[kThreshold + 8]>();
+ void* ptr = unaligned.Allocate(layout);
+ ASSERT_NE(ptr, nullptr);
+ UseMemory(ptr, layout.size());
+ unaligned.Deallocate(ptr, layout);
+}
+
+TEST_F(SplitFreeListAllocatorTest, AllocateAlignmentFailure) {
+ // Determine the total number of available bytes.
+ auto base = reinterpret_cast<uintptr_t>(allocator_.data());
+ uintptr_t addr = AlignUp(base, BlockType::kAlignment);
+ size_t outer_size = allocator_.size() - (addr - base);
+
+ // The first block is large....
+ addr += BlockType::kBlockOverhead + kThreshold;
+
+ // The next block is not aligned...
+ constexpr size_t kAlignment = 128;
+ uintptr_t next = AlignUp(addr + BlockType::kBlockOverhead, kAlignment / 4);
+ if (next % kAlignment == 0) {
+ next += kAlignment / 4;
+ }
+
+ // And the last block consumes the remaining space.
+ // size_t outer_size = allocator_->begin()->OuterSize();
+ size_t inner_size1 = next - addr;
+ size_t inner_size2 = kThreshold * 2;
+ size_t inner_size3 =
+ outer_size - (BlockType::kBlockOverhead * 3 + inner_size1 + inner_size2);
+
+ // Allocate all the blocks.
+ ptrs_[0] = allocator_->Allocate(Layout(inner_size1, 1));
+ ASSERT_NE(ptrs_[0], nullptr);
+
+ ptrs_[1] = allocator_->Allocate(Layout(inner_size2, 1));
+ ASSERT_NE(ptrs_[1], nullptr);
+
+ ptrs_[2] = allocator_->Allocate(Layout(inner_size3, 1));
+ ASSERT_NE(ptrs_[2], nullptr);
+
+ // If done correctly, the second block's usable space should be unaligned.
+ EXPECT_NE(reinterpret_cast<uintptr_t>(ptrs_[1]) % kAlignment, 0U);
+
+ // Free the second region. This leaves an unaligned region available.
+ allocator_->Deallocate(ptrs_[1], Layout(inner_size2, 1));
+ ptrs_[1] = nullptr;
+
+ // The allocator should be unable to create an aligned region..
+ ptrs_[3] = allocator_->Allocate(Layout(inner_size2, kAlignment));
+ EXPECT_EQ(ptrs_[3], nullptr);
}
-TEST_F(SplitFreeListAllocatorTest, AllocateAllDeallocateShuffled) {
+TEST_F(SplitFreeListAllocatorTest, DeallocateNull) {
+ constexpr Layout layout = Layout::Of<uint8_t>();
+ allocator_->Deallocate(nullptr, layout);
+}
+
+TEST_F(SplitFreeListAllocatorTest, DeallocateShuffled) {
constexpr Layout layout = Layout::Of<std::byte[32]>();
- Vector<void*, 256> ptrs;
// Allocate until the pool is exhausted.
- while (true) {
- void* ptr = allocator.Allocate(layout);
- if (ptr == nullptr) {
+ for (size_t i = 0; i < kNumPtrs; ++i) {
+ ptrs_[i] = allocator_->Allocate(layout);
+ if (ptrs_[i] == nullptr) {
break;
}
- ptrs.push_back(ptr);
}
// Mix up the order of allocations.
- for (size_t i = 0; i < ptrs.size(); ++i) {
- if (i % 2 == 0 && i + 1 < ptrs.size()) {
- std::swap(ptrs[i], ptrs[i + 1]);
+ for (size_t i = 0; i < kNumPtrs; ++i) {
+ if (i % 2 == 0 && i + 1 < kNumPtrs) {
+ std::swap(ptrs_[i], ptrs_[i + 1]);
}
- if (i % 3 == 0 && i + 2 < ptrs.size()) {
- std::swap(ptrs[i], ptrs[i + 2]);
+ if (i % 3 == 0 && i + 2 < kNumPtrs) {
+ std::swap(ptrs_[i], ptrs_[i + 2]);
}
}
// Deallocate everything.
- for (void* ptr : ptrs) {
- allocator.Deallocate(ptr, layout);
+ for (size_t i = 0; i < kNumPtrs; ++i) {
+ allocator_->Deallocate(ptrs_[i], layout);
+ ptrs_[i] = nullptr;
}
}
-TEST_F(SplitFreeListAllocatorTest, AllocateDeallocateLargeAlignment) {
- void* ptr1 = allocator.AllocateUnchecked(sizeof(uint32_t), 64);
- void* ptr2 = allocator.AllocateUnchecked(sizeof(uint32_t), 64);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr1) % 64, 0U);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr2) % 64, 0U);
- allocator.DeallocateUnchecked(ptr1, sizeof(uint32_t), 64);
- allocator.DeallocateUnchecked(ptr2, sizeof(uint32_t), 64);
-}
-
-TEST_F(SplitFreeListAllocatorTest, AllocateFromUnaligned) {
- alignas(16) std::array<std::byte, 256> buf;
- SplitFreeListAllocator unaligned;
- unaligned.Initialize(buf.data() + 1, buf.size() - 1, 64);
-
- EXPECT_EQ(unaligned.addr() % sizeof(SplitFreeListAllocator::FreeBlock), 0U);
- EXPECT_EQ(unaligned.size() % sizeof(SplitFreeListAllocator::FreeBlock), 0U);
-
- constexpr Layout layout = Layout::Of<std::byte[72]>();
- EXPECT_EQ(layout.size(), 72U);
- EXPECT_EQ(layout.alignment(), 1U);
-
- void* ptr = unaligned.Allocate(layout);
- unaligned.Deallocate(ptr, layout);
-}
+TEST_F(SplitFreeListAllocatorTest, IterateOverBlocks) {
+ constexpr Layout layout1 = Layout::Of<std::byte[32]>();
+ constexpr Layout layout2 = Layout::Of<std::byte[16]>();
+
+ // Allocate eight blocks of alternating sizes. After this, the will also be a
+ // ninth, unallocated block of the remaining memory.
+ for (size_t i = 0; i < 4; ++i) {
+ ptrs_[i] = allocator_->Allocate(layout1);
+ ASSERT_NE(ptrs_[i], nullptr);
+ ptrs_[i + 4] = allocator_->Allocate(layout2);
+ ASSERT_NE(ptrs_[i + 4], nullptr);
+ }
-TEST_F(SplitFreeListAllocatorTest, AllocateAlignmentFailure) {
- // Find a valid address aligned to 128 bytes.
- auto base = reinterpret_cast<uintptr_t>(buffer.data());
- auto aligned = AlignUp(base + 16, 128);
-
- // Now allocate up to 3 regions:
- // * from the beginning to 16 bytes before the alignment boundary
- // * the next 128 bytes
- // * whatever is left
- size_t size1 = aligned - base - 16;
- void* ptr1 = allocator.AllocateUnchecked(size1, 1);
-
- size_t size2 = 128;
- void* ptr2 = allocator.AllocateUnchecked(size2, 1);
-
- size_t size3 = 128 - size1;
- void* ptr3 = allocator.AllocateUnchecked(size3, 1);
-
- // Now free the second region. This leaves a 128-byte region available, but it
- // is not aligned to a 128 byte boundary.
- allocator.DeallocateUnchecked(ptr2, size2, 1);
-
- // The allocator should be unable to create an aligned region of the given
- // size.
- void* ptr = allocator.AllocateUnchecked(128, 128);
- EXPECT_EQ(ptr, nullptr);
-
- if (ptr1 != nullptr) {
- allocator.DeallocateUnchecked(ptr1, size1, 1);
+ // Deallocate every other block. After this there will be four more
+ // unallocated blocks, for a total of five.
+ for (size_t i = 0; i < 4; ++i) {
+ allocator_->Deallocate(ptrs_[i], layout1);
}
- allocator.DeallocateUnchecked(ptr3, size3, 1);
-}
-TEST_F(SplitFreeListAllocatorTest, DeallocateNull) {
- constexpr Layout layout = Layout::Of<uint8_t>();
- allocator.Deallocate(nullptr, layout);
+ // Count the blocks. The unallocated ones vary in size, but the allocated ones
+ // should all be the same.
+ size_t free_count = 0;
+ size_t used_count = 0;
+ for (auto* block : allocator_->blocks()) {
+ if (block->Used()) {
+ EXPECT_GE(block->InnerSize(), layout2.size());
+ ++used_count;
+ } else {
+ ++free_count;
+ }
+ }
+ EXPECT_EQ(used_count, 4U);
+ EXPECT_EQ(free_count, 5U);
}
TEST_F(SplitFreeListAllocatorTest, QueryLargeValid) {
- constexpr Layout layout = Layout::Of<std::byte[128]>();
- void* ptr = allocator.Allocate(layout);
- EXPECT_EQ(allocator.Query(ptr, layout), OkStatus());
- allocator.Deallocate(ptr, layout);
+ constexpr Layout layout = Layout::Of<std::byte[kThreshold * 2]>();
+ ptrs_[0] = allocator_->Allocate(layout);
+ EXPECT_EQ(allocator_->Query(ptrs_[0], layout), OkStatus());
}
TEST_F(SplitFreeListAllocatorTest, QuerySmallValid) {
constexpr Layout layout = Layout::Of<uint8_t>();
- void* ptr = allocator.Allocate(layout);
- EXPECT_EQ(allocator.Query(ptr, layout), OkStatus());
- allocator.Deallocate(ptr, layout);
+ ptrs_[0] = allocator_->Allocate(layout);
+ EXPECT_EQ(allocator_->Query(ptrs_[0], layout), OkStatus());
}
TEST_F(SplitFreeListAllocatorTest, QueryInvalidPtr) {
constexpr Layout layout = Layout::Of<SplitFreeListAllocatorTest>();
- EXPECT_EQ(allocator.Query(this, layout), Status::OutOfRange());
-}
-
-TEST_F(SplitFreeListAllocatorTest, QueryInvalidSize) {
- constexpr Layout layout = Layout::Of<uint8_t>();
- void* ptr = allocator.Allocate(layout);
- EXPECT_EQ(allocator.QueryUnchecked(ptr, 0, layout.alignment()),
- Status::OutOfRange());
- allocator.Deallocate(ptr, layout);
+ EXPECT_EQ(allocator_->Query(this, layout), Status::OutOfRange());
}
TEST_F(SplitFreeListAllocatorTest, ResizeNull) {
constexpr Layout old_layout = Layout::Of<uint8_t>();
size_t new_size = 1;
- EXPECT_FALSE(allocator.Resize(nullptr, old_layout, new_size));
+ EXPECT_FALSE(allocator_->Resize(nullptr, old_layout, new_size));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSame) {
constexpr Layout old_layout = Layout::Of<uint32_t>();
- void* ptr = allocator.Allocate(old_layout);
- EXPECT_NE(ptr, nullptr);
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
+
constexpr Layout new_layout = Layout::Of<uint32_t>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[0], nullptr);
+ UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeSmaller) {
- constexpr Layout old_layout = Layout::Of<std::byte[240]>();
- void* ptr = allocator.Allocate(old_layout);
+ constexpr Layout old_layout = Layout::Of<std::byte[kMaxSize]>();
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
// Shrinking always succeeds.
- constexpr Layout new_layout = Layout::Of<std::byte[80]>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ constexpr Layout new_layout = Layout::Of<std::byte[kThreshold]>();
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[0], nullptr);
+ UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeLarger) {
- constexpr Layout old_layout = Layout::Of<std::byte[80]>();
- void* ptr = allocator.Allocate(old_layout);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
// Nothing after ptr, so `Resize` should succeed.
- constexpr Layout new_layout = Layout::Of<std::byte[240]>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ constexpr Layout new_layout = Layout::Of<std::byte[kMaxSize]>();
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[0], nullptr);
+ UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeLargerFailure) {
- constexpr Layout old_layout = Layout::Of<std::byte[80]>();
- void* ptr1 = allocator.Allocate(old_layout);
- void* ptr2 = allocator.Allocate(old_layout);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
+
+ ptrs_[1] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[1], nullptr);
// Memory after ptr is already allocated, so `Resize` should fail.
- size_t new_size = 240;
- EXPECT_FALSE(allocator.Resize(ptr1, old_layout, new_size));
- allocator.Deallocate(ptr1, old_layout);
- allocator.Deallocate(ptr2, old_layout);
+ EXPECT_FALSE(allocator_->Resize(ptrs_[0], old_layout, kMaxSize));
}
TEST_F(SplitFreeListAllocatorTest, ResizeLargeSmallerAcrossThreshold) {
- constexpr Layout old_layout = Layout::Of<std::byte[80]>();
- void* ptr = allocator.Allocate(old_layout);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold]>();
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
// Shrinking succeeds, and the pointer is unchanged even though it is now
// below the threshold.
- constexpr Layout new_layout = Layout::Of<std::byte[16]>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ constexpr Layout new_layout = Layout::Of<std::byte[kThreshold / 4]>();
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[0], nullptr);
+ UseMemory(ptrs_[0], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallSmaller) {
constexpr Layout old_layout = Layout::Of<uint32_t>();
- void* ptr = allocator.Allocate(old_layout);
+ ptrs_[0] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[0], nullptr);
// Shrinking always succeeds.
constexpr Layout new_layout = Layout::Of<uint8_t>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ EXPECT_TRUE(allocator_->Resize(ptrs_[0], old_layout, new_layout.size()));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLarger) {
// First, allocate a trailing block.
- constexpr Layout layout1 = Layout::Of<std::byte[16]>();
- void* ptr1 = allocator.Allocate(layout1);
- EXPECT_NE(ptr1, nullptr);
+ constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 4]>();
+ ptrs_[0] = allocator_->Allocate(layout1);
+ ASSERT_NE(ptrs_[0], nullptr);
// Next allocate the memory to be resized.
- constexpr Layout old_layout = Layout::Of<std::byte[16]>();
- void* ptr = allocator.Allocate(old_layout);
- EXPECT_NE(ptr, nullptr);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
+ ptrs_[1] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[1], nullptr);
// Now free the trailing block.
- allocator.Deallocate(ptr1, layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
+ ptrs_[0] = nullptr;
// And finally, resize. Since the memory after the block is available and big
// enough, `Resize` should succeed.
- constexpr Layout new_layout = Layout::Of<std::byte[24]>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ constexpr Layout new_layout = Layout::Of<std::byte[kThreshold / 2]>();
+ EXPECT_TRUE(allocator_->Resize(ptrs_[1], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[1], nullptr);
+ UseMemory(ptrs_[1], new_layout.size());
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLargerFailure) {
// First, allocate a trailing block.
- constexpr Layout layout1 = Layout::Of<std::byte[8]>();
- void* ptr1 = allocator.Allocate(layout1);
- EXPECT_NE(ptr1, nullptr);
+ constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 4]>();
+ ptrs_[0] = allocator_->Allocate(layout1);
+ ASSERT_NE(ptrs_[0], nullptr);
// Next allocate the memory to be resized.
- constexpr Layout old_layout = Layout::Of<std::byte[16]>();
- void* ptr = allocator.Allocate(old_layout);
- EXPECT_NE(ptr, nullptr);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
+ ptrs_[1] = allocator_->Allocate(old_layout);
+ ASSERT_NE(ptrs_[1], nullptr);
// Now free the trailing block.
- allocator.Deallocate(ptr1, layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
+ ptrs_[0] = nullptr;
// And finally, resize. Since the memory after the block is available but not
// big enough, `Resize` should fail.
size_t new_size = 48;
- EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_size));
- allocator.Deallocate(ptr, old_layout);
+ EXPECT_FALSE(allocator_->Resize(ptrs_[1], old_layout, new_size));
}
TEST_F(SplitFreeListAllocatorTest, ResizeSmallLargerAcrossThreshold) {
// First, allocate several trailing block.
- constexpr Layout layout1 = Layout::Of<std::byte[48]>();
- void* ptr1 = allocator.Allocate(layout1);
- EXPECT_NE(ptr1, nullptr);
- void* ptr2 = allocator.Allocate(layout1);
- EXPECT_NE(ptr2, nullptr);
+ constexpr Layout layout1 = Layout::Of<std::byte[kThreshold / 2]>();
+ ptrs_[0] = allocator_->Allocate(layout1);
+ ASSERT_NE(ptrs_[0], nullptr);
+
+ ptrs_[1] = allocator_->Allocate(layout1);
+ ASSERT_NE(ptrs_[1], nullptr);
// Next allocate the memory to be resized.
- constexpr Layout old_layout = Layout::Of<std::byte[16]>();
- void* ptr = allocator.Allocate(old_layout);
- EXPECT_NE(ptr, nullptr);
+ constexpr Layout old_layout = Layout::Of<std::byte[kThreshold / 4]>();
+ ptrs_[2] = allocator_->Allocate(old_layout);
+ EXPECT_NE(ptrs_[2], nullptr);
// Now free the trailing blocks.
- allocator.Deallocate(ptr1, layout1);
- allocator.Deallocate(ptr2, layout1);
+ allocator_->Deallocate(ptrs_[0], layout1);
+ ptrs_[0] = nullptr;
+ allocator_->Deallocate(ptrs_[1], layout1);
+ ptrs_[1] = nullptr;
// Growing succeeds, and the pointer is unchanged even though it is now
// above the threshold.
- constexpr Layout new_layout = Layout::Of<std::byte[96]>();
- EXPECT_TRUE(allocator.Resize(ptr, old_layout, new_layout.size()));
- allocator.Deallocate(ptr, new_layout);
+ constexpr Layout new_layout = Layout::Of<std::byte[kThreshold]>();
+ EXPECT_TRUE(allocator_->Resize(ptrs_[2], old_layout, new_layout.size()));
+ ASSERT_NE(ptrs_[2], nullptr);
+ UseMemory(ptrs_[2], new_layout.size());
}
} // namespace
diff --git a/pw_allocator/unique_ptr_test.cc b/pw_allocator/unique_ptr_test.cc
new file mode 100644
index 000000000..c0aa57cce
--- /dev/null
+++ b/pw_allocator/unique_ptr_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstddef>
+
+#include "gtest/gtest.h"
+#include "pw_allocator/allocator.h"
+#include "pw_allocator/allocator_testing.h"
+
+namespace pw::allocator {
+namespace {
+
+using FakeAllocWithBuffer = test::AllocatorForTestWithBuffer<256>;
+
+TEST(UniquePtr, DefaultInitializationIsNullptr) {
+ UniquePtr<int> empty;
+ EXPECT_EQ(empty.get(), nullptr);
+}
+
+TEST(UniquePtr, OperatorEqNullptrOnEmptyUniquePtrSucceeds) {
+ UniquePtr<int> empty;
+ EXPECT_TRUE(empty == nullptr);
+ EXPECT_FALSE(empty != nullptr);
+}
+
+TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueFails) {
+ FakeAllocWithBuffer alloc;
+ std::optional<UniquePtr<int>> ptr_opt = alloc->MakeUnique<int>(5);
+ ASSERT_TRUE(ptr_opt.has_value());
+ UniquePtr<int> ptr = std::move(*ptr_opt);
+ EXPECT_TRUE(ptr != nullptr);
+ EXPECT_FALSE(ptr == nullptr);
+}
+
+TEST(UniquePtr, OperatorEqNullptrAfterMakeUniqueNullptrTypeFails) {
+ FakeAllocWithBuffer alloc;
+ std::optional<UniquePtr<std::nullptr_t>> ptr_opt =
+ alloc->MakeUnique<std::nullptr_t>(nullptr);
+ ASSERT_TRUE(ptr_opt.has_value());
+ UniquePtr<std::nullptr_t> ptr = std::move(*ptr_opt);
+ EXPECT_TRUE(ptr != nullptr);
+ EXPECT_FALSE(ptr == nullptr);
+ EXPECT_TRUE(*ptr == nullptr);
+ EXPECT_FALSE(*ptr != nullptr);
+}
+
+TEST(UniquePtr, MakeUniqueForwardsConstructorArguments) {
+ class MoveOnly {
+ public:
+ MoveOnly(int value) : value_(value) {}
+ MoveOnly(MoveOnly&) = delete;
+ MoveOnly(MoveOnly&&) {}
+ int Value() const { return value_; }
+
+ private:
+ int value_;
+ };
+
+ class BuiltWithMoveOnly {
+ public:
+ BuiltWithMoveOnly() = delete;
+ BuiltWithMoveOnly(MoveOnly&& mo) : value_(mo.Value()) {}
+ int Value() const { return value_; }
+
+ private:
+ int value_;
+ };
+
+ FakeAllocWithBuffer alloc;
+ MoveOnly mo(6);
+ std::optional<UniquePtr<BuiltWithMoveOnly>> ptr =
+ alloc->MakeUnique<BuiltWithMoveOnly>(std::move(mo));
+ ASSERT_TRUE(ptr.has_value());
+ EXPECT_EQ((*ptr)->Value(), 6);
+}
+
+TEST(UniquePtr, MoveConstructsFromSubClassAndFreesTotalSize) {
+ struct Base {};
+ struct LargerSub : public Base {
+ std::array<std::byte, 128> mem;
+ };
+ FakeAllocWithBuffer alloc;
+ std::optional<UniquePtr<LargerSub>> ptr_opt = alloc->MakeUnique<LargerSub>();
+ ASSERT_TRUE(ptr_opt.has_value());
+ EXPECT_EQ(alloc->allocate_size(), 128ul);
+ UniquePtr<LargerSub> ptr = std::move(*ptr_opt);
+ UniquePtr<Base> base_ptr(std::move(ptr));
+
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
+ // The size that is deallocated here should be the size of the larger
+ // subclass, not the size of the smaller base class.
+ base_ptr.Reset();
+ EXPECT_EQ(alloc->deallocate_size(), 128ul);
+}
+
+TEST(UniquePtr, MoveAssignsFromSubClassAndFreesTotalSize) {
+ struct Base {};
+ struct LargerSub : public Base {
+ std::array<std::byte, 128> mem;
+ };
+ FakeAllocWithBuffer alloc;
+ std::optional<UniquePtr<LargerSub>> ptr_opt = alloc->MakeUnique<LargerSub>();
+ ASSERT_TRUE(ptr_opt.has_value());
+ EXPECT_EQ(alloc->allocate_size(), 128ul);
+ UniquePtr<LargerSub> ptr = std::move(*ptr_opt);
+ UniquePtr<Base> base_ptr = std::move(ptr);
+
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
+ // The size that is deallocated here should be the size of the larger
+ // subclass, not the size of the smaller base class.
+ base_ptr.Reset();
+ EXPECT_EQ(alloc->deallocate_size(), 128ul);
+}
+
+TEST(UniquePtr, DestructorDestroysAndFrees) {
+ int count = 0;
+ class DestructorCounter {
+ public:
+ DestructorCounter(int& count) : count_(&count) {}
+ ~DestructorCounter() { (*count_)++; }
+
+ private:
+ int* count_;
+ };
+ FakeAllocWithBuffer alloc;
+ std::optional<UniquePtr<DestructorCounter>> ptr_opt =
+ alloc->MakeUnique<DestructorCounter>(count);
+ ASSERT_TRUE(ptr_opt.has_value());
+
+ EXPECT_EQ(count, 0);
+ EXPECT_EQ(alloc->deallocate_size(), 0ul);
+ ptr_opt.reset(); // clear the optional, destroying the UniquePtr
+ EXPECT_EQ(count, 1);
+ EXPECT_EQ(alloc->deallocate_size(), sizeof(DestructorCounter));
+}
+
+} // namespace
+} // namespace pw::allocator
diff --git a/pw_analog/BUILD.bazel b/pw_analog/BUILD.bazel
index 5c699c310..ad96f2502 100644
--- a/pw_analog/BUILD.bazel
+++ b/pw_analog/BUILD.bazel
@@ -50,6 +50,7 @@ pw_cc_library(
pw_cc_library(
name = "microvolt_input_gmock",
+ testonly = True,
hdrs = [
"public/pw_analog/microvolt_input_gmock.h",
],
@@ -62,6 +63,7 @@ pw_cc_library(
pw_cc_library(
name = "analog_input_gmock",
+ testonly = True,
hdrs = [
"public/pw_analog/analog_input_gmock.h",
],
diff --git a/pw_arduino_build/py/pw_arduino_build/__main__.py b/pw_arduino_build/py/pw_arduino_build/__main__.py
index 0811bef48..4338b538b 100644
--- a/pw_arduino_build/py/pw_arduino_build/__main__.py
+++ b/pw_arduino_build/py/pw_arduino_build/__main__.py
@@ -26,9 +26,17 @@ from collections import OrderedDict
from pathlib import Path
from typing import List
-from pw_arduino_build import core_installer, log
-from pw_arduino_build.builder import ArduinoBuilder
-from pw_arduino_build.file_operations import decode_file_json
+try:
+ from pw_arduino_build import core_installer, log
+ from pw_arduino_build.builder import ArduinoBuilder
+ from pw_arduino_build.file_operations import decode_file_json
+
+except ImportError:
+ # Load from this directory if pw_arduino_build is not available.
+ import core_installer # type: ignore
+ import log # type: ignore
+ from builder import ArduinoBuilder # type: ignore
+ from file_operations import decode_file_json # type: ignore
_LOG = logging.getLogger(__name__)
diff --git a/pw_arduino_build/py/pw_arduino_build/builder.py b/pw_arduino_build/py/pw_arduino_build/builder.py
index 3d0055a2d..7684e3b84 100755
--- a/pw_arduino_build/py/pw_arduino_build/builder.py
+++ b/pw_arduino_build/py/pw_arduino_build/builder.py
@@ -26,7 +26,12 @@ from collections import OrderedDict
from pathlib import Path
from typing import List
-from pw_arduino_build import file_operations
+try:
+ from pw_arduino_build import file_operations
+
+except ImportError:
+ # Load from this directory if pw_arduino_build is not available.
+ import file_operations # type: ignore
_LOG = logging.getLogger(__name__)
@@ -250,6 +255,19 @@ class ArduinoBuilder:
self.library_names = []
self.library_names.append("SrcWrapper")
+ # Surround args in quotes if they contain any quote characters.
+ # Example:
+ # before: -DVARIANT_H="variant_generic.h"
+ # after: "-DVARIANT_H=\"variant_generic.h\""
+ build_info_args = []
+ for arg in self.platform["build.info.flags"].split():
+ if '"' not in arg:
+ build_info_args.append(arg)
+ continue
+ new_arg = arg.replace('"', '\\"')
+ build_info_args.append(f'"{new_arg}"')
+ self.platform["build.info.flags"] = ' '.join(build_info_args)
+
def _copy_default_menu_options_to_build_variables(self):
# Clear existing options
self.menu_options["selected"] = {}
@@ -571,8 +589,10 @@ class ArduinoBuilder:
self.build_variant_path = bvp
self.board[self.selected_board]["build.variant.path"] = bvp
# Add the variant folder as an include directory
- # (used in stm32l4 core)
- self.variant_includes = "-I{}".format(bvp)
+ # This is used in the stm32l4 and stm32duino cores. Include
+ # directories should be surrounded in quotes in case they contain
+ # spaces or parens.
+ self.variant_includes = "\"-I{}\"".format(bvp)
_LOG.debug("PLATFORM INITIAL: %s", _pretty_format(self.platform))
diff --git a/pw_arduino_build/py/pw_arduino_build/core_installer.py b/pw_arduino_build/py/pw_arduino_build/core_installer.py
index 2a44837bc..758072894 100644
--- a/pw_arduino_build/py/pw_arduino_build/core_installer.py
+++ b/pw_arduino_build/py/pw_arduino_build/core_installer.py
@@ -23,7 +23,12 @@ import platform
import shutil
from typing import Dict
-from pw_arduino_build import file_operations
+try:
+ from pw_arduino_build import file_operations
+
+except ImportError:
+ # Load this directory if pw_arduino_build is not available.
+ import file_operations # type: ignore
_LOG = logging.getLogger(__name__)
@@ -128,10 +133,10 @@ _ARDUINO_CORE_ARTIFACTS: Dict[str, Dict] = {
"stm32duino": {
"all": {
"core": {
- "version": "1.9.0",
- "url": "https://github.com/stm32duino/Arduino_Core_STM32/archive/1.9.0.tar.gz",
- "file_name": "stm32duino-1.9.0.tar.gz",
- "sha256": "4f75ba7a117d90392e8f67c58d31d22393749b9cdd3279bc21e7261ec06c62bf",
+ "version": "2.6.0",
+ "url": "https://github.com/stm32duino/Arduino_Core_STM32/archive/2.6.0.tar.gz",
+ "file_name": "stm32duino-2.6.0.tar.gz",
+ "sha256": "53f37df1202b1bccfb353e4775200f63b36d487fe734fdb8ca9bfa00c2f3429f",
}
},
"Linux": {},
diff --git a/pw_arduino_build/py/setup.cfg b/pw_arduino_build/py/setup.cfg
index d28c23c62..a6799442a 100644
--- a/pw_arduino_build/py/setup.cfg
+++ b/pw_arduino_build/py/setup.cfg
@@ -25,7 +25,6 @@ install_requires =
pyserial>=3.5,<4.0
types-pyserial>=3.5,<4.0
coloredlogs
- parameterized
[options.entry_points]
console_scripts =
diff --git a/pw_assert/assert_backend_compile_test_c.c b/pw_assert/assert_backend_compile_test_c.c
index f0c9d05e5..a13205649 100644
--- a/pw_assert/assert_backend_compile_test_c.c
+++ b/pw_assert/assert_backend_compile_test_c.c
@@ -61,6 +61,7 @@ static const int z = 10;
static int Add3(int a, int b, int c) { return a + b + c; }
+// NOLINTNEXTLINE(google-readability-function-size)
void AssertBackendCompileTestsInC(void) {
{ // TEST(Crash, WithAndWithoutMessageArguments)
MAYBE_SKIP_TEST;
diff --git a/pw_assert/docs.rst b/pw_assert/docs.rst
index 5d97e59bb..6fd97310c 100644
--- a/pw_assert/docs.rst
+++ b/pw_assert/docs.rst
@@ -827,7 +827,7 @@ Available Assert Backends
``abort`` standard library functions to implement the assert facade. Prints
the assert expression, evaluated arguments if any, file/line, function name,
and user message, then aborts. Only suitable for targets that support these
- standard library functions. Compatible with C++14.
+ standard library functions.
- ``pw_assert_basic`` - **Stable** - The assert basic module is a simple assert
handler that displays the failed assert line and the values of captured
arguments. Output is directed to ``pw_sys_io``. This module is a great
diff --git a/pw_assert/print_and_abort_check_public_overrides/pw_assert_backend/check_backend.h b/pw_assert/print_and_abort_check_public_overrides/pw_assert_backend/check_backend.h
index b62a59585..272ad203d 100644
--- a/pw_assert/print_and_abort_check_public_overrides/pw_assert_backend/check_backend.h
+++ b/pw_assert/print_and_abort_check_public_overrides/pw_assert_backend/check_backend.h
@@ -37,16 +37,16 @@
PW_ASSERT_PRINT_EXPRESSION("CHECK", \
arg_a_str " " comparison_op_str " " arg_b_str); \
fprintf(stderr, \
- " EVALUATED CONDITION\n\n " arg_a_str " (=" type_fmt \
- ") " comparison_op_str " " arg_b_str " (=" type_fmt \
+ " \033[33mEVALUATED CONDITION\033[0m\n\n " arg_a_str \
+ " (=" type_fmt ") " comparison_op_str " " arg_b_str " (=" type_fmt \
")" \
".\n\n", \
arg_a_val, \
arg_b_val); \
_PW_ASSERT_PRINT_MESSAGE_AND_ABORT(__VA_ARGS__)
-#define _PW_ASSERT_PRINT_MESSAGE_AND_ABORT(...) \
- fprintf(stderr, " MESSAGE\n\n " __VA_ARGS__); \
- fprintf(stderr, "\n\n"); \
- fflush(stderr); \
+#define _PW_ASSERT_PRINT_MESSAGE_AND_ABORT(...) \
+ fprintf(stderr, " \033[33mMESSAGE\033[0m\n\n " __VA_ARGS__); \
+ fprintf(stderr, "\n\n"); \
+ fflush(stderr); \
abort()
diff --git a/pw_assert/public/pw_assert/internal/print_and_abort.h b/pw_assert/public/pw_assert/internal/print_and_abort.h
index be231f166..5112a1ac8 100644
--- a/pw_assert/public/pw_assert/internal/print_and_abort.h
+++ b/pw_assert/public/pw_assert/internal/print_and_abort.h
@@ -30,6 +30,22 @@
#define _PW_ASSERT_ABORT_FUNCTION __func__
#endif // __GNUC__
+// clang-format off
+#define _PW_ASSERT_CRASH_BANNER \
+ "\n" \
+ " ▄████▄ ██▀███ ▄▄▄ ██████ ██░ ██ \n" \
+ " ▒██▀ ▀█ ▓██ ▒ ██▒ ▒████▄ ▒██ ▒ ▓██░ ██▒\n" \
+ " ▒▓█ 💥 ▄ ▓██ ░▄█ ▒ ▒██ ▀█▄ ░ ▓██▄ ▒██▀▀██░\n" \
+ " ▒▓▓▄ ▄██▒ ▒██▀▀█▄ ░██▄▄▄▄██ ▒ ██▒ ░▓█ ░██ \n" \
+ " ▒ ▓███▀ ░ ░██▓ ▒██▒ ▓█ ▓██▒ ▒██████▒▒ ░▓█▒░██▓\n" \
+ " ░ ░▒ ▒ ░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░ ▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒\n" \
+ " ░ ▒ ░▒ ░ ▒░ ▒ ▒▒ ░ ░ ░▒ ░ ░ ▒ ░▒░ ░\n" \
+ " ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░\n" \
+ " ░ ░ ░ ░ ░ ░ ░ ░ ░\n" \
+ " ░\n" \
+ "\n"
+// clang-format on
+
// This assert implementation prints the file path, line number, and assert
// expression using printf. Uses ANSI escape codes for colors.
//
@@ -39,19 +55,20 @@
fflush(stderr); \
abort()
-#define PW_ASSERT_PRINT_EXPRESSION(macro, expression) \
- fflush(stdout); \
+#define PW_ASSERT_PRINT_EXPRESSION(macro, expression) \
+ fflush(stdout); \
+ fprintf(stderr, "\033[31m" _PW_ASSERT_CRASH_BANNER "\033[0m"); \
fprintf(stderr, \
"\033[41m\033[37m\033[1m%s:%d:\033[0m " \
"\033[1m" \
_PW_ASSERT_MACRO(macro) \
" " \
"\033[31mFAILED!\033[0m\n\n" \
- " FAILED ASSERTION\n\n" \
+ " \033[33mFAILED ASSERTION\033[0m\n\n" \
" %s\n\n" \
- " FILE & LINE\n\n" \
+ " \033[33mFILE & LINE\033[0m\n\n" \
" %s:%d\n\n" \
- " FUNCTION\n\n" \
+ " \033[33mFUNCTION\033[0m\n\n" \
" %s\n\n", \
__FILE__, \
__LINE__, \
diff --git a/pw_bluetooth/public/pw_bluetooth/hci_commands.emb b/pw_bluetooth/public/pw_bluetooth/hci_commands.emb
index 02c43ae6e..fdabc7b62 100644
--- a/pw_bluetooth/public/pw_bluetooth/hci_commands.emb
+++ b/pw_bluetooth/public/pw_bluetooth/hci_commands.emb
@@ -92,7 +92,6 @@ bits ScoPacketType:
$next [+1] Flag not_3_ev3
$next [+1] Flag not_2_ev5
$next [+1] Flag not_3_ev5
- $next [+6] UInt padding
bits PacketType:
@@ -124,16 +123,13 @@ enum OobDataPresent:
bits ScanEnableBits:
- -- Bitmask Values for the Scan_Enable parameter in a
- -- HCI_(Read,Write)_Scan_Enable command.
- 0 [+1] Flag inquiry
+ -- Bitmask Values for the Scan_Enable parameter in a HCI_(Read,Write)_Scan_Enable command.
+ 0 [+1] Flag inquiry
-- Inquiry scan enabled
- $next [+1] Flag page
+ 1 [+1] Flag page
-- Page scan enabled
- $next [+6] UInt padding
-
enum InquiryScanType:
[maximum_bits: 8]
@@ -176,9 +172,7 @@ enum LEPeriodicAdvertisingCreateSyncUseParams:
bits LEPeriodicAdvertisingCreateSyncOptions:
-- First parameter to the LE Periodic Advertising Create Sync command
-
0 [+1] LEPeriodicAdvertisingCreateSyncUseParams advertiser_source
-
$next [+1] Flag enable_reporting
-- 0: Reporting initially enabled
-- 1: Reporting initially disabled
@@ -187,9 +181,6 @@ bits LEPeriodicAdvertisingCreateSyncOptions:
-- 0: Duplicate filtering initially disabled
-- 1: Duplicate filtering initially enabled
- $next [+5] UInt padding
- -- Reserved for future use
-
enum LEPeriodicAdvertisingAddressType:
-- Possible values that can be specified for the |advertiser_address_type| in an LE Periodic
@@ -221,9 +212,6 @@ bits LEPeriodicAdvertisingSyncCTEType:
$next [+1] Flag dont_sync_without_cte
-- Do not sync to packets without a Constant Tone Extension
- $next [+3] UInt padding
- -- Reserved for future use
-
enum LEOwnAddressType:
-- Possible values that can be used for the |own_address_type| parameter in various LE packets.
@@ -266,17 +254,17 @@ enum LEScanFilterPolicy:
bits LEScanPHYBits:
- 0 [+1] Flag le_1m
+ 0 [+1] Flag le_1m
-- Scan advertisements on the LE 1M PHY
- $next [+1] Flag padding1
- -- Reserved for future use
-
- $next [+1] Flag le_coded
+ 2 [+1] Flag le_coded
-- Scan advertisements on the LE Coded PHY
- $next [+5] UInt padding2
- -- Reserved for future use
+
+bits LEInitiatingPHYBits:
+ 0 [+1] Flag le_1m
+ 1 [+1] Flag le_2m
+ 2 [+1] Flag le_coded
enum LEPrivacyMode:
@@ -468,35 +456,38 @@ bits LECISPHYOptions:
0 [+1] Flag le_1m
$next [+1] Flag le_2m
$next [+1] Flag le_coded
- $next [+5] UInt padding
struct LESetCIGParametersCISOptions:
-- Parameters for the CISes defined in a LESetCIGParametersCommand
- 0 [+1] UInt cis_id
+ 0 [+1] UInt cis_id
-- Used to identify a CIS
[requires: 0x00 <= this <= 0xEF]
- $next [+2] UInt max_sdu_c_to_p
+ $next [+2] UInt max_sdu_c_to_p
-- Maximum size, in octets, of the payload from the Central's Host
[requires: 0x0000 <= this <= 0x0FFF]
- $next [+2] UInt max_sdu_p_to_c
+ $next [+2] UInt max_sdu_p_to_c
-- Maximum size, in octets, of the payload from the Peripheral's Host
[requires: 0x0000 <= this <= 0x0FFF]
- $next [+1] LECISPHYOptions phy_c_to_p
- -- Identifies which PHY to use for transmission from the Central to the Peripheral
+ $next [+1] bits:
+
+ 0 [+LECISPHYOptions.$size_in_bits] LECISPHYOptions phy_c_to_p
+ -- Identifies which PHY to use for transmission from the Central to the Peripheral
- $next [+1] LECISPHYOptions phy_p_to_c
- -- Identifies which PHY to use for transmission from the Peripheral to the Central
+ $next [+1] bits:
- $next [+1] UInt rtn_c_to_p
+ 0 [+LECISPHYOptions.$size_in_bits] LECISPHYOptions phy_p_to_c
+ -- Identifies which PHY to use for transmission from the Peripheral to the Central
+
+ $next [+1] UInt rtn_c_to_p
-- Number of times every CIS Data PDU should be retransmitted from the Central to the
-- Peripheral
- $next [+1] UInt rtn_p_to_c
+ $next [+1] UInt rtn_p_to_c
-- Number of times every CIS Data PDU should be retransmitted from the Peripheral to the
-- Central
@@ -952,7 +943,7 @@ struct CodecId:
struct SynchronousConnectionParameters:
-- Enhanced Setup Synchronous Connection Command (CSA2) (BR/EDR)
-
+ -- TODO: b/308794058 - Use CodecId instead of VendorCodingFormat
struct VendorCodingFormat:
0 [+1] hci.CodingFormat coding_format
$next [+2] UInt company_id
@@ -975,77 +966,71 @@ struct SynchronousConnectionParameters:
DONT_CARE = 0xFF
-- SCO or eSCO
- 0 [+4] UInt transmit_bandwidth
+ 0 [+4] UInt transmit_bandwidth
-- Transmit bandwidth in octets per second.
- $next [+4] UInt receive_bandwidth
+ $next [+4] UInt receive_bandwidth
-- Receive bandwidth in octets per second.
let vcf_size = VendorCodingFormat.$size_in_bytes
-
- $next [+vcf_size] VendorCodingFormat transmit_coding_format
+ $next [+vcf_size] VendorCodingFormat transmit_coding_format
-- Local Controller -> Remote Controller coding format.
- $next [+vcf_size] VendorCodingFormat receive_coding_format
+ $next [+vcf_size] VendorCodingFormat receive_coding_format
-- Remote Controller -> Local Controller coding format.
- $next [+2] UInt transmit_codec_frame_size_bytes
-
- $next [+2] UInt receive_codec_frame_size_bytes
-
- $next [+4] UInt input_bandwidth
+ $next [+2] UInt transmit_codec_frame_size_bytes
+ $next [+2] UInt receive_codec_frame_size_bytes
+ $next [+4] UInt input_bandwidth
-- Host->Controller data rate in octets per second.
- $next [+4] UInt output_bandwidth
+ $next [+4] UInt output_bandwidth
-- Controller->Host data rate in octets per second.
- $next [+vcf_size] VendorCodingFormat input_coding_format
+ $next [+vcf_size] VendorCodingFormat input_coding_format
-- Host->Controller coding format.
- $next [+vcf_size] VendorCodingFormat output_coding_format
+ $next [+vcf_size] VendorCodingFormat output_coding_format
-- Controller->Host coding format.
- $next [+2] UInt input_coded_data_size_bits
+ $next [+2] UInt input_coded_data_size_bits
-- Size, in bits, of the sample or framed data.
- $next [+2] UInt output_coded_data_size_bits
+ $next [+2] UInt output_coded_data_size_bits
-- Size, in bits, of the sample or framed data.
- $next [+1] PcmDataFormat input_pcm_data_format
-
- $next [+1] PcmDataFormat output_pcm_data_format
-
- $next [+1] UInt input_pcm_sample_payload_msb_position
+ $next [+1] PcmDataFormat input_pcm_data_format
+ $next [+1] PcmDataFormat output_pcm_data_format
+ $next [+1] UInt input_pcm_sample_payload_msb_position
-- The number of bit positions within an audio sample that the MSB of
-- the sample is away from starting at the MSB of the data.
- $next [+1] UInt output_pcm_sample_payload_msb_position
+ $next [+1] UInt output_pcm_sample_payload_msb_position
-- The number of bit positions within an audio sample that the MSB of
-- the sample is away from starting at the MSB of the data.
- $next [+1] ScoDataPath input_data_path
-
- $next [+1] ScoDataPath output_data_path
-
- $next [+1] UInt input_transport_unit_size_bits
+ $next [+1] ScoDataPath input_data_path
+ $next [+1] ScoDataPath output_data_path
+ $next [+1] UInt input_transport_unit_size_bits
-- The number of bits in each unit of data received from the Host over the audio data transport.
-- 0 indicates "not applicable" (implied by the choice of audio data transport).
- $next [+1] UInt output_transport_unit_size_bits
+ $next [+1] UInt output_transport_unit_size_bits
-- The number of bits in each unit of data sent to the Host over the audio data transport.
-- 0 indicates "not applicable" (implied by the choice of audio data transport).
- $next [+2] UInt max_latency_ms
+ $next [+2] UInt max_latency_ms
-- The value in milliseconds representing the upper limit of the sum of
-- the synchronous interval, and the size of the eSCO window, where the
-- eSCO window is the reserved slots plus the retransmission window.
-- Minimum: 0x0004
-- Don't care: 0xFFFF
- $next [+2] ScoPacketType packet_types
- -- Bitmask of allowed packet types.
+ $next [+2] bits:
+ 0 [+ScoPacketType.$size_in_bits] ScoPacketType packet_types
+ -- Bitmask of allowed packet types.
- $next [+1] ScoRetransmissionEffort retransmission_effort
+ $next [+1] ScoRetransmissionEffort retransmission_effort
struct EnhancedSetupSynchronousConnectionCommand:
@@ -1218,10 +1203,9 @@ struct WriteScanEnableCommand:
-- 7.3.18 Write Scan Enable command (v1.1) (BR/EDR)
-- HCI_Write_Scan_Enable
let hdr_size = hci.CommandHeader.$size_in_bytes
- 0 [+hdr_size] hci.CommandHeader header
- $next [+1] ScanEnableBits scan_enable
- -- Bit Mask of enabled scans. See enum class ScanEnableBits in this file
- -- for how to construct this bitfield.
+ 0 [+hdr_size] hci.CommandHeader header
+ $next [+1] bits:
+ 0 [+ScanEnableBits.$size_in_bits] ScanEnableBits scan_enable
struct ReadPageScanActivityCommand:
@@ -2509,14 +2493,16 @@ struct LESetExtendedScanParametersCommand:
-- HCI_LE_Set_Extended_Scan_Parameters
-- num_entries corresponds to the number of bits set in the |scanning_phys| field
let hdr_size = hci.CommandHeader.$size_in_bytes
- 0 [+hdr_size] hci.CommandHeader header
- $next [+1] LEOwnAddressType own_address_type
- $next [+1] LEScanFilterPolicy scanning_filter_policy
- $next [+1] LEScanPHYBits scanning_phys
+ 0 [+hdr_size] hci.CommandHeader header
+ $next [+1] LEOwnAddressType own_address_type
+ $next [+1] LEScanFilterPolicy scanning_filter_policy
+ $next [+1] bits:
+ 0 [+LEScanPHYBits.$size_in_bits] LEScanPHYBits scanning_phys
+
let single_entry_size = LESetExtendedScanParametersData.$size_in_bytes
let num_entries = (scanning_phys.le_1m ? 1 : 0)+(scanning_phys.le_coded ? 1 : 0)
let total_entries_size = num_entries*single_entry_size
- $next [+total_entries_size] LESetExtendedScanParametersData[num_entries] data
+ $next [+total_entries_size] LESetExtendedScanParametersData[num_entries] data
struct LESetExtendedScanEnableCommand:
@@ -2547,8 +2533,55 @@ struct LESetExtendedScanEnableCommand:
-- Time = N * 1.28 sec
-- Time Range: 1.28 s to 83,884.8 s
-# 7.8.66 LE Extended Create Connection command
-# HCI_LE_Extended_Create_Connection [v1] [v2]
+
+struct LEExtendedCreateConnectionV1:
+ -- 7.8.66 LE Extended Create Connection command version 1
+ -- HCI_LE_Extended_Create_Connection v1
+ let hdr_size = hci.CommandHeader.$size_in_bytes
+ 0 [+hdr_size] hci.CommandHeader header
+ $next [+1] hci.GenericEnableParam initiator_filter_policy
+ $next [+1] LEOwnAddressType own_address_type
+ $next [+1] hci.LEPeerAddressType peer_address_type
+ $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr peer_address
+ $next [+1] bits:
+ 0 [+LEInitiatingPHYBits.$size_in_bits] LEInitiatingPHYBits initiating_phys
+
+ let num_entries = (initiating_phys.le_1m ? 1 : 0)+(initiating_phys.le_2m ? 1 : 0)+(initiating_phys.le_coded ? 1 : 0)
+ $next [+2*num_entries] UInt:16[num_entries] scan_interval
+ -- Time interval from when the Controller started its last scan until it begins the subsequent
+ -- scan on the primary advertising physical channel.
+ -- Time = N × 0.625 ms
+ -- Time Range: 2.5 ms to 40.959375 s
+
+ $next [+2*num_entries] UInt:16[num_entries] scan_window
+ -- Duration of the scan on the primary advertising physical channel.
+ -- Time = N × 0.625 ms
+ -- Time Range: 2.5 ms to 40.959375 s
+
+ $next [+2*num_entries] UInt:16[num_entries] connection_interval_min
+ -- Time: N * 1.25 ms
+ -- Time Range: 7.5 ms to 4 s.
+
+ $next [+2*num_entries] UInt:16[num_entries] connection_interval_max
+ -- Time: N * 1.25 ms
+ -- Time Range: 7.5 ms to 4 s.
+
+ $next [+2*num_entries] UInt:16[num_entries] max_latency
+ -- Maximum Peripheral latency for the connection in number of connection events.
+
+ $next [+2*num_entries] UInt:16[num_entries] supervision_timeout
+ -- See Core Spec v5.3, Vol 6, Part B, Section 4.5.2.
+ -- Time: N * 10 ms
+ -- Time Range: 100 ms to 32 s
+
+ $next [+2*num_entries] UInt:16[num_entries] min_connection_event_length
+ -- Time: N * 0.625 ms
+
+ $next [+2*num_entries] UInt:16[num_entries] max_connection_event_length
+ -- Time: N * 0.625 ms
+
+# 7.8.66 LE Extended Create Connection command version 2
+# HCI_LE_Extended_Create_Connection v2
# TODO: b/265052417 - Definition needs to be added
@@ -2558,33 +2591,37 @@ struct LEPeriodicAdvertisingCreateSyncCommand:
let hdr_size = hci.CommandHeader.$size_in_bytes
- 0 [+hdr_size] hci.CommandHeader header
+ 0 [+hdr_size] hci.CommandHeader header
- $next [+1] LEPeriodicAdvertisingCreateSyncOptions options
+ $next [+1] bits:
- $next [+1] UInt advertising_sid
+ 0 [+LEPeriodicAdvertisingCreateSyncOptions.$size_in_bits] LEPeriodicAdvertisingCreateSyncOptions options
+
+ $next [+1] UInt advertising_sid
-- Advertising SID subfield in the ADI field used to identify the Periodic Advertising
[requires: 0x00 <= this <= 0x0F]
- $next [+1] LEPeriodicAdvertisingAddressType advertiser_address_type
+ $next [+1] hci.LEPeerAddressTypeNoAnon advertiser_address_type
- $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr advertiser_address
+ $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr advertiser_address
-- Public Device Address, Random Device Address, Public Identity Address, or Random (static)
-- Identity Address of the advertiser
- $next [+2] UInt skip
+ $next [+2] UInt skip
-- The maximum number of periodic advertising events that can be skipped after a successful
-- receive
[requires: 0x0000 <= this <= 0x01F3]
- $next [+2] UInt sync_timeout
+ $next [+2] UInt sync_timeout
-- Synchronization timeout for the periodic advertising.
-- Time = N * 10 ms
-- Time Range: 100 ms to 163.84 s
[requires: 0x000A <= this <= 0x4000]
- $next [+1] LEPeriodicAdvertisingSyncCTEType sync_cte_type
- -- Constant Tone Extension sync options
+ $next [+1] bits:
+
+ 0 [+LEPeriodicAdvertisingSyncCTEType.$size_in_bits] LEPeriodicAdvertisingSyncCTEType sync_cte_type
+ -- Constant Tone Extension sync options
struct LEPeriodicAdvertisingCreateSyncCancel:
@@ -2889,8 +2926,7 @@ struct LERemoveISODataPathCommand:
$next [+1] bits:
0 [+1] Flag remove_input_data_path
- $next [+1] Flag remove_output_data_path
- $next [+6] UInt padding
+ 1 [+1] Flag remove_output_data_path
# 7.8.111 LE ISO Transmit Test command
# 7.8.112 LE ISO Receive Test command
diff --git a/pw_bluetooth/public/pw_bluetooth/hci_common.emb b/pw_bluetooth/public/pw_bluetooth/hci_common.emb
index 211d80489..58287161a 100644
--- a/pw_bluetooth/public/pw_bluetooth/hci_common.emb
+++ b/pw_bluetooth/public/pw_bluetooth/hci_common.emb
@@ -339,7 +339,6 @@ enum LEPeerAddressTypeNoAnon:
[maximum_bits: 8]
PUBLIC = 0x00
RANDOM = 0x01
- ANONYMOUS = 0xFF
bits ClockOffset:
@@ -368,7 +367,25 @@ enum LESecondaryAdvertisingPHY:
enum LEAddressType:
- -- Possible values that can be reported for various |*_address_type| parameters in various LE packets.
+ -- Possible values that can be reported for various |*_address_type| parameters in various LE
+ -- packets.
+ [maximum_bits: 8]
+ PUBLIC = 0x00
+ -- Public Device Address (default)
+
+ RANDOM = 0x01
+ -- Random Device Address
+
+ PUBLIC_IDENTITY = 0x02
+ -- Public Identity Address (corresponds to Resolved Private Address)
+
+ RANDOM_IDENTITY = 0x03
+ -- Random (static) Identity Address (corresponds to Resolved Private Address)
+
+
+enum LEExtendedAddressType:
+ -- Possible values that can be reported for various |*_address_type| parameters in various LE
+ -- packets.
[maximum_bits: 8]
PUBLIC = 0x00
-- Public Device Address (default)
diff --git a/pw_bluetooth/public/pw_bluetooth/hci_events.emb b/pw_bluetooth/public/pw_bluetooth/hci_events.emb
index ea764d593..aa5f52931 100644
--- a/pw_bluetooth/public/pw_bluetooth/hci_events.emb
+++ b/pw_bluetooth/public/pw_bluetooth/hci_events.emb
@@ -63,7 +63,8 @@ enum LEAdvertisingDataStatus:
enum LEDirectAddressType:
- -- Possible values that can be reported for the |direct_address_type| parameter in LE Advertising Report events.
+ -- Possible values that can be reported for the |direct_address_type| parameter in LE Advertising
+ -- Report events.
[maximum_bits: 8]
PUBLIC = 0x00
-- Public Device Address
@@ -674,6 +675,12 @@ bits SupportedCommands(octet: UInt:8):
0 [+1] Flag le_extended_create_connection_v2
1 [+1] Flag le_set_periodic_advertising_parameters_v2
+
+enum LEChannelSelectionAlgorithm:
+ [maximum_bits: 8]
+ ALGORITHM1 = 0x00
+ ALGORITHM2 = 0x01
+
# ========================= HCI Event packets ===========================
# Core Spec v5.3 Vol 4, Part E, Section 7.7
@@ -1454,6 +1461,35 @@ struct LELongTermKeyRequestSubevent:
# TODO: b/265052417 - Definition needs to be added
+struct LEEnhancedConnectionCompleteSubeventV1:
+ -- 7.7.65.10 LE Enhanced Connection Complete event
+ -- HCI_LE_Enhanced_Connection_Complete
+ 0 [+LEMetaEvent.$size_in_bytes] LEMetaEvent le_meta_event
+ $next [+1] hci.StatusCode status
+ $next [+2] UInt connection_handle
+ [requires: 0x0000 <= this <= 0x0EFF]
+
+ $next [+1] hci.ConnectionRole role
+ $next [+1] hci.LEAddressType peer_address_type
+ $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr peer_address
+ $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr local_resolvable_private_address
+ $next [+hci.BdAddr.$size_in_bytes] hci.BdAddr peer_resolvable_private_address
+ $next [+2] UInt connection_interval
+ -- Time: N * 1.25 ms
+ -- Range: 7.5 ms to 4 s
+ [requires: 0x0006 <= this <= 0x0C80]
+
+ $next [+2] UInt peripheral_latency
+ [requires: 0x0000 <= this <= 0x01F3]
+
+ $next [+2] UInt supervision_timeout
+ -- Time: N * 10 ms
+ -- Range: 100 ms to 32 s
+ [requires: 0x000A <= this <= 0x0C80]
+
+ $next [+1] LEClockAccuracy central_clock_accuracy
+ -- Only valid for a peripheral. On a central, this parameter shall be set to 0x00.
+
# 7.7.65.10 LE Enhanced Connection Complete event
# HCI_LE_Enhanced_Connection_Complete
# TODO: b/265052417 - Definition needs to be added
@@ -1474,7 +1510,7 @@ struct LEExtendedAdvertisingReportData:
0 [+LEExtendedAdvertisingEventType.$size_in_bits] LEExtendedAdvertisingEventType event_type
- $next [+1] hci.LEAddressType address_type
+ $next [+1] hci.LEExtendedAddressType address_type
-- Address type of the advertiser.
$next [+hci.BdAddr.$size_in_bytes] hci.BdAddr address
@@ -1550,10 +1586,10 @@ struct LEExtendedAdvertisingReportSubevent(reports_size: UInt:8):
# TODO: b/265052417 - Definition needs to be added
-# 7.7.65.17 LE Scan Timeout event
-# HCI_LE_Scan_Timeout
-# TODO: b/265052417 - Definition needs to be added
-
+struct LEScanTimeoutSubevent:
+ -- 7.7.65.17 LE Scan Timeout event
+ -- HCI_LE_Scan_Timeout
+ 0 [+LEMetaEvent.$size_in_bytes] LEMetaEvent le_meta_event
# 7.7.65.18 LE Advertising Set Terminated event
# HCI_LE_Advertising_Set_Terminated
@@ -1565,10 +1601,15 @@ struct LEExtendedAdvertisingReportSubevent(reports_size: UInt:8):
# TODO: b/265052417 - Definition needs to be added
-# 7.7.65.20 LE Channel Selection Algorithm event
-# HCI_LE_Channel_Selection_Algorithm
-# TODO: b/265052417 - Definition needs to be added
+struct LEChannelSelectionAlgorithmSubevent:
+ -- 7.7.65.20 LE Channel Selection Algorithm event
+ -- HCI_LE_Channel_Selection_Algorithm
+ 0 [+LEMetaEvent.$size_in_bytes] LEMetaEvent le_meta_event
+ $next [+2] UInt connection_handle
+ -- Only the lower 12-bits are meaningful.
+ [requires: 0x0000 <= this <= 0x0EFF]
+ $next [+1] LEChannelSelectionAlgorithm channel_selection_algorithm
# 7.7.65.21 LE Connectionless IQ Report event
# HCI_LE_Connectionless_IQ_Report
diff --git a/pw_build/BUILD.bazel b/pw_build/BUILD.bazel
index cc7d1a145..6ee0e3890 100644
--- a/pw_build/BUILD.bazel
+++ b/pw_build/BUILD.bazel
@@ -72,9 +72,17 @@ pw_cc_test(
],
)
-# Special target that may be used instead of a cc_library as the default
-# condition in backend multiplexer select statements. This produces better
-# error messages than e.g. using an invalid label.
+# This empty library is used as a placeholder for label flags that need to
+# point to a library of some kind, but don't actually need the dependency to
+# amount to anything.
+cc_library(
+ name = "empty_cc_library",
+)
+
+# A special target used instead of a cc_library as the default condition in
+# backend multiplexer select statements to signal that a facade is in an
+# unconfigured state. This produces better error messages than e.g. using an
+# invalid label.
#
# If you're a user whose build errored out because a library transitively
# depended on this target: the platform you're targeting did not specify which
diff --git a/pw_build/BUILD.gn b/pw_build/BUILD.gn
index d8258baca..55b0f8305 100644
--- a/pw_build/BUILD.gn
+++ b/pw_build/BUILD.gn
@@ -18,6 +18,7 @@ import("$dir_pw_build/cc_blob_library.gni")
import("$dir_pw_build/python.gni")
import("$dir_pw_build/relative_source_file_names.gni")
import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
import("$dir_pw_toolchain/traits.gni")
import("$dir_pw_unit_test/test.gni")
import("target_types.gni")
@@ -138,10 +139,16 @@ config("extra_strict_warnings") {
# This config MUST NOT be used downstream to allow for warnings to be
# added in the future without breaking downstream.
config("internal_strict_warnings") {
- cflags = [
- "-Wextra-semi",
- "-Wswitch-enum",
- ]
+ cflags = [ "-Wswitch-enum" ]
+
+ # TODO: b/301262374 - Provide a better way to detect the compiler type.
+ if (defined(pw_toolchain_SCOPE.cc) &&
+ get_path_info(pw_toolchain_SCOPE.cc, "file") == "clang") {
+ cflags += [ "-Wextra-semi" ]
+ } else {
+ # TODO: b/306734552 - On GCC, this only works as a C++ flag.
+ cflags_cc = [ "-Wextra-semi" ]
+ }
# TODO: b/243069432 - Enable pedantic warnings on Windows when they pass.
if (host_os != "win") {
diff --git a/pw_build/CMakeLists.txt b/pw_build/CMakeLists.txt
index 62a310549..4d22be379 100644
--- a/pw_build/CMakeLists.txt
+++ b/pw_build/CMakeLists.txt
@@ -27,7 +27,6 @@ pw_add_library_generic(pw_build INTERFACE
$<$<CXX_COMPILER_ID:GNU>:-fdiagnostics-color=always>
PUBLIC_DEPS
pw_build.reduced_size
- pw_build.cpp17
)
if(ZEPHYR_PIGWEED_MODULE_DIR)
target_link_libraries(pw_build INTERFACE zephyr_interface)
diff --git a/pw_build/bazel.rst b/pw_build/bazel.rst
index bc3c0427a..0263eeaa4 100644
--- a/pw_build/bazel.rst
+++ b/pw_build/bazel.rst
@@ -1,3 +1,5 @@
+.. _module-pw_build-bazel:
+
Bazel
=====
Bazel is currently very experimental, and only builds for host and ARM Cortex-M
@@ -10,7 +12,8 @@ Wrapper rules
The common configuration for Bazel for all modules is in the ``pigweed.bzl``
file. The built-in Bazel rules ``cc_binary``, ``cc_library``, and ``cc_test``
are wrapped with ``pw_cc_binary``, ``pw_cc_library``, and ``pw_cc_test``.
-These wrappers add parameters to calls to the compiler and linker.
+
+.. _module-pw_build-bazel-pw_linker_script:
pw_linker_script
----------------
@@ -326,6 +329,24 @@ Example
} // namespace my::stuff
+Miscellaneous utilities
+-----------------------
+
+.. _module-pw_build-bazel-empty_cc_library:
+
+empty_cc_library
+^^^^^^^^^^^^^^^^
+This empty library is used as a placeholder for label flags that need to point
+to a library of some kind, but don't actually need the dependency to amount to
+anything.
+
+unspecified_backend
+^^^^^^^^^^^^^^^^^^^
+A special target used instead of a cc_library as the default condition in
+backend multiplexer select statements to signal that a facade is in an
+unconfigured state. This produces better error messages than e.g. using an
+invalid label.
+
Toolchains and platforms
------------------------
Pigweed provides clang-based host toolchains for Linux and Mac Arm gcc
diff --git a/pw_build/bazel_internal/BUILD.bazel b/pw_build/bazel_internal/BUILD.bazel
index ccf02ce38..822ee7da9 100644
--- a/pw_build/bazel_internal/BUILD.bazel
+++ b/pw_build/bazel_internal/BUILD.bazel
@@ -34,6 +34,7 @@ pw_linker_script(
cc_binary(
name = "test_linker_script",
srcs = ["test.cc"],
+ copts = ["-Wno-unused-variable"],
# Only compatible with platforms that support linker scripts.
target_compatible_with = select({
"@platforms//os:macos": ["@platforms//:incompatible"],
@@ -53,6 +54,7 @@ cc_library(
cc_binary(
name = "test_transitive_linker_script",
srcs = ["test.cc"],
+ copts = ["-Wno-unused-variable"],
# Only compatible with platforms that support linker scripts.
target_compatible_with = select({
"@platforms//os:macos": ["@platforms//:incompatible"],
@@ -67,6 +69,7 @@ cc_binary(
name = "test_direct_linker_script",
srcs = ["test.cc"],
additional_linker_inputs = [":linker_script_test"],
+ copts = ["-Wno-unused-variable"],
linkopts = ["-T $(location :linker_script_test)"],
# Only compatible with platforms that support linker scripts.
target_compatible_with = select({
diff --git a/pw_build/bazel_internal/pigweed_internal.bzl b/pw_build/bazel_internal/pigweed_internal.bzl
index 10b13afa9..b8b4afb67 100644
--- a/pw_build/bazel_internal/pigweed_internal.bzl
+++ b/pw_build/bazel_internal/pigweed_internal.bzl
@@ -17,60 +17,6 @@
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
-DEBUGGING = [
- "-g",
-]
-
-# Standard compiler flags to reduce output binary size.
-REDUCED_SIZE_COPTS = [
- "-fno-common",
- "-fno-exceptions",
- "-ffunction-sections",
- "-fdata-sections",
-]
-
-STRICT_WARNINGS_COPTS = [
- "-Wall",
- "-Wextra",
- # Make all warnings errors, except for the exemptions below.
- "-Werror",
- "-Wno-error=cpp", # preprocessor #warning statement
- "-Wno-error=deprecated-declarations", # [[deprecated]] attribute
-]
-
-PW_DEFAULT_COPTS = (
- DEBUGGING +
- REDUCED_SIZE_COPTS +
- STRICT_WARNINGS_COPTS
-)
-
-KYTHE_COPTS = [
- "-Wno-unknown-warning-option",
-]
-
-def add_defaults(kwargs):
- """Adds default arguments suitable for both C and C++ code to kwargs.
-
- Args:
- kwargs: cc_* arguments to be modified.
- """
-
- copts = PW_DEFAULT_COPTS + kwargs.get("copts", [])
- kwargs["copts"] = select({
- "@pigweed//pw_build:kythe": copts + KYTHE_COPTS,
- "//conditions:default": copts,
- })
-
- # Set linkstatic to avoid building .so files.
- kwargs["linkstatic"] = True
-
- kwargs.setdefault("features", [])
-
- # Crosstool--adding this line to features disables header modules, which
- # don't work with -fno-rtti. Note: this is not a command-line argument,
- # it's "minus use_header_modules".
- kwargs["features"].append("-use_header_modules")
-
def _print_platform_impl(_, ctx):
if hasattr(ctx.rule.attr, "constraint_values"):
for cv in ctx.rule.attr.constraint_values:
diff --git a/pw_build/defaults.gni b/pw_build/defaults.gni
index 3d28b7fc9..f1eb0aa71 100644
--- a/pw_build/defaults.gni
+++ b/pw_build/defaults.gni
@@ -77,7 +77,7 @@ pigweed_default_configs = [
# compatibilty. Using these is not recommended; prefer to use the pw_* target
# types directly.
pw_build_defaults = {
- configs = pw_build_INTERNAL_DEFAULTS.default_configs + pigweed_default_configs
+ configs = pw_build_INTERNAL_DEFAULTS.default_configs
public_deps = pw_build_INTERNAL_DEFAULTS.default_public_deps
if (pw_build_INTERNAL_DEFAULTS.remove_default_configs != []) {
# Add them first to ensure they are present to be removed.
diff --git a/pw_build/generated_pigweed_modules_lists.gni b/pw_build/generated_pigweed_modules_lists.gni
index eb2ac3365..e2ce68be8 100644
--- a/pw_build/generated_pigweed_modules_lists.gni
+++ b/pw_build/generated_pigweed_modules_lists.gni
@@ -80,6 +80,7 @@ declare_args() {
dir_pw_emu = get_path_info("../pw_emu", "abspath")
dir_pw_env_setup = get_path_info("../pw_env_setup", "abspath")
dir_pw_file = get_path_info("../pw_file", "abspath")
+ dir_pw_format = get_path_info("../pw_format", "abspath")
dir_pw_function = get_path_info("../pw_function", "abspath")
dir_pw_fuzzer = get_path_info("../pw_fuzzer", "abspath")
dir_pw_hdlc = get_path_info("../pw_hdlc", "abspath")
@@ -96,6 +97,7 @@ declare_args() {
dir_pw_intrusive_ptr = get_path_info("../pw_intrusive_ptr", "abspath")
dir_pw_kvs = get_path_info("../pw_kvs", "abspath")
dir_pw_libc = get_path_info("../pw_libc", "abspath")
+ dir_pw_libcxx = get_path_info("../pw_libcxx", "abspath")
dir_pw_log = get_path_info("../pw_log", "abspath")
dir_pw_log_android = get_path_info("../pw_log_android", "abspath")
dir_pw_log_basic = get_path_info("../pw_log_basic", "abspath")
@@ -110,6 +112,7 @@ declare_args() {
dir_pw_minimal_cpp_stdlib =
get_path_info("../pw_minimal_cpp_stdlib", "abspath")
dir_pw_module = get_path_info("../pw_module", "abspath")
+ dir_pw_multibuf = get_path_info("../pw_multibuf", "abspath")
dir_pw_multisink = get_path_info("../pw_multisink", "abspath")
dir_pw_package = get_path_info("../pw_package", "abspath")
dir_pw_perf_test = get_path_info("../pw_perf_test", "abspath")
@@ -244,6 +247,7 @@ declare_args() {
dir_pw_emu,
dir_pw_env_setup,
dir_pw_file,
+ dir_pw_format,
dir_pw_function,
dir_pw_fuzzer,
dir_pw_hdlc,
@@ -259,6 +263,7 @@ declare_args() {
dir_pw_intrusive_ptr,
dir_pw_kvs,
dir_pw_libc,
+ dir_pw_libcxx,
dir_pw_log,
dir_pw_log_android,
dir_pw_log_basic,
@@ -272,6 +277,7 @@ declare_args() {
dir_pw_metric,
dir_pw_minimal_cpp_stdlib,
dir_pw_module,
+ dir_pw_multibuf,
dir_pw_multisink,
dir_pw_package,
dir_pw_perf_test,
@@ -396,6 +402,7 @@ declare_args() {
"$dir_pw_emu:tests",
"$dir_pw_env_setup:tests",
"$dir_pw_file:tests",
+ "$dir_pw_format:tests",
"$dir_pw_function:tests",
"$dir_pw_fuzzer:tests",
"$dir_pw_hdlc:tests",
@@ -411,6 +418,7 @@ declare_args() {
"$dir_pw_intrusive_ptr:tests",
"$dir_pw_kvs:tests",
"$dir_pw_libc:tests",
+ "$dir_pw_libcxx:tests",
"$dir_pw_log:tests",
"$dir_pw_log_android:tests",
"$dir_pw_log_basic:tests",
@@ -424,6 +432,7 @@ declare_args() {
"$dir_pw_metric:tests",
"$dir_pw_minimal_cpp_stdlib:tests",
"$dir_pw_module:tests",
+ "$dir_pw_multibuf:tests",
"$dir_pw_multisink:tests",
"$dir_pw_package:tests",
"$dir_pw_perf_test:tests",
@@ -548,6 +557,7 @@ declare_args() {
"$dir_pw_emu:docs",
"$dir_pw_env_setup:docs",
"$dir_pw_file:docs",
+ "$dir_pw_format:docs",
"$dir_pw_function:docs",
"$dir_pw_fuzzer:docs",
"$dir_pw_hdlc:docs",
@@ -563,6 +573,7 @@ declare_args() {
"$dir_pw_intrusive_ptr:docs",
"$dir_pw_kvs:docs",
"$dir_pw_libc:docs",
+ "$dir_pw_libcxx:docs",
"$dir_pw_log:docs",
"$dir_pw_log_android:docs",
"$dir_pw_log_basic:docs",
@@ -576,6 +587,7 @@ declare_args() {
"$dir_pw_metric:docs",
"$dir_pw_minimal_cpp_stdlib:docs",
"$dir_pw_module:docs",
+ "$dir_pw_multibuf:docs",
"$dir_pw_multisink:docs",
"$dir_pw_package:docs",
"$dir_pw_perf_test:docs",
diff --git a/pw_build/gn_internal/build_target.gni b/pw_build/gn_internal/build_target.gni
index 51c30e736..10b8ce25a 100644
--- a/pw_build/gn_internal/build_target.gni
+++ b/pw_build/gn_internal/build_target.gni
@@ -120,11 +120,8 @@ template("pw_internal_build_target") {
}
}
- # Eventually, pigweed_default_configs should not be added here. Instead,
- # pigweed_default_configs should live inside of the toolchain declaration.
_default_configs =
- filter_exclude(pigweed_default_configs +
- pw_build_INTERNAL_DEFAULTS.default_configs,
+ filter_exclude(pw_build_INTERNAL_DEFAULTS.default_configs,
pw_build_INTERNAL_DEFAULTS.remove_default_configs)
if (!defined(remove_configs)) {
remove_configs = []
diff --git a/pw_build/pigweed.bzl b/pw_build/pigweed.bzl
index dd8cc2f32..2192445c6 100644
--- a/pw_build/pigweed.bzl
+++ b/pw_build/pigweed.bzl
@@ -18,8 +18,6 @@ load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_c
load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
load(
"//pw_build/bazel_internal:pigweed_internal.bzl",
- "PW_DEFAULT_COPTS",
- _add_defaults = "add_defaults",
_compile_cc = "compile_cc",
)
@@ -32,12 +30,8 @@ FUZZTEST_OPTS = [
def pw_cc_binary(**kwargs):
"""Wrapper for cc_binary providing some defaults.
- Specifically, this wrapper,
-
- * Adds default copts.
- * Adds a dep on the pw_assert backend.
- * Sets "linkstatic" to True.
- * Disables header modules (via the feature -use_header_modules).
+ Specifically, this wrapper adds deps on backend_impl targets for pw_assert
+ and pw_log.
Args:
**kwargs: Passed to cc_binary.
@@ -48,22 +42,18 @@ def pw_cc_binary(**kwargs):
# way to handle the facades without introducing a circular dependency into
# the build.
kwargs["deps"] = kwargs["deps"] + ["@pigweed//targets:pw_assert_backend_impl"]
- _add_defaults(kwargs)
+ kwargs["deps"] = kwargs["deps"] + ["@pigweed//pw_log:backend_impl"]
native.cc_binary(**kwargs)
def pw_cc_library(**kwargs):
- """Wrapper for cc_library providing some defaults.
-
- Specifically, this wrapper,
+ """Wrapper for cc_library.
- * Adds default copts.
- * Sets "linkstatic" to True.
- * Disables header modules (via the feature -use_header_modules).
+ TODO: b/267498492 - This wrapper no longer does anything. Remove it once
+ all projects have been migrated off of it.
Args:
**kwargs: Passed to cc_library.
"""
- _add_defaults(kwargs)
native.cc_library(**kwargs)
def pw_cc_test(**kwargs):
@@ -71,11 +61,8 @@ def pw_cc_test(**kwargs):
Specifically, this wrapper,
- * Adds default copts.
* Adds a dep on the pw_assert backend.
* Adds a dep on //pw_unit_test:simple_printing_main
- * Sets "linkstatic" to True.
- * Disables header modules (via the feature -use_header_modules).
In addition, a .lib target is created that's a cc_library with the same
kwargs. Such library targets can be used as dependencies of firmware images
@@ -94,7 +81,6 @@ def pw_cc_test(**kwargs):
# way to handle the facades without introducing a circular dependency into
# the build.
kwargs["deps"] = kwargs["deps"] + ["@pigweed//targets:pw_assert_backend_impl"]
- _add_defaults(kwargs)
# Some tests may include FuzzTest, which includes headers that trigger
# warnings. This check must be done here and not in `add_defaults`, since
@@ -135,11 +121,8 @@ def pw_cc_perf_test(**kwargs):
This macro produces a cc_binary and,
- * Adds default copts.
* Adds a dep on the pw_assert backend.
* Adds a dep on //pw_perf_test:logging_main
- * Sets "linkstatic" to True.
- * Disables header modules (via the feature -use_header_modules).
Args:
**kwargs: Passed to cc_binary.
@@ -147,7 +130,6 @@ def pw_cc_perf_test(**kwargs):
kwargs["deps"] = kwargs.get("deps", []) + \
["@pigweed//pw_perf_test:logging_main"]
kwargs["deps"] = kwargs["deps"] + ["@pigweed//targets:pw_assert_backend_impl"]
- _add_defaults(kwargs)
native.cc_binary(**kwargs)
def pw_cc_facade(**kwargs):
@@ -160,7 +142,6 @@ def pw_cc_facade(**kwargs):
if "srcs" in kwargs.keys():
fail("'srcs' attribute does not exist in pw_cc_facade, please use \
main implementing target.")
- _add_defaults(kwargs)
native.cc_library(**kwargs)
def host_backend_alias(name, backend):
@@ -261,7 +242,6 @@ def _pw_cc_blob_library_impl(ctx):
deps = ctx.attr.deps,
includes = [ctx.bin_dir.path + "/" + ctx.label.package],
defines = [],
- user_compile_flags = PW_DEFAULT_COPTS,
)
pw_cc_blob_library = rule(
diff --git a/pw_build/py/generate_cc_blob_library_test.py b/pw_build/py/generate_cc_blob_library_test.py
index 6bdf311fb..33709c9d7 100644
--- a/pw_build/py/generate_cc_blob_library_test.py
+++ b/pw_build/py/generate_cc_blob_library_test.py
@@ -261,8 +261,8 @@ class TestSourceFromBlobs(unittest.TestCase):
'\n'
f'alignas(64) {FOO_BLOB}'
'\n'
- 'PW_PLACE_IN_SECTION(".abc")\n'
- f'alignas(int) {BAR_BLOB}'
+ 'alignas(int) PW_PLACE_IN_SECTION(".abc")\n'
+ f'{BAR_BLOB}'
)
self.assertEqual(expected_source, source)
diff --git a/pw_build/py/pw_build/generate_cc_blob_library.py b/pw_build/py/pw_build/generate_cc_blob_library.py
index 65b799eaa..958c9da8a 100644
--- a/pw_build/py/pw_build/generate_cc_blob_library.py
+++ b/pw_build/py/pw_build/generate_cc_blob_library.py
@@ -78,8 +78,9 @@ BLOB_DECLARATION_TEMPLATE = Template(
LINKER_SECTION_TEMPLATE = Template('PW_PLACE_IN_SECTION("${linker_section}")\n')
BLOB_DEFINITION_MULTI_LINE = Template(
- '\n${section_attr}'
- '${alignas}constexpr std::array<std::byte, ${size_bytes}> ${symbol_name}'
+ '\n${alignas}'
+ '${section_attr}constexpr std::array<std::byte, ${size_bytes}>'
+ ' ${symbol_name}'
' = {\n${bytes_lines}\n};\n'
)
diff --git a/pw_build/py/pw_build/python_runner.py b/pw_build/py/pw_build/python_runner.py
index 0923bb260..8fcdb2eaa 100755
--- a/pw_build/py/pw_build/python_runner.py
+++ b/pw_build/py/pw_build/python_runner.py
@@ -150,8 +150,10 @@ def acquire_lock(lockfile: Path, exclusive: bool):
start_time = time.monotonic()
if exclusive:
+ # pylint: disable-next=used-before-assignment
lock_type = fcntl.LOCK_EX # type: ignore[name-defined]
else:
+ # pylint: disable-next=used-before-assignment
lock_type = fcntl.LOCK_SH # type: ignore[name-defined]
fd = os.open(lockfile, os.O_RDWR | os.O_CREAT)
@@ -165,9 +167,11 @@ def acquire_lock(lockfile: Path, exclusive: bool):
backoff = 1
while time.monotonic() - start_time < _LOCK_ACQUISITION_TIMEOUT:
try:
+ # pylint: disable=used-before-assignment
fcntl.flock( # type: ignore[name-defined]
fd, lock_type | fcntl.LOCK_NB # type: ignore[name-defined]
)
+ # pylint: enable=used-before-assignment
return # Lock acquired!
except BlockingIOError:
pass # Keep waiting.
diff --git a/pw_build/py/pw_build/wrap_ninja.py b/pw_build/py/pw_build/wrap_ninja.py
index e7a0f4911..56d37f740 100644
--- a/pw_build/py/pw_build/wrap_ninja.py
+++ b/pw_build/py/pw_build/wrap_ninja.py
@@ -249,7 +249,7 @@ class Ninja:
self.lock = threading.Lock()
# Launch ninja and configure pseudo-tty.
- # pylint: disable-next=no-member,undefined-variable
+ # pylint: disable-next=no-member,undefined-variable,used-before-assignment
ptty_parent, ptty_child = pty.openpty() # type: ignore
ptty_file = os.fdopen(ptty_parent, 'r')
env = dict(os.environ)
diff --git a/pw_build/py/setup.cfg b/pw_build/py/setup.cfg
index f2d92ef20..16fecaba2 100644
--- a/pw_build/py/setup.cfg
+++ b/pw_build/py/setup.cfg
@@ -31,6 +31,7 @@ install_requires =
mypy>=0.971
pylint>=2.9.3
pip-tools>=6.12.3
+ parameterized
[options.entry_points]
console_scripts =
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 7820da67a..4d5f562e6 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -76,11 +76,6 @@ template("_pw_python_static_analysis_mypy") {
"--pretty",
"--show-error-codes",
- # TODO: b/265836842 - Namespace packages are enabled by default starting in
- # mypy 0.991. This caused problems in some configurations, so return
- # to the prior behavior for now.
- "--no-namespace-packages",
-
# Use a mypy cache dir for this target only to avoid cache conflicts in
# parallel mypy invocations.
"--cache-dir",
diff --git a/pw_bytes/BUILD.bazel b/pw_bytes/BUILD.bazel
index 572528131..9a8ded187 100644
--- a/pw_bytes/BUILD.bazel
+++ b/pw_bytes/BUILD.bazel
@@ -33,6 +33,7 @@ pw_cc_library(
"public/pw_bytes/byte_builder.h",
"public/pw_bytes/endian.h",
"public/pw_bytes/span.h",
+ "public/pw_bytes/suffix.h",
"public/pw_bytes/units.h",
],
includes = ["public"],
@@ -99,6 +100,14 @@ pw_cc_test(
)
pw_cc_test(
+ name = "suffix_test",
+ srcs = ["suffix_test.cc"],
+ deps = [
+ ":pw_bytes",
+ ],
+)
+
+pw_cc_test(
name = "units_test",
srcs = ["units_test.cc"],
deps = [
diff --git a/pw_bytes/BUILD.gn b/pw_bytes/BUILD.gn
index 8d6702dff..5aba51844 100644
--- a/pw_bytes/BUILD.gn
+++ b/pw_bytes/BUILD.gn
@@ -32,6 +32,7 @@ pw_source_set("pw_bytes") {
"public/pw_bytes/byte_builder.h",
"public/pw_bytes/endian.h",
"public/pw_bytes/span.h",
+ "public/pw_bytes/suffix.h",
"public/pw_bytes/units.h",
]
sources = [ "byte_builder.cc" ]
@@ -57,6 +58,7 @@ pw_test_group("tests") {
":bit_test",
":byte_builder_test",
":endian_test",
+ ":suffix_test",
":units_test",
]
group_deps = [
@@ -90,6 +92,11 @@ pw_test("endian_test") {
sources = [ "endian_test.cc" ]
}
+pw_test("suffix_test") {
+ deps = [ ":pw_bytes" ]
+ sources = [ "suffix_test.cc" ]
+}
+
pw_test("units_test") {
deps = [ ":pw_bytes" ]
sources = [ "units_test.cc" ]
diff --git a/pw_bytes/CMakeLists.txt b/pw_bytes/CMakeLists.txt
index c0e0b7bc5..8abe481d5 100644
--- a/pw_bytes/CMakeLists.txt
+++ b/pw_bytes/CMakeLists.txt
@@ -21,6 +21,7 @@ pw_add_library(pw_bytes STATIC
public/pw_bytes/byte_builder.h
public/pw_bytes/endian.h
public/pw_bytes/span.h
+ public/pw_bytes/suffix.h
public/pw_bytes/units.h
PUBLIC_INCLUDES
public
@@ -94,6 +95,16 @@ pw_add_test(pw_bytes.endian_test
pw_bytes
)
+pw_add_test(pw_bytes.suffix_test
+ SOURCES
+ suffix_test.cc
+ PRIVATE_DEPS
+ pw_bytes
+ GROUPS
+ modules
+ pw_bytes
+)
+
pw_add_test(pw_bytes.units_test
SOURCES
units_test.cc
diff --git a/pw_bytes/docs.rst b/pw_bytes/docs.rst
index b2eef2414..82c870bdd 100644
--- a/pw_bytes/docs.rst
+++ b/pw_bytes/docs.rst
@@ -58,6 +58,13 @@ pw_bytes/endian.h
=================
Functions for converting the endianness of integral values.
+pw_bytes/suffix.h
+=================
+This module exports a single ``_b`` literal, making it easier to create
+``std::byte`` values for tests.
+
+.. cpp:function:: constexpr std::byte operator"" _b(unsigned long long value)
+
pw_bytes/units.h
================
Constants, functions and user-defined literals for specifying a number of bytes
diff --git a/pw_bytes/public/pw_bytes/suffix.h b/pw_bytes/public/pw_bytes/suffix.h
new file mode 100644
index 000000000..38c6d1002
--- /dev/null
+++ b/pw_bytes/public/pw_bytes/suffix.h
@@ -0,0 +1,33 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <cstddef>
+
+namespace pw {
+
+/// Returns a ``std::byte`` when used as a ``_b`` suffix.
+///
+/// This is useful for writing byte literals, particularly in tests.
+/// To use, add ``using ::pw::operator""_b;`` and then use like ``5_b``
+/// in order to create a ``std::byte`` with the contents ``5``.
+///
+/// This should not be used in header files, as it requires a ``using``
+/// declaration that will be publicly exported at whatever level it is
+/// used.
+constexpr std::byte operator"" _b(unsigned long long value) {
+ return std::byte(value);
+}
+
+} // namespace pw
diff --git a/pw_bytes/suffix_test.cc b/pw_bytes/suffix_test.cc
new file mode 100644
index 000000000..5825c0326
--- /dev/null
+++ b/pw_bytes/suffix_test.cc
@@ -0,0 +1,28 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_bytes/suffix.h"
+
+#include "gtest/gtest.h"
+
+namespace {
+
+using ::pw::operator""_b;
+
+TEST(Suffix, ReturnsByte) {
+ std::byte x = 5_b;
+ EXPECT_EQ(x, std::byte(5));
+}
+
+} // namespace
diff --git a/pw_cli/py/pw_cli/color.py b/pw_cli/py/pw_cli/color.py
index 5651fa9b7..6b9074bf5 100644
--- a/pw_cli/py/pw_cli/color.py
+++ b/pw_cli/py/pw_cli/color.py
@@ -78,4 +78,11 @@ def colors(enabled: Optional[bool] = None) -> Union[_Color, _NoColor]:
kernel32 = ctypes.windll.kernel32 # type: ignore
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
+ # These are semi-standard ways to turn colors off or on for many projects.
+ # See https://bixense.com/clicolors/ and https://no-color.org/ for more.
+ if 'NO_COLOR' in os.environ:
+ enabled = False
+ elif 'CLICOLOR_FORCE' in os.environ:
+ enabled = True
+
return _Color() if enabled else _NoColor()
diff --git a/pw_cli/py/pw_cli/envparse.py b/pw_cli/py/pw_cli/envparse.py
index b7cccbba8..182936f2d 100644
--- a/pw_cli/py/pw_cli/envparse.py
+++ b/pw_cli/py/pw_cli/envparse.py
@@ -22,6 +22,7 @@ from typing import (
Generic,
IO,
List,
+ Literal,
Mapping,
Optional,
TypeVar,
@@ -205,9 +206,7 @@ def strict_bool(value: str) -> bool:
)
-# TODO(mohrr) Switch to Literal when no longer supporting Python 3.7.
-# OpenMode = Literal['r', 'rb', 'w', 'wb']
-OpenMode = str
+OpenMode = Literal['r', 'rb', 'w', 'wb']
class FileType:
diff --git a/pw_console/docs.rst b/pw_console/docs.rst
index 0c7ffc3e0..b4f2a19d5 100644
--- a/pw_console/docs.rst
+++ b/pw_console/docs.rst
@@ -1,12 +1,14 @@
.. _module-pw_console:
-----------
+==========
pw_console
-----------
-
-:bdg-primary:`host`
-:bdg-secondary:`Python`
-:bdg-success:`stable`
+==========
+.. pigweed-module::
+ :name: pw_console
+ :tagline: Multi-purpose pluggable interactive console for dev & manufacturing
+ :status: stable
+ :languages: Python
+ :code-size-impact: N/A (host) but works best with pw_rpc on device
The Pigweed Console provides a Python repl (read eval print loop) using
`ptpython`_ and a log message viewer in a single-window terminal based
@@ -15,8 +17,9 @@ interface. It is designed to be a replacement for `IPython's embed()`_ function.
.. figure:: images/pw_system_boot.png
:alt: Pigweed Console screenshot with serial debug log messages.
+--------
Features
-========
+--------
``pw_console`` aims to be a complete solution for interacting with hardware
devices using :ref:`module-pw_rpc` over a :ref:`module-pw_hdlc` transport.
@@ -33,18 +36,18 @@ devices using :ref:`module-pw_rpc` over a :ref:`module-pw_hdlc` transport.
- Log viewer with searching and filtering.
+------------
Contributing
-============
-
+------------
- All code submissions to ``pw_console`` require running the
:ref:`module-pw_console-testing`.
- Commit messages should include a ``Testing:`` line with the steps that were
manually run.
+------
Guides
-======
-
+------
.. toctree::
:maxdepth: 1
@@ -54,7 +57,6 @@ Guides
testing
internals
-
.. _IPython's embed(): https://ipython.readthedocs.io/en/stable/interactive/reference.html#embedding
.. _IPython: https://ipython.readthedocs.io/
.. _prompt_toolkit: https://python-prompt-toolkit.readthedocs.io/
diff --git a/pw_console/embedding.rst b/pw_console/embedding.rst
index a5d896c04..65724e9fc 100644
--- a/pw_console/embedding.rst
+++ b/pw_console/embedding.rst
@@ -3,6 +3,9 @@
===============
Embedding Guide
===============
+.. pigweed-module-subpage::
+ :name: pw_console
+ :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing
-------------
Using embed()
@@ -113,7 +116,16 @@ Logging data with sockets
from pw_console.socket_client import SocketClientWithLogging
+ # Name resolution with explicit port
serial_device = SocketClientWithLogging('localhost:1234')
+ # Name resolution with default port.
+ serial_device = SocketClientWithLogging('pigweed.dev')
+ # Link-local IPv6 address with explicit port.
+ serial_device = SocketClientWithLogging('[fe80::100%enp1s0]:1234')
+ # Link-local IPv6 address with default port.
+ serial_device = SocketClientWithLogging('[fe80::100%enp1s0]')
+ # IPv4 address with port.
+ serial_device = SocketClientWithLogging('1.2.3.4:5678')
.. tip::
The ``SocketClient`` takes an optional callback called when a disconnect is
diff --git a/pw_console/internals.rst b/pw_console/internals.rst
index c204a1e3f..8ce8efcf7 100644
--- a/pw_console/internals.rst
+++ b/pw_console/internals.rst
@@ -2,6 +2,9 @@
Internal Design
===============
+.. pigweed-module-subpage::
+ :name: pw_console
+ :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing
Threads and Event Loops
-----------------------
diff --git a/pw_console/plugins.rst b/pw_console/plugins.rst
index c069ea1ba..acd5d48af 100644
--- a/pw_console/plugins.rst
+++ b/pw_console/plugins.rst
@@ -3,6 +3,10 @@
============
Plugin Guide
============
+.. pigweed-module-subpage::
+ :name: pw_console
+ :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing
+
Pigweed Console supports extending the user interface with custom widgets. For
example: Toolbars that display device information and provide buttons for
interacting with the device.
diff --git a/pw_console/py/BUILD.bazel b/pw_console/py/BUILD.bazel
index 798b3354c..9fdeebd91 100644
--- a/pw_console/py/BUILD.bazel
+++ b/pw_console/py/BUILD.bazel
@@ -201,6 +201,17 @@ py_test(
)
py_test(
+ name = "socket_client_test",
+ size = "small",
+ srcs = [
+ "socket_client_test.py",
+ ],
+ deps = [
+ ":pw_console",
+ ],
+)
+
+py_test(
name = "repl_pane_test",
size = "small",
srcs = [
diff --git a/pw_console/py/BUILD.gn b/pw_console/py/BUILD.gn
index 2cc6d1866..3683004d5 100644
--- a/pw_console/py/BUILD.gn
+++ b/pw_console/py/BUILD.gn
@@ -87,6 +87,7 @@ pw_python_package("py") {
"log_store_test.py",
"log_view_test.py",
"repl_pane_test.py",
+ "socket_client_test.py",
"table_test.py",
"text_formatting_test.py",
"window_manager_test.py",
diff --git a/pw_console/py/pw_console/docs/user_guide.rst b/pw_console/py/pw_console/docs/user_guide.rst
index a1e1357af..45cf7cc41 100644
--- a/pw_console/py/pw_console/docs/user_guide.rst
+++ b/pw_console/py/pw_console/docs/user_guide.rst
@@ -2,6 +2,9 @@
User Guide
==========
+.. pigweed-module-subpage::
+ :name: pw_console
+ :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing
.. tip::
diff --git a/pw_console/py/pw_console/socket_client.py b/pw_console/py/pw_console/socket_client.py
index 41a1c66c4..5344c3199 100644
--- a/pw_console/py/pw_console/socket_client.py
+++ b/pw_console/py/pw_console/socket_client.py
@@ -17,6 +17,7 @@ from __future__ import annotations
from typing import Callable, Optional, TYPE_CHECKING, Tuple, Union
import errno
+import re
import socket
from pw_console.plugins.bandwidth_toolbar import SerialBandwidthTracker
@@ -33,43 +34,125 @@ class SocketClient:
DEFAULT_SOCKET_PORT = 33000
PW_RPC_MAX_PACKET_SIZE = 256
+ _InitArgsType = Tuple[
+ socket.AddressFamily, int # pylint: disable=no-member
+ ]
+ # Can be a string, (address, port) for AF_INET or (address, port, flowinfo,
+ # scope_id) AF_INET6.
+ _AddressType = Union[str, Tuple[str, int], Tuple[str, int, int, int]]
+
def __init__(
self,
config: str,
on_disconnect: Optional[Callable[[SocketClient], None]] = None,
):
- self._connection_type: int
- self._interface: Union[str, Tuple[str, int]]
+ """Creates a socket connection.
+
+ Args:
+ config: The socket configuration. Accepted values and formats are:
+ 'default' - uses the default configuration (localhost:33000)
+ 'address:port' - An IPv4 address and port.
+ 'address' - An IPv4 address. Uses default port 33000.
+ '[address]:port' - An IPv6 address and port.
+ '[address]' - An IPv6 address. Uses default port 33000.
+ 'file:path_to_file' - A Unix socket at ``path_to_file``.
+ In the formats above,``address`` can be an actual address or a name
+ that resolves to an address through name-resolution.
+ on_disconnect: An optional callback called when the socket
+ disconnects.
+
+ Raises:
+ TypeError: The type of socket is not supported.
+ ValueError: The socket configuration is invalid.
+ """
+ self.socket: socket.socket
+ (
+ self._socket_init_args,
+ self._address,
+ ) = SocketClient._parse_socket_config(config)
+ self._on_disconnect = on_disconnect
+ self._connected = False
+ self.connect()
+
+ @staticmethod
+ def _parse_socket_config(
+ config: str,
+ ) -> Tuple[SocketClient._InitArgsType, SocketClient._AddressType]:
+ """Sets the variables used to create a socket given a config string.
+
+ Raises:
+ TypeError: The type of socket is not supported.
+ ValueError: The socket configuration is invalid.
+ """
+ init_args: SocketClient._InitArgsType
+ address: SocketClient._AddressType
+
+ # Check if this is using the default settings.
if config == 'default':
- self._connection_type = socket.AF_INET6
- self._interface = (
- self.DEFAULT_SOCKET_SERVER,
- self.DEFAULT_SOCKET_PORT,
+ init_args = socket.AF_INET6, socket.SOCK_STREAM
+ address = (
+ SocketClient.DEFAULT_SOCKET_SERVER,
+ SocketClient.DEFAULT_SOCKET_PORT,
)
- else:
- socket_server, socket_port_or_file = config.split(':')
- if socket_server == self.FILE_SOCKET_SERVER:
- # Unix socket support is available on Windows 10 since April
- # 2018. However, there is no Python support on Windows yet.
- # See https://bugs.python.org/issue33408 for more information.
- if not hasattr(socket, 'AF_UNIX'):
- raise TypeError(
- 'Unix sockets are not supported in this environment.'
- )
- self._connection_type = (
- socket.AF_UNIX # pylint: disable=no-member
+ return init_args, address
+
+ # Check if this is a UNIX socket.
+ unix_socket_file_setting = f'{SocketClient.FILE_SOCKET_SERVER}:'
+ if config.startswith(unix_socket_file_setting):
+ # Unix socket support is available on Windows 10 since April
+ # 2018. However, there is no Python support on Windows yet.
+ # See https://bugs.python.org/issue33408 for more information.
+ if not hasattr(socket, 'AF_UNIX'):
+ raise TypeError(
+ 'Unix sockets are not supported in this environment.'
)
- self._interface = socket_port_or_file
- else:
- self._connection_type = socket.AF_INET6
- self._interface = (socket_server, int(socket_port_or_file))
+ init_args = (
+ socket.AF_UNIX, # pylint: disable=no-member
+ socket.SOCK_STREAM,
+ )
+ address = config[len(unix_socket_file_setting) :]
+ return init_args, address
+
+ # Search for IPv4 or IPv6 address or name and port.
+ # First, try to capture an IPv6 address as anything inside []. If there
+ # are no [] capture the IPv4 address. Lastly, capture the port as the
+ # numbers after :, if any.
+ match = re.match(
+ r'(\[(?P<ipv6_addr>.+)\]:?|(?P<ipv4_addr>[a-zA-Z0-9\._\/]+):?)'
+ r'(?P<port>[0-9]+)?',
+ config,
+ )
+ invalid_config_message = (
+ f'Invalid socket configuration "{config}"'
+ 'Accepted values are "default", "file:<file_path>", '
+ '"<name_or_ipv4_address>" with optional ":<port>", and '
+ '"[<name_or_ipv6_address>]" with optional ":<port>".'
+ )
+ if match is None:
+ raise ValueError(invalid_config_message)
+
+ info = match.groupdict()
+ if info['port']:
+ port = int(info['port'])
+ else:
+ port = SocketClient.DEFAULT_SOCKET_PORT
- self._on_disconnect = on_disconnect
- self._connected = False
- self.connect()
+ if info['ipv4_addr']:
+ ip_addr = info['ipv4_addr']
+ elif info['ipv6_addr']:
+ ip_addr = info['ipv6_addr']
+ else:
+ raise ValueError(invalid_config_message)
+
+ sock_family, sock_type, _, _, address = socket.getaddrinfo(
+ ip_addr, port, type=socket.SOCK_STREAM
+ )[0]
+ init_args = sock_family, sock_type
+ return init_args, address
def __del__(self):
- self.socket.close()
+ if self._connected:
+ self.socket.close()
def write(self, data: ReadableBuffer) -> None:
"""Writes data and detects disconnects."""
@@ -96,13 +179,13 @@ class SocketClient:
def connect(self) -> None:
"""Connects to socket."""
- self.socket = socket.socket(self._connection_type, socket.SOCK_STREAM)
+ self.socket = socket.socket(*self._socket_init_args)
# Enable reusing address and port for reconnections.
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if hasattr(socket, 'SO_REUSEPORT'):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
- self.socket.connect(self._interface)
+ self.socket.connect(self._address)
self._connected = True
def _handle_disconnect(self):
diff --git a/pw_console/py/socket_client_test.py b/pw_console/py/socket_client_test.py
new file mode 100644
index 000000000..f4e5a9f5d
--- /dev/null
+++ b/pw_console/py/socket_client_test.py
@@ -0,0 +1,181 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Tests for pw_console.socket_client"""
+
+import socket
+import unittest
+
+
+from pw_console import socket_client
+
+
+class TestSocketClient(unittest.TestCase):
+ """Tests for SocketClient."""
+
+ def test_parse_config_default(self) -> None:
+ config = "default"
+ with unittest.mock.patch.object(
+ socket_client.SocketClient, 'connect', return_value=None
+ ):
+ client = socket_client.SocketClient(config)
+ self.assertEqual(
+ client._socket_init_args, # pylint: disable=protected-access
+ (socket.AF_INET6, socket.SOCK_STREAM),
+ )
+ self.assertEqual(
+ client._address, # pylint: disable=protected-access
+ (
+ socket_client.SocketClient.DEFAULT_SOCKET_SERVER,
+ socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ ),
+ )
+
+ def test_parse_config_unix_file(self) -> None:
+ # Skip test if UNIX sockets are not supported.
+ if not hasattr(socket, 'AF_UNIX'):
+ return
+
+ config = 'file:fake_file_path'
+ with unittest.mock.patch.object(
+ socket_client.SocketClient, 'connect', return_value=None
+ ):
+ client = socket_client.SocketClient(config)
+ self.assertEqual(
+ client._socket_init_args, # pylint: disable=protected-access
+ (
+ socket.AF_UNIX, # pylint: disable=no-member
+ socket.SOCK_STREAM,
+ ),
+ )
+ self.assertEqual(
+ client._address, # pylint: disable=protected-access
+ 'fake_file_path',
+ )
+
+ def _check_config_parsing(
+ self, config: str, expected_address: str, expected_port: int
+ ) -> None:
+ with unittest.mock.patch.object(
+ socket_client.SocketClient, 'connect', return_value=None
+ ):
+ fake_getaddrinfo_return_value = [
+ (socket.AF_INET6, socket.SOCK_STREAM, 0, None, None)
+ ]
+ with unittest.mock.patch.object(
+ socket,
+ 'getaddrinfo',
+ return_value=fake_getaddrinfo_return_value,
+ ) as mock_getaddrinfo:
+ client = socket_client.SocketClient(config)
+ mock_getaddrinfo.assert_called_with(
+ expected_address, expected_port, type=socket.SOCK_STREAM
+ )
+ # Assert the init args are what is returned by ``getaddrinfo``
+ # not necessarily the correct ones, since this test should not
+ # perform any network action.
+ self.assertEqual(
+ client._socket_init_args, # pylint: disable=protected-access
+ (
+ socket.AF_INET6,
+ socket.SOCK_STREAM,
+ ),
+ )
+
+ def test_parse_config_ipv4_domain(self) -> None:
+ self._check_config_parsing(
+ config='file.com/some_long/path:80',
+ expected_address='file.com/some_long/path',
+ expected_port=80,
+ )
+
+ def test_parse_config_ipv4_domain_no_port(self) -> None:
+ self._check_config_parsing(
+ config='file.com/some/path',
+ expected_address='file.com/some/path',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+ def test_parse_config_ipv4_address(self) -> None:
+ self._check_config_parsing(
+ config='8.8.8.8:8080',
+ expected_address='8.8.8.8',
+ expected_port=8080,
+ )
+
+ def test_parse_config_ipv4_address_no_port(self) -> None:
+ self._check_config_parsing(
+ config='8.8.8.8',
+ expected_address='8.8.8.8',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+ def test_parse_config_ipv6_domain(self) -> None:
+ self._check_config_parsing(
+ config='[file.com/some_long/path]:80',
+ expected_address='file.com/some_long/path',
+ expected_port=80,
+ )
+
+ def test_parse_config_ipv6_domain_no_port(self) -> None:
+ self._check_config_parsing(
+ config='[file.com/some/path]',
+ expected_address='file.com/some/path',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+ def test_parse_config_ipv6_address(self) -> None:
+ self._check_config_parsing(
+ config='[2001:4860:4860::8888:8080]:666',
+ expected_address='2001:4860:4860::8888:8080',
+ expected_port=666,
+ )
+
+ def test_parse_config_ipv6_address_no_port(self) -> None:
+ self._check_config_parsing(
+ config='[2001:4860:4860::8844]',
+ expected_address='2001:4860:4860::8844',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+ def test_parse_config_ipv6_local(self) -> None:
+ self._check_config_parsing(
+ config='[fe80::100%eth0]:80',
+ expected_address='fe80::100%eth0',
+ expected_port=80,
+ )
+
+ def test_parse_config_ipv6_local_no_port(self) -> None:
+ self._check_config_parsing(
+ config='[fe80::100%eth0]',
+ expected_address='fe80::100%eth0',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+ def test_parse_config_ipv6_local_windows(self) -> None:
+ self._check_config_parsing(
+ config='[fe80::100%4]:80',
+ expected_address='fe80::100%4',
+ expected_port=80,
+ )
+
+ def test_parse_config_ipv6_local_no_port_windows(self) -> None:
+ self._check_config_parsing(
+ config='[fe80::100%4]',
+ expected_address='fe80::100%4',
+ expected_port=socket_client.SocketClient.DEFAULT_SOCKET_PORT,
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pw_console/testing.rst b/pw_console/testing.rst
index af91318fb..7cc428fb7 100644
--- a/pw_console/testing.rst
+++ b/pw_console/testing.rst
@@ -3,6 +3,9 @@
=====================
Manual Test Procedure
=====================
+.. pigweed-module-subpage::
+ :name: pw_console
+ :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing
``pw_console`` is a Terminal based user interface which is difficult to
completely test in an automated fashion. Unit tests that don't depend on the
diff --git a/pw_containers/docs.rst b/pw_containers/docs.rst
index 488183515..83f333402 100644
--- a/pw_containers/docs.rst
+++ b/pw_containers/docs.rst
@@ -45,6 +45,7 @@ C
Python
------
.. automodule:: pw_containers.variable_length_entry_queue
+ :members:
-----------------
pw::IntrusiveList
diff --git a/pw_containers/public/pw_containers/variable_length_entry_queue.h b/pw_containers/public/pw_containers/variable_length_entry_queue.h
index f07b7286d..53ccb766f 100644
--- a/pw_containers/public/pw_containers/variable_length_entry_queue.h
+++ b/pw_containers/public/pw_containers/variable_length_entry_queue.h
@@ -94,15 +94,17 @@ extern "C" {
typedef uint32_t* pw_VariableLengthEntryQueue_Handle;
typedef const uint32_t* pw_VariableLengthEntryQueue_ConstHandle;
-/// Declares and initializes a `VariableLengthEntryQueue` that can hold an entry
-/// of up to `max_entry_size_bytes`. Attempting to store larger entries is
-/// invalid and will fail an assertion.
-#define PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(variable, max_entry_size_bytes) \
- uint32_t variable[PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 + \
- _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_entry_size_bytes)] = { \
- _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_entry_size_bytes), \
- /*head=*/0u, \
- /*tail=*/0u}
+/// Declares and initializes a `VariableLengthEntryQueue` that can hold up to
+/// `max_size_bytes` bytes. `max_size_bytes` is the largest supported size for a
+/// single entry; attempting to store larger entries is invalid and will fail an
+/// assertion.
+///
+/// @param variable variable name for the queue
+/// @param max_size_bytes the capacity of the queue
+#define PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(variable, max_size_bytes) \
+ uint32_t variable[PW_VARIABLE_LENGTH_ENTRY_QUEUE_HEADER_SIZE_UINT32 + \
+ _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_size_bytes)] = { \
+ _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes), /*head=*/0u, /*tail=*/0u}
/// The size of the `VariableLengthEntryQueue` header, in `uint32_t` elements.
/// This header stores the buffer length and head and tail offsets.
@@ -117,10 +119,13 @@ typedef const uint32_t* pw_VariableLengthEntryQueue_ConstHandle;
static inline void pw_VariableLengthEntryQueue_Init(uint32_t array[],
size_t array_size_uint32);
+/// Empties the queue.
+static inline void pw_VariableLengthEntryQueue_Clear(
+ pw_VariableLengthEntryQueue_Handle queue);
+
/// Appends an entry to the end of the queue.
///
-/// @pre The entry MUST not be larger than
-/// @cpp_func{pw_VariableLengthEntryQueue_MaxEntrySizeBytes}; asserts if it is.
+/// @pre The entry MUST NOT be larger than `max_size_bytes()`.
void pw_VariableLengthEntryQueue_Push(pw_VariableLengthEntryQueue_Handle queue,
const void* data,
uint32_t data_size_bytes);
@@ -128,14 +133,13 @@ void pw_VariableLengthEntryQueue_Push(pw_VariableLengthEntryQueue_Handle queue,
/// Appends an entry to the end of the queue, removing entries with `Pop`
/// as necessary to make room.
///
-/// @pre The entry MUST not be larger than
-/// @cpp_func{pw_VariableLengthEntryQueue_MaxEntrySizeBytes}; asserts if it is.
+/// @pre The entry MUST NOT be larger than `max_size_bytes()`.
void pw_VariableLengthEntryQueue_PushOverwrite(
pw_VariableLengthEntryQueue_Handle queue,
const void* data,
uint32_t data_size_bytes);
-/// Removes the first entry from the ring buffer.
+/// Removes the first entry from queue.
///
/// @pre The queue MUST have at least one entry.
void pw_VariableLengthEntryQueue_Pop(pw_VariableLengthEntryQueue_Handle queue);
@@ -152,8 +156,8 @@ typedef struct {
uint32_t _pw_offset;
} pw_VariableLengthEntryQueue_Iterator;
-// An entry in the queue. Entries may be stored in up to two segments, so this
-// struct includes pointers to both portions of the entry.
+/// An entry in the queue. Entries may be stored in up to two segments, so this
+/// struct includes pointers to both portions of the entry.
typedef struct {
const uint8_t* data_1;
uint32_t size_1;
@@ -166,7 +170,7 @@ static inline pw_VariableLengthEntryQueue_Iterator
pw_VariableLengthEntryQueue_Begin(
pw_VariableLengthEntryQueue_ConstHandle queue);
-/// Returns an iterator to entry following the last entry, which is not valid.
+/// Returns an iterator that points past the end of the queue.
static inline pw_VariableLengthEntryQueue_Iterator
pw_VariableLengthEntryQueue_End(pw_VariableLengthEntryQueue_ConstHandle queue);
@@ -194,21 +198,26 @@ pw_VariableLengthEntryQueue_Entry pw_VariableLengthEntryQueue_GetEntry(
uint32_t pw_VariableLengthEntryQueue_Entry_Copy(
const pw_VariableLengthEntryQueue_Entry* entry, void* dest, uint32_t count);
+/// Returns the byte at the specified index in the entry. Asserts if index is
+/// out-of-bounds.
+static inline uint8_t pw_VariableLengthEntryQueue_Entry_At(
+ const pw_VariableLengthEntryQueue_Entry* entry, size_t index);
+
/// Returns the number of variable-length entries in the queue. This is O(n) in
/// the number of entries in the queue.
uint32_t pw_VariableLengthEntryQueue_Size(
pw_VariableLengthEntryQueue_ConstHandle queue);
-/// Returns the number of bytes stored in the buffer, including entry metadata.
-/// This can be used with `RawCapacityBytes` to gauge available space for
-/// entries.
-static inline uint32_t pw_VariableLengthEntryQueue_RawSizeBytes(
+/// Returns the combined size in bytes of all entries in the queue, excluding
+/// metadata. This is O(n) in the number of entries in the queue.
+uint32_t pw_VariableLengthEntryQueue_SizeBytes(
pw_VariableLengthEntryQueue_ConstHandle queue);
-/// Returns the maximum number of bytes that can be stored in the buffer,
-/// including per-entry metadata. This can be used with `RawSizeBytes` to gauge
-/// available space for entries.
-static inline uint32_t pw_VariableLengthEntryQueue_RawCapacityBytes(
+/// Returns the the maximum number of bytes that can be stored in the queue.
+/// This is largest possible value of `size_bytes()`, and the size of the
+/// largest single entry that can be stored in this queue. Attempting to store a
+/// larger entry is invalid and results in a crash.
+static inline uint32_t pw_VariableLengthEntryQueue_MaxSizeBytes(
pw_VariableLengthEntryQueue_ConstHandle queue);
/// Returns the size of the raw underlying `VariableLengthEntryQueue` storage.
@@ -217,11 +226,6 @@ static inline uint32_t pw_VariableLengthEntryQueue_RawCapacityBytes(
static inline uint32_t pw_VariableLengthEntryQueue_RawStorageSizeBytes(
pw_VariableLengthEntryQueue_ConstHandle queue);
-/// Returns the size of the largest entry this `VariableLengthEntryQueue` can
-/// hold. Attempting to store a larger entry is invalid and fails an assert.
-static inline uint32_t pw_VariableLengthEntryQueue_MaxEntrySizeBytes(
- pw_VariableLengthEntryQueue_ConstHandle queue);
-
/// Returns true if the `VariableLengthEntryQueue` is empty, false if it has at
/// least one entry.
static inline bool pw_VariableLengthEntryQueue_Empty(
@@ -231,11 +235,11 @@ static inline bool pw_VariableLengthEntryQueue_Empty(
// Implementation details.
-#define _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_entry_size_bytes) \
- ((_PW_VAR_QUEUE_DATA_SIZE_BYTES(max_entry_size_bytes) + 3 /* round up */) / 4)
+#define _PW_VAR_QUEUE_DATA_SIZE_UINT32(max_size_bytes) \
+ ((_PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes) + 3 /* round up */) / 4)
-#define _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_entry_size_bytes) \
- (PW_VARINT_ENCODED_SIZE_BYTES(max_entry_size_bytes) + max_entry_size_bytes + \
+#define _PW_VAR_QUEUE_DATA_SIZE_BYTES(max_size_bytes) \
+ (PW_VARINT_ENCODED_SIZE_BYTES(max_size_bytes) + max_size_bytes + \
1 /*end byte*/)
#define _PW_VAR_QUEUE_ARRAY_SIZE_BYTES queue[0]
@@ -255,6 +259,12 @@ static inline void pw_VariableLengthEntryQueue_Init(uint32_t array[],
array[2] = 0; // tail
}
+static inline void pw_VariableLengthEntryQueue_Clear(
+ pw_VariableLengthEntryQueue_Handle queue) {
+ _PW_VAR_QUEUE_HEAD = 0; // head
+ _PW_VAR_QUEUE_TAIL = 0; // tail
+}
+
static inline pw_VariableLengthEntryQueue_Iterator
pw_VariableLengthEntryQueue_Begin(
pw_VariableLengthEntryQueue_ConstHandle queue) {
@@ -274,18 +284,21 @@ static inline bool pw_VariableLengthEntryQueue_Iterator_Equal(
return lhs->_pw_offset == rhs->_pw_offset && lhs->_pw_queue == rhs->_pw_queue;
}
-static inline uint32_t pw_VariableLengthEntryQueue_RawSizeBytes(
- pw_VariableLengthEntryQueue_ConstHandle queue) {
- uint32_t tail = _PW_VAR_QUEUE_TAIL;
- if (tail < _PW_VAR_QUEUE_HEAD) {
- tail += _PW_VAR_QUEUE_ARRAY_SIZE_BYTES;
+// Private function that returns a pointer to the specified index in the Entry.
+static inline const uint8_t* _pw_VariableLengthEntryQueue_Entry_GetPointer(
+ const pw_VariableLengthEntryQueue_Entry* entry, size_t index) {
+ if (index < entry->size_1) {
+ return &entry->data_1[index];
}
- return tail - _PW_VAR_QUEUE_HEAD;
+ return &entry->data_2[index - entry->size_1];
}
-static inline uint32_t pw_VariableLengthEntryQueue_RawCapacityBytes(
- pw_VariableLengthEntryQueue_ConstHandle queue) {
- return _PW_VAR_QUEUE_ARRAY_SIZE_BYTES - 1;
+const uint8_t* _pw_VariableLengthEntryQueue_Entry_GetPointerChecked(
+ const pw_VariableLengthEntryQueue_Entry* entry, size_t index);
+
+static inline uint8_t pw_VariableLengthEntryQueue_Entry_At(
+ const pw_VariableLengthEntryQueue_Entry* entry, size_t index) {
+ return *_pw_VariableLengthEntryQueue_Entry_GetPointerChecked(entry, index);
}
static inline uint32_t pw_VariableLengthEntryQueue_RawStorageSizeBytes(
@@ -294,7 +307,7 @@ static inline uint32_t pw_VariableLengthEntryQueue_RawStorageSizeBytes(
_PW_VAR_QUEUE_ARRAY_SIZE_BYTES;
}
-static inline uint32_t pw_VariableLengthEntryQueue_MaxEntrySizeBytes(
+static inline uint32_t pw_VariableLengthEntryQueue_MaxSizeBytes(
pw_VariableLengthEntryQueue_ConstHandle queue) {
return _PW_VAR_QUEUE_ARRAY_SIZE_BYTES - 1 -
(uint32_t)pw_varint_EncodedSizeBytes(_PW_VAR_QUEUE_ARRAY_SIZE_BYTES -
diff --git a/pw_containers/pw_containers_private/variable_length_entry_queue_test_oracle.h b/pw_containers/pw_containers_private/variable_length_entry_queue_test_oracle.h
index 9389d53f2..5538ff669 100644
--- a/pw_containers/pw_containers_private/variable_length_entry_queue_test_oracle.h
+++ b/pw_containers/pw_containers_private/variable_length_entry_queue_test_oracle.h
@@ -28,12 +28,17 @@ namespace pw::containers {
// implementation.
class VariableLengthEntryQueueTestOracle {
public:
- VariableLengthEntryQueueTestOracle(uint32_t max_entry_size_bytes)
- : max_entry_size_bytes_(max_entry_size_bytes),
+ VariableLengthEntryQueueTestOracle(uint32_t max_size_bytes)
+ : max_size_bytes_(max_size_bytes),
raw_size_bytes_(0),
raw_capacity_bytes_(
- static_cast<uint32_t>(varint::EncodedSize(max_entry_size_bytes)) +
- max_entry_size_bytes) {}
+ static_cast<uint32_t>(varint::EncodedSize(max_size_bytes)) +
+ max_size_bytes) {}
+
+ void clear() {
+ q_.clear();
+ raw_size_bytes_ = 0;
+ }
void push_overwrite(ConstByteSpan data) {
size_t encoded_size = varint::EncodedSize(data.size()) + data.size();
@@ -44,7 +49,7 @@ class VariableLengthEntryQueueTestOracle {
}
void push(ConstByteSpan data) {
- PW_ASSERT(data.size() <= max_entry_size_bytes_);
+ PW_ASSERT(data.size() <= max_size_bytes_);
size_t encoded_size = varint::EncodedSize(data.size()) + data.size();
PW_ASSERT(encoded_size <= raw_capacity_bytes_ - raw_size_bytes_);
@@ -61,9 +66,14 @@ class VariableLengthEntryQueueTestOracle {
}
uint32_t size() const { return static_cast<uint32_t>(q_.size()); }
- uint32_t raw_size_bytes() const { return raw_size_bytes_; }
- uint32_t raw_capacity_bytes() const { return raw_capacity_bytes_; }
- uint32_t max_entry_size_bytes() const { return max_entry_size_bytes_; }
+ uint32_t size_bytes() const {
+ uint32_t total_bytes = 0;
+ for (const auto& entry : q_) {
+ total_bytes += static_cast<uint32_t>(entry.size());
+ }
+ return total_bytes;
+ }
+ uint32_t max_size_bytes() const { return max_size_bytes_; }
std::deque<std::vector<std::byte>>::const_iterator begin() const {
return q_.begin();
@@ -75,7 +85,7 @@ class VariableLengthEntryQueueTestOracle {
private:
std::deque<std::vector<std::byte>> q_;
- const uint32_t max_entry_size_bytes_;
+ const uint32_t max_size_bytes_;
uint32_t raw_size_bytes_;
const uint32_t raw_capacity_bytes_;
};
diff --git a/pw_containers/py/pw_containers/variable_length_entry_queue.py b/pw_containers/py/pw_containers/variable_length_entry_queue.py
index f3e8f55bb..2c6e53d12 100644
--- a/pw_containers/py/pw_containers/variable_length_entry_queue.py
+++ b/pw_containers/py/pw_containers/variable_length_entry_queue.py
@@ -40,25 +40,25 @@ def _decode_leb128(
raise ValueError(f'Unterminated varint {data[offset:]!r}')
-def parse(ring_buffer: bytes) -> Iterable[bytes]:
- """Decodes the in-memory representation of a sized-entry ring buffer.
+def parse(queue: bytes) -> Iterable[bytes]:
+ """Decodes the in-memory representation of a variable-length entry queue.
Args:
- ring_buffer: The bytes representation of a sized-entry ring buffer.
+ queue: The bytes representation of a variable-length entry queue.
Yields:
Each entry in the buffer as bytes.
"""
- array_size_bytes, head, tail = _HEADER.unpack_from(ring_buffer)
+ array_size_bytes, head, tail = _HEADER.unpack_from(queue)
total_encoded_size = _HEADER.size + array_size_bytes
- if len(ring_buffer) < total_encoded_size:
+ if len(queue) < total_encoded_size:
raise ValueError(
- f'Ring buffer data ({len(ring_buffer)} B) is smaller than the '
- f'encoded size ({total_encoded_size} B)'
+ f'Ring buffer data ({len(queue)} B) is smaller than the encoded '
+ f'size ({total_encoded_size} B)'
)
- data = ring_buffer[_HEADER.size : total_encoded_size]
+ data = queue[_HEADER.size : total_encoded_size]
if tail < head:
data = data[head:] + data[:tail]
diff --git a/pw_containers/variable_length_entry_queue.c b/pw_containers/variable_length_entry_queue.c
index 8f7cc49f0..7089b6fe3 100644
--- a/pw_containers/variable_length_entry_queue.c
+++ b/pw_containers/variable_length_entry_queue.c
@@ -72,7 +72,9 @@ static uint32_t EncodePrefix(pw_VariableLengthEntryQueue_ConstHandle queue,
data_size_bytes, prefix, PW_VARINT_MAX_INT32_SIZE_BYTES);
// Check that the ring buffer is capable of holding entries of this size.
- PW_CHECK_UINT_LE(prefix_size + data_size_bytes, Capacity(queue));
+ PW_CHECK_UINT_LE(prefix_size + data_size_bytes,
+ Capacity(queue),
+ "Entry is too large for this VariableLengthEntryQueue");
return prefix_size;
}
@@ -120,14 +122,23 @@ static void AppendEntryKnownToFit(pw_VariableLengthEntryQueue_Handle queue,
TAIL(queue) = CopyAndWrap(queue, tail, data, size);
}
+static inline uint32_t AvailableBytes(
+ pw_VariableLengthEntryQueue_ConstHandle queue) {
+ uint32_t tail = TAIL(queue);
+ if (tail < HEAD(queue)) {
+ tail += BufferSize(queue);
+ }
+ return Capacity(queue) - (tail - HEAD(queue));
+}
+
void pw_VariableLengthEntryQueue_Push(pw_VariableLengthEntryQueue_Handle queue,
const void* data,
const uint32_t data_size_bytes) {
uint8_t prefix[PW_VARINT_MAX_INT32_SIZE_BYTES];
uint32_t prefix_size = EncodePrefix(queue, prefix, data_size_bytes);
- PW_CHECK(prefix_size + data_size_bytes <=
- Capacity(queue) - pw_VariableLengthEntryQueue_RawSizeBytes(queue));
+ PW_CHECK(prefix_size + data_size_bytes <= AvailableBytes(queue),
+ "Insufficient remaining space for entry");
AppendEntryKnownToFit(queue, prefix, prefix_size, data, data_size_bytes);
}
@@ -139,8 +150,7 @@ void pw_VariableLengthEntryQueue_PushOverwrite(
uint8_t prefix[PW_VARINT_MAX_INT32_SIZE_BYTES];
uint32_t prefix_size = EncodePrefix(queue, prefix, data_size_bytes);
- uint32_t available_bytes =
- Capacity(queue) - pw_VariableLengthEntryQueue_RawSizeBytes(queue);
+ uint32_t available_bytes = AvailableBytes(queue);
while (data_size_bytes + prefix_size > available_bytes) {
available_bytes += PopNonEmpty(queue);
}
@@ -207,6 +217,12 @@ uint32_t pw_VariableLengthEntryQueue_Entry_Copy(
return to_copy;
}
+const uint8_t* _pw_VariableLengthEntryQueue_Entry_GetPointerChecked(
+ const pw_VariableLengthEntryQueue_Entry* entry, size_t index) {
+ PW_CHECK_UINT_LT(index, entry->size_1 + entry->size_2);
+ return _pw_VariableLengthEntryQueue_Entry_GetPointer(entry, index);
+}
+
uint32_t pw_VariableLengthEntryQueue_Size(
pw_VariableLengthEntryQueue_ConstHandle queue) {
uint32_t entry_count = 0;
@@ -218,3 +234,16 @@ uint32_t pw_VariableLengthEntryQueue_Size(
}
return entry_count;
}
+
+uint32_t pw_VariableLengthEntryQueue_SizeBytes(
+ pw_VariableLengthEntryQueue_ConstHandle queue) {
+ uint32_t total_entry_size_bytes = 0;
+ uint32_t offset = HEAD(queue);
+
+ while (offset != TAIL(queue)) {
+ const EntrySize size = ReadEntrySize(queue, offset);
+ offset = WrapIndex(queue, offset + size.prefix + size.data);
+ total_entry_size_bytes += size.data;
+ }
+ return total_entry_size_bytes;
+}
diff --git a/pw_containers/variable_length_entry_queue_test.cc b/pw_containers/variable_length_entry_queue_test.cc
index f329f604a..2e4fb90d7 100644
--- a/pw_containers/variable_length_entry_queue_test.cc
+++ b/pw_containers/variable_length_entry_queue_test.cc
@@ -23,8 +23,6 @@
namespace {
-using std::string_view_literals::operator""sv;
-
struct PushOverwrite {
std::string_view data;
};
@@ -32,11 +30,12 @@ struct Push {
std::string_view data;
};
struct Pop {};
+struct Clear {};
struct SizeEquals {
size_t expected;
};
-using TestStep = std::variant<PushOverwrite, Push, Pop, SizeEquals>;
+using TestStep = std::variant<PushOverwrite, Push, Pop, Clear, SizeEquals>;
// Copies an entry, which might be wrapped, to a single std::vector.
std::vector<std::byte> ReadEntry(
@@ -67,7 +66,7 @@ std::vector<std::byte> ReadEntry(
// after every step.
#define DATA_DRIVEN_TEST(program, max_entry_size) \
TEST(VariableLengthEntryQueue, \
- DataDrivenTest_##program##_MaxEntrySize##max_entry_size) { \
+ DataDrivenTest_##program##_MaxSizeBytes##max_entry_size) { \
pw::containers::VariableLengthEntryQueueTestOracle oracle(max_entry_size); \
PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(c_queue, max_entry_size); \
\
@@ -83,7 +82,7 @@ std::vector<std::byte> ReadEntry(
push->data.data(), \
static_cast<uint32_t>(push->data.size())); \
oracle.push(pw::as_bytes(pw::span(push->data))); \
- } else if (auto pop = std::get_if<Pop>(&step); pop != nullptr) { \
+ } else if (std::holds_alternative<Pop>(step)) { \
pw_VariableLengthEntryQueue_Pop(c_queue); \
oracle.pop(); \
} else if (auto size = std::get_if<SizeEquals>(&step); \
@@ -91,17 +90,18 @@ std::vector<std::byte> ReadEntry(
size_t actual = pw_VariableLengthEntryQueue_Size(c_queue); \
ASSERT_EQ(oracle.size(), actual); \
ASSERT_EQ(size->expected, actual); \
+ } else if (std::holds_alternative<Clear>(step)) { \
+ pw_VariableLengthEntryQueue_Clear(c_queue); \
+ oracle.clear(); \
} else { \
FAIL() << "Unhandled case"; \
} \
/* Check size and other functions */ \
ASSERT_EQ(pw_VariableLengthEntryQueue_Size(c_queue), oracle.size()); \
- ASSERT_EQ(pw_VariableLengthEntryQueue_RawSizeBytes(c_queue), \
- oracle.raw_size_bytes()); \
- ASSERT_EQ(pw_VariableLengthEntryQueue_RawCapacityBytes(c_queue), \
- oracle.raw_capacity_bytes()); \
- ASSERT_EQ(pw_VariableLengthEntryQueue_MaxEntrySizeBytes(c_queue), \
- oracle.max_entry_size_bytes()); \
+ ASSERT_EQ(pw_VariableLengthEntryQueue_SizeBytes(c_queue), \
+ oracle.size_bytes()); \
+ ASSERT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(c_queue), \
+ oracle.max_size_bytes()); \
ASSERT_CONTENTS_EQ(oracle, c_queue); \
} \
} \
@@ -109,84 +109,100 @@ std::vector<std::byte> ReadEntry(
constexpr TestStep kPop[] = {
SizeEquals{0},
- PushOverwrite{""sv},
+ PushOverwrite{""},
SizeEquals{1},
Pop{},
SizeEquals{0},
};
+DATA_DRIVEN_TEST(kPop, 0); // Only holds one empty entry.
DATA_DRIVEN_TEST(kPop, 1);
DATA_DRIVEN_TEST(kPop, 6);
constexpr TestStep kOverwriteLargeEntriesWithSmall[] = {
- TestStep{PushOverwrite{"12345"sv}}, // 6-byte entry
- TestStep{PushOverwrite{"abcde"sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{SizeEquals{6}},
- TestStep{Pop{}},
- TestStep{Pop{}},
- TestStep{Pop{}},
- TestStep{Pop{}},
- TestStep{Pop{}},
- TestStep{Pop{}},
- TestStep{SizeEquals{0}},
+ PushOverwrite{"12345"},
+ PushOverwrite{"abcde"},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ SizeEquals{6},
+ Pop{},
+ Pop{},
+ Pop{},
+ Pop{},
+ Pop{},
+ Pop{},
+ SizeEquals{0},
};
+DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 5);
DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 6);
DATA_DRIVEN_TEST(kOverwriteLargeEntriesWithSmall, 7);
-constexpr TestStep kOverwriteVaryingSizesUpTo3[] = {
- TestStep{PushOverwrite{""sv}}, TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}}, TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}}, TestStep{PushOverwrite{"1"sv}},
- TestStep{PushOverwrite{"2"sv}}, TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{"3"sv}}, TestStep{PushOverwrite{"4"sv}},
- TestStep{PushOverwrite{""sv}}, TestStep{PushOverwrite{"5"sv}},
- TestStep{PushOverwrite{"6"sv}}, TestStep{PushOverwrite{"ab"sv}},
- TestStep{PushOverwrite{"cd"sv}}, TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{"ef"sv}}, TestStep{PushOverwrite{"gh"sv}},
- TestStep{PushOverwrite{"ij"sv}},
+constexpr TestStep kOverwriteVaryingSizes012[] = {
+ PushOverwrite{""}, PushOverwrite{""}, PushOverwrite{""},
+ PushOverwrite{""}, PushOverwrite{""}, PushOverwrite{"1"},
+ PushOverwrite{"2"}, PushOverwrite{""}, PushOverwrite{"3"},
+ PushOverwrite{"4"}, PushOverwrite{""}, PushOverwrite{"5"},
+ PushOverwrite{"6"}, PushOverwrite{"ab"}, PushOverwrite{"cd"},
+ PushOverwrite{""}, PushOverwrite{"ef"}, PushOverwrite{"gh"},
+ PushOverwrite{"ij"},
};
-DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo3, 3);
-DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo3, 4);
-
-constexpr TestStep kOverwriteVaryingSizesUpTo5[] = {
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{""sv}},
- TestStep{PushOverwrite{"1"sv}},
- TestStep{PushOverwrite{"2"sv}},
- TestStep{PushOverwrite{"3"sv}},
- TestStep{PushOverwrite{"ab"sv}},
- TestStep{PushOverwrite{"cd"sv}},
- TestStep{PushOverwrite{"ef"sv}},
- TestStep{PushOverwrite{"123"sv}},
- TestStep{PushOverwrite{"456"sv}},
- TestStep{PushOverwrite{"789"sv}},
- TestStep{PushOverwrite{"abcd"sv}},
- TestStep{PushOverwrite{"efgh"sv}},
- TestStep{PushOverwrite{"ijkl"sv}},
- TestStep{Pop{}},
- TestStep{SizeEquals{0}},
+DATA_DRIVEN_TEST(kOverwriteVaryingSizes012, 2);
+DATA_DRIVEN_TEST(kOverwriteVaryingSizes012, 3);
+
+constexpr TestStep kOverwriteVaryingSizesUpTo4[] = {
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{"1"},
+ PushOverwrite{"2"},
+ PushOverwrite{"3"},
+ PushOverwrite{"ab"},
+ PushOverwrite{"cd"},
+ PushOverwrite{"ef"},
+ PushOverwrite{"123"},
+ PushOverwrite{"456"},
+ PushOverwrite{"789"},
+ PushOverwrite{"abcd"},
+ PushOverwrite{"efgh"},
+ PushOverwrite{"ijkl"},
+ Pop{},
+ SizeEquals{0},
};
-DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo5, 5);
-DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo5, 6);
-DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo5, 7);
+DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 4);
+DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 5);
+DATA_DRIVEN_TEST(kOverwriteVaryingSizesUpTo4, 6);
constexpr char kBigEntryBytes[196]{};
constexpr TestStep kTwoBytePrefix[] = {
- TestStep{PushOverwrite{std::string_view(kBigEntryBytes, 128)}},
- TestStep{PushOverwrite{std::string_view(kBigEntryBytes, 128)}},
- TestStep{PushOverwrite{std::string_view(kBigEntryBytes, 127)}},
- TestStep{PushOverwrite{std::string_view(kBigEntryBytes, 128)}},
- TestStep{PushOverwrite{std::string_view(kBigEntryBytes, 127)}},
+ PushOverwrite{std::string_view(kBigEntryBytes, 128)},
+ PushOverwrite{std::string_view(kBigEntryBytes, 128)},
+ PushOverwrite{std::string_view(kBigEntryBytes, 127)},
+ PushOverwrite{std::string_view(kBigEntryBytes, 128)},
+ PushOverwrite{std::string_view(kBigEntryBytes, 127)},
+ SizeEquals{1},
+ Pop{},
+ SizeEquals{0},
+};
+DATA_DRIVEN_TEST(kTwoBytePrefix, 128);
+DATA_DRIVEN_TEST(kTwoBytePrefix, 129);
+
+constexpr TestStep kClear[] = {
+ Push{"abcdefg"},
+ PushOverwrite{""},
+ PushOverwrite{""},
+ PushOverwrite{"a"},
+ PushOverwrite{"b"},
+ Clear{},
+ SizeEquals{0},
+ Clear{},
};
-DATA_DRIVEN_TEST(kTwoBytePrefix, 130);
+DATA_DRIVEN_TEST(kClear, 7);
+DATA_DRIVEN_TEST(kClear, 100);
TEST(VariableLengthEntryQueue, DeclareMacro) {
PW_VARIABLE_LENGTH_ENTRY_QUEUE_DECLARE(queue, 123);
@@ -198,8 +214,8 @@ TEST(VariableLengthEntryQueue, DeclareMacro) {
EXPECT_EQ(pw_VariableLengthEntryQueue_RawStorageSizeBytes(queue),
kArraySizeBytes - 3 /* padding isn't included */);
- EXPECT_EQ(pw_VariableLengthEntryQueue_MaxEntrySizeBytes(queue), 123u);
- EXPECT_EQ(pw_VariableLengthEntryQueue_RawSizeBytes(queue), 0u);
+ EXPECT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(queue), 123u);
+ EXPECT_EQ(pw_VariableLengthEntryQueue_SizeBytes(queue), 0u);
EXPECT_TRUE(pw_VariableLengthEntryQueue_Empty(queue));
}
@@ -211,9 +227,9 @@ TEST(VariableLengthEntryQueue, InitializeExistingBuffer) {
EXPECT_EQ(pw_VariableLengthEntryQueue_RawStorageSizeBytes(queue),
sizeof(queue));
- EXPECT_EQ(pw_VariableLengthEntryQueue_MaxEntrySizeBytes(queue),
+ EXPECT_EQ(pw_VariableLengthEntryQueue_MaxSizeBytes(queue),
sizeof(uint32_t) * 10u - 1 /*prefix*/ - 1 /*end*/);
- EXPECT_EQ(pw_VariableLengthEntryQueue_RawSizeBytes(queue), 0u);
+ EXPECT_EQ(pw_VariableLengthEntryQueue_SizeBytes(queue), 0u);
EXPECT_EQ(pw_VariableLengthEntryQueue_Size(queue), 0u);
EXPECT_TRUE(pw_VariableLengthEntryQueue_Empty(queue));
}
diff --git a/pw_digital_io_mcuxpresso/digital_io.cc b/pw_digital_io_mcuxpresso/digital_io.cc
index bae7f60bf..c599d58bf 100644
--- a/pw_digital_io_mcuxpresso/digital_io.cc
+++ b/pw_digital_io_mcuxpresso/digital_io.cc
@@ -26,11 +26,8 @@
namespace pw::digital_io {
namespace {
-constexpr size_t kRt595ClockNum = 8;
-constexpr size_t kRt595ResetNum = 8;
-
-constexpr std::array<clock_ip_name_t, kRt595ClockNum> kGpioClocks = GPIO_CLOCKS;
-constexpr std::array<reset_ip_name_t, kRt595ResetNum> kGpioResets = GPIO_RSTS_N;
+constexpr std::array kGpioClocks = GPIO_CLOCKS;
+constexpr std::array kGpioResets = GPIO_RSTS_N;
} // namespace
diff --git a/pw_docgen/docs.rst b/pw_docgen/docs.rst
index 13615030a..b36de7524 100644
--- a/pw_docgen/docs.rst
+++ b/pw_docgen/docs.rst
@@ -196,7 +196,7 @@ example:
:name: pw_string
:tagline: Efficient, easy, and safe string manipulation
:status: stable
- :languages: C++14, C++17, Rust
+ :languages: C++17, Rust
:code-size-impact: 500 to 1500 bytes
Module sales pitch goes here!
diff --git a/pw_docgen/py/pw_docgen/sphinx/pigweed_live.py b/pw_docgen/py/pw_docgen/sphinx/pigweed_live.py
index f385834b9..d7382cd38 100644
--- a/pw_docgen/py/pw_docgen/sphinx/pigweed_live.py
+++ b/pw_docgen/py/pw_docgen/sphinx/pigweed_live.py
@@ -40,7 +40,7 @@ class PigweedLiveDirective(Directive):
'2023-10-09 13:00:00',
'2023-10-23 13:00:00',
'2023-11-06 13:00:00',
- '2023-11-20 13:00:00',
+ # 2023-11-20 skipped since it's a holiday(ish)
'2023-12-04 13:00:00',
'2023-12-18 13:00:00',
# 2024-01-01 and 2024-01-15 are skipped because they're holidays.
diff --git a/pw_doctor/py/pw_doctor/doctor.py b/pw_doctor/py/pw_doctor/doctor.py
index 494b328fe..3e10a7ecd 100755
--- a/pw_doctor/py/pw_doctor/doctor.py
+++ b/pw_doctor/py/pw_doctor/doctor.py
@@ -508,8 +508,9 @@ def run_doctor(strict=False, checks=None):
doctor.log.info(
"Your environment setup has completed, but something isn't right "
'and some things may not work correctly. You may continue with '
- f'development, but please seek support at {new_bug_url} or by '
- 'reaching out to your team.'
+ 'development, but please seek support at %s or by '
+ 'reaching out to your team.',
+ new_bug_url,
)
else:
doctor.log.info('Environment passes all checks!')
diff --git a/pw_emu/config.rst b/pw_emu/config.rst
index 407d7bf58..41d1b2fe4 100644
--- a/pw_emu/config.rst
+++ b/pw_emu/config.rst
@@ -11,8 +11,9 @@ The emulators configuration is part of the Pigweed root configuration file
(``pigweed.json``) and reside in the ``pw:pw_emu`` namespace.
Projects can define emulation targets in the Pigweed root configuration file and
-can also import predefined targets from other files. The ``pw_emu`` module
-provides a set of targets as examples and to promote reusability.
+can also import predefined targets from other files (configuration
+fragments). The ``pw_emu`` module provides a set of targets as examples and to
+promote reusability.
The target configuration allows users to start other programs before
or after starting the main emulator process. This allows extending the
@@ -144,3 +145,11 @@ The following expressions are substituted in the ``pre-start-cmd`` and
* ``$pw_emu_channel_path{channel-name}``: replaces the statement with the path
for the given channel name; the channel type should be ``pty``
+
+
+The followng expressions are substituted in configuration strings, including
+configuration framents:
+
+* ``$pw_env{envvar}``: replaces statement with the value of the ``envar``
+ environment variable; if the variable does not exists in the environment a
+ configuration error is raised
diff --git a/pw_emu/py/mock_emu.py b/pw_emu/py/mock_emu.py
index 0d21767c8..1a49b2ba2 100644
--- a/pw_emu/py/mock_emu.py
+++ b/pw_emu/py/mock_emu.py
@@ -24,7 +24,6 @@ from threading import Thread
def _tcp_thread(sock: socket.socket) -> None:
- sock.listen()
conn, _ = sock.accept()
while True:
data = conn.recv(1)
@@ -84,6 +83,7 @@ def main() -> None:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 0))
port = sock.getsockname()[1]
+ sock.listen()
with open(os.path.join(args.working_dir, chan), 'w') as file:
file.write(str(port))
thread = Thread(target=_tcp_thread, args=(sock,))
diff --git a/pw_emu/py/pw_emu/__main__.py b/pw_emu/py/pw_emu/__main__.py
index 66ebbf4a0..253edd931 100644
--- a/pw_emu/py/pw_emu/__main__.py
+++ b/pw_emu/py/pw_emu/__main__.py
@@ -42,7 +42,7 @@ def _cmd_load(emu: Emulator, args: argparse.Namespace) -> None:
"""Load an executable image via gdb start executing it if pause is
not set"""
- args.command = ['load']
+ args.gdb_cmd = ['load']
_cmd_gdb_cmds(emu, args)
@@ -163,8 +163,7 @@ def _cmd_gdb(emu: Emulator, args: argparse.Namespace) -> None:
signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
- cmd = [
- emu.get_gdb_cmd(),
+ cmd = emu.get_gdb_cmd() + [
'-ex',
f'target remote {emu.get_gdb_remote()}',
executable,
@@ -208,6 +207,12 @@ def _cmd_term(emu: Emulator, args: argparse.Namespace) -> None:
raise Error(f'error connecting to channel `{args.channel}`: {err}')
+def _cmd_resume(emu: Emulator, _args: argparse.Namespace) -> None:
+ """Resume the execution of a paused emulator."""
+
+ emu.cont()
+
+
def get_parser() -> argparse.ArgumentParser:
"""Command line parser"""
@@ -399,6 +404,8 @@ def get_parser() -> argparse.ArgumentParser:
help='channel name',
)
+ resume = add_cmd('resume', _cmd_resume)
+
parser.epilog = f"""commands usage:
{start.format_usage().strip()}
{restart.format_usage().strip()}
@@ -412,6 +419,7 @@ def get_parser() -> argparse.ArgumentParser:
{prop_set.format_usage().strip()}
{gdb_cmds.format_usage().strip()}
{term.format_usage().strip()}
+ {resume.format_usage().strip()}
"""
return parser
diff --git a/pw_emu/py/pw_emu/core.py b/pw_emu/py/pw_emu/core.py
index b563d6093..1e5c2ea9f 100644
--- a/pw_emu/py/pw_emu/core.py
+++ b/pw_emu/py/pw_emu/core.py
@@ -349,6 +349,34 @@ class Config:
def get_targets(self) -> List[str]:
return list(self.get(['targets'], entry_type=dict).keys())
+ def _subst(self, string: str) -> str:
+ """Substitutes $pw_<subst_type>{arg} statements."""
+
+ match = re.search(r'\$pw_([^{]+){([^}]+)}', string)
+ if not match:
+ return string
+
+ subst_type = match.group(1)
+ arg = match.group(2)
+
+ if subst_type == 'env':
+ value = os.environ.get(arg)
+ if value is None:
+ msg = f'Environment variable `{arg}` not set'
+ raise ConfigError(self.path, msg)
+ return string.replace(f'$pw_{subst_type}{{{arg}}}', value)
+
+ raise ConfigError(self.path, f'Invalid substitution type: {subst_type}')
+
+ def _subst_list(self, items: List[Any]) -> List[Any]:
+ new_list = []
+ for item in items:
+ if isinstance(item, str):
+ new_list.append(self._subst(item))
+ else:
+ new_list.append(item)
+ return new_list
+
def get(
self,
keys: List[str],
@@ -383,15 +411,22 @@ class Config:
raise ConfigError(self.path, f'{keys_str}: not found')
entry = entry.get(key)
- if not entry:
- if entry_type:
- return entry_type()
- return entry
+ if entry is None:
+ if optional:
+ if entry_type:
+ return entry_type()
+ return None
+ raise ConfigError(self.path, f'{keys_str}: not found')
if entry_type and not isinstance(entry, entry_type):
msg = f'{keys_str}: expected entry of type `{entry_type}`'
raise ConfigError(self.path, msg)
+ if isinstance(entry, str):
+ entry = self._subst(entry)
+ elif isinstance(entry, list):
+ entry = self._subst_list(entry)
+
return entry
def get_target(
@@ -755,8 +790,11 @@ class Launcher(ABC):
os._exit(0)
try:
- with open(self._path(f'{name}.pid'), 'w') as file:
+ # Make the pid file create and pid write operations atomic to avoid
+ # races with readers.
+ with open(self._path(f'{name}.pid.tmp'), 'w') as file:
file.write(f'{os.getpid()}')
+ os.rename(self._path(f'{name}.pid.tmp'), self._path(f'{name}.pid'))
os.execvp(cmd[0], cmd)
finally:
os._exit(1)
diff --git a/pw_emu/py/pw_emu/renode.py b/pw_emu/py/pw_emu/renode.py
index 609681d01..1e3ccb548 100644
--- a/pw_emu/py/pw_emu/renode.py
+++ b/pw_emu/py/pw_emu/renode.py
@@ -140,19 +140,22 @@ class RenodeLauncher(Launcher):
assert isinstance(robot, Handles.TcpChannel)
# renode is slow to start especially during host load
- deadline = time.monotonic() + 60
+ deadline = time.monotonic() + 120
connected = False
+ err = None
while time.monotonic() < deadline:
try:
sock.connect((robot.host, robot.port))
connected = True
break
- except OSError:
- pass
+ except OSError as exc:
+ err = exc
time.sleep(1)
if not connected:
- raise RenodeRobotError('failed to connect to robot channel')
+ msg = 'failed to connect to robot channel'
+ msg += f'({robot.host}:{robot.port}): {err}'
+ raise RenodeRobotError(msg)
sock.close()
diff --git a/pw_emu/py/tests/cli_test.py b/pw_emu/py/tests/cli_test.py
index 452f78a12..86bde6432 100644
--- a/pw_emu/py/tests/cli_test.py
+++ b/pw_emu/py/tests/cli_test.py
@@ -37,17 +37,6 @@ from tests.common import ConfigHelper
_cli_path = Path(
os.path.join(os.environ['PW_ROOT'], 'pw_emu', 'py', 'pw_emu', '__main__.py')
).resolve()
-# Run the arm_gdb.py wrapper directly.
-_arm_none_eabi_gdb_path = Path(
- os.path.join(
- os.environ['PW_ROOT'],
- 'pw_env_setup',
- 'py',
- 'pw_env_setup',
- 'entry_points',
- 'arm_gdb.py',
- )
-).resolve()
class TestCli(ConfigHelper):
@@ -160,6 +149,12 @@ class TestNonInteractive(TestCli):
self.assertEqual(self._run(['reset']).returncode, 0)
self.assertTrue(os.path.exists(os.path.join(self._wdir.name, 'reset')))
+ def test_load(self) -> None:
+ self.assertEqual(self._run(['load', 'executable']).returncode, 0)
+
+ def test_resume(self) -> None:
+ self.assertEqual(self._run(['resume']).returncode, 0)
+
class TestForeground(TestCli):
"""Test starting in foreground"""
@@ -269,6 +264,13 @@ class TestInteractive(TestCli):
self.assertEqual(wait_pid, pid)
self.assertEqual(ret, 0)
+ def test_gdb(self) -> None:
+ res = self._run(['gdb', '-e', 'executable'], stdout=subprocess.PIPE)
+ self.assertEqual(res.returncode, 0)
+ output = res.stdout.decode('ascii')
+ self.assertTrue('target remote' in output, output)
+ self.assertTrue('executable' in output, output)
+
if __name__ == '__main__':
unittest.main()
diff --git a/pw_emu/py/tests/core_test.py b/pw_emu/py/tests/core_test.py
index e5ab75842..1131d5dd3 100644
--- a/pw_emu/py/tests/core_test.py
+++ b/pw_emu/py/tests/core_test.py
@@ -26,9 +26,12 @@ import unittest
from pathlib import Path
from typing import Any, Dict
+from unittest.mock import patch
+
from pw_emu.core import (
AlreadyRunning,
Config,
+ ConfigError,
Handles,
InvalidTarget,
InvalidChannelName,
@@ -391,5 +394,87 @@ class TestHandles(unittest.TestCase):
tmp.cleanup()
+class TestConfig(ConfigHelper):
+ """Stop tests for valid config."""
+
+ _config: Dict[str, Any] = {
+ 'top': 'entry',
+ 'multi': {
+ 'level': {
+ 'entry': 0,
+ },
+ },
+ 'subst': 'a/$pw_env{PW_EMU_TEST_ENV_SUBST}/c',
+ 'targets': {
+ 'test-target': {
+ 'entry': [1, 2, 3],
+ 'mock-emu': {
+ 'entry': 'test',
+ },
+ }
+ },
+ 'mock-emu': {
+ 'executable': _mock_emu,
+ },
+ 'list': ['a', '$pw_env{PW_EMU_TEST_ENV_SUBST}', 'c'],
+ 'bad-subst-type': '$pw_bad_subst_type{test}',
+ }
+
+ def setUp(self) -> None:
+ super().setUp()
+ self._cfg = Config(Path(self._config_file), 'test-target', 'mock-emu')
+
+ def test_top_entry(self) -> None:
+ self.assertEqual(self._cfg.get(['top']), 'entry')
+
+ def test_empty_subst(self) -> None:
+ with self.assertRaises(ConfigError):
+ self._cfg.get(['subst'])
+
+ def test_subst(self) -> None:
+ with patch.dict('os.environ', {'PW_EMU_TEST_ENV_SUBST': 'b'}):
+ self.assertEqual(self._cfg.get(['subst']), 'a/b/c')
+
+ def test_multi_level_entry(self) -> None:
+ self.assertEqual(self._cfg.get(['multi', 'level', 'entry']), 0)
+
+ def test_get_target(self) -> None:
+ self.assertEqual(self._cfg.get_targets(), ['test-target'])
+
+ def test_target(self) -> None:
+ self.assertEqual(self._cfg.get_target(['entry']), [1, 2, 3])
+
+ def test_target_emu(self) -> None:
+ self.assertEqual(self._cfg.get_target_emu(['entry']), 'test')
+
+ def test_type_checking(self) -> None:
+ with self.assertRaises(ConfigError):
+ self._cfg.get(['top'], entry_type=int)
+ self._cfg.get(['top'], entry_type=str)
+ self._cfg.get_target(['entry'], entry_type=list)
+ self._cfg.get_target_emu(['entry'], entry_type=str)
+ self._cfg.get(['targets'], entry_type=dict)
+
+ def test_non_optional(self) -> None:
+ with self.assertRaises(ConfigError):
+ self._cfg.get(['non-existing'], optional=False)
+
+ def test_optional(self) -> None:
+ self.assertEqual(self._cfg.get(['non-existing']), None)
+ self.assertEqual(self._cfg.get(['non-existing'], entry_type=int), 0)
+ self.assertEqual(self._cfg.get(['non-existing'], entry_type=str), '')
+ self.assertEqual(self._cfg.get(['non-existing'], entry_type=list), [])
+
+ def test_list(self) -> None:
+ with self.assertRaises(ConfigError):
+ self._cfg.get(['list'])
+ with patch.dict('os.environ', {'PW_EMU_TEST_ENV_SUBST': 'b'}):
+ self.assertEqual(self._cfg.get(['list']), ['a', 'b', 'c'])
+
+ def test_bad_subst(self) -> None:
+ with self.assertRaises(ConfigError):
+ self._cfg.get(['bad-subst-type'])
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/bazel.json b/pw_env_setup/py/pw_env_setup/cipd_setup/bazel.json
index 6a3f535b7..8b8bd5ce6 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/bazel.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/bazel.json
@@ -4,14 +4,15 @@
],
"packages": [
{
- "path": "fuchsia/third_party/bazel/${platform}",
+ "path": "fuchsia/third_party/bazel-prerelease/${platform}",
"platforms": [
"linux-amd64",
"mac-amd64",
+ "mac-arm64",
"windows-amd64"
],
"tags": [
- "version:2@6.3.2.6"
+ "version:2@7.0.0-pre.20231011.2.6"
],
"version_file": ".versions/bazel.cipd_version"
},
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/go.json b/pw_env_setup/py/pw_env_setup/cipd_setup/go.json
index 5bc9f704a..b4d4bdd3c 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/go.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/go.json
@@ -10,7 +10,7 @@
"windows-amd64"
],
"tags": [
- "version:2@1.21.3"
+ "version:2@1.21.4"
]
},
{
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/host_tools.json b/pw_env_setup/py/pw_env_setup/cipd_setup/host_tools.json
index 636f6dabe..5dd487a2c 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/host_tools.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/host_tools.json
@@ -8,7 +8,7 @@
"windows-amd64"
],
"tags": [
- "git_revision:19a0282cf752c14418d6d4b8ab8048f4e201ee1a"
+ "git_revision:0280acaad36a2544c150e1246cb344a1b6e6a02d"
],
"version_file": ".versions/host_tools.cipd_version"
}
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/luci.json b/pw_env_setup/py/pw_env_setup/cipd_setup/luci.json
index 46b37651f..1f3f5cd5c 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/luci.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/luci.json
@@ -124,6 +124,18 @@
"latest"
],
"version_file": ".versions/prpc.cipd_version"
+ },
+ {
+ "path": "infra/3pp/tools/gsutil",
+ "platforms": [
+ "linux-amd64",
+ "mac-amd64",
+ "mac-arm64"
+ ],
+ "tags": [
+ "latest"
+ ],
+ "version_file": ".versions/gsutil.cipd_version"
}
]
}
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json b/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json
index 685f39941..8201b8d64 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json
@@ -10,7 +10,7 @@
"windows-amd64"
],
"tags": [
- "git_revision:182a6eb05d15cc76d2302f7928fdb4f645d52c53"
+ "git_revision:e4702d7409069c4f12d45ea7b7f0890717ca3f4b"
],
"version_file": ".versions/gn.cipd_version"
},
@@ -47,7 +47,7 @@
"windows-amd64"
],
"tags": [
- "version:2@3.17.3"
+ "version:2@24.4"
]
},
{
@@ -57,7 +57,7 @@
"mac-arm64"
],
"tags": [
- "version:2@3.17.3"
+ "version:2@24.4"
]
},
{
@@ -96,7 +96,7 @@
"mac-arm64"
],
"tags": [
- "version:2@0.11.0-3"
+ "version:2@0.12.0-2"
]
},
{
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/python310.json b/pw_env_setup/py/pw_env_setup/cipd_setup/python310.json
index 0296812c4..d7b664d67 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/python310.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/python310.json
@@ -22,7 +22,7 @@
],
"subdir": "pip_cache",
"tags": [
- "git_revision:462a39c178dba43c77c8910d010b85673a60aba3"
+ "git_revision:9c0316b7067a011583fe245fa764bba382f92d43"
]
}
]
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/python311.json b/pw_env_setup/py/pw_env_setup/cipd_setup/python311.json
index 6f4451e9d..86363b72b 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/python311.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/python311.json
@@ -23,7 +23,7 @@
],
"subdir": "pip_cache",
"tags": [
- "git_revision:462a39c178dba43c77c8910d010b85673a60aba3"
+ "git_revision:9c0316b7067a011583fe245fa764bba382f92d43"
]
}
]
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/python38.json b/pw_env_setup/py/pw_env_setup/cipd_setup/python38.json
index 042ee98e9..c751acc48 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/python38.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/python38.json
@@ -22,7 +22,7 @@
],
"subdir": "pip_cache",
"tags": [
- "git_revision:462a39c178dba43c77c8910d010b85673a60aba3"
+ "git_revision:9c0316b7067a011583fe245fa764bba382f92d43"
]
}
]
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/python39.json b/pw_env_setup/py/pw_env_setup/cipd_setup/python39.json
index 4cb6b6bcc..0a2b89364 100644
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/python39.json
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/python39.json
@@ -22,7 +22,7 @@
],
"subdir": "pip_cache",
"tags": [
- "git_revision:462a39c178dba43c77c8910d010b85673a60aba3"
+ "git_revision:9c0316b7067a011583fe245fa764bba382f92d43"
]
}
]
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list
index 006d6ddeb..0b78fa271 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list
@@ -2,27 +2,30 @@
alabaster==0.7.13
appdirs==1.4.4
-astroid==2.15.6
-Babel==2.12.1
+astroid==3.0.1
+asttokens==2.4.0
+babel==2.12.1
backcall==0.2.0
black==23.1.0
cachetools==5.0.0
certifi==2021.10.8
cffi==1.15.1
charset-normalizer==3.2.0
+click==8.1.3
coloredlogs==15.0.1
coverage==7.2.7
cryptography==41.0.2
decorator==5.1.1
dill==0.3.6
docutils==0.20.1
-google-api-core==2.7.1
-googleapis-common-protos==1.56.2
-google-auth==2.6.3
-google-cloud-core==2.2.3
-google-cloud-storage==2.2.1
+executing==2.0.0
+google-api-core==2.12.0
+googleapis-common-protos==1.61.0
+google-auth==2.23.3
+google-cloud-core==2.3.3
+google-cloud-storage==2.12.0
google-crc32c==1.5.0
-google-resumable-media==2.3.2
+google-resumable-media==2.6.0
graphlib-backport==1.0.3; python_version < "3.9"
humanfriendly==10.0
idna==3.4
@@ -30,39 +33,41 @@ imagesize==1.4.1
importlib-metadata==6.8.0
ipython==8.12.2
isort==5.10.1
-jedi==0.18.1
+jedi==0.19.1
Jinja2==3.1.2
json5==0.9.11
+lockfile==0.12.2
kconfiglib==14.1.0
-lazy-object-proxy==1.9.0
-MarkupSafe==2.1.3
+markupsafe==2.1.3
matplotlib-inline==0.1.3
mccabe==0.6.1
-mypy==1.5.0
+mypy==1.6.1
mypy-extensions==1.0.0
-mypy-protobuf==3.3.0
-parameterized==0.8.1
+mypy-protobuf==3.5.0
+parameterized==0.9.0
parso==0.8.3
pathspec==0.11.1
pexpect==4.8.0
pickleshare==0.7.5
platformdirs==3.0.0
prompt-toolkit==3.0.39
-protobuf==3.20.3
+protobuf==4.24.4
psutil==5.9.4
ptpython==3.0.23
ptyprocess==0.7.0
+pure-eval==0.2.2
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.21
pyelftools==0.27
-Pygments==2.16.1
-pylint==2.17.5
+pygments==2.16.1
+pylint==3.0.1
pyperclip==1.8.2
pyserial==3.5
+python-daemon==3.0.1
pytz==2023.3
pyusb==1.2.1
-PyYAML==6.0.1
+pyyaml==6.0.1
requests==2.31.0
rsa==4.8
setuptools==68.0.0
@@ -77,15 +82,14 @@ sphinxcontrib-jsmath==1.0.1
sphinxcontrib-mermaid==0.9.2
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
-sphinx-copybutton==0.5.1
sphinx-design==0.5.0
-sphinx-sitemap==2.5.1
+stack-data==0.6.3
toml==0.10.2
+tomli==2.0.1; python_version < "3.11"
tomlkit==0.11.6
traitlets==5.1.1
types-docutils==0.20.0.3
-types-futures==3.3.2
-types-protobuf==3.20.4.6
+types-protobuf==4.24.0.2
types-Pygments==2.16.0.0
types-pyserial==3.5.0.7
types-PyYAML==6.0.12.11
@@ -98,6 +102,5 @@ urllib3==2.0.4
watchdog==2.3.1
wcwidth==0.2.6
websockets==10.4
-wrapt==1.15.0
yapf==0.31.0
zipp==3.16.2
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_darwin.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_darwin.list
index bdf8e0782..7d9f7e4b9 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_darwin.list
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_darwin.list
@@ -22,15 +22,15 @@ appnope==0.1.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -353,37 +353,38 @@ docutils==0.20.1 \
--hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -456,16 +457,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
@@ -507,9 +509,9 @@ isort==5.10.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -524,53 +526,22 @@ json5==0.9.11 \
--hash=sha256:1aa54b80b5e507dfe31d12b7743a642e2ffa6f70bf73b8e3d7d1d5fba83d99bd \
--hash=sha256:4f1e196acc55b83985a51318489f345963c7ba84aa37607e49073066c562e99b
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -578,6 +549,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -586,6 +558,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -593,9 +566,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -614,7 +590,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# jinja2
@@ -630,29 +608,34 @@ mccabe==0.6.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -661,9 +644,9 @@ mypy-extensions==1.0.0 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -673,9 +656,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -701,9 +684,9 @@ pickleshare==0.7.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -719,33 +702,23 @@ prompt-toolkit==3.0.39 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -811,9 +784,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -828,6 +801,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -835,7 +812,9 @@ pytz==2023.3 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# babel
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -843,7 +822,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -851,9 +833,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -868,7 +853,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -896,7 +883,6 @@ six==1.16.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
@@ -958,9 +944,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -998,9 +984,9 @@ types-docutils==0.20.0.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# mypy-protobuf
@@ -1165,85 +1151,6 @@ wheel==0.40.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1255,9 +1162,9 @@ zipp==3.16.2 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
@@ -1267,3 +1174,4 @@ setuptools==68.0.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_linux.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_linux.list
index 07b61b9a8..db05de24a 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_linux.list
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_linux.list
@@ -16,15 +16,15 @@ appdirs==1.4.4 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ptpython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -347,37 +347,38 @@ docutils==0.20.1 \
--hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -450,16 +451,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
@@ -501,9 +503,9 @@ isort==5.10.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -518,53 +520,22 @@ json5==0.9.11 \
--hash=sha256:1aa54b80b5e507dfe31d12b7743a642e2ffa6f70bf73b8e3d7d1d5fba83d99bd \
--hash=sha256:4f1e196acc55b83985a51318489f345963c7ba84aa37607e49073066c562e99b
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -572,6 +543,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -580,6 +552,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -587,9 +560,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -608,7 +584,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# jinja2
@@ -624,29 +602,34 @@ mccabe==0.6.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -655,9 +638,9 @@ mypy-extensions==1.0.0 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -667,9 +650,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -695,9 +678,9 @@ pickleshare==0.7.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -713,33 +696,23 @@ prompt-toolkit==3.0.39 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -805,9 +778,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -822,6 +795,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -829,7 +806,9 @@ pytz==2023.3 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# babel
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -837,7 +816,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -845,9 +827,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -862,7 +847,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -890,7 +877,6 @@ six==1.16.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
@@ -952,9 +938,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -992,9 +978,9 @@ types-docutils==0.20.0.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# mypy-protobuf
@@ -1159,85 +1145,6 @@ wheel==0.40.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1249,9 +1156,9 @@ zipp==3.16.2 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
@@ -1261,3 +1168,4 @@ setuptools==68.0.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_windows.list b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_windows.list
index bab92215d..961f31bf6 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_windows.list
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint_hashes_windows.list
@@ -16,15 +16,15 @@ appdirs==1.4.4 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ptpython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# stack-data
@@ -357,37 +357,38 @@ docutils==0.20.1 \
--hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# stack-data
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -460,16 +461,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
@@ -511,9 +513,9 @@ isort==5.10.1 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
@@ -528,53 +530,22 @@ json5==0.9.11 \
--hash=sha256:1aa54b80b5e507dfe31d12b7743a642e2ffa6f70bf73b8e3d7d1d5fba83d99bd \
--hash=sha256:4f1e196acc55b83985a51318489f345963c7ba84aa37607e49073066c562e99b
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -582,6 +553,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -590,6 +562,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -597,9 +570,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -618,7 +594,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# jinja2
@@ -634,29 +612,34 @@ mccabe==0.6.1 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -665,9 +648,9 @@ mypy-extensions==1.0.0 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -677,9 +660,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -699,9 +682,9 @@ pickleshare==0.7.5 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -717,33 +700,23 @@ prompt-toolkit==3.0.39 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -803,9 +776,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -826,6 +799,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -833,7 +810,9 @@ pytz==2023.3 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# babel
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -841,7 +820,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -849,9 +831,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -866,7 +851,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -894,7 +881,6 @@ six==1.16.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
@@ -956,9 +942,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
@@ -996,9 +982,9 @@ types-docutils==0.20.0.3 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# mypy-protobuf
@@ -1163,85 +1149,6 @@ wheel==0.40.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1253,9 +1160,9 @@ zipp==3.16.2 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
@@ -1265,3 +1172,4 @@ setuptools==68.0.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/python_base_requirements.txt b/pw_env_setup/py/pw_env_setup/virtualenv_setup/python_base_requirements.txt
index dac783801..eb737e7fc 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/python_base_requirements.txt
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/python_base_requirements.txt
@@ -1,8 +1,8 @@
build==0.10.0
click==8.1.3
packaging==23.0
-pip-tools==7.0.0
-pip==23.2
+pip-tools==7.3.0
+pip==23.2.1
pyproject-hooks==1.0.0
setuptools==68.0.0
tomli==2.0.1; python_version < "3.11"
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_darwin_lock.txt b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_darwin_lock.txt
index 89899874d..386a39720 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_darwin_lock.txt
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_darwin_lock.txt
@@ -22,15 +22,15 @@ appnope==0.1.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -364,10 +364,11 @@ docutils==0.20.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# breathe
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -375,30 +376,30 @@ furo==2023.8.19 \
--hash=sha256:12f99f87a1873b6746228cfde18f77244e6c1ffb85d7fed95e638aae70d80590 \
--hash=sha256:e671ee638ab3f1b472f4033b0167f502ab407830e0db0f843b1c1028119c9cd1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -471,16 +472,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
@@ -522,9 +524,9 @@ isort==5.10.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -543,53 +545,22 @@ kconfiglib==14.1.0 \
--hash=sha256:bed2cc2216f538eca4255a83a4588d8823563cdd50114f86cf1a2674e602c93c \
--hash=sha256:edcd35a20e7e138a9a9e96149027f805f785e818d2eae400b6fa8b0c8845114a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -597,6 +568,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -605,6 +577,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -612,9 +585,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -633,7 +609,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# jinja2
@@ -649,29 +627,34 @@ mccabe==0.6.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -680,9 +663,9 @@ mypy-extensions==1.0.0 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -692,9 +675,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -720,9 +703,9 @@ pickleshare==0.7.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -738,33 +721,23 @@ prompt-toolkit==3.0.39 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -831,9 +804,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -848,6 +821,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -859,7 +836,9 @@ pyusb==1.2.1 \
--hash=sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -867,7 +846,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -875,9 +857,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -892,7 +877,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -924,16 +911,15 @@ six==1.16.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-soupsieve==2.4.1 \
- --hash=sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8 \
- --hash=sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea
+soupsieve==2.5 \
+ --hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
+ --hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# beautifulsoup4
@@ -948,6 +934,7 @@ sphinx==7.1.2 \
# sphinx-basic-ng
# sphinx-copybutton
# sphinx-design
+ # sphinx-sitemap
sphinx-argparse==0.4.0 \
--hash=sha256:73bee01f7276fae2bf621ccfe4d167af7306e7288e3482005405d9f826f9b037 \
--hash=sha256:e0f34184eb56f12face774fbc87b880abdb9017a0998d1ec559b267e9697e449
@@ -966,6 +953,10 @@ sphinx-design==0.5.0 \
--hash=sha256:1af1267b4cea2eedd6724614f19dcc88fe2e15aff65d06b2f6252cee9c4f4c1e \
--hash=sha256:e8e513acea6f92d15c6de3b34e954458f245b8e761b45b63950f65373352ab00
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+sphinx-sitemap==2.5.1 \
+ --hash=sha256:0b7bce2835f287687f75584d7695e4eb8efaec028e5e7b36e9f791de3c344686 \
+ --hash=sha256:984bef068bbdbc26cfae209a8b61392e9681abc9191b477cd30da406e3a60ee5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
sphinxcontrib-applehelp==1.0.4 \
--hash=sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228 \
--hash=sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e
@@ -1006,9 +997,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -1046,9 +1037,9 @@ types-docutils==0.20.0.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# mypy-protobuf
@@ -1213,85 +1204,6 @@ wheel==0.40.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1303,9 +1215,9 @@ zipp==3.16.2 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
@@ -1315,3 +1227,4 @@ setuptools==68.0.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_linux_lock.txt b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_linux_lock.txt
index b73330864..8c2afe95f 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_linux_lock.txt
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_linux_lock.txt
@@ -16,15 +16,15 @@ appdirs==1.4.4 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ptpython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -358,10 +358,11 @@ docutils==0.20.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# breathe
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# stack-data
@@ -369,30 +370,30 @@ furo==2023.8.19 \
--hash=sha256:12f99f87a1873b6746228cfde18f77244e6c1ffb85d7fed95e638aae70d80590 \
--hash=sha256:e671ee638ab3f1b472f4033b0167f502ab407830e0db0f843b1c1028119c9cd1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -465,16 +466,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
@@ -516,9 +518,9 @@ isort==5.10.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -537,53 +539,22 @@ kconfiglib==14.1.0 \
--hash=sha256:bed2cc2216f538eca4255a83a4588d8823563cdd50114f86cf1a2674e602c93c \
--hash=sha256:edcd35a20e7e138a9a9e96149027f805f785e818d2eae400b6fa8b0c8845114a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -591,6 +562,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -599,6 +571,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -606,9 +579,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -627,7 +603,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# jinja2
@@ -643,29 +621,34 @@ mccabe==0.6.1 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -674,9 +657,9 @@ mypy-extensions==1.0.0 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -686,9 +669,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -714,9 +697,9 @@ pickleshare==0.7.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -732,33 +715,23 @@ prompt-toolkit==3.0.39 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -825,9 +798,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -842,6 +815,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -853,7 +830,9 @@ pyusb==1.2.1 \
--hash=sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -861,7 +840,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -869,9 +851,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -886,7 +871,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -918,16 +905,15 @@ six==1.16.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-soupsieve==2.4.1 \
- --hash=sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8 \
- --hash=sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea
+soupsieve==2.5 \
+ --hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
+ --hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# beautifulsoup4
@@ -942,6 +928,7 @@ sphinx==7.1.2 \
# sphinx-basic-ng
# sphinx-copybutton
# sphinx-design
+ # sphinx-sitemap
sphinx-argparse==0.4.0 \
--hash=sha256:73bee01f7276fae2bf621ccfe4d167af7306e7288e3482005405d9f826f9b037 \
--hash=sha256:e0f34184eb56f12face774fbc87b880abdb9017a0998d1ec559b267e9697e449
@@ -960,6 +947,10 @@ sphinx-design==0.5.0 \
--hash=sha256:1af1267b4cea2eedd6724614f19dcc88fe2e15aff65d06b2f6252cee9c4f4c1e \
--hash=sha256:e8e513acea6f92d15c6de3b34e954458f245b8e761b45b63950f65373352ab00
# via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
+sphinx-sitemap==2.5.1 \
+ --hash=sha256:0b7bce2835f287687f75584d7695e4eb8efaec028e5e7b36e9f791de3c344686 \
+ --hash=sha256:984bef068bbdbc26cfae209a8b61392e9681abc9191b477cd30da406e3a60ee5
+ # via -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
sphinxcontrib-applehelp==1.0.4 \
--hash=sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228 \
--hash=sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e
@@ -1000,9 +991,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# ipython
@@ -1040,9 +1031,9 @@ types-docutils==0.20.0.3 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# mypy-protobuf
@@ -1207,85 +1198,6 @@ wheel==0.40.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1297,9 +1209,9 @@ zipp==3.16.2 \
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
@@ -1309,3 +1221,4 @@ setuptools==68.0.0 \
# via
# -r /python/gen/pw_env_setup/pigweed_build_venv/compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_windows_lock.txt b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_windows_lock.txt
index 98865ed26..37fbbc8c2 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_windows_lock.txt
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/upstream_requirements_windows_lock.txt
@@ -16,15 +16,15 @@ appdirs==1.4.4 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ptpython
-astroid==2.15.6 \
- --hash=sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c \
- --hash=sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd
+astroid==3.0.1 \
+ --hash=sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca \
+ --hash=sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-asttokens==2.2.1 \
- --hash=sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3 \
- --hash=sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c
+asttokens==2.4.0 \
+ --hash=sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e \
+ --hash=sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# stack-data
@@ -368,10 +368,11 @@ docutils==0.20.1 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# breathe
+ # python-daemon
# sphinx
-executing==1.2.0 \
- --hash=sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc \
- --hash=sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107
+executing==2.0.0 \
+ --hash=sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657 \
+ --hash=sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# stack-data
@@ -379,30 +380,30 @@ furo==2023.8.19 \
--hash=sha256:12f99f87a1873b6746228cfde18f77244e6c1ffb85d7fed95e638aae70d80590 \
--hash=sha256:e671ee638ab3f1b472f4033b0167f502ab407830e0db0f843b1c1028119c9cd1
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
-google-api-core==2.7.1 \
- --hash=sha256:6be1fc59e2a7ba9f66808bbc22f976f81e4c3e7ab20fa0620ce42686288787d0 \
- --hash=sha256:b0fa577e512f0c8e063386b974718b8614586a798c5894ed34bedf256d9dae24
+google-api-core==2.12.0 \
+ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \
+ --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-core
# google-cloud-storage
-google-auth==2.6.3 \
- --hash=sha256:5e079eb4d21df1853d55cf2b6766b77ef36f7f7bdaf7d4a70434aa97c7578d60 \
- --hash=sha256:d65bb0e3701eaaa64fd2aa85e1325580524b0bddc6dc5db3ab89c481b6a20141
+google-auth==2.23.3 \
+ --hash=sha256:6864247895eea5d13b9c57c9e03abb49cb94ce2dc7c58e91cba3248c7477c9e3 \
+ --hash=sha256:a8f4608e65c244ead9e0538f181a96c6e11199ec114d41f1d7b1bffa96937bda
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
# google-cloud-core
# google-cloud-storage
-google-cloud-core==2.2.3 \
- --hash=sha256:89d2f7189bc6dc74de128d423ea52cc8719f0a5dbccd9ca80433f6504a20255c \
- --hash=sha256:a423852f4c36622376c8f0be509b67533690e061062368b763b92694c4ee06a7
+google-cloud-core==2.3.3 \
+ --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \
+ --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-storage
-google-cloud-storage==2.2.1 \
- --hash=sha256:0244f4612710cb5ec445fc6774387564e23f9823363fb408b28724e2102401b7 \
- --hash=sha256:abdf0fadf26516172e804e00b9c24819a3b3f7351cd32f35ca249bbfac965494
+google-cloud-storage==2.12.0 \
+ --hash=sha256:57c0bcda2f5e11f008a155d8636d8381d5abab46b58e0cae0e46dd5e595e6b46 \
+ --hash=sha256:bc52563439d42981b6e21b071a76da2791672776eda3ba99d13a8061ebbd6e5e
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
google-crc32c==1.5.0 \
--hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \
@@ -475,16 +476,17 @@ google-crc32c==1.5.0 \
--hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+ # google-cloud-storage
# google-resumable-media
-google-resumable-media==2.3.2 \
- --hash=sha256:06924e8b1e79f158f0202e7dd151ad75b0ea9d59b997c850f56bdd4a5a361513 \
- --hash=sha256:3c13f84813861ac8f5b6371254bdd437076bf1f3bac527a9f3fd123a70166f52
+google-resumable-media==2.6.0 \
+ --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \
+ --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-cloud-storage
-googleapis-common-protos==1.56.2 \
- --hash=sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c \
- --hash=sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3
+googleapis-common-protos==1.61.0 \
+ --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \
+ --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
@@ -526,9 +528,9 @@ isort==5.10.1 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-jedi==0.18.1 \
- --hash=sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d \
- --hash=sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab
+jedi==0.19.1 \
+ --hash=sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd \
+ --hash=sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
@@ -547,53 +549,22 @@ kconfiglib==14.1.0 \
--hash=sha256:bed2cc2216f538eca4255a83a4588d8823563cdd50114f86cf1a2674e602c93c \
--hash=sha256:edcd35a20e7e138a9a9e96149027f805f785e818d2eae400b6fa8b0c8845114a
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
-lazy-object-proxy==1.9.0 \
- --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \
- --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \
- --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \
- --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \
- --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \
- --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \
- --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \
- --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \
- --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \
- --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \
- --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \
- --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \
- --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \
- --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \
- --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \
- --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \
- --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \
- --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \
- --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \
- --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \
- --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \
- --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \
- --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \
- --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \
- --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \
- --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \
- --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \
- --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \
- --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \
- --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \
- --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \
- --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \
- --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \
- --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \
- --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \
- --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59
+lockfile==0.12.2 \
+ --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \
+ --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
- # astroid
+ # python-daemon
markupsafe==2.1.3 \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
--hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
--hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
--hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
+ --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
--hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
--hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
--hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
@@ -601,6 +572,7 @@ markupsafe==2.1.3 \
--hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
--hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
--hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
--hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
--hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
--hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
@@ -609,6 +581,7 @@ markupsafe==2.1.3 \
--hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
--hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
--hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
--hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
--hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
--hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
@@ -616,9 +589,12 @@ markupsafe==2.1.3 \
--hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
--hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
--hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
--hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
--hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
--hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
--hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
--hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
--hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
@@ -637,7 +613,9 @@ markupsafe==2.1.3 \
--hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
--hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
--hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
+ --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# jinja2
@@ -653,29 +631,34 @@ mccabe==0.6.1 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pylint
-mypy==1.5.0 \
- --hash=sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf \
- --hash=sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f \
- --hash=sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f \
- --hash=sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823 \
- --hash=sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735 \
- --hash=sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be \
- --hash=sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0 \
- --hash=sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4 \
- --hash=sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718 \
- --hash=sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f \
- --hash=sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6 \
- --hash=sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a \
- --hash=sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc \
- --hash=sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d \
- --hash=sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1 \
- --hash=sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374 \
- --hash=sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813 \
- --hash=sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c \
- --hash=sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564 \
- --hash=sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4 \
- --hash=sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212 \
- --hash=sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69
+mypy==1.6.1 \
+ --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \
+ --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \
+ --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \
+ --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \
+ --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \
+ --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \
+ --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \
+ --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \
+ --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \
+ --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \
+ --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \
+ --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \
+ --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \
+ --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \
+ --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \
+ --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \
+ --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \
+ --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \
+ --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \
+ --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \
+ --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \
+ --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \
+ --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \
+ --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \
+ --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \
+ --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \
+ --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
@@ -684,9 +667,9 @@ mypy-extensions==1.0.0 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# black
# mypy
-mypy-protobuf==3.3.0 \
- --hash=sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d \
- --hash=sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248
+mypy-protobuf==3.5.0 \
+ --hash=sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393 \
+ --hash=sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
packaging==23.0 \
--hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \
@@ -696,9 +679,9 @@ packaging==23.0 \
# black
# build
# sphinx
-parameterized==0.8.1 \
- --hash=sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c \
- --hash=sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9
+parameterized==0.9.0 \
+ --hash=sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b \
+ --hash=sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
parso==0.8.3 \
--hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \
@@ -718,9 +701,9 @@ pickleshare==0.7.5 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
-pip-tools==7.0.0 \
- --hash=sha256:6a2308712727c86cc8a6cedc0e6ba01232a337c706d63926d3789462ad083d06 \
- --hash=sha256:ae185db747195c8ed011866c366279cbb64f7f8c1528e7a828f515bd2bb0b31b
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
platformdirs==3.0.0 \
--hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \
@@ -736,33 +719,23 @@ prompt-toolkit==3.0.39 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
# ptpython
-protobuf==3.20.3 \
- --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \
- --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \
- --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \
- --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \
- --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \
- --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \
- --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \
- --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \
- --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \
- --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \
- --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \
- --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \
- --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \
- --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \
- --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \
- --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \
- --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \
- --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \
- --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \
- --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \
- --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \
- --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee
+protobuf==4.24.4 \
+ --hash=sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe \
+ --hash=sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085 \
+ --hash=sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b \
+ --hash=sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667 \
+ --hash=sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37 \
+ --hash=sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92 \
+ --hash=sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4 \
+ --hash=sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b \
+ --hash=sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9 \
+ --hash=sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd \
+ --hash=sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e \
+ --hash=sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46 \
+ --hash=sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# google-api-core
- # google-cloud-storage
# googleapis-common-protos
# mypy-protobuf
psutil==5.9.4 \
@@ -823,9 +796,9 @@ pygments==2.16.1 \
# ipython
# ptpython
# sphinx
-pylint==2.17.5 \
- --hash=sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413 \
- --hash=sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252
+pylint==3.0.1 \
+ --hash=sha256:81c6125637be216b4652ae50cc42b9f8208dfb725cdc7e04c48f6902f4dbdf40 \
+ --hash=sha256:9c90b89e2af7809a1697f6f5f93f1d0e518ac566e2ac4d2af881a69c13ad01ea
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
pyperclip==1.8.2 \
--hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
@@ -846,6 +819,10 @@ pyserial==3.5 \
--hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \
--hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+python-daemon==3.0.1 \
+ --hash=sha256:42bb848a3260a027fa71ad47ecd959e471327cb34da5965962edd5926229f341 \
+ --hash=sha256:6c57452372f7eaff40934a1c03ad1826bf5e793558e87fef49131e6464b4dae5
+ # via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
pytz==2023.3 \
--hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \
--hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb
@@ -857,7 +834,9 @@ pyusb==1.2.1 \
--hash=sha256:a4cc7404a203144754164b8b40994e2849fde1cfff06b08492f12fff9d9de7b9
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
pyyaml==6.0.1 \
+ --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
+ --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@@ -865,7 +844,10 @@ pyyaml==6.0.1 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
+ --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
+ --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
+ --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@@ -873,9 +855,12 @@ pyyaml==6.0.1 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
+ --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
+ --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
+ --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@@ -890,7 +875,9 @@ pyyaml==6.0.1 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
+ --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
+ --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
@@ -922,16 +909,15 @@ six==1.16.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# asttokens
- # google-auth
snowballstemmer==2.2.0 \
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# sphinx
-soupsieve==2.4.1 \
- --hash=sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8 \
- --hash=sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea
+soupsieve==2.5 \
+ --hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
+ --hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# beautifulsoup4
@@ -946,6 +932,7 @@ sphinx==7.1.2 \
# sphinx-basic-ng
# sphinx-copybutton
# sphinx-design
+ # sphinx-sitemap
sphinx-argparse==0.4.0 \
--hash=sha256:73bee01f7276fae2bf621ccfe4d167af7306e7288e3482005405d9f826f9b037 \
--hash=sha256:e0f34184eb56f12face774fbc87b880abdb9017a0998d1ec559b267e9697e449
@@ -964,6 +951,10 @@ sphinx-design==0.5.0 \
--hash=sha256:1af1267b4cea2eedd6724614f19dcc88fe2e15aff65d06b2f6252cee9c4f4c1e \
--hash=sha256:e8e513acea6f92d15c6de3b34e954458f245b8e761b45b63950f65373352ab00
# via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
+sphinx-sitemap==2.5.1 \
+ --hash=sha256:0b7bce2835f287687f75584d7695e4eb8efaec028e5e7b36e9f791de3c344686 \
+ --hash=sha256:984bef068bbdbc26cfae209a8b61392e9681abc9191b477cd30da406e3a60ee5
+ # via -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
sphinxcontrib-applehelp==1.0.4 \
--hash=sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228 \
--hash=sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e
@@ -1004,9 +995,9 @@ sphinxcontrib-serializinghtml==1.1.5 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# sphinx
-stack-data==0.6.2 \
- --hash=sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815 \
- --hash=sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8
+stack-data==0.6.3 \
+ --hash=sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9 \
+ --hash=sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# ipython
@@ -1044,9 +1035,9 @@ types-docutils==0.20.0.3 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# types-pygments
-types-protobuf==3.20.4.6 \
- --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \
- --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965
+types-protobuf==4.24.0.2 \
+ --hash=sha256:598bb2290b9b0ea65f4f63569a09deaa4475edd7bb0d8589057a38deb0b19f4f \
+ --hash=sha256:b86b0deefd1cb1582d355be4fd7a2a807cf49c993d9744d3c9fbe1cbf1e6b044
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# mypy-protobuf
@@ -1211,85 +1202,6 @@ wheel==0.40.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
-wrapt==1.15.0 \
- --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
- --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
- --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
- --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
- --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
- --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
- --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
- --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
- --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
- --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
- --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
- --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
- --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
- --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
- --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
- --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
- --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
- --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
- --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
- --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
- --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
- --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
- --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
- --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
- --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
- --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
- --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
- --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
- --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
- --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
- --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
- --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
- --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
- --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
- --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
- --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
- --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
- --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
- --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
- --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
- --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
- --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
- --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
- --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
- --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
- --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
- --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
- --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
- --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
- --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
- --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
- --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
- --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
- --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
- --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
- --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
- --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
- --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
- --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
- --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
- --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
- --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
- --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
- --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
- --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
- --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
- --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
- --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
- --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
- --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
- --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
- --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
- --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
- --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
- --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
- # via
- # -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
- # astroid
yapf==0.31.0 \
--hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \
--hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e
@@ -1301,9 +1213,9 @@ zipp==3.16.2 \
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.2 \
- --hash=sha256:78e5353a9dda374b462f2054f83a7b63f3f065c98236a68361845c1b0ee7e35f \
- --hash=sha256:a160a170f3331d9ca1a0247eb1cd79c758879f1f81158f9cd05bbb5df80bea5c
+pip==23.2.1 \
+ --hash=sha256:7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be \
+ --hash=sha256:fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
@@ -1313,3 +1225,4 @@ setuptools==68.0.0 \
# via
# -r \python\gen\pw_env_setup\pigweed_build_venv\compiled_requirements.txt
# pip-tools
+ # python-daemon
diff --git a/pw_format/BUILD.gn b/pw_format/BUILD.gn
new file mode 100644
index 000000000..394bbd336
--- /dev/null
+++ b/pw_format/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_bloat/bloat.gni")
+import("$dir_pw_build/module_config.gni")
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_fuzzer/fuzzer.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_unit_test/test.gni")
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
+
+# Since this module only contains Rust code, it is only built and tested under
+# Bazel at the moment.
+pw_test_group("tests") {
+ tests = []
+}
diff --git a/pw_format/docs.rst b/pw_format/docs.rst
new file mode 100644
index 000000000..3cd388d76
--- /dev/null
+++ b/pw_format/docs.rst
@@ -0,0 +1,22 @@
+.. _module-pw_format:
+
+=========
+pw_format
+=========
+.. pigweed-module::
+ :name: pw_format
+ :tagline: String formatting
+ :status: experimental
+ :languages: Rust
+
+``pw_format`` supports parsing ``printf`` and Rust ``core::fmt`` style format
+strings and using them to format output in languages other than C/C++.
+
+Disambiguation: If you're looking for code formatting support, see
+:ref:`pw_presubmit <module-pw_presubmit>`.
+
+----
+Rust
+----
+``pw_format``'s Rust API is documented in the
+`pw_format crate's docs </rustdoc/pw_format>`_.
diff --git a/pw_format/rust/BUILD.bazel b/pw_format/rust/BUILD.bazel
new file mode 100644
index 000000000..e7b85a9a6
--- /dev/null
+++ b/pw_format/rust/BUILD.bazel
@@ -0,0 +1,112 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load("@rules_rust//rust:defs.bzl", "rust_doc", "rust_doc_test", "rust_library", "rust_proc_macro", "rust_test")
+
+rust_library(
+ name = "pw_format",
+ srcs = [
+ "pw_format/lib.rs",
+ "pw_format/macros.rs",
+ "pw_format/tests.rs",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//pw_status/rust:pw_status",
+ "@rust_crates//:nom",
+ "@rust_crates//:proc-macro2",
+ "@rust_crates//:quote",
+ "@rust_crates//:syn",
+ ],
+)
+
+rust_test(
+ name = "pw_format_test",
+ crate = ":pw_format",
+)
+
+rust_doc_test(
+ name = "pw_format_doc_test",
+ crate = ":pw_format",
+)
+
+rust_doc(
+ name = "pw_format_doc",
+ crate = ":pw_format",
+)
+
+rust_proc_macro(
+ name = "pw_format_example_macro",
+ srcs = [
+ "pw_format_example_macro.rs",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":pw_format",
+ "//pw_status/rust:pw_status",
+ "@rust_crates//:proc-macro2",
+ "@rust_crates//:quote",
+ "@rust_crates//:syn",
+ ],
+)
+
+rust_library(
+ name = "pw_format_example_macro_test",
+ srcs = [
+ "pw_format_example_macro_test.rs",
+ ],
+ proc_macro_deps = [
+ ":pw_format_example_macro",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+rust_test(
+ name = "pw_format_example_macro_test_test",
+ crate = ":pw_format_example_macro_test",
+)
+
+rust_proc_macro(
+ name = "pw_format_test_macros",
+ srcs = [
+ "pw_format_test_macros.rs",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":pw_format",
+ "//pw_status/rust:pw_status",
+ "@rust_crates//:proc-macro2",
+ "@rust_crates//:quote",
+ "@rust_crates//:syn",
+ ],
+)
+
+rust_library(
+ name = "pw_format_test_macros_test",
+ srcs = [
+ "pw_format_test_macros_test.rs",
+ ],
+ proc_macro_deps = [
+ ":pw_format_test_macros",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":pw_format",
+ ],
+)
+
+rust_test(
+ name = "pw_format_test_macros_test_test",
+ crate = ":pw_format_test_macros_test",
+)
diff --git a/pw_tokenizer/rust/pw_tokenizer_printf/lib.rs b/pw_format/rust/pw_format/lib.rs
index 9ac56ca83..59954a1f3 100644
--- a/pw_tokenizer/rust/pw_tokenizer_printf/lib.rs
+++ b/pw_format/rust/pw_format/lib.rs
@@ -12,20 +12,24 @@
// License for the specific language governing permissions and limitations under
// the License.
-//! The `pw_tokenizer_printf` crate is a parser used by `pw_tokenizer`'s
-//! proc macros to:
-//! * Understand tokenization argument types at compile time.
+//! The `pw_format` crate is a parser used to implement proc macros that:
+//! * Understand format string argument types at compile time.
//! * Syntax check format strings.
//!
-//! `pw_tokenizer_printf` is written against `std` and is not intended to be
+//! `pw_format` is written against `std` and is not intended to be
//! used in an embedded context. Some efficiency and memory is traded for a
//! more expressive interface that exposes the format string's "syntax tree"
//! to the API client.
//!
+//! # Proc Macros
+//!
+//! The [`macros`] module provides infrastructure for implementing proc macros
+//! that take format strings as arguments.
+//!
//! # Example
//!
//! ```
-//! use pw_tokenizer_printf::{
+//! use pw_format::{
//! ConversionSpec, Flag, FormatFragment, FormatString, Length, Precision, Specifier, MinFieldWidth,
//! };
//!
@@ -34,7 +38,7 @@
//!
//! assert_eq!(format_string, FormatString {
//! fragments: vec![
-//! FormatFragment::Literal("long double "),
+//! FormatFragment::Literal("long double ".to_string()),
//! FormatFragment::Conversion(ConversionSpec {
//! flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
//! min_field_width: MinFieldWidth::Fixed(4),
@@ -42,7 +46,7 @@
//! length: Some(Length::LongDouble),
//! specifier: Specifier::SmallDouble
//! }),
-//! FormatFragment::Literal(" is "),
+//! FormatFragment::Literal(" is ".to_string()),
//! FormatFragment::Conversion(ConversionSpec {
//! flags: [Flag::LeftJustify, Flag::LeadingZeros]
//! .into_iter()
@@ -53,7 +57,7 @@
//! specifier: Specifier::Decimal
//! }),
//! FormatFragment::Percent,
-//! FormatFragment::Literal("."),
+//! FormatFragment::Literal(".".to_string()),
//! ]
//! });
//! ```
@@ -71,6 +75,8 @@ use nom::{
IResult,
};
+pub mod macros;
+
#[derive(Debug, Clone, PartialEq, Eq)]
/// A printf specifier (the 'd' in %d).
pub enum Specifier {
@@ -253,9 +259,9 @@ pub struct ConversionSpec {
#[derive(Debug, PartialEq, Eq)]
/// A fragment of a printf format string.
-pub enum FormatFragment<'a> {
+pub enum FormatFragment {
/// A literal string value.
- Literal(&'a str),
+ Literal(String),
/// A conversion specification (i.e. %d).
Conversion(ConversionSpec),
@@ -266,9 +272,9 @@ pub enum FormatFragment<'a> {
#[derive(Debug, PartialEq, Eq)]
/// A parsed printf format string.
-pub struct FormatString<'a> {
+pub struct FormatString {
/// The [FormatFragment]s that comprise the [FormatString].
- pub fragments: Vec<FormatFragment<'a>>,
+ pub fragments: Vec<FormatFragment>,
}
fn specifier(input: &str) -> IResult<&str, Specifier> {
@@ -360,7 +366,9 @@ fn conversion_spec(input: &str) -> IResult<&str, ConversionSpec> {
}
fn literal_fragment(input: &str) -> IResult<&str, FormatFragment> {
- map(take_till1(|c| c == '%'), FormatFragment::Literal)(input)
+ map(take_till1(|c| c == '%'), |s: &str| {
+ FormatFragment::Literal(s.to_string())
+ })(input)
}
fn percent_fragment(input: &str) -> IResult<&str, FormatFragment> {
@@ -381,9 +389,9 @@ fn format_string(input: &str) -> IResult<&str, FormatString> {
Ok((input, FormatString { fragments }))
}
-impl<'a> FormatString<'a> {
+impl FormatString {
/// Parses a printf style format string.
- pub fn parse(s: &'a str) -> Result<Self, String> {
+ pub fn parse(s: &str) -> Result<Self, String> {
// TODO: b/281858500 - Add better errors to failed parses.
let (rest, result) =
format_string(s).map_err(|e| format!("Failed to parse format string \"{s}\": {e}"))?;
diff --git a/pw_format/rust/pw_format/macros.rs b/pw_format/rust/pw_format/macros.rs
new file mode 100644
index 000000000..1c049471d
--- /dev/null
+++ b/pw_format/rust/pw_format/macros.rs
@@ -0,0 +1,487 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+//! The `macro` module provides helpers that simplify writing proc macros
+//! that take format strings and arguments. This is accomplish with three
+//! main constructs:
+//! * [`FormatAndArgs`]: A struct that implements [syn::parse::Parse] to
+//! parse a format string and its following arguments.
+//! * [`FormatMacroGenerator`]: A trait used to implement the macro specific
+//! logic to generate code.
+//! * [`generate`]: A function to handle the execution of the proc macro by
+//! calling into a [FormatMacroGenerator].
+//!
+//! Additionally [`PrintfFormatMacroGenerator`] trait and [`generate_printf`]
+//! function are provided to help when implementing generators that need to
+//! produce `printf` style format strings as part of their code generation.
+//!
+//! ## Example
+//!
+//! An example of implementing a proc macro is provided in the
+//! [pw_format_example_macro crate](https://pigweed.googlesource.com/pigweed/pigweed/+/refs/heads/main/pw_format/rust/pw_format_example_macro.rs)
+//!
+//!
+
+use std::collections::VecDeque;
+
+use proc_macro2::Ident;
+use quote::{format_ident, quote, ToTokens};
+use syn::{
+ parse::{Parse, ParseStream},
+ punctuated::Punctuated,
+ Expr, LitStr, Token,
+};
+
+use crate::{
+ ConversionSpec, FormatFragment, FormatString, Length, MinFieldWidth, Precision, Specifier,
+};
+
+type TokenStream2 = proc_macro2::TokenStream;
+
+/// An error occurring during proc macro evaluation.
+///
+/// In order to stay as flexible as possible to implementors of
+/// [`FormatMacroGenerator`], the error is simply represent by a
+/// string.
+#[derive(Debug)]
+pub struct Error {
+ text: String,
+}
+
+impl Error {
+ /// Create a new proc macro evaluation error.
+ pub fn new(text: &str) -> Self {
+ Self {
+ text: text.to_string(),
+ }
+ }
+}
+
+/// An alias for a Result with an ``Error``
+pub type Result<T> = core::result::Result<T, Error>;
+
+/// Style in which to display the an integer.
+///
+/// In order to maintain compatibility with `printf` style systems, sign
+/// and base are combined.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum IntegerDisplayType {
+ /// Signed integer
+ Signed,
+ /// Unsigned integer
+ Unsigned,
+ /// Unsigned octal
+ Octal,
+ /// Unsigned hex with lower case letters
+ Hex,
+ /// Unsigned hex with upper case letters
+ UpperHex,
+}
+
+impl TryFrom<crate::Specifier> for IntegerDisplayType {
+ type Error = Error;
+
+ fn try_from(value: Specifier) -> Result<Self> {
+ match value {
+ Specifier::Decimal | Specifier::Integer => Ok(Self::Signed),
+ Specifier::Unsigned => Ok(Self::Unsigned),
+ Specifier::Octal => Ok(Self::Octal),
+ Specifier::Hex => Ok(Self::Hex),
+ Specifier::UpperHex => Ok(Self::UpperHex),
+ _ => Err(Error::new("No valid IntegerDisplayType for {value:?}.")),
+ }
+ }
+}
+
+/// Implemented for testing through the pw_format_test_macros crate.
+impl ToTokens for IntegerDisplayType {
+ fn to_tokens(&self, tokens: &mut TokenStream2) {
+ let new_tokens = match self {
+ IntegerDisplayType::Signed => quote!(pw_format::macros::IntegerDisplayType::Signed),
+ IntegerDisplayType::Unsigned => {
+ quote!(pw_format::macros::IntegerDisplayType::Unsigned)
+ }
+ IntegerDisplayType::Octal => quote!(pw_format::macros::IntegerDisplayType::Octal),
+ IntegerDisplayType::Hex => quote!(pw_format::macros::IntegerDisplayType::Hex),
+ IntegerDisplayType::UpperHex => {
+ quote!(pw_format::macros::IntegerDisplayType::UpperHex)
+ }
+ };
+ new_tokens.to_tokens(tokens);
+ }
+}
+
+/// A code generator for implementing a `pw_format` style macro.
+///
+/// This trait serves as the primary interface between `pw_format` and a
+/// proc macro using it to implement format string and argument parsing. When
+/// evaluating the proc macro and generating code, [`generate`] will make
+/// repeated calls to [`string_fragment`](FormatMacroGenerator::string_fragment)
+/// and the conversion functions. These calls will be made in the order they
+/// appear in the format string. After all fragments and conversions are
+/// processed, [`generate`] will call
+/// [`finalize`](FormatMacroGenerator::finalize).
+///
+/// For an example of implementing a `FormatMacroGenerator` see the
+/// [pw_format_example_macro crate](https://pigweed.googlesource.com/pigweed/pigweed/+/refs/heads/main/pw_format/rust/pw_format_example_macro.rs).
+pub trait FormatMacroGenerator {
+ /// Called by [`generate`] at the end of code generation.
+ ///
+ /// Consumes `self` and returns the code to be emitted by the proc macro of
+ /// and [`Error`].
+ fn finalize(self) -> Result<TokenStream2>;
+
+ /// Process a string fragment.
+ ///
+ /// A string fragment is a string of characters that appear in a format
+ /// string. This is different than a
+ /// [`string_conversion`](FormatMacroGenerator::string_conversion) which is
+ /// a string provided through a conversion specifier (i.e. `"%s"`).
+ fn string_fragment(&mut self, string: &str) -> Result<()>;
+
+ /// Process an integer conversion.
+ fn integer_conversion(
+ &mut self,
+ display: IntegerDisplayType,
+ type_width: u8, // This should probably be an enum
+ expression: Expr,
+ ) -> Result<()>;
+
+ /// Process a string conversion.
+ ///
+ /// See [`string_fragment`](FormatMacroGenerator::string_fragment) for a
+ /// disambiguation between that function and this one.
+ fn string_conversion(&mut self, expression: Expr) -> Result<()>;
+
+ /// Process a character conversion.
+ fn char_conversion(&mut self, expression: Expr) -> Result<()>;
+}
+
+/// A parsed format string and it's arguments.
+///
+/// `FormatAndArgs` implements [`syn::parse::Parse`] and can be used to parse
+/// arguments to proc maros that take format strings. Arguments are parsed
+/// according to the pattern: `($format_string:literal, $($args:expr),*)`
+#[derive(Debug)]
+pub struct FormatAndArgs {
+ format_string: LitStr,
+ parsed: FormatString,
+ args: VecDeque<Expr>,
+}
+
+impl Parse for FormatAndArgs {
+ fn parse(input: ParseStream) -> syn::parse::Result<Self> {
+ let format_string = input.parse::<LitStr>()?;
+
+ let args = if input.is_empty() {
+ // If there are no more tokens, no arguments were specified.
+ VecDeque::new()
+ } else {
+ // Eat the `,` following the format string.
+ input.parse::<Token![,]>()?;
+
+ let punctuated = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
+ punctuated.into_iter().collect()
+ };
+
+ let parsed = FormatString::parse(&format_string.value()).map_err(|e| {
+ syn::Error::new_spanned(
+ format_string.to_token_stream(),
+ format!("Error parsing format string {e}"),
+ )
+ })?;
+
+ Ok(FormatAndArgs {
+ format_string,
+ parsed,
+ args,
+ })
+ }
+}
+
+// Grab the next argument returning a descriptive error if no more args are left.
+fn next_arg(spec: &ConversionSpec, args: &mut VecDeque<Expr>) -> Result<Expr> {
+ args.pop_front()
+ .ok_or_else(|| Error::new(&format!("No argument given for {spec:?}")))
+}
+
+// Handle a single format conversion specifier (i.e. `%08x`). Grabs the
+// necessary arguments for the specifier from `args` and generates code
+// to marshal the arguments into the buffer declared in `_tokenize_to_buffer`.
+// Returns an error if args is too short of if a format specifier is unsupported.
+fn handle_conversion(
+ generator: &mut dyn FormatMacroGenerator,
+ spec: &ConversionSpec,
+ args: &mut VecDeque<Expr>,
+) -> Result<()> {
+ match spec.specifier {
+ Specifier::Decimal
+ | Specifier::Integer
+ | Specifier::Octal
+ | Specifier::Unsigned
+ | Specifier::Hex
+ | Specifier::UpperHex => {
+ // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
+ if spec.min_field_width == MinFieldWidth::Variable {
+ return Err(Error::new(
+ "Variable width '*' integer formats are not supported.",
+ ));
+ }
+
+ if spec.precision == Precision::Variable {
+ return Err(Error::new(
+ "Variable precision '*' integer formats are not supported.",
+ ));
+ }
+
+ let arg = next_arg(spec, args)?;
+ let bits = match spec.length.unwrap_or(Length::Long) {
+ Length::Char => 8,
+ Length::Short => 16,
+ Length::Long => 32,
+ Length::LongLong => 64,
+ Length::IntMax => 64,
+ Length::Size => 32,
+ Length::PointerDiff => 32,
+ Length::LongDouble => {
+ return Err(Error::new(
+ "Long double length parameter invalid for integer formats",
+ ))
+ }
+ };
+
+ let display: IntegerDisplayType =
+ spec.specifier.clone().try_into().expect(
+ "Specifier is guaranteed to convert display type but enclosing match arm.",
+ );
+ generator.integer_conversion(display, bits, arg)
+ }
+ Specifier::String => {
+ // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
+ if spec.min_field_width == MinFieldWidth::Variable {
+ return Err(Error::new(
+ "Variable width '*' string formats are not supported.",
+ ));
+ }
+
+ if spec.precision == Precision::Variable {
+ return Err(Error::new(
+ "Variable precision '*' string formats are not supported.",
+ ));
+ }
+
+ let arg = next_arg(spec, args)?;
+ generator.string_conversion(arg)
+ }
+ Specifier::Char => {
+ let arg = next_arg(spec, args)?;
+ generator.char_conversion(arg)
+ }
+
+ Specifier::Double
+ | Specifier::UpperDouble
+ | Specifier::Exponential
+ | Specifier::UpperExponential
+ | Specifier::SmallDouble
+ | Specifier::UpperSmallDouble => {
+ // TODO: b/281862328 - Support floating point numbers.
+ Err(Error::new("Floating point numbers are not supported."))
+ }
+
+ // TODO: b/281862333 - Support pointers.
+ Specifier::Pointer => Err(Error::new("Pointer types are not supported.")),
+ }
+}
+
+/// Generate code for a `pw_format` style proc macro.
+///
+/// `generate` takes a [`FormatMacroGenerator`] and a [`FormatAndArgs`] struct
+/// and uses them to produce the code output for a proc macro.
+pub fn generate(
+ mut generator: impl FormatMacroGenerator,
+ format_and_args: FormatAndArgs,
+) -> core::result::Result<TokenStream2, syn::Error> {
+ let mut args = format_and_args.args;
+ let mut errors = Vec::new();
+
+ for fragment in format_and_args.parsed.fragments {
+ let result = match fragment {
+ FormatFragment::Conversion(spec) => handle_conversion(&mut generator, &spec, &mut args),
+ FormatFragment::Literal(string) => generator.string_fragment(&string),
+ FormatFragment::Percent => generator.string_fragment("%"),
+ };
+ if let Err(e) = result {
+ errors.push(syn::Error::new_spanned(
+ format_and_args.format_string.to_token_stream(),
+ e.text,
+ ));
+ }
+ }
+
+ if !errors.is_empty() {
+ return Err(errors
+ .into_iter()
+ .reduce(|mut accumulated_errors, error| {
+ accumulated_errors.combine(error);
+ accumulated_errors
+ })
+ .expect("errors should not be empty"));
+ }
+
+ generator.finalize().map_err(|e| {
+ syn::Error::new_spanned(format_and_args.format_string.to_token_stream(), e.text)
+ })
+}
+
+/// A specialized generator for proc macros that produce `printf` style format strings.
+///
+/// For proc macros that need to translate a `pw_format` invocation into a
+/// `printf` style format string, `PrintfFormatMacroGenerator` offer a
+/// specialized form of [`FormatMacroGenerator`] that builds the format string
+/// and provides it as an argument to
+/// [`finalize`](PrintfFormatMacroGenerator::finalize).
+///
+/// In cases where a generator needs to override the conversion specifier it
+/// can return it from its appropriate conversion method. An example of using
+/// this would be wanting to pass a Rust string directly to a `printf` call
+/// over FFI. In that case,
+/// [`string_conversion`](PrintfFormatMacroGenerator::string_conversion) could
+/// return `Ok(Some("%.*s".to_string()))` to allow both the length and string
+/// pointer to be passed to `printf`.
+pub trait PrintfFormatMacroGenerator {
+ /// Called by [`generate_printf`] at the end of code generation.
+ ///
+ /// Works like [`FormatMacroGenerator::finalize`] with the addition of
+ /// being provided a `printf_style` format string.
+ fn finalize(self, format_string: String) -> Result<TokenStream2>;
+
+ /// Process a string fragment.
+ ///
+ /// **NOTE**: This string may contain unescaped `%` characters.
+ ///
+ /// See [`FormatMacroGenerator::string_fragment`] for a disambiguation
+ /// between a string fragment and string conversion.
+ fn string_fragment(&mut self, string: &str) -> Result<()>;
+
+ /// Process an integer conversion.
+ ///
+ /// May optionally return a printf format string (i.e. "%d") to override the
+ /// default.
+ fn integer_conversion(&mut self, ty: Ident, expression: Expr) -> Result<Option<String>>;
+
+ /// Process a string conversion.
+ ///
+ /// May optionally return a printf format string (i.e. "%s") to override the
+ /// default.
+ ///
+ /// See [`FormatMacroGenerator::string_fragment`] for a disambiguation
+ /// between a string fragment and string conversion.
+ /// FIXME: docs
+ fn string_conversion(&mut self, expression: Expr) -> Result<Option<String>>;
+
+ /// Process a character conversion.
+ ///
+ /// May optionally return a printf format string (i.e. "%c") to override the
+ /// default.
+ fn char_conversion(&mut self, expression: Expr) -> Result<Option<String>>;
+}
+
+// Wraps a `PrintfFormatMacroGenerator` in a `FormatMacroGenerator` that
+// generates the format string as it goes.
+struct PrintfGenerator<GENERATOR: PrintfFormatMacroGenerator> {
+ inner: GENERATOR,
+ format_string: String,
+}
+
+impl<GENERATOR: PrintfFormatMacroGenerator> FormatMacroGenerator for PrintfGenerator<GENERATOR> {
+ fn finalize(self) -> Result<TokenStream2> {
+ self.inner.finalize(self.format_string)
+ }
+
+ fn string_fragment(&mut self, string: &str) -> Result<()> {
+ // Escape '%' characters.
+ let format_string = string.replace("%", "%%");
+
+ self.format_string.push_str(&format_string);
+ self.inner.string_fragment(string)
+ }
+
+ fn integer_conversion(
+ &mut self,
+ display: IntegerDisplayType,
+ type_width: u8, // in bits
+ expression: Expr,
+ ) -> Result<()> {
+ let length_modifer = match type_width {
+ 8 => "hh",
+ 16 => "h",
+ 32 => "",
+ 64 => "ll",
+ _ => {
+ return Err(Error::new(&format!(
+ "printf backend does not support {} bit field width",
+ type_width
+ )))
+ }
+ };
+
+ let (conversion, ty) = match display {
+ IntegerDisplayType::Signed => ("d", format_ident!("i{type_width}")),
+ IntegerDisplayType::Unsigned => ("u", format_ident!("u{type_width}")),
+ IntegerDisplayType::Octal => ("o", format_ident!("u{type_width}")),
+ IntegerDisplayType::Hex => ("x", format_ident!("u{type_width}")),
+ IntegerDisplayType::UpperHex => ("X", format_ident!("u{type_width}")),
+ };
+
+ match self.inner.integer_conversion(ty, expression)? {
+ Some(s) => self.format_string.push_str(&s),
+ None => self
+ .format_string
+ .push_str(&format!("%{}{}", length_modifer, conversion)),
+ }
+
+ Ok(())
+ }
+
+ fn string_conversion(&mut self, expression: Expr) -> Result<()> {
+ match self.inner.string_conversion(expression)? {
+ Some(s) => self.format_string.push_str(&s),
+ None => self.format_string.push_str("%s"),
+ }
+ Ok(())
+ }
+
+ fn char_conversion(&mut self, expression: Expr) -> Result<()> {
+ match self.inner.char_conversion(expression)? {
+ Some(s) => self.format_string.push_str(&s),
+ None => self.format_string.push_str("%c"),
+ }
+ Ok(())
+ }
+}
+
+/// Generate code for a `pw_format` style proc macro that needs a `printf` format string.
+///
+/// `generate_printf` is a specialized version of [`generate`] which works with
+/// [`PrintfFormatMacroGenerator`]
+pub fn generate_printf(
+ generator: impl PrintfFormatMacroGenerator,
+ format_and_args: FormatAndArgs,
+) -> core::result::Result<TokenStream2, syn::Error> {
+ let generator = PrintfGenerator {
+ inner: generator,
+ format_string: "".into(),
+ };
+ generate(generator, format_and_args)
+}
diff --git a/pw_tokenizer/rust/pw_tokenizer_printf/tests.rs b/pw_format/rust/pw_format/tests.rs
index f5b8c37c7..9a5cb7496 100644
--- a/pw_tokenizer/rust/pw_tokenizer_printf/tests.rs
+++ b/pw_format/rust/pw_format/tests.rs
@@ -172,7 +172,7 @@ fn test_format_string() {
"",
FormatString {
fragments: vec![
- FormatFragment::Literal("long double "),
+ FormatFragment::Literal("long double ".to_string()),
FormatFragment::Conversion(ConversionSpec {
flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
min_field_width: MinFieldWidth::Fixed(4),
@@ -180,7 +180,7 @@ fn test_format_string() {
length: Some(Length::LongDouble),
specifier: Specifier::SmallDouble
}),
- FormatFragment::Literal(" is "),
+ FormatFragment::Literal(" is ".to_string()),
FormatFragment::Conversion(ConversionSpec {
flags: [Flag::LeftJustify, Flag::LeadingZeros]
.into_iter()
@@ -191,7 +191,7 @@ fn test_format_string() {
specifier: Specifier::Decimal
}),
FormatFragment::Percent,
- FormatFragment::Literal("."),
+ FormatFragment::Literal(".".to_string()),
]
}
))
@@ -204,7 +204,7 @@ fn test_parse() {
FormatString::parse("long double %+ 4.2Lg is %-03hd%%."),
Ok(FormatString {
fragments: vec![
- FormatFragment::Literal("long double "),
+ FormatFragment::Literal("long double ".to_string()),
FormatFragment::Conversion(ConversionSpec {
flags: [Flag::ForceSign, Flag::SpaceSign].into_iter().collect(),
min_field_width: MinFieldWidth::Fixed(4),
@@ -212,7 +212,7 @@ fn test_parse() {
length: Some(Length::LongDouble),
specifier: Specifier::SmallDouble
}),
- FormatFragment::Literal(" is "),
+ FormatFragment::Literal(" is ".to_string()),
FormatFragment::Conversion(ConversionSpec {
flags: [Flag::LeftJustify, Flag::LeadingZeros]
.into_iter()
@@ -223,7 +223,7 @@ fn test_parse() {
specifier: Specifier::Decimal
}),
FormatFragment::Percent,
- FormatFragment::Literal("."),
+ FormatFragment::Literal(".".to_string()),
]
})
);
diff --git a/pw_format/rust/pw_format_example_macro.rs b/pw_format/rust/pw_format_example_macro.rs
new file mode 100644
index 000000000..3c9806f66
--- /dev/null
+++ b/pw_format/rust/pw_format_example_macro.rs
@@ -0,0 +1,142 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+use proc_macro::TokenStream;
+use pw_format::macros::{
+ generate, FormatAndArgs, FormatMacroGenerator, IntegerDisplayType, Result,
+};
+use quote::quote;
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input, Expr, Token,
+};
+
+type TokenStream2 = proc_macro2::TokenStream;
+
+// Declare a struct to hold our proc macro arguments.
+#[derive(Debug)]
+struct MacroArgs {
+ prefix: Expr,
+ format_and_args: FormatAndArgs,
+}
+
+// Implement `Parse` for our argument struct.
+impl Parse for MacroArgs {
+ fn parse(input: ParseStream) -> syn::parse::Result<Self> {
+ // Our prefix argument comes first argument and ends with a `,`.
+ let prefix: Expr = input.parse()?;
+ input.parse::<Token![,]>()?;
+
+ // Prase the remaining arguments as a format string and arguments.
+ let format_and_args: FormatAndArgs = input.parse()?;
+
+ Ok(MacroArgs {
+ prefix,
+ format_and_args,
+ })
+ }
+}
+
+// Our generator struct needs to track the prefix as well as the code
+// fragments we've generated.
+struct Generator {
+ prefix: Expr,
+ code_fragments: Vec<TokenStream2>,
+}
+
+impl Generator {
+ pub fn new(prefix: Expr) -> Self {
+ Self {
+ prefix,
+ code_fragments: Vec::new(),
+ }
+ }
+}
+
+// This toy example implements the generator by calling `format!()` at runtime.
+impl FormatMacroGenerator for Generator {
+ // Wrap all our fragments in boilerplate and return the code.
+ fn finalize(self) -> Result<TokenStream2> {
+ // Create locally scoped alias so we can refer to them in `quote!()`.
+ let prefix = self.prefix;
+ let code_fragments = self.code_fragments;
+
+ Ok(quote! {
+ {
+ // Initialize the result string with our prefix.
+ let mut result = String::new();
+ result.push_str(#prefix);
+
+ // Enumerate all our code fragments.
+ #(#code_fragments);*;
+
+ // Return the resulting string
+ result
+ }
+ })
+ }
+
+ // Handles a string embedded in the format string. This is different from
+ // `string_conversion` which is used to handle a string referenced by the
+ // format string (i.e. "%s'").
+ fn string_fragment(&mut self, string: &str) -> Result<()> {
+ self.code_fragments.push(quote! {
+ result.push_str(#string);
+ });
+ Ok(())
+ }
+
+ // This example ignores display type and width.
+ fn integer_conversion(
+ &mut self,
+ _display: IntegerDisplayType,
+ _type_width: u8, // in bits
+ expression: Expr,
+ ) -> Result<()> {
+ self.code_fragments.push(quote! {
+ result.push_str(&format!("{}", #expression));
+ });
+ Ok(())
+ }
+
+ fn string_conversion(&mut self, expression: Expr) -> Result<()> {
+ self.code_fragments.push(quote! {
+ result.push_str(&format!("{}", #expression));
+ });
+ Ok(())
+ }
+
+ fn char_conversion(&mut self, expression: Expr) -> Result<()> {
+ self.code_fragments.push(quote! {
+ result.push_str(&format!("{}", #expression));
+ });
+ Ok(())
+ }
+}
+
+// Lastly we declare our proc macro.
+#[proc_macro]
+pub fn example_macro(tokens: TokenStream) -> TokenStream {
+ // Parse our proc macro arguments.
+ let input = parse_macro_input!(tokens as MacroArgs);
+
+ // Create our generator.
+ let generator = Generator::new(input.prefix);
+
+ // Call into `generate()` to handle the code generation.
+ match generate(generator, input.format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
diff --git a/pw_format/rust/pw_format_example_macro_test.rs b/pw_format/rust/pw_format_example_macro_test.rs
new file mode 100644
index 000000000..f1c8dab05
--- /dev/null
+++ b/pw_format/rust/pw_format_example_macro_test.rs
@@ -0,0 +1,24 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#[cfg(test)]
+mod tests {
+ use pw_format_example_macro::example_macro;
+
+ #[test]
+ fn test() {
+ let string = example_macro!("the answer: ", "%d", 42);
+ assert_eq!(&string, "the answer: 42");
+ }
+}
diff --git a/pw_format/rust/pw_format_test_macros.rs b/pw_format/rust/pw_format_test_macros.rs
new file mode 100644
index 000000000..3c1c6643e
--- /dev/null
+++ b/pw_format/rust/pw_format_test_macros.rs
@@ -0,0 +1,227 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+use proc_macro::TokenStream;
+use proc_macro2::Ident;
+use pw_format::macros::{
+ generate, generate_printf, FormatAndArgs, FormatMacroGenerator, IntegerDisplayType,
+ PrintfFormatMacroGenerator, Result,
+};
+use quote::quote;
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input, Expr, Token,
+};
+
+type TokenStream2 = proc_macro2::TokenStream;
+
+// Generator for testing `generate()`.
+struct TestGenerator {
+ code_fragments: Vec<TokenStream2>,
+}
+
+impl TestGenerator {
+ pub fn new() -> Self {
+ Self {
+ code_fragments: Vec::new(),
+ }
+ }
+}
+
+impl FormatMacroGenerator for TestGenerator {
+ fn finalize(self) -> Result<TokenStream2> {
+ let code_fragments = self.code_fragments;
+
+ Ok(quote! {
+ {
+ use pw_format_test_macros_test::TestGeneratorOps;
+ let mut ops = Vec::new();
+ #(#code_fragments);*;
+ ops.push(TestGeneratorOps::Finalize);
+ ops
+ }
+ })
+ }
+
+ fn string_fragment(&mut self, string: &str) -> Result<()> {
+ self.code_fragments.push(quote! {
+ ops.push(TestGeneratorOps::StringFragment(#string.to_string()));
+ });
+ Ok(())
+ }
+
+ // This example ignores display type and width.
+ fn integer_conversion(
+ &mut self,
+ display_type: IntegerDisplayType,
+ type_width: u8, // in bits
+ _expression: Expr,
+ ) -> Result<()> {
+ self.code_fragments.push(quote! {
+ ops.push(TestGeneratorOps::IntegerConversion{
+ display_type: #display_type,
+ type_width: #type_width
+ });
+ });
+ Ok(())
+ }
+
+ fn string_conversion(&mut self, expression: Expr) -> Result<()> {
+ self.code_fragments.push(quote! {
+ ops.push(TestGeneratorOps::StringConversion);
+ });
+ Ok(())
+ }
+
+ fn char_conversion(&mut self, expression: Expr) -> Result<()> {
+ self.code_fragments.push(quote! {
+ ops.push(TestGeneratorOps::CharConversion);
+ });
+ Ok(())
+ }
+}
+
+#[proc_macro]
+pub fn generator_test_macro(tokens: TokenStream) -> TokenStream {
+ let format_and_args = parse_macro_input!(tokens as FormatAndArgs);
+ let generator = TestGenerator::new();
+ match generate(generator, format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+
+// Generator for testing `generate_printf()`. Allows control over the return
+// value of the conversion functions.
+struct PrintfTestGenerator {
+ format_string: Option<String>,
+ code_fragments: Vec<TokenStream2>,
+ integer_specifier_override: Option<String>,
+ string_specifier_override: Option<String>,
+ char_specifier_override: Option<String>,
+}
+
+impl PrintfTestGenerator {
+ pub fn new() -> Self {
+ Self {
+ format_string: Some(String::new()),
+ code_fragments: Vec::new(),
+ integer_specifier_override: None,
+ string_specifier_override: None,
+ char_specifier_override: None,
+ }
+ }
+
+ pub fn with_integer_specifier(mut self, specifier: &str) -> Self {
+ self.integer_specifier_override = Some(specifier.to_string());
+ self
+ }
+
+ pub fn with_string_specifier(mut self, specifier: &str) -> Self {
+ self.string_specifier_override = Some(specifier.to_string());
+ self
+ }
+
+ pub fn with_char_specifier(mut self, specifier: &str) -> Self {
+ self.char_specifier_override = Some(specifier.to_string());
+ self
+ }
+}
+
+impl PrintfFormatMacroGenerator for PrintfTestGenerator {
+ fn finalize(self, format_string: String) -> Result<TokenStream2> {
+ // Create locally scoped alias so we can refer to them in `quote!()`.
+ let code_fragments = self.code_fragments;
+
+ Ok(quote! {
+ {
+ use pw_format_test_macros_test::PrintfTestGeneratorOps;
+ let mut ops = Vec::new();
+ #(#code_fragments);*;
+ ops.push(PrintfTestGeneratorOps::Finalize);
+ (#format_string, ops)
+ }
+ })
+ }
+ fn string_fragment(&mut self, string: &str) -> Result<()> {
+ self.code_fragments.push(quote! {
+ ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string()));
+ });
+ Ok(())
+ }
+ fn integer_conversion(&mut self, ty: Ident, _expression: Expr) -> Result<Option<String>> {
+ let ty_str = format!("{ty}");
+ self.code_fragments.push(quote! {
+ ops.push(PrintfTestGeneratorOps::IntegerConversion{ ty: #ty_str.to_string() });
+ });
+ Ok(self.integer_specifier_override.clone())
+ }
+
+ fn string_conversion(&mut self, _expression: Expr) -> Result<Option<String>> {
+ self.code_fragments.push(quote! {
+ ops.push(PrintfTestGeneratorOps::StringConversion);
+ });
+ Ok(self.string_specifier_override.clone())
+ }
+
+ fn char_conversion(&mut self, _expression: Expr) -> Result<Option<String>> {
+ self.code_fragments.push(quote! {
+ ops.push(PrintfTestGeneratorOps::CharConversion);
+ });
+ Ok(self.char_specifier_override.clone())
+ }
+}
+
+#[proc_macro]
+pub fn printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
+ let format_and_args = parse_macro_input!(tokens as FormatAndArgs);
+ let generator = PrintfTestGenerator::new();
+ match generate_printf(generator, format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+
+// Causes the generator to substitute %d with %K.
+#[proc_macro]
+pub fn integer_sub_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
+ let format_and_args = parse_macro_input!(tokens as FormatAndArgs);
+ let generator = PrintfTestGenerator::new().with_integer_specifier("%K");
+ match generate_printf(generator, format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+
+// Causes the generator to substitute %s with %K.
+#[proc_macro]
+pub fn string_sub_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
+ let format_and_args = parse_macro_input!(tokens as FormatAndArgs);
+ let generator = PrintfTestGenerator::new().with_string_specifier("%K");
+ match generate_printf(generator, format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+
+// Causes the generator to substitute %c with %K.
+#[proc_macro]
+pub fn char_sub_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
+ let format_and_args = parse_macro_input!(tokens as FormatAndArgs);
+ let generator = PrintfTestGenerator::new().with_char_specifier("%K");
+ match generate_printf(generator, format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
diff --git a/pw_format/rust/pw_format_test_macros_test.rs b/pw_format/rust/pw_format_test_macros_test.rs
new file mode 100644
index 000000000..3ae1f546d
--- /dev/null
+++ b/pw_format/rust/pw_format_test_macros_test.rs
@@ -0,0 +1,168 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+use pw_format::macros::IntegerDisplayType;
+
+// Used to record calls into the test generator from `generator_test_macro!`.
+#[derive(Debug, PartialEq)]
+pub enum TestGeneratorOps {
+ Finalize,
+ StringFragment(String),
+ IntegerConversion {
+ display_type: IntegerDisplayType,
+ type_width: u8,
+ },
+ StringConversion,
+ CharConversion,
+}
+
+// Used to record calls into the test generator from `printf_generator_test_macro!` and friends.
+#[derive(Debug, PartialEq)]
+pub enum PrintfTestGeneratorOps {
+ Finalize,
+ StringFragment(String),
+ IntegerConversion { ty: String },
+ StringConversion,
+ CharConversion,
+}
+
+#[cfg(test)]
+mod tests {
+ use pw_format::macros::IntegerDisplayType;
+ use pw_format_test_macros::{
+ char_sub_printf_generator_test_macro, generator_test_macro,
+ integer_sub_printf_generator_test_macro, printf_generator_test_macro,
+ string_sub_printf_generator_test_macro,
+ };
+
+ // Create an alias to ourselves so that the proc macro can name our crate.
+ use crate as pw_format_test_macros_test;
+
+ use super::*;
+
+ #[test]
+ fn generate_calls_generator_correctly() {
+ assert_eq!(
+ generator_test_macro!("test %ld %s %c", 5, "test", 'c'),
+ vec![
+ TestGeneratorOps::StringFragment("test ".to_string()),
+ TestGeneratorOps::IntegerConversion {
+ display_type: IntegerDisplayType::Signed,
+ type_width: 32,
+ },
+ TestGeneratorOps::StringFragment(" ".to_string()),
+ TestGeneratorOps::StringConversion,
+ TestGeneratorOps::StringFragment(" ".to_string()),
+ TestGeneratorOps::CharConversion,
+ TestGeneratorOps::Finalize
+ ]
+ );
+ }
+
+ #[test]
+ fn generate_printf_calls_generator_correctly() {
+ assert_eq!(
+ printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'),
+ (
+ // %ld gets converted to %d because they are equivalent for 32 bit
+ // systems.
+ "test %d %s %c",
+ vec![
+ PrintfTestGeneratorOps::StringFragment("test ".to_string()),
+ PrintfTestGeneratorOps::IntegerConversion {
+ ty: "i32".to_string(),
+ },
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::StringConversion,
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::CharConversion,
+ PrintfTestGeneratorOps::Finalize
+ ]
+ )
+ );
+ }
+
+ // Test that a generator returning an overridden integer conversion specifier
+ // changes that and only that conversion specifier in the format string.
+ #[test]
+ fn generate_printf_substitutes_integer_conversion() {
+ assert_eq!(
+ integer_sub_printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'),
+ (
+ "test %K %s %c",
+ vec![
+ PrintfTestGeneratorOps::StringFragment("test ".to_string()),
+ PrintfTestGeneratorOps::IntegerConversion {
+ ty: "i32".to_string(),
+ },
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::StringConversion,
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::CharConversion,
+ PrintfTestGeneratorOps::Finalize
+ ]
+ )
+ );
+ }
+
+ // Test that a generator returning an overridden string conversion specifier
+ // changes that and only that conversion specifier in the format string.
+ #[test]
+ fn generate_printf_substitutes_string_conversion() {
+ assert_eq!(
+ string_sub_printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'),
+ (
+ // %ld gets converted to %d because they are equivalent for 32 bit
+ // systems.
+ "test %d %K %c",
+ vec![
+ PrintfTestGeneratorOps::StringFragment("test ".to_string()),
+ PrintfTestGeneratorOps::IntegerConversion {
+ ty: "i32".to_string(),
+ },
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::StringConversion,
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::CharConversion,
+ PrintfTestGeneratorOps::Finalize
+ ]
+ )
+ );
+ }
+
+ // Test that a generator returning an overridden character conversion specifier
+ // changes that and only that conversion specifier in the format string.
+ #[test]
+ fn generate_printf_substitutes_char_conversion() {
+ assert_eq!(
+ char_sub_printf_generator_test_macro!("test %ld %s %c", 5, "test", 'c'),
+ (
+ // %ld gets converted to %d because they are equivalent for 32 bit
+ // systems.
+ "test %d %s %K",
+ vec![
+ PrintfTestGeneratorOps::StringFragment("test ".to_string()),
+ PrintfTestGeneratorOps::IntegerConversion {
+ ty: "i32".to_string(),
+ },
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::StringConversion,
+ PrintfTestGeneratorOps::StringFragment(" ".to_string()),
+ PrintfTestGeneratorOps::CharConversion,
+ PrintfTestGeneratorOps::Finalize
+ ]
+ )
+ );
+ }
+}
diff --git a/pw_fuzzer/BUILD.gn b/pw_fuzzer/BUILD.gn
index 6848c29a1..5a1e49f97 100644
--- a/pw_fuzzer/BUILD.gn
+++ b/pw_fuzzer/BUILD.gn
@@ -234,7 +234,7 @@ group("fuzzers") {
# Add flags for adding LLVM sanitizer coverage for fuzzing. This is added by
# the host_clang_fuzz toolchains.
-config("fuzz_instrumentation") {
+config("instrumentation") {
cflags = [ "-fsanitize=fuzzer-no-link" ]
}
diff --git a/pw_fuzzer/docs.rst b/pw_fuzzer/docs.rst
index ee300f4cf..e50038df3 100644
--- a/pw_fuzzer/docs.rst
+++ b/pw_fuzzer/docs.rst
@@ -7,7 +7,7 @@ pw_fuzzer
:name: pw_fuzzer
:tagline: Better C++ code through easier fuzzing
:status: unstable
- :languages: C++14, C++17, C++20
+ :languages: C++17, C++20
Use state of the art tools to automatically find bugs in your C++ code with 5
lines of code or less!
diff --git a/pw_fuzzer/examples/libfuzzer/BUILD.bazel b/pw_fuzzer/examples/libfuzzer/BUILD.bazel
index 7dc3c9a18..f4a02ad9c 100644
--- a/pw_fuzzer/examples/libfuzzer/BUILD.bazel
+++ b/pw_fuzzer/examples/libfuzzer/BUILD.bazel
@@ -17,5 +17,6 @@ load("//pw_fuzzer:fuzzer.bzl", "pw_cc_fuzz_test")
pw_cc_fuzz_test(
name = "toy_fuzzer",
srcs = ["toy_fuzzer.cc"],
+ tags = ["no-oss-fuzz"],
deps = ["//pw_status"],
)
diff --git a/pw_fuzzer/fuzzer.bzl b/pw_fuzzer/fuzzer.bzl
index 6401ad2b1..df9e103b5 100644
--- a/pw_fuzzer/fuzzer.bzl
+++ b/pw_fuzzer/fuzzer.bzl
@@ -14,10 +14,6 @@
"""Utilities for fuzzing."""
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
-load(
- "//pw_build/bazel_internal:pigweed_internal.bzl",
- _add_defaults = "add_defaults",
-)
def pw_cc_fuzz_test(**kwargs):
"""Wrapper for cc_fuzz_test that adds required Pigweed dependencies.
@@ -34,5 +30,4 @@ def pw_cc_fuzz_test(**kwargs):
# TODO: b/292628774 - Only linux is supported for now.
kwargs["target_compatible_with"] = ["@platforms//os:linux"]
- _add_defaults(kwargs)
cc_fuzz_test(**kwargs)
diff --git a/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h b/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h
index 61066d50e..de3b7f382 100644
--- a/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h
+++ b/pw_fuzzer/private_overrides/pw_fuzzer/internal/fuzztest.h
@@ -55,7 +55,6 @@
fuzztest::internal::TypeCheckFuzzTest(test_name).IgnoreFunction()
namespace fuzztest {
-namespace internal {
/// Stub for a FuzzTest domain that produces values.
///
@@ -71,6 +70,8 @@ struct Domain {
using value_type = T;
};
+namespace internal {
+
/// Stub for a FuzzTest domain that produces containers of values.
///
/// This struct is an extension of `Domain` that add stubs for the methods that
@@ -149,7 +150,7 @@ namespace internal_no_adl {
template <typename T>
auto Arbitrary() {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
////////////////////////////////////////////////////////////////
@@ -159,20 +160,20 @@ auto Arbitrary() {
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
template <int&... ExplicitArgumentBarrier, typename T, typename... Domains>
-auto OneOf(internal::Domain<T>, Domains...) {
- return internal::Domain<T>{};
+auto OneOf(Domain<T>, Domains...) {
+ return Domain<T>{};
}
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#oneof
template <typename T>
auto Just(T) {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#map
template <int&... ExplicitArgumentBarrier, typename Mapper, typename... Inner>
auto Map(Mapper, Inner...) {
- return internal::Domain<std::decay_t<
+ return Domain<std::decay_t<
std::invoke_result_t<Mapper, typename Inner::value_type&...>>>{};
}
@@ -184,14 +185,14 @@ template <int&... ExplicitArgumentBarrier,
typename FlatMapper,
typename... Inner>
auto FlatMap(FlatMapper, Inner...) {
- return internal::Domain<
+ return Domain<
typename FlatMapOutputDomain<FlatMapper, Inner...>::value_type>{};
}
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#filter
template <int&... ExplicitArgumentBarrier, typename T, typename Pred>
-auto Filter(Pred, internal::Domain<T>) {
- return internal::Domain<T>{};
+auto Filter(Pred, Domain<T>) {
+ return Domain<T>{};
}
////////////////////////////////////////////////////////////////
@@ -271,7 +272,7 @@ inline auto PrintableAsciiChar() { return InRange<char>(32, 126); }
// TODO: b/285775246 - Add support for `fuzztest::InRegexp`.
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#inregexp-domains
// inline auto InRegexp(std::string_view) {
-// return internal::Domain<std::string_view>{};
+// return Domain<std::string_view>{};
// }
////////////////////////////////////////////////////////////////
@@ -280,12 +281,12 @@ inline auto PrintableAsciiChar() { return InRange<char>(32, 126); }
template <typename T>
auto ElementOf(std::initializer_list<T>) {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
template <typename T>
auto BitFlagCombinationOf(std::initializer_list<T>) {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
////////////////////////////////////////////////////////////////
@@ -293,19 +294,19 @@ auto BitFlagCombinationOf(std::initializer_list<T>) {
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
template <typename T, int&... ExplicitArgumentBarrier, typename U>
-auto ContainerOf(internal::Domain<U>) {
+auto ContainerOf(Domain<U>) {
return internal::ContainerDomain<T>{};
}
template <template <typename, typename...> class T,
int&... ExplicitArgumentBarrier,
typename U>
-auto ContainerOf(internal::Domain<U>) {
+auto ContainerOf(Domain<U>) {
return internal::ContainerDomain<T<U>>{};
}
template <typename T, int&... ExplicitArgumentBarrier, typename U>
-auto UniqueElementsContainerOf(internal::Domain<U>) {
+auto UniqueElementsContainerOf(Domain<U>) {
return internal::ContainerDomain<T>{};
}
@@ -319,62 +320,62 @@ auto NonEmpty(internal::ContainerDomain<T> inner) {
// https://github.com/google/fuzztest/blob/main/doc/domains-reference.md#container-combinators
template <int&... ExplicitArgumentBarrier, typename T, typename... Domains>
-auto ArrayOf(internal::Domain<T>, Domains... others) {
- return internal::Domain<std::array<T, 1 + sizeof...(others)>>{};
+auto ArrayOf(Domain<T>, Domains... others) {
+ return Domain<std::array<T, 1 + sizeof...(others)>>{};
}
template <int N, int&... ExplicitArgumentBarrier, typename T>
-auto ArrayOf(const internal::Domain<T>&) {
- return internal::Domain<std::array<T, N>>{};
+auto ArrayOf(const Domain<T>&) {
+ return Domain<std::array<T, N>>{};
}
template <typename T, int&... ExplicitArgumentBarrier, typename... Inner>
auto StructOf(Inner...) {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
template <typename T, int&... ExplicitArgumentBarrier>
auto ConstructorOf() {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
template <typename T,
int&... ExplicitArgumentBarrier,
typename U,
typename... Inner>
-auto ConstructorOf(internal::Domain<U>, Inner... inner) {
+auto ConstructorOf(Domain<U>, Inner... inner) {
return ConstructorOf<T>(inner...);
}
template <int&... ExplicitArgumentBarrier, typename T1, typename T2>
-auto PairOf(internal::Domain<T1>, internal::Domain<T2>) {
- return internal::Domain<std::pair<T1, T2>>{};
+auto PairOf(Domain<T1>, Domain<T2>) {
+ return Domain<std::pair<T1, T2>>{};
}
template <int&... ExplicitArgumentBarrier, typename... Inner>
auto TupleOf(Inner...) {
- return internal::Domain<std::tuple<typename Inner::value_type...>>{};
+ return Domain<std::tuple<typename Inner::value_type...>>{};
}
template <typename T, int&... ExplicitArgumentBarrier, typename... Inner>
auto VariantOf(Inner...) {
- return internal::Domain<T>{};
+ return Domain<T>{};
}
template <int&... ExplicitArgumentBarrier, typename... Inner>
auto VariantOf(Inner...) {
- return internal::Domain<std::variant<typename Inner::value_type...>>{};
+ return Domain<std::variant<typename Inner::value_type...>>{};
}
template <template <typename> class Optional,
int&... ExplicitArgumentBarrier,
typename T>
-auto OptionalOf(internal::Domain<T>) {
+auto OptionalOf(Domain<T>) {
return internal::OptionalDomain<Optional<T>>{};
}
template <int&... ExplicitArgumentBarrier, typename T>
-auto OptionalOf(internal::Domain<T> inner) {
+auto OptionalOf(Domain<T> inner) {
return OptionalOf<std::optional>(inner);
}
diff --git a/pw_fuzzer/public/pw_fuzzer/fuzztest.h b/pw_fuzzer/public/pw_fuzzer/fuzztest.h
index f2eea67c5..9e72ec274 100644
--- a/pw_fuzzer/public/pw_fuzzer/fuzztest.h
+++ b/pw_fuzzer/public/pw_fuzzer/fuzztest.h
@@ -49,6 +49,9 @@
namespace pw::fuzzer {
+template <typename T>
+using Domain = fuzztest::Domain<T>;
+
////////////////////////////////////////////////////////////////
// Arbitrary domains
// Additional specializations are provided with the Pigweed domains.
@@ -395,7 +398,7 @@ struct ArbitraryImpl<StatusWithSize> {
/// Like @cpp_func{pw::fuzzer::Arbitrary<Status>}, except that
/// @cpp_func{pw::OkStatus} is filtered out.
-auto NonOkStatus() {
+inline auto NonOkStatus() {
return ConstructorOf<pw::Status>(
Map([](int code) { return static_cast<pw_Status>(code); },
InRange<int>(PW_STATUS_CANCELLED, PW_STATUS_UNAUTHENTICATED)));
diff --git a/pw_hdlc/api.rst b/pw_hdlc/api.rst
index f269813c8..581d432cb 100644
--- a/pw_hdlc/api.rst
+++ b/pw_hdlc/api.rst
@@ -217,14 +217,6 @@ RPC
# The serial_device object is closed, and reader thread stopped.
return 0
- .. warning::
- The ``pw_hdlc.rpc.RpcClient`` derived classes, and
- ``pw_system.device.Device`` accept a read callback type for legacy
- reasons and will be deprecated in 2023 Q4. Passing a read callback is
- unsafe because the read thread can be leaked on exit if the callback is
- blocking. Using a ``CancellableReader`` guarantees safe thread
- destruction by cancelling the read process.
-
.. autoclass:: pw_hdlc.rpc.channel_output
:members:
:noindex:
diff --git a/pw_hdlc/py/pw_hdlc/rpc.py b/pw_hdlc/py/pw_hdlc/rpc.py
index ae09c772a..f9949071a 100644
--- a/pw_hdlc/py/pw_hdlc/rpc.py
+++ b/pw_hdlc/py/pw_hdlc/rpc.py
@@ -218,16 +218,6 @@ class SerialReader(CancellableReader):
self._base_obj.close()
-# TODO: b/301496598 - Remove this class once a callable is deprecated from the
-# RpcClient objects.
-class _StubReader(CancellableReader):
- def read(self) -> bytes:
- return self._base_obj()
-
- def cancel_read(self) -> None:
- pass
-
-
class DataReaderAndExecutor:
"""Reads incoming bytes, data processor that delegates frame handling.
@@ -263,11 +253,7 @@ class DataReaderAndExecutor:
self._frame_handler = frame_handler
self._handler_threads = handler_threads
- # TODO: b/301496598 - Make thread non-daemon when RpcClients stop
- # accepting reader's Callable type.
- self._reader_thread = threading.Thread(
- target=self._run, daemon=isinstance(self._reader, _StubReader)
- )
+ self._reader_thread = threading.Thread(target=self._run)
self._reader_thread_stop = threading.Event()
def start(self) -> None:
@@ -281,20 +267,16 @@ class DataReaderAndExecutor:
This requests that the reading process stop and waits
for the background thread to exit.
-
- NOTE: Currently the thread is not joined when providing a ``read()``
- callback instead of a :py:class:`CancellableReader` through a
- :py:class:`RpcClient` or :py:class:`Device` object. This will be
- deprecated in b/301496598.
"""
_LOG.debug('Stopping read process')
self._reader_thread_stop.set()
self._reader.cancel_read()
-
- # TODO: b/301496598 - Unconditionally join the thread when RpcClients
- # stop accepting reader's Callable type.
- if not isinstance(self._reader, _StubReader):
- self._reader_thread.join()
+ self._reader_thread.join(30)
+ if self._reader_thread.is_alive():
+ warnings.warn(
+ 'Timed out waiting for read thread to terminate.\n'
+ 'Tip: Use a `CancellableReader` to cancel reads.'
+ )
def _run(self) -> None:
"""Reads raw data in a background thread."""
@@ -412,11 +394,9 @@ class HdlcRpcClient(RpcClient):
payloads.
"""
- # TODO: b/301496598 - Deprecate reader's Callable type and accept only
- # CancellableReader classes in downstream projects.
def __init__(
self,
- reader: Union[CancellableReader, Callable[[], bytes]],
+ reader: CancellableReader,
paths_or_modules: PathsModulesOrProtoLibrary,
channels: Iterable[pw_rpc.Channel],
output: Callable[[bytes], Any] = write_to_file,
@@ -483,14 +463,6 @@ class HdlcRpcClient(RpcClient):
def on_read_error(exc: Exception) -> None:
_LOG.error('data reader encountered an error', exc_info=exc)
- if not isinstance(reader, CancellableReader):
- warnings.warn(
- 'The reader as Callablle is deprecated. Use CancellableReader'
- 'instead.',
- DeprecationWarning,
- )
- reader = _StubReader(reader)
-
reader_and_executor = DataReaderAndExecutor(
reader, on_read_error, decoder.process_valid_frames, handle_frame
)
@@ -505,11 +477,9 @@ class NoEncodingSingleChannelRpcClient(RpcClient):
The caveat is that the provided read function must read entire frames.
"""
- # TODO: b/301496598 - Deprecate reader's Callable type and accept only
- # CancellableReader classes in downstream projects.
def __init__(
self,
- reader: Union[CancellableReader, Callable[[], bytes]],
+ reader: CancellableReader,
paths_or_modules: PathsModulesOrProtoLibrary,
channel: pw_rpc.Channel,
client_impl: Optional[pw_rpc.client.ClientImpl] = None,
@@ -530,14 +500,6 @@ class NoEncodingSingleChannelRpcClient(RpcClient):
def on_read_error(exc: Exception) -> None:
_LOG.error('data reader encountered an error', exc_info=exc)
- if not isinstance(reader, CancellableReader):
- warnings.warn(
- 'The reader as Callablle is deprecated. Use CancellableReader'
- 'instead.',
- DeprecationWarning,
- )
- reader = _StubReader(reader)
-
reader_and_executor = DataReaderAndExecutor(
reader, on_read_error, process_data, self.handle_rpc_packet
)
diff --git a/pw_i2c/BUILD.bazel b/pw_i2c/BUILD.bazel
index 212ae7913..a9d0af354 100644
--- a/pw_i2c/BUILD.bazel
+++ b/pw_i2c/BUILD.bazel
@@ -117,6 +117,7 @@ pw_cc_library(
pw_cc_library(
name = "initiator_gmock",
+ testonly = True,
hdrs = [
"public/pw_i2c/initiator_gmock.h",
],
diff --git a/pw_ide/py/pw_ide/cli.py b/pw_ide/py/pw_ide/cli.py
index 999de3056..7db4c6143 100644
--- a/pw_ide/py/pw_ide/cli.py
+++ b/pw_ide/py/pw_ide/cli.py
@@ -262,6 +262,14 @@ def _build_argument_parser() -> argparse.ArgumentParser:
func=lambda *_args, **_kwargs: parser_root.print_help()
)
+ parser_root.add_argument(
+ '-o',
+ '--output',
+ choices=['stdout', 'log'],
+ default='pretty',
+ help='where program output should go',
+ )
+
subcommand_parser = parser_root.add_subparsers(help='Subcommands')
add_parser = _parser_adder(subcommand_parser)
diff --git a/pw_ide/py/pw_ide/commands.py b/pw_ide/py/pw_ide/commands.py
index 087728618..393acfaed 100644
--- a/pw_ide/py/pw_ide/commands.py
+++ b/pw_ide/py/pw_ide/commands.py
@@ -47,7 +47,7 @@ from pw_ide.settings import (
SupportedEditor,
)
-from pw_ide.status_reporter import StatusReporter
+from pw_ide.status_reporter import LoggingStatusReporter, StatusReporter
from pw_ide import vscode
from pw_ide.vscode import (
@@ -60,6 +60,22 @@ _LOG = logging.getLogger(__package__)
env = pigweed_environment()
+def _inject_reporter(func):
+ """Inject a status reporter instance based on selected output type."""
+
+ def wrapped(*args, **kwargs):
+ output = kwargs.pop('output', 'stdout')
+ reporter = StatusReporter()
+
+ if output == 'log':
+ reporter = LoggingStatusReporter(_LOG)
+
+ kwargs['reporter'] = reporter
+ return func(*args, **kwargs)
+
+ return wrapped
+
+
def _make_working_dir(
reporter: StatusReporter, settings: PigweedIdeSettings, quiet: bool = False
) -> None:
@@ -79,6 +95,7 @@ def _report_unrecognized_editor(reporter: StatusReporter, editor: str) -> None:
reporter.wrn(f'Automatically-supported editors: {supported_editors}')
+@_inject_reporter
def cmd_sync(
reporter: StatusReporter = StatusReporter(),
pw_ide_settings: PigweedIdeSettings = PigweedIdeSettings(),
@@ -109,6 +126,7 @@ def cmd_sync(
reporter.info('Done')
+@_inject_reporter
def cmd_setup(
reporter: StatusReporter = StatusReporter(),
pw_ide_settings: PigweedIdeSettings = PigweedIdeSettings(),
@@ -120,6 +138,7 @@ def cmd_setup(
cmd_sync(reporter, pw_ide_settings)
+@_inject_reporter
def cmd_vscode(
include: Optional[List[VscSettingsType]] = None,
exclude: Optional[List[VscSettingsType]] = None,
@@ -496,6 +515,7 @@ def _process_compdbs( # pylint: disable=too-many-locals
return False
+@_inject_reporter
def cmd_cpp( # pylint: disable=too-many-arguments, too-many-locals, too-many-branches, too-many-statements
should_list_targets: bool,
should_get_target: bool,
@@ -756,6 +776,7 @@ def install_py_module_as_editable(
reporter.wrn('Note that running bootstrap or building will reverse this.')
+@_inject_reporter
def cmd_python(
should_print_venv: bool,
install_editable: Optional[str] = None,
diff --git a/pw_ide/py/pw_ide/vscode.py b/pw_ide/py/pw_ide/vscode.py
index fe7b1ede4..76f0f4288 100644
--- a/pw_ide/py/pw_ide/vscode.py
+++ b/pw_ide/py/pw_ide/vscode.py
@@ -180,7 +180,6 @@ _DEFAULT_SETTINGS: EditorSettingsDict = OrderedDict(
"gulp.autoDetect": "off",
"jake.autoDetect": "off",
"npm.autoDetect": "off",
- "clangd.onConfigChanged": "restart",
"C_Cpp.intelliSenseEngine": "Disabled",
"[cpp]": OrderedDict(
{"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"}
@@ -203,6 +202,7 @@ _DEFAULT_SETTINGS: EditorSettingsDict = OrderedDict(
"[proto3]": OrderedDict(
{"editor.defaultFormatter": "zxh404.vscode-proto3"}
),
+ "[restructuredtext]": OrderedDict({"editor.tabSize": 3}),
}
)
@@ -337,13 +337,11 @@ _DEFAULT_EXTENSIONS: EditorSettingsDict = OrderedDict(
"msedge-dev.gnls",
"zxh404.vscode-proto3",
"josetr.cmake-language-support-vscode",
- "swyddfa.esbonio",
],
"unwantedRecommendations": [
"ms-vscode.cpptools",
"persidskiy.vscode-gnformat",
"lextudio.restructuredtext",
- "trond-snekvik.simple-rst",
],
}
)
diff --git a/pw_ide/ts/pigweed-vscode/CHANGELOG.md b/pw_ide/ts/pigweed-vscode/CHANGELOG.md
index 5f86168fb..f1b145a48 100644
--- a/pw_ide/ts/pigweed-vscode/CHANGELOG.md
+++ b/pw_ide/ts/pigweed-vscode/CHANGELOG.md
@@ -1,9 +1,13 @@
# Change Log
-All notable changes to the "pigweed-ide" extension will be documented in this file.
+## [0.1.1]
-Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
+- Fixes cases where "Pigweed: Check Extensions" was not running on startup.
-## [Unreleased]
+## [0.1.0]
-- Initial release \ No newline at end of file
+- Adds the "Pigweed: Check Extensions" command, which prompts the user to
+ install all recommended extensions and disable all unwanted extensions, as
+ defined by the project's `extensions.json`. This makes "recommended"
+ extensions required, and "unwanted" extensions forbidden, allowing Pigweed
+ projects to define more consistent development environments for their teams.
diff --git a/pw_ide/ts/pigweed-vscode/README.md b/pw_ide/ts/pigweed-vscode/README.md
index e23ea3136..434603d7a 100644
--- a/pw_ide/ts/pigweed-vscode/README.md
+++ b/pw_ide/ts/pigweed-vscode/README.md
@@ -23,11 +23,3 @@ This is highly experimental!
- Install the build tool: `npm install -g @vscode/vsce`
- Build the VSIX: `vsce package`
-
-## Changelog
-
-### 0.0.1
-
-- Adds the "Pigweed: Check Extensions" command, which prompts the user to
- install all recommended extensions and disable all unwanted extensions, as
- defined by the project's `extensions.json`.
diff --git a/pw_ide/ts/pigweed-vscode/package-lock.json b/pw_ide/ts/pigweed-vscode/package-lock.json
new file mode 100644
index 000000000..be6ff320a
--- /dev/null
+++ b/pw_ide/ts/pigweed-vscode/package-lock.json
@@ -0,0 +1,4452 @@
+{
+ "name": "pigweed-vscode",
+ "version": "0.1.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pigweed-vscode",
+ "version": "0.1.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "glob": "^8.1.0",
+ "hjson": "^3.2.2",
+ "node-polyfill-webpack-plugin": "^2.0.1"
+ },
+ "devDependencies": {
+ "@types/glob": "^8.1.0",
+ "@types/hjson": "2.4.3",
+ "@types/mocha": "^10.0.1",
+ "@types/node": "20.2.5",
+ "@types/vscode": "^1.64.0",
+ "@typescript-eslint/eslint-plugin": "^5.59.8",
+ "@typescript-eslint/parser": "^5.59.8",
+ "@vscode/test-electron": "^2.3.2",
+ "eslint": "^8.41.0",
+ "mocha": "^10.2.0",
+ "ts-loader": "^9.4.4",
+ "typescript": "^5.1.3",
+ "webpack": "^5.88.2",
+ "webpack-cli": "^5.1.4"
+ },
+ "engines": {
+ "vscode": "^1.64.0"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
+ "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz",
+ "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+ "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.44.2",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz",
+ "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
+ "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+ "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
+ },
+ "node_modules/@types/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimatch": "^5.1.2",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/hjson": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/@types/hjson/-/hjson-2.4.3.tgz",
+ "integrity": "sha512-EDixutNn3FQwa5HdET+Tx0h7TirP2knJa9TB21ySqr8XVqT/VsvvOwnanTLHUOOpNofcUhkRKhOk0Wh4YD9RSA==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
+ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
+ },
+ "node_modules/@types/minimatch": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+ "dev": true
+ },
+ "node_modules/@types/mocha": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
+ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.2.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ=="
+ },
+ "node_modules/@types/semver": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz",
+ "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==",
+ "dev": true
+ },
+ "node_modules/@types/vscode": {
+ "version": "1.82.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.82.0.tgz",
+ "integrity": "sha512-VSHV+VnpF8DEm8LNrn8OJ8VuUNcBzN3tMvKrNpbhhfuVjFm82+6v44AbDhLvVFgCzn6vs94EJNTp7w8S6+Q1Rw==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
+ "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.4.0",
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/type-utils": "5.62.0",
+ "@typescript-eslint/utils": "5.62.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
+ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
+ "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
+ "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "@typescript-eslint/utils": "5.62.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
+ "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
+ "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
+ "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "eslint-scope": "^5.1.1",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
+ "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vscode/test-electron": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.4.tgz",
+ "integrity": "sha512-eWzIqXMhvlcoXfEFNWrVu/yYT5w6De+WZXR/bafUQhAp8+8GkQo95Oe14phwiRUPv8L+geAKl/QM2+PoT3YW3g==",
+ "dev": true,
+ "dependencies": {
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "jszip": "^3.10.1",
+ "semver": "^7.5.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
+ "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+ "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
+ "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
+ "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
+ "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/helper-wasm-section": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-opt": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6",
+ "@webassemblyjs/wast-printer": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
+ "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
+ "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
+ "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
+ "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
+ "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
+ "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
+ "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x",
+ "webpack-cli": "5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-import-assertions": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
+ "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
+ "peerDependencies": {
+ "acorn": "^8"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/browserify-rsa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
+ "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
+ "dependencies": {
+ "bn.js": "^5.0.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "node_modules/browserify-sign": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+ "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+ "dependencies": {
+ "bn.js": "^5.1.1",
+ "browserify-rsa": "^4.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.3",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.5",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.21.10",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz",
+ "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001517",
+ "electron-to-chromium": "^1.4.477",
+ "node-releases": "^2.0.13",
+ "update-browserslist-db": "^1.0.11"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
+ },
+ "node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ=="
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001534",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz",
+ "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/console-browserify": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="
+ },
+ "node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ=="
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dependencies": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz",
+ "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/des.js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz",
+ "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/domain-browser": {
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz",
+ "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.520",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz",
+ "integrity": "sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g=="
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
+ "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz",
+ "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==",
+ "dev": true,
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz",
+ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q=="
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz",
+ "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.49.0",
+ "@humanwhocodes/config-array": "^0.11.11",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/filter-obj": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz",
+ "integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz",
+ "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.7",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hash-base/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/hash-base/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/hjson": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/hjson/-/hjson-3.2.2.tgz",
+ "integrity": "sha512-MkUeB0cTIlppeSsndgESkfFD21T2nXPRaBStLtf3cAYA2bVEFdXlodZB0TukwZiobPD1Ksax5DK4RTZeaXCI3Q==",
+ "bin": {
+ "hjson": "bin/hjson"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/interpret": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
+ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
+ "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+ "dependencies": {
+ "which-typed-array": "^1.1.11"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz",
+ "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+ "engines": {
+ "node": ">=6.11.5"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
+ "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.3",
+ "debug": "4.3.4",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.2.0",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "5.0.1",
+ "ms": "2.1.3",
+ "nanoid": "3.3.3",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "workerpool": "6.2.1",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/mocha/node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
+ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+ "dev": true
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
+ "node_modules/node-polyfill-webpack-plugin": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz",
+ "integrity": "sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==",
+ "dependencies": {
+ "assert": "^2.0.0",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^6.0.3",
+ "console-browserify": "^1.2.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.12.0",
+ "domain-browser": "^4.22.0",
+ "events": "^3.3.0",
+ "filter-obj": "^2.0.2",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "^1.0.1",
+ "process": "^0.11.10",
+ "punycode": "^2.1.1",
+ "querystring-es3": "^0.2.1",
+ "readable-stream": "^4.0.0",
+ "stream-browserify": "^3.0.0",
+ "stream-http": "^3.2.0",
+ "string_decoder": "^1.3.0",
+ "timers-browserify": "^2.0.12",
+ "tty-browserify": "^0.0.1",
+ "type-fest": "^2.14.0",
+ "url": "^0.11.0",
+ "util": "^0.12.4",
+ "vm-browserify": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "webpack": ">=5"
+ }
+ },
+ "node_modules/node-polyfill-webpack-plugin/node_modules/readable-stream": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
+ "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/node-polyfill-webpack-plugin/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/node-polyfill-webpack-plugin/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
+ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+ "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-asn1": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+ "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+ "dependencies": {
+ "asn1.js": "^5.2.0",
+ "browserify-aes": "^1.0.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "dependencies": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
+ "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
+ "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
+ "dev": true,
+ "dependencies": {
+ "resolve": "^1.20.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-cwd/node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/stream-browserify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+ "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
+ "dependencies": {
+ "inherits": "~2.0.4",
+ "readable-stream": "^3.5.0"
+ }
+ },
+ "node_modules/stream-browserify/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/stream-http": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz",
+ "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/stream-http/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.19.4",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.4.tgz",
+ "integrity": "sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.9",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
+ "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.16.8"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/timers-browserify": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+ "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
+ "dependencies": {
+ "setimmediate": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-loader": {
+ "version": "9.4.4",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz",
+ "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "typescript": "*",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/tty-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
+ "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.2.tgz",
+ "integrity": "sha512-7yIgNnrST44S7PJ5+jXbdIupfU1nWUdQJBFBeJRclPXiWgCvrSq5Frw8lr/i//n5sqDfzoKmBymMS81l4U/7cg==",
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.11.2"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
+ },
+ "node_modules/watchpack": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
+ "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "5.88.2",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
+ "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^1.0.0",
+ "@webassemblyjs/ast": "^1.11.5",
+ "@webassemblyjs/wasm-edit": "^1.11.5",
+ "@webassemblyjs/wasm-parser": "^1.11.5",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.9.0",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.15.0",
+ "es-module-lexer": "^1.2.1",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.2.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.3.7",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz",
+ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==",
+ "dev": true,
+ "dependencies": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^2.1.1",
+ "@webpack-cli/info": "^2.0.2",
+ "@webpack-cli/serve": "^2.0.5",
+ "colorette": "^2.0.14",
+ "commander": "^10.0.1",
+ "cross-spawn": "^7.0.3",
+ "envinfo": "^7.7.3",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^3.1.1",
+ "rechoir": "^0.8.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "bin": {
+ "webpack-cli": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "@webpack-cli/generators": {
+ "optional": true
+ },
+ "webpack-bundle-analyzer": {
+ "optional": true
+ },
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "5.9.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz",
+ "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==",
+ "dev": true,
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
+ "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.5",
+ "call-bind": "^1.0.2",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+ "dev": true
+ },
+ "node_modules/workerpool": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/pw_ide/ts/pigweed-vscode/package.json b/pw_ide/ts/pigweed-vscode/package.json
index b98d57c87..fb5104714 100644
--- a/pw_ide/ts/pigweed-vscode/package.json
+++ b/pw_ide/ts/pigweed-vscode/package.json
@@ -1,10 +1,14 @@
{
- "publisher": "pigweed-authors",
+ "publisher": "pigweed",
"name": "pigweed-vscode",
"displayName": "Pigweed Visual Studio Code Extension",
"description": "Visual Studio Code support for Pigweed projects",
- "version": "0.0.1",
- "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://pigweed.googlesource.com/pigweed/pigweed"
+ },
+ "version": "0.1.1",
+ "license": "Apache-2.0",
"engines": {
"vscode": "^1.64.0"
},
@@ -29,26 +33,35 @@
]
},
"scripts": {
- "vscode:prepublish": "npm run compile",
+ "vscode:prepublish": "npm run package",
+ "webpack": "webpack --mode development",
+ "webpack-dev": "webpack --mode development --watch",
+ "package": "webpack --mode production --devtool hidden-source-map",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js"
},
+ "dependencies": {
+ "glob": "^8.1.0",
+ "hjson": "^3.2.2"
+ },
"devDependencies": {
- "@types/vscode": "^1.64.0",
"@types/glob": "^8.1.0",
"@types/hjson": "2.4.3",
"@types/mocha": "^10.0.1",
"@types/node": "20.2.5",
+ "@types/vscode": "^1.64.0",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
+ "@vscode/test-electron": "^2.3.2",
"eslint": "^8.41.0",
- "glob": "^8.1.0",
- "hjson": "3.2.2",
"mocha": "^10.2.0",
+ "node-polyfill-webpack-plugin": "^2.0.1",
+ "ts-loader": "^9.4.4",
"typescript": "^5.1.3",
- "@vscode/test-electron": "^2.3.2"
+ "webpack": "^5.88.2",
+ "webpack-cli": "^5.1.4"
}
}
diff --git a/pw_ide/ts/pigweed-vscode/pigweed-ide-0.0.1.vsix b/pw_ide/ts/pigweed-vscode/pigweed-ide-0.0.1.vsix
deleted file mode 100644
index 3d8ab20fa..000000000
--- a/pw_ide/ts/pigweed-vscode/pigweed-ide-0.0.1.vsix
+++ /dev/null
Binary files differ
diff --git a/pw_ide/ts/pigweed-vscode/src/extension.ts b/pw_ide/ts/pigweed-vscode/src/extension.ts
index e0ff8277e..16e1ca51f 100644
--- a/pw_ide/ts/pigweed-vscode/src/extension.ts
+++ b/pw_ide/ts/pigweed-vscode/src/extension.ts
@@ -210,7 +210,7 @@ async function disableUnwantedExtensions(unwanted: string[]) {
);
}
-async function checkExtensions(context: vscode.ExtensionContext) {
+async function checkExtensions() {
const extensions = await getExtensionsJson();
const num_recommendations = extensions.recommendations?.length ?? 0;
@@ -230,10 +230,11 @@ async function checkExtensions(context: vscode.ExtensionContext) {
export function activate(context: vscode.ExtensionContext) {
const pwCheckExtensions = vscode.commands.registerCommand(
'pigweed.check-extensions',
- () => checkExtensions(context),
+ () => checkExtensions(),
);
context.subscriptions.push(pwCheckExtensions);
+ checkExtensions();
}
export function deactivate() {
diff --git a/pw_ide/ts/pigweed-vscode/tsconfig.json b/pw_ide/ts/pigweed-vscode/tsconfig.json
index 833390c60..8a14b3fce 100644
--- a/pw_ide/ts/pigweed-vscode/tsconfig.json
+++ b/pw_ide/ts/pigweed-vscode/tsconfig.json
@@ -6,8 +6,10 @@
"lib": [
"ES2020"
],
+ "exclude": [
+ "*.js"
+ ],
"sourceMap": true,
- "rootDir": "src",
"strict": true
}
}
diff --git a/pw_ide/ts/pigweed-vscode/webpack.config.js b/pw_ide/ts/pigweed-vscode/webpack.config.js
new file mode 100644
index 000000000..df60d43f5
--- /dev/null
+++ b/pw_ide/ts/pigweed-vscode/webpack.config.js
@@ -0,0 +1,72 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+'use strict';
+
+const path = require('path');
+const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
+const webpack = require('webpack');
+
+/**@type {import('webpack').Configuration}*/
+const config = {
+ // vscode extensions run in webworker context for VS
+ // Code web 📖 -> https://webpack.js.org/configuration/target/#target
+ target: 'webworker',
+
+ // the entry point of this extension, 📖 ->
+ // https://webpack.js.org/configuration/entry-context/
+ entry: './src/extension.ts',
+
+ output: {
+ // the bundle is stored in the 'dist' folder (check package.json), 📖 ->
+ // https://webpack.js.org/configuration/output/
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'extension.js',
+ libraryTarget: 'commonjs2',
+ devtoolModuleFilenameTemplate: '../[resource-path]',
+ },
+ devtool: 'source-map',
+ plugins: [new NodePolyfillPlugin()],
+ externals: {
+ // the vscode-module is created on-the-fly and must
+ // be excluded. Add other modules that cannot be
+ // webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
+ vscode: 'commonjs vscode',
+ },
+ resolve: {
+ // support reading TypeScript and JavaScript files, 📖 ->
+ // https://github.com/TypeStrong/ts-loader
+ // look for `browser` entry point in imported node modules
+ mainFields: ['browser', 'module', 'main'],
+ extensions: ['.ts', '.js'],
+ alias: {
+ // provides alternate implementation for node module and source files
+ },
+ fallback: {
+ // Webpack 5 no longer polyfills Node.js core modules automatically.
+ // see https://webpack.js.org/configuration/resolve/#resolvefallback
+ // for the list of Node.js core module polyfills.
+ },
+ },
+ module: {
+ rules: [
+ {
+ test: /\.ts$/,
+ exclude: /node_modules/,
+ use: [{ loader: 'ts-loader' }],
+ },
+ ],
+ },
+};
+module.exports = config;
diff --git a/pw_libc/BUILD.gn b/pw_libc/BUILD.gn
index 9f441e89e..d9fd80be7 100644
--- a/pw_libc/BUILD.gn
+++ b/pw_libc/BUILD.gn
@@ -152,12 +152,12 @@ if (dir_pw_third_party_llvm_libc != "") {
pw_test_group("llvm_libc_tests") {
tests = [
- ":ctype.test",
- ":math.test",
- ":stdio.test",
- ":stdlib.test",
- ":string.test",
- ":time.test",
+ ":ctype_tests",
+ ":math_tests",
+ ":stdio_tests",
+ ":stdlib_tests",
+ ":string_tests",
+ ":time_tests",
]
}
} else {
diff --git a/pw_trace_tokenized/py/pyproject.toml b/pw_libcxx/BUILD.bazel
index 798b747ec..0ef580840 100644
--- a/pw_trace_tokenized/py/pyproject.toml
+++ b/pw_libcxx/BUILD.bazel
@@ -1,4 +1,4 @@
-# Copyright 2021 The Pigweed Authors
+# Copyright 2023 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -11,6 +11,17 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-[build-system]
-requires = ['setuptools', 'wheel']
-build-backend = 'setuptools.build_meta'
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_library",
+)
+
+pw_cc_library(
+ name = "pw_libcxx",
+ srcs = [
+ "__cxa_deleted_virtual.cc",
+ "__cxa_pure_virtual.cc",
+ "operator_delete.cc",
+ ],
+)
diff --git a/pw_libcxx/BUILD.gn b/pw_libcxx/BUILD.gn
new file mode 100644
index 000000000..4083abf4c
--- /dev/null
+++ b/pw_libcxx/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/cc_library.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+pw_static_library("pw_libcxx") {
+ sources = [
+ "__cxa_deleted_virtual.cc",
+ "__cxa_pure_virtual.cc",
+ "operator_delete.cc",
+ ]
+}
+
+pw_test_group("tests") {
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_libcxx/__cxa_deleted_virtual.cc b/pw_libcxx/__cxa_deleted_virtual.cc
new file mode 100644
index 000000000..6f02ab1ed
--- /dev/null
+++ b/pw_libcxx/__cxa_deleted_virtual.cc
@@ -0,0 +1,15 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+extern "C" void __cxa_deleted_virtual() { __builtin_trap(); }
diff --git a/pw_libcxx/__cxa_pure_virtual.cc b/pw_libcxx/__cxa_pure_virtual.cc
new file mode 100644
index 000000000..b0c73b510
--- /dev/null
+++ b/pw_libcxx/__cxa_pure_virtual.cc
@@ -0,0 +1,15 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+extern "C" void __cxa_pure_virtual() { __builtin_trap(); }
diff --git a/pw_libcxx/docs.rst b/pw_libcxx/docs.rst
new file mode 100644
index 000000000..4243fee74
--- /dev/null
+++ b/pw_libcxx/docs.rst
@@ -0,0 +1,9 @@
+.. _module-pw_libcxx:
+
+---------
+pw_libcxx
+---------
+The ``pw_libcxx`` module provides libcxx symbols, and will eventually facilitate
+pulling in headers as well. Currently, none of the library is built from
+upstream LLVM libcxx, instead the symbols provided should just crash in
+an embedded context.
diff --git a/pw_libcxx/operator_delete.cc b/pw_libcxx/operator_delete.cc
new file mode 100644
index 000000000..a545f525d
--- /dev/null
+++ b/pw_libcxx/operator_delete.cc
@@ -0,0 +1,37 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <new>
+
+// fxbug.dev/132869: We should look into compiler changes to avoid emitting
+// references to these symbols.
+// These operator delete implementations are provided to satisfy references from
+// the vtable for the deleting destructor. In practice, these will never be
+// reached because users should not be using new/delete.
+
+void operator delete(void*) noexcept { __builtin_trap(); }
+void operator delete[](void*) noexcept { __builtin_trap(); }
+
+void operator delete(void*, std::align_val_t) noexcept { __builtin_trap(); }
+void operator delete[](void*, std::align_val_t) noexcept { __builtin_trap(); }
+
+void operator delete(void*, std::size_t) noexcept { __builtin_trap(); }
+void operator delete[](void*, std::size_t) noexcept { __builtin_trap(); }
+
+void operator delete(void*, std::size_t, std::align_val_t) noexcept {
+ __builtin_trap();
+}
+void operator delete[](void*, std::size_t, std::align_val_t) noexcept {
+ __builtin_trap();
+}
diff --git a/pw_log/Android.bp b/pw_log/Android.bp
index 979192246..9b38ba320 100644
--- a/pw_log/Android.bp
+++ b/pw_log/Android.bp
@@ -69,7 +69,11 @@ genrule {
genrule {
name: "pw_log_log_proto_pwpb_h",
- srcs: [":pw_log_log_proto_with_prefix",],
+ srcs: [
+ ":libprotobuf-internal-protos",
+ ":pw_log_log_proto_with_prefix",
+ ":pw_protobuf_common_proto",
+ ],
cmd: "python3 $(location pw_protobuf_compiler_py) " +
"--proto-path=external/pigweed/pw_protobuf/ " +
// Requires the generated pw_tokenizer/proto/options.proto filepath.
@@ -79,10 +83,14 @@ genrule {
"--out-dir=$(genDir) " +
"--plugin-path=$(location pw_protobuf_plugin_py) " +
"--compile-dir=$(genDir) " +
- "--sources $(in) " +
+ "--sources $(location :pw_log_log_proto_with_prefix) " +
"--language pwpb " +
"--no-experimental-proto3-optional " +
- "--protoc=$(location aprotoc) ",
+ "--protoc=$(location aprotoc) && " +
+ // TODO(b/308678575) - Ideally the previous command would create the file at the right location,
+ // but following all the layers of pigweed wrappers around protoc is too difficult.
+ "python3 -c \"import os; import shutil; " +
+ "shutil.copy2(os.path.splitext('$(location :pw_log_log_proto_with_prefix)')[0]+'.pwpb.h', '$(out)')\"",
out: [
"pw_log/proto/log.pwpb.h",
],
@@ -98,7 +106,11 @@ genrule {
genrule {
name: "pw_log_log_rpc_pwpb_h",
- srcs: [":pw_log_log_proto_with_prefix",],
+ srcs: [
+ ":libprotobuf-internal-protos",
+ ":pw_log_log_proto_with_prefix",
+ ":pw_protobuf_common_proto",
+ ],
cmd: "python3 $(location pw_protobuf_compiler_py) " +
"--proto-path=external/pigweed/pw_protobuf/ " +
// Requires the generated pw_tokenizer/proto/options.proto filepath.
@@ -108,10 +120,14 @@ genrule {
"--out-dir=$(genDir) " +
"--plugin-path=$(location pw_rpc_plugin_pwpb_py) " +
"--compile-dir=$(genDir) " +
- "--sources $(in) " +
+ "--sources $(location :pw_log_log_proto_with_prefix) " +
"--language pwpb_rpc " +
"--no-experimental-proto3-optional " +
- "--protoc=$(location aprotoc) ",
+ "--protoc=$(location aprotoc) && " +
+ // TODO(b/308678575) - Ideally the previous command would create the file at the right location,
+ // but following all the layers of pigweed wrappers around protoc is too difficult.
+ "python3 -c \"import os; import shutil; " +
+ "shutil.copy2(os.path.splitext('$(location :pw_log_log_proto_with_prefix)')[0]+'.rpc.pwpb.h', '$(out)')\"",
out: [
"pw_log/proto/log.rpc.pwpb.h",
],
@@ -127,7 +143,11 @@ genrule {
genrule {
name: "pw_log_log_raw_rpc_h",
- srcs: [":pw_log_log_proto_with_prefix",],
+ srcs: [
+ ":libprotobuf-internal-protos",
+ ":pw_log_log_proto_with_prefix",
+ ":pw_protobuf_common_proto",
+ ],
cmd: "python3 $(location pw_protobuf_compiler_py) " +
"--proto-path=external/pigweed/pw_protobuf/ " +
// Requires the generated pw_tokenizer/proto/options.proto filepath.
@@ -137,10 +157,14 @@ genrule {
"--out-dir=$(genDir) " +
"--plugin-path=$(location pw_rpc_plugin_rawpb_py) " +
"--compile-dir=$(genDir) " +
- "--sources $(in) " +
+ "--sources $(location :pw_log_log_proto_with_prefix) " +
"--language raw_rpc " +
"--no-experimental-proto3-optional " +
- "--protoc=$(location aprotoc) ",
+ "--protoc=$(location aprotoc) && " +
+ // TODO(b/308678575) - Ideally the previous command would create the file at the right location,
+ // but following all the layers of pigweed wrappers around protoc is too difficult.
+ "python3 -c \"import os; import shutil; " +
+ "shutil.copy2(os.path.splitext('$(location :pw_log_log_proto_with_prefix)')[0]+'.raw_rpc.pb.h', '$(out)')\"",
out: [
"pw_log/proto/log.raw_rpc.pb.h",
],
diff --git a/pw_log/BUILD.bazel b/pw_log/BUILD.bazel
index 6642cffb7..6a4776f5c 100644
--- a/pw_log/BUILD.bazel
+++ b/pw_log/BUILD.bazel
@@ -59,6 +59,11 @@ pw_cc_library(
],
)
+label_flag(
+ name = "backend_impl",
+ build_setting_default = "//pw_log_basic:impl",
+)
+
pw_cc_library(
name = "glog_adapter",
hdrs = [
diff --git a/pw_log/docs.rst b/pw_log/docs.rst
index f809e47a4..950a82c3e 100644
--- a/pw_log/docs.rst
+++ b/pw_log/docs.rst
@@ -311,6 +311,8 @@ common for the ``pw_log`` backend to cause circular dependencies. Because of
this, log backends may avoid declaring explicit dependencies, instead relying
on include paths to access header files.
+GN
+==
In GN, the ``pw_log`` backend's full implementation with true dependencies is
made available through the ``$dir_pw_log:impl`` group. When ``pw_log_BACKEND``
is set, ``$dir_pw_log:impl`` must be listed in the ``pw_build_LINK_DEPS``
@@ -327,6 +329,18 @@ to directly provide dependencies through include paths only, rather than GN
``public_deps``. In this case, GN header checking can be disabled with
``check_includes = false``.
+.. _module-pw_log-bazel-backend_impl:
+
+Bazel
+=====
+In Bazel, log backends may avoid cyclic dependencies by placing the full
+implementation in an ``impl`` target, like ``//pw_log_tokenized:impl``. The
+``//pw_log:backend_impl`` label flag should be set to the ``impl`` target
+required by the log backend used by the platform.
+
+You must add a dependency on the ``@pigweed//pw_log:backend_impl`` target to
+any binary using ``pw_log``.
+
----------------------
Google Logging Adapter
----------------------
diff --git a/pw_log_basic/BUILD.bazel b/pw_log_basic/BUILD.bazel
index 1b21fc886..89e40fb54 100644
--- a/pw_log_basic/BUILD.bazel
+++ b/pw_log_basic/BUILD.bazel
@@ -49,3 +49,9 @@ pw_cc_library(
"//pw_sys_io",
],
)
+
+# The impl of pw_log_basic is an empty library: it's so basic that there's no
+# risk of circular dependencies.
+cc_library(
+ name = "impl",
+)
diff --git a/pw_log_string/BUILD.bazel b/pw_log_string/BUILD.bazel
index bb3bff2c0..6ba51d52e 100644
--- a/pw_log_string/BUILD.bazel
+++ b/pw_log_string/BUILD.bazel
@@ -46,6 +46,11 @@ pw_cc_facade(
deps = ["//pw_preprocessor"],
)
+alias(
+ name = "impl",
+ actual = ":handler",
+)
+
pw_cc_library(
name = "handler",
srcs = ["handler.cc"],
diff --git a/pw_log_string/docs.rst b/pw_log_string/docs.rst
index 8cd07eb34..b4a67efcf 100644
--- a/pw_log_string/docs.rst
+++ b/pw_log_string/docs.rst
@@ -20,6 +20,8 @@ as the backend for ``pw_log`` via ``pw_log_string``. For example it can be
useful to mix tokenized and string based logging in case you have a C ABI where
tokenization can not be used on the other side.
+.. _module-pw_log_string-get-started-gn:
+
----------------
Get started (GN)
----------------
diff --git a/pw_log_tokenized/BUILD.bazel b/pw_log_tokenized/BUILD.bazel
index 77e716368..b598ad3cc 100644
--- a/pw_log_tokenized/BUILD.bazel
+++ b/pw_log_tokenized/BUILD.bazel
@@ -14,6 +14,7 @@
load(
"//pw_build:pigweed.bzl",
+ "pw_cc_facade",
"pw_cc_library",
"pw_cc_test",
)
@@ -46,11 +47,10 @@ pw_cc_library(
"public_overrides/pw_log_backend/log_backend_uses_pw_tokenizer.h",
],
includes = [
- "public",
"public_overrides",
],
deps = [
- ":handler",
+ ":handler_facade",
":headers",
"//pw_log:facade",
],
@@ -75,7 +75,12 @@ pw_cc_library(
],
)
-pw_cc_library(
+alias(
+ name = "impl",
+ actual = ":handler",
+)
+
+pw_cc_facade(
name = "handler_facade",
hdrs = ["public/pw_log_tokenized/handler.h"],
includes = ["public"],
@@ -84,9 +89,11 @@ pw_cc_library(
pw_cc_library(
name = "handler",
+ hdrs = ["public/pw_log_tokenized/handler.h"],
+ includes = ["public"],
deps = [
- ":handler_facade",
- "@pigweed//targets:pw_log_tokenized_handler_backend",
+ "//pw_preprocessor",
+ "//targets:pw_log_tokenized_handler_backend",
],
)
diff --git a/pw_minimal_cpp_stdlib/BUILD.gn b/pw_minimal_cpp_stdlib/BUILD.gn
index f60b2198a..7fcf11b01 100644
--- a/pw_minimal_cpp_stdlib/BUILD.gn
+++ b/pw_minimal_cpp_stdlib/BUILD.gn
@@ -76,7 +76,7 @@ pw_source_set("pw_minimal_cpp_stdlib") {
"public/pw_minimal_cpp_stdlib/internal/type_traits.h",
"public/pw_minimal_cpp_stdlib/internal/utility.h",
]
- public_deps = [ dir_pw_polyfill ]
+ public_deps = [ "$dir_pw_polyfill:standard_library" ]
remove_public_deps = [ "$dir_pw_minimal_cpp_stdlib" ]
}
diff --git a/pw_minimal_cpp_stdlib/CMakeLists.txt b/pw_minimal_cpp_stdlib/CMakeLists.txt
index 5ab9ffeee..af027fe61 100644
--- a/pw_minimal_cpp_stdlib/CMakeLists.txt
+++ b/pw_minimal_cpp_stdlib/CMakeLists.txt
@@ -14,3 +14,7 @@
add_library(pw_minimal_cpp_stdlib INTERFACE)
target_include_directories(pw_minimal_cpp_stdlib INTERFACE public)
+target_link_libraries(pw_minimal_cpp_stdlib
+ INTERFACE
+ pw_polyfill._standard_library_public
+)
diff --git a/pw_multibuf/BUILD.bazel b/pw_multibuf/BUILD.bazel
new file mode 100644
index 000000000..c078e6098
--- /dev/null
+++ b/pw_multibuf/BUILD.bazel
@@ -0,0 +1,76 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_library",
+ "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+pw_cc_library(
+ name = "chunk",
+ srcs = ["chunk.cc"],
+ hdrs = ["public/pw_multibuf/chunk.h"],
+ includes = ["public"],
+ deps = [
+ "//pw_assert",
+ "//pw_bytes",
+ "//pw_preprocessor",
+ "//pw_span",
+ "//pw_sync:mutex",
+ ],
+)
+
+pw_cc_library(
+ name = "test_utils",
+ hdrs = ["public/pw_multibuf/internal/test_utils.h"],
+ includes = ["public"],
+ visibility = [":__subpackages__"],
+ deps = [
+ ":chunk",
+ "//pw_allocator:allocator_metric_proxy",
+ "//pw_allocator:split_free_list_allocator",
+ ],
+)
+
+pw_cc_test(
+ name = "chunk_test",
+ srcs = ["chunk_test.cc"],
+ deps = [
+ ":chunk",
+ ":test_utils",
+ "//pw_unit_test",
+ ],
+)
+
+pw_cc_library(
+ name = "pw_multibuf",
+ srcs = ["multibuf.cc"],
+ hdrs = ["public/pw_multibuf/multibuf.h"],
+ deps = [":chunk"],
+)
+
+pw_cc_test(
+ name = "multibuf_test",
+ srcs = ["multibuf_test.cc"],
+ deps = [
+ ":pw_multibuf",
+ ":test_utils",
+ "//pw_unit_test",
+ ],
+)
diff --git a/pw_multibuf/BUILD.gn b/pw_multibuf/BUILD.gn
new file mode 100644
index 000000000..41a9f20d9
--- /dev/null
+++ b/pw_multibuf/BUILD.gn
@@ -0,0 +1,82 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+
+config("public_include_path") {
+ include_dirs = [ "public" ]
+ visibility = [ ":*" ]
+}
+
+pw_source_set("chunk") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_multibuf/chunk.h" ]
+ sources = [ "chunk.cc" ]
+ public_deps = [
+ "$dir_pw_sync:mutex",
+ dir_pw_assert,
+ dir_pw_bytes,
+ dir_pw_preprocessor,
+ dir_pw_span,
+ ]
+ deps = [ "$dir_pw_assert:check" ]
+}
+
+pw_source_set("test_utils") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_multibuf/internal/test_utils.h" ]
+ public_deps = [
+ ":chunk",
+ "$dir_pw_allocator:allocator_metric_proxy",
+ "$dir_pw_allocator:split_free_list_allocator",
+ ]
+}
+
+pw_test("chunk_test") {
+ deps = [
+ ":chunk",
+ ":test_utils",
+ ]
+ sources = [ "chunk_test.cc" ]
+}
+
+pw_source_set("pw_multibuf") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_multibuf/multibuf.h" ]
+ sources = [ "multibuf.cc" ]
+ public_deps = [ ":chunk" ]
+}
+
+pw_test("multibuf_test") {
+ deps = [
+ ":pw_multibuf",
+ ":test_utils",
+ ]
+ sources = [ "multibuf_test.cc" ]
+}
+
+pw_test_group("tests") {
+ tests = [
+ ":chunk_test",
+ ":multibuf_test",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_multibuf/CMakeLists.txt b/pw_multibuf/CMakeLists.txt
new file mode 100644
index 000000000..217b70a60
--- /dev/null
+++ b/pw_multibuf/CMakeLists.txt
@@ -0,0 +1,75 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_add_library(pw_multibuf.chunk STATIC
+ HEADERS
+ public/pw_multibuf/chunk.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_assert
+ pw_bytes
+ pw_preprocessor
+ pw_span
+ pw_sync.mutex
+ PRIVATE_DEPS
+ pw_assert.check
+ SOURCES
+ chunk.cc
+)
+
+pw_add_library(pw_multibuf.test_utils STATIC
+ HEADERS
+ public/pw_multibuf/internal/test_utils.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_multibuf.chunk
+ pw_allocator.allocator_metric_proxy
+ pw_allocator.split_free_list_allocator
+
+pw_add_test(pw_multibuf.chunk_test STATIC
+ SOURCES
+ chunk_test.cc
+ PRIVATE_DEPS
+ pw_multibuf.chunk
+ pw_multibuf.test_utils
+ GROUPS
+ modules
+ pw_multibuf
+)
+
+pw_add_library(pw_multibuf.pw_multibuf STATIC
+ HEADERS
+ public/pw_multibuf/multibuf.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_multibuf.chunk
+ SOURCES
+ multibuf.cc
+)
+
+pw_add_test(pw_multibuf.multibuf_test STATIC
+ SOURCES
+ multibuf_test.cc
+ PRIVATE_DEPS
+ pw_multibuf.multibuf
+ pw_multibuf.test_utils
+ GROUPS
+ modules
+ pw_multibuf
+)
diff --git a/pw_multibuf/chunk.cc b/pw_multibuf/chunk.cc
new file mode 100644
index 000000000..628a9d30c
--- /dev/null
+++ b/pw_multibuf/chunk.cc
@@ -0,0 +1,247 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_multibuf/chunk.h"
+
+#include <mutex>
+
+#include "pw_assert/check.h"
+
+namespace pw::multibuf {
+namespace {
+std::byte* CheckedAdd(std::byte* ptr, size_t offset) {
+ uintptr_t ptr_int = reinterpret_cast<uintptr_t>(ptr);
+ if (std::numeric_limits<uintptr_t>::max() - ptr_int < offset) {
+ return nullptr;
+ }
+ return reinterpret_cast<std::byte*>(ptr_int + offset);
+}
+
+std::byte* CheckedSub(std::byte* ptr, size_t offset) {
+ uintptr_t ptr_int = reinterpret_cast<uintptr_t>(ptr);
+ if (ptr_int < offset) {
+ return nullptr;
+ }
+ return reinterpret_cast<std::byte*>(ptr_int - offset);
+}
+
+std::byte* BeginPtr(ByteSpan span) { return span.data(); }
+
+std::byte* EndPtr(ByteSpan span) { return span.data() + span.size(); }
+
+} // namespace
+
+bool Chunk::CanMerge(const Chunk& next_chunk) const {
+ return region_tracker_ == next_chunk.region_tracker_ &&
+ EndPtr(span_) == BeginPtr(next_chunk.span_);
+}
+
+bool Chunk::Merge(OwnedChunk& next_chunk_owned) {
+ if (!CanMerge(*next_chunk_owned)) {
+ return false;
+ }
+ Chunk* next_chunk = next_chunk_owned.inner_;
+ next_chunk_owned.inner_ = nullptr;
+
+ // Note: Both chunks have the same ``region_tracker_``.
+ //
+ // We lock the one from `next_chunk` to satisfy the automatic
+ // checker that ``RemoveFromRegionList`` is safe to call.
+ std::lock_guard lock(next_chunk->region_tracker_->lock_);
+ PW_DCHECK(next_in_region_ == next_chunk);
+ span_ = ByteSpan(data(), size() + next_chunk->size());
+ next_chunk->RemoveFromRegionList();
+ region_tracker_->DeallocateChunkClass(next_chunk);
+ return true;
+}
+
+void Chunk::InsertAfterInRegionList(Chunk* new_chunk)
+ PW_EXCLUSIVE_LOCKS_REQUIRED(region_tracker_ -> lock_) {
+ new_chunk->next_in_region_ = next_in_region_;
+ new_chunk->prev_in_region_ = this;
+ if (next_in_region_ != nullptr) {
+ next_in_region_->prev_in_region_ = new_chunk;
+ }
+ next_in_region_ = new_chunk;
+}
+
+void Chunk::InsertBeforeInRegionList(Chunk* new_chunk)
+ PW_EXCLUSIVE_LOCKS_REQUIRED(region_tracker_ -> lock_) {
+ new_chunk->next_in_region_ = this;
+ new_chunk->prev_in_region_ = prev_in_region_;
+ if (prev_in_region_ != nullptr) {
+ prev_in_region_->next_in_region_ = new_chunk;
+ }
+ prev_in_region_ = new_chunk;
+}
+
+void Chunk::RemoveFromRegionList()
+ PW_EXCLUSIVE_LOCKS_REQUIRED(region_tracker_ -> lock_) {
+ if (prev_in_region_ != nullptr) {
+ prev_in_region_->next_in_region_ = next_in_region_;
+ }
+ if (next_in_region_ != nullptr) {
+ next_in_region_->prev_in_region_ = prev_in_region_;
+ }
+ prev_in_region_ = nullptr;
+ next_in_region_ = nullptr;
+}
+
+std::optional<OwnedChunk> Chunk::CreateFirstForRegion(
+ ChunkRegionTracker& region_tracker) {
+ void* memory = region_tracker.AllocateChunkClass();
+ if (memory == nullptr) {
+ return std::nullopt;
+ }
+ // Note: `Region()` is `const`, so no lock is required.
+ Chunk* chunk = new (memory) Chunk(&region_tracker, region_tracker.Region());
+ return OwnedChunk(chunk);
+}
+
+void Chunk::Free() {
+ span_ = ByteSpan();
+ bool region_empty;
+ // Record `region_track` so that it is available for use even after
+ // `this` is free'd by the call to `DeallocateChunkClass`.
+ ChunkRegionTracker* region_tracker = region_tracker_;
+ {
+ std::lock_guard lock(region_tracker_->lock_);
+ region_empty = prev_in_region_ == nullptr && next_in_region_ == nullptr;
+ RemoveFromRegionList();
+ // NOTE: do *not* attempt to access any fields of `this` after this point.
+ //
+ // The lock must be held while deallocating this, otherwise another
+ // ``Chunk::Release`` in the same region could race and call
+ // ``ChunkRegionTracker::Destroy``, making this call no longer valid.
+ region_tracker->DeallocateChunkClass(this);
+ }
+ if (region_empty) {
+ region_tracker->Destroy();
+ }
+}
+
+void OwnedChunk::Release() {
+ if (inner_ == nullptr) {
+ return;
+ }
+ inner_->Free();
+ inner_ = nullptr;
+}
+
+bool Chunk::ClaimPrefix(size_t bytes_to_claim) {
+ if (bytes_to_claim == 0) {
+ return true;
+ }
+ // In order to roll back `bytes_to_claim`, the current chunk must start at
+ // least `bytes_to_claim` after the beginning of the current region.
+ std::byte* new_start = CheckedSub(data(), bytes_to_claim);
+ // Note: `Region()` is `const`, so no lock is required.
+ if (new_start == nullptr || new_start < BeginPtr(region_tracker_->Region())) {
+ return false;
+ }
+
+ // `lock` is acquired in order to traverse the linked list and mutate `span_`.
+ std::lock_guard lock(region_tracker_->lock_);
+
+ // If there are any chunks before this one, they must not end after
+ // `new_start`.
+ Chunk* prev = prev_in_region_;
+ if (prev != nullptr && EndPtr(prev->span()) > new_start) {
+ return false;
+ }
+
+ size_t old_size = span_.size();
+ span_ = ByteSpan(new_start, old_size + bytes_to_claim);
+ return true;
+}
+
+bool Chunk::ClaimSuffix(size_t bytes_to_claim) {
+ if (bytes_to_claim == 0) {
+ return true;
+ }
+ // In order to expand forward `bytes_to_claim`, the current chunk must start
+ // at least `subytes` before the end of the current region.
+ std::byte* new_end = CheckedAdd(EndPtr(span()), bytes_to_claim);
+ // Note: `Region()` is `const`, so no lock is required.
+ if (new_end == nullptr || new_end > EndPtr(region_tracker_->Region())) {
+ return false;
+ }
+
+ // `lock` is acquired in order to traverse the linked list and mutate `span_`.
+ std::lock_guard lock(region_tracker_->lock_);
+
+ // If there are any chunks after this one, they must not start before
+ // `new_end`.
+ Chunk* next = next_in_region_;
+ if (next != nullptr && BeginPtr(next->span_) < new_end) {
+ return false;
+ }
+
+ size_t old_size = span_.size();
+ span_ = ByteSpan(data(), old_size + bytes_to_claim);
+ return true;
+}
+
+void Chunk::DiscardFront(size_t bytes_to_discard) {
+ Slice(bytes_to_discard, size());
+}
+
+void Chunk::Slice(size_t begin, size_t end) {
+ PW_DCHECK(begin <= size());
+ PW_DCHECK(end <= size());
+ PW_DCHECK(end >= begin);
+ ByteSpan new_span(data() + begin, end - begin);
+ std::lock_guard lock(region_tracker_->lock_);
+ span_ = new_span;
+}
+
+void Chunk::Truncate(size_t len) { Slice(0, len); }
+
+std::optional<OwnedChunk> Chunk::TakeFront(size_t bytes_to_take) {
+ void* new_chunk_memory = region_tracker_->AllocateChunkClass();
+ if (new_chunk_memory == nullptr) {
+ return std::nullopt;
+ }
+
+ PW_DCHECK(bytes_to_take <= size());
+ ByteSpan first_span = ByteSpan(data(), bytes_to_take);
+ ByteSpan second_span =
+ ByteSpan(data() + bytes_to_take, size() - bytes_to_take);
+
+ std::lock_guard lock(region_tracker_->lock_);
+ span_ = second_span;
+ Chunk* new_chunk = new (new_chunk_memory) Chunk(region_tracker_, first_span);
+ InsertBeforeInRegionList(new_chunk);
+ return OwnedChunk(new_chunk);
+}
+
+std::optional<OwnedChunk> Chunk::TakeTail(size_t bytes_to_take) {
+ void* new_chunk_memory = region_tracker_->AllocateChunkClass();
+ if (new_chunk_memory == nullptr) {
+ return std::nullopt;
+ }
+
+ PW_DCHECK(bytes_to_take <= size());
+ ByteSpan first_span = ByteSpan(data(), size() - bytes_to_take);
+ ByteSpan second_span =
+ ByteSpan(EndPtr(span()) - bytes_to_take, bytes_to_take);
+
+ std::lock_guard lock(region_tracker_->lock_);
+ span_ = first_span;
+ Chunk* new_chunk = new (new_chunk_memory) Chunk(region_tracker_, second_span);
+ InsertAfterInRegionList(new_chunk);
+ return OwnedChunk(new_chunk);
+}
+
+} // namespace pw::multibuf
diff --git a/pw_multibuf/chunk_test.cc b/pw_multibuf/chunk_test.cc
new file mode 100644
index 000000000..23ff0e3f6
--- /dev/null
+++ b/pw_multibuf/chunk_test.cc
@@ -0,0 +1,430 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_multibuf/chunk.h"
+
+#include <memory>
+
+#if __cplusplus >= 202002L
+#include <ranges>
+#endif // __cplusplus >= 202002L
+
+#include "gtest/gtest.h"
+#include "pw_multibuf/internal/test_utils.h"
+
+namespace pw::multibuf {
+namespace {
+
+using ::pw::multibuf::internal::HeaderChunkRegionTracker;
+using ::pw::multibuf::internal::TrackingAllocatorWithMemory;
+
+/// Returns literal with ``_size`` suffix as a ``size_t``.
+///
+/// This is useful for writing size-related test assertions without
+/// explicit (verbose) casts.
+constexpr size_t operator"" _size(unsigned long long n) { return n; }
+
+const size_t kArbitraryAllocatorSize = 1024;
+const size_t kArbitraryChunkSize = 32;
+
+#if __cplusplus >= 202002L
+static_assert(std::ranges::contiguous_range<Chunk>);
+#endif // __cplusplus >= 202002L
+
+void TakesSpan([[maybe_unused]] ByteSpan span) {}
+
+TEST(Chunk, IsImplicitlyConvertibleToSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk.has_value());
+ // ``Chunk`` should convert to ``ByteSpan``.
+ TakesSpan(**chunk);
+}
+
+TEST(OwnedChunk, ReleaseDestroysChunkRegion) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ auto tracker =
+ HeaderChunkRegionTracker::AllocateRegion(&alloc, kArbitraryChunkSize);
+ ASSERT_NE(tracker, nullptr);
+ EXPECT_EQ(alloc.count(), 1_size);
+
+ std::optional<OwnedChunk> chunk_opt = Chunk::CreateFirstForRegion(*tracker);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ EXPECT_EQ(alloc.count(), 2_size);
+ EXPECT_EQ(chunk.size(), kArbitraryChunkSize);
+
+ chunk.Release();
+ EXPECT_EQ(chunk.size(), 0_size);
+ EXPECT_EQ(alloc.count(), 0_size);
+ EXPECT_EQ(alloc.used(), 0_size);
+}
+
+TEST(OwnedChunk, DestructorDestroysChunkRegion) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ auto tracker =
+ HeaderChunkRegionTracker::AllocateRegion(&alloc, kArbitraryChunkSize);
+ ASSERT_NE(tracker, nullptr);
+ EXPECT_EQ(alloc.count(), 1_size);
+
+ {
+ std::optional<OwnedChunk> chunk = Chunk::CreateFirstForRegion(*tracker);
+ ASSERT_TRUE(chunk.has_value());
+ EXPECT_EQ(alloc.count(), 2_size);
+ EXPECT_EQ(chunk->size(), kArbitraryChunkSize);
+ }
+
+ EXPECT_EQ(alloc.count(), 0_size);
+ EXPECT_EQ(alloc.used(), 0_size);
+}
+
+TEST(Chunk, DiscardFrontDiscardsFrontOfSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kDiscarded = 4;
+ chunk->DiscardFront(kDiscarded);
+ EXPECT_EQ(chunk.size(), old_span.size() - kDiscarded);
+ EXPECT_EQ(chunk.data(), old_span.data() + kDiscarded);
+}
+
+TEST(Chunk, TakeFrontTakesFrontOfSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kTaken = 4;
+ std::optional<OwnedChunk> front_opt = chunk->TakeFront(kTaken);
+ ASSERT_TRUE(front_opt.has_value());
+ auto& front = *front_opt;
+ EXPECT_EQ(front->size(), kTaken);
+ EXPECT_EQ(front->data(), old_span.data());
+ EXPECT_EQ(chunk.size(), old_span.size() - kTaken);
+ EXPECT_EQ(chunk.data(), old_span.data() + kTaken);
+}
+
+TEST(Chunk, TruncateDiscardsEndOfSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kShorter = 5;
+ chunk->Truncate(old_span.size() - kShorter);
+ EXPECT_EQ(chunk.size(), old_span.size() - kShorter);
+ EXPECT_EQ(chunk.data(), old_span.data());
+}
+
+TEST(Chunk, TakeTailTakesEndOfSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kTaken = 5;
+ std::optional<OwnedChunk> tail_opt = chunk->TakeTail(kTaken);
+ ASSERT_TRUE(tail_opt.has_value());
+ auto& tail = *tail_opt;
+ EXPECT_EQ(tail.size(), kTaken);
+ EXPECT_EQ(tail.data(), old_span.data() + old_span.size() - kTaken);
+ EXPECT_EQ(chunk.size(), old_span.size() - kTaken);
+ EXPECT_EQ(chunk.data(), old_span.data());
+}
+
+TEST(Chunk, SliceRemovesSidesOfSpan) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kBegin = 4;
+ const size_t kEnd = 9;
+ chunk->Slice(kBegin, kEnd);
+ EXPECT_EQ(chunk.data(), old_span.data() + kBegin);
+ EXPECT_EQ(chunk.size(), kEnd - kBegin);
+}
+
+TEST(Chunk, RegionPersistsUntilAllChunksReleased) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ // One allocation for the region tracker, one for the chunk.
+ EXPECT_EQ(alloc.count(), 2_size);
+ const size_t kSplitPoint = 13;
+ auto split_opt = chunk->TakeFront(kSplitPoint);
+ ASSERT_TRUE(split_opt.has_value());
+ auto& split = *split_opt;
+ // One allocation for the region tracker, one for each of two chunks.
+ EXPECT_EQ(alloc.count(), 3_size);
+ chunk.Release();
+ EXPECT_EQ(alloc.count(), 2_size);
+ split.Release();
+ EXPECT_EQ(alloc.count(), 0_size);
+}
+
+TEST(Chunk, ClaimPrefixReclaimsDiscardedFront) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk.span();
+ const size_t kDiscarded = 4;
+ chunk->DiscardFront(kDiscarded);
+ EXPECT_TRUE(chunk->ClaimPrefix(kDiscarded));
+ EXPECT_EQ(chunk.size(), old_span.size());
+ EXPECT_EQ(chunk.data(), old_span.data());
+}
+
+TEST(Chunk, ClaimPrefixFailsOnFullRegionChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ EXPECT_FALSE(chunk->ClaimPrefix(1));
+}
+
+TEST(Chunk, ClaimPrefixFailsOnNeighboringChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kSplitPoint = 22;
+ auto front = chunk->TakeFront(kSplitPoint);
+ ASSERT_TRUE(front.has_value());
+ EXPECT_FALSE(chunk->ClaimPrefix(1));
+}
+
+TEST(Chunk,
+ ClaimPrefixFailsAtStartOfRegionEvenAfterReleasingChunkAtEndOfRegion) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kTaken = 13;
+ auto split = chunk->TakeTail(kTaken);
+ ASSERT_TRUE(split.has_value());
+ split->Release();
+ EXPECT_FALSE(chunk->ClaimPrefix(1));
+}
+
+TEST(Chunk, ClaimPrefixReclaimsPrecedingChunksDiscardedSuffix) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kSplitPoint = 13;
+ auto split_opt = chunk->TakeFront(kSplitPoint);
+ ASSERT_TRUE(split_opt.has_value());
+ auto& split = *split_opt;
+ const size_t kDiscard = 3;
+ split->Truncate(split.size() - kDiscard);
+ EXPECT_TRUE(chunk->ClaimPrefix(kDiscard));
+ EXPECT_FALSE(chunk->ClaimPrefix(1));
+}
+
+TEST(Chunk, ClaimSuffixReclaimsTruncatedEnd) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ ConstByteSpan old_span = chunk->span();
+ const size_t kDiscarded = 4;
+ chunk->Truncate(old_span.size() - kDiscarded);
+ EXPECT_TRUE(chunk->ClaimSuffix(kDiscarded));
+ EXPECT_EQ(chunk->size(), old_span.size());
+ EXPECT_EQ(chunk->data(), old_span.data());
+}
+
+TEST(Chunk, ClaimSuffixFailsOnFullRegionChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ EXPECT_FALSE(chunk->ClaimSuffix(1));
+}
+
+TEST(Chunk, ClaimSuffixFailsWithNeighboringChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kSplitPoint = 22;
+ auto split_opt = chunk->TakeFront(kSplitPoint);
+ ASSERT_TRUE(split_opt.has_value());
+ auto& split = *split_opt;
+ EXPECT_FALSE(split->ClaimSuffix(1));
+}
+
+TEST(Chunk, ClaimSuffixFailsAtEndOfRegionEvenAfterReleasingFirstChunkInRegion) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kTaken = 22;
+ auto split_opt = chunk->TakeTail(kTaken);
+ ASSERT_TRUE(split_opt.has_value());
+ auto& split = *split_opt;
+ EXPECT_FALSE(split->ClaimSuffix(1));
+}
+
+TEST(Chunk, ClaimSuffixReclaimsFollowingChunksDiscardedPrefix) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_opt.has_value());
+ auto& chunk = *chunk_opt;
+ const size_t kSplitPoint = 22;
+ auto split_opt = chunk->TakeFront(kSplitPoint);
+ ASSERT_TRUE(split_opt.has_value());
+ auto& split = *split_opt;
+ const size_t kDiscarded = 3;
+ chunk->DiscardFront(kDiscarded);
+ EXPECT_TRUE(split->ClaimSuffix(kDiscarded));
+ EXPECT_FALSE(split->ClaimSuffix(1));
+}
+
+TEST(Chunk, MergeReturnsFalseForChunksFromDifferentRegions) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_1_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_1_opt.has_value());
+ OwnedChunk& chunk_1 = *chunk_1_opt;
+ std::optional<OwnedChunk> chunk_2_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_2_opt.has_value());
+ OwnedChunk& chunk_2 = *chunk_2_opt;
+ EXPECT_FALSE(chunk_1->CanMerge(*chunk_2));
+ EXPECT_FALSE(chunk_1->Merge(chunk_2));
+ // Ensure that neither chunk was modified
+ EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
+ EXPECT_EQ(chunk_2.size(), kArbitraryChunkSize);
+}
+
+TEST(Chunk, MergeReturnsFalseForNonAdjacentChunksFromSameRegion) {
+ const size_t kTakenFromOne = 8;
+ const size_t kTakenFromTwo = 4;
+
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_1_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_1_opt.has_value());
+ OwnedChunk& chunk_1 = *chunk_1_opt;
+
+ std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeTail(kTakenFromOne);
+ ASSERT_TRUE(chunk_2_opt.has_value());
+ OwnedChunk& chunk_2 = *chunk_2_opt;
+
+ std::optional<OwnedChunk> chunk_3_opt = chunk_2->TakeTail(kTakenFromTwo);
+ ASSERT_TRUE(chunk_3_opt.has_value());
+ OwnedChunk& chunk_3 = *chunk_3_opt;
+
+ EXPECT_FALSE(chunk_1->CanMerge(*chunk_3));
+ EXPECT_FALSE(chunk_1->Merge(chunk_3));
+ EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize - kTakenFromOne);
+ EXPECT_EQ(chunk_2.size(), kTakenFromOne - kTakenFromTwo);
+ EXPECT_EQ(chunk_3.size(), kTakenFromTwo);
+}
+
+TEST(Chunk, MergeJoinsMultipleAdjacentChunksFromSameRegion) {
+ const size_t kTakenFromOne = 8;
+ const size_t kTakenFromTwo = 4;
+
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_1_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_1_opt.has_value());
+ OwnedChunk& chunk_1 = *chunk_1_opt;
+
+ std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeTail(kTakenFromOne);
+ ASSERT_TRUE(chunk_2_opt.has_value());
+ OwnedChunk& chunk_2 = *chunk_2_opt;
+
+ std::optional<OwnedChunk> chunk_3_opt = chunk_2->TakeTail(kTakenFromTwo);
+ ASSERT_TRUE(chunk_3_opt.has_value());
+ OwnedChunk& chunk_3 = *chunk_3_opt;
+
+ EXPECT_TRUE(chunk_1->CanMerge(*chunk_2));
+ EXPECT_TRUE(chunk_1->Merge(chunk_2));
+ EXPECT_TRUE(chunk_1->CanMerge(*chunk_3));
+ EXPECT_TRUE(chunk_1->Merge(chunk_3));
+
+ EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
+ EXPECT_EQ(chunk_2.size(), 0_size);
+ EXPECT_EQ(chunk_3.size(), 0_size);
+}
+
+TEST(Chunk, MergeJoinsAdjacentChunksFromSameRegion) {
+ const size_t kTaken = 4;
+
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ std::optional<OwnedChunk> chunk_1_opt =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc,
+ kArbitraryChunkSize);
+ ASSERT_TRUE(chunk_1_opt.has_value());
+ OwnedChunk& chunk_1 = *chunk_1_opt;
+ std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeTail(kTaken);
+ ASSERT_TRUE(chunk_2_opt.has_value());
+ OwnedChunk& chunk_2 = *chunk_2_opt;
+ EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize - kTaken);
+ EXPECT_EQ(chunk_2.size(), kTaken);
+
+ EXPECT_TRUE(chunk_1->CanMerge(*chunk_2));
+ EXPECT_TRUE(chunk_1->Merge(chunk_2));
+ EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
+ EXPECT_EQ(chunk_2.size(), 0_size);
+}
+
+} // namespace
+} // namespace pw::multibuf
diff --git a/pw_multibuf/docs.rst b/pw_multibuf/docs.rst
new file mode 100644
index 000000000..e19f8e7ce
--- /dev/null
+++ b/pw_multibuf/docs.rst
@@ -0,0 +1,87 @@
+.. _module-pw_multibuf:
+
+===========
+pw_multibuf
+===========
+.. pigweed-module::
+ :name: pw_multibuf
+ :tagline: A buffer API optimized for zero-copy messaging
+ :status: unstable
+ :languages: C++17
+
+Sending or receiving messages via RPC, transfer, or sockets often requires a
+series of intermediate buffers, each requiring their own copy of the data.
+``pw_multibuf`` allows data to be written *once*, eliminating the memory, CPU
+and latency overhead of copying.
+
+-----------------
+How does it work?
+-----------------
+``pw_multibuf`` uses several techniques to minimize copying of data:
+
+- **Header and Footer Reservation**: Lower-level components can reserve space
+ within a buffer for headers and/or footers. This allows headers and footers
+ to be added to user-provided data without moving users' data.
+- **Native Scatter/Gather and Fragmentation Support**: Buffers can refer to
+ multiple separate chunks of memory. Messages can be built up from
+ discontiguous allocations, and users' data can be fragmented across multiple
+ packets.
+- **Divisible Memory Regions**: Incoming buffers can be divided without a copy,
+ allowing incoming data to be freely demultiplexed.
+
+-------------------------------
+What kinds of data is this for?
+-------------------------------
+``pw_multibuf`` is best used in code that wants to read, write, or pass along
+data which are one of the following:
+
+- **Large**: ``pw_multibuf`` is designed to allow breaking up data into
+ multiple chunks. It also supports asynchronous allocation for when there may
+ not be sufficient space for incoming data.
+- **Communications-Oriented**: Data which is being received or sent across
+ sockets, various packets, or shared-memory protocols can benefit from the
+ fragmentation, multiplexing, and header/footer-reservation properties of
+ ``pw_multibuf``.
+- **Copy-Averse**: ``pw_multibuf`` is structured to allow users to pass around
+ and mutate buffers without copying or moving data in-memory. This can be
+ especially useful when working in systems that are latency-sensitive,
+ need to pass large amounts of data, or when memory usage is constrained.
+
+-------------
+API Reference
+-------------
+Most users of ``pw_multibuf`` will start by allocating a ``MultiBuf`` using
+a ``MultiBufAllocator`` class.
+
+``MultiBuf`` s consist of a number of ``Chunk`` s of contiguous memory.
+These ``Chunk`` s can be grown, shrunk, modified, or extracted from the
+``MultiBuf``. ``MultiBuf`` exposes an ``std::byte`` iterator interface as well
+as a ``Chunk`` iterator available through the ``Chunks()`` method.
+
+An RAII-style ``OwnedChunk`` is also provided, and manages the lifetime of
+``Chunk`` s which are not currently stored inside of a ``MultiBuf``.
+
+.. doxygenclass:: pw::multibuf::Chunk
+ :members:
+
+.. doxygenclass:: pw::multibuf::OwnedChunk
+ :members:
+
+.. doxygenclass:: pw::multibuf::MultiBuf
+ :members:
+
+.. doxygenclass:: pw::multibuf::MultiBufAllocator
+ :members:
+
+---------------------------
+Allocator Implementors' API
+---------------------------
+Some users will need to directly implement the ``MultiBufAllocator`` interface
+in order to provide allocation out of a particular region, provide particular
+allocation policy, fix Chunks to some size (such as MTU size - header for
+socket implementations), or specify other custom behavior.
+
+These users will also need to understand and implement the following APIs:
+
+.. doxygenclass:: pw::multibuf::ChunkRegionTracker
+ :members:
diff --git a/pw_multibuf/multibuf.cc b/pw_multibuf/multibuf.cc
new file mode 100644
index 000000000..b1eb80568
--- /dev/null
+++ b/pw_multibuf/multibuf.cc
@@ -0,0 +1,129 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_multibuf/multibuf.h"
+
+#include "pw_assert/check.h"
+
+namespace pw::multibuf {
+
+void MultiBuf::Release() noexcept {
+ while (first_ != nullptr) {
+ Chunk* removed = first_;
+ first_ = first_->next_in_buf_;
+ removed->Free();
+ }
+}
+
+size_t MultiBuf::size() const {
+ size_t len = 0;
+ for (const auto& chunk : Chunks()) {
+ len += chunk.size();
+ }
+ return len;
+}
+
+void MultiBuf::PushFrontChunk(OwnedChunk chunk) {
+ PW_DCHECK(chunk->next_in_buf_ == nullptr);
+ Chunk* new_chunk = std::move(chunk).Take();
+ Chunk* old_first = first_;
+ new_chunk->next_in_buf_ = old_first;
+ first_ = new_chunk;
+}
+
+MultiBuf::ChunkIterator MultiBuf::InsertChunk(ChunkIterator position,
+ OwnedChunk chunk) {
+ // Note: this also catches the cases where ``first_ == nullptr``
+ PW_DCHECK(chunk->next_in_buf_ == nullptr);
+ if (position == ChunkBegin()) {
+ PushFrontChunk(std::move(chunk));
+ return ChunkIterator(first_);
+ }
+ Chunk* previous = Previous(position.chunk_);
+ Chunk* old_next = previous->next_in_buf_;
+ Chunk* new_chunk = std::move(chunk).Take();
+ new_chunk->next_in_buf_ = old_next;
+ previous->next_in_buf_ = new_chunk;
+ return ChunkIterator(new_chunk);
+}
+
+std::tuple<MultiBuf::ChunkIterator, OwnedChunk> MultiBuf::TakeChunk(
+ ChunkIterator position) {
+ Chunk* chunk = position.chunk_;
+ if (position == ChunkBegin()) {
+ Chunk* old_first = first_;
+ first_ = old_first->next_in_buf_;
+ old_first->next_in_buf_ = nullptr;
+ return std::make_tuple(ChunkIterator(first_), OwnedChunk(old_first));
+ }
+ Chunk* previous = Previous(chunk);
+ previous->next_in_buf_ = chunk->next_in_buf_;
+ chunk->next_in_buf_ = nullptr;
+ return std::make_tuple(ChunkIterator(previous->next_in_buf_),
+ OwnedChunk(chunk));
+}
+
+Chunk* MultiBuf::Previous(Chunk* chunk) const {
+ Chunk* previous = first_;
+ while (previous != nullptr && previous->next_in_buf_ != chunk) {
+ previous = previous->next_in_buf_;
+ }
+ return previous;
+}
+
+MultiBuf::iterator& MultiBuf::iterator::operator++() {
+ if (byte_index_ + 1 == chunk_->size()) {
+ chunk_ = chunk_->next_in_buf_;
+ byte_index_ = 0;
+ AdvanceToData();
+ } else {
+ ++byte_index_;
+ }
+ return *this;
+}
+
+void MultiBuf::iterator::AdvanceToData() {
+ while (chunk_ != nullptr && chunk_->size() == 0) {
+ chunk_ = chunk_->next_in_buf_;
+ }
+}
+
+MultiBuf::const_iterator& MultiBuf::const_iterator::operator++() {
+ if (byte_index_ + 1 == chunk_->size()) {
+ chunk_ = chunk_->next_in_buf_;
+ byte_index_ = 0;
+ AdvanceToData();
+ } else {
+ ++byte_index_;
+ }
+ return *this;
+}
+
+void MultiBuf::const_iterator::AdvanceToData() {
+ while (chunk_ != nullptr && chunk_->size() == 0) {
+ chunk_ = chunk_->next_in_buf_;
+ }
+}
+
+size_t MultiBuf::ChunkIterable::size() const {
+ Chunk* current = first_;
+ size_t i = 0;
+ while (current != nullptr) {
+ ++i;
+ current = current->next_in_buf_;
+ }
+ return i;
+}
+
+} // namespace pw::multibuf
diff --git a/pw_multibuf/multibuf_test.cc b/pw_multibuf/multibuf_test.cc
new file mode 100644
index 000000000..d1bfe969d
--- /dev/null
+++ b/pw_multibuf/multibuf_test.cc
@@ -0,0 +1,214 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_multibuf/multibuf.h"
+
+#include "gtest/gtest.h"
+#include "pw_bytes/suffix.h"
+#include "pw_multibuf/internal/test_utils.h"
+
+namespace pw::multibuf {
+namespace {
+
+using ::pw::multibuf::internal::HeaderChunkRegionTracker;
+using ::pw::multibuf::internal::TrackingAllocatorWithMemory;
+
+const size_t kArbitraryAllocatorSize = 1024;
+const size_t kArbitraryChunkSize = 32;
+
+#if __cplusplus >= 202002L
+static_assert(std::forward_iterator<MultiBuf::iterator>);
+static_assert(std::forward_iterator<MultiBuf::const_iterator>);
+static_assert(std::forward_iterator<MultiBuf::ChunkIterator>);
+static_assert(std::forward_iterator<MultiBuf::ConstChunkIterator>);
+#endif // __cplusplus >= 202002L
+
+OwnedChunk MakeChunk(pw::allocator::Allocator& alloc, size_t size) {
+ std::optional<OwnedChunk> chunk =
+ HeaderChunkRegionTracker::AllocateRegionAsChunk(&alloc, size);
+ assert(chunk.has_value());
+ return std::move(*chunk);
+}
+
+TEST(MultiBuf, IsDefaultConstructible) { [[maybe_unused]] MultiBuf buf; }
+
+TEST(MultiBuf, WithOneChunkReleases) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ EXPECT_EQ(alloc.count(), 2U);
+ buf.Release();
+ EXPECT_EQ(alloc.count(), 0U);
+}
+
+TEST(MultiBuf, WithOneChunkReleasesOnDestruction) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ {
+ MultiBuf buf;
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ EXPECT_EQ(alloc.count(), 2U);
+ }
+ EXPECT_EQ(alloc.count(), 0U);
+}
+
+TEST(MultiBuf, WithMultipleChunksReleasesAllOnDestruction) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ {
+ MultiBuf buf;
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ EXPECT_EQ(alloc.count(), 4U);
+ }
+ EXPECT_EQ(alloc.count(), 0U);
+}
+
+TEST(MultiBuf, SizeReturnsNumberOfBytes) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+ EXPECT_EQ(buf.size(), 0U);
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ EXPECT_EQ(buf.size(), kArbitraryChunkSize);
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+ EXPECT_EQ(buf.size(), kArbitraryChunkSize * 2);
+}
+
+TEST(MultiBuf, PushFrontChunkAddsBytesToFront) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+
+ const std::array<std::byte, 3> kBytesOne = {0_b, 1_b, 2_b};
+ auto chunk_one = MakeChunk(alloc, kBytesOne.size());
+ std::copy(kBytesOne.begin(), kBytesOne.end(), chunk_one->begin());
+ buf.PushFrontChunk(std::move(chunk_one));
+
+ size_t i = 0;
+ auto buf_iter = buf.begin();
+ for (; i < kBytesOne.size(); i++, buf_iter++) {
+ ASSERT_NE(buf_iter, buf.end());
+ EXPECT_EQ(*buf_iter, kBytesOne[i]);
+ }
+
+ const std::array<std::byte, 4> kBytesTwo = {9_b, 10_b, 11_b, 12_b};
+ auto chunk_two = MakeChunk(alloc, kBytesTwo.size());
+ std::copy(kBytesTwo.begin(), kBytesTwo.end(), chunk_two->begin());
+ buf.PushFrontChunk(std::move(chunk_two));
+
+ std::array<std::byte, 7> expected = {
+ 9_b,
+ 10_b,
+ 11_b,
+ 12_b,
+ 0_b,
+ 1_b,
+ 2_b,
+ };
+ i = 0;
+ buf_iter = buf.begin();
+ for (; i < expected.size(); i++, buf_iter++) {
+ ASSERT_NE(buf_iter, buf.end());
+ EXPECT_EQ(*buf_iter, expected[i]);
+ }
+}
+
+TEST(MultiBuf, InsertChunkOnEmptyBufAddsFirstChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+
+ const std::array<std::byte, 3> kBytes = {0_b, 1_b, 2_b};
+ auto chunk = MakeChunk(alloc, kBytes.size());
+ std::copy(kBytes.begin(), kBytes.end(), chunk->begin());
+ auto inserted_iter = buf.InsertChunk(buf.Chunks().begin(), std::move(chunk));
+ EXPECT_EQ(inserted_iter, buf.Chunks().begin());
+
+ size_t i = 0;
+ auto buf_iter = buf.begin();
+ for (; i < kBytes.size(); i++, buf_iter++) {
+ ASSERT_NE(buf_iter, buf.end());
+ EXPECT_EQ(*buf_iter, kBytes[i]);
+ }
+
+ EXPECT_EQ(++inserted_iter, buf.Chunks().end());
+}
+
+TEST(MultiBuf, InsertChunkAtEndOfBufAddsLastChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+
+ // Add a chunk to the beginning
+ buf.PushFrontChunk(MakeChunk(alloc, kArbitraryChunkSize));
+
+ const std::array<std::byte, 3> kBytes = {0_b, 1_b, 2_b};
+ auto chunk = MakeChunk(alloc, kBytes.size());
+ std::copy(kBytes.begin(), kBytes.end(), chunk->begin());
+ auto inserted_iter = buf.InsertChunk(buf.Chunks().end(), std::move(chunk));
+ EXPECT_EQ(inserted_iter, ++buf.Chunks().begin());
+ EXPECT_EQ(++inserted_iter, buf.Chunks().end());
+
+ size_t i = 0;
+ auto buf_iter = buf.Chunks().begin();
+ buf_iter++;
+ auto chunk_iter = buf_iter->begin();
+ for (; i < kBytes.size(); i++, chunk_iter++) {
+ ASSERT_NE(chunk_iter, buf_iter->end());
+ EXPECT_EQ(*chunk_iter, kBytes[i]);
+ }
+}
+
+TEST(MultiBuf, TakeChunkAtBeginRemovesAndReturnsFirstChunk) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+ auto insert_iter = buf.Chunks().begin();
+ insert_iter = buf.InsertChunk(insert_iter, MakeChunk(alloc, 2));
+ insert_iter = buf.InsertChunk(++insert_iter, MakeChunk(alloc, 4));
+
+ auto [chunk_iter, chunk] = buf.TakeChunk(buf.Chunks().begin());
+ EXPECT_EQ(chunk.size(), 2U);
+ EXPECT_EQ(chunk_iter->size(), 4U);
+ chunk_iter++;
+ EXPECT_EQ(chunk_iter, buf.Chunks().end());
+}
+
+TEST(MultiBuf, TakeChunkOnLastInsertedIterReturnsLastInserted) {
+ TrackingAllocatorWithMemory<kArbitraryAllocatorSize> alloc;
+ MultiBuf buf;
+ auto iter = buf.Chunks().begin();
+ iter = buf.InsertChunk(iter, MakeChunk(alloc, 42));
+ iter = buf.InsertChunk(++iter, MakeChunk(alloc, 11));
+ iter = buf.InsertChunk(++iter, MakeChunk(alloc, 65));
+ OwnedChunk chunk;
+ std::tie(iter, chunk) = buf.TakeChunk(iter);
+ EXPECT_EQ(iter, buf.Chunks().end());
+ EXPECT_EQ(chunk.size(), 65U);
+}
+
+TEST(MultiBuf, RangeBasedForLoopsCompile) {
+ MultiBuf buf;
+ for ([[maybe_unused]] std::byte& byte : buf) {
+ }
+ for ([[maybe_unused]] const std::byte& byte : buf) {
+ }
+ for ([[maybe_unused]] Chunk& chunk : buf.Chunks()) {
+ }
+ for ([[maybe_unused]] const Chunk& chunk : buf.Chunks()) {
+ }
+
+ const MultiBuf const_buf;
+ for ([[maybe_unused]] const std::byte& byte : const_buf) {
+ }
+ for ([[maybe_unused]] const Chunk& chunk : const_buf.Chunks()) {
+ }
+}
+
+} // namespace
+} // namespace pw::multibuf
diff --git a/pw_multibuf/public/pw_multibuf/chunk.h b/pw_multibuf/public/pw_multibuf/chunk.h
new file mode 100644
index 000000000..9747b944a
--- /dev/null
+++ b/pw_multibuf/public/pw_multibuf/chunk.h
@@ -0,0 +1,372 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <optional>
+
+#include "pw_assert/assert.h"
+#include "pw_bytes/span.h"
+#include "pw_sync/mutex.h"
+
+namespace pw::multibuf {
+
+class ChunkRegionTracker;
+class OwnedChunk;
+
+/// A handle to a contiguous slice of data.
+///
+/// A ``Chunk`` is similar to a ``ByteSpan``, but is aware of the underlying
+/// memory allocation, and is able to split, shrink, and grow into neighboring
+/// empty space.
+///
+/// This class is optimized to allow multiple owners to write into neighboring
+/// regions of the same allocation. One important usecase for this is
+/// communication protocols which want to reserve space at the front or rear of
+/// a buffer for headers or footers.
+///
+/// In order to support zero-copy DMA of communications buffers, allocators can
+/// create properly-aligned ``Chunk`` regions in appropriate memory. The driver
+/// can then ``DiscardFront`` in order to reserve bytes for headers,
+/// ``Truncate`` in order to reserve bytes for footers, and then pass the
+/// ``Chunk`` to the user to fill in. The header and footer space can then
+/// be reclaimed using the ``ClaimPrefix`` and ``ClaimSuffix`` methods.
+class Chunk {
+ public:
+ Chunk() = delete;
+ // Not copyable or movable.
+ Chunk(Chunk&) = delete;
+ Chunk& operator=(Chunk&) = delete;
+ Chunk(Chunk&&) = delete;
+ Chunk& operator=(Chunk&&) = delete;
+
+ /// Creates the first ``Chunk`` referencing a whole region of memory.
+ ///
+ /// This must only be called once per ``ChunkRegionTracker``, when the region
+ /// is first created. Multiple calls will result in undefined behavior.
+ ///
+ /// Returns ``std::nullopt`` if ``AllocateChunkStorage`` returns ``nullptr``.
+ static std::optional<OwnedChunk> CreateFirstForRegion(
+ ChunkRegionTracker& region_tracker);
+
+ std::byte* data() { return span_.data(); }
+ const std::byte* data() const { return span_.data(); }
+ size_t size() const { return span_.size(); }
+ ByteSpan span() { return span_; }
+ ConstByteSpan span() const { return span_; }
+
+ std::byte& operator[](size_t index) { return span_[index]; }
+ const std::byte& operator[](size_t index) const { return span_[index]; }
+
+ // Container declarations
+ using element_type = std::byte;
+ using value_type = std::byte;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using pointer = std::byte*;
+ using const_pointer = const std::byte*;
+ using reference = std::byte&;
+ using const_reference = const std::byte&;
+ using iterator = std::byte*;
+ using const_iterator = const std::byte*;
+ using reverse_iterator = std::byte*;
+ using const_reverse_iterator = const std::byte*;
+
+ std::byte* begin() { return span_.data(); }
+ const std::byte* cbegin() const { return span_.data(); }
+ std::byte* end() { return span_.data() + span_.size(); }
+ const std::byte* cend() const { return span_.data() + span_.size(); }
+
+ /// Returns if ``next_chunk`` is mergeable into the end of this ``Chunk``.
+ ///
+ /// This will only succeed when the two ``Chunk`` s are adjacent in
+ /// memory and originated from the same allocation.
+ [[nodiscard]] bool CanMerge(const Chunk& next_chunk) const;
+
+ /// Attempts to merge ``next_chunk`` into the end of this ``Chunk``.
+ ///
+ /// If the chunks are successfully merged, this ``Chunk`` will be extended
+ /// forwards to encompass the space of ``next_chunk``, and ``next_chunk``
+ /// will be emptied and ``Release``d.
+ ///
+ /// This will only succeed when the two ``Chunk`` s are adjacent in
+ /// memory and originated from the same allocation.
+ ///
+ /// If the chunks are not mergeable, neither ``Chunk`` will be modified.
+ bool Merge(OwnedChunk& next_chunk);
+
+ /// Attempts to add ``bytes_to_claim`` to the front of this buffer by
+ /// advancing its range backwards in memory. Returns ``true`` if the operation
+ /// succeeded.
+ ///
+ /// This will only succeed if this ``Chunk`` points to a section of a region
+ /// that has unreferenced bytes preceeding it. For example, a ``Chunk`` which
+ /// has been shrunk using ``DiscardFront`` can be re-expanded using
+ /// ``ClaimPrefix``.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ [[nodiscard]] bool ClaimPrefix(size_t bytes_to_claim);
+
+ /// Attempts to add ``bytes_to_claim`` to the front of this buffer by
+ /// advancing its range forwards in memory. Returns ``true`` if the operation
+ /// succeeded.
+ ///
+ /// This will only succeed if this ``Chunk`` points to a section of a region
+ /// that has unreferenced bytes following it. For example, a ``Chunk`` which
+ /// has been shrunk using ``Truncate`` can be re-expanded using
+ ///
+ /// This method will acquire a mutex and is not IRQ safe. /// ``ClaimSuffix``.
+ [[nodiscard]] bool ClaimSuffix(size_t bytes_to_claim);
+
+ /// Shrinks this handle to refer to the data beginning at offset
+ /// ``bytes_to_discard``.
+ ///
+ /// Does not modify the underlying data.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void DiscardFront(size_t bytes_to_discard);
+
+ /// Shrinks this handle to refer to data in the range ``begin..<end``.
+ ///
+ /// Does not modify the underlying data.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void Slice(size_t begin, size_t end);
+
+ /// Shrinks this handle to refer to only the first ``len`` bytes.
+ ///
+ /// Does not modify the underlying data.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void Truncate(size_t len);
+
+ /// Attempts to shrink this handle to refer to the data beginning at
+ /// offset ``bytes_to_take``, returning the first ``bytes_to_take`` bytes as
+ /// a new ``OwnedChunk``.
+ ///
+ /// If the inner call to ``AllocateChunkClass`` fails, this function
+ /// will return ``std::nullopt` and this handle's span will not change.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ std::optional<OwnedChunk> TakeFront(size_t bytes_to_take);
+
+ /// Attempts to shrink this handle to refer only the first
+ /// ``len - bytes_to_take`` bytes, returning the last ``bytes_to_take``
+ /// bytes as a new ``OwnedChunk``.
+ ///
+ /// If the inner call to ``AllocateChunkClass`` fails, this function
+ /// will return ``std::nullopt`` and this handle's span will not change.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ std::optional<OwnedChunk> TakeTail(size_t bytes_to_take);
+
+ private:
+ Chunk(ChunkRegionTracker* region_tracker, ByteSpan span)
+ : region_tracker_(region_tracker),
+ next_in_region_(nullptr),
+ prev_in_region_(nullptr),
+ span_(span) {}
+
+ // NOTE: these functions are logically
+ // `PW_EXCLUSIVE_LOCKS_REQUIRED(region_tracker_->lock_)`, however this
+ // annotation cannot be applied due to the forward declaration of
+ // `region_tracker_`.
+ void InsertAfterInRegionList(Chunk* new_chunk);
+ void InsertBeforeInRegionList(Chunk* new_chunk);
+ void RemoveFromRegionList();
+
+ /// Frees this ``Chunk``. Future accesses to this class after calls to
+ /// ``Free`` are undefined behavior.
+ ///
+ /// Decrements the reference count on the underlying chunk of data.
+ ///
+ /// Does not modify the underlying data, but may cause it to be deallocated
+ /// if this was the only remaining ``Chunk`` referring to its region.
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void Free();
+
+ /// The region_tracker_ for this chunk.
+ ChunkRegionTracker* const region_tracker_;
+
+ /// Pointer to the next chunk in the same ``region_tracker_``.
+ ///
+ /// Guarded by ``region_tracker_->lock_`` (but not annotated as such due to
+ /// the fact that annotations aren't smart enough to understand that all
+ /// chunks within a region share a tracker + lock).
+ ///
+ /// ``nullptr`` if this is the last ``Chunk`` in its ``region_tracker_``.
+ Chunk* next_in_region_;
+
+ /// Pointer to the previous chunk in the same ``region_tracker_``.
+ ///
+ /// Guarded by ``region_tracker_->lock_`` (but not annotated as such due to
+ /// the fact that annotations aren't smart enough to understand that all
+ /// chunks within a region share a tracker + lock).
+ ///
+ /// ``nullptr`` if this is the first ``Chunk`` in its ``region_tracker_``.
+ Chunk* prev_in_region_;
+
+ /// Pointer to the next chunk in a ``MultiBuf``.
+ ///
+ /// This is ``nullptr`` if this chunk is not in a ``MultiBuf``, or is the
+ /// last element of a ``MultiBuf``.
+ //
+ // reserved for use by the MultiBuf class.
+ Chunk* next_in_buf_;
+
+ /// Pointer to the sub-region to which this chunk has exclusive access.
+ ///
+ /// This ``span_`` is conceptually owned by this ``Chunk`` object, and
+ /// may be read or written to by a ``Chunk`` user (the normal rules of
+ /// thread compatibility apply-- `const` methods may be called
+ /// concurrently, non-`const` methods may not).
+ ///
+ /// Neighboring ``Chunk`` objects in this region looking to expand via
+ /// ``ClaimPrefix`` or ``ClaimSuffix`` may read this span's
+ /// boundaries. These other ``Chunk``s must acquire
+ /// ``region_tracker_->lock_`` before reading, and this ``Chunk`` must
+ /// acquire ``region_tracker_->lock_`` before changing ``span_``.
+ ByteSpan span_;
+
+ friend class OwnedChunk; // for ``Free``.
+ friend class MultiBuf; // for ``Free`` and ``next_in_buf_``.
+};
+
+/// An object that manages a single allocated region which is referenced by one
+/// or more ``Chunk`` objects.
+///
+/// This class is typically implemented by ``MultiBufAllocator``
+/// implementations in order to customize the behavior of region deallocation.
+///
+/// ``ChunkRegionTracker`` s have three responsibilities:
+///
+/// - Tracking the region of memory into which ``Chunk`` s can expand.
+/// This is reported via the ``Region`` method. ``Chunk`` s in this region
+/// can refer to memory within this region sparsely, but they can grow or
+/// shrink so long as they stay within the bounds of the ``Region``.
+///
+/// - Deallocating the region and the ``ChunkRegionTracker`` itself.
+/// This is implemented via the ``Destroy`` method, which is called once all
+/// of the ``Chunk`` s in a region have been released.
+///
+/// - Allocating and deallocating space for ``Chunk`` classes. This is merely
+/// allocating space for the ``Chunk`` object itself, not the memory to which
+/// it refers. This can be implemented straightforwardly by delegating to an
+/// existing generic allocator such as ``malloc`` or a
+/// ``pw::allocator::Allocator`` implementation.
+class ChunkRegionTracker {
+ protected:
+ ChunkRegionTracker() = default;
+ virtual ~ChunkRegionTracker() = default;
+
+ /// Destroys the ``ChunkRegionTracker``.
+ ///
+ /// Typical implementations will call ``std::destroy_at(this)`` and then free
+ /// the memory associated with the region and the tracker.
+ virtual void Destroy() = 0;
+
+ /// Returns the entire span of the region being managed.
+ ///
+ /// ``Chunk`` s referencing this tracker will not expand beyond this region,
+ /// nor into one another's portions of the region.
+ ///
+ /// This region must not change for the lifetime of this
+ /// ``ChunkRegionTracker``.
+ virtual ByteSpan Region() const = 0;
+
+ /// Returns a pointer to ``sizeof(Chunk)`` bytes.
+ /// Returns ``nullptr`` on failure.
+ virtual void* AllocateChunkClass() = 0;
+
+ /// Deallocates a pointer returned by ``AllocateChunkClass``.
+ virtual void DeallocateChunkClass(void*) = 0;
+
+ private:
+ /// A lock used to manage an internal linked-list of ``Chunk``s.
+ ///
+ /// This allows chunks to:
+ /// - know whether they can expand to fill neighboring regions of memory.
+ /// - know when the last chunk has been destructed, triggering `Destroy`.
+ pw::sync::Mutex lock_;
+ friend Chunk;
+};
+
+/// An RAII handle to a contiguous slice of data.
+///
+/// Note: ``OwnedChunk`` may acquire a ``pw::sync::Mutex`` during destruction,
+/// and so must not be destroyed within ISR contexts.
+class OwnedChunk {
+ public:
+ OwnedChunk() : inner_(nullptr) {}
+ OwnedChunk(OwnedChunk&& other) noexcept : inner_(other.inner_) {
+ other.inner_ = nullptr;
+ }
+ OwnedChunk& operator=(OwnedChunk&& other) noexcept {
+ inner_ = other.inner_;
+ other.inner_ = nullptr;
+ return *this;
+ }
+ /// This method will acquire a mutex and is not IRQ safe.
+ ~OwnedChunk() { Release(); }
+
+ std::byte* data() { return span().data(); }
+ const std::byte* data() const { return span().data(); }
+ size_t size() const { return span().size(); }
+ ByteSpan span() { return inner_ == nullptr ? ByteSpan() : inner_->span(); }
+ ConstByteSpan span() const {
+ return inner_ == nullptr ? ConstByteSpan() : inner_->span();
+ }
+
+ std::byte& operator[](size_t index) { return span()[index]; }
+ std::byte operator[](size_t index) const { return span()[index]; }
+
+ Chunk& operator*() { return *inner_; }
+ const Chunk& operator*() const { return *inner_; }
+ Chunk* operator->() { return inner_; }
+ const Chunk* operator->() const { return inner_; }
+
+ /// Decrements the reference count on the underlying chunk of data and
+ /// empties this handle so that `span()` now returns an empty (zero-sized)
+ /// span.
+ ///
+ /// Does not modify the underlying data, but may cause it to be deallocated
+ /// if this was the only remaining ``Chunk`` referring to its region.
+ ///
+ /// This method is equivalent to ``{ Chunk _unused = std::move(chunk_ref); }``
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void Release();
+
+ /// Returns the contained ``Chunk*`` and empties this ``OwnedChunk`` without
+ /// releasing the underlying ``Chunk``.
+ Chunk* Take() && {
+ Chunk* result = inner_;
+ inner_ = nullptr;
+ return result;
+ }
+
+ private:
+ /// Constructs a new ``OwnedChunk`` from an existing ``Chunk*``.
+ OwnedChunk(Chunk* inner) : inner_(inner) {}
+
+ /// A pointer to the owned ``Chunk``.
+ Chunk* inner_;
+
+ /// Allow ``Chunk`` and ``MultiBuf`` to create ``OwnedChunk``s using the
+ /// private constructor above.
+ friend class Chunk;
+ friend class MultiBuf;
+};
+
+} // namespace pw::multibuf
diff --git a/pw_multibuf/public/pw_multibuf/internal/test_utils.h b/pw_multibuf/public/pw_multibuf/internal/test_utils.h
new file mode 100644
index 000000000..07e325016
--- /dev/null
+++ b/pw_multibuf/public/pw_multibuf/internal/test_utils.h
@@ -0,0 +1,162 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#pragma once
+
+#include <memory>
+
+#include "pw_allocator/allocator_metric_proxy.h"
+#include "pw_allocator/split_free_list_allocator.h"
+#include "pw_multibuf/chunk.h"
+
+namespace pw::multibuf::internal {
+
+/// A basic ``Allocator`` implementation that reports the number and size of
+/// allocations.
+class TrackingAllocator : public pw::allocator::Allocator {
+ public:
+ /// Constructs a new ``TrackingAllocator`` which allocates from the provided
+ /// region of memory.
+ TrackingAllocator(ByteSpan span) : alloc_stats_(kFakeToken) {
+ Status status = alloc_.Init(span, kFakeThreshold);
+ EXPECT_EQ(status, OkStatus());
+ alloc_stats_.Initialize(alloc_);
+ }
+
+ /// Returns the number of current allocations.
+ size_t count() const { return alloc_stats_.count(); }
+
+ /// Returns the combined size in bytes of all current allocations.
+ size_t used() const { return alloc_stats_.used(); }
+
+ protected:
+ void* DoAllocate(allocator::Layout layout) override {
+ return alloc_stats_.Allocate(layout);
+ }
+ bool DoResize(void* ptr,
+ allocator::Layout old_layout,
+ size_t new_size) override {
+ return alloc_stats_.Resize(ptr, old_layout, new_size);
+ }
+ void DoDeallocate(void* ptr, allocator::Layout layout) override {
+ alloc_stats_.Deallocate(ptr, layout);
+ }
+
+ private:
+ const size_t kFakeThreshold = 0;
+ const int32_t kFakeToken = 0;
+
+ pw::allocator::SplitFreeListAllocator<> alloc_;
+ pw::allocator::AllocatorMetricProxy alloc_stats_;
+};
+
+/// A ``TrackingAllocator`` which holds an internal buffer of size `num_buffer`
+/// for its allocations.
+template <auto num_bytes>
+class TrackingAllocatorWithMemory : public pw::allocator::Allocator {
+ public:
+ TrackingAllocatorWithMemory() : mem_(), alloc_(mem_) {}
+ size_t count() const { return alloc_.count(); }
+ size_t used() const { return alloc_.used(); }
+ void* DoAllocate(allocator::Layout layout) override {
+ return alloc_.Allocate(layout);
+ }
+ bool DoResize(void* ptr,
+ allocator::Layout old_layout,
+ size_t new_size) override {
+ return alloc_.Resize(ptr, old_layout, new_size);
+ }
+ void DoDeallocate(void* ptr, allocator::Layout layout) override {
+ alloc_.Deallocate(ptr, layout);
+ }
+
+ private:
+ std::array<std::byte, num_bytes> mem_;
+ TrackingAllocator alloc_;
+};
+
+/// A ``ChunkRegionTracker`` which stores its ``Chunk`` and region metadata
+/// in a ``pw::allocator::Allocator`` allocation alongside the data.
+class HeaderChunkRegionTracker final : public ChunkRegionTracker {
+ public:
+ /// Allocates a new ``Chunk`` region of ``size`` bytes in ``alloc``.
+ ///
+ /// The underlyiing allocation will also store the
+ /// ``HeaderChunkRegionTracker`` itself.
+ ///
+ /// Returns the newly-created ``OwnedChunk`` if successful.
+ static std::optional<OwnedChunk> AllocateRegionAsChunk(
+ pw::allocator::Allocator* alloc, size_t size) {
+ HeaderChunkRegionTracker* tracker = AllocateRegion(alloc, size);
+ if (tracker == nullptr) {
+ return std::nullopt;
+ }
+ std::optional<OwnedChunk> chunk = Chunk::CreateFirstForRegion(*tracker);
+ if (!chunk.has_value()) {
+ tracker->Destroy();
+ return std::nullopt;
+ }
+ return chunk;
+ }
+
+ /// Allocates a new ``Chunk`` region of ``size`` bytes in ``alloc``.
+ ///
+ /// The underlyiing allocation will also store the
+ /// ``HeaderChunkRegionTracker`` itself.
+ ///
+ /// Returns a pointer to the newly-created ``HeaderChunkRegionTracker``
+ /// or ``nullptr`` if the allocation failed.
+ static HeaderChunkRegionTracker* AllocateRegion(
+ pw::allocator::Allocator* alloc, size_t size) {
+ auto layout =
+ allocator::Layout::Of<HeaderChunkRegionTracker>().Extend(size);
+ void* ptr = alloc->Allocate(layout);
+ if (ptr == nullptr) {
+ return nullptr;
+ }
+ std::byte* data =
+ reinterpret_cast<std::byte*>(ptr) + sizeof(HeaderChunkRegionTracker);
+ return new (ptr) HeaderChunkRegionTracker(ByteSpan(data, size), alloc);
+ }
+
+ ByteSpan Region() const final { return region_; }
+ ~HeaderChunkRegionTracker() final {}
+
+ protected:
+ void Destroy() final {
+ std::byte* ptr = reinterpret_cast<std::byte*>(this);
+ auto layout = allocator::Layout::Of<HeaderChunkRegionTracker>().Extend(
+ region_.size());
+ auto alloc = alloc_;
+ std::destroy_at(this);
+ alloc->Deallocate(ptr, layout);
+ }
+ void* AllocateChunkClass() final {
+ return alloc_->Allocate(pw::allocator::Layout::Of<Chunk>());
+ }
+ void DeallocateChunkClass(void* ptr) final {
+ alloc_->Deallocate(ptr, pw::allocator::Layout::Of<Chunk>());
+ }
+
+ private:
+ ByteSpan region_;
+ pw::allocator::Allocator* alloc_;
+
+ // NOTE: `region` must directly follow this `FakeChunkRegionTracker`
+ // in memory allocated by allocated by `alloc`.
+ HeaderChunkRegionTracker(ByteSpan region, pw::allocator::Allocator* alloc)
+ : region_(region), alloc_(alloc) {}
+};
+
+} // namespace pw::multibuf::internal
diff --git a/pw_multibuf/public/pw_multibuf/multibuf.h b/pw_multibuf/public/pw_multibuf/multibuf.h
new file mode 100644
index 000000000..a48ce6fde
--- /dev/null
+++ b/pw_multibuf/public/pw_multibuf/multibuf.h
@@ -0,0 +1,373 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <tuple>
+
+#include "pw_multibuf/chunk.h"
+
+namespace pw::multibuf {
+
+/// A buffer optimized for zero-copy data transfer.
+///
+/// A ``MultiBuf`` consists of multiple ``Chunk`` s of data.
+class MultiBuf {
+ public:
+ class iterator;
+ class const_iterator;
+ class ChunkIterator;
+ class ConstChunkIterator;
+ class ChunkIterable;
+ class ConstChunkIterable;
+
+ constexpr MultiBuf() : first_(nullptr) {}
+ constexpr MultiBuf(MultiBuf&& other) noexcept : first_(other.first_) {
+ other.first_ = nullptr;
+ }
+ MultiBuf& operator=(MultiBuf&& other) noexcept {
+ Release();
+ first_ = other.first_;
+ other.first_ = nullptr;
+ return *this;
+ }
+
+ /// Decrements the reference count on the underlying chunks of data and
+ /// empties this ``MultiBuf`` so that ``size() == 0``.
+ ///
+ /// Does not modify the underlying data, but may cause it to be deallocated
+ ///
+ /// This method is equivalent to ``{ MultiBuf _unused = std::move(multibuf);
+ /// }``
+ ///
+ /// This method will acquire a mutex and is not IRQ safe.
+ void Release() noexcept;
+
+ /// This destructor will acquire a mutex and is not IRQ safe.
+ ~MultiBuf() { Release(); }
+
+ /// Returns the number of bytes in this container.
+ ///
+ /// This method's complexity is ``O(Chunks().size())``.
+ [[nodiscard]] size_t size() const;
+
+ /// Pushes ``Chunk`` onto the front of the ``MultiBuf``.
+ ///
+ /// This operation does not move any data and is ``O(1)``.
+ void PushFrontChunk(OwnedChunk chunk);
+
+ /// Inserts ``chunk`` into the specified position in the ``MultiBuf``.
+ ///
+ /// This operation does not move any data and is ``O(Chunks().size())``.
+ ///
+ /// Returns an iterator pointing to the newly-inserted ``Chunk``.
+ //
+ // Implementation note: ``Chunks().size()`` should be remain relatively
+ // small, but this could be made ``O(1)`` in the future by adding a ``prev``
+ // pointer to the ``ChunkIterator``.
+ ChunkIterator InsertChunk(ChunkIterator position, OwnedChunk chunk);
+
+ /// Removes a ``Chunk`` from the specified position.
+ ///
+ /// This operation does not move any data and is ``O(Chunks().size())``.
+ ///
+ /// Returns an iterator pointing to the ``Chunk`` after the removed
+ /// ``Chunk``, or ``Chunks().end()`` if this was the last ``Chunk`` in the
+ /// ``MultiBuf``.
+ //
+ // Implementation note: ``Chunks().size()`` should be remain relatively
+ // small, but this could be made ``O(1)`` in the future by adding a ``prev``
+ // pointer to the ``ChunkIterator``.
+ std::tuple<ChunkIterator, OwnedChunk> TakeChunk(ChunkIterator position);
+
+ /// Returns an iterator pointing to the first byte of this ``MultiBuf`.
+ iterator begin() { return iterator(first_, 0); }
+ /// Returns a const iterator pointing to the first byte of this ``MultiBuf`.
+ const_iterator begin() const { return const_iterator(first_, 0); }
+ /// Returns a const iterator pointing to the first byte of this ``MultiBuf`.
+ const_iterator cbegin() const { return const_iterator(first_, 0); }
+
+ /// Returns an iterator pointing to the end of this ``MultiBuf``.
+ iterator end() { return iterator::end(); }
+ /// Returns a const iterator pointing to the end of this ``MultiBuf``.
+ const_iterator end() const { return const_iterator::end(); }
+ /// Returns a const iterator pointing to the end of this ``MultiBuf``.
+ const_iterator cend() const { return const_iterator::end(); }
+
+ /// Returns an iterable container which yields the ``Chunk``s in this
+ /// ``MultiBuf``.
+ constexpr ChunkIterable Chunks() { return ChunkIterable(first_); }
+
+ /// Returns an iterable container which yields the ``const Chunk``s in
+ /// this ``MultiBuf``.
+ constexpr const ChunkIterable Chunks() const { return ChunkIterable(first_); }
+
+ /// Returns an iterator pointing to the first ``Chunk`` in this ``MultiBuf``.
+ constexpr ChunkIterator ChunkBegin() { return ChunkIterator(first_); }
+ /// Returns an iterator pointing to the end of the ``Chunk``s in this
+ /// ``MultiBuf``.
+ constexpr ChunkIterator ChunkEnd() { return ChunkIterator::end(); }
+ /// Returns a const iterator pointing to the first ``Chunk`` in this
+ /// ``MultiBuf``.
+ constexpr ConstChunkIterator ConstChunkBegin() {
+ return ConstChunkIterator(first_);
+ }
+ /// Returns a const iterator pointing to the end of the ``Chunk``s in this
+ /// ``MultiBuf``.
+ constexpr ConstChunkIterator ConstChunkEnd() {
+ return ConstChunkIterator::end();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //--------------------- Iterator details ------------------------//
+ ///////////////////////////////////////////////////////////////////
+
+ using element_type = std::byte;
+ using value_type = std::byte;
+ using pointer = std::byte*;
+ using const_pointer = const std::byte*;
+ using reference = std::byte&;
+ using const_reference = const std::byte&;
+ using difference_type = std::ptrdiff_t;
+ using size_type = std::size_t;
+
+ /// An ``std::forward_iterator`` over the bytes of a ``MultiBuf``.
+ class iterator {
+ public:
+ using value_type = std::byte;
+ using difference_type = std::ptrdiff_t;
+ using reference = std::byte&;
+ using pointer = std::byte*;
+ using iterator_category = std::forward_iterator_tag;
+
+ constexpr iterator() = default;
+ iterator(Chunk* chunk, size_t byte_index)
+ : chunk_(chunk), byte_index_(byte_index) {
+ AdvanceToData();
+ }
+ static iterator end() { return iterator(nullptr, 0); }
+
+ reference operator*() const { return (*chunk_)[byte_index_]; }
+ pointer operator->() const { return &(*chunk_)[byte_index_]; }
+
+ iterator& operator++();
+ iterator operator++(int) {
+ iterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+ constexpr bool operator==(const iterator& other) const {
+ return chunk_ == other.chunk_ && byte_index_ == other.byte_index_;
+ }
+ constexpr bool operator!=(const iterator& other) const {
+ return chunk_ != other.chunk_ || byte_index_ != other.byte_index_;
+ }
+
+ /// Returns the current ``Chunk`` pointed to by this `iterator`.
+ constexpr Chunk* chunk() const { return chunk_; }
+
+ /// Returns the index of the byte pointed to by this `iterator` within the
+ /// current ``Chunk``.
+ constexpr size_t byte_index() const { return byte_index_; }
+
+ private:
+ void AdvanceToData();
+
+ Chunk* chunk_ = nullptr;
+ size_t byte_index_ = 0;
+ };
+
+ /// A const ``std::forward_iterator`` over the bytes of a ``MultiBuf``.
+ class const_iterator {
+ public:
+ using value_type = std::byte;
+ using difference_type = std::ptrdiff_t;
+ using reference = const std::byte&;
+ using pointer = const std::byte*;
+ using iterator_category = std::forward_iterator_tag;
+
+ constexpr const_iterator() = default;
+ const_iterator(const Chunk* chunk, size_t byte_index)
+ : chunk_(chunk), byte_index_(byte_index) {
+ AdvanceToData();
+ }
+ static const_iterator end() { return const_iterator(nullptr, 0); }
+
+ reference operator*() const { return (*chunk_)[byte_index_]; }
+ pointer operator->() const { return &(*chunk_)[byte_index_]; }
+
+ const_iterator& operator++();
+ const_iterator operator++(int) {
+ const_iterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ constexpr bool operator==(const const_iterator& other) const {
+ return chunk_ == other.chunk_ && byte_index_ == other.byte_index_;
+ }
+
+ constexpr bool operator!=(const const_iterator& other) const {
+ return chunk_ != other.chunk_ || byte_index_ != other.byte_index_;
+ }
+
+ /// Returns the current ``Chunk`` pointed to by this `iterator`.
+ constexpr const Chunk* chunk() const { return chunk_; }
+
+ /// Returns the index of the byte pointed to by this `iterator` within the
+ /// current ``Chunk``.
+ constexpr size_t byte_index() const { return byte_index_; }
+
+ private:
+ void AdvanceToData();
+
+ const Chunk* chunk_ = nullptr;
+ size_t byte_index_ = 0;
+ };
+
+ /// An iterable containing the ``Chunk`` s of a ``MultiBuf``.
+ class ChunkIterable {
+ public:
+ using element_type = Chunk;
+ using value_type = Chunk;
+ using pointer = Chunk*;
+ using reference = Chunk&;
+ using const_pointer = const Chunk*;
+ using difference_type = std::ptrdiff_t;
+ using const_reference = const Chunk&;
+ using size_type = std::size_t;
+
+ constexpr ChunkIterator begin() { return ChunkIterator(first_); }
+ constexpr ConstChunkIterator begin() const { return cbegin(); }
+ constexpr ConstChunkIterator cbegin() const {
+ return ConstChunkIterator(first_);
+ }
+ constexpr ChunkIterator end() { return ChunkIterator::end(); }
+ constexpr ConstChunkIterator end() const { return cend(); }
+ constexpr ConstChunkIterator cend() const {
+ return ConstChunkIterator::end();
+ }
+
+ /// Returns the number of ``Chunk``s in this iterable.
+ size_t size() const;
+
+ private:
+ Chunk* first_ = nullptr;
+ constexpr ChunkIterable(Chunk* chunk) : first_(chunk) {}
+ friend class MultiBuf;
+ };
+
+ /// A ``std::forward_iterator`` over the ``Chunk``s of a ``MultiBuf``.
+ class ChunkIterator {
+ public:
+ using value_type = Chunk;
+ using difference_type = std::ptrdiff_t;
+ using reference = Chunk&;
+ using pointer = Chunk*;
+ using iterator_category = std::forward_iterator_tag;
+
+ constexpr ChunkIterator() = default;
+
+ constexpr reference operator*() const { return *chunk_; }
+ constexpr pointer operator->() const { return chunk_; }
+
+ constexpr ChunkIterator& operator++() {
+ chunk_ = chunk_->next_in_buf_;
+ return *this;
+ }
+
+ constexpr ChunkIterator operator++(int) {
+ ChunkIterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ constexpr bool operator==(const ChunkIterator& other) const {
+ return chunk_ == other.chunk_;
+ }
+
+ constexpr bool operator!=(const ChunkIterator& other) const {
+ return chunk_ != other.chunk_;
+ }
+
+ constexpr Chunk* chunk() const { return chunk_; }
+
+ private:
+ constexpr ChunkIterator(Chunk* chunk) : chunk_(chunk) {}
+ static constexpr ChunkIterator end() { return ChunkIterator(nullptr); }
+ Chunk* chunk_ = nullptr;
+ friend class MultiBuf;
+ friend class ChunkIterable;
+ };
+
+ /// A const ``std::forward_iterator`` over the ``Chunk``s of a ``MultiBuf``.
+ class ConstChunkIterator {
+ public:
+ using value_type = const Chunk;
+ using difference_type = std::ptrdiff_t;
+ using reference = const Chunk&;
+ using pointer = const Chunk*;
+ using iterator_category = std::forward_iterator_tag;
+
+ constexpr ConstChunkIterator() = default;
+
+ constexpr reference operator*() const { return *chunk_; }
+ constexpr pointer operator->() const { return chunk_; }
+
+ constexpr ConstChunkIterator& operator++() {
+ chunk_ = chunk_->next_in_buf_;
+ return *this;
+ }
+
+ constexpr ConstChunkIterator operator++(int) {
+ ConstChunkIterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ constexpr bool operator==(const ConstChunkIterator& other) const {
+ return chunk_ == other.chunk_;
+ }
+
+ constexpr bool operator!=(const ConstChunkIterator& other) const {
+ return chunk_ != other.chunk_;
+ }
+
+ constexpr const Chunk* chunk() const { return chunk_; }
+
+ private:
+ constexpr ConstChunkIterator(const Chunk* chunk) : chunk_(chunk) {}
+ static constexpr ConstChunkIterator end() {
+ return ConstChunkIterator(nullptr);
+ }
+ const Chunk* chunk_ = nullptr;
+ friend class MultiBuf;
+ friend class ChunkIterable;
+ };
+
+ private:
+ /// Returns the ``Chunk`` preceding ``chunk`` in this ``MultiBuf``.
+ ///
+ /// Requires that this ``MultiBuf`` is not empty, and that ``chunk``
+ /// is either in ``MultiBuf`` or is ``nullptr``, in which case the last
+ /// ``Chunk`` in ``MultiBuf`` will be returned.
+ ///
+ /// This operation is ``O(Chunks().size())``.
+ Chunk* Previous(Chunk* chunk) const;
+
+ Chunk* first_;
+};
+
+class MultiBufAllocator {};
+
+} // namespace pw::multibuf
diff --git a/pw_package/py/pw_package/packages/stm32cube.py b/pw_package/py/pw_package/packages/stm32cube.py
index a2e704e04..460029477 100644
--- a/pw_package/py/pw_package/packages/stm32cube.py
+++ b/pw_package/py/pw_package/packages/stm32cube.py
@@ -110,7 +110,10 @@ class Stm32Cube(pw_package.package_manager.Package):
def __init__(self, family, tags, *args, **kwargs):
super().__init__(*args, name=f'stm32cube_{family}', **kwargs)
- st_github_url = 'https://github.com/STMicroelectronics'
+ st_github_url = (
+ 'https://pigweed.googlesource.com/'
+ 'third_party/github/STMicroelectronics'
+ )
self._hal_driver = pw_package.git_repo.GitRepo(
name='hal_driver',
diff --git a/pw_package/py/pw_package/packages/zephyr.py b/pw_package/py/pw_package/packages/zephyr.py
index 1122474c4..89646d711 100644
--- a/pw_package/py/pw_package/packages/zephyr.py
+++ b/pw_package/py/pw_package/packages/zephyr.py
@@ -36,10 +36,10 @@ class Zephyr(pw_package.git_repo.GitRepo):
*args,
name='zephyr',
url=(
- 'https://pigweed.git.corp.google.com/third_party/'
+ 'https://pigweed.googlesource.com/third_party/'
'github/zephyrproject-rtos/zephyr'
),
- commit='356c8cbe63ae01b3ab438382639d25bb418a0213', # v3.4 release
+ commit='a6eef0ba3755f2530c5ce93524e5ac4f5be30194', # v3.5 release
**kwargs,
)
diff --git a/pw_perf_test/BUILD.bazel b/pw_perf_test/BUILD.bazel
index 7b0a8fa11..75998ad69 100644
--- a/pw_perf_test/BUILD.bazel
+++ b/pw_perf_test/BUILD.bazel
@@ -29,47 +29,50 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"])
pw_cc_library(
- name = "duration_unit",
- hdrs = [
- "public/pw_perf_test/internal/duration_unit.h",
+ name = "pw_perf_test",
+ srcs = [
+ "framework.cc",
+ "perf_test.cc",
+ "test_info.cc",
],
- includes = ["public"],
- visibility = ["//visibility:private"],
-)
-
-pw_cc_facade(
- name = "timer_interface_facade",
hdrs = [
- "public/pw_perf_test/internal/timer.h",
+ "public/pw_perf_test/internal/framework.h",
+ "public/pw_perf_test/internal/test_info.h",
+ "public/pw_perf_test/perf_test.h",
],
includes = ["public"],
- visibility = ["//visibility:private"],
deps = [
- ":duration_unit",
+ ":event_handler",
+ ":state",
+ ":timer",
+ "//pw_preprocessor",
],
)
pw_cc_library(
- name = "timer",
+ name = "state",
+ srcs = [
+ "state.cc",
+ ],
hdrs = [
- "public/pw_perf_test/internal/timer.h",
+ "public/pw_perf_test/state.h",
],
includes = ["public"],
deps = [
- ":duration_unit",
- "@pigweed//targets:pw_perf_test_timer_backend",
+ ":event_handler",
+ ":timer",
+ "//pw_assert",
+ "//pw_log",
],
)
-pw_cc_library(
- name = "timer_multiplexer",
- visibility = ["@pigweed//targets:__pkg__"],
- deps = select({
- "//conditions:default": [":chrono_timer"],
- }),
+pw_cc_test(
+ name = "state_test",
+ srcs = ["state_test.cc"],
+ deps = [":pw_perf_test"],
)
-# EventHandler Configuraitions
+# Event handlers
pw_cc_library(
name = "event_handler",
@@ -79,19 +82,6 @@ pw_cc_library(
)
pw_cc_library(
- name = "pw_perf_test",
- srcs = ["perf_test.cc"],
- hdrs = ["public/pw_perf_test/perf_test.h"],
- includes = ["public"],
- deps = [
- ":event_handler",
- ":timer",
- "//pw_assert",
- "//pw_log",
- ],
-)
-
-pw_cc_library(
name = "google_test_style_event_strings",
hdrs = ["public/pw_perf_test/googletest_style_event_handler.h"],
)
@@ -119,52 +109,61 @@ pw_cc_library(
],
)
-pw_cc_perf_test(
- name = "generic_test",
- srcs = ["performance_test_generic.cc"],
-)
+# Timer facade
-# Test Declarations
+pw_cc_library(
+ name = "duration_unit",
+ hdrs = [
+ "public/pw_perf_test/internal/duration_unit.h",
+ ],
+ includes = ["public"],
+ visibility = ["//visibility:private"],
+)
-pw_cc_test(
- name = "perf_test_test",
- srcs = ["perf_test_test.cc"],
- deps = [":pw_perf_test"],
+pw_cc_facade(
+ name = "timer_interface_facade",
+ hdrs = [
+ "public/pw_perf_test/internal/timer.h",
+ ],
+ includes = ["public"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":duration_unit",
+ ],
)
-pw_cc_test(
- name = "timer_test",
- srcs = ["timer_test.cc"],
+pw_cc_library(
+ name = "timer",
+ hdrs = [
+ "public/pw_perf_test/internal/timer.h",
+ ],
+ includes = ["public"],
deps = [
- ":timer",
- "//pw_chrono:system_clock",
- "//pw_thread:sleep",
+ ":duration_unit",
+ "@pigweed//targets:pw_perf_test_timer_backend",
],
)
+pw_cc_library(
+ name = "timer_multiplexer",
+ visibility = ["@pigweed//targets:__pkg__"],
+ deps = select({
+ "//conditions:default": [":chrono_timer"],
+ }),
+)
+
pw_cc_test(
- name = "chrono_timer_test",
- srcs = ["chrono_test.cc"],
+ name = "timer_test",
+ srcs = ["timer_test.cc"],
deps = [
- ":chrono_timer",
+ ":timer",
"//pw_chrono:system_clock",
"//pw_thread:sleep",
],
)
-pw_cc_test(
- name = "state_test",
- srcs = ["state_test.cc"],
- deps = [":pw_perf_test"],
-)
-
-# Bazel does not yet support building docs.
-filegroup(
- name = "docs",
- srcs = ["docs.rst"],
-)
+# Chrono timer facade implementation
-# Chrono Implementation
pw_cc_library(
name = "chrono_timer",
hdrs = [
@@ -183,7 +182,18 @@ pw_cc_library(
],
)
-# ARM Cortex Implementation
+pw_cc_test(
+ name = "chrono_timer_test",
+ srcs = ["chrono_test.cc"],
+ deps = [
+ ":chrono_timer",
+ "//pw_chrono:system_clock",
+ "//pw_thread:sleep",
+ ],
+)
+
+# ARM Cortex timer facade implementation
+
pw_cc_library(
name = "arm_cortex_timer",
hdrs = [
@@ -200,3 +210,16 @@ pw_cc_library(
":timer_interface_facade",
],
)
+
+# Module-level targets
+
+pw_cc_perf_test(
+ name = "example_perf_test",
+ srcs = ["examples/example_perf_test.cc"],
+)
+
+# Bazel does not yet support building docs.
+filegroup(
+ name = "docs",
+ srcs = ["docs.rst"],
+)
diff --git a/pw_perf_test/BUILD.gn b/pw_perf_test/BUILD.gn
index 81ee5d930..8772591c4 100644
--- a/pw_perf_test/BUILD.gn
+++ b/pw_perf_test/BUILD.gn
@@ -26,63 +26,51 @@ config("public_include_path") {
visibility = [ ":*" ]
}
-config("arm_config") {
- include_dirs = [ "arm_cortex_cyccnt_public_overrides" ]
- visibility = [ ":*" ]
-}
-
-config("chrono_config") {
- include_dirs = [ "chrono_public_overrides" ]
- visibility = [ ":*" ]
-}
-
-pw_test_group("tests") {
- tests = [
- ":perf_test_test",
- ":timer_facade_test",
- ":chrono_timer_test",
- ":state_test",
+pw_source_set("pw_perf_test") {
+ public_configs = [ ":public_include_path" ]
+ public = [
+ "public/pw_perf_test/internal/framework.h",
+ "public/pw_perf_test/internal/test_info.h",
+ "public/pw_perf_test/perf_test.h",
+ ]
+ public_deps = [
+ ":event_handler",
+ ":state",
+ ":timer_interface",
+ dir_pw_preprocessor,
+ ]
+ sources = [
+ "framework.cc",
+ "perf_test.cc",
+ "test_info.cc",
]
}
-group("perf_test_tests_test") {
- deps = [ ":generic_perf_test" ]
-}
-
-# Timing interface variables
-
-pw_source_set("duration_unit") {
- public = [ "public/pw_perf_test/internal/duration_unit.h" ]
+pw_source_set("state") {
public_configs = [ ":public_include_path" ]
- visibility = [ ":*" ]
+ public = [ "public/pw_perf_test/state.h" ]
+ public_deps = [
+ ":event_handler",
+ ":timer_interface",
+ dir_pw_assert,
+ ]
+ deps = [ dir_pw_log ]
+ sources = [ "state.cc" ]
}
-pw_facade("timer_interface") {
- backend = pw_perf_test_TIMER_INTERFACE_BACKEND
- public = [ "public/pw_perf_test/internal/timer.h" ]
- public_deps = [ ":duration_unit" ]
- visibility = [ ":*" ]
+pw_test("state_test") {
+ enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
+ sources = [ "state_test.cc" ]
+ deps = [ ":state" ]
}
-# Event Handler Configurations
+# Event handlers
pw_source_set("event_handler") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_perf_test/event_handler.h" ]
}
-pw_source_set("pw_perf_test") {
- public_configs = [ ":public_include_path" ]
- public = [ "public/pw_perf_test/perf_test.h" ]
- public_deps = [
- ":event_handler",
- ":timer_interface",
- dir_pw_assert,
- ]
- deps = [ dir_pw_log ]
- sources = [ "perf_test.cc" ]
-}
-
pw_source_set("googletest_style_event_handler") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_perf_test/googletest_style_event_handler.h" ]
@@ -105,48 +93,34 @@ pw_source_set("log_perf_handler_main") {
sources = [ "log_perf_handler_main.cc" ]
}
-pw_perf_test("generic_perf_test") {
- enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
- sources = [ "performance_test_generic.cc" ]
-}
+# Timer facade
-# Declaring module tests
+pw_source_set("duration_unit") {
+ public = [ "public/pw_perf_test/internal/duration_unit.h" ]
+ public_configs = [ ":public_include_path" ]
+ visibility = [ ":*" ]
+}
-pw_test("perf_test_test") {
- enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
- sources = [ "perf_test_test.cc" ]
- deps = [ ":pw_perf_test" ]
+pw_facade("timer_interface") {
+ backend = pw_perf_test_TIMER_INTERFACE_BACKEND
+ public = [ "public/pw_perf_test/internal/timer.h" ]
+ public_deps = [ ":duration_unit" ]
+ visibility = [ ":*" ]
}
pw_test("timer_facade_test") {
enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
sources = [ "timer_test.cc" ]
- public_deps = [ ":timer_interface" ]
-}
-
-pw_test("chrono_timer_test") {
- enable_if = pw_chrono_SYSTEM_TIMER_BACKEND != ""
- sources = [ "chrono_test.cc" ]
- public_deps = [
- ":chrono_timer",
- "$dir_pw_chrono:system_timer",
- "$dir_pw_thread:sleep",
- ]
-}
-
-pw_test("state_test") {
- enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
- sources = [ "state_test.cc" ]
- public_deps = [ ":pw_perf_test" ]
+ deps = [ ":timer_interface" ]
}
-# Documentation declaration
+# Chrono timer facade implementation
-pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+config("chrono_config") {
+ include_dirs = [ "chrono_public_overrides" ]
+ visibility = [ ":*" ]
}
-# Chrono Implementation
pw_source_set("pw_perf_test_chrono") {
public_configs = [ ":chrono_config" ]
public = [ "chrono_public_overrides/pw_perf_test_timer_backend/timer.h" ]
@@ -166,7 +140,22 @@ pw_source_set("chrono_timer") {
visibility = [ ":*" ]
}
-# ARM Cortex Implementation
+pw_test("chrono_timer_test") {
+ enable_if = pw_chrono_SYSTEM_TIMER_BACKEND != ""
+ sources = [ "chrono_test.cc" ]
+ deps = [
+ ":chrono_timer",
+ "$dir_pw_chrono:system_timer",
+ "$dir_pw_thread:sleep",
+ ]
+}
+
+# ARM Cortex timer facade implementation
+
+config("arm_config") {
+ include_dirs = [ "arm_cortex_cyccnt_public_overrides" ]
+ visibility = [ ":*" ]
+}
pw_source_set("arm_cortex_timer") {
public_configs = [
@@ -185,3 +174,26 @@ pw_source_set("pw_perf_test_arm_cortex") {
]
public_deps = [ ":arm_cortex_timer" ]
}
+
+# Module-level targets
+
+pw_perf_test("example_perf_test") {
+ enable_if = pw_perf_test_TIMER_INTERFACE_BACKEND != ""
+ sources = [ "examples/example_perf_test.cc" ]
+}
+
+group("examples") {
+ deps = [ ":example_perf_test" ]
+}
+
+pw_test_group("tests") {
+ tests = [
+ ":chrono_timer_test",
+ ":state_test",
+ ":timer_facade_test",
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+}
diff --git a/pw_perf_test/CMakeLists.txt b/pw_perf_test/CMakeLists.txt
index 80f4487a2..3f5c5c782 100644
--- a/pw_perf_test/CMakeLists.txt
+++ b/pw_perf_test/CMakeLists.txt
@@ -16,44 +16,58 @@ include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
include($ENV{PW_ROOT}/pw_perf_test/backend.cmake)
include($ENV{PW_ROOT}/pw_protobuf_compiler/proto.cmake)
-pw_add_library(pw_perf_test.duration_unit INTERFACE
- HEADERS
- public/pw_perf_test/internal/duration_unit.h
+pw_add_library(pw_perf_test STATIC
PUBLIC_INCLUDES
public
-)
-
-pw_add_facade(pw_perf_test.timer INTERFACE
- BACKEND
- pw_perf_test.TIMER_INTERFACE_BACKEND
HEADERS
- public/pw_perf_test/internal/timer.h
- PUBLIC_INCLUDES
- public
+ public/pw_perf_test/internal/framework.h
+ public/pw_perf_test/internal/test_info.h
+ public/pw_perf_test/perf_test.h
PUBLIC_DEPS
- pw_perf_test.duration_unit
-)
-
-pw_add_library(pw_perf_test.event_handler INTERFACE
- HEADERS
- public/pw_perf_test/event_handler.h
- PUBLIC_INCLUDES
- public
+ pw_perf_test.event_handler
+ pw_perf_test.state
+ pw_perf_test.timer
+ SOURCES
+ framework.cc
+ perf_test.cc
+ test_info.cc
)
-pw_add_library(pw_perf_test STATIC
+pw_add_library(pw_perf_test.state STATIC
PUBLIC_INCLUDES
public
HEADERS
- public/pw_perf_test/perf_test.h
+ public/pw_perf_test/state.h
PUBLIC_DEPS
pw_perf_test.timer
pw_perf_test.event_handler
+ pw_assert
PRIVATE_DEPS
pw_log
- pw_assert
SOURCES
- perf_test.cc
+ state.cc
+)
+
+if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}" STREQUAL "")
+ pw_add_test(pw_perf_test.state_test
+ SOURCES
+ state_test.cc
+ PRIVATE_DEPS
+ pw_assert.assert
+ pw_perf_test
+ GROUPS
+ modules
+ pw_perf_test
+ )
+endif()
+
+# Event handlers
+
+pw_add_library(pw_perf_test.event_handler INTERFACE
+ HEADERS
+ public/pw_perf_test/event_handler.h
+ PUBLIC_INCLUDES
+ public
)
pw_add_library(pw_perf_test.googletest_style_event_handler INTERFACE
@@ -85,15 +99,23 @@ pw_add_library(pw_perf_test.log_perf_handler_main STATIC
log_perf_handler_main.cc
)
-pw_add_library(pw_perf_test.chrono_timer INTERFACE
+# Timer facade
+
+pw_add_library(pw_perf_test.duration_unit INTERFACE
HEADERS
- chrono_public_overrides/pw_perf_test_timer_backend/timer.h
- public/pw_perf_test/internal/chrono_timer_interface.h
+ public/pw_perf_test/internal/duration_unit.h
+ PUBLIC_INCLUDES
+ public
+)
+
+pw_add_facade(pw_perf_test.timer INTERFACE
+ BACKEND
+ pw_perf_test.TIMER_INTERFACE_BACKEND
+ HEADERS
+ public/pw_perf_test/internal/timer.h
PUBLIC_INCLUDES
- chrono_public_overrides
public
PUBLIC_DEPS
- pw_chrono.system_clock
pw_perf_test.duration_unit
)
@@ -111,6 +133,20 @@ if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}" STREQUAL "")
)
endif()
+# Chrono timer facade implementation
+
+pw_add_library(pw_perf_test.chrono_timer INTERFACE
+ HEADERS
+ chrono_public_overrides/pw_perf_test_timer_backend/timer.h
+ public/pw_perf_test/internal/chrono_timer_interface.h
+ PUBLIC_INCLUDES
+ chrono_public_overrides
+ public
+ PUBLIC_DEPS
+ pw_chrono.system_clock
+ pw_perf_test.duration_unit
+)
+
if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}"
STREQUAL "pw_chrono.SYSTEM_CLOCK_BACKEND.NO_BACKEND_SET")
pw_add_test(pw_perf_test.chrono_timer_test
@@ -125,29 +161,3 @@ if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}"
pw_perf_test
)
endif()
-
-if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}" STREQUAL "")
- pw_add_test(pw_perf_test.state_test
- SOURCES
- state_test.cc
- PRIVATE_DEPS
- pw_assert.assert
- pw_perf_test
- GROUPS
- modules
- pw_perf_test
- )
-endif()
-
-if(NOT "${pw_perf_test.TIMER_INTERFACE_BACKEND}" STREQUAL "")
- pw_add_test(pw_perf_test.perf_test_test
- SOURCES
- perf_test_test.cc
- PRIVATE_DEPS
- pw_assert.assert
- pw_perf_test
- GROUPS
- modules
- pw_perf_test
- )
-endif()
diff --git a/pw_perf_test/performance_test_generic.cc b/pw_perf_test/examples/example_perf_test.cc
index b1318b062..b1318b062 100644
--- a/pw_perf_test/performance_test_generic.cc
+++ b/pw_perf_test/examples/example_perf_test.cc
diff --git a/pw_perf_test/framework.cc b/pw_perf_test/framework.cc
new file mode 100644
index 000000000..ccfc9cc3f
--- /dev/null
+++ b/pw_perf_test/framework.cc
@@ -0,0 +1,53 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_perf_test/internal/framework.h"
+
+#include "pw_perf_test/internal/test_info.h"
+#include "pw_perf_test/internal/timer.h"
+
+namespace pw::perf_test::internal {
+
+Framework Framework::framework_;
+
+int Framework::RunAllTests() {
+ if (!internal::TimerPrepare()) {
+ return false;
+ }
+
+ event_handler_->RunAllTestsStart(run_info_);
+
+ for (const TestInfo* test = tests_; test != nullptr; test = test->next()) {
+ State test_state = internal::CreateState(
+ kDefaultIterations, *event_handler_, test->test_name());
+ test->Run(test_state);
+ }
+ internal::TimerCleanup();
+ event_handler_->RunAllTestsEnd();
+ return true;
+}
+
+void Framework::RegisterTest(TestInfo& new_test) {
+ ++run_info_.total_tests;
+ if (tests_ == nullptr) {
+ tests_ = &new_test;
+ return;
+ }
+ TestInfo* info = tests_;
+ for (; info->next() != nullptr; info = info->next()) {
+ }
+ info->SetNext(&new_test);
+}
+
+} // namespace pw::perf_test::internal
diff --git a/pw_perf_test/perf_test.cc b/pw_perf_test/perf_test.cc
index 0e2f8d1ee..3cbe0e2f1 100644
--- a/pw_perf_test/perf_test.cc
+++ b/pw_perf_test/perf_test.cc
@@ -16,89 +16,11 @@
#include "pw_perf_test/perf_test.h"
-#include <cstdint>
-
-#include "pw_log/log.h"
#include "pw_perf_test/event_handler.h"
+#include "pw_perf_test/internal/framework.h"
#include "pw_perf_test/internal/timer.h"
namespace pw::perf_test {
-namespace internal {
-
-Framework Framework::framework_;
-
-int Framework::RunAllTests() {
- if (!internal::TimerPrepare()) {
- return false;
- }
-
- event_handler_->RunAllTestsStart(run_info_);
-
- for (const TestInfo* test = tests_; test != nullptr; test = test->next()) {
- State test_state =
- CreateState(kDefaultIterations, *event_handler_, test->test_name());
- test->Run(test_state);
- }
- internal::TimerCleanup();
- event_handler_->RunAllTestsEnd();
- return true;
-}
-
-void Framework::RegisterTest(TestInfo& new_test) {
- ++run_info_.total_tests;
- if (tests_ == nullptr) {
- tests_ = &new_test;
- return;
- }
- TestInfo* info = tests_;
- for (; info->next() != nullptr; info = info->next()) {
- }
- info->SetNext(&new_test);
-}
-
-State CreateState(int durations,
- EventHandler& event_handler,
- const char* test_name) {
- return State(durations, event_handler, test_name);
-}
-} // namespace internal
-
-bool State::KeepRunning() {
- internal::Timestamp iteration_end = internal::GetCurrentTimestamp();
- if (current_iteration_ == -1) {
- ++current_iteration_;
- event_handler_->TestCaseStart(test_info);
- iteration_start_ = internal::GetCurrentTimestamp();
- return true;
- }
- int64_t duration = internal::GetDuration(iteration_start_, iteration_end);
- if (duration > max_) {
- max_ = duration;
- }
- if (duration < min_) {
- min_ = duration;
- }
- total_duration_ += duration;
- ++current_iteration_;
- PW_LOG_DEBUG("Iteration number: %d - Duration: %ld",
- current_iteration_,
- static_cast<long>(duration));
- event_handler_->TestCaseIteration({current_iteration_, duration});
- if (current_iteration_ == test_iterations_) {
- PW_LOG_DEBUG("Total Duration: %ld Total Iterations: %d",
- static_cast<long>(total_duration_),
- test_iterations_);
- mean_ = total_duration_ / test_iterations_;
- PW_LOG_DEBUG("Mean: %ld: ", static_cast<long>(mean_));
- PW_LOG_DEBUG("Minimum: %ld", static_cast<long>(min_));
- PW_LOG_DEBUG("Maxmimum: %ld", static_cast<long>(max_));
- event_handler_->TestCaseEnd(test_info,
- Results{mean_, max_, min_, test_iterations_});
- return false;
- }
- iteration_start_ = internal::GetCurrentTimestamp();
- return true;
-}
void RunAllTests(EventHandler& handler) {
internal::Framework::Get().RegisterEventHandler(handler);
diff --git a/pw_perf_test/perf_test_test.cc b/pw_perf_test/perf_test_test.cc
deleted file mode 100644
index 2cdea93e1..000000000
--- a/pw_perf_test/perf_test_test.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2022 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#include "pw_perf_test/perf_test.h"
-
-#include "gtest/gtest.h"
-
-void TestingFunction(pw::perf_test::State& state) {
- while (state.KeepRunning()) {
- // Intentionally empty.
- }
-}
-
-// This function is intentionally left blank
-void SimpleFunction() {}
-
-void SimpleFunctionWithArgs(int, bool) {}
-
-namespace pw::perf_test {
-namespace {
-
-PW_PERF_TEST(TestingComponentRegistration, TestingFunction);
-
-PW_PERF_TEST_SIMPLE(TestingSimpleRegistration, SimpleFunction);
-
-PW_PERF_TEST_SIMPLE(TestingSimpleRegistrationArgs,
- SimpleFunctionWithArgs,
- 123,
- false);
-
-} // namespace
-} // namespace pw::perf_test
diff --git a/pw_perf_test/public/pw_perf_test/internal/framework.h b/pw_perf_test/public/pw_perf_test/internal/framework.h
new file mode 100644
index 000000000..bb3ebab22
--- /dev/null
+++ b/pw_perf_test/public/pw_perf_test/internal/framework.h
@@ -0,0 +1,57 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_perf_test/event_handler.h"
+
+namespace pw::perf_test::internal {
+
+// Forward declaration.
+class TestInfo;
+
+/// Singleton that manages and runs performance tests.
+///
+/// This class mimics pw::unit_test::Framework.
+class Framework {
+ public:
+ constexpr Framework()
+ : event_handler_(nullptr),
+ tests_(nullptr),
+ run_info_{.total_tests = 0, .default_iterations = kDefaultIterations} {}
+
+ static Framework& Get() { return framework_; }
+
+ void RegisterEventHandler(EventHandler& event_handler) {
+ event_handler_ = &event_handler;
+ }
+
+ void RegisterTest(TestInfo&);
+
+ int RunAllTests();
+
+ private:
+ static constexpr int kDefaultIterations = 10;
+
+ EventHandler* event_handler_;
+
+ // Pointer to the list of tests
+ TestInfo* tests_;
+
+ TestRunInfo run_info_;
+
+ // Singleton
+ static Framework framework_;
+};
+
+} // namespace pw::perf_test::internal
diff --git a/pw_perf_test/public/pw_perf_test/internal/test_info.h b/pw_perf_test/public/pw_perf_test/internal/test_info.h
new file mode 100644
index 000000000..fd776e9d4
--- /dev/null
+++ b/pw_perf_test/public/pw_perf_test/internal/test_info.h
@@ -0,0 +1,50 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_perf_test/state.h"
+
+namespace pw::perf_test::internal {
+
+/// Represents a single test case.
+///
+/// Each instance includes a pointer to a function which constructs and runs the
+/// test class. These are statically allocated instead of the test classes, as
+/// test classes can be very large.
+///
+/// This class mimics pw::unit_test::TestInfo.
+class TestInfo {
+ public:
+ TestInfo(const char* test_name, void (*function_body)(State&));
+
+ // Returns the next registered test
+ TestInfo* next() const { return next_; }
+
+ void SetNext(TestInfo* next) { next_ = next; }
+
+ void Run(State& state) const { run_(state); }
+
+ const char* test_name() const { return test_name_; }
+
+ private:
+ // Function pointer to the code that will be measured
+ void (*run_)(State&);
+
+ // Intrusively linked list, this acts as a pointer to the next test
+ TestInfo* next_ = nullptr;
+
+ const char* test_name_;
+};
+
+} // namespace pw::perf_test::internal
diff --git a/pw_perf_test/public/pw_perf_test/perf_test.h b/pw_perf_test/public/pw_perf_test/perf_test.h
index e092087f9..90efb0cb0 100644
--- a/pw_perf_test/public/pw_perf_test/perf_test.h
+++ b/pw_perf_test/public/pw_perf_test/perf_test.h
@@ -13,13 +13,9 @@
// the License.
#pragma once
-#include <cstdint>
-#include <limits>
-
-#include "pw_assert/assert.h"
#include "pw_perf_test/event_handler.h"
-#include "pw_perf_test/internal/duration_unit.h"
-#include "pw_perf_test/internal/timer.h"
+#include "pw_perf_test/internal/test_info.h"
+#include "pw_perf_test/state.h"
#include "pw_preprocessor/arguments.h"
#define PW_PERF_TEST(name, function, ...) \
@@ -42,137 +38,6 @@
namespace pw::perf_test {
-class State;
-
-namespace internal {
-
-class TestInfo;
-
-// Allows access to the private State object constructor
-State CreateState(int durations,
- EventHandler& event_handler,
- const char* test_name);
-
-class Framework {
- public:
- constexpr Framework()
- : event_handler_(nullptr),
- tests_(nullptr),
- run_info_{.total_tests = 0, .default_iterations = kDefaultIterations} {}
-
- static Framework& Get() { return framework_; }
-
- void RegisterEventHandler(EventHandler& event_handler) {
- event_handler_ = &event_handler;
- }
-
- void RegisterTest(TestInfo&);
-
- int RunAllTests();
-
- private:
- static constexpr int kDefaultIterations = 10;
-
- EventHandler* event_handler_;
-
- // Pointer to the list of tests
- TestInfo* tests_;
-
- TestRunInfo run_info_;
-
- static Framework framework_;
-};
-
-class TestInfo {
- public:
- TestInfo(const char* test_name, void (*function_body)(State&))
- : run_(function_body), test_name_(test_name) {
- // Once a TestInfo object is created by the macro, this adds itself to the
- // list of registered tests
- Framework::Get().RegisterTest(*this);
- }
-
- // Returns the next registered test
- TestInfo* next() const { return next_; }
-
- void SetNext(TestInfo* next) { next_ = next; }
-
- void Run(State& state) const { run_(state); }
-
- const char* test_name() const { return test_name_; }
-
- private:
- // Function pointer to the code that will be measured
- void (*run_)(State&);
-
- // Intrusively linked list, this acts as a pointer to the next test
- TestInfo* next_ = nullptr;
-
- const char* test_name_;
-};
-
-} // namespace internal
-
-class State {
- public:
- // KeepRunning() should be called in a while loop. Responsible for managing
- // iterations and timestamps.
- bool KeepRunning();
-
- private:
- // Allows the framework to create state objects and unit tests for the state
- // class
- friend State internal::CreateState(int durations,
- EventHandler& event_handler,
- const char* test_name);
-
- // Privated constructor to prevent unauthorized instances of the state class.
- constexpr State(int iterations,
- EventHandler& event_handler,
- const char* test_name)
- : mean_(-1),
- test_iterations_(iterations),
- total_duration_(0),
- min_(std::numeric_limits<int64_t>::max()),
- max_(std::numeric_limits<int64_t>::min()),
- iteration_start_(),
- current_iteration_(-1),
- event_handler_(&event_handler),
- test_info{.name = test_name} {
- PW_ASSERT(test_iterations_ > 0);
- }
- // Set public after deciding how exactly to set user-defined iterations
- void SetIterations(int iterations) {
- PW_ASSERT(current_iteration_ == -1);
- test_iterations_ = iterations;
- PW_ASSERT(test_iterations_ > 0);
- }
-
- int64_t mean_;
-
- // Stores the total number of iterations wanted
- int test_iterations_;
-
- // Stores the total duration of the tests.
- int64_t total_duration_;
-
- // Smallest value of the iterations
- int64_t min_;
-
- // Largest value of the iterations
- int64_t max_;
-
- // Time at the start of the iteration
- internal::Timestamp iteration_start_;
-
- // The current iteration
- int current_iteration_;
-
- EventHandler* event_handler_;
-
- TestCase test_info;
-};
-
-void RunAllTests(pw::perf_test::EventHandler& handler);
+void RunAllTests(EventHandler& handler);
} // namespace pw::perf_test
diff --git a/pw_perf_test/public/pw_perf_test/state.h b/pw_perf_test/public/pw_perf_test/state.h
new file mode 100644
index 000000000..cd0e04f0c
--- /dev/null
+++ b/pw_perf_test/public/pw_perf_test/state.h
@@ -0,0 +1,87 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <cstdint>
+#include <limits>
+
+#include "pw_assert/assert.h"
+#include "pw_perf_test/event_handler.h"
+#include "pw_perf_test/internal/timer.h"
+
+namespace pw::perf_test {
+
+// Forward declaration.
+class State;
+
+namespace internal {
+
+// Allows access to the private State object constructor
+State CreateState(int durations,
+ EventHandler& event_handler,
+ const char* test_name);
+
+} // namespace internal
+
+/// Records the performance of a test case over many iterations.
+class State {
+ public:
+ // KeepRunning() should be called in a while loop. Responsible for managing
+ // iterations and timestamps.
+ bool KeepRunning();
+
+ private:
+ // Allows the framework to create state objects and unit tests for the state
+ // class
+ friend State internal::CreateState(int durations,
+ EventHandler& event_handler,
+ const char* test_name);
+
+ // Privated constructor to prevent unauthorized instances of the state class.
+ constexpr State(int iterations,
+ EventHandler& event_handler,
+ const char* test_name)
+ : test_iterations_(iterations),
+ iteration_start_(),
+ event_handler_(&event_handler),
+ test_info{.name = test_name} {
+ PW_ASSERT(test_iterations_ > 0);
+ }
+
+ int64_t mean_ = -1;
+
+ // Stores the total number of iterations wanted
+ int test_iterations_;
+
+ // Stores the total duration of the tests.
+ int64_t total_duration_ = 0;
+
+ // Smallest value of the iterations
+ int64_t min_ = std::numeric_limits<int64_t>::max();
+
+ // Largest value of the iterations
+ int64_t max_ = std::numeric_limits<int64_t>::min();
+
+ // Time at the start of the iteration
+ internal::Timestamp iteration_start_;
+
+ // The current iteration.
+ int current_iteration_ = -1;
+
+ EventHandler* event_handler_;
+
+ TestCase test_info;
+};
+
+} // namespace pw::perf_test
diff --git a/pw_perf_test/state.cc b/pw_perf_test/state.cc
new file mode 100644
index 000000000..7fde71adc
--- /dev/null
+++ b/pw_perf_test/state.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_perf_test/state.h"
+
+#include "pw_log/log.h"
+
+namespace pw::perf_test {
+namespace internal {
+
+State CreateState(int durations,
+ EventHandler& event_handler,
+ const char* test_name) {
+ return State(durations, event_handler, test_name);
+}
+} // namespace internal
+
+bool State::KeepRunning() {
+ internal::Timestamp iteration_end = internal::GetCurrentTimestamp();
+ if (current_iteration_ == -1) {
+ ++current_iteration_;
+ event_handler_->TestCaseStart(test_info);
+ iteration_start_ = internal::GetCurrentTimestamp();
+ return true;
+ }
+ int64_t duration = internal::GetDuration(iteration_start_, iteration_end);
+ if (duration > max_) {
+ max_ = duration;
+ }
+ if (duration < min_) {
+ min_ = duration;
+ }
+ total_duration_ += duration;
+ ++current_iteration_;
+ PW_LOG_DEBUG("Iteration number: %d - Duration: %ld",
+ current_iteration_,
+ static_cast<long>(duration));
+ event_handler_->TestCaseIteration({current_iteration_, duration});
+ if (current_iteration_ == test_iterations_) {
+ PW_LOG_DEBUG("Total Duration: %ld Total Iterations: %d",
+ static_cast<long>(total_duration_),
+ test_iterations_);
+ mean_ = total_duration_ / test_iterations_;
+ PW_LOG_DEBUG("Mean: %ld: ", static_cast<long>(mean_));
+ PW_LOG_DEBUG("Minimum: %ld", static_cast<long>(min_));
+ PW_LOG_DEBUG("Maxmimum: %ld", static_cast<long>(max_));
+ event_handler_->TestCaseEnd(test_info,
+ Results{mean_, max_, min_, test_iterations_});
+ return false;
+ }
+ iteration_start_ = internal::GetCurrentTimestamp();
+ return true;
+}
+
+} // namespace pw::perf_test
diff --git a/pw_perf_test/state_test.cc b/pw_perf_test/state_test.cc
index 647e47a3b..bd3e87330 100644
--- a/pw_perf_test/state_test.cc
+++ b/pw_perf_test/state_test.cc
@@ -12,9 +12,10 @@
// License for the specific language governing permissions and limitations under
// the License.
+#include "pw_perf_test/state.h"
+
#include "gtest/gtest.h"
#include "pw_perf_test/event_handler.h"
-#include "pw_perf_test/perf_test.h"
namespace pw::perf_test {
namespace {
diff --git a/pw_perf_test/test_info.cc b/pw_perf_test/test_info.cc
new file mode 100644
index 000000000..91a390ef0
--- /dev/null
+++ b/pw_perf_test/test_info.cc
@@ -0,0 +1,28 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_perf_test/internal/test_info.h"
+
+#include "pw_perf_test/internal/framework.h"
+
+namespace pw::perf_test::internal {
+
+TestInfo::TestInfo(const char* test_name, void (*function_body)(State&))
+ : run_(function_body), test_name_(test_name) {
+ // Once a TestInfo object is created by the macro, this adds itself to the
+ // list of registered tests
+ Framework::Get().RegisterTest(*this);
+}
+
+} // namespace pw::perf_test::internal
diff --git a/pw_polyfill/BUILD.bazel b/pw_polyfill/BUILD.bazel
index 473173a37..eca11f07a 100644
--- a/pw_polyfill/BUILD.bazel
+++ b/pw_polyfill/BUILD.bazel
@@ -31,56 +31,19 @@ pw_cc_library(
includes = ["public"],
)
-# Provides <cstddef>'s std::byte for C++14.
-pw_cc_library(
- name = "cstddef",
- hdrs = [
- "cstddef_public_overrides/cstddef",
- "standard_library_public/pw_polyfill/standard_library/cstddef.h",
- ],
- includes = [
- "public_overrides",
- "standard_library_public",
- ],
- # Polyfills aren't supported in the Bazel build, so disallow use.
- visibility = ["//visibility:private"],
- deps = [":standard_library"],
-)
-
-# Provides <iterator>'s std::data and std::size for C++14.
-pw_cc_library(
- name = "iterator",
- hdrs = [
- "iterator_public_overrides/iterator",
- "standard_library_public/pw_polyfill/standard_library/iterator.h",
- ],
- includes = [
- "public_overrides",
- "standard_library_public",
- ],
- # Polyfills aren't supported in the Bazel build, so disallow use.
- visibility = ["//visibility:private"],
- deps = [":standard_library"],
-)
-
pw_cc_library(
name = "standard_library",
hdrs = [
"standard_library_public/pw_polyfill/standard_library/namespace.h",
],
includes = ["standard_library_public"],
- visibility = [
- "//pw_minimal_cpp_stdlib:__pkg__",
- "//pw_span:__pkg__",
- ],
+ visibility = ["//pw_minimal_cpp_stdlib:__pkg__"],
)
pw_cc_test(
name = "test",
srcs = ["test.cc"],
deps = [
- ":cstddef",
- ":iterator",
":pw_polyfill",
":standard_library",
"//pw_unit_test",
diff --git a/pw_polyfill/BUILD.gn b/pw_polyfill/BUILD.gn
index 0302de39a..a99e98de3 100644
--- a/pw_polyfill/BUILD.gn
+++ b/pw_polyfill/BUILD.gn
@@ -26,52 +26,15 @@ config("public_include_path") {
pw_source_set("pw_polyfill") {
public_configs = [ ":public_include_path" ]
remove_public_deps = [ "*" ]
- public_deps = [ ":standard_library" ]
public = [
"public/pw_polyfill/language_feature_macros.h",
"public/pw_polyfill/standard.h",
]
}
-config("cstddef_overrides_config") {
- include_dirs = [ "cstddef_public_overrides" ]
- cflags = [ "-Wno-gnu-include-next" ]
- visibility = [ ":*" ]
-}
-
-config("iterator_overrides_config") {
- include_dirs = [ "iterator_public_overrides" ]
- cflags = [ "-Wno-gnu-include-next" ]
- visibility = [ ":*" ]
-}
-
config("standard_library_public") {
include_dirs = [ "standard_library_public" ]
-}
-
-# Provides <cstddef>'s std::byte for C++14.
-pw_source_set("cstddef") {
- public_configs = [
- ":standard_library_public",
- ":cstddef_overrides_config",
- ]
- public_deps = [ ":standard_library" ]
- remove_public_deps = [ "*" ]
- public = [ "cstddef_public_overrides/cstddef" ]
- sources = [ "standard_library_public/pw_polyfill/standard_library/cstddef.h" ]
-}
-
-# Provides <iterator>'s std::data and std::size for C++14.
-pw_source_set("iterator") {
- public_configs = [
- ":standard_library_public",
- ":iterator_overrides_config",
- ]
- public_deps = [ ":standard_library" ]
- remove_public_deps = [ "*" ]
- public = [ "iterator_public_overrides/iterator" ]
- sources =
- [ "standard_library_public/pw_polyfill/standard_library/iterator.h" ]
+ visibility = [ ":*" ]
}
pw_source_set("standard_library") {
@@ -79,7 +42,10 @@ pw_source_set("standard_library") {
remove_public_deps = [ "*" ]
public =
[ "standard_library_public/pw_polyfill/standard_library/namespace.h" ]
- visibility = [ ":*" ]
+ visibility = [
+ ":*",
+ "$dir_pw_minimal_cpp_stdlib:*",
+ ]
}
pw_test_group("tests") {
@@ -88,16 +54,11 @@ pw_test_group("tests") {
}
pw_test("test") {
- deps = [ ":pw_polyfill" ]
-
- # Do not depend on :cstddef and :iterator since they override library headers.
- # Instead, add their include path and list them as sources.
- configs = [ ":standard_library_public" ]
- sources = [
- "standard_library_public/pw_polyfill/standard_library/cstddef.h",
- "standard_library_public/pw_polyfill/standard_library/iterator.h",
- "test.cc",
+ deps = [
+ ":pw_polyfill",
+ ":standard_library",
]
+ sources = [ "test.cc" ]
}
pw_doc_group("docs") {
diff --git a/pw_polyfill/CMakeLists.txt b/pw_polyfill/CMakeLists.txt
index 5f1ebf1ef..781ca94e1 100644
--- a/pw_polyfill/CMakeLists.txt
+++ b/pw_polyfill/CMakeLists.txt
@@ -22,7 +22,7 @@ pw_add_library(pw_polyfill INTERFACE
public
)
-pw_add_library(pw_polyfill.standard_library INTERFACE
+pw_add_library(pw_polyfill._standard_library INTERFACE
HEADERS
standard_library_public/pw_polyfill/standard_library/namespace.h
PUBLIC_INCLUDES
diff --git a/pw_polyfill/README.md b/pw_polyfill/README.md
index fa360fd0f..1b1f862c4 100644
--- a/pw_polyfill/README.md
+++ b/pw_polyfill/README.md
@@ -1 +1 @@
-# pw\_polyfill: Backports C++17 features to C++14
+# pw\_polyfill: Supports writing code against different C++ standards
diff --git a/pw_polyfill/docs.rst b/pw_polyfill/docs.rst
index 1188b6f59..08eff995c 100644
--- a/pw_polyfill/docs.rst
+++ b/pw_polyfill/docs.rst
@@ -4,7 +4,7 @@
pw_polyfill
===========
The ``pw_polyfill`` module supports compiling code against different C++
-standards. It also supports backporting a few C++17 features to C++14.
+standards.
----------------------------------------------------
Adapt code to compile with different versions of C++
@@ -61,12 +61,7 @@ Pigweed backports a few C++ features to older C++ standards. These features
are provided in the ``pw`` namespace. If the features are provided by the
toolchain, the ``pw`` versions are aliases of the ``std`` versions.
-``pw_polyfill`` also backports a few C++17 library features to C++14 by wrapping
-the standard C++ and C headers. The wrapper headers include the original header
-using `#include_next
-<https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html>`_, then add missing
-features. The backported features are only defined if they aren't provided by
-the standard header and can only be used when compiling with C++14 in GN.
+These features are documented here, but are not implemented in ``pw_polyfill``.
Backported features
===================
@@ -91,24 +86,12 @@ Backported features
- :ref:`module-pw_bytes`
- ``pw_bytes/bit.h``
- ``pw::endian``
- * - ``<cstdlib>``
- - ``std::byte``
- - ``__cpp_lib_byte``
- - pw_polyfill
- - ``<cstdlib>``
- - ``std::byte``
* - ``<expected>``
- ``std::expected``
- ``__cpp_lib_expected``
- :ref:`module-pw_result`
- ``pw_result/expected.h``
- ``pw::expected``
- * - ``<iterator>``
- - ``std::data``, ``std::size``
- - ``__cpp_lib_nonmember_container_access``
- - pw_polyfill
- - ``<iterator>``
- - ``std::data``, ``std::size``
* - ``<span>``
- ``std::span``
- ``__cpp_lib_span``
@@ -116,12 +99,8 @@ Backported features
- ``pw_span/span.h``
- ``pw::span``
--------------
-Compatibility
--------------
-C++14
-
+------
Zephyr
-======
+------
To enable ``pw_polyfill`` for Zephyr add ``CONFIG_PIGWEED_POLYFILL=y`` to the
project's configuration.
diff --git a/pw_polyfill/standard_library_public/pw_polyfill/standard_library/cstddef.h b/pw_polyfill/standard_library_public/pw_polyfill/standard_library/cstddef.h
deleted file mode 100644
index fc6bfa26c..000000000
--- a/pw_polyfill/standard_library_public/pw_polyfill/standard_library/cstddef.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2020 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-#pragma once
-
-#include <cstddef>
-
-#include "pw_polyfill/standard_library/namespace.h"
-
-// Defines the std::byte type if it is not present.
-#ifndef __cpp_lib_byte
-#define __cpp_lib_byte 201603L
-
-_PW_POLYFILL_BEGIN_NAMESPACE_STD
-
-enum class byte : unsigned char {};
-
-template <typename I>
-constexpr I to_integer(byte b) noexcept {
- return I(b);
-}
-
-constexpr byte operator|(byte l, byte r) noexcept {
- return byte(static_cast<unsigned int>(l) | static_cast<unsigned int>(r));
-}
-
-constexpr byte operator&(byte l, byte r) noexcept {
- return byte(static_cast<unsigned int>(l) & static_cast<unsigned int>(r));
-}
-
-constexpr byte operator^(byte l, byte r) noexcept {
- return byte(static_cast<unsigned int>(l) ^ static_cast<unsigned int>(r));
-}
-
-constexpr byte operator~(byte b) noexcept {
- return byte(~static_cast<unsigned int>(b));
-}
-
-template <typename I>
-constexpr byte operator<<(byte b, I shift) noexcept {
- return byte(static_cast<unsigned int>(b) << shift);
-}
-
-template <typename I>
-constexpr byte operator>>(byte b, I shift) noexcept {
- return byte(static_cast<unsigned int>(b) >> shift);
-}
-
-constexpr byte& operator|=(byte& l, byte r) noexcept { return l = l | r; }
-constexpr byte& operator&=(byte& l, byte r) noexcept { return l = l & r; }
-constexpr byte& operator^=(byte& l, byte r) noexcept { return l = l ^ r; }
-
-template <typename I>
-constexpr byte& operator<<=(byte& b, I shift) noexcept {
- return b = b << shift;
-}
-
-template <typename I>
-constexpr byte& operator>>=(byte& b, I shift) noexcept {
- return b = b >> shift;
-}
-
-_PW_POLYFILL_END_NAMESPACE_STD
-
-#endif // __cpp_lib_byte
diff --git a/pw_polyfill/standard_library_public/pw_polyfill/standard_library/iterator.h b/pw_polyfill/standard_library_public/pw_polyfill/standard_library/iterator.h
deleted file mode 100644
index 2a49b92b1..000000000
--- a/pw_polyfill/standard_library_public/pw_polyfill/standard_library/iterator.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2020 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-#pragma once
-
-#include <iterator>
-
-#include "pw_polyfill/standard_library/namespace.h"
-
-// Define std::data and std::size.
-#ifndef __cpp_lib_nonmember_container_access
-#define __cpp_lib_nonmember_container_access 201411L
-
-#include <cstddef>
-
-_PW_POLYFILL_BEGIN_NAMESPACE_STD
-
-template <typename C>
-constexpr auto data(C& container) -> decltype(container.data()) {
- return container.data();
-}
-
-template <typename C>
-constexpr auto data(const C& container) -> decltype(container.data()) {
- return container.data();
-}
-
-template <typename T, size_t kSize>
-constexpr T* data(T (&array)[kSize]) noexcept {
- return array;
-}
-
-template <typename C>
-constexpr auto size(const C& container) -> decltype(container.size()) {
- return container.size();
-}
-
-template <typename T, size_t kSize>
-constexpr size_t size(const T (&)[kSize]) noexcept {
- return kSize;
-}
-
-_PW_POLYFILL_END_NAMESPACE_STD
-
-#endif // __cpp_lib_nonmember_container_access
diff --git a/pw_polyfill/test.cc b/pw_polyfill/test.cc
index d17475df4..2f0397e6b 100644
--- a/pw_polyfill/test.cc
+++ b/pw_polyfill/test.cc
@@ -12,13 +12,9 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include <array>
-
#include "gtest/gtest.h"
#include "pw_polyfill/language_feature_macros.h"
#include "pw_polyfill/standard.h"
-#include "pw_polyfill/standard_library/cstddef.h"
-#include "pw_polyfill/standard_library/iterator.h"
namespace pw {
namespace polyfill {
@@ -44,43 +40,11 @@ static_assert(PW_CXX_STANDARD_IS_SUPPORTED(20), "C++20 must be supported");
static_assert(!PW_CXX_STANDARD_IS_SUPPORTED(20), "C++20 must not be supported");
#endif // __cplusplus >= 202002L
-TEST(Cstddef, Byte_Operators) {
- std::byte value = std::byte(0);
- EXPECT_EQ((value | std::byte(0x12)), std::byte(0x12));
- EXPECT_EQ((value & std::byte(0x12)), std::byte(0));
- EXPECT_EQ((value ^ std::byte(0x12)), std::byte(0x12));
- EXPECT_EQ(~std::byte(0), std::byte(-1));
- EXPECT_EQ((std::byte(1) << 3), std::byte(0x8));
- EXPECT_EQ((std::byte(0x8) >> 3), std::byte(1));
-}
-
-TEST(Cstddef, Byte_AssignmentOperators) {
- std::byte value = std::byte(0);
- EXPECT_EQ((value |= std::byte(0x12)), std::byte(0x12));
- EXPECT_EQ((value &= std::byte(0x0F)), std::byte(0x02));
- EXPECT_EQ((value ^= std::byte(0xFF)), std::byte(0xFD));
- EXPECT_EQ((value <<= 4), std::byte(0xD0));
- EXPECT_EQ((value >>= 5), std::byte(0x6));
-}
-
// Check that consteval is at least equivalent to constexpr.
PW_CONSTEVAL int ConstevalFunction() { return 123; }
static_assert(ConstevalFunction() == 123,
"Function should work in static_assert");
-int c_array[5423] = {};
-std::array<int, 32> array;
-
-TEST(Iterator, Size) {
- EXPECT_EQ(std::size(c_array), sizeof(c_array) / sizeof(*c_array));
- EXPECT_EQ(std::size(array), array.size());
-}
-
-TEST(Iterator, Data) {
- EXPECT_EQ(std::data(c_array), c_array);
- EXPECT_EQ(std::data(array), array.data());
-}
-
PW_CONSTINIT bool mutable_value = true;
TEST(Constinit, ValueIsMutable) {
diff --git a/pw_presubmit/docs.rst b/pw_presubmit/docs.rst
index e8a84ce42..f7f8a5b6b 100644
--- a/pw_presubmit/docs.rst
+++ b/pw_presubmit/docs.rst
@@ -95,6 +95,8 @@ path is ``out/presubmit``. A subdirectory is created for each presubmit step.
This directory persists between presubmit runs and can be cleaned by deleting it
or running ``pw presubmit --clean``.
+.. _module-pw_presubmit-presubmit-checks:
+
Presubmit checks
================
A presubmit check is defined as a function or other callable. The function must
@@ -180,6 +182,13 @@ others. All of these checks can be included by adding
``pw_presubmit.format_code.presubmit_checks()`` to a presubmit program. These
all use language-specific formatters like clang-format or black.
+Example changes demonstrating how to add formatters:
+
+* `CSS <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/178810>`_
+* `JSON <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/171991>`_
+* `reStructuredText <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168541>`_
+* `TypeScript <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/164825>`_
+
These will suggest fixes using ``pw format --fix``.
Options for code formatting can be specified in the ``pigweed.json`` file
diff --git a/pw_presubmit/py/pw_presubmit/build.py b/pw_presubmit/py/pw_presubmit/build.py
index fc486cba4..17ca25f40 100644
--- a/pw_presubmit/py/pw_presubmit/build.py
+++ b/pw_presubmit/py/pw_presubmit/build.py
@@ -15,10 +15,12 @@
import base64
import contextlib
+from dataclasses import dataclass
import itertools
import json
import logging
import os
+import posixpath
from pathlib import Path
import re
import subprocess
@@ -693,6 +695,26 @@ _INCREMENTAL_COVERAGE_TOOL = 'cloud_client'
_ABSOLUTE_COVERAGE_TOOL = 'raw_coverage_cloud_uploader'
+@dataclass(frozen=True)
+class CoverageOptions:
+ """Coverage collection configuration.
+
+ For Google use only. See go/kalypsi-abs and go/kalypsi-inc for documentation
+ of the metadata fields.
+ """
+
+ target_bucket_root: str
+ target_bucket_project: str
+ project: str
+ trace_type: str
+ ref: str
+ source: str
+ owner: str
+ bug_component: str
+ trim_prefix: str = ''
+ add_prefix: str = ''
+
+
class _NinjaBase(Check):
"""Thin wrapper of Check for steps that call ninja."""
@@ -702,7 +724,7 @@ class _NinjaBase(Check):
packages: Sequence[str] = (),
ninja_contexts: Sequence[_CtxMgrOrLambda] = (),
ninja_targets: Union[str, Sequence[str], Sequence[Sequence[str]]] = (),
- coverage: bool = False,
+ coverage_options: Optional[CoverageOptions] = None,
**kwargs,
):
"""Initializes a _NinjaBase object.
@@ -715,7 +737,8 @@ class _NinjaBase(Check):
ninja_targets: Single ninja target, list of Ninja targets, or list
of list of ninja targets. If a list of a list, ninja will be
called multiple times with the same build directory.
- coverage: Whether to collect coverage data.
+ coverage_options: Coverage collection options (or None, if not
+ collecting coverage data).
**kwargs: Passed on to superclass.
"""
super().__init__(*args, **kwargs)
@@ -723,7 +746,7 @@ class _NinjaBase(Check):
self._ninja_contexts: Tuple[_CtxMgrOrLambda, ...] = tuple(
ninja_contexts
)
- self._collect_coverage = coverage
+ self._coverage_options = coverage_options
if isinstance(ninja_targets, str):
ninja_targets = (ninja_targets,)
@@ -772,7 +795,9 @@ class _NinjaBase(Check):
ninja(ctx, *targets)
return PresubmitResult.PASS
- def _coverage(self, ctx: PresubmitContext) -> PresubmitResult:
+ def _coverage(
+ self, ctx: PresubmitContext, options: CoverageOptions
+ ) -> PresubmitResult:
"""Archive and (on LUCI) upload coverage reports."""
reports = ctx.output_dir / 'coverage_reports'
os.makedirs(reports, exist_ok=True)
@@ -806,47 +831,24 @@ class _NinjaBase(Check):
return PresubmitResult.PASS
with self._context(ctx):
- assert len(ctx.luci.triggers) == 1
- change = ctx.luci.triggers[0]
-
- common_args = [
- f'--host={change.gerrit_host}',
- f'--project={change.project}',
- f'--uploader_name={ctx.luci.builder}',
- f'--uploader_id={ctx.luci.buildbucket_id}',
- '--format=LLVM',
- f'--coverage_file={coverage_json}',
- f'--insert_dir_prefix={ctx.output_dir}',
- ]
-
- if ctx.luci.is_try:
- cmd = [
- _INCREMENTAL_COVERAGE_TOOL,
- '--env=prod',
- f'--change_id={change.number}',
- f'--patchset={change.patchset}',
- *common_args,
- ]
-
- else:
- requests_log = (
- ctx.output_dir / 'coverage-upload-requests.log'
- )
-
- cmd = [
- _ABSOLUTE_COVERAGE_TOOL,
- '--absolute_coverage_service_env=prod',
- f'--ref={change.branch}',
- f'--commit_id={change.ref}',
- f'--requests_log={requests_log}',
- '--timeout=5m',
- *common_args,
- ]
-
- upload_stdout = ctx.output_dir / 'coverage-upload.stdout'
- ctx.output_dir.mkdir(exist_ok=True, parents=True)
- with upload_stdout.open('w') as outs:
- call(*cmd, tee=outs)
+ # GCS bucket paths are POSIX-like.
+ coverage_gcs_path = posixpath.join(
+ options.target_bucket_root,
+ 'incremental' if ctx.luci.is_try else 'absolute',
+ options.target_bucket_project,
+ str(ctx.luci.buildbucket_id),
+ )
+ _copy_to_gcs(
+ ctx,
+ coverage_json,
+ posixpath.join(coverage_gcs_path, 'report.json'),
+ )
+ metadata_json = _write_coverage_metadata(ctx, options)
+ _copy_to_gcs(
+ ctx,
+ metadata_json,
+ posixpath.join(coverage_gcs_path, 'metadata.json'),
+ )
return PresubmitResult.PASS
@@ -873,8 +875,71 @@ class _NinjaBase(Check):
yield SubStep(f'ninja {targets_part}', self._ninja, (targets,))
def _coverage_substeps(self) -> Iterator[SubStep]:
- if self._collect_coverage:
- yield SubStep('coverage', self._coverage)
+ if self._coverage_options is not None:
+ yield SubStep('coverage', self._coverage, (self._coverage_options,))
+
+
+def _copy_to_gcs(ctx: PresubmitContext, filepath: Path, gcs_dst: str):
+ cmd = [
+ "gsutil",
+ "cp",
+ filepath,
+ gcs_dst,
+ ]
+
+ upload_stdout = ctx.output_dir / (filepath.name + '.stdout')
+ with upload_stdout.open('w') as outs:
+ call(*cmd, tee=outs)
+
+
+def _write_coverage_metadata(
+ ctx: PresubmitContext, options: CoverageOptions
+) -> Path:
+ """Write out Kalypsi coverage metadata and return the path to it."""
+ assert ctx.luci is not None
+ assert len(ctx.luci.triggers) == 1
+ change = ctx.luci.triggers[0]
+
+ metadata = {
+ 'host': change.gerrit_host,
+ 'project': options.project,
+ 'trace_type': options.trace_type,
+ 'trim_prefix': options.trim_prefix,
+ 'patchset_num': change.patchset,
+ 'change_id': change.number,
+ 'owner': options.owner,
+ 'bug_component': options.bug_component,
+ }
+
+ if ctx.luci.is_try:
+ # Running in CQ: uploading incremental coverage
+ metadata.update(
+ {
+ # Note: no `add_prefix`. According to the documentation, that's
+ # only supported for absolute coverage.
+ #
+ # TODO(tpudlik): Follow up with Kalypsi team, since this is
+ # surprising (given that trim_prefix is supported for both types
+ # of coverage). This might be an error in the docs.
+ 'patchset_num': change.patchset,
+ 'change_id': change.number,
+ }
+ )
+ else:
+ # Running in CI: uploading absolute coverage
+ metadata.update(
+ {
+ 'add_prefix': options.add_prefix,
+ 'commit_id': change.ref,
+ 'ref': options.ref,
+ 'source': options.source,
+ }
+ )
+
+ metadata_json = ctx.output_dir / "metadata.json"
+ with metadata_json.open('w') as metadata_file:
+ json.dump(metadata, metadata_file)
+ return metadata_json
class GnGenNinja(_NinjaBase):
@@ -907,7 +972,7 @@ class GnGenNinja(_NinjaBase):
def _gn_gen(self, ctx: PresubmitContext) -> PresubmitResult:
args: Dict[str, Any] = {}
- if self._collect_coverage:
+ if self._coverage_options is not None:
args['pw_toolchain_COVERAGE_ENABLED'] = True
args['pw_build_PYTHON_TEST_COVERAGE'] = True
args['pw_C_OPTIMIZATION_LEVELS'] = ('debug',)
diff --git a/pw_presubmit/py/pw_presubmit/format_code.py b/pw_presubmit/py/pw_presubmit/format_code.py
index 08e2c9c7b..df81ad3f9 100755
--- a/pw_presubmit/py/pw_presubmit/format_code.py
+++ b/pw_presubmit/py/pw_presubmit/format_code.py
@@ -610,6 +610,14 @@ TYPESCRIPT_FORMAT: CodeFormat = CodeFormat(
typescript_format_fix,
)
+# TODO: b/308948504 - Add real code formatting support for CSS
+CSS_FORMAT: CodeFormat = CodeFormat(
+ 'css',
+ FileFilter(endswith=['.css']),
+ check_trailing_space,
+ fix_trailing_space,
+)
+
GO_FORMAT: CodeFormat = CodeFormat(
'Go', FileFilter(endswith=['.go']), check_go_format, fix_go_format
)
@@ -683,6 +691,7 @@ CODE_FORMATS: Tuple[CodeFormat, ...] = tuple(
BAZEL_FORMAT,
CMAKE_FORMAT,
COPYBARA_FORMAT,
+ CSS_FORMAT,
C_FORMAT,
GN_FORMAT,
GO_FORMAT,
diff --git a/pw_presubmit/py/pw_presubmit/git_repo.py b/pw_presubmit/py/pw_presubmit/git_repo.py
index f489fcdc1..0fb82568c 100644
--- a/pw_presubmit/py/pw_presubmit/git_repo.py
+++ b/pw_presubmit/py/pw_presubmit/git_repo.py
@@ -26,6 +26,7 @@ PatternOrStr = Union[Pattern, str]
TRACKING_BRANCH_ALIAS = '@{upstream}'
_TRACKING_BRANCH_ALIASES = TRACKING_BRANCH_ALIAS, '@{u}'
+_NON_TRACKING_FALLBACK = 'HEAD~10'
def git_stdout(
@@ -74,7 +75,10 @@ def _diff_names(
yield full_path
-def tracking_branch(repo_path: Optional[Path] = None) -> Optional[str]:
+def tracking_branch(
+ repo_path: Optional[Path] = None,
+ fallback: Optional[str] = None,
+) -> Optional[str]:
"""Returns the tracking branch of the current branch.
Since most callers of this function can safely handle a return value of
@@ -106,7 +110,7 @@ def tracking_branch(repo_path: Optional[Path] = None) -> Optional[str]:
)
except subprocess.CalledProcessError:
- return None
+ return fallback
def list_files(
@@ -128,7 +132,7 @@ def list_files(
repo_path = Path.cwd()
if commit in _TRACKING_BRANCH_ALIASES:
- commit = tracking_branch(repo_path)
+ commit = tracking_branch(repo_path, fallback=_NON_TRACKING_FALLBACK)
if commit:
try:
diff --git a/pw_presubmit/py/pw_presubmit/keep_sorted.py b/pw_presubmit/py/pw_presubmit/keep_sorted.py
index 33ed26fa7..fc2fab090 100644
--- a/pw_presubmit/py/pw_presubmit/keep_sorted.py
+++ b/pw_presubmit/py/pw_presubmit/keep_sorted.py
@@ -181,7 +181,7 @@ class _FileSorter:
if not block.allow_dupes:
lines = list({x.full: x for x in lines}.values())
- StrLinePair = Tuple[str, _Line]
+ StrLinePair = Tuple[str, _Line] # pylint: disable=invalid-name
sort_key_funcs: List[Callable[[StrLinePair], StrLinePair]] = []
if block.ignored_prefixes:
diff --git a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
index f62600875..65f1d7f0e 100755
--- a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
@@ -102,7 +102,6 @@ def gn_clang_build(ctx: PresubmitContext):
"""Checks all compile targets that rely on LLVM tooling."""
build_targets = [
*_at_all_optimization_levels('host_clang'),
- 'cpp14_compatibility',
'cpp20_compatibility',
'asan',
'tsan',
@@ -171,7 +170,6 @@ def _gn_combined_build_check_targets() -> Sequence[str]:
# TODO: b/234645359 - Re-enable on Windows when compatibility tests build.
if sys.platform != 'win32':
- build_targets.append('cpp14_compatibility')
build_targets.append('cpp20_compatibility')
# clang-tidy doesn't run on Windows.
@@ -214,7 +212,17 @@ coverage = build.GnGenNinja(
doc='Run coverage for the host build.',
path_filter=_BUILD_FILE_FILTER,
ninja_targets=('coverage',),
- coverage=True,
+ coverage_options=build.CoverageOptions(
+ target_bucket_root='gs://ng3-metrics/ng3-pigweed-coverage',
+ target_bucket_project='pigweed',
+ project='codesearch',
+ trace_type='LLVM',
+ trim_prefix='/b/s/w/ir/x/w/co',
+ ref='refs/heads/main',
+ source='infra:main',
+ owner='pigweed-infra@google.com',
+ bug_component='503634',
+ ),
)
@@ -729,6 +737,14 @@ def bazel_build(ctx: PresubmitContext) -> None:
'//pw_cpu_exception/...',
)
+ # Build the pw_system example for the Discovery board using STM32Cube.
+ build.bazel(
+ ctx,
+ 'build',
+ '--config=stm32f429i',
+ '//pw_system:system_example',
+ )
+
def pw_transfer_integration_test(ctx: PresubmitContext) -> None:
"""Runs the pw_transfer cross-language integration test only.
@@ -1130,6 +1146,7 @@ _EXCLUDE_FROM_TODO_CHECK = (
r'.gitignore$',
r'.pylintrc$',
r'\bdocs/build_system.rst',
+ r'\bdocs/code_reviews.rst',
r'\bpw_assert_basic/basic_handler.cc',
r'\bpw_assert_basic/public/pw_assert_basic/handler.h',
r'\bpw_blob_store/public/pw_blob_store/flat_file_system_entry.h',
diff --git a/pw_protobuf/Android.bp b/pw_protobuf/Android.bp
index 77b7f2fe3..1a2c79a8f 100644
--- a/pw_protobuf/Android.bp
+++ b/pw_protobuf/Android.bp
@@ -94,6 +94,13 @@ genrule_defaults {
"mkdir -p $${prefix}; cp -t $${prefix} $(in);"
}
+filegroup {
+ name: "pw_protobuf_common_proto",
+ srcs: [
+ "pw_protobuf_protos/common.proto",
+ ],
+}
+
genrule {
name: "pw_protobuf_common_proto_with_prefix",
defaults: ["pw_protobuf_add_prefix_to_proto"],
diff --git a/pw_protobuf/py/setup.cfg b/pw_protobuf/py/setup.cfg
index 209a5338c..1be00722c 100644
--- a/pw_protobuf/py/setup.cfg
+++ b/pw_protobuf/py/setup.cfg
@@ -22,12 +22,10 @@ description = Lightweight streaming protobuf implementation
packages = find:
zip_safe = False
install_requires =
- # protoc 3.17 requires a protobuf version >= 3.20.1 and <= 3.20.3
- # https://developers.google.com/protocol-buffers/docs/news/2022-05-06#python-updates
- # Match all 3.20.* versions:
+ # Ensure protoc and language-specific protobuf libraries are kept in sync.
# This is also specified in //pw_protobuf_compiler/py/setup.cfg
- protobuf~=3.20.1
- googleapis-common-protos>=1.56.2
+ protobuf~=4.24.4
+ googleapis-common-protos>=1.61.0
graphlib-backport;python_version<'3.9'
[options.entry_points]
diff --git a/pw_protobuf_compiler/proto.gni b/pw_protobuf_compiler/proto.gni
index 66f354dbf..305a05f4a 100644
--- a/pw_protobuf_compiler/proto.gni
+++ b/pw_protobuf_compiler/proto.gni
@@ -13,6 +13,7 @@
# the License.
import("//build_overrides/pigweed.gni")
+import("//build_overrides/pigweed_environment.gni")
import("$dir_pw_build/error.gni")
import("$dir_pw_build/input_group.gni")
@@ -32,11 +33,26 @@ _forwarded_vars = [
]
declare_args() {
- # To override the protobuf compiler used set this to the GN target that builds the protobuf compiler.
+ # To override the protobuf compiler used set this to the GN target that builds
+ # the protobuf compiler.
pw_protobuf_compiler_PROTOC_TARGET = ""
+}
- # To override the protobuf compiler used set this to the path, relative to the root_build_dir, to the protoc binary.
- pw_protobuf_compiler_PROTOC_BINARY = ""
+if (pw_protobuf_compiler_PROTOC_TARGET == "" &&
+ defined(pw_env_setup_CIPD_PIGWEED)) {
+ _default_protc =
+ rebase_path("$pw_env_setup_CIPD_PIGWEED/bin/protoc", root_build_dir)
+ if (host_os == "win") {
+ _default_protc += ".exe"
+ }
+} else {
+ _default_protc = ""
+}
+
+declare_args() {
+ # To override the protobuf compiler used set this to the path, relative to the
+ # root_build_dir, to the protoc binary.
+ pw_protobuf_compiler_PROTOC_BINARY = _default_protc
}
# Internal template that invokes protoc with a pw_python_action. This should not
diff --git a/pw_protobuf_compiler/pw_proto_library.bzl b/pw_protobuf_compiler/pw_proto_library.bzl
index 911e28fa7..3f73689a6 100644
--- a/pw_protobuf_compiler/pw_proto_library.bzl
+++ b/pw_protobuf_compiler/pw_proto_library.bzl
@@ -42,7 +42,6 @@ load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "use_cpp_toolchain")
load(
"@pigweed//pw_build/bazel_internal:pigweed_internal.bzl",
- "PW_DEFAULT_COPTS",
_compile_cc = "compile_cc",
)
load("@rules_proto//proto:defs.bzl", "ProtoInfo")
@@ -499,7 +498,6 @@ def _impl_pw_proto_library(ctx):
ctx.attr.deps,
all_includes,
defines = [],
- user_compile_flags = PW_DEFAULT_COPTS,
)
# Instantiate the aspects and rules for generating code using specific plugins.
diff --git a/pw_protobuf_compiler/py/setup.cfg b/pw_protobuf_compiler/py/setup.cfg
index 11bc544c1..b679dd65f 100644
--- a/pw_protobuf_compiler/py/setup.cfg
+++ b/pw_protobuf_compiler/py/setup.cfg
@@ -23,13 +23,12 @@ packages = find:
zip_safe = False
install_requires =
# NOTE: protobuf needs to stay in sync with mypy-protobuf
- # Currently using mypy protobuf 3.20.3 and mypy-protobuf 3.3.0 (see
- # constraint.list). These requirements should stay as >= the lowest version
- # we support.
- mypy-protobuf>=3.2.0,<3.4.0
+ # Currently using mypy protobuf 3.5.0 (see constraint.list). These
+ # requirements should stay as >= the lowest version we support.
+ mypy-protobuf>=3.5.0,<3.6.0
# This is also specified in //pw_protobuf/py/setup.cfg
- protobuf~=3.20.1
- types-protobuf>=3.19.22,<4.0.0
+ protobuf~=4.24.4
+ types-protobuf>=4.24.0.0,<5.0.0
[options.package_data]
pw_protobuf_compiler = py.typed
diff --git a/pw_rpc/java/main/dev/pigweed/pw_rpc/Service.java b/pw_rpc/java/main/dev/pigweed/pw_rpc/Service.java
index ec6f7eb6f..a239f10ef 100644
--- a/pw_rpc/java/main/dev/pigweed/pw_rpc/Service.java
+++ b/pw_rpc/java/main/dev/pigweed/pw_rpc/Service.java
@@ -190,9 +190,11 @@ public class Service {
return (Parser<? extends MessageLite>) messageType.getMethod("parser").invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new LinkageError(
- String.format("Service method was created with %s, which does not have parser() method; "
- + "either the class is not a generated protobuf class "
- + "or the parser() method was optimized out (see b/293361955)",
+ String.format(
+ "Service method created with %s is missing parser() method, likely optimized out. "
+ + "Pass MyMessage.parser() instead of MyMessage.class in service declarations. "
+ + "The class-based API is deprecated and will be removed. "
+ + "See b/293361955.",
messageType),
e);
}
diff --git a/pw_rpc/public/pw_rpc/synchronous_call.h b/pw_rpc/public/pw_rpc/synchronous_call.h
index f597b7fe3..ec02cac95 100644
--- a/pw_rpc/public/pw_rpc/synchronous_call.h
+++ b/pw_rpc/public/pw_rpc/synchronous_call.h
@@ -201,10 +201,10 @@ SynchronousCallFor(
/// @param client The generated service client to use for the call
/// @param request The proto struct to send as the request
/// @param timeout Duration to block for before returning with Timeout
-template <auto kRpcMethod>
+template <auto kRpcMethod, typename GeneratedClient>
SynchronousCallResult<typename internal::MethodInfo<kRpcMethod>::Response>
SynchronousCallFor(
- const typename internal::MethodInfo<kRpcMethod>::GeneratedClient& client,
+ const GeneratedClient& client,
const typename internal::MethodInfo<kRpcMethod>::Request& request,
chrono::SystemClock::duration timeout) {
return internal::StructSynchronousCall<kRpcMethod>(
diff --git a/pw_rpc/pwpb/docs.rst b/pw_rpc/pwpb/docs.rst
index b4c98ecf1..28dd4e991 100644
--- a/pw_rpc/pwpb/docs.rst
+++ b/pw_rpc/pwpb/docs.rst
@@ -127,6 +127,9 @@ Bidirectional streaming RPC
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. attention:: Supported, but the documentation is still under construction.
+
+.. _module-pw_rpc_pw_protobuf-client:
+
Client-side
-----------
A corresponding client class is generated for every service defined in the proto
diff --git a/pw_rust/BUILD.bazel b/pw_rust/BUILD.bazel
index 48cf0c72e..cf6ea0d88 100644
--- a/pw_rust/BUILD.bazel
+++ b/pw_rust/BUILD.bazel
@@ -23,11 +23,11 @@ rust_docs(
# TODO: b/295227522 - Add support to `rust_docs` to automatically processs
# crates in topological order.
crates = [
+ "//pw_format/rust:pw_format",
"//pw_status/rust:pw_status",
"//pw_stream/rust:pw_stream",
"//pw_varint/rust:pw_varint",
"//pw_tokenizer/rust:pw_tokenizer_core",
- "//pw_tokenizer/rust:pw_tokenizer_printf",
"//pw_tokenizer/rust:pw_tokenizer",
],
)
diff --git a/pw_snapshot/py/metadata_test.py b/pw_snapshot/py/metadata_test.py
index 53ed719ce..60c724018 100644
--- a/pw_snapshot/py/metadata_test.py
+++ b/pw_snapshot/py/metadata_test.py
@@ -147,6 +147,22 @@ class MetadataProcessorTest(unittest.TestCase):
)
self.assertEqual(expected, str(meta))
+ def test_no_token_db(self):
+ meta = MetadataProcessor(self.snapshot.metadata)
+ expected = '\n'.join(
+ (
+ 'Snapshot capture reason:',
+ ' $w8SbOg==',
+ '',
+ 'Reason token: 0x3a9bc4c3',
+ 'Project name: $IwkXAQ==',
+ 'Device: hyper-fast-gshoe',
+ 'Device FW version: gShoe-debug-1.2.1-6f23412b+',
+ 'Snapshot UUID: 00000001',
+ )
+ )
+ self.assertEqual(expected, str(meta))
+
def test_serialized_snapshot(self):
self.snapshot.tags['type'] = 'obviously a crash'
expected = '\n'.join(
diff --git a/pw_snapshot/py/pw_snapshot_metadata/metadata.py b/pw_snapshot/py/pw_snapshot_metadata/metadata.py
index d11a2f403..0992c5a86 100644
--- a/pw_snapshot/py/pw_snapshot_metadata/metadata.py
+++ b/pw_snapshot/py/pw_snapshot_metadata/metadata.py
@@ -75,7 +75,7 @@ class MetadataProcessor:
def __init__(
self,
metadata: snapshot_metadata_pb2.Metadata,
- tokenizer_db: Optional[pw_tokenizer.Detokenizer],
+ tokenizer_db: Optional[pw_tokenizer.Detokenizer] = None,
):
self._metadata = metadata
self._tokenizer_db = (
diff --git a/pw_software_update/docs.rst b/pw_software_update/docs.rst
index 33666203b..0cc292e25 100644
--- a/pw_software_update/docs.rst
+++ b/pw_software_update/docs.rst
@@ -9,7 +9,7 @@ pw_software_update
:name: pw_software_update
:tagline: Secure software delivery
:status: experimental
- :languages: Python, C++14, C++17
+ :languages: Python, C++17
The ``pw_software_update`` module offers the following building blocks for
setting up your own end-to-end software delivery system.
diff --git a/pw_span/CMakeLists.txt b/pw_span/CMakeLists.txt
index 4fe5e2734..37e277ea1 100644
--- a/pw_span/CMakeLists.txt
+++ b/pw_span/CMakeLists.txt
@@ -24,7 +24,7 @@ pw_add_library(pw_span INTERFACE
public
PUBLIC_DEPS
pw_assert
- pw_polyfill.standard_library
+ pw_polyfill._standard_library
)
pw_add_test(pw_span.pw_span_test
diff --git a/pw_span/docs.rst b/pw_span/docs.rst
index d1dcd5bff..18b229738 100644
--- a/pw_span/docs.rst
+++ b/pw_span/docs.rst
@@ -88,8 +88,7 @@ more details.
-------------
Compatibility
-------------
-Works with C++14, but some features require C++17. In C++20, use ``std::span``
-instead.
+Works with C++17. In C++20, use ``std::span`` instead.
------
Zephyr
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h
index 3bc9ac559..8583a682b 100644
--- a/pw_span/public/pw_span/span.h
+++ b/pw_span/public/pw_span/span.h
@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations under
// the License.
-// pw::span is an implementation of std::span for C++14 or newer. The
+// pw::span is an implementation of std::span for C++17 or newer. The
// implementation is shared with the std::span polyfill class.
#pragma once
diff --git a/pw_stm32cube_build/docs.rst b/pw_stm32cube_build/docs.rst
index 632149536..6857db0d4 100644
--- a/pw_stm32cube_build/docs.rst
+++ b/pw_stm32cube_build/docs.rst
@@ -1,9 +1,8 @@
.. _module-pw_stm32cube_build:
-------------------
+==================
pw_stm32cube_build
-------------------
-
+==================
The ``pw_stm32cube_build`` module provides helper utilities for building a
target with the stm32cube HAL and/or the stm32cube initialization code.
@@ -12,13 +11,29 @@ are documented here. The rationale for keeping the build files in `third_party`
is that code depending on stm32cube can clearly see that their dependency is on
third party, not pigweed code.
-STM32Cube directory setup
-=========================
+.. _module-pw_stm32cube_build-components:
+
+------------------------
+STM32Cube MCU Components
+------------------------
Each stm32 product family (ex. F4, L5, etc.) has its own stm32cube libraries.
This integration depends on ST's 3 core `MCU Components`_ instead of their
monolithic `MCU Package`. The components are the hal_driver, cmsis_core, and
cmsis_device. All of these repos exist on `ST's GitHub page`_. Compatible
version tags are specified on the ``README.md`` of each MCU component.
+
+To use Pigweed's STM32Cube integration, you will need to acquire the three
+components. The details are build-system dependent.
+
+--------
+GN build
+--------
+The primary ``pw_source_set`` for this integration is
+``$dir_pw_third_party/stm32cube:stm32cube``. This source set includes all of
+the HAL, init code, and templates, depending on the value of the `GN args`_.
+
+Directory setup
+===============
Within a single directory, the following directory/file names are required.
=============== =============================================
@@ -40,26 +55,20 @@ generate the ``files.txt``.
pw package install stm32cube_{family}
-GN build
-========
-The primary ``pw_source_set`` for this integration is
-``$dir_pw_third_party/stm32cube:stm32cube``. This source set includes all of
-the HAL, init code, and templates, depending on value of the `GN args`_.
-
Headers
--------
+=======
``$dir_pw_third_party/stm32cube:stm32cube`` contains the following primary
headers that external targets / applications would care about.
``{family}.h``
-^^^^^^^^^^^^^^
+--------------
ex. ``stm32f4xx.h``, ``stm32l5xx.h``
This is the primary HAL header provided by stm32cube. It includes the entire
HAL and all product specific defines.
``stm32cube/stm32cube.h``
-^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------------------
This is a convenience define provided by this integration. It simply includes
``{family}.h``.
@@ -70,7 +79,7 @@ header allows for stm32 family agnostic modules (ex. ``pw_sys_io_stm32``, which
could work with most, if not all families).
``stm32cube/init.h``
-^^^^^^^^^^^^^^^^^^^^
+--------------------
As described in the inject_init_ section, if you decide to use the built in
init functionality, a pre main init function call, ``pw_stm32cube_Init()``, is
injected into ST's startup scripts.
@@ -79,18 +88,18 @@ This header contains the ``pw_stm32cube_Init()`` function declaration. It
should be included and implemented by target init code.
GN args
--------
+=======
The stm32cube GN build arguments are defined in
``$dir_pw_third_party/stm32cube/stm32cube.gni``.
``dir_pw_third_party_stm32cube_xx``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-----------------------------------
These should be set to point to the stm32cube directory for each family that
you need to build for. These are optional to set and are only provided for
convenience if you need to build for multiple families in the same project.
``dir_pw_third_party_stm32cube``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-----------------------------------
This needs to point to the stm32cube directory for the current build.
For multi target projects, the standard practice to set this for each target:
@@ -101,35 +110,36 @@ For multi target projects, the standard practice to set this for each target:
``pw_third_party_stm32cube_PRODUCT``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+------------------------------------
The product specified in as much detail as possible.
ex. ``stm32f429zit``, ``stm32l552ze``, ``stm32f207zg``, etc.
``pw_third_party_stm32cube_CONFIG``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+------------------------------------
The pw_source_set that provides ``stm32{family}xx_hal_conf.h``. The default
uses the in-tree ``stm32{family}xx_hal_conf_template.h``.
``pw_third_party_stm32cube_TIMEBASE``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------------------------------
The pw_source_set containing the timebase. The default uses the in-tree
``stm32{family}xx_hal_timebase_tim_template.c``.
``pw_third_party_stm32cube_CMSIS_INIT``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+---------------------------------------
The pw_source_set containing the cmsis init logic. The default uses the in-tree
``system_stm32{family}xx.c``.
``pw_third_party_stm32cube_CORE_INIT``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+--------------------------------------
pw_source_set containing the core initialization logic. This normally includes
a ``startup_stm32{...}.s`` + a dependent ``pw_linker_script``. The default
``core_init_template`` uses the upstream startup and linker script matching
``pw_third_party_stm32cube_PRODUCT``. If set to "", you must provide your own
linker/startup logic somewhere else in the build.
+-----------------
stm32cube_builder
-=================
+-----------------
``stm32cube_builder`` is utility that contains the backend scripts used by
``pw_package/stm32cube`` and the GN build scripts in ``third_party/stm32cube``
to interact with the stm32cube repos. You should only need to interact with
@@ -138,7 +148,7 @@ using git submodules instead of pw_package, forking the stm32cube libraries,
interfacing with a different build system, or using your own init.
gen_file_list
--------------
+=============
Build systems like GN are unable to depend on arbitrary directories. Instead,
they must have dependencies on specific files. The HAL for each stm32 product
family has different filenames, so ``files.txt`` was created as a workaround.
@@ -154,7 +164,7 @@ directories.
stm32cube_builder gen_file_list /path/to/stm32cube_dir
find_files
-----------
+==========
Within each stm32 family, there are specific products. Although most of the
HAL is common between products, the init code is almost always different.
``find_files`` looks for all of the files relevant to a particular product
@@ -179,7 +189,7 @@ The following variables are output: ``family``, ``product_define``,
stm32cube_builder find_files /path/to/stm32cube_dir stm32{family}{product} [--init]
inject_init
------------
+=============
ST provides init assembly files for every product in ``cmsis_device``. This is
helpful for getting up and running quickly, but they directly call into
``main()`` before initializing the hardware / peripherals. This is because they
@@ -200,7 +210,7 @@ the pre main init call. The output is printed to stdout, or to the specified
stm32cube_builder inject_init /path/to/startup.s [--out-startup-path /path/to/new_startup.s]
icf_to_ld
----------
+=========
Pigweed primarily uses GCC for its Cortex-M builds. However, ST only provides
IAR linker scripts in ``cmsis_device`` for most product families. This script
converts from ST's IAR linker script format (.icf) to a basic GCC linker
@@ -217,3 +227,131 @@ stdout or the specified ``--ld-path``.
.. _`MCU Components`: https://github.com/STMicroelectronics/STM32Cube_MCU_Overall_Offer#stm32cube-mcu-components
.. _`ST's GitHub page`: https://github.com/STMicroelectronics
+
+.. _module-pw_stm32cube_build-bazel:
+
+-----------
+Bazel build
+-----------
+
+External dependencies
+=====================
+As discussed above in :ref:`module-pw_stm32cube_build-components`, you need the
+three STM32Cube Components for your MCU family to use Pigweed's STM32Cube
+integration. You need to add the following git repositories to your workspace:
+
+* ``stm32{family}xx_hal_driver`` (e.g., `HAL driver repo for the F4 family
+ <https://github.com/STMicroelectronics/stm32f4xx_hal_driver/>`_). We provide
+ a Bazel build file which works for any family at
+ ``@pigweed//third_party/stm32cube/stm32_hal_driver.BUILD.bazel``. By default,
+ we assume this repository will be named ``@hal_driver``, but this can be
+ overriden with a label flag (discussed below).
+* ``cmsis_device_{family}`` (e.g., `CMSIS device repo for the F4 family
+ <https://github.com/STMicroelectronics/cmsis_device_f4>`_). We provide a
+ Bazel build file which works for any family at
+ ``@pigweed//third_party/stm32cube/cmsis_device.BUILD.bazel``. By default, we
+ assume this repository will be named ``@cmsis_device``, but this can be
+ overriden with a label flag (discussed below).
+* ``cmsis_core``, at https://github.com/STMicroelectronics/cmsis_core. We
+ provide a Bazel build file for it at
+ ``@pigweed//third_party/stm32cube/cmsis_core.BUILD.bazel``. By
+ default, we assume this repository will be named ``@cmsis_core``, but this
+ can be overriden with a label flag (discussed below).
+
+.. _module-pw_stm32cube_build-bazel-multifamily:
+
+Building for more than one MCU family
+-------------------------------------
+Different MCU families require different HAL driver and CMSIS device packages
+from STM. So, if your project builds firmware for more than one MCU family, you
+will need to configure separate sets of the three [#]_ STM repositories for each MCU
+family. To do so,
+
+1. Add the appropriate repositories to your WORKSPACE under different names,
+ eg. ``@stm32f4xx_hal_driver`` and ``@stm32h7xx_hal_driver``.
+2. Set the corresponding :ref:`module-pw_stm32cube_build-bazel-label-flags` as
+ part of the platform configuration for your embedded target platforms.
+ Currently, the best way to do this is via a `bazelrc config
+ <https://bazel.build/run/bazelrc#config>`_, which would look like this:
+
+ .. code-block::
+
+ build:stm32f429i --platforms=//targets/stm32f429i_disc1_stm32cube:platform
+ build:stm32f429i --@pigweed//third_party/stm32cube:stm32_hal_driver=@stm32f4xx_hal_driver//:hal_driver
+ build:stm32f429i --@stm32f4xx_hal_driver//:cmsis_device=@cmsis_device_f4//:cmsis_device
+ build:stm32f429i --@stm32f4xx_hal_driver//:cmsis_init=@cmsis_device_f4//:default_cmsis_init
+
+ However, once `platform-based flags
+ <https://github.com/bazelbuild/proposals/blob/main/designs/2023-06-08-platform-based-flags.md>`_
+ are implemented in Bazel, it will be possible to set these flags directly
+ in the platform definition.
+
+.. [#] Although CMSIS core is shared by all MCU families, different CMSIS
+ device repositories may not be compatible with the same version of CMSIS
+ core. In this case, you may need to use separate versions of CMSIS core,
+ too.
+
+Defines
+=======
+
+``STM32CUBE_HEADER``
+--------------------
+Upstream Pigweed modules that depend on the STM32Cube HAL, like
+:ref:`module-pw_sys_io_stm32cube`, include the HAL through the family-agnostic
+header ``stm32cube/stm32cube.h``. This header expects the family to be set
+through a define of ``STM32CUBE_HEADER``. So, to use these Pigweed modules, you
+need to set that define to the correct value (e.g., ``\"stm32f4xx.h\"``; note
+the backslashes) as part of your build. This is most conveniently done through
+``copts`` associated with the target platform.
+
+.. _module-pw_stm32cube_build-bazel-label-flags:
+
+Label flags
+===========
+Required
+--------
+``@hal_driver//:hal_config``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Points to the ``cc_library`` target providing a header with the HAL
+configuration. Note that this header needs an appropriate, family-specific name
+(e.g., ``stm32f4xx_hal_conf.h`` for the F4 family).
+
+Optional
+--------
+These label flags can be used to further customize the behavior of STM32Cube.
+
+``//third_party/stm32cube:stm32_hal_driver``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This label_flag introduces a layer of indirection useful when building a
+project that requires more than one STM32Cube package (see
+:ref:`module-pw_stm32cube_build-bazel-multifamily`). It should point to the
+repository containing the HAL driver.
+
+The default value is ``@hal_driver``.
+
+``@cmsis_device//:cmsis_core``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This label flag should point to the repository containing the CMSIS core build
+target.
+
+The default value is ``@cmsis_core``.
+
+``@hal_driver//:cmsis_device``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This label flag should point to the repository containing the CMSIS device
+build target.
+
+The default value is ``@cmsis_device``.
+
+``@hal_driver//:cmsis_init``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This label flag should point to the CMSIS initialization code. By default it
+points to the ``system_{family}.c`` template provided in the CMSIS device
+repository.
+
+``@hal_driver//:timebase``
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+This label flag should point to a ``cc_library`` providing a timebase
+implementation. By default it points to the template included with STM's HAL
+repository.
+
diff --git a/pw_stream/socket_stream.cc b/pw_stream/socket_stream.cc
index 9316a3d11..b3125439c 100644
--- a/pw_stream/socket_stream.cc
+++ b/pw_stream/socket_stream.cc
@@ -17,6 +17,7 @@
#if defined(_WIN32) && _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
+#define SHUT_RDWR SD_BOTH
#else
#include <arpa/inet.h>
#include <netdb.h>
diff --git a/pw_string/BUILD.gn b/pw_string/BUILD.gn
index 8c267eeec..ef3fda62c 100644
--- a/pw_string/BUILD.gn
+++ b/pw_string/BUILD.gn
@@ -205,6 +205,7 @@ pw_test("util_test") {
pw_doc_group("docs") {
sources = [
"api.rst",
+ "code_size.rst",
"design.rst",
"docs.rst",
"guide.rst",
diff --git a/pw_string/api.rst b/pw_string/api.rst
index 7915ccc75..f90918591 100644
--- a/pw_string/api.rst
+++ b/pw_string/api.rst
@@ -5,12 +5,12 @@ API Reference
=============
.. pigweed-module-subpage::
:name: pw_string
- :tagline: Efficient, easy, and safe string manipulation
+ :tagline: pw_string: Efficient, easy, and safe string manipulation
--------
Overview
--------
-This module provides two types of strings, and utility functions for working
+This module provides two types of strings and utility functions for working
with strings.
**pw::StringBuilder**
@@ -89,7 +89,6 @@ pw::string::Format()
.. doxygenfunction:: pw::string::FormatOverwrite(InlineString<>& string, const char* format, ...)
.. doxygenfunction:: pw::string::FormatOverwriteVaList(InlineString<>& string, const char* format, va_list args)
-
pw::string::NullTerminatedLength()
----------------------------------
.. doxygenfunction:: pw::string::NullTerminatedLength(const char* str, size_t max_len)
diff --git a/pw_string/code_size.rst b/pw_string/code_size.rst
new file mode 100644
index 000000000..575c1cfe9
--- /dev/null
+++ b/pw_string/code_size.rst
@@ -0,0 +1,40 @@
+.. _module-pw_string-size-reports:
+
+==================
+Code Size Analysis
+==================
+.. pigweed-module-subpage::
+ :name: pw_string
+ :tagline: pw_string: Efficient, easy, and safe string manipulation
+
+Save code space by replacing ``snprintf``
+=========================================
+The C standard library function ``snprintf`` is commonly used for string
+formatting. However, it isn't optimized for embedded systems, and using it will
+bring in a lot of other standard library code that will inflate your binary
+size.
+
+Size comparison: snprintf versus pw::StringBuilder
+--------------------------------------------------
+The fixed code size cost of :cpp:type:`pw::StringBuilder` is smaller than
+that of ``std::snprintf``. Using only :cpp:type:`pw::StringBuilder`'s ``<<`` and
+``append`` methods instead of ``snprintf`` leads to significant code size
+reductions.
+
+However, there are cases when the incremental code size cost of
+:cpp:type:`pw::StringBuilder` is similar to that of ``snprintf``. For example,
+each argument to :cpp:type:`pw::StringBuilder`'s ``<<`` method expands to a
+function call, but one or two :cpp:type:`pw::StringBuilder` appends may still
+have a smaller code size impact than a single ``snprintf`` call. Using
+:cpp:type:`pw::StringBuilder` error handling will also impact code size in a
+way that is comparable to ``snprintf``.
+
+.. include:: string_builder_size_report
+
+Size comparison: snprintf versus pw::string::Format
+---------------------------------------------------
+The ``pw::string::Format`` functions have a small, fixed code size
+cost. However, relative to equivalent ``std::snprintf`` calls, there is no
+incremental code size cost to using ``pw::string::Format``.
+
+.. include:: format_size_report
diff --git a/pw_string/design.rst b/pw_string/design.rst
index 43fa4cc35..ae52f4187 100644
--- a/pw_string/design.rst
+++ b/pw_string/design.rst
@@ -1,20 +1,22 @@
.. _module-pw_string-design:
================
-pw_string design
+Design & Roadmap
================
.. pigweed-module-subpage::
:name: pw_string
- :tagline: Efficient, easy, and safe string manipulation
+ :tagline: pw_string: Efficient, easy, and safe string manipulation
``pw_string`` provides string classes and utility functions designed to
prioritize safety and static allocation. The APIs are broadly similar to those
of the string classes in the C++ standard library, so familiarity with those
classes will provide some context around ``pw_string`` design decisions.
-------------
-InlineString
-------------
+.. _module-pw_string-design-inlinestring:
+
+--------------------------
+Design of pw::InlineString
+--------------------------
:cpp:type:`pw::InlineString` / :cpp:class:`pw::InlineBasicString` are designed
to match the ``std::string`` / ``std::basic_string<T>`` API as closely as
possible, but with key differences to improve performance on embedded systems:
@@ -60,9 +62,9 @@ fail an assertion, resulting in a crash. Helpers are provided in
``pw_string/util.h`` that return ``pw::Status::ResourceExhausted()`` instead of
failing an assert when the capacity would be exceeded.
-------------------------
-String utility functions
-------------------------
+----------------------------------
+Design of string utility functions
+----------------------------------
Safe length checking
====================
@@ -77,3 +79,12 @@ null-termination.
Second, a constexpr specialized form is offered where null termination is
required through :cpp:func:`pw::string::NullTerminatedLength`. This will only
return a length if the string is null-terminated.
+
+.. _module-pw_string-roadmap:
+
+-------
+Roadmap
+-------
+* The fixed size cost of :cpp:type:`pw::StringBuilder` can be dramatically
+ reduced by limiting support for 64-bit integers.
+* ``pw_string`` may be integrated with :ref:`module-pw_tokenizer`.
diff --git a/pw_string/docs.rst b/pw_string/docs.rst
index 4be6d93ae..cbd8906bf 100644
--- a/pw_string/docs.rst
+++ b/pw_string/docs.rst
@@ -7,7 +7,7 @@ pw_string
:name: pw_string
:tagline: Efficient, easy, and safe string manipulation
:status: stable
- :languages: C++14, C++17
+ :languages: C++17
:code-size-impact: 500 to 1500 bytes
- **Efficient**: No memory allocation, no pointer indirection.
@@ -86,48 +86,43 @@ meets your needs.
gives you the flexibility to move to smaller platforms later with much less
rework.
-.. _module-pw_string-get-started:
+.. toctree::
+ :hidden:
+ :maxdepth: 1
----------------
-Getting Started
----------------
+ guide
+ api
+ design
+ code_size
-.. tab-set::
+.. grid:: 2
- .. tab-item:: GN
+ .. grid-item-card:: :octicon:`rocket` Get Started & Guides
+ :link: module-pw_string-get-started
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
- Add ``$dir_pw_string`` to the ``deps`` list in your ``pw_executable()``
- build target:
+ Integrate pw_string into your project and learn common use cases
- .. code-block::
+ .. grid-item-card:: :octicon:`code-square` API Reference
+ :link: module-pw_string-api
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
- pw_executable("...") {
- # ...
- deps = [
- # ...
- "$dir_pw_string",
- # ...
- ]
- }
+ Detailed description of the pw_string's classes and methods
- See `//source/BUILD.gn <https://pigweed.googlesource.com/pigweed/sample_project/+/refs/heads/main/source/BUILD.gn>`_
- in the Pigweed Sample Project for an example.
+.. grid:: 2
- .. tab-item:: Zephyr
+ .. grid-item-card:: :octicon:`code-square` Design & Roadmap
+ :link: module-pw_string-design
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
- Add ``CONFIG_PIGWEED_STRING=y`` to the Zephyr project's configuration.
+ Learn why pw_string is designed the way it is, and upcoming plans
--------
-Roadmap
--------
-* The fixed size cost of :cpp:type:`pw::StringBuilder` can be dramatically
- reduced by limiting support for 64-bit integers.
-* ``pw_string`` may be integrated with :ref:`module-pw_tokenizer`.
+ .. grid-item-card:: :octicon:`code-square` Code Size Analysis
+ :link: module-pw_string-size-reports
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- design
- guide
- api
+ Understand pw_string's code footprint and savings potential
diff --git a/pw_string/guide.rst b/pw_string/guide.rst
index a723948b4..dfa83b39b 100644
--- a/pw_string/guide.rst
+++ b/pw_string/guide.rst
@@ -1,22 +1,103 @@
.. _module-pw_string-guide:
-================
-pw_string: Guide
-================
+====================
+Get Started & Guides
+====================
.. pigweed-module-subpage::
:name: pw_string
- :tagline: Efficient, easy, and safe string manipulation
+ :tagline: pw_string: Efficient, easy, and safe string manipulation
-InlineString and StringBuilder?
-===============================
-Use :cpp:type:`pw::InlineString` if you need:
+.. _module-pw_string-get-started:
+
+Get Started
+===========
+.. tab-set::
+
+ .. tab-item:: Bazel
+
+ Add ``@pigweed//pw_string`` to the ``deps`` list in your Bazel target:
+
+ .. code-block::
+
+ cc_library("...") {
+ # ...
+ deps = [
+ # ...
+ "@pigweed//pw_string",
+ # ...
+ ]
+ }
+
+ If only one part of the module is needed, depend only on it; for example
+ ``@pigweed//pw_string:format``.
+
+ This assumes ``@pigweed`` is the name you pulled Pigweed into your Bazel
+ ``WORKSPACE`` as.
+
+ .. tab-item:: GN
+
+ Add ``$dir_pw_string`` to the ``deps`` list in your ``pw_executable()``
+ build target:
+
+ .. code-block::
+
+ pw_executable("...") {
+ # ...
+ deps = [
+ # ...
+ "$dir_pw_string",
+ # ...
+ ]
+ }
+
+ See `//source/BUILD.gn <https://pigweed.googlesource.com/pigweed/sample_project/+/refs/heads/main/source/BUILD.gn>`_
+ in the Pigweed Sample Project for an example.
+
+ .. tab-item:: CMake
+
+ Add ``pw_string`` to your ``pw_add_library`` or similar CMake target:
+
+ .. code-block::
+
+ pw_add_library(my_library STATIC
+ HEADERS
+ ...
+ PRIVATE_DEPS
+ # ...
+ pw_string
+ # ...
+ )
+
+ For a narrower dependency, depend on subtargets like
+ ``pw_string.builder``, etc.
+
+ .. tab-item:: Zephyr
+
+ There are two ways to use ``pw_string`` from a Zephyr project:
+
+ #. Depend on ``pw_string`` in your CMake target (see CMake tab). This is
+ Pigweed Team's suggested approach since it enables precise CMake
+ dependency analysis.
+
+ #. Add ``CONFIG_PIGWEED_STRING=y`` to the Zephyr project's configuration,
+ which causes ``pw_string`` to become a global dependency and have the
+ includes exposed to all targets. Pigweed team does not recommend this
+ approach, though it is the typical Zephyr solution.
+
+Choose between pw::InlineString and pw::StringBuilder
+=====================================================
+`pw::InlineString` is intended to replace typical null terminated character
+arrays in embedded data structures. Use :cpp:type:`pw::InlineString` if you
+need:
* Compatibility with ``std::string``
* Storage internal to the object
* A string object to persist in other data structures
* Lower code size overhead
-Use :cpp:class:`pw::StringBuilder` if you need:
+`pw::StringBuilder` is intended to ease constructing strings in external data;
+typically created on the stack and disposed of in the same function. Use
+:cpp:class:`pw::StringBuilder` if you need:
* Compatibility with ``std::ostringstream``, including custom object support
* Storage external to the object
@@ -62,9 +143,10 @@ constructing a string for external use.
return sb.status();
}
+.. _module-pw_string-guide-stringbuilder:
-Building strings with pw::StringBuilder
-=======================================
+Build a string with pw::StringBuilder
+=====================================
The following shows basic use of a :cpp:class:`pw::StringBuilder`.
.. code-block:: cpp
@@ -93,8 +175,8 @@ The following shows basic use of a :cpp:class:`pw::StringBuilder`.
return sb.status();
}
-Building strings with pw::InlineString
-======================================
+Build a string with pw::InlineString
+====================================
:cpp:type:`pw::InlineString` objects must be constructed by specifying a fixed
capacity for the string.
@@ -130,8 +212,8 @@ capacity for the string.
FunctionThatTakesAnInlineString(std::string_view("1234", 4));
-Building strings inside InlineString with a StringBuilder
-=========================================================
+Build a string inside an pw::InlineString with a pw::StringBuilder
+==================================================================
:cpp:class:`pw::StringBuilder` can build a string in a
:cpp:type:`pw::InlineString`:
@@ -146,8 +228,8 @@ Building strings inside InlineString with a StringBuilder
// inline_str contains "456"
}
-Passing InlineStrings as parameters
-===================================
+Pass an pw::InlineString object as a parameter
+==============================================
:cpp:type:`pw::InlineString` objects can be passed to non-templated functions
via type erasure. This saves code size in most cases, since it avoids template
expansions triggered by string size differences.
@@ -196,8 +278,8 @@ Known size strings
return string;
}();
-Compact initialization of InlineStrings
-=======================================
+Initialization of pw::InlineString objects
+===========================================
:cpp:type:`pw::InlineBasicString` supports class template argument deduction
(CTAD) in C++17 and newer. Since :cpp:type:`pw::InlineString` is an alias, CTAD
is not supported until C++20.
@@ -211,9 +293,9 @@ is not supported until C++20.
// In C++20, CTAD may be used with the pw::InlineString alias.
pw::InlineString my_other_string("123456789");
-Supporting custom types with StringBuilder
-==========================================
-As with ``std::ostream``, StringBuilder supports printing custom types by
+Custom types with pw::StringBuilder
+===================================
+As with ``std::ostream``, pw::StringBuilder supports printing custom types by
overriding the ``<<`` operator. This is is done by defining ``operator<<`` in
the same namespace as the custom type. For example:
@@ -249,37 +331,3 @@ This example shows how to specialize ``pw::ToString``:
}
} // namespace pw
-
-.. _module-pw_string-size-reports:
-
-Saving code space by replacing ``snprintf``
-===========================================
-The C standard library function ``snprintf`` is commonly used for string
-formatting. However, it isn't optimized for embedded systems, and using it will
-bring in a lot of other standard library code that will inflate your binary
-size.
-
-Size comparison: snprintf versus pw::StringBuilder
---------------------------------------------------
-The fixed code size cost of :cpp:type:`pw::StringBuilder` is smaller than
-that of ``std::snprintf``. Using only :cpp:type:`pw::StringBuilder`'s ``<<`` and
-``append`` methods instead of ``snprintf`` leads to significant code size
-reductions.
-
-However, there are cases when the incremental code size cost of
-:cpp:type:`pw::StringBuilder` is similar to that of ``snprintf``. For example,
-each argument to :cpp:type:`pw::StringBuilder`'s ``<<`` method expands to a
-function call, but one or two :cpp:type:`pw::StringBuilder` appends may still
-have a smaller code size impact than a single ``snprintf`` call. Using
-:cpp:type:`pw::StringBuilder` error handling will also impact code size in a
-way that is comparable to ``snprintf``.
-
-.. include:: string_builder_size_report
-
-Size comparison: snprintf versus pw::string::Format
----------------------------------------------------
-The ``pw::string::Format`` functions have a small, fixed code size
-cost. However, relative to equivalent ``std::snprintf`` calls, there is no
-incremental code size cost to using ``pw::string::Format``.
-
-.. include:: format_size_report
diff --git a/pw_sys_io_ambiq_sdk/BUILD.bazel b/pw_sys_io_ambiq_sdk/BUILD.bazel
index bd423e103..59fba7284 100644
--- a/pw_sys_io_ambiq_sdk/BUILD.bazel
+++ b/pw_sys_io_ambiq_sdk/BUILD.bazel
@@ -34,6 +34,7 @@ pw_cc_library(
deps = [
"//pw_boot_cortex_m:armv7m",
"//pw_preprocessor",
+ "//pw_sys_io:default_putget_bytes",
"//pw_sys_io:facade",
],
)
diff --git a/pw_sys_io_arduino/BUILD.bazel b/pw_sys_io_arduino/BUILD.bazel
index 7803aefd7..4b88a368e 100644
--- a/pw_sys_io_arduino/BUILD.bazel
+++ b/pw_sys_io_arduino/BUILD.bazel
@@ -34,7 +34,8 @@ pw_cc_library(
tags = ["manual"],
deps = [
"//pw_preprocessor",
- "//pw_sys_io",
+ "//pw_sys_io:default_putget_bytes",
+ "//pw_sys_io:facade",
],
)
diff --git a/pw_sys_io_baremetal_lm3s6965evb/BUILD.bazel b/pw_sys_io_baremetal_lm3s6965evb/BUILD.bazel
index 909feee92..e2e9984f3 100644
--- a/pw_sys_io_baremetal_lm3s6965evb/BUILD.bazel
+++ b/pw_sys_io_baremetal_lm3s6965evb/BUILD.bazel
@@ -37,6 +37,7 @@ pw_cc_library(
deps = [
"//pw_boot_cortex_m:armv7m",
"//pw_preprocessor",
+ "//pw_sys_io:default_putget_bytes",
"//pw_sys_io:facade",
],
)
diff --git a/pw_sys_io_emcraft_sf2/BUILD.bazel b/pw_sys_io_emcraft_sf2/BUILD.bazel
index 364372790..026fa6eee 100644
--- a/pw_sys_io_emcraft_sf2/BUILD.bazel
+++ b/pw_sys_io_emcraft_sf2/BUILD.bazel
@@ -39,6 +39,7 @@ pw_cc_library(
deps = [
"//pw_boot_cortex_m:armv7m",
"//pw_preprocessor",
- "//pw_sys_io",
+ "//pw_sys_io:default_putget_bytes",
+ "//pw_sys_io:facade",
],
)
diff --git a/pw_sys_io_rp2040/BUILD.bazel b/pw_sys_io_rp2040/BUILD.bazel
index 5193076bd..9017aa7fd 100644
--- a/pw_sys_io_rp2040/BUILD.bazel
+++ b/pw_sys_io_rp2040/BUILD.bazel
@@ -35,6 +35,7 @@ pw_cc_library(
tags = ["manual"],
deps = [
"//pw_status",
- "//pw_sys_io",
+ "//pw_sys_io:default_putget_bytes",
+ "//pw_sys_io:facade",
],
)
diff --git a/pw_sys_io_stm32cube/BUILD.bazel b/pw_sys_io_stm32cube/BUILD.bazel
index 02d347085..4e97350fc 100644
--- a/pw_sys_io_stm32cube/BUILD.bazel
+++ b/pw_sys_io_stm32cube/BUILD.bazel
@@ -33,11 +33,13 @@ pw_cc_library(
"sys_io.cc",
],
hdrs = ["public/pw_sys_io_stm32cube/init.h"],
- # TODO: b/259151566 - Get this to build.
- tags = ["manual"],
+ copts = ["-Wno-unused-parameter"],
+ includes = ["public"],
+ target_compatible_with = [":backend"],
deps = [
"//pw_preprocessor",
"//pw_status",
+ "//pw_sys_io:default_putget_bytes",
"//pw_sys_io:facade",
"//third_party/stm32cube",
],
diff --git a/pw_system/BUILD.bazel b/pw_system/BUILD.bazel
index cf649a738..5bdd83dbc 100644
--- a/pw_system/BUILD.bazel
+++ b/pw_system/BUILD.bazel
@@ -22,11 +22,25 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"])
+constraint_setting(
+ name = "system_example_tracing_setting",
+)
+
+constraint_value(
+ name = "system_example_tracing",
+ constraint_setting = ":system_example_tracing_setting",
+)
+
pw_cc_library(
name = "config",
hdrs = [
"public/pw_system/config.h",
],
+ defines = select({
+ "//conditions:default": [
+ "PW_SYSTEM_ENABLE_TRACE_SERVICE=0",
+ ],
+ }),
)
pw_cc_library(
@@ -72,6 +86,12 @@ pw_cc_library(
"//pw_sync:lock_annotations",
"//pw_tokenizer",
],
+ # Log backends, like assert backends, generally need to be alwayslink'ed
+ # because we don't inform Bazel correctly about dependencies on them. We
+ # only add them as deps of binary targets, not intermediate library targets,
+ # to avoid circular dependencies. But this may lead the linker to eagerly
+ # remove some symbols defined here as unused.
+ alwayslink = 1,
)
pw_cc_library(
@@ -92,6 +112,7 @@ pw_cc_library(
"//pw_hdlc:rpc_channel_output",
"//pw_sync:mutex",
"//pw_thread:thread_core",
+ "//pw_trace",
],
)
@@ -130,16 +151,26 @@ pw_cc_library(
],
includes = ["public"],
deps = [
+ ":file_manager",
":log",
":rpc_server",
":target_hooks",
":thread_snapshot_service",
+ ":transfer_service",
":work_queue",
"//pw_metric:global",
"//pw_metric:metric_service_pwpb",
"//pw_rpc/pwpb:echo_service",
"//pw_thread:thread",
- ],
+ ] + select({
+ ":system_example_tracing": [
+ ":file_service",
+ ":trace_service",
+ "//pw_trace",
+ ],
+ "//conditions:default": [
+ ],
+ }),
)
pw_cc_library(
@@ -186,6 +217,90 @@ pw_cc_library(
)
pw_cc_library(
+ name = "transfer_handlers",
+ srcs = [
+ "transfer_handlers.cc",
+ ],
+ hdrs = [
+ "public/pw_system/transfer_handlers.h",
+ ],
+ includes = ["public"],
+ deps = [
+ "//pw_persistent_ram",
+ "//pw_trace_tokenized:config",
+ "//pw_transfer",
+ ],
+)
+
+pw_cc_library(
+ name = "file_manager",
+ srcs = [
+ "file_manager.cc",
+ ],
+ hdrs = [
+ "public/pw_system/file_manager.h",
+ ],
+ includes = ["public"],
+ deps = [
+ ":config",
+ ":transfer_handlers",
+ "//pw_file:flat_file_system",
+ "//pw_persistent_ram:flat_file_system_entry",
+ ] + select({
+ ":system_example_tracing": [
+ ":trace_service",
+ ],
+ "//conditions:default": [
+ ],
+ }),
+)
+
+pw_cc_library(
+ name = "transfer_service",
+ srcs = [
+ "transfer_service.cc",
+ ],
+ hdrs = [
+ "public/pw_system/transfer_service.h",
+ ],
+ includes = ["public"],
+ deps = [
+ ":file_manager",
+ "//pw_transfer",
+ ],
+)
+
+pw_cc_library(
+ name = "file_service",
+ srcs = [
+ "file_service.cc",
+ ],
+ hdrs = [
+ "public/pw_system/file_service.h",
+ ],
+ includes = ["public"],
+ deps = [
+ ":file_manager",
+ ],
+)
+
+pw_cc_library(
+ name = "trace_service",
+ srcs = [
+ "trace_service.cc",
+ ],
+ hdrs = [
+ "public/pw_system/trace_service.h",
+ ],
+ includes = ["public"],
+ deps = [
+ ":transfer_handlers",
+ "//pw_persistent_ram",
+ "//pw_trace_tokenized:trace_service_pwpb",
+ ],
+)
+
+pw_cc_library(
name = "target_hooks",
hdrs = [
"public/pw_system/target_hooks.h",
@@ -214,7 +329,9 @@ pw_cc_library(
srcs = [
"stl_target_hooks.cc",
],
+ includes = ["public"],
deps = [
+ ":config",
"//pw_thread:thread",
"//pw_thread_stl:thread",
],
@@ -233,6 +350,7 @@ pw_cc_library(
"//pw_build/constraints/rtos:freertos",
],
deps = [
+ ":config",
"//pw_thread:thread",
"//pw_thread_freertos:thread",
],
@@ -249,7 +367,11 @@ pw_cc_binary(
"//pw_stream:sys_io_stream",
"//pw_unit_test:rpc_service",
] + select({
- "//pw_build/constraints/rtos:freertos": [],
+ "//pw_build/constraints/rtos:freertos": [
+ "//pw_tokenizer:linker_script",
+ "//targets/stm32f429i_disc1_stm32cube:linker_script",
+ "//targets/stm32f429i_disc1_stm32cube:pre_init",
+ ],
"//conditions:default": ["//targets/host_device_simulator:boot"],
}),
)
diff --git a/pw_system/BUILD.gn b/pw_system/BUILD.gn
index 37ba684c8..993b748ce 100644
--- a/pw_system/BUILD.gn
+++ b/pw_system/BUILD.gn
@@ -142,15 +142,20 @@ pw_source_set("init") {
public = [ "public/pw_system/init.h" ]
sources = [ "init.cc" ]
deps = [
+ ":file_manager",
+ ":file_service",
":log",
":rpc_server",
":target_hooks.facade",
":thread_snapshot_service",
+ ":trace_service",
+ ":transfer_service",
":work_queue",
"$dir_pw_metric:global",
"$dir_pw_metric:metric_service_pwpb",
"$dir_pw_rpc/pwpb:echo_service",
"$dir_pw_thread:thread",
+ "$dir_pw_trace",
]
}
@@ -165,6 +170,7 @@ pw_source_set("hdlc_rpc_server") {
"$dir_pw_hdlc:rpc_channel_output",
"$dir_pw_log",
"$dir_pw_sync:mutex",
+ "$dir_pw_trace",
]
}
@@ -196,6 +202,58 @@ pw_source_set("socket_target_io") {
]
}
+pw_source_set("transfer_handlers") {
+ public = [ "public/pw_system/transfer_handlers.h" ]
+ public_configs = [ ":public_include_path" ]
+ public_deps = [
+ "$dir_pw_persistent_ram",
+ "$dir_pw_trace_tokenized:config",
+ "$dir_pw_transfer",
+ ]
+ sources = [ "transfer_handlers.cc" ]
+ deps = []
+}
+
+pw_source_set("file_manager") {
+ public = [ "public/pw_system/file_manager.h" ]
+ public_configs = [ ":public_include_path" ]
+ public_deps = [
+ ":config",
+ ":transfer_handlers",
+ "$dir_pw_file:flat_file_system",
+ "$dir_pw_persistent_ram:flat_file_system_entry",
+ ]
+ sources = [ "file_manager.cc" ]
+ deps = [ ":trace_service" ]
+}
+
+pw_source_set("transfer_service") {
+ public = [ "public/pw_system/transfer_service.h" ]
+ public_configs = [ ":public_include_path" ]
+ public_deps = [ "$dir_pw_transfer" ]
+ sources = [ "transfer_service.cc" ]
+ deps = [ ":file_manager" ]
+}
+
+pw_source_set("file_service") {
+ public = [ "public/pw_system/file_service.h" ]
+ public_configs = [ ":public_include_path" ]
+ public_deps = []
+ sources = [ "file_service.cc" ]
+ deps = [ ":file_manager" ]
+}
+
+pw_source_set("trace_service") {
+ public = [ "public/pw_system/trace_service.h" ]
+ public_configs = [ ":public_include_path" ]
+ public_deps = [ ":transfer_handlers" ]
+ sources = [ "trace_service.cc" ]
+ deps = [
+ "$dir_pw_persistent_ram",
+ "$dir_pw_trace_tokenized:trace_service_pwpb",
+ ]
+}
+
pw_source_set("thread_snapshot_service") {
public = [ "public/pw_system/thread_snapshot_service.h" ]
public_configs = [ ":public_include_path" ]
@@ -219,6 +277,7 @@ if (pw_system_TARGET_HOOKS_BACKEND == "") {
get_label_info(":stl_target_hooks", "label_no_toolchain")) {
pw_source_set("stl_target_hooks") {
deps = [
+ ":config",
"$dir_pw_thread:thread",
"$dir_pw_thread_stl:thread",
]
@@ -229,6 +288,7 @@ if (pw_system_TARGET_HOOKS_BACKEND == "") {
get_label_info(":freertos_target_hooks", "label_no_toolchain")) {
pw_source_set("freertos_target_hooks") {
deps = [
+ ":config",
":init",
"$dir_pw_third_party/freertos",
"$dir_pw_thread:thread",
@@ -258,6 +318,7 @@ pw_executable("system_example") {
":pw_system",
"$dir_pw_log",
"$dir_pw_thread:sleep",
+ "$dir_pw_trace",
"$dir_pw_unit_test:rpc_service",
# Adds a test that the test server can run.
@@ -274,6 +335,7 @@ group("system_examples") {
if (dir_pw_third_party_stm32cube_f4 != "" &&
dir_pw_third_party_freertos != "") {
deps += [ ":system_example($dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f429i_disc1_stm32cube.size_optimized)" ]
+ deps += [ ":system_example($dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f429i_disc1_stm32cube_clang.size_optimized)" ]
}
if (dir_pw_third_party_smartfusion_mss != "" &&
dir_pw_third_party_freertos != "") {
diff --git a/pw_system/CMakeLists.txt b/pw_system/CMakeLists.txt
index e8da3dc89..7e1afc975 100644
--- a/pw_system/CMakeLists.txt
+++ b/pw_system/CMakeLists.txt
@@ -101,6 +101,73 @@ pw_add_library(pw_system.thread_snapshot_service STATIC
thread_snapshot_service.cc
)
+pw_add_library(pw_system.transfer_handlers STATIC
+ HEADERS
+ public/pw_system/transfer_handlers.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_persistent_ram
+ pw_trace_tokenized.config
+ pw_transfer
+ pw_transfer.proto.pwpb
+ SOURCES
+ transfer_handlers.cc
+)
+
+pw_add_library(pw_system.file_manager STATIC
+ HEADERS
+ public/pw_system/file_manager.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_system.config
+ pw_system.transfer_handlers
+ pw_persistent_ram.flat_file_system_entry
+ PRIVATE_DEPS
+ pw_system.trace_service
+ SOURCES
+ file_manager.cc
+)
+
+pw_add_library(pw_system.transfer_service STATIC
+ HEADERS
+ public/pw_system/transfer_service.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_transfer
+ PRIVATE_DEPS
+ pw_system.file_manager
+ SOURCES
+ transfer_service.cc
+)
+
+pw_add_library(pw_system.file_service STATIC
+ HEADERS
+ public/pw_system/file_service.h
+ PUBLIC_INCLUDES
+ public
+ PRIVATE_DEPS
+ pw_system.file_manager
+ SOURCES
+ file_service.cc
+)
+
+pw_add_library(pw_system.trace_service STATIC
+ HEADERS
+ public/pw_system/trace_service.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_system.transfer_handlers
+ PRIVATE_DEPS
+ pw_persistent_ram
+ pw_trace_tokenized.trace_service_pwpb
+ SOURCES
+ trace_service.cc
+)
+
pw_add_library(pw_system.io INTERFACE
HEADERS
public/pw_system/io.h
@@ -118,14 +185,19 @@ pw_add_library(pw_system.init STATIC
SOURCES
init.cc
PRIVATE_DEPS
+ pw_system.file_service
pw_system.log
pw_system.rpc_server
pw_system.target_hooks
pw_system.thread_snapshot_service
+ pw_system.trace_service
+ pw_system.transfer_service
+ pw_system.file_manager
pw_system.work_queue
pw_rpc.pwpb.echo_service
pw_metric.metric_service_pwpb
pw_thread.thread
+ pw_trace
)
pw_add_library(pw_system.work_queue STATIC
@@ -163,10 +235,10 @@ pw_add_facade(pw_system.target_hooks INTERFACE
pw_add_library(pw_system.stl_target_hooks STATIC
PRIVATE_DEPS
+ pw_system.config
pw_thread.sleep
pw_thread.thread
pw_thread_stl.thread
-
SOURCES
stl_target_hooks.cc
)
@@ -175,6 +247,7 @@ pw_add_library(pw_system.freertos_target_hooks STATIC
SOURCES
freertos_target_hooks.cc
PRIVATE_DEPS
+ pw_system.config
pw_thread.thread
pw_thread_freertos.thread
# TODO: b/234876414 - This should depend on FreeRTOS but our third parties
diff --git a/pw_system/example_user_app_init.cc b/pw_system/example_user_app_init.cc
index 025dc7b8f..e8951d91a 100644
--- a/pw_system/example_user_app_init.cc
+++ b/pw_system/example_user_app_init.cc
@@ -15,7 +15,7 @@
#include "pw_log/log.h"
#include "pw_system/rpc_server.h"
-#include "pw_thread/sleep.h"
+#include "pw_trace/trace.h"
#include "pw_unit_test/unit_test_service.h"
namespace pw::system {
@@ -25,6 +25,8 @@ pw::unit_test::UnitTestService unit_test_service;
// This will run once after pw::system::Init() completes. This callback must
// return or it will block the work queue.
void UserAppInit() {
+ PW_TRACE_FUNCTION();
+
PW_LOG_INFO("Pigweed is fun!");
GetRpcServer().RegisterService(unit_test_service);
}
diff --git a/pw_system/file_manager.cc b/pw_system/file_manager.cc
new file mode 100644
index 000000000..97fb4987b
--- /dev/null
+++ b/pw_system/file_manager.cc
@@ -0,0 +1,47 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_system/file_manager.h"
+
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+#include "pw_system/trace_service.h"
+#endif
+
+namespace pw::system {
+
+namespace {
+FileManager file_manager;
+}
+
+FileManager& GetFileManager() { return file_manager; }
+
+FileManager::FileManager()
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ : trace_data_handler_(kTraceTransferHandlerId, GetTraceData()),
+ trace_data_filesystem_entry_(
+ "/trace/0.bin",
+ kTraceTransferHandlerId,
+ file::FlatFileSystemService::Entry::FilePermissions::READ,
+ GetTraceData())
+#endif
+{
+ // Every handler & filesystem element must be added to the collections, using
+ // the associated handler ID as the index.
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ transfer_handlers_[kTraceTransferHandlerId] = &trace_data_handler_;
+ file_system_entries_[kTraceTransferHandlerId] = &trace_data_filesystem_entry_;
+#endif
+}
+
+} // namespace pw::system
diff --git a/pw_system/file_service.cc b/pw_system/file_service.cc
new file mode 100644
index 000000000..4e3f3519a
--- /dev/null
+++ b/pw_system/file_service.cc
@@ -0,0 +1,32 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_system/file_service.h"
+
+#include "public/pw_system/file_manager.h"
+
+namespace pw::system {
+namespace {
+
+constexpr size_t kMaxFileNameLength = 48;
+file::FlatFileSystemServiceWithBuffer<kMaxFileNameLength, 1> file_service(
+ GetFileManager().GetFileSystemEntries());
+
+} // namespace
+
+void RegisterFileService(rpc::Server& rpc_server) {
+ rpc_server.RegisterService(file_service);
+}
+
+} // namespace pw::system
diff --git a/pw_system/freertos_backends.gni b/pw_system/freertos_backends.gni
index 8829bc2bb..2c5b85a5d 100644
--- a/pw_system/freertos_backends.gni
+++ b/pw_system/freertos_backends.gni
@@ -33,6 +33,7 @@ PW_SYSTEM_FREERTOS_BACKENDS = {
"$dir_pw_thread_freertos:thread_iteration"
pw_thread_YIELD_BACKEND = "$dir_pw_thread_freertos:yield"
pw_system_TARGET_HOOKS_BACKEND = "$dir_pw_system:freertos_target_hooks"
+ pw_trace_tokenizer_time = "$dir_pw_trace_tokenized:fake_trace_time"
# Enable pw_third_party_freertos_DISABLE_TASKS_STATICS so thread iteration
# works out-of-the-box.
diff --git a/pw_system/freertos_target_hooks.cc b/pw_system/freertos_target_hooks.cc
index d533308a6..c617f41d4 100644
--- a/pw_system/freertos_target_hooks.cc
+++ b/pw_system/freertos_target_hooks.cc
@@ -13,6 +13,7 @@
// the License.
#include "FreeRTOS.h"
+#include "pw_system/config.h"
#include "pw_thread/detached_thread.h"
#include "pw_thread/thread.h"
#include "pw_thread_freertos/context.h"
@@ -27,6 +28,9 @@ enum class ThreadPriority : UBaseType_t {
// there's synchronization issues when they are.
kLog = kWorkQueue,
kRpc = kWorkQueue,
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+ kTransfer = kWorkQueue,
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
kNumPriorities,
};
@@ -57,6 +61,20 @@ const thread::Options& RpcThreadOptions() {
return options;
}
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+static constexpr size_t kTransferThreadStackWords = 512;
+static thread::freertos::StaticContextWithStack<kTransferThreadStackWords>
+ transfer_thread_context;
+const thread::Options& TransferThreadOptions() {
+ static constexpr auto options =
+ pw::thread::freertos::Options()
+ .set_name("TransferThread")
+ .set_static_context(transfer_thread_context)
+ .set_priority(static_cast<UBaseType_t>(ThreadPriority::kTransfer));
+ return options;
+}
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
static constexpr size_t kWorkQueueThreadStackWords = 512;
static thread::freertos::StaticContextWithStack<kWorkQueueThreadStackWords>
work_queue_thread_context;
diff --git a/pw_system/hdlc_rpc_server.cc b/pw_system/hdlc_rpc_server.cc
index 3923d4ff0..72c6c925b 100644
--- a/pw_system/hdlc_rpc_server.cc
+++ b/pw_system/hdlc_rpc_server.cc
@@ -28,6 +28,7 @@
#include "pw_system/config.h"
#include "pw_system/io.h"
#include "pw_system/rpc_server.h"
+#include "pw_trace/trace.h"
#if PW_SYSTEM_DEFAULT_CHANNEL_ID != PW_SYSTEM_LOGGING_CHANNEL_ID && \
PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS == PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS
@@ -117,6 +118,7 @@ class RpcDispatchThread final : public thread::ThreadCore {
for (std::byte byte : ret_val.value()) {
if (auto result = decoder.Process(byte); result.ok()) {
hdlc::Frame& frame = result.value();
+ PW_TRACE_SCOPE("RPC process frame");
if (frame.address() == PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS ||
frame.address() == PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS) {
server.ProcessPacket(frame.data());
diff --git a/pw_system/init.cc b/pw_system/init.cc
index 4196115d3..96d5dad39 100644
--- a/pw_system/init.cc
+++ b/pw_system/init.cc
@@ -26,6 +26,18 @@
#include "pw_system_private/log.h"
#include "pw_thread/detached_thread.h"
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+#include "pw_system/transfer_service.h"
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+#include "pw_system/file_service.h"
+#include "pw_system/trace_service.h"
+#include "pw_trace/trace.h"
+#endif // PW_SYSTEM_ENABLE_TRACE_SERVICE
+
+#include "pw_system/file_manager.h"
+
#if PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE
#include "pw_system/thread_snapshot_service.h"
#endif // PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE
@@ -38,6 +50,12 @@ metric::MetricService metric_service(metric::global_metrics,
rpc::EchoService echo_service;
void InitImpl() {
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ // tracing is off by default, requring a user to enable it through
+ // the trace service
+ PW_TRACE_SET_ENABLED(false);
+#endif
+
PW_LOG_INFO("System init");
// Setup logging.
@@ -52,6 +70,16 @@ void InitImpl() {
GetRpcServer().RegisterService(echo_service);
GetRpcServer().RegisterService(GetLogService());
GetRpcServer().RegisterService(metric_service);
+
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+ RegisterTransferService(GetRpcServer());
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ RegisterFileService(GetRpcServer());
+ RegisterTraceService(GetRpcServer(), FileManager::kTraceTransferHandlerId);
+#endif // PW_SYSTEM_ENABLE_TRACE_SERVICE
+
#if PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE
RegisterThreadSnapshotService(GetRpcServer());
#endif // PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE
@@ -61,6 +89,11 @@ void InitImpl() {
thread::DetachedThread(system::LogThreadOptions(), GetLogThread());
thread::DetachedThread(system::RpcThreadOptions(), GetRpcDispatchThread());
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+ thread::DetachedThread(system::TransferThreadOptions(), GetTransferThread());
+ InitTransferService();
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
GetWorkQueue().CheckPushWork(UserAppInit);
}
diff --git a/pw_system/public/pw_system/config.h b/pw_system/public/pw_system/config.h
index 0d04158cb..b4024ca89 100644
--- a/pw_system/public/pw_system/config.h
+++ b/pw_system/public/pw_system/config.h
@@ -68,6 +68,21 @@
#define PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS PW_SYSTEM_DEFAULT_RPC_HDLC_ADDRESS
#endif // PW_SYSTEM_LOGGING_RPC_HDLC_ADDRESS
+// PW_SYSTEM_ENABLE_TRACE_SERVICE specifies if the trace RPC service is enabled.
+//
+// Defaults to 1.
+#ifndef PW_SYSTEM_ENABLE_TRACE_SERVICE
+#define PW_SYSTEM_ENABLE_TRACE_SERVICE 1
+#endif // PW_SYSTEM_ENABLE_TRACE_SERVICE
+
+// PW_SYSTEM_ENABLE_TRANSFER_SERVICE specifies if the transfer RPC service is
+// enabled.
+//
+// Defaults to 1.
+#ifndef PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+#define PW_SYSTEM_ENABLE_TRANSFER_SERVICE 1
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
// PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE specifies if the thread snapshot
// RPC service is enabled.
//
diff --git a/pw_system/public/pw_system/file_manager.h b/pw_system/public/pw_system/file_manager.h
new file mode 100644
index 000000000..a061cd5a3
--- /dev/null
+++ b/pw_system/public/pw_system/file_manager.h
@@ -0,0 +1,61 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_persistent_ram/flat_file_system_entry.h"
+#include "pw_system/config.h"
+#include "pw_system/transfer_handlers.h"
+
+namespace pw::system {
+
+class FileManager {
+ public:
+ // Each transfer handler ID corresponds 1:1 with a transfer handler and
+ // filesystem element pair. The ID must be unique and increment from 0 to
+ // ensure no gaps in the FileManager handler & filesystem arrays.
+ // NOTE: the enumerators should never have values defined, to ensure they
+ // increment from zero and kNumFileSystemEntries is correct
+ enum TransferHandlerId : uint32_t {
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ kTraceTransferHandlerId,
+#endif
+ kNumFileSystemEntries
+ };
+
+ FileManager();
+
+ std::array<transfer::Handler*, kNumFileSystemEntries>& GetTransferHandlers() {
+ return transfer_handlers_;
+ }
+ std::array<file::FlatFileSystemService::Entry*, kNumFileSystemEntries>&
+ GetFileSystemEntries() {
+ return file_system_entries_;
+ }
+
+ private:
+#if PW_SYSTEM_ENABLE_TRACE_SERVICE
+ TracePersistentBufferTransfer trace_data_handler_;
+ persistent_ram::FlatFileSystemPersistentBufferEntry<
+ PW_TRACE_BUFFER_SIZE_BYTES>
+ trace_data_filesystem_entry_;
+#endif
+
+ std::array<transfer::Handler*, kNumFileSystemEntries> transfer_handlers_;
+ std::array<file::FlatFileSystemService::Entry*, kNumFileSystemEntries>
+ file_system_entries_;
+};
+
+FileManager& GetFileManager();
+
+} // namespace pw::system
diff --git a/pw_polyfill/cstddef_public_overrides/cstddef b/pw_system/public/pw_system/file_service.h
index b0cff3b14..1610bff01 100644
--- a/pw_polyfill/cstddef_public_overrides/cstddef
+++ b/pw_system/public/pw_system/file_service.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Pigweed Authors
+// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
@@ -13,10 +13,10 @@
// the License.
#pragma once
-static_assert(__cplusplus < 201703L,
- "The <cstddef> polyfill header is only intended for C++14. "
- "It cannot be used when building with C++17 or newer.");
+#include "pw_rpc/server.h"
-#include_next <cstddef>
+namespace pw::system {
-#include "pw_polyfill/standard_library/cstddef.h"
+void RegisterFileService(rpc::Server& rpc_server);
+
+} // namespace pw::system
diff --git a/pw_system/public/pw_system/target_hooks.h b/pw_system/public/pw_system/target_hooks.h
index 58460ca8b..19628c2ad 100644
--- a/pw_system/public/pw_system/target_hooks.h
+++ b/pw_system/public/pw_system/target_hooks.h
@@ -21,6 +21,8 @@ const thread::Options& LogThreadOptions();
const thread::Options& RpcThreadOptions();
+const thread::Options& TransferThreadOptions();
+
const thread::Options& WorkQueueThreadOptions();
// This will run once after pw::system::Init() completes. This callback must
diff --git a/pw_polyfill/iterator_public_overrides/iterator b/pw_system/public/pw_system/trace_service.h
index 80394d868..7c16c0e38 100644
--- a/pw_polyfill/iterator_public_overrides/iterator
+++ b/pw_system/public/pw_system/trace_service.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Pigweed Authors
+// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
@@ -13,10 +13,13 @@
// the License.
#pragma once
-static_assert(__cplusplus < 201703L,
- "The <iterator> polyfill header is only intended for C++14. "
- "It cannot be used when building with C++17 or newer.");
+#include "pw_rpc/server.h"
+#include "pw_system/transfer_handlers.h"
-#include_next <iterator>
+namespace pw::system {
-#include "pw_polyfill/standard_library/iterator.h"
+void RegisterTraceService(rpc::Server& rpc_server, uint32_t transfer_id);
+
+TracePersistentBuffer& GetTraceData();
+
+} // namespace pw::system
diff --git a/pw_system/public/pw_system/transfer_handlers.h b/pw_system/public/pw_system/transfer_handlers.h
new file mode 100644
index 000000000..f82308559
--- /dev/null
+++ b/pw_system/public/pw_system/transfer_handlers.h
@@ -0,0 +1,37 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_persistent_ram/persistent_buffer.h"
+#include "pw_trace_tokenized/config.h"
+#include "pw_transfer/transfer.h"
+
+namespace pw::system {
+
+using TracePersistentBuffer =
+ persistent_ram::PersistentBuffer<PW_TRACE_BUFFER_SIZE_BYTES>;
+
+class TracePersistentBufferTransfer : public transfer::ReadOnlyHandler {
+ public:
+ TracePersistentBufferTransfer(uint32_t id,
+ TracePersistentBuffer& persistent_buffer);
+
+ Status PrepareRead() final;
+
+ private:
+ TracePersistentBuffer& persistent_buffer_;
+ stream::MemoryReader reader_;
+};
+
+} // namespace pw::system
diff --git a/pw_system/public/pw_system/transfer_service.h b/pw_system/public/pw_system/transfer_service.h
new file mode 100644
index 000000000..64a60b99c
--- /dev/null
+++ b/pw_system/public/pw_system/transfer_service.h
@@ -0,0 +1,27 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_rpc/server.h"
+#include "pw_transfer/transfer_thread.h"
+
+namespace pw::system {
+
+void RegisterTransferService(rpc::Server& rpc_server);
+
+void InitTransferService();
+
+transfer::TransferThread& GetTransferThread();
+
+} // namespace pw::system
diff --git a/pw_system/py/BUILD.bazel b/pw_system/py/BUILD.bazel
index a45c81dd9..61c72038d 100644
--- a/pw_system/py/BUILD.bazel
+++ b/pw_system/py/BUILD.bazel
@@ -33,6 +33,7 @@ py_library(
"pw_system/__init__.py",
"pw_system/console.py",
"pw_system/device.py",
+ "pw_system/device_tracing.py",
],
imports = ["."],
tags = ["manual"],
diff --git a/pw_system/py/BUILD.gn b/pw_system/py/BUILD.gn
index 755fb508a..7799436a3 100644
--- a/pw_system/py/BUILD.gn
+++ b/pw_system/py/BUILD.gn
@@ -25,10 +25,13 @@ pw_python_package("py") {
"pw_system/__init__.py",
"pw_system/console.py",
"pw_system/device.py",
+ "pw_system/device_tracing.py",
+ "pw_system/trace_client.py",
]
python_deps = [
"$dir_pw_cli/py",
"$dir_pw_console/py",
+ "$dir_pw_file/py",
"$dir_pw_hdlc/py",
"$dir_pw_log:protos.python",
"$dir_pw_log/py",
@@ -40,6 +43,8 @@ pw_python_package("py") {
"$dir_pw_thread:protos.python",
"$dir_pw_thread/py",
"$dir_pw_tokenizer/py",
+ "$dir_pw_trace_tokenized:protos.python",
+ "$dir_pw_transfer:proto.python",
"$dir_pw_unit_test:unit_test_proto.python",
"$dir_pw_unit_test/py",
]
diff --git a/pw_system/py/pw_system/console.py b/pw_system/py/pw_system/console.py
index 172d370d7..6f6405cac 100644
--- a/pw_system/py/pw_system/console.py
+++ b/pw_system/py/pw_system/console.py
@@ -64,6 +64,7 @@ from pw_console.socket_client import SocketClient, SocketClientWithLogging
from pw_hdlc import rpc
from pw_rpc.console_tools.console import flattened_rpc_completions
from pw_system.device import Device
+from pw_system.device_tracing import DeviceWithTracing
from pw_tokenizer.detokenize import AutoUpdatingDetokenizer
# Default proto imports:
@@ -71,6 +72,9 @@ from pw_log.proto import log_pb2
from pw_metric_proto import metric_service_pb2
from pw_thread_protos import thread_snapshot_service_pb2
from pw_unit_test_proto import unit_test_pb2
+from pw_file import file_pb2
+from pw_trace_protos import trace_service_pb2
+from pw_transfer import transfer_pb2
_LOG = logging.getLogger('tools')
_DEVICE_LOG = logging.getLogger('rpc_device')
@@ -184,6 +188,13 @@ def get_parser() -> argparse.ArgumentParser:
help='glob pattern for .proto files.',
)
parser.add_argument(
+ '-f',
+ '--ticks_per_second',
+ type=int,
+ dest='ticks_per_second',
+ help=('The clock rate of the trace events.'),
+ )
+ parser.add_argument(
'-v',
'--verbose',
action='store_true',
@@ -351,6 +362,7 @@ def console(
device: str,
baudrate: int,
proto_globs: Collection[str],
+ ticks_per_second: Optional[int],
token_databases: Collection[Path],
socket_addr: str,
logfile: str,
@@ -438,7 +450,11 @@ def console(
detokenizer = None
if token_databases:
- detokenizer = AutoUpdatingDetokenizer(*token_databases)
+ token_databases_with_domains = [] * len(token_databases)
+ for token_database in token_databases:
+ token_databases_with_domains.append(str(token_database) + "#trace")
+
+ detokenizer = AutoUpdatingDetokenizer(*token_databases_with_domains)
detokenizer.show_errors = True
protos: List[Union[ModuleType, Path]] = list(_expand_globs(proto_globs))
@@ -455,6 +471,9 @@ def console(
protos.extend(compiled_protos)
protos.append(metric_service_pb2)
protos.append(thread_snapshot_service_pb2)
+ protos.append(file_pb2)
+ protos.append(trace_service_pb2)
+ protos.append(transfer_pb2)
if not protos:
_LOG.critical(
@@ -516,7 +535,8 @@ def console(
return 1
with reader:
- device_client = Device(
+ device_client = DeviceWithTracing(
+ ticks_per_second,
channel_id,
reader,
write,
diff --git a/pw_system/py/pw_system/device.py b/pw_system/py/pw_system/device.py
index 1ef454210..470ddc236 100644
--- a/pw_system/py/pw_system/device.py
+++ b/pw_system/py/pw_system/device.py
@@ -17,8 +17,8 @@ import logging
from pathlib import Path
from types import ModuleType
from typing import Any, Callable, List, Union, Optional
-import warnings
+from pw_thread_protos import thread_pb2
from pw_hdlc.rpc import (
HdlcRpcClient,
channel_output,
@@ -36,7 +36,6 @@ from pw_log_rpc.rpc_log_stream import LogStreamHandler
from pw_metric import metric_parser
from pw_rpc import callback_client, Channel, console_tools
from pw_thread.thread_analyzer import ThreadSnapshotAnalyzer
-from pw_thread_protos import thread_pb2
from pw_tokenizer import detokenize
from pw_tokenizer.proto import decode_optionally_tokenized
from pw_unit_test.rpc import run_tests as pw_unit_test_run_tests, TestRecord
@@ -54,18 +53,18 @@ class Device:
Note: use this class as a base for specialized device representations.
"""
- # TODO: b/301496598 - Deprecate read Callable type and accept only
- # StoppableReadable classes in downstream projects. Then change the argument
- # name to "reader".
+ # pylint: disable=too-many-instance-attributes
def __init__(
+ # pylint: disable=too-many-arguments
self,
channel_id: int,
- read: Union[CancellableReader, Callable[[], bytes]],
+ reader: CancellableReader,
write,
proto_library: List[Union[ModuleType, Path]],
detokenizer: Optional[detokenize.Detokenizer] = None,
timestamp_decoder: Optional[Callable[[int], str]] = None,
rpc_timeout_s: float = 5,
+ time_offset: int = 0,
use_rpc_logging: bool = True,
use_hdlc_encoding: bool = True,
logger: logging.Logger = DEFAULT_DEVICE_LOGGER,
@@ -74,6 +73,7 @@ class Device:
self.protos = proto_library
self.detokenizer = detokenizer
self.rpc_timeout_s = rpc_timeout_s
+ self.time_offset = time_offset
self.logger = logger
self.logger.setLevel(logging.DEBUG) # Allow all device logs through.
@@ -96,17 +96,11 @@ class Device:
for line in log_messages.splitlines():
self.logger.info(line)
- if not isinstance(read, CancellableReader):
- warnings.warn(
- 'The read as Callablle is deprecated. Use CancellableReader'
- 'instead.',
- DeprecationWarning,
- )
self.client: RpcClient
if use_hdlc_encoding:
channels = [Channel(self.channel_id, channel_output(write))]
self.client = HdlcRpcClient(
- read,
+ reader,
self.protos,
channels,
detokenize_and_log_output,
@@ -115,7 +109,7 @@ class Device:
else:
channel = Channel(self.channel_id, write)
self.client = NoEncodingSingleChannelRpcClient(
- read,
+ reader,
self.protos,
channel,
client_impl=callback_client_impl,
diff --git a/pw_system/py/pw_system/device_tracing.py b/pw_system/py/pw_system/device_tracing.py
new file mode 100644
index 000000000..c97dabe08
--- /dev/null
+++ b/pw_system/py/pw_system/device_tracing.py
@@ -0,0 +1,162 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Device tracing classes to interact with targets via RPC."""
+
+import os
+import logging
+import tempfile
+
+# from pathlib import Path
+# from types import ModuleType
+# from typing import Callable, List, Optional, Union
+from typing import List, Optional
+
+import pw_transfer
+from pw_file import file_pb2
+from pw_rpc.callback_client.errors import RpcError
+from pw_system.device import Device
+from pw_trace import trace
+from pw_trace_tokenized import trace_tokenized
+
+_LOG = logging.getLogger('tracing')
+DEFAULT_TICKS_PER_SECOND = 1000
+
+
+class DeviceWithTracing(Device):
+ """Represents an RPC Client for a device running a Pigweed target with
+ tracing.
+
+ The target must have and RPC support for the following services:
+ - logging
+ - file
+ - transfer
+ - tracing
+ Note: use this class as a base for specialized device representations.
+ """
+
+ def __init__(self, ticks_per_second: Optional[int], *argv, **kargv):
+ super().__init__(*argv, **kargv)
+
+ # Create the transfer manager
+ self.transfer_service = self.rpcs.pw.transfer.Transfer
+ self.transfer_manager = pw_transfer.Manager(
+ self.transfer_service,
+ default_response_timeout_s=self.rpc_timeout_s,
+ initial_response_timeout_s=self.rpc_timeout_s,
+ default_protocol_version=pw_transfer.ProtocolVersion.LATEST,
+ )
+
+ if ticks_per_second:
+ self.ticks_per_second = ticks_per_second
+ else:
+ self.ticks_per_second = self.get_ticks_per_second()
+ _LOG.info('ticks_per_second set to %i', self.ticks_per_second)
+
+ def get_ticks_per_second(self) -> int:
+ trace_service = self.rpcs.pw.trace.proto.TraceService
+ try:
+ resp = trace_service.GetClockParameters()
+ if not resp.status.ok():
+ _LOG.error(
+ 'Failed to get clock parameters: %s. Using default \
+ value',
+ resp.status,
+ )
+ return DEFAULT_TICKS_PER_SECOND
+ except RpcError as rpc_err:
+ _LOG.exception('%s. Using default value', rpc_err)
+ return DEFAULT_TICKS_PER_SECOND
+
+ return resp.response.clock_parameters.tick_period_seconds_denominator
+
+ def list_files(self) -> List:
+ """Lists all files on this device."""
+ fs_service = self.rpcs.pw.file.FileSystem
+ stream_response = fs_service.List()
+
+ if not stream_response.status.ok():
+ _LOG.error('Failed to list files %s', stream_response.status)
+ return []
+
+ return stream_response.responses
+
+ def delete_file(self, path: str) -> bool:
+ """Delete a file on this device."""
+ fs_service = self.rpcs.pw.file.FileSystem
+ req = file_pb2.DeleteRequest(path=path)
+ stream_response = fs_service.Delete(req)
+ if not stream_response.status.ok():
+ _LOG.error(
+ 'Failed to delete file %s file: %s',
+ path,
+ stream_response.status,
+ )
+ return False
+
+ return True
+
+ def transfer_file(self, file_id: int, dest_path: str) -> bool:
+ """Transfer a file on this device to the host."""
+ try:
+ data = self.transfer_manager.read(file_id)
+ with open(dest_path, "wb") as bin_file:
+ bin_file.write(data)
+ except pw_transfer.Error:
+ _LOG.exception('Failed to transfer file_id %i', file_id)
+ return False
+
+ return True
+
+ def start_tracing(self) -> None:
+ """Turns on tracing on this device."""
+ trace_service = self.rpcs.pw.trace.proto.TraceService
+ trace_service.Start()
+
+ def stop_tracing(self, trace_output_path: str = "trace.json") -> None:
+ """Turns off tracing on this device and downloads the trace file."""
+ trace_service = self.rpcs.pw.trace.proto.TraceService
+ resp = trace_service.Stop()
+
+ # If there's no tokenizer, there's no need to transfer the trace
+ # file from the device after stopping tracing, as there's not much
+ # that can be done with it.
+ if not self.detokenizer:
+ _LOG.error('No tokenizer specified. Not transfering trace')
+ return
+
+ trace_bin_path = tempfile.NamedTemporaryFile(delete=False)
+ trace_bin_path.close()
+ try:
+ if not self.transfer_file(
+ resp.response.file_id, trace_bin_path.name
+ ):
+ return
+
+ with open(trace_bin_path.name, 'rb') as bin_file:
+ trace_data = bin_file.read()
+ events = trace_tokenized.get_trace_events(
+ [self.detokenizer.database],
+ trace_data,
+ self.ticks_per_second,
+ self.time_offset,
+ )
+ json_lines = trace.generate_trace_json(events)
+ trace_tokenized.save_trace_file(json_lines, trace_output_path)
+
+ _LOG.info(
+ 'Wrote trace file %s',
+ trace_output_path,
+ )
+ finally:
+ os.remove(trace_bin_path.name)
diff --git a/pw_system/py/pw_system/trace_client.py b/pw_system/py/pw_system/trace_client.py
new file mode 100644
index 000000000..575659b26
--- /dev/null
+++ b/pw_system/py/pw_system/trace_client.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Generates json trace files viewable using chrome://tracing using RPCs from a
+connected trace service.
+
+Example usage:
+python pw_console/py/pw_console/trace_client.py
+ -o trace.json
+ -t out/host_device_simulator.speed_optimized/obj/pw_system/bin/system_example
+"""
+
+import argparse
+import logging
+import sys
+from pathlib import Path
+from types import ModuleType
+from typing import List, Union
+
+
+from pw_transfer import transfer_pb2
+from pw_log.proto import log_pb2
+from pw_trace_protos import trace_service_pb2
+from pw_trace import trace
+from pw_trace_tokenized import trace_tokenized
+import pw_transfer
+from pw_file import file_pb2
+from pw_hdlc import rpc
+from pw_system.device_tracing import DeviceWithTracing
+from pw_tokenizer.detokenize import AutoUpdatingDetokenizer
+from pw_console.socket_client import SocketClient
+
+
+_LOG = logging.getLogger('pw_console_trace_client')
+_LOG.level = logging.DEBUG
+_LOG.addHandler(logging.StreamHandler(sys.stdout))
+
+
+def start_tracing_on_device(client):
+ """Start tracing on the device"""
+ service = client.rpcs.pw.trace.proto.TraceService
+ service.Start()
+
+
+def stop_tracing_on_device(client):
+ """Stop tracing on the device"""
+ service = client.rpcs.pw.trace.proto.TraceService
+ return service.Stop()
+
+
+def list_files_on_device(client):
+ """List files on the device"""
+ service = client.rpcs.pw.file.FileSystem
+ return service.List()
+
+
+def delete_file_on_device(client, path):
+ """Delete a file on the device"""
+ service = client.rpcs.pw.file.FileSystem
+ req = file_pb2.DeleteRequest(path=path)
+ return service.Delete(req)
+
+
+def _parse_args():
+ """Parse and return command line arguments."""
+
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ group = parser.add_mutually_exclusive_group(required=False)
+ group.add_argument('-d', '--device', help='the serial port to use')
+ parser.add_argument(
+ '-b',
+ '--baudrate',
+ type=int,
+ default=115200,
+ help='the baud rate to use',
+ )
+ group.add_argument(
+ '-s',
+ '--socket-addr',
+ type=str,
+ default="default",
+ help='use socket to connect to server, type default for\
+ localhost:33000, or manually input the server address:port',
+ )
+ parser.add_argument(
+ '-o',
+ '--trace_output',
+ dest='trace_output_file',
+ help=('The json file to which to write the output.'),
+ )
+ parser.add_argument(
+ '-t',
+ '--trace_token_database',
+ help='Databases (ELF, binary, or CSV) to use to lookup trace tokens.',
+ )
+ parser.add_argument(
+ '-f',
+ '--ticks_per_second',
+ type=int,
+ dest='ticks_per_second',
+ help=('The clock rate of the trace events.'),
+ )
+ parser.add_argument(
+ '--time_offset',
+ type=int,
+ dest='time_offset',
+ default=0,
+ help=('Time offset (us) of the trace events (Default 0).'),
+ )
+ parser.add_argument(
+ '--channel-id',
+ type=int,
+ dest='channel_id',
+ default=rpc.DEFAULT_CHANNEL_ID,
+ help="Channel ID used in RPC communications.",
+ )
+ return parser.parse_args()
+
+
+def _main(args) -> int:
+ detokenizer = AutoUpdatingDetokenizer(args.trace_token_database + "#trace")
+ detokenizer.show_errors = True
+
+ socket_impl = SocketClient
+ try:
+ socket_device = socket_impl(args.socket_addr)
+ reader = rpc.SelectableReader(socket_device)
+ write = socket_device.write
+ except ValueError:
+ _LOG.exception('Failed to initialize socket at %s', args.socket_addr)
+ return 1
+
+ protos: List[Union[ModuleType, Path]] = [
+ log_pb2,
+ file_pb2,
+ transfer_pb2,
+ trace_service_pb2,
+ ]
+
+ with reader:
+ device_client = DeviceWithTracing(
+ args.ticks_per_second,
+ args.channel_id,
+ reader,
+ write,
+ protos,
+ detokenizer=detokenizer,
+ timestamp_decoder=None,
+ rpc_timeout_s=5,
+ use_rpc_logging=True,
+ use_hdlc_encoding=True,
+ )
+
+ with device_client:
+ _LOG.info("Starting tracing")
+ start_tracing_on_device(device_client)
+
+ _LOG.info("Stopping tracing")
+ file_id = stop_tracing_on_device(device_client)
+ _LOG.info("Trace file id = %d", file_id.response.file_id)
+
+ _LOG.info("Listing Files")
+ stream_response = list_files_on_device(device_client)
+
+ if not stream_response.status.ok():
+ _LOG.error('Failed to list files %s', stream_response.status)
+ return 1
+
+ for list_response in stream_response.responses:
+ for file in list_response.paths:
+ _LOG.info("Transfering File: %s", file.path)
+ try:
+ data = device_client.transfer_manager.read(file.file_id)
+ events = trace_tokenized.get_trace_events(
+ [detokenizer.database],
+ data,
+ device_client.ticks_per_second,
+ args.time_offset,
+ )
+ json_lines = trace.generate_trace_json(events)
+ trace_tokenized.save_trace_file(
+ json_lines, args.trace_output_file
+ )
+ except pw_transfer.Error as err:
+ print('Failed to read:', err.status)
+
+ _LOG.info("Deleting File: %s", file.path)
+ delete_file_on_device(device_client, file.path)
+
+ _LOG.info("All trace transfers completed successfully")
+
+ return 0
+
+
+if __name__ == '__main__':
+ _main(_parse_args())
diff --git a/pw_system/stl_backends.gni b/pw_system/stl_backends.gni
index 06d901ebd..f8d60a89e 100644
--- a/pw_system/stl_backends.gni
+++ b/pw_system/stl_backends.gni
@@ -36,4 +36,5 @@ PW_SYSTEM_STL_BACKENDS = {
pw_thread_THREAD_ITERATION_BACKEND = "$dir_pw_thread_stl:thread_iteration"
pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield"
pw_system_TARGET_HOOKS_BACKEND = "$dir_pw_system:stl_target_hooks"
+ pw_trace_tokenizer_time = "$dir_pw_trace_tokenized:host_trace_time"
}
diff --git a/pw_system/stl_target_hooks.cc b/pw_system/stl_target_hooks.cc
index 048153d4c..e470a97af 100644
--- a/pw_system/stl_target_hooks.cc
+++ b/pw_system/stl_target_hooks.cc
@@ -12,6 +12,7 @@
// License for the specific language governing permissions and limitations under
// the License.
+#include "pw_system/config.h"
#include "pw_thread/thread.h"
#include "pw_thread_stl/options.h"
@@ -27,6 +28,13 @@ const thread::Options& RpcThreadOptions() {
return rpc_thread_options;
}
+#if PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+const thread::Options& TransferThreadOptions() {
+ static thread::stl::Options transfer_thread_options;
+ return transfer_thread_options;
+}
+#endif // PW_SYSTEM_ENABLE_TRANSFER_SERVICE
+
const thread::Options& WorkQueueThreadOptions() {
static thread::stl::Options work_queue_thread_options;
return work_queue_thread_options;
diff --git a/pw_system/system_target.gni b/pw_system/system_target.gni
index 736dd7427..55dbcf1b7 100644
--- a/pw_system/system_target.gni
+++ b/pw_system/system_target.gni
@@ -30,6 +30,8 @@ import("$dir_pw_toolchain/arm_gcc/toolchains.gni")
import("$dir_pw_toolchain/generate_toolchain.gni")
import("$dir_pw_toolchain/host_clang/toolchains.gni")
import("$dir_pw_toolchain/host_gcc/toolchains.gni")
+import("$dir_pw_trace/backend.gni")
+import("$dir_pw_trace_tokenized/config.gni")
import("$dir_pw_unit_test/test.gni")
import("backend.gni")
import("freertos_backends.gni")
@@ -126,6 +128,8 @@ template("pw_system_target") {
# when RPC is working.
pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"
+ pw_trace_BACKEND = "$dir_pw_trace_tokenized"
+
if (pw_system_USE_MULTI_ENDPOINT_CONFIG) {
pw_system_CONFIG = "$dir_pw_system:multi_endpoint_rpc_config"
}
diff --git a/pw_system/trace_service.cc b/pw_system/trace_service.cc
new file mode 100644
index 000000000..c0a92dcba
--- /dev/null
+++ b/pw_system/trace_service.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_system/trace_service.h"
+
+#include "pw_trace_tokenized/trace_service_pwpb.h"
+#include "pw_trace_tokenized/trace_tokenized.h"
+
+namespace pw::system {
+namespace {
+
+// TODO: b/305795949 - place trace_data in a persistent region of memory
+TracePersistentBuffer trace_data;
+persistent_ram::PersistentBufferWriter trace_data_writer(
+ trace_data.GetWriter());
+
+trace::TraceService trace_service(trace::GetTokenizedTracer(),
+ trace_data_writer);
+
+} // namespace
+
+TracePersistentBuffer& GetTraceData() { return trace_data; }
+
+void RegisterTraceService(rpc::Server& rpc_server, uint32_t transfer_id) {
+ rpc_server.RegisterService(trace_service);
+ trace_service.SetTransferId(transfer_id);
+}
+
+} // namespace pw::system
diff --git a/pw_system/transfer_handlers.cc b/pw_system/transfer_handlers.cc
new file mode 100644
index 000000000..2737f8e8f
--- /dev/null
+++ b/pw_system/transfer_handlers.cc
@@ -0,0 +1,39 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#include "pw_system/transfer_handlers.h"
+
+namespace pw::system {
+
+TracePersistentBufferTransfer::TracePersistentBufferTransfer(
+ uint32_t id, TracePersistentBuffer& persistent_buffer)
+ : transfer::ReadOnlyHandler(id),
+ persistent_buffer_(persistent_buffer),
+ reader_({}) {
+ set_reader(reader_);
+}
+
+Status TracePersistentBufferTransfer::PrepareRead() {
+ if (!persistent_buffer_.has_value()) {
+ return Status::Unavailable();
+ }
+
+ // As seeking is not yet supported, reinitialize the reader from the start
+ // of the snapshot buffer.
+ new (&reader_) stream::MemoryReader(
+ pw::ConstByteSpan{persistent_buffer_.data(), persistent_buffer_.size()});
+
+ return OkStatus();
+}
+
+} // namespace pw::system
diff --git a/pw_system/transfer_service.cc b/pw_system/transfer_service.cc
new file mode 100644
index 000000000..7a68376cc
--- /dev/null
+++ b/pw_system/transfer_service.cc
@@ -0,0 +1,68 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_system/transfer_service.h"
+
+#include "pw_system/file_manager.h"
+
+namespace pw::system {
+namespace {
+// The maximum number of concurrent transfers the thread should support as
+// either a client or a server. These can be set to 0 (if only using one or
+// the other).
+constexpr size_t kMaxConcurrentClientTransfers = 5;
+constexpr size_t kMaxConcurrentServerTransfers = 3;
+
+// The maximum payload size that can be transmitted by the system's
+// transport stack. This would typically be defined within some transport
+// header.
+constexpr size_t kMaxTransmissionUnit = 512;
+
+// The maximum amount of data that should be sent within a single transfer
+// packet. By necessity, this should be less than the max transmission unit.
+//
+// pw_transfer requires some additional per-packet overhead, so the actual
+// amount of data it sends may be lower than this.
+constexpr size_t kMaxTransferChunkSizeBytes = 480;
+
+// In a write transfer, the maximum number of bytes to receive at one time
+// (potentially across multiple chunks), unless specified otherwise by the
+// transfer handler's stream::Writer.
+constexpr size_t kDefaultMaxBytesToReceive = 1024;
+
+// Buffers for storing and encoding chunks (see documentation above).
+std::array<std::byte, kMaxTransferChunkSizeBytes> chunk_buffer;
+std::array<std::byte, kMaxTransmissionUnit> encode_buffer;
+
+transfer::Thread<kMaxConcurrentClientTransfers, kMaxConcurrentServerTransfers>
+ transfer_thread(chunk_buffer, encode_buffer);
+
+transfer::TransferService transfer_service(transfer_thread,
+ kDefaultMaxBytesToReceive);
+} // namespace
+
+void RegisterTransferService(rpc::Server& rpc_server) {
+ rpc_server.RegisterService(transfer_service);
+}
+
+void InitTransferService() {
+ // the handlers need to be registered after the transfer thread has started
+ for (auto handler : GetFileManager().GetTransferHandlers()) {
+ transfer_service.RegisterHandler(*handler);
+ }
+}
+
+transfer::TransferThread& GetTransferThread() { return transfer_thread; }
+
+} // namespace pw::system
diff --git a/pw_thread/py/thread_analyzer_test.py b/pw_thread/py/thread_analyzer_test.py
index 4f380f300..b2104c16c 100644
--- a/pw_thread/py/thread_analyzer_test.py
+++ b/pw_thread/py/thread_analyzer_test.py
@@ -17,6 +17,8 @@
import unittest
from pw_thread.thread_analyzer import ThreadInfo, ThreadSnapshotAnalyzer
from pw_thread_protos import thread_pb2
+import pw_tokenizer
+from pw_tokenizer import tokens
class ThreadInfoTest(unittest.TestCase):
@@ -292,6 +294,91 @@ class ThreadSnapshotAnalyzerTest(unittest.TestCase):
self.assertEqual(analyzer.active_thread(), temp_thread)
self.assertEqual(str(ThreadSnapshotAnalyzer(snapshot)), expected)
+ def test_tokenized_thread_name(self):
+ """Ensures a tokenized thread name is detokenized."""
+ snapshot = thread_pb2.SnapshotThreadInfo()
+ detokenizer = pw_tokenizer.Detokenizer(
+ tokens.Database(
+ [
+ tokens.TokenizedStringEntry(
+ 0x46BE7497, 'The thread for Kuzco'
+ ),
+ ]
+ )
+ )
+
+ temp_thread = thread_pb2.Thread()
+ temp_thread.name = b'\x97\x74\xBE\x46'
+ snapshot.threads.append(temp_thread)
+ temp_thread.name = b'\x5D\xA8\x66\xAE'
+ snapshot.threads.append(temp_thread)
+
+ # pylint: disable=line-too-long
+ expected = '\n'.join(
+ (
+ 'Thread State',
+ ' 2 threads running.',
+ '',
+ 'Thread (UNKNOWN): The thread for Kuzco',
+ 'Est CPU usage: unknown',
+ 'Stack info',
+ ' Current usage: 0x???????? - 0x???????? (size unknown)',
+ ' Est peak usage: size unknown',
+ ' Stack limits: 0x???????? - 0x???????? (size unknown)',
+ '',
+ 'Thread (UNKNOWN): $Xahmrg==',
+ 'Est CPU usage: unknown',
+ 'Stack info',
+ ' Current usage: 0x???????? - 0x???????? (size unknown)',
+ ' Est peak usage: size unknown',
+ ' Stack limits: 0x???????? - 0x???????? (size unknown)',
+ '',
+ )
+ )
+ # pylint: enable=line-too-long
+ analyzer = ThreadSnapshotAnalyzer(snapshot, tokenizer_db=detokenizer)
+
+ # Ensure text dump matches expected contents.
+ self.assertEqual(str(analyzer), expected)
+
+ def test_no_db_tokenized_thread_name(self):
+ """Ensures a tokenized thread name is detokenized."""
+ snapshot = thread_pb2.SnapshotThreadInfo()
+
+ temp_thread = thread_pb2.Thread()
+ temp_thread.name = b'\x97\x74\xBE\x46'
+ snapshot.threads.append(temp_thread)
+ temp_thread.name = b'\x5D\xA8\x66\xAE'
+ snapshot.threads.append(temp_thread)
+
+ # pylint: disable=line-too-long
+ expected = '\n'.join(
+ (
+ 'Thread State',
+ ' 2 threads running.',
+ '',
+ 'Thread (UNKNOWN): $l3S+Rg==',
+ 'Est CPU usage: unknown',
+ 'Stack info',
+ ' Current usage: 0x???????? - 0x???????? (size unknown)',
+ ' Est peak usage: size unknown',
+ ' Stack limits: 0x???????? - 0x???????? (size unknown)',
+ '',
+ 'Thread (UNKNOWN): $Xahmrg==',
+ 'Est CPU usage: unknown',
+ 'Stack info',
+ ' Current usage: 0x???????? - 0x???????? (size unknown)',
+ ' Est peak usage: size unknown',
+ ' Stack limits: 0x???????? - 0x???????? (size unknown)',
+ '',
+ )
+ )
+ # pylint: enable=line-too-long
+ analyzer = ThreadSnapshotAnalyzer(snapshot)
+
+ # Ensure text dump matches expected contents.
+ self.assertEqual(str(analyzer), expected)
+
if __name__ == '__main__':
unittest.main()
diff --git a/pw_tls_client/test_server_test.cc b/pw_tls_client/test_server_test.cc
index 3e76aef85..5a15e92f5 100644
--- a/pw_tls_client/test_server_test.cc
+++ b/pw_tls_client/test_server_test.cc
@@ -90,7 +90,8 @@ void CreateSSLClient(bssl::UniquePtr<SSL_CTX>* ctx,
// Load trust anchors to client
auto store = SSL_CTX_get_cert_store(ctx->get());
- X509_VERIFY_PARAM_clear_flags(store->param, X509_V_FLAG_USE_CHECK_TIME);
+ X509_VERIFY_PARAM_clear_flags(X509_STORE_get0_param(store),
+ X509_V_FLAG_USE_CHECK_TIME);
const pw::ConstByteSpan kTrustAnchors[] = {kRootACert, kRootBCert};
for (auto cert : kTrustAnchors) {
auto res = ParseDerCertificate(cert);
diff --git a/pw_tokenizer/Android.bp b/pw_tokenizer/Android.bp
index 5f1c470d2..d3b1614e3 100644
--- a/pw_tokenizer/Android.bp
+++ b/pw_tokenizer/Android.bp
@@ -167,14 +167,17 @@ filegroup {
genrule {
name: "pw_tokenizer_proto_options_pwpb_h",
- srcs: [":pw_tokenizer_options_proto_with_prefix",],
+ srcs: [
+ ":libprotobuf-internal-protos",
+ ":pw_tokenizer_options_proto_with_prefix",
+ ],
cmd: "python3 $(location pw_protobuf_compiler_py) " +
"--proto-path=external/pigweed/pw_tokenizer/ " +
"--proto-path=external/protobuf/src/ " +
"--out-dir=$$(dirname $(location pw_tokenizer/proto/options.pwpb.h)) " +
"--plugin-path=$(location pw_protobuf_plugin_py) " +
- "--compile-dir=$$(dirname $(in)) " +
- "--sources $(in) " +
+ "--compile-dir=$$(dirname $(location :pw_tokenizer_options_proto_with_prefix)) " +
+ "--sources $(location :pw_tokenizer_options_proto_with_prefix) " +
"--language pwpb " +
"--no-experimental-proto3-optional " +
"--protoc=$(location aprotoc) ",
diff --git a/pw_tokenizer/docs.rst b/pw_tokenizer/docs.rst
index 2b90827c1..01d619a62 100644
--- a/pw_tokenizer/docs.rst
+++ b/pw_tokenizer/docs.rst
@@ -7,8 +7,8 @@ pw_tokenizer
:name: pw_tokenizer
:tagline: Compress strings to shrink logs by +75%
:status: stable
- :languages: C11, C++14, Python, Rust, TypeScript
- :code-size-impact: 50% reduction in binary log size
+ :languages: C++, C11, Python, Rust, TypeScript, Java
+ :code-size-impact: 50% reduction in log size
Logging is critical, but developers are often forced to choose between
additional logging or saving crucial flash space. The ``pw_tokenizer`` module
diff --git a/pw_tokenizer/get_started.rst b/pw_tokenizer/get_started.rst
index f609b4107..f424ed9bb 100644
--- a/pw_tokenizer/get_started.rst
+++ b/pw_tokenizer/get_started.rst
@@ -36,6 +36,8 @@ Here's an overview of what happens when ``pw_tokenizer`` is used:
6. Off-device, the detokenizer tools use the token database to decode the
strings to human-readable form.
+.. _module-pw_tokenizer-get-started-integration:
+
Integrating with Bazel / GN / CMake projects
============================================
Integrating ``pw_tokenizer`` requires a few steps beyond building the code. This
diff --git a/pw_tokenizer/public/pw_tokenizer/config.h b/pw_tokenizer/public/pw_tokenizer/config.h
index 23768540c..6557907c0 100644
--- a/pw_tokenizer/public/pw_tokenizer/config.h
+++ b/pw_tokenizer/public/pw_tokenizer/config.h
@@ -58,3 +58,13 @@
#ifndef PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES
#define PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES 52
#endif // PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES
+
+// This character is used to mark the start of all tokenized messages. For
+// consistency, it is recommended to always use $ if possible.
+// If required, a different non-Base64 character may be used as a prefix.
+//
+// A string version of the character is required for format-string-literal
+// concatenation.
+#ifndef PW_TOKENIZER_NESTED_PREFIX_STR
+#define PW_TOKENIZER_NESTED_PREFIX_STR "$"
+#endif // PW_TOKENIZER_NESTED_PREFIX_STR
diff --git a/pw_tokenizer/public/pw_tokenizer/nested_tokenization.h b/pw_tokenizer/public/pw_tokenizer/nested_tokenization.h
index ab720c703..6c6fe25e5 100644
--- a/pw_tokenizer/public/pw_tokenizer/nested_tokenization.h
+++ b/pw_tokenizer/public/pw_tokenizer/nested_tokenization.h
@@ -17,13 +17,8 @@
#include <inttypes.h>
-// This character is used to mark the start of all tokenized messages. For
-// consistency, it is recommended to always use $ if possible.
-// If required, a different non-Base64 character may be used as a prefix.
-//
-// A string version of the character is required for format-string-literal
-// concatenation.
-#define PW_TOKENIZER_NESTED_PREFIX_STR "$"
+#include "pw_tokenizer/config.h"
+
#define PW_TOKENIZER_NESTED_PREFIX PW_TOKENIZER_NESTED_PREFIX_STR[0]
/// Format specifier for a token argument.
diff --git a/pw_tokenizer/rust/BUILD.bazel b/pw_tokenizer/rust/BUILD.bazel
index bdaefed94..862f01149 100644
--- a/pw_tokenizer/rust/BUILD.bazel
+++ b/pw_tokenizer/rust/BUILD.bazel
@@ -22,7 +22,7 @@ rust_proc_macro(
visibility = ["//visibility:public"],
deps = [
":pw_tokenizer_core",
- ":pw_tokenizer_printf",
+ "//pw_format/rust:pw_format",
"//pw_status/rust:pw_status",
"@rust_crates//:proc-macro2",
"@rust_crates//:quote",
@@ -60,34 +60,6 @@ rust_doc(
)
rust_library(
- name = "pw_tokenizer_printf",
- srcs = [
- "pw_tokenizer_printf/lib.rs",
- "pw_tokenizer_printf/tests.rs",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//pw_status/rust:pw_status",
- "@rust_crates//:nom",
- ],
-)
-
-rust_test(
- name = "pw_tokenizer_printf_test",
- crate = ":pw_tokenizer_printf",
-)
-
-rust_doc_test(
- name = "pw_tokenizer_printf_doc_test",
- crate = ":pw_tokenizer_printf",
-)
-
-rust_doc(
- name = "pw_tokenizer_printf_doc",
- crate = ":pw_tokenizer_printf",
-)
-
-rust_library(
name = "pw_tokenizer",
srcs = [
"pw_tokenizer/internal.rs",
diff --git a/pw_tokenizer/rust/pw_tokenizer/lib.rs b/pw_tokenizer/rust/pw_tokenizer/lib.rs
index 1700fdd86..7909f124b 100644
--- a/pw_tokenizer/rust/pw_tokenizer/lib.rs
+++ b/pw_tokenizer/rust/pw_tokenizer/lib.rs
@@ -56,6 +56,7 @@ pub mod internal;
// name.
pub mod __private {
pub use crate::*;
+ pub use pw_status::Result;
pub use pw_stream::{Cursor, Seek, WriteInteger, WriteVarint};
pub use pw_tokenizer_macro::{_token, _tokenize_to_buffer};
}
@@ -200,7 +201,16 @@ mod tests {
fn test_misc_integer_format() {
// %d, %i, %o, %u, %x, %X all encode integers the same.
tokenize_to_buffer_test!(
- &[0x57, 0x88, 0xc5, 0xd8, 0x2], // expected buffer
+ &[0x52, 0x1c, 0xb0, 0x4c, 0x2], // expected buffer
+ 64, // buffer size
+ "The answer is %d!",
+ 1
+ );
+
+ // Because %i is an alias for %d, it gets converted to a %d by the
+ // `pw_format` macro infrastructure.
+ tokenize_to_buffer_test!(
+ &[0x52, 0x1c, 0xb0, 0x4c, 0x2], // expected buffer
64, // buffer size
"The answer is %i!",
1
@@ -210,28 +220,28 @@ mod tests {
&[0x5d, 0x70, 0x12, 0xb4, 0x2], // expected buffer
64, // buffer size
"The answer is %o!",
- 1
+ 1u32
);
tokenize_to_buffer_test!(
&[0x63, 0x58, 0x5f, 0x8f, 0x2], // expected buffer
64, // buffer size
"The answer is %u!",
- 1
+ 1u32
);
tokenize_to_buffer_test!(
&[0x66, 0xcc, 0x05, 0x7d, 0x2], // expected buffer
64, // buffer size
"The answer is %x!",
- 1
+ 1u32
);
tokenize_to_buffer_test!(
&[0x46, 0x4c, 0x16, 0x96, 0x2], // expected buffer
64, // buffer size
"The answer is %X!",
- 1
+ 1u32
);
}
diff --git a/pw_tokenizer/rust/pw_tokenizer_macro.rs b/pw_tokenizer/rust/pw_tokenizer_macro.rs
index d22a10ef9..b271ec433 100644
--- a/pw_tokenizer/rust/pw_tokenizer_macro.rs
+++ b/pw_tokenizer/rust/pw_tokenizer_macro.rs
@@ -15,37 +15,21 @@
// This proc macro crate is a private API for the `pw_tokenizer` crate.
#![doc(hidden)]
-use std::collections::VecDeque;
use std::ffi::CString;
use proc_macro::TokenStream;
-use quote::{format_ident, quote, ToTokens};
+use proc_macro2::Ident;
+use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
- parse_macro_input,
- punctuated::Punctuated,
- Expr, LitStr, Token,
+ parse_macro_input, Expr, LitStr, Token,
};
+use pw_format::macros::{generate_printf, FormatAndArgs, PrintfFormatMacroGenerator, Result};
use pw_tokenizer_core::{hash_string, TOKENIZER_ENTRY_MAGIC};
-use pw_tokenizer_printf as printf;
type TokenStream2 = proc_macro2::TokenStream;
-struct Error {
- text: String,
-}
-
-impl Error {
- fn new(text: &str) -> Self {
- Self {
- text: text.to_string(),
- }
- }
-}
-
-type Result<T> = core::result::Result<T, Error>;
-
// Handles tokenizing (hashing) `string` and adding it to the token database
// with the specified `domain`. A detailed description of what's happening is
// found in the docs for [`pw_tokenizer::token`] macro.
@@ -54,7 +38,7 @@ fn token_backend(domain: &str, string: &str) -> TokenStream2 {
// Line number is omitted as getting that info requires an experimental API:
// https://doc.rust-lang.org/proc_macro/struct.Span.html#method.start
- let ident = format_ident!("_pw_tokenizer_string_entry_{:08X}", hash);
+ let ident = format_ident!("_PW_TOKENIZER_STRING_ENTRY_{:08X}", hash);
// pw_tokenizer is intended for use with ELF files only. Mach-O files (macOS
// executables) do not support section names longer than 16 characters, so a
@@ -113,130 +97,97 @@ pub fn _token(tokens: TokenStream) -> TokenStream {
// Args to tokenize to buffer that are parsed according to the pattern:
// ($buffer:expr, $format_string:literal, $($args:expr),*)
#[derive(Debug)]
-struct TokenizeToBuffer {
+struct TokenizeToBufferArgs {
buffer: Expr,
- format_string: LitStr,
- args: VecDeque<Expr>,
+ format_and_args: FormatAndArgs,
}
-impl Parse for TokenizeToBuffer {
+impl Parse for TokenizeToBufferArgs {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let buffer: Expr = input.parse()?;
input.parse::<Token![,]>()?;
- let format_string: LitStr = input.parse()?;
-
- let args = if input.is_empty() {
- // If there are no more tokens, no arguments were specified.
- VecDeque::new()
- } else {
- // Eat the `,` following the format string.
- input.parse::<Token![,]>()?;
-
- let punctuated = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
- punctuated.into_iter().collect()
- };
+ let format_and_args: FormatAndArgs = input.parse()?;
- Ok(TokenizeToBuffer {
+ Ok(TokenizeToBufferArgs {
buffer,
- format_string,
- args,
+ format_and_args,
})
}
}
-// Grab the next argument returning a descriptive error if no more args are left.
-fn next_arg(spec: &printf::ConversionSpec, args: &mut VecDeque<Expr>) -> Result<Expr> {
- args.pop_front()
- .ok_or_else(|| Error::new(&format!("No argument given for {spec:?}")))
+struct TokenizeToBufferGenerator<'a> {
+ domain: &'a str,
+ buffer: &'a Expr,
+ encoding_fragments: Vec<TokenStream2>,
}
-// Handle a single format conversion specifier (i.e. `%08x`). Grabs the
-// necessary arguments for the specifier from `args` and generates code
-// to marshal the arguments into the buffer declared in `_tokenize_to_buffer`.
-// Returns an error if args is too short of if a format specifier is unsupported.
-fn handle_conversion(
- spec: &printf::ConversionSpec,
- args: &mut VecDeque<Expr>,
-) -> Result<TokenStream2> {
- match spec.specifier {
- printf::Specifier::Decimal
- | printf::Specifier::Integer
- | printf::Specifier::Octal
- | printf::Specifier::Unsigned
- | printf::Specifier::Hex
- | printf::Specifier::UpperHex => {
- // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
- if spec.min_field_width == printf::MinFieldWidth::Variable {
- return Err(Error::new(
- "Variable width '*' integer formats are not supported.",
- ));
- }
+impl<'a> TokenizeToBufferGenerator<'a> {
+ fn new(domain: &'a str, buffer: &'a Expr) -> Self {
+ Self {
+ domain,
+ buffer,
+ encoding_fragments: Vec::new(),
+ }
+ }
+}
- if spec.precision == printf::Precision::Variable {
- return Err(Error::new(
- "Variable precision '*' integer formats are not supported.",
- ));
+impl<'a> PrintfFormatMacroGenerator for TokenizeToBufferGenerator<'a> {
+ fn finalize(self, format_string: String) -> Result<TokenStream2> {
+ // Locally scoped aliases so we can refer to them in `quote!()`
+ let buffer = self.buffer;
+ let encoding_fragments = self.encoding_fragments;
+
+ // `token_backend` returns a `TokenStream2` which both inserts the
+ // string into the token database and returns the hash value.
+ let token = token_backend(self.domain, &format_string);
+
+ Ok(quote! {
+ {
+ // Wrapping code in an internal function to allow `?` to work in
+ // functions that don't return Results.
+ fn _pw_tokenizer_internal_encode(
+ buffer: &mut [u8],
+ token: u32
+ ) -> __pw_tokenizer_crate::Result<usize> {
+ // use pw_tokenizer's private re-export of these pw_stream bits to
+ // allow referencing with needing `pw_stream` in scope.
+ use __pw_tokenizer_crate::{Cursor, Seek, WriteInteger, WriteVarint};
+ let mut cursor = Cursor::new(buffer);
+ cursor.write_u32_le(&token)?;
+ #(#encoding_fragments);*;
+ Ok(cursor.stream_position()? as usize)
}
+ _pw_tokenizer_internal_encode(#buffer, #token)
+ }
+ })
+ }
- let arg = next_arg(spec, args)?;
- let bits = match spec.length.unwrap_or(printf::Length::Long) {
- printf::Length::Char => 8,
- printf::Length::Short => 16,
- printf::Length::Long => 32,
- printf::Length::LongLong => 64,
- printf::Length::IntMax => 64,
- printf::Length::Size => 32,
- printf::Length::PointerDiff => 32,
- printf::Length::LongDouble => {
- return Err(Error::new(
- "Long double length parameter invalid for integer formats",
- ))
- }
- };
- let ty = format_ident!("i{bits}");
- Ok(quote! {
- // pw_tokenizer always uses signed packing for all integers.
- cursor.write_signed_varint(#ty::from(#arg) as i64)?;
- })
- }
- printf::Specifier::String => {
- // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
- if spec.min_field_width == printf::MinFieldWidth::Variable {
- return Err(Error::new(
- "Variable width '*' string formats are not supported.",
- ));
- }
+ fn string_fragment(&mut self, _string: &str) -> Result<()> {
+ // String fragments are encoded directly into the format string.
+ Ok(())
+ }
- if spec.precision == printf::Precision::Variable {
- return Err(Error::new(
- "Variable precision '*' string formats are not supported.",
- ));
- }
+ fn integer_conversion(&mut self, ty: Ident, expression: Expr) -> Result<Option<String>> {
+ self.encoding_fragments.push(quote! {
+ // pw_tokenizer always uses signed packing for all integers.
+ cursor.write_signed_varint(#ty::from(#expression) as i64)?;
+ });
- let arg = next_arg(spec, args)?;
- Ok(quote! {
- let mut buffer = __pw_tokenizer_crate::internal::encode_string(&mut cursor, #arg)?;
- })
- }
- printf::Specifier::Char => {
- let arg = next_arg(spec, args)?;
- Ok(quote! {
- cursor.write_u8_le(&u8::from(#arg))?;
- })
- }
+ Ok(None)
+ }
- printf::Specifier::Double
- | printf::Specifier::UpperDouble
- | printf::Specifier::Exponential
- | printf::Specifier::UpperExponential
- | printf::Specifier::SmallDouble
- | printf::Specifier::UpperSmallDouble => {
- // TODO: b/281862328 - Support floating point numbers.
- Err(Error::new("Floating point numbers are not supported."))
- }
+ fn string_conversion(&mut self, expression: Expr) -> Result<Option<String>> {
+ self.encoding_fragments.push(quote! {
+ __pw_tokenizer_crate::internal::encode_string(&mut cursor, #expression)?;
+ });
+ Ok(None)
+ }
- // TODO: b/281862333 - Support pointers.
- printf::Specifier::Pointer => Err(Error::new("Pointer types are not supported.")),
+ fn char_conversion(&mut self, expression: Expr) -> Result<Option<String>> {
+ self.encoding_fragments.push(quote! {
+ cursor.write_u8_le(&u8::from(#expression))?;
+ });
+ Ok(None)
}
}
@@ -247,69 +198,15 @@ fn handle_conversion(
// fill the buffer incrementally.
#[proc_macro]
pub fn _tokenize_to_buffer(tokens: TokenStream) -> TokenStream {
- let input = parse_macro_input!(tokens as TokenizeToBuffer);
- let token = token_backend("", &input.format_string.value());
- let buffer = input.buffer;
+ let input = parse_macro_input!(tokens as TokenizeToBufferArgs);
- let format_string = input.format_string.value();
+ // Hard codes domain to "".
+ let generator = TokenizeToBufferGenerator::new("", &input.buffer);
- let format = match printf::FormatString::parse(&format_string) {
- Ok(format) => format,
- Err(e) => {
- return syn::Error::new_spanned(
- input.format_string.to_token_stream(),
- format!("Error parsing format string {e}"),
- )
- .to_compile_error()
- .into()
- }
- };
- let mut args = input.args;
- let mut arg_encodings = Vec::new();
-
- let mut errors = Vec::new();
-
- for fragment in format.fragments {
- if let printf::FormatFragment::Conversion(spec) = fragment {
- match handle_conversion(&spec, &mut args) {
- Ok(encoding) => arg_encodings.push(encoding),
- Err(e) => errors.push(syn::Error::new_spanned(
- input.format_string.to_token_stream(),
- e.text,
- )),
- }
- }
+ match generate_printf(generator, input.format_and_args) {
+ Ok(token_stream) => token_stream.into(),
+ Err(e) => e.to_compile_error().into(),
}
-
- if !errors.is_empty() {
- return errors
- .into_iter()
- .reduce(|mut accumulated_errors, error| {
- accumulated_errors.combine(error);
- accumulated_errors
- })
- .expect("errors should not be empty")
- .to_compile_error()
- .into();
- }
-
- let code = quote! {
- {
- // Wrapping code in an internal function to allow `?` to work in
- // functions that don't return Results.
- fn _pw_tokenizer_internal_encode(buffer: &mut [u8], token: u32) -> pw_status::Result<usize> {
- // use pw_tokenizer's private re-export of these pw_stream bits to
- // allow referencing with needing `pw_stream` in scope.
- use __pw_tokenizer_crate::{Cursor, Seek, WriteInteger, WriteVarint};
- let mut cursor = Cursor::new(buffer);
- cursor.write_u32_le(&token)?;
- #(#arg_encodings);*;
- Ok(cursor.stream_position()? as usize)
- }
- _pw_tokenizer_internal_encode(#buffer, #token)
- }
- };
- code.into()
}
// Macros tested in `pw_tokenizer` crate.
diff --git a/pw_tokenizer/tokenize.cc b/pw_tokenizer/tokenize.cc
index dbe3fd696..c4dcf4798 100644
--- a/pw_tokenizer/tokenize.cc
+++ b/pw_tokenizer/tokenize.cc
@@ -27,6 +27,9 @@ namespace pw {
namespace tokenizer {
namespace {
+static_assert(sizeof(PW_TOKENIZER_NESTED_PREFIX_STR) == 2,
+ "The nested prefix must be a single character string");
+
// Store metadata about this compilation's string tokenization in the ELF.
//
// The tokenizer metadata will not go into the on-device executable binary code.
diff --git a/pw_toolchain/BUILD.bazel b/pw_toolchain/BUILD.bazel
index 15510fdd1..512ea27e1 100644
--- a/pw_toolchain/BUILD.bazel
+++ b/pw_toolchain/BUILD.bazel
@@ -42,4 +42,5 @@ pw_cc_library(
srcs = ["wrap_abort.cc"],
linkopts = ["-Wl,--wrap=abort"],
deps = ["//pw_assert"],
+ alwayslink = 1,
)
diff --git a/pw_toolchain/BUILD.gn b/pw_toolchain/BUILD.gn
index a62608714..c0cff8a94 100644
--- a/pw_toolchain/BUILD.gn
+++ b/pw_toolchain/BUILD.gn
@@ -45,6 +45,10 @@ config("public_include_path") {
include_dirs = [ "public" ]
}
+group("builtins") {
+ deps = [ "$dir_pw_third_party/llvm_builtins" ]
+}
+
pw_doc_group("docs") {
sources = [ "docs.rst" ]
}
diff --git a/pw_toolchain/arm_clang/toolchains.gni b/pw_toolchain/arm_clang/toolchains.gni
index 9bce69c9c..b1e824baa 100644
--- a/pw_toolchain/arm_clang/toolchains.gni
+++ b/pw_toolchain/arm_clang/toolchains.gni
@@ -14,6 +14,7 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/defaults.gni")
import("$dir_pw_toolchain/clang_tools.gni")
# Specifies the tools used by host Clang toolchains.
@@ -75,168 +76,192 @@ pw_toolchain_arm_clang = {
name = "arm_clang_cortex_m0plus_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m0plus + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m0plus_speed_optimized = {
name = "arm_clang_cortex_m0plus_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m0plus + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m0plus_size_optimized = {
name = "arm_clang_cortex_m0plus_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m0plus + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m3_debug = {
name = "arm_clang_cortex_m3_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m3 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m3 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m3_speed_optimized = {
name = "arm_clang_cortex_m3_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m3 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m3 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m3_size_optimized = {
name = "arm_clang_cortex_m3_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m3 + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m3 +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m4_debug = {
name = "arm_clang_cortex_m4_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m4 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m4_speed_optimized = {
name = "arm_clang_cortex_m4_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m4 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m4_size_optimized = {
name = "arm_clang_cortex_m4_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4 + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m4 +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m4f_debug = {
name = "arm_clang_cortex_m4f_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m4f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m4f_speed_optimized = {
name = "arm_clang_cortex_m4f_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m4f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m4f_size_optimized = {
name = "arm_clang_cortex_m4f_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m4f + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m4f +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m7_debug = {
name = "arm_clang_cortex_m7_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m7 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m7_speed_optimized = {
name = "arm_clang_cortex_m7_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m7 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m7_size_optimized = {
name = "arm_clang_cortex_m7_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7 + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m7 +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m7f_debug = {
name = "arm_clang_cortex_m7f_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m7f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m7f_speed_optimized = {
name = "arm_clang_cortex_m7f_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m7f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m7f_size_optimized = {
name = "arm_clang_cortex_m7f_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m7f + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m7f +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m33_debug = {
name = "arm_clang_cortex_m33_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m33 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m33_speed_optimized = {
name = "arm_clang_cortex_m33_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m33 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m33_size_optimized = {
name = "arm_clang_cortex_m33_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33 + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m33 +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
cortex_m33f_debug = {
name = "arm_clang_cortex_m33f_debug"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _cortex_m33f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m33f_speed_optimized = {
name = "arm_clang_cortex_m33f_speed_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _cortex_m33f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m33f_size_optimized = {
name = "arm_clang_cortex_m33f_size_optimized"
forward_variables_from(_arm_clang_toolchain, "*")
defaults = {
- default_configs = _cortex_m33f + [ "$dir_pw_build:optimize_size_clang" ]
+ default_configs = pigweed_default_configs + _cortex_m33f +
+ [ "$dir_pw_build:optimize_size_clang" ]
}
}
}
diff --git a/pw_toolchain/arm_gcc/BUILD.bazel b/pw_toolchain/arm_gcc/BUILD.bazel
index ff3ea3be4..e93e104b0 100644
--- a/pw_toolchain/arm_gcc/BUILD.bazel
+++ b/pw_toolchain/arm_gcc/BUILD.bazel
@@ -35,6 +35,7 @@ pw_cc_library(
],
visibility = ["//visibility:public"],
deps = ["//pw_assert"],
+ alwayslink = 1,
)
pw_cc_library(
@@ -46,6 +47,21 @@ pw_cc_library(
],
)
+# Although we use similar warnings for clang and arm_gcc, we don't have one
+# centralized list, since we might want to use different warnings based on the
+# compiler in the future.
+pw_cc_toolchain_feature(
+ name = "warnings",
+ copts = [
+ "-Wall",
+ "-Wextra",
+ # Make all warnings errors, except for the exemptions below.
+ "-Werror",
+ "-Wno-error=cpp", # preprocessor #warning statement
+ "-Wno-error=deprecated-declarations", # [[deprecated]] attribute
+ ],
+)
+
pw_cc_toolchain_feature(
name = "sysroot",
builtin_sysroot = "external/gcc_arm_none_eabi_toolchain",
@@ -68,7 +84,6 @@ pw_cc_toolchain_feature(
],
copts = [
"-ffreestanding",
- "-fno-common",
"-Wno-psabi",
"-specs=nano.specs",
"-specs=nosys.specs",
@@ -187,6 +202,8 @@ pw_cc_toolchain_feature(
feature_deps = [
"@pw_toolchain//features:o2",
"@pw_toolchain//features:c++17",
+ "@pw_toolchain//features:debugging",
+ "@pw_toolchain//features:reduced_size",
"@pw_toolchain//features:no_canonical_prefixes",
"@pw_toolchain//features:no_rtti",
"@pw_toolchain//features:wno_register",
@@ -194,6 +211,7 @@ pw_cc_toolchain_feature(
":" + mcpu,
":sysroot",
":cortex_common",
+ ":warnings",
],
gcc = "@gcc_arm_none_eabi_toolchain//:bin/arm-none-eabi-gcc",
gcov = "@gcc_arm_none_eabi_toolchain//:bin/arm-none-eabi-gcov",
@@ -202,6 +220,7 @@ pw_cc_toolchain_feature(
linker_files = "@gcc_arm_none_eabi_toolchain//:all",
objcopy = "@gcc_arm_none_eabi_toolchain//:bin/arm-none-eabi-objcopy",
objcopy_files = "@gcc_arm_none_eabi_toolchain//:all",
+ objdump = "@gcc_arm_none_eabi_toolchain//:bin/arm-none-eabi-objdump",
strip = "@gcc_arm_none_eabi_toolchain//:bin/arm-none-eabi-strip",
strip_files = "@gcc_arm_none_eabi_toolchain//:all",
supports_param_files = 0,
diff --git a/pw_toolchain/arm_gcc/BUILD.gn b/pw_toolchain/arm_gcc/BUILD.gn
index 82770538a..f0a9d7e3c 100644
--- a/pw_toolchain/arm_gcc/BUILD.gn
+++ b/pw_toolchain/arm_gcc/BUILD.gn
@@ -16,6 +16,7 @@ import("//build_overrides/pigweed.gni")
import("//build_overrides/pigweed_environment.gni")
import("$dir_pw_build/target_types.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
# Disable obnoxious ABI warning.
#
@@ -156,8 +157,13 @@ pw_source_set("newlib_os_interface_stubs") {
# Basic libraries any arm-none-eabi-gcc target should use. This library should
# be included in pw_build_LINK_DEPS.
group("arm_none_eabi_gcc_support") {
- deps = [
- ":newlib_os_interface_stubs",
- "$dir_pw_toolchain:wrap_abort",
- ]
+ deps = [ "$dir_pw_toolchain:wrap_abort" ]
+
+ # TODO: b/301079199 - Stubs are not yet usable on clang.
+ # AttempedToInvokeUnsupportedNewlibOsInterfaceFunction (write_) is triggering,
+ # but not sure why yet.
+ # TODO: b/301262374 - Provide a better way to detect the compiler type.
+ if (get_path_info(pw_toolchain_SCOPE.cc, "file") != "clang") {
+ deps += [ ":newlib_os_interface_stubs" ]
+ }
}
diff --git a/pw_toolchain/arm_gcc/toolchains.gni b/pw_toolchain/arm_gcc/toolchains.gni
index 47f6d1648..48fc3ab46 100644
--- a/pw_toolchain/arm_gcc/toolchains.gni
+++ b/pw_toolchain/arm_gcc/toolchains.gni
@@ -14,6 +14,8 @@
import("//build_overrides/pigweed.gni")
import("//build_overrides/pigweed_environment.gni")
+
+import("$dir_pw_build/defaults.gni")
import("$dir_pw_toolchain/rbe.gni")
_default_compiler_prefix = ""
@@ -155,6 +157,7 @@ _cortex_m33f = [
_cortex_a32 = [
"$dir_pw_toolchain/arm_gcc:cortex_common",
+ "$dir_pw_toolchain/arm_gcc:cortex_common",
"$dir_pw_toolchain/arm_gcc:cortex_a32",
]
@@ -164,192 +167,192 @@ pw_toolchain_arm_gcc = {
name = "arm_gcc_cortex_m0plus_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m0plus + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m0plus_speed_optimized = {
name = "arm_gcc_cortex_m0plus_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m0plus + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m0plus_size_optimized = {
name = "arm_gcc_cortex_m0plus_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m0plus + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m0plus +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m3_debug = {
name = "arm_gcc_cortex_m3_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m3 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m3_speed_optimized = {
name = "arm_gcc_cortex_m3_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m3 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m3_size_optimized = {
name = "arm_gcc_cortex_m3_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m3 +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m4_debug = {
name = "arm_gcc_cortex_m4_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m4_speed_optimized = {
name = "arm_gcc_cortex_m4_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m4_size_optimized = {
name = "arm_gcc_cortex_m4_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4 +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m4f_debug = {
name = "arm_gcc_cortex_m4f_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m4f_speed_optimized = {
name = "arm_gcc_cortex_m4f_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m4f_size_optimized = {
name = "arm_gcc_cortex_m4f_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m4f +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m7_debug = {
name = "arm_gcc_cortex_m7_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m7_speed_optimized = {
name = "arm_gcc_cortex_m7_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m7_size_optimized = {
name = "arm_gcc_cortex_m7_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7 + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7 +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m7f_debug = {
name = "arm_gcc_cortex_m7f_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m7f_speed_optimized = {
name = "arm_gcc_cortex_m7f_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m7f_size_optimized = {
name = "arm_gcc_cortex_m7f_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m7f + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m7f +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m33_debug = {
name = "arm_gcc_cortex_m33_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m33_speed_optimized = {
name = "arm_gcc_cortex_m33_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m33_size_optimized = {
name = "arm_gcc_cortex_m33_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33 + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33 +
+ [ "$dir_pw_build:optimize_size" ]
}
}
cortex_m33f_debug = {
name = "arm_gcc_cortex_m33f_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33f + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33f +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_m33f_speed_optimized = {
name = "arm_gcc_cortex_m33f_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33f + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33f +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_m33f_size_optimized = {
name = "arm_gcc_cortex_m33f_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_m33f + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_m33f +
+ [ "$dir_pw_build:optimize_size" ]
}
}
@@ -357,24 +360,24 @@ pw_toolchain_arm_gcc = {
name = "arm_gcc_cortex_a32_debug"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_a32 + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_a32 +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
cortex_a32_speed_optimized = {
name = "arm_gcc_cortex_a32_speed_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_a32 + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_a32 +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
cortex_a32_size_optimized = {
name = "arm_gcc_cortex_a32_size_optimized"
forward_variables_from(arm_gcc_toolchain_tools, "*")
defaults = {
- default_configs =
- _arm_gcc + _cortex_a32 + [ "$dir_pw_build:optimize_size" ]
+ default_configs = pigweed_default_configs + _arm_gcc + _cortex_a32 +
+ [ "$dir_pw_build:optimize_size" ]
}
}
}
diff --git a/pw_toolchain/docs.rst b/pw_toolchain/docs.rst
index a9b05a53c..08d5a400d 100644
--- a/pw_toolchain/docs.rst
+++ b/pw_toolchain/docs.rst
@@ -231,3 +231,10 @@ using ``pw_toolchain/arm_gcc:newlib_os_interface_stubs``.
pw_toolchain/no_destructor.h
============================
.. doxygenclass:: pw::NoDestructor
+
+builtins
+========
+builtins are LLVM's equivalent of libgcc, the compiler will insert calls to
+these routines. Setting the ``dir_pw_third_party_builtins`` gn var to your
+compiler-rt/builtins checkout will enable building builtins from source instead
+of relying on the shipped libgcc.
diff --git a/pw_toolchain/host_clang/BUILD.bazel b/pw_toolchain/host_clang/BUILD.bazel
index b11451766..7f8009787 100644
--- a/pw_toolchain/host_clang/BUILD.bazel
+++ b/pw_toolchain/host_clang/BUILD.bazel
@@ -14,6 +14,9 @@
load(
"@pw_toolchain//cc_toolchain:defs.bzl",
+ "ALL_CPP_COMPILER_ACTIONS",
+ "ALL_C_COMPILER_ACTIONS",
+ "pw_cc_flag_set",
"pw_cc_toolchain",
"pw_cc_toolchain_feature",
)
@@ -53,6 +56,30 @@ pw_cc_toolchain_feature(
],
)
+# Although we use similar warnings for clang and arm_gcc, we don't have one
+# centralized list, since we might want to use different warnings based on the
+# compiler in the future.
+pw_cc_flag_set(
+ name = "warnings",
+ actions = ALL_C_COMPILER_ACTIONS + ALL_CPP_COMPILER_ACTIONS,
+ flags = [
+ "-Wall",
+ "-Wextra",
+ # Make all warnings errors, except for the exemptions below.
+ "-Werror",
+ "-Wno-error=cpp", # preprocessor #warning statement
+ "-Wno-error=deprecated-declarations", # [[deprecated]] attribute
+ ],
+)
+
+pw_cc_flag_set(
+ name = "no_unknown_warning_option",
+ actions = ALL_C_COMPILER_ACTIONS + ALL_CPP_COMPILER_ACTIONS,
+ flags = [
+ "-Wno-unknown-warning-option",
+ ],
+)
+
filegroup(
name = "all_linux_files",
srcs = [
@@ -65,6 +92,9 @@ pw_cc_toolchain(
name = "host_toolchain_macos",
abi_libc_version = "unknown",
abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ],
all_files = "@llvm_toolchain//:all",
ar = "@llvm_toolchain//:bin/llvm-ar",
ar_files = "@llvm_toolchain//:all",
@@ -79,6 +109,8 @@ pw_cc_toolchain(
":macos_stdlib",
"@pw_toolchain//features/macos:macos_sysroot",
"@pw_toolchain//features:c++17",
+ "@pw_toolchain//features:debugging",
+ "@pw_toolchain//features:reduced_size",
"@pw_toolchain//features:no_canonical_prefixes",
"@pw_toolchain//features:no_rtti",
"@pw_toolchain//features:wno_register",
@@ -91,6 +123,7 @@ pw_cc_toolchain(
linker_files = "@llvm_toolchain//:all",
objcopy = "@llvm_toolchain//:bin/llvm-objcopy",
objcopy_files = "@llvm_toolchain//:all",
+ objdump = "@llvm_toolchain//:bin/llvm-objdump",
strip = "@llvm_toolchain//:bin/llvm-strip",
strip_files = "@llvm_toolchain//:all",
supports_param_files = 0,
@@ -119,6 +152,9 @@ pw_cc_toolchain(
name = "host_toolchain_linux",
abi_libc_version = "unknown",
abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ],
all_files = ":all_linux_files",
ar = "@llvm_toolchain//:bin/llvm-ar",
ar_files = ":all_linux_files",
@@ -131,6 +167,8 @@ pw_cc_toolchain(
feature_deps = [
":linux_sysroot",
"@pw_toolchain//features:c++17",
+ "@pw_toolchain//features:debugging",
+ "@pw_toolchain//features:reduced_size",
"@pw_toolchain//features:no_canonical_prefixes",
"@pw_toolchain//features:no_rtti",
"@pw_toolchain//features:wno_register",
@@ -143,6 +181,53 @@ pw_cc_toolchain(
linker_files = ":all_linux_files",
objcopy = "@llvm_toolchain//:bin/llvm-objcopy",
objcopy_files = ":all_linux_files",
+ objdump = "@llvm_toolchain//:bin/llvm-objdump",
+ strip = "@llvm_toolchain//:bin/llvm-strip",
+ strip_files = ":all_linux_files",
+ supports_param_files = 0,
+ target_cpu = "unknown",
+ target_libc = "unknown",
+ target_system_name = "unknown",
+ toolchain_identifier = "host-toolchain-linux",
+)
+
+# A toolchain for Kythe. Identical to the regular Linux toolchain except for
+# one extra feature, ":no_unknown_warning_option".
+pw_cc_toolchain(
+ name = "host_toolchain_linux_kythe",
+ abi_libc_version = "unknown",
+ abi_version = "unknown",
+ action_config_flag_sets = [
+ ":warnings",
+ ":no_unknown_warning_option",
+ ],
+ all_files = ":all_linux_files",
+ ar = "@llvm_toolchain//:bin/llvm-ar",
+ ar_files = ":all_linux_files",
+ as_files = ":all_linux_files",
+ compiler = "unknown",
+ compiler_files = ":all_linux_files",
+ coverage_files = ":all_linux_files",
+ cpp = "@llvm_toolchain//:bin/clang++",
+ dwp_files = ":all_linux_files",
+ feature_deps = [
+ ":linux_sysroot",
+ "@pw_toolchain//features:c++17",
+ "@pw_toolchain//features:debugging",
+ "@pw_toolchain//features:reduced_size",
+ "@pw_toolchain//features:no_canonical_prefixes",
+ "@pw_toolchain//features:no_rtti",
+ "@pw_toolchain//features:wno_register",
+ "@pw_toolchain//features:wnon_virtual_dtor",
+ ],
+ gcc = "@llvm_toolchain//:bin/clang",
+ gcov = "@llvm_toolchain//:bin/llvm-cov",
+ host_system_name = "unknown",
+ ld = "@llvm_toolchain//:bin/clang++",
+ linker_files = ":all_linux_files",
+ objcopy = "@llvm_toolchain//:bin/llvm-objcopy",
+ objcopy_files = ":all_linux_files",
+ objdump = "@llvm_toolchain//:bin/llvm-objdump",
strip = "@llvm_toolchain//:bin/llvm-strip",
strip_files = ":all_linux_files",
supports_param_files = 0,
@@ -163,3 +248,18 @@ toolchain(
toolchain = ":host_toolchain_linux",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)
+
+toolchain(
+ name = "host_cc_toolchain_linux_kythe",
+ exec_compatible_with = [
+ "@platforms//os:linux",
+ ],
+ target_compatible_with = [
+ "@platforms//os:linux",
+ ],
+ # This toolchain will only be selected in toolchain resolution if this
+ # config_setting is active.
+ target_settings = ["//pw_build:kythe"],
+ toolchain = ":host_toolchain_linux_kythe",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
diff --git a/pw_toolchain/host_clang/toolchain.cmake b/pw_toolchain/host_clang/toolchain.cmake
index 6cb29c09b..37d8586c1 100644
--- a/pw_toolchain/host_clang/toolchain.cmake
+++ b/pw_toolchain/host_clang/toolchain.cmake
@@ -29,6 +29,8 @@ include($ENV{PW_ROOT}/pw_trace/backend.cmake)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
+pw_add_global_compile_options(-std=c++20 LANGUAGES CXX)
+
# Configure backend for assert facade.
pw_set_backend(pw_assert.check pw_assert.print_and_abort_check_backend)
pw_set_backend(pw_assert.assert pw_assert.print_and_abort_assert_backend)
diff --git a/pw_toolchain/host_clang/toolchains.gni b/pw_toolchain/host_clang/toolchains.gni
index e148c86e1..3ee4c4fed 100644
--- a/pw_toolchain/host_clang/toolchains.gni
+++ b/pw_toolchain/host_clang/toolchains.gni
@@ -14,6 +14,7 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/defaults.gni")
import("$dir_pw_toolchain/clang_tools.gni")
declare_args() {
@@ -58,11 +59,11 @@ _defaults = {
# TODO: b/234888755 - amend toolchain declaration process to
# remove this hack.
default_configs = []
- default_configs = [
- "$dir_pw_build:extra_debugging",
- "$dir_pw_toolchain/host_clang:no_system_libcpp",
- "$dir_pw_toolchain/host_clang:xcode_sysroot",
- ]
+ default_configs = pigweed_default_configs + [
+ "$dir_pw_build:extra_debugging",
+ "$dir_pw_toolchain/host_clang:no_system_libcpp",
+ "$dir_pw_toolchain/host_clang:xcode_sysroot",
+ ]
# OSS-Fuzz uses -stdlib=libc++, which isn't included in the CIPD-provided
# Linux sysroot (it instead provides libstdc++).
@@ -141,9 +142,9 @@ pw_toolchain_host_clang = {
pw_toolchain_FUZZING_ENABLED = true
if (pw_toolchain_OSS_FUZZ_ENABLED) {
- default_configs += [ "$dir_pw_fuzzer:fuzz_instrumentation" ]
- } else {
default_configs += [ "$dir_pw_fuzzer:oss_fuzz_instrumentation" ]
+ } else {
+ default_configs += [ "$dir_pw_fuzzer:instrumentation" ]
}
# Fuzz faster.
diff --git a/pw_toolchain/host_gcc/toolchain.cmake b/pw_toolchain/host_gcc/toolchain.cmake
index ed011e21c..019c3ae95 100644
--- a/pw_toolchain/host_gcc/toolchain.cmake
+++ b/pw_toolchain/host_gcc/toolchain.cmake
@@ -29,6 +29,8 @@ include($ENV{PW_ROOT}/pw_trace/backend.cmake)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
+pw_add_global_compile_options(-std=c++20 LANGUAGES CXX)
+
# Configure backend for assert facade.
pw_set_backend(pw_assert.check pw_assert.print_and_abort_check_backend)
pw_set_backend(pw_assert.assert pw_assert.print_and_abort_assert_backend)
diff --git a/pw_toolchain/host_gcc/toolchains.gni b/pw_toolchain/host_gcc/toolchains.gni
index 66144bbc2..901d08389 100644
--- a/pw_toolchain/host_gcc/toolchains.gni
+++ b/pw_toolchain/host_gcc/toolchains.gni
@@ -14,6 +14,8 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/defaults.gni")
+
# Specifies the tools used by host GCC toolchains.
_host_gcc_toolchain = {
ar = "ar"
@@ -34,7 +36,8 @@ pw_toolchain_host_gcc = {
name = "host_gcc_debug"
forward_variables_from(_host_gcc_toolchain, "*")
defaults = {
- default_configs = _configs + [ "$dir_pw_build:optimize_debugging" ]
+ default_configs = pigweed_default_configs + _configs +
+ [ "$dir_pw_build:optimize_debugging" ]
}
}
@@ -42,7 +45,8 @@ pw_toolchain_host_gcc = {
name = "host_gcc_speed_optimized"
forward_variables_from(_host_gcc_toolchain, "*")
defaults = {
- default_configs = _configs + [ "$dir_pw_build:optimize_speed" ]
+ default_configs = pigweed_default_configs + _configs +
+ [ "$dir_pw_build:optimize_speed" ]
}
}
@@ -50,7 +54,8 @@ pw_toolchain_host_gcc = {
name = "host_gcc_size_optimized"
forward_variables_from(_host_gcc_toolchain, "*")
defaults = {
- default_configs = _configs + [ "$dir_pw_build:optimize_size" ]
+ default_configs =
+ pigweed_default_configs + _configs + [ "$dir_pw_build:optimize_size" ]
}
}
}
diff --git a/pw_toolchain/traits.gni b/pw_toolchain/traits.gni
index fee99dd23..ed4854754 100644
--- a/pw_toolchain/traits.gni
+++ b/pw_toolchain/traits.gni
@@ -53,5 +53,5 @@ assert(
"unsupported value. The toolchain must set it to one of the " +
"pw_toolchain_STANDARD constants (e.g. pw_toolchain_STANDARD.CXX17).")
-assert(pw_toolchain_CXX_STANDARD >= pw_toolchain_STANDARD.CXX14,
- "Pigweed requires at least C++14.")
+assert(pw_toolchain_CXX_STANDARD >= pw_toolchain_STANDARD.CXX17,
+ "Pigweed requires at least C++17.")
diff --git a/pw_toolchain_bazel/BUILD.gn b/pw_toolchain_bazel/BUILD.gn
index 4537bcc74..bf6234783 100644
--- a/pw_toolchain_bazel/BUILD.gn
+++ b/pw_toolchain_bazel/BUILD.gn
@@ -24,5 +24,8 @@ pw_test_group("tests") {
}
pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+ sources = [
+ "api.rst",
+ "docs.rst",
+ ]
}
diff --git a/pw_toolchain_bazel/api.rst b/pw_toolchain_bazel/api.rst
new file mode 100644
index 000000000..e0e61d33f
--- /dev/null
+++ b/pw_toolchain_bazel/api.rst
@@ -0,0 +1,298 @@
+.. _module-pw_toolchain_bazel-api:
+
+=============
+API reference
+=============
+
+.. py:class:: pw_cc_toolchain
+
+ This rule is the core of a C/C++ toolchain definition. Critically, it is
+ intended to fully specify the following:
+
+ * Which tools to use for various compile/link actions.
+ * Which `well-known features <https://bazel.build/docs/cc-toolchain-config-reference#wellknown-features>`_
+ are supported.
+ * Which flags to apply to various actions.
+
+ .. py:attribute:: feature_deps
+ :type: List[label]
+
+ ``pw_cc_toolchain_feature`` labels that provide features for this toolchain.
+
+ .. py:attribute:: ar
+ :type: File
+
+ Path to the tool to use for ``ar`` (static link) actions.
+
+ .. py:attribute:: cpp
+ :type: File
+
+ Path to the tool to use for C++ compile actions.
+
+ .. py:attribute:: gcc
+ :type: File
+
+ Path to the tool to use for C compile actions.
+
+ .. py:attribute:: gcov
+ :type: File
+
+ Path to the tool to use for generating code coverage data.
+
+ .. py:attribute:: ld
+ :type: File
+
+ Path to the tool to use for link actions.
+
+ .. py:attribute:: strip
+ :type: File
+
+ Path to the tool to use for strip actions.
+
+ .. py:attribute:: objcopy
+ :type: File
+
+ Path to the tool to use for objcopy actions.
+
+ .. py:attribute:: objdump
+ :type: File
+
+ Path to the tool to use for objdump actions.
+
+ .. py:attribute:: action_config_flag_sets
+ :type: List[label]
+
+ List of flag sets to apply to the respective ``action_config``\s. The vast
+ majority of labels listed here will point to :py:class:`pw_cc_flag_set`
+ rules.
+
+ .. py:attribute:: toolchain_identifier
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: host_system_name
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_system_name
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_cpu
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: target_libc
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: compiler
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: abi_version
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: abi_libc_version
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+ .. py:attribute:: cc_target_os
+ :type: str
+
+ See `cc_common.create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_\.
+
+.. py:class:: pw_cc_flag_set
+
+ Declares an ordered set of flags bound to a set of actions.
+
+ Flag sets can be attached to a :py:class:`pw_cc_toolchain` via
+ :py:attr:`pw_cc_toolchain.action_config_flag_sets`\.
+
+ Examples:
+
+ .. code-block:: py
+
+ pw_cc_flag_set(
+ name = "warnings_as_errors",
+ flags = ["-Werror"],
+ )
+
+ pw_cc_flag_set(
+ name = "layering_check",
+ flag_groups = [
+ ":strict_module_headers",
+ ":dependent_module_map_files",
+ ],
+ )
+
+ .. inclusive-language: disable
+
+ Note: In the vast majority of cases, alphabetical sorting is not desirable
+ for the :py:attr:`pw_cc_flag_set.flags` and
+ :py:attr:`pw_cc_flag_set.flag_groups` attributes.
+ `Buildifier <https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md>`_
+ shouldn't ever try to sort these, but in the off chance it starts to these
+ members should be listed as exceptions in the ``SortableDenylist``.
+
+ .. inclusive-language: enable
+
+ .. py:attribute:: actions
+ :type: List[str]
+
+ A list of action names that this flag set applies to.
+
+ .. inclusive-language: disable
+
+ Valid choices are listed at
+ `@rules_cc//cc:action_names.bzl <https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl>`_\.
+
+ .. inclusive-language: enable
+
+ It is possible for some needed action names to not be enumerated in this list,
+ so there is not rigid validation for these strings. Prefer using constants
+ rather than manually typing action names.
+
+ .. py:attribute:: flags
+ :type: List[str]
+
+ Flags that should be applied to the specified actions.
+
+ These are evaluated in order, with earlier flags appearing earlier in the
+ invocation of the underlying tool. If you need expansion logic, prefer
+ enumerating flags in a :py:class:`pw_cc_flag_group` or create a custom
+ rule that provides ``FlagGroupInfo``.
+
+ Note: :py:attr:`pw_cc_flag_set.flags` and
+ :py:attr:`pw_cc_flag_set.flag_groups` are mutually exclusive.
+
+ .. py:attribute:: flag_groups
+ :type: List[label]
+
+ Labels pointing to :py:class:`pw_cc_flag_group` rules.
+
+ This is intended to be compatible with any other rules that provide
+ ``FlagGroupInfo``. These are evaluated in order, with earlier flag groups
+ appearing earlier in the invocation of the underlying tool.
+
+ Note: :py:attr:`pw_cc_flag_set.flag_groups` and
+ :py:attr:`pw_cc_flag_set.flags` are mutually exclusive.
+
+.. py:class:: pw_cc_flag_group
+
+ Declares an (optionally parametric) ordered set of flags.
+
+ :py:class:`pw_cc_flag_group` rules are expected to be consumed exclusively by
+ :py:class:`pw_cc_flag_set` rules. Though simple lists of flags can be
+ expressed by populating ``flags`` on a :py:class:`pw_cc_flag_set`,
+ :py:class:`pw_cc_flag_group` provides additional power in the following two
+ ways:
+
+ 1. Iteration and conditional expansion. Using
+ :py:attr:`pw_cc_flag_group.iterate_over`,
+ :py:attr:`pw_cc_flag_group.expand_if_available`\, and
+ :py:attr:`pw_cc_flag_group.expand_if_not_available`\, more complex
+ flag expressions can be made. This is critical for implementing things
+ like the ``libraries_to_link`` feature, where library names are
+ transformed into flags that end up in the final link invocation.
+
+ Note: ``expand_if_equal``, ``expand_if_true``, and ``expand_if_false``
+ are not yet supported.
+
+ 2. Flags are tool-independent. A :py:class:`pw_cc_flag_group` expresses
+ ordered flags that may be reused across various
+ :py:class:`pw_cc_flag_set` rules. This is useful for cases where multiple
+ :py:class:`pw_cc_flag_set` rules must be created to implement a feature
+ for which flags are slightly different depending on the action (e.g.
+ compile vs link). Common flags can be expressed in a shared
+ :py:class:`pw_cc_flag_group`, and the differences can be relegated to
+ separate :py:class:`pw_cc_flag_group` instances.
+
+ Examples:
+
+ .. code-block:: py
+
+ pw_cc_flag_group(
+ name = "user_compile_flag_expansion",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # This flag_group might be referenced from various FDO-related
+ # `pw_cc_flag_set` rules. More importantly, the flag sets pulling this in
+ # may apply to different sets of actions.
+ pw_cc_flag_group(
+ name = "fdo_profile_correction",
+ flags = ["-fprofile-correction"],
+ expand_if_available = "fdo_profile_path",
+ )
+
+ .. py:attribute:: flags
+ :type: List[str]
+
+ List of flags provided by this rule.
+
+ For extremely complex expressions of flags that require nested flag groups
+ with multiple layers of expansion, prefer creating a custom rule in
+ `Starlark <https://bazel.build/rules/language>`_ that provides
+ ``FlagGroupInfo`` or ``FlagSetInfo``.
+
+
+ .. py:attribute:: iterate_over
+ :type: str
+
+ Expands :py:attr:`pw_cc_flag_group.flags` for items in the named list.
+
+ Toolchain actions have various variables accessible as names that can be
+ used to guide flag expansions. For variables that are lists,
+ :py:attr:`pw_cc_flag_group.iterate_over` must be used to expand the list into a series of flags.
+
+ Note that :py:attr:`pw_cc_flag_group.iterate_over` is the string name of a
+ build variable, and not an actual list. Valid options are listed in the
+ `C++ Toolchain Configuration <https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables>`_
+ reference.
+
+
+
+ Note that the flag expansion stamps out the entire list of flags in
+ :py:attr:`pw_cc_flag_group.flags` once for each item in the list.
+
+ Example:
+
+ .. code-block:: py
+
+ # Expands each path in ``system_include_paths`` to a series of
+ # ``-isystem`` includes.
+ #
+ # Example input:
+ # system_include_paths = ["/usr/local/include", "/usr/include"]
+ #
+ # Expected result:
+ # "-isystem /usr/local/include -isystem /usr/include"
+ pw_cc_flag_group(
+ name = "system_include_paths",
+ flags = ["-isystem", "%{system_include_paths}"],
+ iterate_over = "system_include_paths",
+ )
+
+ .. py:attribute:: expand_if_available
+ :type: str
+
+ Expands the expression in :py:attr:`pw_cc_flag_group.flags` if the
+ specified build variable is set.
+
+ .. py:attribute:: expand_if_not_available
+ :type: str
+
+ Expands the expression in :py:attr:`pw_cc_flag_group.flags` if the
+ specified build variable is **NOT** set.
diff --git a/pw_toolchain_bazel/cc_toolchain/defs.bzl b/pw_toolchain_bazel/cc_toolchain/defs.bzl
index 87243e562..b5340c54b 100644
--- a/pw_toolchain_bazel/cc_toolchain/defs.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/defs.bzl
@@ -16,17 +16,42 @@
load(
"//cc_toolchain/private:cc_toolchain.bzl",
_OBJ_COPY_ACTION_NAME = "OBJ_COPY_ACTION_NAME",
+ _OBJ_DUMP_ACTION_NAME = "OBJ_DUMP_ACTION_NAME",
_pw_cc_toolchain = "pw_cc_toolchain",
)
load(
+ "//cc_toolchain/private:flag_set.bzl",
+ _pw_cc_flag_group = "pw_cc_flag_group",
+ _pw_cc_flag_set = "pw_cc_flag_set",
+)
+load(
"//cc_toolchain/private:toolchain_feature.bzl",
_pw_cc_toolchain_feature = "pw_cc_toolchain_feature",
)
+load(
+ "//cc_toolchain/private:utils.bzl",
+ _ALL_AR_ACTIONS = "ALL_AR_ACTIONS",
+ _ALL_ASM_ACTIONS = "ALL_ASM_ACTIONS",
+ _ALL_CPP_COMPILER_ACTIONS = "ALL_CPP_COMPILER_ACTIONS",
+ _ALL_C_COMPILER_ACTIONS = "ALL_C_COMPILER_ACTIONS",
+ _ALL_LINK_ACTIONS = "ALL_LINK_ACTIONS",
+)
+
+ALL_ASM_ACTIONS = _ALL_ASM_ACTIONS
+ALL_C_COMPILER_ACTIONS = _ALL_C_COMPILER_ACTIONS
+ALL_CPP_COMPILER_ACTIONS = _ALL_CPP_COMPILER_ACTIONS
+ALL_LINK_ACTIONS = _ALL_LINK_ACTIONS
+ALL_AR_ACTIONS = _ALL_AR_ACTIONS
# TODO(b/301004620): Remove when bazel 7 is released and this constant exists in
# ACTION_NAMES
OBJ_COPY_ACTION_NAME = _OBJ_COPY_ACTION_NAME
+OBJ_DUMP_ACTION_NAME = _OBJ_DUMP_ACTION_NAME
+
+pw_cc_flag_group = _pw_cc_flag_group
+pw_cc_flag_set = _pw_cc_flag_set
pw_cc_toolchain = _pw_cc_toolchain
+# TODO: b/309533028 - This is deprecated, and will soon be removed.
pw_cc_toolchain_feature = _pw_cc_toolchain_feature
diff --git a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
index 94df446d6..d36ec8e96 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl
@@ -16,6 +16,7 @@
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load(
"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagSetInfo",
"action_config",
"feature",
"flag_group",
@@ -39,16 +40,19 @@ load(
# TODO(b/301004620): Remove when bazel 7 is released and this constant exists in
# ACTION_NAMES
OBJ_COPY_ACTION_NAME = "objcopy_embed_data"
+OBJ_DUMP_ACTION_NAME = "objdump_embed_data"
PW_CC_TOOLCHAIN_CONFIG_ATTRS = {
- "feature_deps": "pw_cc_toolchain_feature labels that provide features for this toolchain",
- "ar": "Path to the tool to use for ar (static link) actions",
+ "feature_deps": "`pw_cc_toolchain_feature` labels that provide features for this toolchain",
+ "ar": "Path to the tool to use for `ar` (static link) actions",
"cpp": "Path to the tool to use for C++ compile actions",
"gcc": "Path to the tool to use for C compile actions",
- "gcov": "Pah to the tool to use for generating code coverag data",
+ "gcov": "Path to the tool to use for generating code coverage data",
"ld": "Path to the tool to use for link actions",
"strip": "Path to the tool to use for strip actions",
"objcopy": "Path to the tool to use for objcopy actions",
+ "objdump": "Path to the tool to use for objdump actions",
+ "action_config_flag_sets": "List of flag sets to apply to the respective `action_config`s",
# Attributes originally part of create_cc_toolchain_config_info.
"toolchain_identifier": "See documentation for cc_common.create_cc_toolchain_config_info()",
@@ -75,12 +79,13 @@ PW_CC_TOOLCHAIN_BLOCKED_ATTRS = {
"builtin_sysroot": "Use a pw_cc_toolchain_feature to add a builtin_sysroot",
}
-def _action_configs(action_tool, action_list):
+def _action_configs(action_tool, action_list, flag_sets_by_action):
"""Binds a tool to an action.
Args:
action_tool (File): Tool to bind to the specified actions.
action_list (List[str]): List of actions to bind to the specified tool.
+ flag_sets_by_action: Dictionary mapping action names to lists of applicable flag sets.
Returns:
action_config: A action_config binding the provided tool to the
@@ -94,6 +99,7 @@ def _action_configs(action_tool, action_list):
tool = action_tool,
),
],
+ flag_sets = flag_sets_by_action.get(action, default = []),
)
for action in action_list
]
@@ -178,6 +184,40 @@ def _archiver_flags(is_mac):
else:
return ["rcsD"]
+def _strip_actions(flag_set_to_copy):
+ """Copies a flag_set, stripping `actions`.
+
+ Args:
+ flag_set_to_copy: The base flag_set to copy.
+ Returns:
+ flag_set with empty `actions` list.
+ """
+ return flag_set(
+ with_features = flag_set_to_copy.with_features,
+ flag_groups = flag_set_to_copy.flag_groups,
+ )
+
+def _create_action_flag_set_map(flag_sets):
+ """Creates a mapping of action names to flag sets.
+
+ Args:
+ flag_sets: the flag sets to expand.
+ Returns:
+ Dictionary mapping action names to lists of FlagSetInfo providers.
+ """
+ flag_sets_by_action = {}
+ for fs in flag_sets:
+ handled_actions = {}
+ for action in fs.actions:
+ if action not in flag_sets_by_action:
+ flag_sets_by_action[action] = []
+
+ # Dedupe action set list.
+ if action not in handled_actions:
+ handled_actions[action] = True
+ flag_sets_by_action[action].append(_strip_actions(fs))
+ return flag_sets_by_action
+
def _pw_cc_toolchain_config_impl(ctx):
"""Rule that provides a CcToolchainConfigInfo.
@@ -188,11 +228,14 @@ def _pw_cc_toolchain_config_impl(ctx):
CcToolchainConfigInfo
"""
check_deps(ctx)
+
+ flag_sets_by_action = _create_action_flag_set_map([dep[FlagSetInfo] for dep in ctx.attr.action_config_flag_sets])
+
all_actions = []
- all_actions += _action_configs(ctx.executable.gcc, ALL_ASM_ACTIONS)
- all_actions += _action_configs(ctx.executable.gcc, ALL_C_COMPILER_ACTIONS)
- all_actions += _action_configs(ctx.executable.cpp, ALL_CPP_COMPILER_ACTIONS)
- all_actions += _action_configs(ctx.executable.cpp, ALL_LINK_ACTIONS)
+ all_actions += _action_configs(ctx.executable.gcc, ALL_ASM_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.gcc, ALL_C_COMPILER_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.cpp, ALL_CPP_COMPILER_ACTIONS, flag_sets_by_action)
+ all_actions += _action_configs(ctx.executable.cpp, ALL_LINK_ACTIONS, flag_sets_by_action)
all_actions += [
action_config(
@@ -203,6 +246,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.ar,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.cpp_link_static_library, default = []),
),
action_config(
action_name = ACTION_NAMES.llvm_cov,
@@ -211,6 +255,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.gcov,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.llvm_cov, default = []),
),
action_config(
action_name = OBJ_COPY_ACTION_NAME,
@@ -219,6 +264,16 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.objcopy,
),
],
+ flag_sets = flag_sets_by_action.get(OBJ_COPY_ACTION_NAME, default = []),
+ ),
+ action_config(
+ action_name = OBJ_DUMP_ACTION_NAME,
+ tools = [
+ tool(
+ tool = ctx.executable.objdump,
+ ),
+ ],
+ flag_sets = flag_sets_by_action.get(OBJ_DUMP_ACTION_NAME, default = []),
),
action_config(
action_name = ACTION_NAMES.strip,
@@ -227,6 +282,7 @@ def _pw_cc_toolchain_config_impl(ctx):
tool = ctx.executable.strip,
),
],
+ flag_sets = flag_sets_by_action.get(ACTION_NAMES.strip, default = []),
),
]
@@ -272,7 +328,9 @@ pw_cc_toolchain_config = rule(
"cpp": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
"gcov": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
"objcopy": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
+ "objdump": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
"strip": attr.label(allow_single_file = True, executable = True, cfg = "exec"),
+ "action_config_flag_sets": attr.label_list(),
# Attributes from create_cc_toolchain_config_info.
"toolchain_identifier": attr.string(),
diff --git a/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
new file mode 100644
index 000000000..23094e864
--- /dev/null
+++ b/pw_toolchain_bazel/cc_toolchain/private/flag_set.bzl
@@ -0,0 +1,225 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Implementation of pw_cc_flag_set and pw_cc_flag_group."""
+
+load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagGroupInfo",
+ "FlagSetInfo",
+ "flag_group",
+ "flag_set",
+)
+
+def _pw_cc_flag_group_impl(ctx):
+ """Implementation for pw_cc_flag_group."""
+
+ # If these are empty strings, they are handled differently than if they
+ # are None. Rather than an explicit error or breakage, there's just silent
+ # behavioral differences. Ideally, these attributes default to `None`, but
+ # that is not supported with string types. Since these have no practical
+ # meaning if they are empty strings, just remap empty strings to `None`.
+ #
+ # A minimal reproducer of this behavior with some useful analysis is
+ # provided here:
+ #
+ # https://github.com/armandomontanez/bazel_reproducers/tree/main/flag_group_with_empty_strings
+ iterate_over = ctx.attr.iterate_over if ctx.attr.iterate_over else None
+ expand_if = ctx.attr.expand_if_available if ctx.attr.expand_if_available else None
+ expand_if_not = ctx.attr.expand_if_not_available if ctx.attr.expand_if_not_available else None
+ return flag_group(
+ flags = ctx.attr.flags,
+ iterate_over = iterate_over,
+ expand_if_available = expand_if,
+ expand_if_not_available = expand_if_not,
+ )
+
+pw_cc_flag_group = rule(
+ implementation = _pw_cc_flag_group_impl,
+ attrs = {
+ "flags": attr.string_list(
+ doc = """List of flags provided by this rule.
+
+For extremely complex expressions of flags that require nested flag groups with
+multiple layers of expansion, prefer creating a custom rule in Starlark that
+provides `FlagGroupInfo` or `FlagSetInfo`.
+""",
+ ),
+ "iterate_over": attr.string(
+ doc = """Expands `flags` for items in the named list.
+
+Toolchain actions have various variables accessible as names that can be used
+to guide flag expansions. For variables that are lists, `iterate_over` must be
+used to expand the list into a series of flags.
+
+Note that `iterate_over` is the string name of a build variable, and not an
+actual list. Valid options are listed at:
+
+ https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables
+
+Note that the flag expansion stamps out the entire list of flags in `flags`
+once for each item in the list.
+
+Example:
+
+ # Expands each path in `system_include_paths` to a series of `-isystem`
+ # includes.
+ #
+ # Example input:
+ # system_include_paths = ["/usr/local/include", "/usr/include"]
+ #
+ # Expected result:
+ # "-isystem /usr/local/include -isystem /usr/include"
+ pw_cc_flag_group(
+ name = "system_include_paths",
+ flags = ["-isystem", "%{system_include_paths}"],
+ iterate_over = "system_include_paths",
+ )
+""",
+ ),
+ "expand_if_available": attr.string(
+ doc = "Expands the expression in `flags` if the specified build variable is set.",
+ ),
+ "expand_if_not_available": attr.string(
+ doc = "Expands the expression in `flags` if the specified build variable is NOT set.",
+ ),
+ },
+ provides = [FlagGroupInfo],
+ doc = """Declares an (optionally parametric) ordered set of flags.
+
+`pw_cc_flag_group` rules are expected to be consumed exclusively by
+`pw_cc_flag_set` rules. Though simple lists of flags can be expressed by
+populating `flags` on a `pw_cc_flag_set`, `pw_cc_flag_group` provides additional
+power in the following two ways:
+
+ 1. Iteration and conditional expansion. Using `iterate_over`,
+ `expand_if_available`, and `expand_if_not_available`, more complex flag
+ expressions can be made. This is critical for implementing things like
+ the `libraries_to_link` feature, where library names are transformed
+ into flags that end up in the final link invocation.
+
+ Note: `expand_if_equal`, `expand_if_true`, and `expand_if_false` are not
+ yet supported.
+
+ 2. Flags are tool-independent. A `pw_cc_flag_group` expresses ordered flags
+ that may be reused across various `pw_cc_flag_set` rules. This is useful
+ for cases where multiple `pw_cc_flag_set` rules must be created to
+ implement a feature for which flags are slightly different depending on
+ the action (e.g. compile vs link). Common flags can be expressed in a
+ shared `pw_cc_flag_group`, and the differences can be relegated to
+ separate `pw_cc_flag_group` instances.
+
+Examples:
+
+ pw_cc_flag_group(
+ name = "user_compile_flag_expansion",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # This flag_group might be referenced from various FDO-related
+ # `pw_cc_flag_set` rules. More importantly, the flag sets pulling this in
+ # may apply to different sets of actions.
+ pw_cc_flag_group(
+ name = "fdo_profile_correction",
+ flags = ["-fprofile-correction"],
+ expand_if_available = "fdo_profile_path",
+ )
+""",
+)
+
+def _pw_cc_flag_set_impl(ctx):
+ """Implementation for pw_cc_flag_set."""
+ if ctx.attr.flags and ctx.attr.flag_groups:
+ fail("{} specifies both `flag_groups` and `flags`, but only one can be specified. Consider splitting into two `pw_cc_flag_set` rules to make the intended order clearer.".format(ctx.label))
+ flag_groups = []
+ if ctx.attr.flags:
+ flag_groups.append(flag_group(flags = ctx.attr.flags))
+ elif ctx.attr.flag_groups:
+ for dep in ctx.attr.flag_groups:
+ if not dep[FlagGroupInfo]:
+ fail("{} in `flag_groups` of {} does not provide FlagGroupInfo".format(dep.label, ctx.label))
+
+ flag_groups = [dep[FlagGroupInfo] for dep in ctx.attr.flag_groups]
+ return flag_set(
+ actions = ctx.attr.actions,
+ flag_groups = flag_groups,
+ )
+
+pw_cc_flag_set = rule(
+ implementation = _pw_cc_flag_set_impl,
+ attrs = {
+ "actions": attr.string_list(
+ mandatory = True,
+ # inclusive-language: disable
+ doc = """A list of action names that this flag set applies to.
+
+Valid choices are listed here:
+
+ https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
+
+It is possible for some needed action names to not be enumerated in this list,
+so there is not rigid validation for these strings. Prefer using constants
+rather than manually typing action names.
+""",
+ # inclusive-language: enable
+ ),
+ "flag_groups": attr.label_list(
+ doc = """Labels pointing to `pw_cc_flag_group` rules.
+
+This is intended to be compatible with any other rules that provide
+`FlagGroupInfo`. These are evaluated in order, with earlier flag groups
+appearing earlier in the invocation of the underlying tool.
+
+Note: `flag_groups` and `flags` are mutually exclusive.
+""",
+ ),
+ "flags": attr.string_list(
+ doc = """Flags that should be applied to the specified actions.
+
+These are evaluated in order, with earlier flags appearing earlier in the
+invocation of the underlying tool. If you need expansion logic, prefer
+enumerating flags in a `pw_cc_flag_group` or create a custom rule that provides
+`FlagGroupInfo`.
+
+Note: `flags` and `flag_groups` are mutually exclusive.
+""",
+ ),
+ },
+ provides = [FlagSetInfo],
+ doc = """Declares an ordered set of flags bound to a set of actions.
+
+Flag sets can be attached to a `pw_cc_toolchain` via `action_config_flag_sets`.
+
+Examples:
+
+ pw_cc_flag_set(
+ name = "warnings_as_errors",
+ flags = ["-Werror"],
+ )
+
+ pw_cc_flag_set(
+ name = "layering_check",
+ flag_groups = [
+ ":strict_module_headers",
+ ":dependent_module_map_files",
+ ],
+ )
+
+Note: In the vast majority of cases, alphabetical sorting is not desirable for
+the `flags` and `flag_groups` attributes. Buildifier shouldn't ever try to sort
+these, but in the off chance it starts to these members should be listed as
+exceptions in the `SortableDenylist`.
+""",
+)
diff --git a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
index 321cafad4..7030f2498 100644
--- a/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
+++ b/pw_toolchain_bazel/cc_toolchain/private/utils.bzl
@@ -15,6 +15,10 @@
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "FlagSetInfo",
+)
+load(
"//cc_toolchain/private:providers.bzl",
"ToolchainFeatureInfo",
)
@@ -52,12 +56,18 @@ ACTION_MAP = {
"linkopts": ALL_LINK_ACTIONS,
}
+def _check_dep_provides(ctx_label, dep, provider, what_provides):
+ if provider not in dep:
+ fail(
+ "{} listed as a dependency of {}, but it's not a {}".format(
+ dep.label,
+ ctx_label,
+ what_provides,
+ ),
+ )
+
def check_deps(ctx):
for dep in ctx.attr.feature_deps:
- if ToolchainFeatureInfo not in dep:
- fail(
- "{} listed as a dependency of {}, but it's not a pw_cc_toolchain_feature".format(
- dep.label,
- ctx.label,
- ),
- )
+ _check_dep_provides(ctx.label, dep, ToolchainFeatureInfo, "pw_cc_toolchain_feature")
+ for dep in ctx.attr.action_config_flag_sets:
+ _check_dep_provides(ctx.label, dep, FlagSetInfo, "pw_cc_flag_set")
diff --git a/pw_toolchain_bazel/docs.rst b/pw_toolchain_bazel/docs.rst
index 9c885c14f..075dc3f7b 100644
--- a/pw_toolchain_bazel/docs.rst
+++ b/pw_toolchain_bazel/docs.rst
@@ -3,16 +3,57 @@
==================
pw_toolchain_bazel
==================
-This module provides building blocks for Bazel's ``cc_toolchain`` API in a way
-that increases modularity and reusability. While this module does NOT provide a
-hermetic toolchain, Pigweed does provide fully instantiated and supported
-toolchains as part of ``pw_toolchain``.
+
+.. pigweed-module::
+ :name: pw_toolchain_bazel
+ :tagline: Modular Bazel C/C++ toolchain API
+ :status: unstable
+ :languages: Starlark
+
+Assembling a complete, hermetic toolchain with Bazel using the native primitives
+can be quite challenging. Additionally, Bazel's native API for declaring C/C++
+toolchains doesn't inherently encourage modularity or reusability.
+
+``pw_toolchain_bazel`` provides a suite of building blocks that make the process
+of assembling a complete, hermetic toolchain significantly easier. The Bazel
+rules introduced by this module push the vast majority of a toolchain's
+declaration into build files, and encourages reusability through sharing of
+flag groups, tools, and toolchain feature implementations.
+
+While this module does **not** provide a hermetic toolchain, Pigweed provides
+`fully instantiated and supported toolchains <https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain/host_clang/BUILD.bazel>`_
+that are a useful reference for building your own toolchain.
.. warning::
- This module is under construction and is subject to major breaking changes.
+ `b/309533028 <https://issues.pigweed.dev/309533028>`_\: This module is under
+ construction and is subject to major breaking changes.
+
+.. grid:: 1
+
+ .. grid-item-card:: :octicon:`info` API reference
+ :link: module-pw_toolchain_bazel-api
+ :link-type: ref
+ :class-item: sales-pitch-cta-primary
+
+ Detailed reference information about the pw_toolchain_bazel API.
+
+.. grid:: 1
+
+ .. grid-item-card:: :octicon:`file` Original SEED
+ :link: seed-0113
+ :link-type: ref
+ :class-item: sales-pitch-cta-secondary
+
+ SEED-0113: Add modular Bazel C/C++ toolchain API
------------
Dependencies
------------
This module is not permitted to have dependencies on other modules. When this
module stabilizes, it will be broken out into a separate repository.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ API reference <api>
diff --git a/pw_toolchain_bazel/features/BUILD.bazel b/pw_toolchain_bazel/features/BUILD.bazel
index 49431c472..c41e01396 100644
--- a/pw_toolchain_bazel/features/BUILD.bazel
+++ b/pw_toolchain_bazel/features/BUILD.bazel
@@ -69,3 +69,19 @@ pw_cc_toolchain_feature(
name = "wnon_virtual_dtor",
cxxopts = ["-Wnon-virtual-dtor"],
)
+
+# Standard compiler flags to reduce output binary size.
+pw_cc_toolchain_feature(
+ name = "reduced_size",
+ copts = [
+ "-fno-common",
+ "-fno-exceptions",
+ "-ffunction-sections",
+ "-fdata-sections",
+ ],
+)
+
+pw_cc_toolchain_feature(
+ name = "debugging",
+ copts = ["-g"],
+)
diff --git a/pw_trace_tokenized/BUILD.bazel b/pw_trace_tokenized/BUILD.bazel
index 4263ef3ef..72c5357dc 100644
--- a/pw_trace_tokenized/BUILD.bazel
+++ b/pw_trace_tokenized/BUILD.bazel
@@ -18,6 +18,7 @@ load(
"pw_cc_library",
"pw_cc_test",
)
+load("//pw_build/bazel_internal:py_proto_library.bzl", "py_proto_library")
load("//pw_protobuf_compiler:pw_proto_library.bzl", "pw_proto_library")
package(default_visibility = ["//visibility:public"])
@@ -31,12 +32,21 @@ pw_cc_library(
)
pw_cc_library(
+ name = "config",
+ hdrs = [
+ "public/pw_trace_tokenized/config.h",
+ ],
+ includes = [
+ "public",
+ ],
+)
+
+pw_cc_library(
name = "pw_trace_tokenized",
srcs = [
"trace.cc",
],
hdrs = [
- "public/pw_trace_tokenized/config.h",
"public/pw_trace_tokenized/internal/trace_tokenized_internal.h",
"public/pw_trace_tokenized/trace_callback.h",
"public/pw_trace_tokenized/trace_tokenized.h",
@@ -47,6 +57,7 @@ pw_cc_library(
"public_overrides",
],
deps = [
+ ":config",
"//pw_assert",
"//pw_log",
"//pw_preprocessor",
@@ -58,6 +69,43 @@ pw_cc_library(
)
pw_cc_library(
+ name = "base_trace_service",
+ srcs = [
+ "base_trace_service.cc",
+ ],
+ hdrs = [
+ "public/pw_trace_tokenized/base_trace_service.h",
+ ],
+ includes = [
+ "public",
+ ],
+ deps = [
+ ":buffer",
+ ":pw_trace_tokenized",
+ "//pw_ring_buffer",
+ "//pw_stream",
+ ],
+)
+
+pw_cc_library(
+ name = "trace_service_pwpb",
+ srcs = [
+ "trace_service_pwpb.cc",
+ ],
+ hdrs = [
+ "public/pw_trace_tokenized/trace_service_pwpb.h",
+ ],
+ includes = [
+ "public",
+ ],
+ deps = [
+ ":base_trace_service",
+ ":protos_cc.pwpb_rpc",
+ "//pw_chrono:system_clock",
+ ],
+)
+
+pw_cc_library(
name = "trace_rpc_service",
srcs = [
"trace_rpc_service_nanopb.cc",
@@ -121,10 +169,14 @@ proto_library(
srcs = [
"pw_trace_protos/trace.proto",
"pw_trace_protos/trace_rpc.proto",
+ "pw_trace_protos/trace_service.proto",
+ ],
+ deps = [
+ "//pw_chrono:chrono_proto",
],
# TODO(tpudlik): We should provide trace_rpc.options to nanopb here, but the
# current proto codegen implementation provides no mechanism for doing so.
- # inputs = [ "pw_trace_protos/trace_rpc.options" ]
+ # inputs = [ "pw_trace_protos/trace_rpc.options", "pw_trace_protos/trace_service.options"]
)
pw_proto_library(
@@ -132,6 +184,13 @@ pw_proto_library(
deps = [":protos"],
)
+py_proto_library(
+ name = "proto_py",
+ # TODO(b/241456982): Get this target to build.
+ tags = ["manual"],
+ deps = [":protos"],
+)
+
pw_cc_library(
name = "pw_trace_tokenized_fake_time",
srcs = [
@@ -190,6 +249,22 @@ pw_cc_test(
],
)
+pw_cc_test(
+ name = "trace_service_pwpb_test",
+ srcs = [
+ "trace_service_pwpb_test.cc",
+ ],
+ # TODO(b/260641850): Get pw_trace_tokenized building in Bazel.
+ tags = ["manual"],
+ deps = [
+ ":pw_trace_host_trace_time",
+ ":trace_service_pwpb",
+ "//pw_chrono:system_clock",
+ "//pw_rpc/pwpb:test_method_context",
+ "//pw_trace",
+ ],
+)
+
pw_cc_library(
name = "pw_trace_host_trace_time",
srcs = ["host_trace_time.cc"],
diff --git a/pw_trace_tokenized/BUILD.gn b/pw_trace_tokenized/BUILD.gn
index 0e7be3121..c2c928e91 100644
--- a/pw_trace_tokenized/BUILD.gn
+++ b/pw_trace_tokenized/BUILD.gn
@@ -62,6 +62,7 @@ pw_test_group("tests") {
":trace_tokenized_test",
":tokenized_trace_buffer_test",
":tokenized_trace_buffer_log_test",
+ ":trace_service_pwpb_test",
]
}
@@ -101,8 +102,14 @@ pw_proto_library("protos") {
sources = [
"pw_trace_protos/trace.proto",
"pw_trace_protos/trace_rpc.proto",
+ "pw_trace_protos/trace_service.proto",
]
- inputs = [ "pw_trace_protos/trace_rpc.options" ]
+ inputs = [
+ "pw_trace_protos/trace_rpc.options",
+ "pw_trace_protos/trace_service.options",
+ ]
+ python_package = "py"
+ deps = [ "$dir_pw_chrono:protos" ]
}
pw_source_set("trace_rpc_service") {
@@ -120,6 +127,46 @@ pw_source_set("trace_rpc_service") {
]
}
+pw_source_set("base_trace_service") {
+ public_configs = [ ":public_include_path" ]
+ public_deps = [
+ ":core",
+ ":tokenized_trace_buffer",
+ ]
+ deps = [
+ "$dir_pw_ring_buffer",
+ "$dir_pw_stream",
+ ]
+ sources = [
+ "base_trace_service.cc",
+ "public/pw_trace_tokenized/base_trace_service.h",
+ ]
+}
+
+pw_source_set("trace_service_pwpb") {
+ public_configs = [ ":public_include_path" ]
+ public_deps = [
+ ":base_trace_service",
+ ":protos.pwpb_rpc",
+ ]
+ deps = [ "$dir_pw_chrono:system_clock" ]
+ sources = [
+ "public/pw_trace_tokenized/trace_service_pwpb.h",
+ "trace_service_pwpb.cc",
+ ]
+}
+
+pw_test("trace_service_pwpb_test") {
+ enable_if = _pw_trace_tokenized_is_selected
+ deps = [
+ ":trace_service_pwpb",
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_rpc/pwpb:test_method_context",
+ "$dir_pw_trace",
+ ]
+ sources = [ "trace_service_pwpb_test.cc" ]
+}
+
pw_source_set("tokenized_trace_buffer") {
deps = [ ":core" ]
public_deps = [
diff --git a/pw_trace_tokenized/CMakeLists.txt b/pw_trace_tokenized/CMakeLists.txt
index e8780f6a4..bc6268f90 100644
--- a/pw_trace_tokenized/CMakeLists.txt
+++ b/pw_trace_tokenized/CMakeLists.txt
@@ -69,8 +69,12 @@ pw_add_library(pw_trace_tokenized.trace_buffer STATIC
pw_proto_library(pw_trace_tokenized.protos
SOURCES
pw_trace_protos/trace_rpc.proto
+ pw_trace_protos/trace_service.proto
INPUTS
pw_trace_protos/trace_rpc.options
+ pw_trace_protos/trace_service.options
+ DEPS
+ pw_chrono.protos
)
pw_add_library(pw_trace_tokenized.rpc_service STATIC
@@ -87,3 +91,25 @@ pw_add_library(pw_trace_tokenized.rpc_service STATIC
pw_tokenizer
pw_status
)
+
+pw_add_library(pw_trace_tokenized.base_trace_service STATIC
+ SOURCES
+ base_trace_service.cc
+ PRIVATE_DEPS
+ pw_log
+ pw_stream
+ pw_ring_buffer
+ PUBLIC_DEPS
+ pw_trace_tokenized
+ pw_trace_tokenized.trace_buffer
+)
+
+pw_add_library(pw_trace_tokenized.trace_service_pwpb STATIC
+ SOURCES
+ trace_service_pwpb.cc
+ PRIVATE_DEPS
+ pw_chrono.system_clock
+ PUBLIC_DEPS
+ pw_trace_tokenized.base_trace_service
+ pw_trace_tokenized.protos.pwpb_rpc
+)
diff --git a/pw_trace_tokenized/base_trace_service.cc b/pw_trace_tokenized/base_trace_service.cc
new file mode 100644
index 000000000..56331f377
--- /dev/null
+++ b/pw_trace_tokenized/base_trace_service.cc
@@ -0,0 +1,76 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_trace_tokenized/base_trace_service.h"
+
+#include "pw_log/log.h"
+#include "pw_trace_tokenized/trace_buffer.h"
+
+namespace pw::trace {
+BaseTraceService::BaseTraceService(TokenizedTracer& tokenized_tracer,
+ stream::Writer& trace_writer)
+ : tokenized_tracer_(tokenized_tracer), trace_writer_(trace_writer) {
+ tokenized_tracer_.Enable(false);
+}
+
+Status BaseTraceService::Start() {
+ PW_LOG_INFO("Starting Tracing");
+
+ if (tokenized_tracer_.IsEnabled()) {
+ PW_LOG_INFO("Tracing already started");
+ return Status::FailedPrecondition();
+ }
+
+ tokenized_tracer_.Enable(true);
+
+ return OkStatus();
+}
+
+Status BaseTraceService::Stop() {
+ PW_LOG_INFO("Stopping Tracing");
+
+ if (!tokenized_tracer_.IsEnabled()) {
+ PW_LOG_INFO("Tracing not started");
+ return Status::FailedPrecondition();
+ }
+
+ tokenized_tracer_.Enable(false);
+
+ auto ring_buffer = trace::GetBuffer();
+
+ if (ring_buffer->EntryCount() == 0) {
+ PW_LOG_WARN("EntryCount(%zu)", ring_buffer->EntryCount());
+ return Status::Unavailable();
+ } else if (ring_buffer->CheckForCorruption() != OkStatus()) {
+ PW_LOG_ERROR("EntryCount(%zu), Corruption(%d)",
+ ring_buffer->EntryCount(),
+ ring_buffer->CheckForCorruption().code());
+ return Status::Unavailable();
+ }
+
+ PW_LOG_INFO("EntryCount(%zu)", ring_buffer->EntryCount());
+
+ ConstByteSpan inner_trace_span = trace::DeringAndViewRawBuffer();
+ if (auto status = trace_writer_.Write(inner_trace_span);
+ status != OkStatus()) {
+ PW_LOG_ERROR("Failed to write trace data: %d)", status.code());
+ return status;
+ }
+
+ ring_buffer->Clear();
+
+ return OkStatus();
+}
+
+} // namespace pw::trace
diff --git a/pw_trace_tokenized/public/pw_trace_tokenized/base_trace_service.h b/pw_trace_tokenized/public/pw_trace_tokenized/base_trace_service.h
new file mode 100644
index 000000000..d6f219a36
--- /dev/null
+++ b/pw_trace_tokenized/public/pw_trace_tokenized/base_trace_service.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <cstdint>
+#include <optional>
+
+#include "pw_status/status.h"
+#include "pw_stream/stream.h"
+#include "pw_trace_tokenized/trace_tokenized.h"
+
+namespace pw::trace {
+class BaseTraceService {
+ public:
+ BaseTraceService(TokenizedTracer& tokenized_tracer,
+ stream::Writer& trace_writer);
+
+ void SetTransferId(uint32_t id) { transfer_id_ = id; }
+
+ protected:
+ Status Start();
+ Status Stop();
+
+ private:
+ TokenizedTracer& tokenized_tracer_;
+ stream::Writer& trace_writer_;
+
+ protected:
+ std::optional<uint32_t> transfer_id_;
+};
+
+} // namespace pw::trace
diff --git a/pw_trace_tokenized/public/pw_trace_tokenized/trace_service_pwpb.h b/pw_trace_tokenized/public/pw_trace_tokenized/trace_service_pwpb.h
new file mode 100644
index 000000000..c915dfb34
--- /dev/null
+++ b/pw_trace_tokenized/public/pw_trace_tokenized/trace_service_pwpb.h
@@ -0,0 +1,38 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_trace_protos/trace_service.rpc.pwpb.h"
+#include "pw_trace_tokenized/base_trace_service.h"
+
+namespace pw::trace {
+
+class TraceService final
+ : public proto::pw_rpc::pwpb::TraceService::Service<TraceService>,
+ public BaseTraceService {
+ public:
+ TraceService(TokenizedTracer& tokenized_tracer, stream::Writer& trace_writer);
+
+ Status Start(const proto::pwpb::StartRequest::Message& request,
+ proto::pwpb::StartResponse::Message& response);
+
+ Status Stop(const proto::pwpb::StopRequest::Message& request,
+ proto::pwpb::StopResponse::Message& response);
+
+ Status GetClockParameters(
+ const proto::pwpb::ClockParametersRequest::Message& request,
+ proto::pwpb::ClockParametersResponse::Message& response);
+};
+
+} // namespace pw::trace
diff --git a/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto b/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto
index 39971e7c3..2b9b3bc28 100644
--- a/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto
+++ b/pw_trace_tokenized/pw_trace_protos/trace_rpc.proto
@@ -15,6 +15,10 @@ syntax = "proto3";
package pw.trace;
+// TODO: b/309643763 - This service has been deprecated in favor of the tracing
+// service defined in trace_service.proto
+// This service will be deleted once existing clients have been migrated awaw
+// from it, so please don't use this for any new projects.
service TraceService {
rpc Enable(TraceEnableMessage) returns (TraceEnableMessage) {}
rpc IsEnabled(Empty) returns (TraceEnableMessage) {}
diff --git a/pw_trace_tokenized/pw_trace_protos/trace_service.options b/pw_trace_tokenized/pw_trace_protos/trace_service.options
new file mode 100644
index 000000000..714d9397f
--- /dev/null
+++ b/pw_trace_tokenized/pw_trace_protos/trace_service.options
@@ -0,0 +1,13 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
diff --git a/pw_trace_tokenized/pw_trace_protos/trace_service.proto b/pw_trace_tokenized/pw_trace_protos/trace_service.proto
new file mode 100644
index 000000000..62be6fe9b
--- /dev/null
+++ b/pw_trace_tokenized/pw_trace_protos/trace_service.proto
@@ -0,0 +1,53 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+syntax = "proto3";
+
+package pw.trace.proto;
+
+import "pw_chrono_protos/chrono.proto";
+
+// TODO: b/309643763 - This is the prefered service for tracing, and the service
+// defined in trace_rpc.proto has been deprecated and will be removed once
+// existings clients migrate to this service.
+service TraceService {
+ // Start will enable tracing, populating the trace ring buffer.
+ rpc Start(StartRequest) returns (StartResponse) {}
+
+ // On stop the ring buffer will be written to the configured
+ // stream. No data is written to the stream until Stop is called.
+ rpc Stop(StopRequest) returns (StopResponse) {}
+
+ // Returns the clock paramaters of the system.
+ rpc GetClockParameters(ClockParametersRequest)
+ returns (ClockParametersResponse) {}
+}
+
+message StartRequest {}
+
+message StartResponse {}
+
+message StopRequest {}
+
+message StopResponse {
+ // as a convenience, the file id is returned on stop which can be
+ // used to start a transfer directly, rather that requiring a user
+ // list the files to obtain the file id.
+ optional uint32 file_id = 1;
+}
+
+message ClockParametersRequest {}
+
+message ClockParametersResponse {
+ pw.chrono.ClockParameters clock_parameters = 1;
+}
diff --git a/pw_trace_tokenized/py/BUILD.gn b/pw_trace_tokenized/py/BUILD.gn
index 78c7f998d..4ad26412b 100644
--- a/pw_trace_tokenized/py/BUILD.gn
+++ b/pw_trace_tokenized/py/BUILD.gn
@@ -17,10 +17,12 @@ import("//build_overrides/pigweed.gni")
import("$dir_pw_build/python.gni")
pw_python_package("py") {
- setup = [
- "pyproject.toml",
- "setup.cfg",
- ]
+ generate_setup = {
+ metadata = {
+ name = "pw_trace_tokenized"
+ version = "0.0.1"
+ }
+ }
sources = [
"pw_trace_tokenized/__init__.py",
"pw_trace_tokenized/get_trace.py",
@@ -34,4 +36,5 @@ pw_python_package("py") {
]
pylintrc = "$dir_pigweed/.pylintrc"
mypy_ini = "$dir_pigweed/.mypy.ini"
+ proto_library = "..:protos"
}
diff --git a/pw_trace_tokenized/trace_service_pwpb.cc b/pw_trace_tokenized/trace_service_pwpb.cc
new file mode 100644
index 000000000..af4b90a7c
--- /dev/null
+++ b/pw_trace_tokenized/trace_service_pwpb.cc
@@ -0,0 +1,75 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_trace_tokenized/trace_service_pwpb.h"
+
+#include "pw_chrono/system_clock.h"
+
+namespace pw::trace {
+
+TraceService::TraceService(TokenizedTracer& tokenized_tracer,
+ stream::Writer& trace_writer)
+ : BaseTraceService(tokenized_tracer, trace_writer) {}
+
+Status TraceService::Start(
+ const proto::pwpb::StartRequest::Message& /*request*/,
+ proto::pwpb::StartResponse::Message& /*response*/) {
+ return BaseTraceService::Start();
+}
+
+Status TraceService::Stop(const proto::pwpb::StopRequest::Message& /*request*/,
+ proto::pwpb::StopResponse::Message& response) {
+ if (auto status = BaseTraceService::Stop(); status != pw::OkStatus()) {
+ return status;
+ }
+
+ response.file_id = transfer_id_;
+ return pw::OkStatus();
+}
+
+Status TraceService::GetClockParameters(
+ const proto::pwpb::ClockParametersRequest::Message& /*request*/,
+ proto::pwpb::ClockParametersResponse::Message& response) {
+ response.clock_parameters.tick_period_seconds_numerator =
+ PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR;
+ response.clock_parameters.tick_period_seconds_denominator =
+ PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR;
+
+ switch (chrono::SystemClock::epoch) {
+ case chrono::Epoch::kUnknown:
+ response.clock_parameters.epoch_type =
+ chrono::pwpb::EpochType::Enum::kUnknown;
+ break;
+ case chrono::Epoch::kTimeSinceBoot:
+ response.clock_parameters.epoch_type =
+ chrono::pwpb::EpochType::Enum::kTimeSinceBoot;
+ break;
+ case chrono::Epoch::kUtcWallClock:
+ response.clock_parameters.epoch_type =
+ chrono::pwpb::EpochType::Enum::kUtcWallClock;
+ break;
+ case chrono::Epoch::kGpsWallClock:
+ response.clock_parameters.epoch_type =
+ chrono::pwpb::EpochType::Enum::kGpsWallClock;
+ break;
+ case chrono::Epoch::kTaiWallClock:
+ response.clock_parameters.epoch_type =
+ chrono::pwpb::EpochType::Enum::kTaiWallClock;
+ break;
+ }
+
+ return pw::OkStatus();
+}
+
+} // namespace pw::trace
diff --git a/pw_trace_tokenized/trace_service_pwpb_test.cc b/pw_trace_tokenized/trace_service_pwpb_test.cc
new file mode 100644
index 000000000..008210193
--- /dev/null
+++ b/pw_trace_tokenized/trace_service_pwpb_test.cc
@@ -0,0 +1,130 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_trace_tokenized/trace_service_pwpb.h"
+
+#include "gtest/gtest.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_rpc/pwpb/test_method_context.h"
+#include "pw_stream/memory_stream.h"
+#include "pw_trace/trace.h"
+#include "pw_trace_tokenized/trace_tokenized.h"
+
+namespace pw::trace {
+
+class TraceServiceTest : public ::testing::Test {
+ public:
+ TraceServiceTest() {}
+
+ static constexpr uint32_t kTraceTransferHandlerId = 7;
+};
+
+TEST_F(TraceServiceTest, Start) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, Start)
+ context(tracer, writer);
+
+ ASSERT_FALSE(tracer.IsEnabled());
+ ASSERT_EQ(context.call({}), OkStatus());
+ ASSERT_TRUE(tracer.IsEnabled());
+
+ // multiple calls to start are disallowed
+ ASSERT_EQ(context.call({}), Status::FailedPrecondition());
+}
+
+TEST_F(TraceServiceTest, Stop) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, Stop)
+ context(tracer, writer);
+ context.service().SetTransferId(kTraceTransferHandlerId);
+
+ tracer.Enable(true);
+ PW_TRACE_INSTANT("TestTrace");
+
+ ASSERT_EQ(context.call({}), OkStatus());
+ ASSERT_FALSE(tracer.IsEnabled());
+ EXPECT_EQ(kTraceTransferHandlerId, context.response().file_id);
+ EXPECT_LT(0u, writer.bytes_written());
+}
+
+TEST_F(TraceServiceTest, StopNoTransferHandlerId) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, Stop)
+ context(tracer, writer);
+
+ tracer.Enable(true);
+ PW_TRACE_INSTANT("TestTrace");
+
+ ASSERT_EQ(context.call({}), OkStatus());
+ ASSERT_FALSE(tracer.IsEnabled());
+ EXPECT_FALSE(context.response().file_id.has_value());
+ EXPECT_LT(0u, writer.bytes_written());
+}
+
+TEST_F(TraceServiceTest, StopNotStarted) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, Stop)
+ context(tracer, writer);
+
+ // stopping while tracing is disabled results in FailedPrecondition
+ ASSERT_EQ(context.call({}), Status::FailedPrecondition());
+}
+
+TEST_F(TraceServiceTest, StopNoData) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, Stop)
+ context(tracer, writer);
+
+ tracer.Enable(true);
+
+ // stopping with no trace data results in Unavailable
+ ASSERT_EQ(context.call({}), Status::Unavailable());
+}
+
+TEST_F(TraceServiceTest, GetClockParameters) {
+ auto& tracer = trace::GetTokenizedTracer();
+
+ std::array<std::byte, PW_TRACE_BUFFER_SIZE_BYTES> dest_buffer;
+ stream::MemoryWriter writer(dest_buffer);
+
+ PW_PWPB_TEST_METHOD_CONTEXT(TraceService, GetClockParameters)
+ context(tracer, writer);
+
+ ASSERT_EQ(context.call({}), OkStatus());
+ EXPECT_EQ(PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR,
+ context.response().clock_parameters.tick_period_seconds_numerator);
+ EXPECT_EQ(
+ PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR,
+ context.response().clock_parameters.tick_period_seconds_denominator);
+ EXPECT_EQ(
+ static_cast<int32_t>(chrono::SystemClock::epoch),
+ static_cast<int32_t>(*context.response().clock_parameters.epoch_type));
+}
+
+} // namespace pw::trace
diff --git a/pw_transfer/client_test.cc b/pw_transfer/client_test.cc
index 0340ac0c0..323570dcf 100644
--- a/pw_transfer/client_test.cc
+++ b/pw_transfer/client_test.cc
@@ -21,7 +21,6 @@
#include "pw_bytes/array.h"
#include "pw_rpc/raw/client_testing.h"
#include "pw_rpc/test_helpers.h"
-#include "pw_thread/sleep.h"
#include "pw_thread/thread.h"
#include "pw_thread_stl/options.h"
#include "pw_transfer_private/chunk_testing.h"
@@ -611,8 +610,8 @@ TEST_F(ReadTransfer, ResendsParametersIfSentRepeatedChunkDuringRecovery) {
EXPECT_EQ(transfer_status, OkStatus());
}
-constexpr chrono::SystemClock::duration kTestTimeout =
- std::chrono::milliseconds(50);
+// Use a long timeout to avoid accidentally triggering timeouts.
+constexpr chrono::SystemClock::duration kTestTimeout = std::chrono::seconds(30);
constexpr uint8_t kTestRetries = 3;
TEST_F(ReadTransfer, Timeout_ResendsCurrentParameters) {
@@ -780,7 +779,7 @@ TEST_F(ReadTransfer, Timeout_EndsTransferAfterMaxRetries) {
EXPECT_EQ(transfer_status, Status::Unknown());
}
- // Sleep one more time after the final retry. The client should cancel the
+ // Time out one more time after the final retry. The client should cancel the
// transfer at this point. As no packets were received from the server, no
// final status chunk should be sent.
transfer_thread_.SimulateClientTimeout(14);
@@ -788,9 +787,10 @@ TEST_F(ReadTransfer, Timeout_EndsTransferAfterMaxRetries) {
EXPECT_EQ(transfer_status, Status::DeadlineExceeded());
- // After finishing the transfer, nothing else should be sent. Verify this by
- // waiting for a bit.
- this_thread::sleep_for(kTestTimeout * 4);
+ // After finishing the transfer, nothing else should be sent.
+ transfer_thread_.SimulateClientTimeout(14);
+ transfer_thread_.SimulateClientTimeout(14);
+ transfer_thread_.SimulateClientTimeout(14);
ASSERT_EQ(payloads.size(), 4u);
}
@@ -1466,7 +1466,7 @@ TEST_F(WriteTransfer, Timeout_EndsTransferAfterMaxRetries) {
EXPECT_EQ(transfer_status, Status::Unknown());
}
- // Sleep one more time after the final retry. The client should cancel the
+ // Time out one more time after the final retry. The client should cancel the
// transfer at this point. As no packets were received from the server, no
// final status chunk should be sent.
transfer_thread_.SimulateClientTimeout(13);
@@ -1474,9 +1474,10 @@ TEST_F(WriteTransfer, Timeout_EndsTransferAfterMaxRetries) {
EXPECT_EQ(transfer_status, Status::DeadlineExceeded());
- // After finishing the transfer, nothing else should be sent. Verify this by
- // waiting for a bit.
- this_thread::sleep_for(kTestTimeout * 4);
+ // After finishing the transfer, nothing else should be sent.
+ transfer_thread_.SimulateClientTimeout(13);
+ transfer_thread_.SimulateClientTimeout(13);
+ transfer_thread_.SimulateClientTimeout(13);
ASSERT_EQ(payloads.size(), 4u);
// Ensure we don't leave a dangling reference to transfer_status.
@@ -1580,12 +1581,14 @@ TEST_F(WriteTransfer, ManualCancel) {
EXPECT_EQ(chunk.type(), Chunk::Type::kStart);
// Get a response from the server, then cancel the transfer.
+ // This must request a smaller chunk than the entire available write data to
+ // prevent the client from trying to send an additional finish chunk.
context_.server().SendServerStream<Transfer::Write>(EncodeChunk(
Chunk(ProtocolVersion::kLegacy, Chunk::Type::kParametersRetransmit)
.set_session_id(15)
.set_offset(0)
- .set_window_end_offset(64)
- .set_max_chunk_size_bytes(32)));
+ .set_window_end_offset(16)
+ .set_max_chunk_size_bytes(16)));
transfer_thread_.WaitUntilEventIsProcessed();
ASSERT_EQ(payloads.size(), 2u);
diff --git a/pw_transfer/integration_test/cross_language_large_read_test.py b/pw_transfer/integration_test/cross_language_large_read_test.py
index 31d41e82e..37d51d9c2 100644
--- a/pw_transfer/integration_test/cross_language_large_read_test.py
+++ b/pw_transfer/integration_test/cross_language_large_read_test.py
@@ -35,7 +35,7 @@ import random
from google.protobuf import text_format
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import (
TransferConfig,
TransferIntegrationTest,
diff --git a/pw_transfer/integration_test/cross_language_large_write_test.py b/pw_transfer/integration_test/cross_language_large_write_test.py
index 0f195e2da..ee4c31878 100644
--- a/pw_transfer/integration_test/cross_language_large_write_test.py
+++ b/pw_transfer/integration_test/cross_language_large_write_test.py
@@ -35,7 +35,7 @@ import random
from google.protobuf import text_format
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import (
TransferConfig,
TransferIntegrationTest,
diff --git a/pw_transfer/integration_test/cross_language_medium_read_test.py b/pw_transfer/integration_test/cross_language_medium_read_test.py
index 8899d8f78..6adea14cd 100644
--- a/pw_transfer/integration_test/cross_language_medium_read_test.py
+++ b/pw_transfer/integration_test/cross_language_medium_read_test.py
@@ -37,7 +37,7 @@ import random
from google.protobuf import text_format
from pigweed.pw_transfer.integration_test import config_pb2
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness, TransferConfig
_ALL_LANGUAGES = ("cpp", "java", "python")
diff --git a/pw_transfer/integration_test/cross_language_medium_write_test.py b/pw_transfer/integration_test/cross_language_medium_write_test.py
index 5f8c314dc..a4e33db9b 100644
--- a/pw_transfer/integration_test/cross_language_medium_write_test.py
+++ b/pw_transfer/integration_test/cross_language_medium_write_test.py
@@ -37,7 +37,7 @@ import random
from google.protobuf import text_format
from pigweed.pw_transfer.integration_test import config_pb2
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness, TransferConfig
_ALL_LANGUAGES = ("cpp", "java", "python")
diff --git a/pw_transfer/integration_test/cross_language_small_test.py b/pw_transfer/integration_test/cross_language_small_test.py
index cfda8f0b4..fd05b7802 100644
--- a/pw_transfer/integration_test/cross_language_small_test.py
+++ b/pw_transfer/integration_test/cross_language_small_test.py
@@ -33,7 +33,7 @@ import itertools
from parameterized import parameterized
from pigweed.pw_transfer.integration_test import config_pb2
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness
_ALL_LANGUAGES = ("cpp", "java", "python")
diff --git a/pw_transfer/integration_test/expected_errors_test.py b/pw_transfer/integration_test/expected_errors_test.py
index 64d90a390..9a12b6879 100644
--- a/pw_transfer/integration_test/expected_errors_test.py
+++ b/pw_transfer/integration_test/expected_errors_test.py
@@ -41,7 +41,7 @@ from google.protobuf import text_format
from pigweed.pw_transfer.integration_test import config_pb2
from pigweed.pw_protobuf.pw_protobuf_protos import status_pb2
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness, TransferConfig
diff --git a/pw_transfer/integration_test/legacy_binaries_test.py b/pw_transfer/integration_test/legacy_binaries_test.py
index 84bcb0d0f..671260911 100644
--- a/pw_transfer/integration_test/legacy_binaries_test.py
+++ b/pw_transfer/integration_test/legacy_binaries_test.py
@@ -34,7 +34,7 @@ from parameterized import parameterized
import random
from pigweed.pw_transfer.integration_test import config_pb2
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness
from rules_python.python.runfiles import runfiles
diff --git a/pw_transfer/integration_test/multi_transfer_test.py b/pw_transfer/integration_test/multi_transfer_test.py
index 88ec079c1..90c792466 100644
--- a/pw_transfer/integration_test/multi_transfer_test.py
+++ b/pw_transfer/integration_test/multi_transfer_test.py
@@ -34,7 +34,7 @@ from parameterized import parameterized
import random
from typing import List
-import test_fixture
+from pigweed.pw_transfer.integration_test import test_fixture
from test_fixture import TransferIntegrationTestHarness, BasicTransfer
from pigweed.pw_transfer.integration_test import config_pb2
diff --git a/pw_transfer/public/pw_transfer/internal/config.h b/pw_transfer/public/pw_transfer/internal/config.h
index e17f748d7..04bfa78f8 100644
--- a/pw_transfer/public/pw_transfer/internal/config.h
+++ b/pw_transfer/public/pw_transfer/internal/config.h
@@ -87,9 +87,11 @@ inline constexpr uint16_t kDefaultMaxLifetimeRetries =
PW_TRANSFER_DEFAULT_MAX_LIFETIME_RETRIES;
inline constexpr chrono::SystemClock::duration kDefaultChunkTimeout =
- std::chrono::milliseconds(PW_TRANSFER_DEFAULT_TIMEOUT_MS);
+ chrono::SystemClock::for_at_least(
+ std::chrono::milliseconds(PW_TRANSFER_DEFAULT_TIMEOUT_MS));
inline constexpr chrono::SystemClock::duration kDefaultInitialChunkTimeout =
- std::chrono::milliseconds(PW_TRANSFER_DEFAULT_INITIAL_TIMEOUT_MS);
+ chrono::SystemClock::for_at_least(
+ std::chrono::milliseconds(PW_TRANSFER_DEFAULT_INITIAL_TIMEOUT_MS));
inline constexpr uint32_t kDefaultExtendWindowDivisor =
PW_TRANSFER_DEFAULT_EXTEND_WINDOW_DIVISOR;
diff --git a/pw_transfer/public/pw_transfer/internal/context.h b/pw_transfer/public/pw_transfer/internal/context.h
index 6baaa6edd..b4a80ec98 100644
--- a/pw_transfer/public/pw_transfer/internal/context.h
+++ b/pw_transfer/public/pw_transfer/internal/context.h
@@ -348,7 +348,7 @@ class Context {
// status chunk will be re-sent for every non-ACK chunk received,
// continually notifying the other end that the transfer is over.
static constexpr chrono::SystemClock::duration kFinalChunkAckTimeout =
- std::chrono::milliseconds(5000);
+ chrono::SystemClock::for_at_least(std::chrono::milliseconds(5000));
static constexpr chrono::SystemClock::time_point kNoTimeout =
chrono::SystemClock::time_point(chrono::SystemClock::duration(0));
diff --git a/pw_transfer/transfer_test.cc b/pw_transfer/transfer_test.cc
index 1ba291baa..fcc4e1999 100644
--- a/pw_transfer/transfer_test.cc
+++ b/pw_transfer/transfer_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Pigweed Authors
+// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
@@ -114,7 +114,10 @@ class ReadTransfer : public ::testing::Test {
: handler_(3, kData),
transfer_thread_(span(data_buffer_).first(max_chunk_size_bytes),
encode_buffer_),
- ctx_(transfer_thread_, 64),
+ ctx_(transfer_thread_,
+ 64,
+ // Use a long timeout to avoid accidentally triggering timeouts.
+ std::chrono::minutes(1)),
system_thread_(TransferThreadOptions(), transfer_thread_) {
ctx_.service().RegisterHandler(handler_);
@@ -394,10 +397,12 @@ TEST_F(ReadTransfer, MaxChunkSize_Client) {
}
TEST_F(ReadTransfer, HandlerIsClearedAfterTransfer) {
+ // Request an end offset smaller than the data size to prevent the server
+ // from sending a final chunk.
ctx_.SendClientStream(
EncodeChunk(Chunk(ProtocolVersion::kLegacy, Chunk::Type::kStart)
.set_session_id(3)
- .set_window_end_offset(64)
+ .set_window_end_offset(16)
.set_offset(0)));
ctx_.SendClientStream(
EncodeChunk(Chunk::Final(ProtocolVersion::kLegacy, 3, OkStatus())));
@@ -413,10 +418,12 @@ TEST_F(ReadTransfer, HandlerIsClearedAfterTransfer) {
handler_.prepare_read_called = false;
handler_.finalize_read_called = false;
+ // Request an end offset smaller than the data size to prevent the server
+ // from sending a final chunk.
ctx_.SendClientStream(
EncodeChunk(Chunk(ProtocolVersion::kLegacy, Chunk::Type::kStart)
.set_session_id(3)
- .set_window_end_offset(64)
+ .set_window_end_offset(16)
.set_offset(0)));
transfer_thread_.WaitUntilEventIsProcessed();
diff --git a/pw_unit_test/BUILD.bazel b/pw_unit_test/BUILD.bazel
index c4e5e5f4c..3077e5546 100644
--- a/pw_unit_test/BUILD.bazel
+++ b/pw_unit_test/BUILD.bazel
@@ -94,6 +94,7 @@ pw_cc_library(
pw_cc_library(
name = "googletest_handler_adapter",
+ testonly = True,
srcs = ["googletest_handler_adapter.cc"],
hdrs = ["public/pw_unit_test/googletest_handler_adapter.h"],
includes = ["public"],
@@ -105,6 +106,26 @@ pw_cc_library(
)
pw_cc_library(
+ name = "googletest_test_matchers",
+ testonly = True,
+ hdrs = ["public/pw_unit_test/googletest_test_matchers.h"],
+ includes = ["public"],
+ deps = [
+ "//pw_result",
+ "//pw_status",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+pw_cc_test(
+ name = "googletest_test_matchers_test",
+ srcs = ["googletest_test_matchers_test.cc"],
+ deps = [
+ ":googletest_test_matchers",
+ ],
+)
+
+pw_cc_library(
name = "simple_printing_event_handler",
srcs = ["simple_printing_event_handler.cc"],
hdrs = [
diff --git a/pw_unit_test/BUILD.gn b/pw_unit_test/BUILD.gn
index ba516e113..714acec38 100644
--- a/pw_unit_test/BUILD.gn
+++ b/pw_unit_test/BUILD.gn
@@ -116,6 +116,22 @@ pw_source_set("googletest_handler_adapter") {
sources = [ "googletest_handler_adapter.cc" ]
}
+pw_source_set("googletest_test_matchers") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_unit_test/googletest_test_matchers.h" ]
+ public_deps = [
+ "$dir_pw_third_party/googletest",
+ dir_pw_result,
+ dir_pw_status,
+ ]
+}
+
+pw_test("googletest_test_matchers_test") {
+ enable_if = pw_unit_test_GOOGLETEST_BACKEND != ""
+ sources = [ "googletest_test_matchers_test.cc" ]
+ deps = [ ":googletest_test_matchers" ]
+}
+
# Library providing an event handler which outputs human-readable text.
pw_source_set("simple_printing_event_handler") {
public_deps = [
@@ -311,4 +327,7 @@ pw_test_group("tests") {
":framework_test",
":static_library_support_test",
]
+ if (dir_pw_third_party_googletest != "") {
+ tests += [ ":googletest_test_matchers_test" ]
+ }
}
diff --git a/pw_unit_test/CMakeLists.txt b/pw_unit_test/CMakeLists.txt
index f66f9dfdc..6892f46d0 100644
--- a/pw_unit_test/CMakeLists.txt
+++ b/pw_unit_test/CMakeLists.txt
@@ -74,6 +74,27 @@ pw_add_library(pw_unit_test.googletest_style_event_handler STATIC
googletest_style_event_handler.cc
)
+if(NOT ${pw_unit_test_GOOGLETEST_BACKEND} STREQUAL "")
+ pw_add_library(pw_unit_test.googletest_test_matchers INTERFACE
+ HEADERS
+ public/pw_unit_test/googletest_test_matchers.h
+ PUBLIC_INCLUDES
+ public
+ PUBLIC_DEPS
+ pw_result
+ pw_status
+ ${pw_unit_test_GOOGLETEST_BACKEND}
+ )
+ pw_add_test(pw_unit_test.googletest_test_matchers_test
+ SOURCES
+ googletest_test_matchers_test.cc
+ PRIVATE_DEPS
+ pw_unit_test.googletest_test_matchers
+ GROUPS
+ pw_unit_test
+ )
+endif()
+
pw_add_library(pw_unit_test.simple_printing_main STATIC
SOURCES
simple_printing_main.cc
diff --git a/pw_unit_test/docs.rst b/pw_unit_test/docs.rst
index c8c139d30..85b54820a 100644
--- a/pw_unit_test/docs.rst
+++ b/pw_unit_test/docs.rst
@@ -38,7 +38,13 @@ GoogleTest compatibility
pw_unit_test implements a subset of GoogleTest. Supported features include:
* Test and test suite declarations.
-* Most ``EXPECT`` and ``ASSERT`` macros.
+* Most ``EXPECT`` and ``ASSERT`` macros, including ``EXPECT_OK`` and
+ ``ASSERT_OK`` for functions returning a status.
+* ``ASSERT_OK_AND_ASSIGN`` to test assigning a value when status is ``OK`` or
+ fail the test.
+* ``StatusIs`` matcher to expect a specific ``pw::Status`` other that ``OK``.
+* ``IsOkAndHolds`` matcher to expect an object's status is ``OK`` and the value
+ matches an expected value.
* Stream-style expectation messages, such as
``EXPECT_EQ(val, 5) << "Inputs: " << input``. Messages are currently ignored.
@@ -57,6 +63,49 @@ To request a feature addition, please `let us know
See `Using upstream GoogleTest`_ below for information
about using upstream GoogleTest instead.
+API Reference
+-------------
+
+Expectations
+````````````
+Expectations perform a check that when fails continues the test's execution
+while still marking the test as a failure. They're particularly handy when
+verifying multiple dimensions of the same feature so we can see all the errors
+at the same time.
+
+.. doxygendefine:: EXPECT_TRUE
+.. doxygendefine:: EXPECT_FALSE
+.. doxygendefine:: EXPECT_EQ
+.. doxygendefine:: EXPECT_NE
+.. doxygendefine:: EXPECT_GT
+.. doxygendefine:: EXPECT_GE
+.. doxygendefine:: EXPECT_LT
+.. doxygendefine:: EXPECT_LE
+.. doxygendefine:: EXPECT_NEAR
+.. doxygendefine:: EXPECT_FLOAT_EQ
+.. doxygendefine:: EXPECT_DOUBLE_EQ
+.. doxygendefine:: EXPECT_STREQ
+.. doxygendefine:: EXPECT_STRNE
+
+Assertions
+``````````
+Assertions work exactly the same as expectations, but stop the execution of the
+test as soon as a failed condition is met.
+
+.. doxygendefine:: ASSERT_TRUE
+.. doxygendefine:: ASSERT_FALSE
+.. doxygendefine:: ASSERT_EQ
+.. doxygendefine:: ASSERT_NE
+.. doxygendefine:: ASSERT_GT
+.. doxygendefine:: ASSERT_GE
+.. doxygendefine:: ASSERT_LT
+.. doxygendefine:: ASSERT_LE
+.. doxygendefine:: ASSERT_NEAR
+.. doxygendefine:: ASSERT_FLOAT_EQ
+.. doxygendefine:: ASSERT_DOUBLE_EQ
+.. doxygendefine:: ASSERT_STREQ
+.. doxygendefine:: ASSERT_STRNE
+
The EventHandler interface
==========================
The ``EventHandler`` class in ``public/pw_unit_test/event_handler.h`` defines
@@ -116,8 +165,7 @@ GoogleTest-style output using the shared
.. cpp:class:: PrintfEventHandler : public GoogleTestStyleEventHandler
- A C++14-compatible event handler that uses ``std::printf`` to output test
- results.
+ Event handler that uses ``std::printf`` to output test results.
.. cpp:namespace-pop::
@@ -702,14 +750,21 @@ the results of the test run.
.. code-block:: python
- from pw_hdlc.rpc import HdlcRpcClient
- from pw_unit_test.rpc import run_tests
+ import serial
- PROTO = Path(os.environ['PW_ROOT'],
- 'pw_unit_test/pw_unit_test_proto/unit_test.proto')
+ from pw_hdlc import rpc
+ from pw_unit_test.rpc import run_tests
- client = HdlcRpcClient(serial.Serial(device, baud), PROTO)
- run_tests(client.rpcs())
+ PROTO = Path(
+ os.environ['PW_ROOT'],
+ 'pw_unit_test/pw_unit_test_proto/unit_test.proto'
+ )
+ serial_device = serial.Serial(device, baud)
+ with rpc.SerialReader(serial_device) as reader:
+ with rpc.HdlcRpcClient(
+ reader, PROTO, rpc.default_channels(serial_device.write)
+ ) as client:
+ run_tests(client.rpcs())
pw_unit_test.rpc
----------------
diff --git a/pw_unit_test/framework_test.cc b/pw_unit_test/framework_test.cc
index 2e1fe90c4..211c665ce 100644
--- a/pw_unit_test/framework_test.cc
+++ b/pw_unit_test/framework_test.cc
@@ -57,6 +57,46 @@ TEST(PigweedTest, ExpectBasicComparisons) {
ASSERT_LE(-2, -2);
}
+TEST(PigweedTest, ExpectNearComparisons) {
+ EXPECT_NEAR(1, 2, 1);
+ ASSERT_NEAR(1, 2, 1);
+
+ EXPECT_NEAR(-5, 5, 10);
+ ASSERT_NEAR(-5, 5, 10);
+
+ int x = 17;
+ int epsilon = 5;
+
+ EXPECT_NEAR(x, 15, epsilon);
+ ASSERT_NEAR(x, 15, epsilon);
+}
+
+TEST(PigweedTest, ExpectFloatComparisons) {
+ EXPECT_FLOAT_EQ(5.0f, 10.0f / 2);
+ ASSERT_FLOAT_EQ(5.0f, 10.0f / 2);
+
+ EXPECT_FLOAT_EQ(-0.5f, -5.0f / 10);
+ ASSERT_FLOAT_EQ(-0.5f, -5.0f / 10);
+
+ float x = 17.0f / 20.0f;
+
+ EXPECT_FLOAT_EQ(x, 17.0f / 20.0f);
+ ASSERT_FLOAT_EQ(x, 17.0f / 20.0f);
+}
+
+TEST(PigweedTest, ExpectDoubleComparisons) {
+ EXPECT_DOUBLE_EQ(5.0, 10.0 / 2);
+ ASSERT_DOUBLE_EQ(5.0, 10.0 / 2);
+
+ EXPECT_DOUBLE_EQ(-0.5, -5.0 / 10);
+ ASSERT_DOUBLE_EQ(-0.5, -5.0 / 10);
+
+ double x = 17.0 / 20.0;
+
+ EXPECT_DOUBLE_EQ(x, 17.0 / 20.0);
+ ASSERT_DOUBLE_EQ(x, 17.0 / 20.0);
+}
+
TEST(PigweedTest, ExpectStringEquality) {
EXPECT_STREQ("", "");
EXPECT_STREQ("Yes", "Yes");
diff --git a/pw_unit_test/googletest_test_matchers_test.cc b/pw_unit_test/googletest_test_matchers_test.cc
new file mode 100644
index 000000000..58d7a5bdf
--- /dev/null
+++ b/pw_unit_test/googletest_test_matchers_test.cc
@@ -0,0 +1,193 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#include "pw_unit_test/googletest_test_matchers.h"
+
+#include <cstdlib>
+
+#include "pw_status/status.h"
+
+namespace pw::unit_test {
+namespace {
+
+using ::testing::Eq;
+using ::testing::Not;
+
+TEST(TestMatchers, AssertOk) { ASSERT_OK(OkStatus()); }
+TEST(TestMatchers, AssertOkStatusWithSize) { ASSERT_OK(StatusWithSize(123)); }
+TEST(TestMatchers, AssertOkResult) { ASSERT_OK(Result<int>(123)); }
+
+TEST(TestMatchers, ExpectOk) { EXPECT_OK(OkStatus()); }
+TEST(TestMatchers, ExpectOkStatusWithSize) { EXPECT_OK(StatusWithSize(123)); }
+TEST(TestMatchers, ExpectOkResult) { EXPECT_OK(Result<int>(123)); }
+
+TEST(TestMatchers, StatusIsSuccess) {
+ EXPECT_THAT(OkStatus(), StatusIs(OkStatus()));
+ EXPECT_THAT(Status::Cancelled(), StatusIs(Status::Cancelled()));
+ EXPECT_THAT(Status::Unknown(), StatusIs(Status::Unknown()));
+ EXPECT_THAT(Status::InvalidArgument(), StatusIs(Status::InvalidArgument()));
+ EXPECT_THAT(Status::DeadlineExceeded(), StatusIs(Status::DeadlineExceeded()));
+ EXPECT_THAT(Status::NotFound(), StatusIs(Status::NotFound()));
+ EXPECT_THAT(Status::AlreadyExists(), StatusIs(Status::AlreadyExists()));
+ EXPECT_THAT(Status::PermissionDenied(), StatusIs(Status::PermissionDenied()));
+ EXPECT_THAT(Status::ResourceExhausted(),
+ StatusIs(Status::ResourceExhausted()));
+ EXPECT_THAT(Status::FailedPrecondition(),
+ StatusIs(Status::FailedPrecondition()));
+ EXPECT_THAT(Status::Aborted(), StatusIs(Status::Aborted()));
+ EXPECT_THAT(Status::OutOfRange(), StatusIs(Status::OutOfRange()));
+ EXPECT_THAT(Status::Unimplemented(), StatusIs(Status::Unimplemented()));
+ EXPECT_THAT(Status::Internal(), StatusIs(Status::Internal()));
+ EXPECT_THAT(Status::Unavailable(), StatusIs(Status::Unavailable()));
+ EXPECT_THAT(Status::DataLoss(), StatusIs(Status::DataLoss()));
+ EXPECT_THAT(Status::Unauthenticated(), StatusIs(Status::Unauthenticated()));
+}
+
+TEST(TestMatchers, StatusIsSuccessStatusWithSize) {
+ EXPECT_THAT(StatusWithSize(), StatusIs(OkStatus()));
+ EXPECT_THAT(StatusWithSize::Cancelled(), StatusIs(Status::Cancelled()));
+ EXPECT_THAT(StatusWithSize::Unknown(), StatusIs(Status::Unknown()));
+ EXPECT_THAT(StatusWithSize::InvalidArgument(),
+ StatusIs(Status::InvalidArgument()));
+ EXPECT_THAT(StatusWithSize::DeadlineExceeded(),
+ StatusIs(Status::DeadlineExceeded()));
+ EXPECT_THAT(StatusWithSize::NotFound(), StatusIs(Status::NotFound()));
+ EXPECT_THAT(StatusWithSize::AlreadyExists(),
+ StatusIs(Status::AlreadyExists()));
+ EXPECT_THAT(StatusWithSize::PermissionDenied(),
+ StatusIs(Status::PermissionDenied()));
+ EXPECT_THAT(StatusWithSize::ResourceExhausted(),
+ StatusIs(Status::ResourceExhausted()));
+ EXPECT_THAT(StatusWithSize::FailedPrecondition(),
+ StatusIs(Status::FailedPrecondition()));
+ EXPECT_THAT(StatusWithSize::Aborted(), StatusIs(Status::Aborted()));
+ EXPECT_THAT(StatusWithSize::OutOfRange(), StatusIs(Status::OutOfRange()));
+ EXPECT_THAT(StatusWithSize::Unimplemented(),
+ StatusIs(Status::Unimplemented()));
+ EXPECT_THAT(StatusWithSize::Internal(), StatusIs(Status::Internal()));
+ EXPECT_THAT(StatusWithSize::Unavailable(), StatusIs(Status::Unavailable()));
+ EXPECT_THAT(StatusWithSize::DataLoss(), StatusIs(Status::DataLoss()));
+ EXPECT_THAT(StatusWithSize::Unauthenticated(),
+ StatusIs(Status::Unauthenticated()));
+}
+
+TEST(TestMatchers, StatusIsSuccessOkResult) {
+ const Result<int> result = 46;
+ EXPECT_THAT(result, StatusIs(OkStatus()));
+}
+
+TEST(TestMatchers, StatusIsSuccessResult) {
+ EXPECT_THAT(Result<int>(Status::Cancelled()), StatusIs(Status::Cancelled()));
+ EXPECT_THAT(Result<int>(Status::Unknown()), StatusIs(Status::Unknown()));
+ EXPECT_THAT(Result<int>(Status::InvalidArgument()),
+ StatusIs(Status::InvalidArgument()));
+ EXPECT_THAT(Result<int>(Status::DeadlineExceeded()),
+ StatusIs(Status::DeadlineExceeded()));
+ EXPECT_THAT(Result<int>(Status::NotFound()), StatusIs(Status::NotFound()));
+ EXPECT_THAT(Result<int>(Status::AlreadyExists()),
+ StatusIs(Status::AlreadyExists()));
+ EXPECT_THAT(Result<int>(Status::PermissionDenied()),
+ StatusIs(Status::PermissionDenied()));
+ EXPECT_THAT(Result<int>(Status::ResourceExhausted()),
+ StatusIs(Status::ResourceExhausted()));
+ EXPECT_THAT(Result<int>(Status::FailedPrecondition()),
+ StatusIs(Status::FailedPrecondition()));
+ EXPECT_THAT(Result<int>(Status::Aborted()), StatusIs(Status::Aborted()));
+ EXPECT_THAT(Result<int>(Status::OutOfRange()),
+ StatusIs(Status::OutOfRange()));
+ EXPECT_THAT(Result<int>(Status::Unimplemented()),
+ StatusIs(Status::Unimplemented()));
+ EXPECT_THAT(Result<int>(Status::Internal()), StatusIs(Status::Internal()));
+ EXPECT_THAT(Result<int>(Status::Unavailable()),
+ StatusIs(Status::Unavailable()));
+ EXPECT_THAT(Result<int>(Status::DataLoss()), StatusIs(Status::DataLoss()));
+ EXPECT_THAT(Result<int>(Status::Unauthenticated()),
+ StatusIs(Status::Unauthenticated()));
+}
+
+TEST(IsOkAndHoldsTest, StatusWithSize) {
+ const auto status_with_size = StatusWithSize{OkStatus(), 42};
+ EXPECT_THAT(status_with_size, IsOkAndHolds(Eq(42u)));
+}
+
+TEST(IsOkAndHoldsTest, Result) {
+ auto value = Result<int>{42};
+ EXPECT_THAT(value, IsOkAndHolds(Eq(42)));
+}
+
+TEST(IsOkAndHoldsTest, BadStatusWithSize) {
+ const auto status_with_size = StatusWithSize{Status::InvalidArgument(), 0};
+ EXPECT_THAT(status_with_size, Not(IsOkAndHolds(Eq(42u))));
+}
+
+TEST(IsOkAndHoldsTest, WrongStatusWithSize) {
+ const auto status_with_size = StatusWithSize{OkStatus(), 100};
+ EXPECT_THAT(status_with_size, IsOkAndHolds(Not(Eq(42u))));
+ EXPECT_THAT(status_with_size, Not(IsOkAndHolds(Eq(42u))));
+}
+
+TEST(IsOkAndHoldsTest, BadResult) {
+ const auto value = Result<int>{Status::InvalidArgument()};
+ EXPECT_THAT(value, Not(IsOkAndHolds(Eq(42))));
+}
+
+TEST(IsOkAndHoldsTest, WrongResult) {
+ const auto value = Result<int>{100};
+ EXPECT_THAT(value, IsOkAndHolds(Not(Eq(42))));
+ EXPECT_THAT(value, Not(IsOkAndHolds(Eq(42))));
+}
+
+TEST(AssertOkAndAssignTest, OkResult) {
+ const auto value = Result<int>(5);
+
+ int existing_value = 0;
+ ASSERT_OK_AND_ASSIGN(existing_value, value);
+ EXPECT_EQ(5, existing_value);
+
+ ASSERT_OK_AND_ASSIGN(int declare_and_assign, value);
+ EXPECT_EQ(5, declare_and_assign);
+
+ ASSERT_OK_AND_ASSIGN(auto& declare_auto_ref_and_assign, value);
+ EXPECT_EQ(5, declare_auto_ref_and_assign);
+}
+
+// The following test is commented out and is only for checking what
+// failure cases would look like. For example, when uncommenting the test,
+// the output is:
+//
+// ERR pw_unit_test/googletest_test_matchers_test.cc:50: Failure
+// ERR Expected:
+// ERR Actual: Value of: OkStatus()
+// Expected: has status UNKNOWN
+// Actual: 4-byte object <00-00 00-00>, which has status OK
+
+// ERR pw_unit_test/googletest_test_matchers_test.cc:51: Failure
+// ERR Expected:
+// ERR Actual: Value of: Status::Unknown()
+// Expected: is OK
+// Actual: 4-byte object <02-00 00-00>, which has status UNKNOWN
+
+// ERR pw_unit_test/googletest_test_matchers_test.cc:52: Failure
+// ERR Expected:
+// ERR Actual: Value of: Status::Unknown()
+// Expected: is OK
+// Actual: 4-byte object <02-00 00-00>, which has status UNKNOWN
+//
+// TEST(TestMatchers, SampleFailures) {
+// EXPECT_THAT(OkStatus(), StatusIs(Status::Unknown()));
+// EXPECT_OK(Status::Unknown());
+// ASSERT_OK(Status::Unknown());
+// }
+
+} // namespace
+} // namespace pw::unit_test \ No newline at end of file
diff --git a/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h b/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h
new file mode 100644
index 000000000..e7965aaa5
--- /dev/null
+++ b/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h
@@ -0,0 +1,256 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+#include "gmock/gmock-matchers.h"
+#include "gtest/gtest.h"
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+#include "pw_status/status_with_size.h"
+
+namespace pw::unit_test {
+namespace internal {
+// Gets the pw::Status of different types of objects with a pw::Status for
+// Matchers that check the status.
+inline constexpr Status GetStatus(Status status) { return status; }
+
+inline constexpr Status GetStatus(StatusWithSize status_with_size) {
+ return status_with_size.status();
+}
+
+template <typename T>
+inline constexpr Status GetStatus(const Result<T>& result) {
+ return result.status();
+}
+
+// Gets the value of an object whose value is guarded by a ``pw::OkStatus()``.
+// Used by Matchers.
+constexpr size_t GetValue(StatusWithSize status_with_size) {
+ return status_with_size.size();
+}
+
+template <typename V>
+constexpr const V& GetValue(const Result<V>& result) {
+ return result.value();
+}
+
+template <typename V>
+constexpr V GetValue(Result<V>&& result) {
+ return std::move(result).value();
+}
+
+// Implements IsOk().
+class IsOkMatcher {
+ public:
+ using is_gtest_matcher = void;
+
+ void DescribeTo(std::ostream* os) const { *os << "is OK"; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "isn't OK"; }
+
+ template <typename T>
+ bool MatchAndExplain(T&& actual_value,
+ ::testing::MatchResultListener* listener) const {
+ const auto status = GetStatus(actual_value);
+ if (!status.ok()) {
+ *listener << "which has status " << pw_StatusString(status);
+ return false;
+ }
+ return true;
+ }
+};
+
+// Implements IsOkAndHolds(m) as a monomorphic matcher.
+template <typename StatusType>
+class IsOkAndHoldsMatcherImpl {
+ public:
+ using is_gtest_matcher = void;
+ using ValueType = decltype(GetValue(std::declval<StatusType>()));
+
+ // NOLINTBEGIN(bugprone-forwarding-reference-overload)
+ template <typename InnerMatcher>
+ explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
+ : inner_matcher_(::testing::SafeMatcherCast<const ValueType&>(
+ std::forward<InnerMatcher>(inner_matcher))) {}
+ // NOLINTEND(bugprone-forwarding-reference-overload)
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "is OK and has a value that ";
+ inner_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "isn't OK or has a value that ";
+ inner_matcher_.DescribeNegationTo(os);
+ }
+
+ bool MatchAndExplain(const StatusType& actual_value,
+ ::testing::MatchResultListener* listener) const {
+ const auto& status = GetStatus(actual_value);
+ if (!status.ok()) {
+ *listener << "which has status " << pw_StatusString(status);
+ return false;
+ }
+
+ const auto& value = GetValue(actual_value);
+ *listener << "which contains value " << ::testing::PrintToString(value);
+
+ ::testing::StringMatchResultListener inner_listener;
+ const bool matches = inner_matcher_.MatchAndExplain(value, &inner_listener);
+ const std::string inner_explanation = inner_listener.str();
+ if (!inner_explanation.empty()) {
+ *listener << ", " << inner_explanation;
+ }
+
+ return matches;
+ }
+
+ private:
+ const ::testing::Matcher<const ValueType&> inner_matcher_;
+};
+
+// Implements IsOkAndHolds(m) as a polymorphic matcher.
+//
+// We have to manually create it as a class instead of using the
+// `::testing::MakePolymorphicMatcher()` helper because of the custom conversion
+// to Matcher<T>.
+template <typename InnerMatcher>
+class IsOkAndHoldsMatcher {
+ public:
+ explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
+ : inner_matcher_(std::move(inner_matcher)) {}
+
+ // NOLINTBEGIN(google-explicit-constructor)
+ template <typename StatusType>
+ operator ::testing::Matcher<StatusType>() const {
+ return ::testing::Matcher<StatusType>(
+ internal::IsOkAndHoldsMatcherImpl<const StatusType&>(inner_matcher_));
+ }
+ // NOLINTEND(google-explicit-constructor)
+
+ private:
+ const InnerMatcher inner_matcher_;
+};
+
+// Implements StatusIs().
+class StatusIsMatcher {
+ public:
+ explicit StatusIsMatcher(Status expected_status)
+ : expected_status_(expected_status) {}
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "has status " << pw_StatusString(expected_status_);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "does not have status " << pw_StatusString(expected_status_);
+ }
+
+ template <typename T>
+ bool MatchAndExplain(T&& actual_value,
+ ::testing::MatchResultListener* listener) const {
+ const auto status = GetStatus(actual_value);
+ if (status != expected_status_) {
+ *listener << "which has status " << pw_StatusString(status);
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ const Status expected_status_;
+};
+
+} // namespace internal
+
+/// Macros for testing the results of functions that return ``pw::Status``,
+/// ``pw::StatusWithSize``, or ``pw::Result<T>`` (for any T).
+#define EXPECT_OK(expression) EXPECT_THAT(expression, ::pw::unit_test::IsOk())
+#define ASSERT_OK(expression) ASSERT_THAT(expression, ::pw::unit_test::IsOk())
+
+/// Returns a gMock matcher that matches a `pw::Status`, `pw::StatusWithSize`,
+/// or `pw::Result<T>` (for any T) which is OK.
+inline internal::IsOkMatcher IsOk() { return {}; }
+
+/// Returns a gMock matcher that matches a `pw::Status`, `pw::StatusWithSize`,
+/// or `pw::Result<T>` (for any T) which has the given status.
+inline auto StatusIs(Status expected_status) {
+ return ::testing::MakePolymorphicMatcher(
+ internal::StatusIsMatcher(expected_status));
+}
+
+/// Returns a gMock matcher that matches a `pw::StatusWithSize` or
+/// `pw::Result<T>` (for any T) which is OK and holds a value matching the inner
+/// matcher.
+template <typename InnerMatcher>
+inline internal::IsOkAndHoldsMatcher<InnerMatcher> IsOkAndHolds(
+ InnerMatcher&& inner_matcher) {
+ return internal::IsOkAndHoldsMatcher<InnerMatcher>(
+ std::forward<InnerMatcher>(inner_matcher));
+}
+
+/// Executes an expression that returns a `pw::Result` or `pw::StatusWithSize`
+/// and assigns or moves that value to lhs if the error code is OK. If the
+// status is non-OK, generates a test failure and returns from the current
+/// function, which must have a void return type.
+///
+/// The MOVE variant moves the content out of the `pw::Result` and into lhs.
+/// This variant is required for move-only types.
+//
+/// Example: Declaring and initializing a new value. E.g.:
+/// ASSERT_OK_AND_ASSIGN(auto value, MaybeGetValue(arg));
+/// ASSERT_OK_AND_ASSIGN(const ValueType& value, MaybeGetValue(arg));
+/// ASSERT_OK_AND_MOVE(auto ptr, MaybeGetUniquePtr(arg))
+///
+/// Example: Assigning to an existing value
+/// ValueType value;
+/// ASSERT_OK_AND_ASSIGN(value, MaybeGetValue(arg));
+///
+/// The value assignment example would expand into something like:
+/// auto status_or_value = MaybeGetValue(arg);
+/// ASSERT_OK(status_or_value.status());
+/// value = status_or_value.ValueOrDie();
+///
+/// WARNING: ASSERT_OK_AND_ASSIGN (and the move variant) expand into multiple
+/// statements; it cannot be used in a single statement (e.g. as the body of
+/// an if statement without {})!
+#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
+ ASSERT_OK_AND_ASSIGN_DETAIL(UNIQUE_IDENTIFIER_DETAIL(__LINE__), lhs, rexpr)
+#define ASSERT_OK_AND_MOVE(lhs, rexpr) \
+ ASSERT_OK_AND_MOVE_DETAIL(UNIQUE_IDENTIFIER_DETAIL(__LINE__), lhs, rexpr)
+
+// NOLINTBEGIN(bugprone-macro-parentheses)
+// The suggestion would produce bad code.
+#define ASSERT_OK_AND_ASSIGN_DETAIL(result, lhs, rexpr) \
+ const auto& result = (rexpr); \
+ if (!result.ok()) { \
+ FAIL() << #rexpr << " is not OK."; \
+ } \
+ lhs = ::pw::unit_test::internal::GetValue(result);
+#define ASSERT_OK_AND_MOVE_DETAIL(result, lhs, rexpr) \
+ auto&& result = (rexpr); \
+ if (!result.ok()) { \
+ FAIL() << #rexpr << " is not OK."; \
+ } \
+ lhs = ::pw::unit_test::internal::GetValue(std::move(result));
+// NOLINTEND(bugprone-macro-parentheses)
+
+#define UNIQUE_IDENTIFIER_DETAIL(line) UNIQUE_IDENTIFIER_EXPANDED_DETAIL(line)
+#define UNIQUE_IDENTIFIER_EXPANDED_DETAIL(line) \
+ _assert_ok_and_assign_unique_name_##line
+
+} // namespace pw::unit_test
diff --git a/pw_unit_test/public/pw_unit_test/internal/framework.h b/pw_unit_test/public/pw_unit_test/internal/framework.h
index fce2293d1..be93c64f8 100644
--- a/pw_unit_test/public/pw_unit_test/internal/framework.h
+++ b/pw_unit_test/public/pw_unit_test/internal/framework.h
@@ -52,26 +52,163 @@
#define FRIEND_TEST(test_suite_name, test_name) \
friend class test_suite_name##_##test_name##_Test
+/// @def EXPECT_TRUE
+/// Verifies that @p expr evaluates to true.
+/// @param expr Condition to evaluate
#define EXPECT_TRUE(expr) _PW_TEST_EXPECT(_PW_TEST_BOOL(expr, true))
+
+/// @def EXPECT_FALSE
+/// Verifies that @p expr evaluates to false.
+/// @param expr Condition to evaluate
#define EXPECT_FALSE(expr) _PW_TEST_EXPECT(_PW_TEST_BOOL(expr, false))
+
+/// @def EXPECT_EQ
+/// Verifies that @p lhs == @p rhs
+/// <p>
+/// Does pointer equality on pointers. If used on two C strings, it tests if
+/// they are in the same memory location, not if they have the same value. Use
+/// #EXPECT_STREQ to compare C strings (e.g. <code>const char*</code>) by value.
+/// <p>
+/// When comparing a pointer to <code>NULL</code> use
+/// <code>EXPECT_EQ(ptr, nullptr)</code> instead of
+/// <code>EXPECT_EQ(ptr, NULL)</code>.
+/// @param lhs The left side of the equality comparison
+/// @param rhs The right side of the equality comparison
#define EXPECT_EQ(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, ==))
+
+/// @def EXPECT_NE
+/// Verifies that @p lhs != @p rhs
+/// <p>
+/// Does pointer equality on pointers. If used on two C strings, it tests if
+/// they are in different memory locations, not if they have different values.
+/// Use #EXPECT_STRNE to compare C strings (e.g. <code>const char*</code>) by
+/// value.
+/// <p>
+/// When comparing a pointer to <code>NULL</code>, use
+/// <code>EXPECT_NE(ptr, nullptr)</code> instead of
+/// <code>EXPECT_NE(ptr, NULL)</code>.
+/// @param lhs The left side of the inequality comparison
+/// @param rhs The right side of the inequality comparison
#define EXPECT_NE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, !=))
+
+/// @def EXPECT_GT
+/// Verifies that @p lhs > @p rhs
+/// @param lhs The left side of the comparison
+/// @param rhs The right side of the comparison
#define EXPECT_GT(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, >))
+
+/// @def EXPECT_GE
+/// Verifies that @p lhs >= @p rhs
+/// @param lhs The left side of the comparison
+/// @param rhs The right side of the comparison
#define EXPECT_GE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, >=))
+
+/// @def EXPECT_LT
+/// Verifies that @p lhs < @p rhs
+/// @param lhs The left side of the comparison
+/// @param rhs The right side of the comparison
#define EXPECT_LT(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <))
+
+/// @def EXPECT_LE
+/// Verifies that @p lhs <= @p rhs
+/// @param lhs The left side of the comparison
+/// @param rhs The right side of the comparison
#define EXPECT_LE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <=))
+
+/// @def EXPECT_NEAR
+/// Verifies that the difference between @p lhs and @p rhs does not exceed the
+/// absolute error bound @p epsilon.
+/// @param lhs The left side of the comparison
+/// @param rhs The right side of the comparison
+/// @param epsilon The maximum difference between @p lhs and @p rhs
+#define EXPECT_NEAR(lhs, rhs, epsilon) \
+ _PW_TEST_EXPECT(_PW_TEST_NEAR(lhs, rhs, epsilon))
+
+/// @def EXPECT_FLOAT_EQ
+/// Verifies that the two float values @p rhs and @p lhs are approximately
+/// equal, to within 4 ULPs from each other.
+/// @param lhs The left side of the equality comparison
+/// @param rhs The right side of the equality comparison
+#define EXPECT_FLOAT_EQ(lhs, rhs) \
+ _PW_TEST_EXPECT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon()))
+
+/// @def EXPECT_DOUBLE_EQ
+/// Verifies that the two double values @p rhs and @p lhs are approximately
+/// equal, to within 4 ULPs from each other.
+/// @param lhs The left side of the equality comparison
+/// @param rhs The right side of the equality comparison
+#define EXPECT_DOUBLE_EQ(lhs, rhs) \
+ _PW_TEST_EXPECT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon()))
+
+/// @def EXPECT_STREQ
+/// Verifies that the two C strings @p lhs and @p rhs have the same contents.
+/// @param lhs The left side of the equality comparison
+/// @param rhs The right side of the equality comparison
#define EXPECT_STREQ(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, ==))
+
+/// @def EXPECT_STRNE
+/// Verifies that the two C strings @p lhs and @p rhs have different content
+/// @param lhs The left side of the inequality comparison
+/// @param rhs The right side of the inequality comparison
#define EXPECT_STRNE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, !=))
+/// @def ASSERT_TRUE
+/// @see EXPECT_TRUE
#define ASSERT_TRUE(expr) _PW_TEST_ASSERT(_PW_TEST_BOOL(expr, true))
+
+/// @def ASSERT_FALSE
+/// @see EXPECT_FALSE
#define ASSERT_FALSE(expr) _PW_TEST_ASSERT(_PW_TEST_BOOL(expr, false))
+
+/// @def ASSERT_EQ
+/// @see EXPECT_EQ
#define ASSERT_EQ(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, ==))
+
+/// @def ASSERT_NE
+/// @see EXPECT_NE
#define ASSERT_NE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, !=))
+
+/// @def ASSERT_GT
+/// @see EXPECT_GT
#define ASSERT_GT(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, >))
+
+/// @def ASSERT_GE
+/// @see EXPECT_GE
#define ASSERT_GE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, >=))
+
+/// @def ASSERT_LT
+/// @see EXPECT_LT
#define ASSERT_LT(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <))
+
+/// @def ASSERT_LE
+/// @see EXPECT_LE
#define ASSERT_LE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <=))
+
+/// @def ASSERT_NEAR
+/// @see EXPECT_NEAR
+#define ASSERT_NEAR(lhs, rhs, epsilon) \
+ _PW_TEST_ASSERT(_PW_TEST_NEAR(lhs, rhs, epsilon))
+
+/// @def ASSERT_FLOAT_EQ
+/// @see EXPECT_FLOAT_EQ
+#define ASSERT_FLOAT_EQ(lhs, rhs) \
+ _PW_TEST_ASSERT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon()))
+
+/// @def ASSERT_DOUBLE_EQ
+/// @see EXPECT_DOUBLE_EQ
+#define ASSERT_DOUBLE_EQ(lhs, rhs) \
+ _PW_TEST_ASSERT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon()))
+
+/// @def ASSERT_STREQ
+/// @see EXPECT_STREQ
#define ASSERT_STREQ(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, ==))
+
+/// @def ASSERT_STRNE
+/// @see EXPECT_STRNE
#define ASSERT_STRNE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, !=))
// Generates a non-fatal failure with a generic message.
@@ -309,6 +446,37 @@ class Framework {
framework.EndCurrentTest();
}
+ template <typename Expectation, typename Lhs, typename Rhs, typename Epsilon>
+ bool CurrentTestExpect(Expectation expectation,
+ const Lhs& lhs,
+ const Rhs& rhs,
+ const Epsilon& epsilon,
+ const char* expression,
+ int line) {
+ // Size of the buffer into which to write the string with the evaluated
+ // version of the arguments. This buffer is allocated on the unit test's
+ // stack, so it shouldn't be too large.
+ // TODO(hepler): Make this configurable.
+ [[maybe_unused]] constexpr size_t kExpectationBufferSizeBytes = 192;
+
+ const bool success = expectation(lhs, rhs, epsilon);
+ CurrentTestExpectSimple(
+ expression,
+#if PW_CXX_STANDARD_IS_SUPPORTED(17)
+ MakeString<kExpectationBufferSizeBytes>(ConvertForPrint(lhs),
+ " within ",
+ ConvertForPrint(epsilon),
+ " of ",
+ ConvertForPrint(rhs))
+ .c_str(),
+#else
+ "(evaluation requires C++17)",
+#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
+ line,
+ success);
+ return success;
+ }
+
// Runs an expectation function for the currently active test case.
template <typename Expectation, typename Lhs, typename Rhs>
bool CurrentTestExpect(Expectation expectation,
@@ -625,6 +793,17 @@ inline void SetTestSuitesToRun(span<std::string_view> test_suites) {
#lhs " " #op " " #rhs, \
__LINE__)
+#define _PW_TEST_NEAR(lhs, rhs, epsilon) \
+ ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \
+ [](const auto& _pw_lhs, const auto& _pw_rhs, const auto& _pw_epsilon) { \
+ return std::abs(_pw_lhs - _pw_rhs) <= _pw_epsilon; \
+ }, \
+ (lhs), \
+ (rhs), \
+ (epsilon), \
+ #lhs " within " #epsilon " of " #rhs, \
+ __LINE__)
+
#define _PW_TEST_C_STR(lhs, rhs, op) \
::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \
[](const auto& _pw_lhs, const auto& _pw_rhs) { \
diff --git a/pw_unit_test/py/pw_unit_test/test_runner.py b/pw_unit_test/py/pw_unit_test/test_runner.py
index 93307f096..1869ce026 100644
--- a/pw_unit_test/py/pw_unit_test/test_runner.py
+++ b/pw_unit_test/py/pw_unit_test/test_runner.py
@@ -163,6 +163,7 @@ class TestRunner:
tests: Iterable[Test],
env: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
+ verbose: bool = False,
) -> None:
self._executable: str = executable
self._args: Sequence[str] = args
@@ -170,6 +171,7 @@ class TestRunner:
self._env: Dict[str, str] = env or {}
self._timeout = timeout
self._result_sink: Optional[Dict[str, str]] = None
+ self.verbose = verbose
# Access go/result-sink, if available.
ctx_path = Path(os.environ.get("LUCI_CONTEXT", ''))
@@ -203,7 +205,10 @@ class TestRunner:
start_time = time.monotonic()
try:
process = await pw_cli.process.run_async(
- *command, env=self._env, timeout=self._timeout
+ *command,
+ env=self._env,
+ timeout=self._timeout,
+ log_output=self.verbose,
)
except subprocess.CalledProcessError as err:
_LOG.error(err)
@@ -487,6 +492,7 @@ async def find_and_run_tests(
timeout: Optional[float] = None,
group: Optional[Sequence[str]] = None,
test: Optional[Sequence[str]] = None,
+ verbose: bool = False,
) -> int:
"""Runs some unit tests."""
@@ -497,7 +503,9 @@ async def find_and_run_tests(
envvars = parse_env(env)
- test_runner = TestRunner(runner, runner_args, tests, envvars, timeout)
+ test_runner = TestRunner(
+ runner, runner_args, tests, envvars, timeout, verbose
+ )
await test_runner.run_tests()
return 0 if test_runner.all_passed() else 1
@@ -516,7 +524,6 @@ def main() -> int:
)
args_as_dict = dict(vars(parser.parse_args()))
- del args_as_dict['verbose']
return asyncio.run(find_and_run_tests(**args_as_dict))
diff --git a/pw_varint/docs.rst b/pw_varint/docs.rst
index d29455563..5d8fc5da5 100644
--- a/pw_varint/docs.rst
+++ b/pw_varint/docs.rst
@@ -10,7 +10,7 @@ pw_varint
Compatibility
-------------
* C
-* C++14 (with :doc:`../pw_polyfill/docs`)
+* C++
* `Rust </rustdoc/pw_varint>`_
-------------
diff --git a/pw_watch/BUILD.gn b/pw_watch/BUILD.gn
index f1ab7ee5e..f8d02ee0c 100644
--- a/pw_watch/BUILD.gn
+++ b/pw_watch/BUILD.gn
@@ -18,10 +18,6 @@ import("$dir_pw_docgen/docs.gni")
import("$dir_pw_unit_test/test.gni")
pw_doc_group("docs") {
- inputs = [
- "doc_resources/pw_watch_on_device_demo.gif",
- "doc_resources/pw_watch_test_demo2.gif",
- ]
sources = [
"cli.rst",
"docs.rst",
diff --git a/pw_watch/doc_resources/pw_watch_on_device_demo.gif b/pw_watch/doc_resources/pw_watch_on_device_demo.gif
deleted file mode 120000
index dd323496d..000000000
--- a/pw_watch/doc_resources/pw_watch_on_device_demo.gif
+++ /dev/null
@@ -1 +0,0 @@
-../../docs/images/pw_watch_on_device_demo.gif \ No newline at end of file
diff --git a/pw_watch/doc_resources/pw_watch_test_demo2.gif b/pw_watch/doc_resources/pw_watch_test_demo2.gif
deleted file mode 120000
index a3c62134e..000000000
--- a/pw_watch/doc_resources/pw_watch_test_demo2.gif
+++ /dev/null
@@ -1 +0,0 @@
-../../docs/images/pw_watch_test_demo2.gif \ No newline at end of file
diff --git a/pw_watch/docs.rst b/pw_watch/docs.rst
index 25f6468b4..62e4503d5 100644
--- a/pw_watch/docs.rst
+++ b/pw_watch/docs.rst
@@ -31,7 +31,7 @@ Our solution
tooling but is focused around embedded development use cases. After changing
source code, ``pw_watch`` can instantly compile, flash, and run tests.
-.. figure:: doc_resources/pw_watch_test_demo2.gif
+.. figure:: https://storage.googleapis.com/pigweed-media/pw_watch_test_demo2.gif
:width: 1420
:alt: ``pw watch`` running in fullscreen mode and displaying errors
@@ -46,7 +46,7 @@ and verifying the test runs as expected. Once this is set up, you can attach
multiple devices to run tests in a distributed manner to reduce the time it
takes to run tests.
-.. image:: doc_resources/pw_watch_on_device_demo.gif
+.. figure:: https://storage.googleapis.com/pigweed-media/pw_watch_on_device_demo.gif
:width: 800
:alt: pw_watch running on-device tests
diff --git a/pw_watch/guide.rst b/pw_watch/guide.rst
index 98ae2cbc0..a6845de19 100644
--- a/pw_watch/guide.rst
+++ b/pw_watch/guide.rst
@@ -131,9 +131,7 @@ Automatically reload docs
-------------------------
When using ``--serve-docs``, by default the docs will be rebuilt when changed,
just like code files. However, you will need to manually reload the page in
-your browser to see changes. If you install the ``httpwatcher`` Python package
-into your Pigweed environment (``pip install httpwatcher``), docs pages will
-automatically reload when changed.
+your browser to see changes.
Disable automatic rebuilds
--------------------------
diff --git a/pw_watch/py/pw_watch/watch.py b/pw_watch/py/pw_watch/watch.py
index 01bbf26db..cd75ab596 100755
--- a/pw_watch/py/pw_watch/watch.py
+++ b/pw_watch/py/pw_watch/watch.py
@@ -64,11 +64,6 @@ from typing import (
Tuple,
)
-try:
- import httpwatcher # type: ignore[import]
-except ImportError:
- httpwatcher = None
-
from watchdog.events import FileSystemEventHandler # type: ignore[import]
from watchdog.observers import Observer # type: ignore[import]
@@ -691,19 +686,6 @@ def _simple_docs_server(
return simple_http_server_thread
-def _httpwatcher_docs_server(
- address: str, port: int, path: Path
-) -> Callable[[], None]:
- def httpwatcher_thread():
- # Disable logs from httpwatcher and deps
- logging.getLogger('httpwatcher').setLevel(logging.CRITICAL)
- logging.getLogger('tornado').setLevel(logging.CRITICAL)
-
- httpwatcher.watch(path, host=address, port=port)
-
- return httpwatcher_thread
-
-
def _serve_docs(
build_dir: Path,
docs_path: Path,
@@ -712,17 +694,7 @@ def _serve_docs(
) -> None:
address = '127.0.0.1'
docs_path = build_dir.joinpath(docs_path.joinpath('html'))
-
- if httpwatcher is not None:
- _LOG.info('Using httpwatcher. Docs will reload when changed.')
- server_thread = _httpwatcher_docs_server(address, port, docs_path)
- else:
- _LOG.info(
- 'Using simple HTTP server. Docs will not reload when changed.'
- )
- _LOG.info('Install httpwatcher and restart for automatic docs reload.')
- server_thread = _simple_docs_server(address, port, docs_path)
-
+ server_thread = _simple_docs_server(address, port, docs_path)
_LOG.info('Serving docs at http://%s:%d', address, port)
# Spin up server in a new thread since it blocks
diff --git a/pw_web/BUILD.gn b/pw_web/BUILD.gn
index a4d5ebe10..5069f943c 100644
--- a/pw_web/BUILD.gn
+++ b/pw_web/BUILD.gn
@@ -18,7 +18,10 @@ import("$dir_pw_docgen/docs.gni")
import("$dir_pw_unit_test/test.gni")
pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+ sources = [
+ "docs.rst",
+ "testing.rst",
+ ]
}
pw_test_group("tests") {
diff --git a/pw_web/docs.rst b/pw_web/docs.rst
index 95914a016..d90c22a6a 100644
--- a/pw_web/docs.rst
+++ b/pw_web/docs.rst
@@ -3,7 +3,6 @@
---------
pw_web
---------
-
Pigweed provides an NPM package with modules to build web apps for Pigweed
devices.
@@ -29,7 +28,6 @@ After installing, you can import modules from ``pigweedjs`` in this way:
Import Directly in HTML
^^^^^^^^^^^^^^^^^^^^^^^
-
If you don't want to set up a bundler, you can also load Pigweed directly in
your HTML page by:
@@ -149,7 +147,6 @@ Device has following public API:
WebSerialTransport
------------------
-
To help with connecting to WebSerial and listening for serial data, a helper
class is also included under ``WebSerial.WebSerialTransport``. Here is an
example usage:
@@ -192,7 +189,6 @@ Following Pigweed modules are included in the NPM package:
Web Console
===========
-
Pigweed includes a web console that demonstrates `pigweedjs` usage in a
React-based web app. Web console includes a log viewer and a REPL that supports
autocomplete. Here's how to run the web console locally:
@@ -205,7 +201,6 @@ autocomplete. Here's how to run the web console locally:
Log viewer component
====================
-
The NPM package also includes a log viewer component that can be embedded in any
webapp. The component works with Pigweed's RPC stack out-of-the-box but also
supports defining your own log source.
@@ -260,7 +255,6 @@ example app using that:
Custom Log Source
-----------------
-
You can define a custom log source that works with the log viewer component by
just extending the abstract `LogSource` class and emitting the `logEntry` events
like this:
@@ -298,7 +292,6 @@ for reference.
Color Scheme
------------
-
The log viewer web component provides the ability to set the color scheme manually, overriding any default or system preferences.
To set the color scheme, first obtain a reference to the ``log-viewer`` element in the DOM. A common way to do this is by using ``querySelector()``:
@@ -318,3 +311,11 @@ You can then set the color scheme dynamically by updating the component's `color
logViewer.setAttribute('colorscheme', 'dark');
The color scheme can be set to ``'dark'``, ``'light'``, or the default ``'auto'`` which allows the component to adapt to the preferences in the operating system settings.
+
+Guides
+======
+
+.. toctree::
+ :maxdepth: 1
+
+ testing
diff --git a/pw_web/log-viewer/src/components/log-list/log-list.styles.ts b/pw_web/log-viewer/src/components/log-list/log-list.styles.ts
index 6dabfb4a0..a10b36360 100644
--- a/pw_web/log-viewer/src/components/log-list/log-list.styles.ts
+++ b/pw_web/log-viewer/src/components/log-list/log-list.styles.ts
@@ -88,9 +88,9 @@ export const styles = css`
--icon-color: var(--sys-log-viewer-color-debug);
}
- .log-row--warning .cell-content--icon,
- .log-row--error .cell-content--icon,
- .log-row--critical .cell-content--icon {
+ .log-row--warning .cell-icon,
+ .log-row--error .cell-icon,
+ .log-row--critical .cell-icon {
color: var(--icon-color);
}
@@ -138,7 +138,7 @@ export const styles = css`
display: block;
grid-row: 1;
overflow: hidden;
- padding: 0.5rem 1rem;
+ padding: var(--sys-log-viewer-table-cell-padding);
text-align: left;
text-overflow: ellipsis;
}
@@ -212,10 +212,6 @@ export const styles = css`
width: 1rem;
}
- .cell-content--icon {
- padding-top: 0.125rem;
- }
-
.cell-icon {
display: block;
font-variation-settings:
@@ -223,8 +219,11 @@ export const styles = css`
'wght' 400,
'GRAD' 200,
'opsz' 58;
- font-size: 1.25rem;
+ font-size: var(--sys-log-viewer-table-cell-icon-size);
user-select: none;
+ display: grid;
+ place-content: center;
+ place-items: center;
}
.overflow-indicator {
@@ -268,8 +267,8 @@ export const styles = css`
mark {
background-color: var(--sys-log-viewer-color-table-mark);
- border-radius: 2px;
- color: var(--sys-log-viewer-color-table-mark);
+ border-radius: 4px;
+ color: var(--sys-log-viewer-color-table-mark-text);
outline: 1px solid var(--sys-log-viewer-color-table-mark);
}
diff --git a/pw_web/log-viewer/src/components/log-list/log-list.ts b/pw_web/log-viewer/src/components/log-list/log-list.ts
index ca450fc94..58d635ce7 100644
--- a/pw_web/log-viewer/src/components/log-list/log-list.ts
+++ b/pw_web/log-viewer/src/components/log-list/log-list.ts
@@ -59,14 +59,6 @@ export class LogList extends LitElement {
@state()
private _isOverflowingToRight = false;
- /** A number representing the scroll percentage in the horizontal direction. */
- @state()
- private _scrollPercentageLeft = 0;
-
- /** Indicates whether to enable autosizing of incoming log entries. */
- @state()
- private _autosizeLocked = false;
-
/**
* Indicates whether to automatically scroll the table container to the bottom
* when new log entries are added.
@@ -74,31 +66,42 @@ export class LogList extends LitElement {
@state()
private _autoscrollIsEnabled = true;
+ /** A number representing the scroll percentage in the horizontal direction. */
+ @state()
+ private _scrollPercentageLeft = 0;
+
@query('.table-container') private _tableContainer!: HTMLDivElement;
@query('table') private _table!: HTMLTableElement;
@query('tbody') private _tableBody!: HTMLTableSectionElement;
@queryAll('tr') private _tableRows!: HTMLTableRowElement[];
- /**
- * Data used for column resizing including the column index, the starting
- * mouse position (X-coordinate), and the initial width of the column.
- */
- private columnResizeData: {
- columnIndex: number;
- startX: number;
- startWidth: number;
- } | null = null;
+ /** Indicates whether to enable autosizing of incoming log entries. */
+ private _autosizeLocked = false;
/** The number of times the `logs` array has been updated. */
private logUpdateCount: number = 0;
+
+ /** The last known vertical scroll position of the table container. */
+ private lastScrollTop: number = 0;
+
/** The maximum number of log entries to render in the list. */
private readonly MAX_ENTRIES = 100_000;
+
/** The maximum number of log updates until autosize is disabled. */
private readonly AUTOSIZE_LIMIT: number = 8;
+
/** The minimum width (in px) for table columns. */
private readonly MIN_COL_WIDTH: number = 52;
- /** The last known vertical scroll position of the table container. */
- private lastScrollTop: number = 0;
+
+ /**
+ * Data used for column resizing including the column index, the starting
+ * mouse position (X-coordinate), and the initial width of the column.
+ */
+ private columnResizeData: {
+ columnIndex: number;
+ startX: number;
+ startWidth: number;
+ } | null = null;
firstUpdated() {
setInterval(() => this.updateHorizontalOverflowState(), 1000);
@@ -148,9 +151,6 @@ export class LogList extends LitElement {
if (this.logUpdateCount >= this.AUTOSIZE_LIMIT) {
this._autosizeLocked = true;
}
- if (!this._autosizeLocked) {
- this.autosizeColumns();
- }
};
/** Called when the Lit virtualizer updates its range of entries. */
@@ -170,6 +170,11 @@ export class LogList extends LitElement {
}, 0); // Complete any rendering tasks before scrolling
}
+ private onJumpToBottomButtonClick() {
+ this._autoscrollIsEnabled = true;
+ this.scrollTableToBottom();
+ }
+
/**
* Calculates the maximum column widths for the table and updates the table
* rows.
@@ -223,7 +228,7 @@ export class LogList extends LitElement {
columnValue = `${col.manualWidth}px`;
} else {
if (i === 0) {
- columnValue = '3.25rem';
+ columnValue = '3rem';
} else {
const chWidth = col.characterLength;
const padding = 34;
@@ -249,11 +254,11 @@ export class LogList extends LitElement {
* @param {string} text - The table cell text to be processed.
*/
private highlightMatchedText(text: string): TemplateResult[] {
- const searchPhrase = this.searchText?.replace(/(^"|')|("|'$)/g, '');
- if (!searchPhrase) {
+ if (!this.searchText) {
return [html`${text}`];
}
+ const searchPhrase = this.searchText?.replace(/(^"|')|("|'$)/g, '');
const escapedsearchText = searchPhrase.replace(
/[.*+?^${}()|[\]\\]/g,
'\\$&',
@@ -285,34 +290,36 @@ export class LogList extends LitElement {
const scrollY =
container.scrollHeight - currentScrollTop - container.clientHeight;
const maxScrollLeft = container.scrollWidth - containerWidth;
- const rowHeight = this._tableRows[0].offsetHeight;
// Determine scroll direction and update the last known scroll position
const isScrollingVertically = currentScrollTop !== this.lastScrollTop;
const isScrollingUp = currentScrollTop < this.lastScrollTop;
this.lastScrollTop = currentScrollTop;
- // Only run autoscroll logic if the user is scrolling vertically
+ const logsAreCleared = this.logs.length == 0;
+
+ if (logsAreCleared) {
+ this._autoscrollIsEnabled = true;
+ return;
+ }
+
+ // Run autoscroll logic if scrolling vertically
if (!isScrollingVertically) {
this._scrollPercentageLeft = scrollLeft / maxScrollLeft || 0;
return;
}
- // User is scrolling up, disable autoscroll
+ // Scroll direction up, disable autoscroll
if (isScrollingUp) {
this._autoscrollIsEnabled = false;
return;
}
- // User is scrolling down, enable autoscroll if they're near the bottom
+ // Scroll direction down, enable autoscroll if near the bottom
if (Math.abs(scrollY) <= 1) {
this._autoscrollIsEnabled = true;
return;
}
-
- if (Math.round(scrollY - rowHeight) >= 1) {
- this._autoscrollIsEnabled = false;
- }
};
/**
@@ -523,7 +530,7 @@ export class LogList extends LitElement {
return html`
<td ?hidden=${!isVisible}>
- <div class="cell-content cell-content--icon">
+ <div class="cell-content">
<md-icon
class="cell-icon"
title="${toTitleCase(field.value.toString())}"
@@ -570,7 +577,7 @@ export class LogList extends LitElement {
<md-filled-button
class="jump-to-bottom-btn"
title="Jump to Bottom"
- @click="${this.scrollTableToBottom}"
+ @click="${this.onJumpToBottomButtonClick}"
leading-icon
data-visible="${this._autoscrollIsEnabled ? 'false' : 'true'}"
>
diff --git a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts
index 0f17bc9f1..0277857eb 100644
--- a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts
+++ b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.styles.ts
@@ -83,10 +83,10 @@ export const styles = css`
font-size: 1rem;
height: 0.75rem;
line-height: 0.75;
- max-width: 100%;
+ max-width: 30rem;
overflow: hidden;
padding: 0.5rem 1rem;
- width: 25rem;
+ width: 100%;
}
input[type='text'] {
diff --git a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts
index f31f8e129..8c0226f7b 100644
--- a/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts
+++ b/pw_web/log-viewer/src/components/log-view-controls/log-view-controls.ts
@@ -50,9 +50,6 @@ export class LogViewControls extends LitElement {
_stateStore: StateStore = new LocalStorageState();
@state()
- _state: State;
-
- @state()
_viewTitle = 'Log View';
@state()
@@ -66,6 +63,8 @@ export class LogViewControls extends LitElement {
@queryAll('.item-checkboxes') _itemCheckboxes!: HTMLCollection[];
+ private _state: State;
+
/** The timer identifier for debouncing search input. */
private _inputDebounceTimer: number | null = null;
diff --git a/pw_web/log-viewer/src/components/log-view/log-view.ts b/pw_web/log-viewer/src/components/log-view/log-view.ts
index e975c044f..900637e5c 100644
--- a/pw_web/log-viewer/src/components/log-view/log-view.ts
+++ b/pw_web/log-viewer/src/components/log-view/log-view.ts
@@ -55,47 +55,44 @@ export class LogView extends LitElement {
@state()
_lineWrap = false;
+ /** The field keys (column values) for the incoming log entries. */
+ @state()
+ private _columnData: TableColumn[] = [];
+
+ /** A string representing the value contained in the search field. */
+ @state()
+ public searchText = '';
+
+ /** A StateStore object that stores state of views */
+ @state()
+ _stateStore: StateStore = new LocalStorageState();
+
+ @query('log-list') _logList!: LogList;
+
/**
* An array containing the logs that remain after the current filter has been
* applied.
*/
- @state()
private _filteredLogs: LogEntry[] = [];
- /** The field keys (column values) for the incoming log entries. */
- @state()
- private _columnData: TableColumn[] = [];
-
/** A function used for filtering rows that contain a certain substring. */
- @state()
private _stringFilter: FilterFunction = () => true;
/**
* A function used for filtering rows that contain a timestamp within a
* certain window.
*/
- @state()
private _timeFilter: FilterFunction = () => true;
- /** A string representing the value contained in the search field. */
- @state()
- public searchText = '';
-
- /** A StateStore object that stores state of views */
- @state()
- _stateStore: StateStore = new LocalStorageState();
-
- @state()
- _state: State;
-
- @query('log-list') _logList!: LogList;
+ private _state: State;
private _debounceTimeout: NodeJS.Timeout | null = null;
+ /** The number of elements in the `logs` array since last updated. */
+ private _lastKnownLogLength: number = 0;
+
/** The amount of time, in ms, before the filter expression is executed. */
private readonly FILTER_DELAY = 100;
- /** The number of elements in the `logs` array since last updated. */
- private lastKnownLogLength: number = 0;
constructor() {
super();
@@ -117,10 +114,13 @@ export class LogView extends LitElement {
super.updated(changedProperties);
if (changedProperties.has('logs')) {
- const newLogs = this.logs.slice(this.lastKnownLogLength);
- this.lastKnownLogLength = this.logs.length;
+ const newLogs = this.logs.slice(this._lastKnownLogLength);
+ this._lastKnownLogLength = this.logs.length;
this.updateFieldsFromNewLogs(newLogs);
+ }
+
+ if (changedProperties.has('logs') || changedProperties.has('searchText')) {
this.filterLogs();
}
@@ -263,16 +263,20 @@ export class LogView extends LitElement {
}
/**
- * Combines constituent filter expressions and filters the logs. The filtered
- * logs are stored in the `_filteredLogs` state property.
+ * Combines filter expressions and filters the logs. The filtered
+ * logs are stored in the `_filteredLogs` property.
*/
private filterLogs() {
const combinedFilter = (logEntry: LogEntry) =>
this._timeFilter(logEntry) && this._stringFilter(logEntry);
- this._filteredLogs = JSON.parse(
- JSON.stringify(this.logs.filter(combinedFilter)),
- );
+ const newFilteredLogs = this.logs.filter(combinedFilter);
+
+ if (
+ JSON.stringify(newFilteredLogs) !== JSON.stringify(this._filteredLogs)
+ ) {
+ this._filteredLogs = newFilteredLogs;
+ }
}
private updateColumnData(event: CustomEvent) {
@@ -322,7 +326,7 @@ export class LogView extends LitElement {
render() {
return html` <log-view-controls
- .columnData=${[...this._columnData]}
+ .columnData=${this._columnData}
.viewId=${this.id}
.hideCloseButton=${!this.isOneOfMany}
.stateStore=${this._stateStore}
diff --git a/pw_web/log-viewer/src/components/log-viewer.styles.ts b/pw_web/log-viewer/src/components/log-viewer.styles.ts
index bc155b383..0bf3a88e6 100644
--- a/pw_web/log-viewer/src/components/log-viewer.styles.ts
+++ b/pw_web/log-viewer/src/components/log-viewer.styles.ts
@@ -43,6 +43,10 @@ export const styles = css`
--sys-log-viewer-height: 100%;
--sys-log-viewer-view-outline-width: 1px;
--sys-log-viewer-view-corner-radius: 0.5rem;
+
+ /* Log List */
+ --sys-log-viewer-table-cell-padding: 0.375rem 0.75rem;
+ --sys-log-viewer-table-cell-icon-size: 1.125rem;
}
.grid-container {
diff --git a/pw_web/log-viewer/src/components/log-viewer.ts b/pw_web/log-viewer/src/components/log-viewer.ts
index 91dba1e76..06c695309 100644
--- a/pw_web/log-viewer/src/components/log-viewer.ts
+++ b/pw_web/log-viewer/src/components/log-viewer.ts
@@ -51,12 +51,11 @@ export class LogViewer extends LitElement {
@state()
_logViews: LogView[] = [];
- /** A StateStore object that stores state of views */
+ /** An object that stores the state of log views */
@state()
_stateStore: StateStore;
- @state()
- _state: State;
+ private _state: State;
constructor(state: StateStore) {
super();
@@ -175,7 +174,7 @@ export class LogViewer extends LitElement {
(view) => html`
<log-view
id=${view.id}
- .logs=${[...this.logs]}
+ .logs=${this.logs}
.isOneOfMany=${this._logViews.length > 1}
.stateStore=${this._stateStore}
@add-view="${this.addLogView}"
diff --git a/pw_web/log-viewer/src/index.css b/pw_web/log-viewer/src/index.css
index f16bd1612..04a79a2c2 100644
--- a/pw_web/log-viewer/src/index.css
+++ b/pw_web/log-viewer/src/index.css
@@ -44,10 +44,16 @@ button {
main {
height: 100vh;
- padding: 2rem;
+ padding: 16px;
width: 100vw;
}
+@media (min-width: 840px) {
+ main {
+ padding: 24px;
+ }
+}
+
a {
color: var(--md-sys-color-primary);
font-weight: 500;
diff --git a/pw_web/testing.rst b/pw_web/testing.rst
new file mode 100644
index 000000000..8b7f29f9c
--- /dev/null
+++ b/pw_web/testing.rst
@@ -0,0 +1,57 @@
+.. _module-pw_web-testing:
+
+=====================
+Manual Test Procedure
+=====================
+``pw_web`` is a web based log viewer and the manual tests here are intended
+to address situations where automated tests are not able to cover.
+
+Test Sections
+=============
+
+Log View Controls
+^^^^^^^^^^^^^^^^^
+
+.. list-table::
+ :widths: 5 45 45 5
+ :header-rows: 1
+
+ * - #
+ - Test Action
+ - Expected Result
+ - ✅
+
+ * - 1
+ - | Input bar is empty
+ | Press the :guilabel:`clear logs` button (trash can with lines)
+ - | Logs are cleared and entries after time of button press are addded.
+ - |checkbox|
+
+ * - 2
+ - | Input bar has a single word filter
+ | Press the :guilabel:`clear logs` button (trash can with lines)
+ - | Logs are cleared and filtered entries after time of button press are addded.
+ - |checkbox|
+
+ * - 3
+ - | Table is autoscrolling, scroll up
+ - | Autoscroll is disabled and :guilabel:`jump to bottom` button is visible.
+ - |checkbox|
+
+ * - 4
+ - | Press :guilabel:`clear logs` button (trash can with lines)
+ - | Autoscroll is enabled and :guilabel:`jump to bottom` button is not visible.
+ - |checkbox|
+
+Add note to the commit message
+==============================
+Add a ``Testing:`` line to your commit message and mention the steps
+executed. For example:
+
+.. code-block:: text
+
+ Testing: Log Pane Steps 1-6
+
+.. |checkbox| raw:: html
+
+ <input type="checkbox">
diff --git a/seed/0000-index.rst b/seed/0000-index.rst
index 369c377fb..218a70e77 100644
--- a/seed/0000-index.rst
+++ b/seed/0000-index.rst
@@ -19,8 +19,10 @@ All pending, active, and resolved SEEDs are listed below.
0107-communications
0108-pw_emu-emulators-frontend
0109-comms-buffers
- 0110: Memory Allocation Interfaces <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168772>
+ 0110-memory-allocation-interfaces
0111-build-systems
- 0112: Async Poll Model <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168337>
- 0113: Modular Bazel C/C++ toolchain API <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/173453>
+ 0112-async-poll
+ 0113-bazel-cc-toolchain-api
0114: Channels <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/175471>
+ 0115: pw_sensor Sensors <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/176760>
+ 0116: pw_net Sockets <http://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177696>
diff --git a/seed/0102-module-docs.rst b/seed/0102-module-docs.rst
index 3e817915a..7cd8659e7 100644
--- a/seed/0102-module-docs.rst
+++ b/seed/0102-module-docs.rst
@@ -10,6 +10,20 @@
:proposal_date: 2023-02-10
:cl: 128811, 130410
+---------------------
+Status (October 2023)
+---------------------
+If you're looking for guidelines on how to author module docs, use these:
+:ref:`docs-contrib-moduledocs`
+
+.. caution::
+
+ SEED-0102 is still considered accepted because we are still using some parts
+ of it in our current module docs guidelines. However, over the course of
+ 2023 we discovered that other parts of the SEED-0102 plan didn't achieve our
+ goals. Therefore, at this point you should only read SEED-0102 for historical
+ context on how our docs guidelines have evolved.
+
-------
Summary
-------
diff --git a/seed/0107-communications.rst b/seed/0107-communications.rst
index a79a13084..98f38e570 100644
--- a/seed/0107-communications.rst
+++ b/seed/0107-communications.rst
@@ -277,9 +277,10 @@ well-defined semantics for a communications channel, but applications do not
need to know how data is sent and received. The same API can be used to exchange
data with another process on the same machine or with a device across the world.
-.. admonition:: Sockets SEED
+.. admonition:: Sockets SEEDs
- The Pigweed sockets API will be explored in an upcoming SEED.
+ The Pigweed sockets API is described in SEED-0116. The sockets API is based
+ on ``pw_channel``, which is proposed in SEED-0114.
Socket types
------------
@@ -333,7 +334,7 @@ network. Any application can open a socket to communicate across the network.
A future revision of ``pw_rpc`` will use the sockets API in place of its current
``Channel`` API.
-The sockets API will support both synchronous and :ref:`asynchonous
+The sockets API will support both synchronous and :ref:`asynchronous
<seed-0107-async>` use. The synchronous API may be built using the async API.
It will also support :ref:`zero-copy <seed-0107-buffers>` data transmission.
@@ -536,7 +537,7 @@ asynchronous APIs. Network protocols themselves are heavily asynchronous.
.. admonition:: Async SEED
- Pigweed's async pattern will be explored in an upcoming SEED.
+ Pigweed's async pattern is proposed in :ref:`SEED-0112 <seed-0112>`.
.. _seed-0107-buffers:
@@ -576,7 +577,8 @@ power on constrained systems.
.. admonition:: Buffer management SEED
- Pigweed's buffer management system will be explored in an upcoming SEED.
+ Pigweed's buffer management system is proposed in :ref:`SEED-0109
+ <seed-0109>`.
Vectored I/O
------------
@@ -604,9 +606,9 @@ Pigweed's communications revamp will proceed loosely as follows:
* Write SEEDs to explore existing solutions, distill requirements, and propose
new Pigweed features for these areas:
- - Sockets API
- - Async pattern
- - Buffer management
+ - Sockets API (SEED-0116)
+ - Async pattern (:ref:`SEED-0112 <seed-0112>`).
+ - Buffer management (:ref:`SEED-0109 <seed-0109>`)
- Network protocol stack
* Implement the Sockets API.
diff --git a/seed/0110-memory-allocation-interfaces.rst b/seed/0110-memory-allocation-interfaces.rst
new file mode 100644
index 000000000..931cf6391
--- /dev/null
+++ b/seed/0110-memory-allocation-interfaces.rst
@@ -0,0 +1,464 @@
+.. _seed-0110:
+
+==================================
+0110: Memory Allocation Interfaces
+==================================
+.. seed::
+ :number: 110
+ :name: Memory Allocation Interfaces
+ :status: Accepted
+ :proposal_date: 2023-09-06
+ :cl: 168772
+
+-------
+Summary
+-------
+With dynamic memory allocation becoming more common in embedded devices, Pigweed
+should provide standardized interfaces for working with different memory
+allocators, giving both upstream and downstream developers the flexibility to
+move beyond manually sizing their modules' resource pools.
+
+----------
+Motivation
+----------
+Traditionally, most embedded firmware have laid out their systems' memory usage
+statically, with every component's buffers and resources set at compile time.
+As systems grow larger and more complex, the ability to dynamically allocate
+memory has become more desirable by firmware authors.
+
+Pigweed has made basic explorations into dynamic allocation in the past: the
+``pw_allocator`` provides basic building blocks which projects can use to
+assemble their own allocators. ``pw_allocator`` also provides a proof-of-concept
+allocator (``FreeListHeap``) based off of these building blocks.
+
+Since then, Pigweed has become a part of more complex projects and has
+developed more advanced capabilities into its own modules. As the project has
+progressed, several developers --- both on the core and customer teams --- have
+noted areas where dynamic allocation would simplify code and improve memory
+usage by enabling sharing and eliminating large reservations.
+
+--------
+Proposal
+--------
+
+Allocator Interfaces
+====================
+The core of the ``pw_allocator`` module will define abstract interfaces for
+memory allocation. Several interfaces are provided with different allocator
+properties, all of which inherit from a base generic ``Allocator``.
+
+Core Allocators
+---------------
+
+Allocator
+^^^^^^^^^
+
+.. code-block:: c++
+
+ class Allocator {
+ public:
+ class Layout {
+ public:
+ constexpr Layout(
+ size_t size, std::align_val_t alignment = alignof(std::max_align_t))
+ : size_(size), alignment_(alignment) {}
+
+ // Creates a Layout for the given type.
+ template <typename T>
+ static constexpr Layout Of() {
+ return Layout(sizeof(T), alignof(T));
+ }
+
+ size_t size() const { return size_; }
+ size_t alignment() const { return alignment_; }
+
+ private:
+ size_t size_;
+ size_t alignment_;
+ };
+
+ template <typename T, typename... Args>
+ T* New(Args&&... args);
+
+ template <typename T>
+ void Delete(T* obj);
+
+ template <typename T, typename... Args>
+ std::optional<UniquePtr<T>> MakeUnique(Args&&... args);
+
+ void* Allocate(Layout layout) {
+ return DoAllocate(layout);
+ }
+
+ void Deallocate(void* ptr, Layout layout) {
+ return DoDeallocate(layout);
+ }
+
+ bool Resize(void* ptr, Layout old_layout, size_t new_size) {
+ if (ptr == nullptr) {
+ return false;
+ }
+ return DoResize(ptr, old_layout, new_size);
+ }
+
+ void* Reallocate(void* ptr, Layout old_layout, size_t new_size) {
+ return DoReallocate(void* ptr, Layout old_layout, size_t new_size);
+ }
+
+ protected:
+ virtual void* DoAllocate(Layout layout) = 0;
+ virtual void DoDeallocate(void* ptr, Layout layout) = 0;
+
+ virtual bool DoResize(void* ptr, Layout old_layout, size_t new_size) {
+ return false;
+ }
+
+ virtual void* DoReallocate(void* ptr, Layout old_layout, size_t new_size) {
+ if (new_size == 0) {
+ DoDeallocate(ptr, old_layout);
+ return nullptr;
+ }
+
+ if (DoResize(ptr, old_layout, new_size)) {
+ return ptr;
+ }
+
+ void* new_ptr = DoAllocate(new_layout);
+ if (new_ptr == nullptr) {
+ return nullptr;
+ }
+
+ if (ptr != nullptr && old_layout.size() != 0) {
+ std::memcpy(new_ptr, ptr, std::min(old_layout.size(), new_size));
+ DoDeallocate(ptr, old_layout);
+ }
+
+ return new_ptr;
+ }
+ };
+
+``Allocator`` is the most generic and fundamental interface provided by the
+module, representing any object capable of dynamic memory allocation.
+
+The ``Allocator`` interface makes no guarantees about its implementation.
+Consumers of the generic interface must not make any assumptions around
+allocator behavior, thread safety, or performance.
+
+**Layout**
+
+Allocation parameters are passed to the allocator through a ``Layout`` object.
+This object ensures that the values provided to the allocator are valid, as well
+as providing some convenient helper functions for common allocation use cases,
+such as allocating space for a specific type of object.
+
+**Virtual functions**
+
+Implementers of the allocator interface are responsible for providing the
+following operations:
+
+* ``DoAllocate`` (required): Obtains a block of memory from the allocator with a
+ requested size and power-of-two alignment. Returns ``nullptr`` if the
+ allocation cannot be performed.
+
+ The size and alignment values in the provided layout are guaranteed to be
+ valid.
+
+ Memory returned from ``DoAllocate`` is uninitialized.
+
+* ``DoDeallocate`` (required): Releases a block of memory back to the allocator.
+
+ If ``ptr`` is ``nullptr``, does nothing.
+
+ If ``ptr`` was not previously obtained from this allocator the behavior is
+ undefined.
+
+* ``DoResize`` (optional): Extends or shrinks a previously-allocated block of
+ memory in place. If this operation cannot be performed, returns ``false``.
+
+ ``ptr`` is guaranteed to be non-null. If ``ptr`` was not previously obtained
+ from this allocator the behavior is undefined.
+
+ If the allocated block is grown, the memory in the extended region is
+ uninitialized.
+
+* ``DoReallocate`` (optional): Extends or shrinks a previously-allocated block
+ of memory, potentially copying its data to a different location. A default
+ implementation is provided, which first attempts to call ``Resize``, falling
+ back to allocating a new block and copying data if it fails.
+
+ If ``ptr`` is ``nullptr``, behaves identically to ``Allocate(new_layout)``.
+
+ If the new block cannot be allocated, returns ``nullptr``, leaving the
+ original allocation intact.
+
+ If ``new_layout.size == 0``, frees the old block and returns ``nullptr``.
+
+ If the allocated block is grown, the memory in the extended region is
+ uninitialized.
+
+**Provided functions**
+
+* ``New``: Allocates memory for an object from the allocator and constructs it.
+
+* ``Delete``: Destructs and releases memory for a previously-allocated object.
+
+* ``MakeUnique``: Allocates and constructs an object wrapped in a ``UniquePtr``
+ which owns it and manages its release.
+
+Allocator Utilities
+===================
+In addition to allocator interfaces, ``pw_allocator`` will provide utilities for
+working with allocators in a system.
+
+UniquePtr
+---------
+``pw::allocator::UniquePtr`` is a "smart pointer" analogous to
+``std::unique_ptr``, designed to work with Pigweed allocators. It owns and
+manages an allocated object, automatically deallocating its memory when it goes
+out of scope.
+
+Unlike ``std::unique_ptr``, Pigweed's ``UniquePtr`` cannot be manually
+constructed from an existing non-null pointer; it must be done through the
+``Allocator::MakeUnique`` API. This is required as the allocator associated with
+the object allocation must be known in order to release it.
+
+Usage reporting
+---------------
+``pw_allocator`` will not require any usage reporting as part of its core
+interfaces to keep them minimal and reduce implementation burden.
+
+However, ``pw_allocator`` encourages setting up reporting and will provide
+utilities for doing so. Initially, this consists of a layered proxy allocator
+which wraps another allocator implementation with basic usage reporting through
+``pw_metric``.
+
+.. code-block:: c++
+
+ class AllocatorMetricProxy : public Allocator {
+ public:
+ constexpr explicit AllocatorMetricProxy(metric::Token token)
+ : memusage_(token) {}
+
+ // Sets the wrapped allocator.
+ void Initialize(Allocator& allocator);
+
+ // Exposed usage statistics.
+ metric::Group& memusage() { return memusage_; }
+ size_t used() const { return used_.value(); }
+ size_t peak() const { return peak_.value(); }
+ size_t count() const { return count_.value(); }
+
+ // Implements the Allocator interface by forwarding through to the
+ // sub-allocator provided to Initialize.
+
+ };
+
+Integration with C++ polymorphic memory resources
+-------------------------------------------------
+The C++ standard library has similar allocator interfaces to those proposed
+defined as part of its PMR library. The reasons why Pigweed is not using these
+directly are :ref:`described below <seed-0110-why-not-pmr>`; however, Pigweed
+will provide a wrapper which exposes a Pigweed allocator through the PMR
+``memory_resource`` interface. An example of how this wrapper might look is
+presented here.
+
+.. code-block:: c++
+
+ template <typename Allocator>
+ class MemoryResource : public std::pmr::memory_resource {
+ public:
+ template <typename... Args>
+ MemoryResource(Args&&... args) : allocator_(std::forward<Args>(args)...) {}
+
+ private:
+ void* do_allocate(size_t bytes, size_t alignment) override {
+ void* p = allocator_.Allocate(bytes, alignment);
+ PW_ASSERT(p != nullptr); // Cannot throw in Pigweed code.
+ return p;
+ }
+
+ void do_deallocate(void* p, size_t bytes, size_t alignment) override {
+ allocator_.Deallocate(p, bytes, alignment);
+ }
+
+ bool do_is_equal(const std::pmr::memory_resource&) override {
+ // Pigweed allocators do not yet support the concept of equality; this
+ // remains an open question for the future.
+ return false;
+ }
+
+ Allocator allocator_;
+ };
+
+Future Considerations
+=====================
+
+Allocator traits
+----------------
+It can be useful for users to know additional details about a specific
+implementation of an allocator to determine whether it is suitable for their
+use case. For example, some allocators may have internal synchronization,
+removing the need for external locking. Certain allocators may be suitable for
+uses in specialized contexts such as interrupts.
+
+To enable users to enforce these types of requirements, it would be useful to
+provide a way for allocator implementations to define certain traits.
+Originally, this proposal accommodated for this by defining derived allocator
+interfaces which semantically enforced additional implementation contracts.
+However, this approach could have led to an explosion of different allocator
+types throughout the codebase for each permutation of traits. As such, it was
+removed from the initial allocator plan for future reinvestigation.
+
+Dynamic collections
+-------------------
+The ``pw_containers`` module defines several collections such as ``pw::Vector``.
+These collections are modeled after STL equivalents, though being
+embedded-friendly, they reserve a fixed maximum size for their elements.
+
+With the addition of dynamic allocation to Pigweed, these containers will be
+expanded to support the use of allocators. Unless absolutely necessary, upstream
+containers should be designed to work on the base ``Allocator`` interface ---
+not any of its derived classes --- to offer maximum flexibility to projects
+using them.
+
+.. code-block:: c++
+
+ template <typename T>
+ class DynamicVector {
+ DynamicVector(Allocator& allocator);
+ };
+
+Per-allocation tagging
+----------------------
+Another interface which was originally proposed but shelved for the time being
+allowed for the association of an integer tag with each specific call to
+``Allocate``. This can be incredibly useful for debugging, but requires
+allocator implementations to store additional information with each allocation.
+This added complexity to allocators, so it was temporarily removed to focus on
+refining the core allocator interface.
+
+The proposed 32-bit integer tags happen to be the same as the tokens generated
+from strings by the ``pw_tokenizer`` module. Combining the two could result in
+the ability to precisely track the source of allocations in a project.
+
+For example, ``pw_allocator`` could provide a macro which tokenizes a user
+string to an allocator tag, automatically inserting additional metadata such as
+the file and line number of the allocation.
+
+.. code-block:: c++
+
+ void GenerateAndProcessData(TaggedAllocator& allocator) {
+ void* data = allocator->AllocatedTagged(
+ Layout::Sized(kDataSize), PW_ALLOCATOR_TAG("my data buffer"));
+ if (data == nullptr) {
+ return;
+ }
+
+ GenerateData(data);
+ ProcessData(data);
+
+ allocator->Deallocate(data);
+ }
+
+Allocator implementations
+-------------------------
+Over time, Pigweed expects to implement a handful of different allocators
+covering the interfaces proposed here. No specific new implementations are
+suggested as part of this proposal. Pigweed's existing ``FreeListHeap``
+allocator will be refactored to implement the ``Allocator`` interface.
+
+---------------------
+Problem Investigation
+---------------------
+
+Use cases and requirements
+==========================
+
+* **General-purpose memory allocation.** The target of ``pw_allocator`` is
+ general-purpose dynamic memory usage by typical applications, rather than
+ specialized types of memory allocation that may be required by lower-level
+ code such as drivers.
+
+* **Generic interfaces with minimal policy.** Every project has different
+ resources and requirements, and particularly in constrained systems, memory
+ management is often optimized for their specific use cases. Pigweed's core
+ allocation interfaces should offer as broad of an implementation contract as
+ possible and not bake in assumptions about how they will be run.
+
+* **RTOS or bare metal usage.** While many systems make use of an RTOS which
+ provides utilities such as threads and synchronization primitives, Pigweed
+ also targets systems which run without one. As such, the core allocators
+ should not be tied to any RTOS requirements, and accommodations should be made
+ for different system contexts.
+
+Out of scope
+------------
+
+* **Asynchronous allocation.** As this proposal is centered around simple
+ general-purpose allocation, it does not consider asynchronous allocations.
+ While these are important use cases, they are typically more specialized and
+ therefore outside the scope of this proposal. Pigweed is considering some
+ forms of asynchronous memory allocation, such as the proposal in the
+ :ref:`Communication Buffers SEED <seed-0109>`.
+
+* **Direct STL integration.** The C++ STL makes heavy use of dynamic memory and
+ offers several ways for projects to plug in their own allocators. This SEED
+ does not propose any direct Pigweed to STL-style allocator adapters, nor does
+ it offer utilities for replacing the global ``new`` and ``delete`` operators.
+ These are additions which may come in future changes.
+
+ It is still possible to use Pigweed allocators with the STL in an indirect way
+ by going through the PMR interface, which is discussed later.
+
+* **Global Pigweed allocators.** Pigweed modules will not assume a global
+ allocator instantiation. Any usage of allocators by modules should rely on
+ dependency injection, leaving consumers with control over how they choose to
+ manage their memory usage.
+
+Alternative solutions
+=====================
+
+.. _seed-0110-why-not-pmr:
+
+C++ polymorphic allocators
+--------------------------
+C++17 introduced the ``<memory_resource>`` header with support for polymorphic
+memory resources (PMR), i.e. allocators. This library defines many allocator
+interfaces similar to those in this proposal. Naturally, this raises the
+question of whether Pigweed can use them directly, benefitting from the larger
+C++ ecosystem.
+
+The primary issue with PMR with regards to Pigweed is that the interfaces
+require the use of C++ language features prohibited by Pigweed. The allocator
+is expected to throw an exception in the case of failure, and equality
+comparisons require RTTI. The team is not prepared to change or make exceptions
+to this policy, prohibiting the direct usage of PMR.
+
+Despite this, Pigweed's allocator interfaces have taken inspiration from the
+design of PMR, incorporating many of its ideas. The core proposed ``Allocator``
+interface is similar to ``std::pmr::memory_resource``, making it possible to
+wrap Pigweed allocators with a PMR adapter for use with the C++ STL, albeit at
+the cost of an extra layer of virtual indirection.
+
+--------------
+Open Questions
+--------------
+This SEED proposal is only a starting point for the improvement of the
+``pw_allocator`` module, and Pigweed's memory management story in general.
+
+There are several open questions around Pigweed allocators which the team
+expects to answer in future SEEDs:
+
+* Should generic interfaces for asynchronous allocations be provided, and how
+ would they look?
+
+* Reference counted allocations and "smart pointers": where do they fit in?
+
+* The concept of allocator equality is essential to enable certain use cases,
+ such as efficiently using dynamic containers with their own allocators.
+ This proposal excludes APIs paralleling PMR's ``is_equal`` due to RTTI
+ requirements. Could Pigweed allocators implement a watered-down version of an
+ RTTI / type ID system to support this?
+
+* How do allocators integrate with the monolithic ``pw_system`` as a starting
+ point for projects?
diff --git a/seed/0112-async-poll.rst b/seed/0112-async-poll.rst
new file mode 100644
index 000000000..e205062d0
--- /dev/null
+++ b/seed/0112-async-poll.rst
@@ -0,0 +1,615 @@
+.. _seed-0112:
+
+======================
+0112: Async Poll Model
+======================
+.. seed::
+ :number: 0112
+ :name: Async Poll Model
+ :status: Accepted
+ :proposal_date: 2023-9-19
+ :cl: 168337
+
+-------
+Summary
+-------
+This SEED proposes the development of a new “informed-Poll”-based pw::async
+library. The “informed Poll” model, popularized by
+`Rust’s Future trait, <https://doc.rust-lang.org/std/future/trait.Future.html>`_
+offers an alternative to callback-based APIs. Rather than invoking a separate
+callback for every event, the informed Poll model runs asynchronous ``Task`` s.
+
+A ``Task`` is an asynchronous unit of work. Informed Poll-based asynchronous
+systems use ``Task`` s similar to how synchronous systems use threads.
+Users implement the ``Poll`` function of a ``Task`` in order to define the
+asynchronous behavior of a routine.
+
+.. code-block:: cpp
+
+ class Task {
+ public:
+ /// Does some work, returning ``Complete`` if done.
+ /// If not complete, returns ``Pending`` and arranges for `cx.waker()` to be
+ /// awoken when `Task:Poll` should be invoked again.
+ virtual pw::MaybeReady<Complete> Poll(pw::async::Context& cx);
+ };
+
+Users can start running a ``Task`` by ``Post`` ing it to a ``Dispatcher``.
+``Dispatcher`` s are asynchronous event loops which are responsible for calling
+``Poll`` every time the ``Task`` indicates that it is ready to make progress.
+
+This API structure allows Pigweed async code to operate efficiently, with low
+memory overhead, zero dynamic allocations, and simpler state management.
+
+Pigweed’s new async APIs will enable multi-step asynchronous operations without
+queuing multiple callbacks. Here is an example in which a proxy object receives
+data and then sends it out before completing:
+
+.. code-block:: cpp
+
+ class ProxyOneMessage : public Task {
+ public:
+ /// Proxies one ``Data`` packet from a ``Receiver`` to a ``Sender``.
+ ///
+ /// Returns:
+ /// ``pw::async::Complete`` when the task has completed. This happens
+ /// after a ``Data`` packet has been received and sent, or an error
+ /// has occurred and been logged.
+ /// ``pw::async::Pending`` if unable to complete. ``cx.waker()`` will be
+ /// awoken when ``Poll`` should be re-invoked.
+ pw::async::MaybeReady<pw::async::Complete> Poll(pw::async::Context& cx) {
+ if (!send_future_) {
+ // ``PollRead`` checks for available data or errors.
+ pw::async::MaybeReady<pw::Result<Data>> new_data = receiver_.PollRead(cx);
+ if (new_data.is_pending()) {
+ return pw::async::Pending;
+ }
+ if (!new_data->ok()) {
+ PW_LOG_ERROR("Receiving failed: %s", data->status().str());
+ return pw::async::Complete;
+ }
+ Data& data = **new_data;
+ send_future_ = sender_.Send(std::move(data));
+ }
+ // ``PollSend`` attempts to send `data_`, returning `Pending` if
+ // `sender_` was not yet able to accept `data_`.
+ pw::async::MaybeReady<pw::Status> sent = send_future_.Poll(cx);
+ if (sent.is_pending()) {
+ return pw::async::Pending;
+ }
+ if (!sent->ok()) {
+ PW_LOG_ERROR("Sending failed: %s", sent->str());
+ }
+ return pw::async::Complete;
+ }
+
+ private:
+ // ``SendFuture`` is some type returned by `Sender::Send` that offers a
+ // ``Poll`` method similar to the one on ``Task``.
+ std::optional<SendFuture> send_future_;
+ // `receiver_` and `sender_` are provided by the `ProxyOneMessage` constructor.
+ Receiver receiver_;
+ Sender sender_;
+ };
+
+ // --- Usage ---
+ // ``static`` is used for simplicity, but real ``Task`` s can have temporary
+ // lifetimes.
+ static ProxyOneMessage proxy(receiver, sender);
+
+ // Runs `proxy` until it completes, either by successfully receiving and
+ // sending a message, or by exiting early after logging an error.
+ dispatcher.Post(proxy);
+
+--------
+Proposal
+--------
+This SEED proposes that Pigweed develop a set of async APIs and utilities
+designed around the informed Poll model. If early trials with partner teams are
+successful, this new library will be used as the basis for future async code in
+Pigweed.
+
+-----
+Goals
+-----
+The goals of this SEED are as follows:
+
+* Establish community consensus that informed ``Poll`` is the best async model
+ for Pigweed to pursue.
+* Outline an initial API for ``Dispatcher`` implementors (platform authors) and
+ top-level ``Task`` writers.
+
+----------
+Motivation
+----------
+The purpose of this SEED is to gather agreement that ``Poll``-based async
+APIs are worth pursuing. We believe that these APIs provide the needed support
+for:
+
+* Small code size
+* Environments without dynamic allocation
+* Creating reusable building blocks and high-level modules
+
+The current ``Task`` API is limited in these respects: a single ``Task`` must
+be created and stored for every individual asynchronous event. ``Task`` s
+cannot be reused, and the memory allocated for a ``Task`` can only be reclaimed
+after a ``Task`` has been completed or cancelled, resulting in complex
+semantics for multithreaded environments or those with interrupt-driven events.
+
+Completing a sequence of events therefore requires either dynamic allocation
+or statically saving a separate ``Task`` worth of memory for every kind of
+event that may occur.
+
+Additionally, every asynchronous layer requires introducing another round of
+callbacks whose semantics may be unclear and whose captures may add lifetime
+challenges.
+
+This proposal resolves these issues by choosing an alternative approach.
+
+-----------
+API Summary
+-----------
+
+A Note On Specificity
+=====================
+This SEED provides API outlines in order to more clearly explain the intended
+API direction. The specific function signatures shown here are not meant to be
+authoritative, and are subject to change. As the implementation develops
+support for more platforms and features, some additions, changes, or removals
+may be necessary and will be considered as part of the regular CL review
+process.
+
+With that in mind, asynchronous ``Task`` s in this model could adopt an API
+like the following:
+
+The ``MaybeReady`` Type
+=======================
+Functions return ``MaybeReady<T>`` to indicate that their result may or may
+not be available yet. ``MaybeReady<T>`` is a generic sum type similar to
+``std::optional<T>``. It has two variants, ``Ready(T)`` or ``Pending``.
+
+The API is similar to ``std::optional<T>``, but ``MaybeReady<T>`` provides extra
+semantic clarification that the absense of a value means that it is not ready
+yet.
+
+Paired with the ``Complete`` type, ``MaybeReady<Complete>`` acts like
+``bool IsComplete``, but provides more semantic information to the user than
+returning a simple ``bool``.
+
+.. code-block:: cpp
+
+ /// A value that is ready, and
+ template<typename T>
+ struct Ready<T> { value: T };
+
+ /// A content-less struct that indicates a not-ready value.
+ struct Pending {};
+
+ /// A value of type `T` that is possibly available.
+ ///
+ /// This is similar to ``std::optional<T>``, but provides additional
+ /// semantic indication that the value is not ready yet (still pending).
+ /// This can aid in making type signatures such as
+ /// ``MaybeReady<std::optional<Item>>`` easier to understand, and provides
+ /// clearer naming like `IsReady` (compared to ``has_value()``).
+ template<typename T>
+ class MaybeReady {
+ public:
+ /// Implicitly converts from ``T``, ``Ready<T>`` or ``Pending``.
+ MaybeReady(T);
+ MaybeReady(Ready<T>);
+ MaybeReady(Pending);
+ bool IsReady();
+ T Value() &&;
+ ...
+ };
+
+ /// A content-less struct that indicates completion.
+ struct Complete {};
+
+Note that the ``Pending`` type takes no type arguments, and so can be created
+and returned from macros that don't know which ``T`` is returned by the
+function they are in. For example:
+
+.. code-block:: cpp
+
+ // Simplified assignment macro
+ #define PW_ASSIGN_IF_READY(lhs, expr) \
+ auto __priv = (expr); \
+ if (!__priv.IsReady()) { \
+ return pw::async::Pending; \
+ } \
+ lhs = std::move(__priv.Value()) \
+
+ MaybeReady<Bar> PollCreateBar(Context& cx);
+
+ Poll<Foo> DoSomething(Context& cx) {
+ PW_ASSIGN_IF_READY(Bar b, PollCreateBar(cx));
+ return CreateFoo();
+ }
+
+This is similar to the role of the ``std::nullopt_t`` type.
+
+The ``Dispatcher`` Type
+=======================
+Dispatchers are the event loops responsible for running ``Task`` s. They sleep
+when there is no work to do, and wake up when there are ``Task`` s ready to
+make progress.
+
+On some platforms, the ``Dispatcher`` may also provide special hooks in order
+to support single-threaded asynchronous I/O.
+
+.. code-block:: cpp
+
+ class Dispatcher {
+ public:
+ /// Tells the ``Dispatcher`` to run ``Task`` to completion.
+ /// This method does not block.
+ ///
+ /// After ``Post`` is called, ``Task::Poll`` will be invoked once.
+ /// If ``Task::Poll`` does not complete, the ``Dispatcher`` will wait
+ /// until the ``Task`` is "awoken", at which point it will call ``Poll``
+ /// again until the ``Task`` completes.
+ void Post(Task&);
+ ...
+ };
+
+The ``Waker`` Type
+==================
+A ``Waker`` is responsible for telling a ``Dispatcher`` when a ``Task`` is
+ready to be ``Poll`` ed again. This allows ``Dispatcher`` s to intelligently
+schedule calls to ``Poll`` rather than retrying in a loop (this is the
+"informed" part of "informed Poll").
+
+When a ``Dispatcher`` calls ``Task::Poll``, it provides a ``Waker`` that will
+enqueue the ``Task`` when awoken. ``Dispatcher`` s can implement this
+functionality by having ``Waker`` add the ``Task`` to an intrusive linked list,
+add a pointer to the ``Task`` to a ``Dispatcher``-managed vector, or by pushing
+a ``Task`` ID onto a system-level async construct such as ``epoll``.
+
+.. code-block:: cpp
+
+ /// An object which can respond to asynchronous events by queueing work to
+ /// be done in response, such as placing a ``Task`` on a ``Dispatcher`` loop.
+ class Waker {
+ public:
+ /// Wakes up the ``Waker``'s creator, alerting it that an asynchronous
+ /// event has occurred that may allow it to make progress.
+ ///
+ /// ``Wake`` operates on an rvalue reference (``&&``) in order to indicate
+ /// that the event that was waited on has been completed. This makes it
+ /// possible to track the outstanding events that may cause a ``Task`` to
+ /// wake up and make progress.
+ void Wake() &&;
+
+ /// Creates a second ``Waker`` from this ``Waker``.
+ ///
+ /// ``Clone`` is made explicit in order to allow for easier tracking of
+ /// the different ``Waker``s that may wake up a ``Task``.
+ Waker Clone(Token wait_reason_indicator) &;
+ ...
+ };
+
+The ``Wake`` function itself may be called by any system with knowledge that
+the ``Task`` is now ready to make progress. This can be done from an interrupt,
+from a separate task, from another thread, or from any other function that
+knows that the `Poll`'d type may be able to make progress.
+
+The ``Context`` Type
+====================
+``Context`` is a bundle of arguments supplied to ``Task::Poll`` that give the
+``Task`` information about its asynchronous environment. The most important
+parts of the ``Context`` are the ``Dispatcher``, which is used to ``Post``
+new ``Task`` s, and the ``Waker``, which is used to tell the ``Dispatcher``
+when to run this ``Task`` again.
+
+.. code-block:: cpp
+
+ class Context {
+ public:
+ Context(Dispatcher&, Waker&);
+ Dispatcher& Dispatcher();
+ Waker& Waker();
+ ...
+ };
+
+The ``Task`` Type
+=================
+Finally, the ``Task`` type is implemented by users in order to run some
+asynchronous work. When a new asynchronous "thread" of execution must be run,
+users can create a new ``Task`` object and send it to be run on a
+``Dispatcher``.
+
+.. code-block:: cpp
+
+ /// A task which may complete one or more asynchronous operations.
+ ///
+ /// ``Task`` s should be actively ``Poll`` ed to completion, either by a
+ /// ``Dispatcher`` or by a parent ``Task`` object.
+ class Task {
+ public:
+ MaybeReady<Complete> Poll(Context&);
+ ...
+ protected:
+ /// Returns whether or not the ``Task`` has completed.
+ ///
+ /// If the ``Task`` has not completed, `Poll::Pending` will be returned,
+ /// and `context.Waker()` will receive a `Wake()` call when the ``Task``
+ /// is ready to make progress and should be ``Poll`` ed again.
+ virtual MaybeReady<Complete> DoPoll(Context&) = 0;
+ ...
+ };
+
+This structure makes it possible to run complex asynchronous ``Task`` s
+containing multiple concurrent or sequential asynchronous events.
+
+------------------------------------
+Relationship to Futures and Promises
+------------------------------------
+The terms "future" and "promise" are unfortunately quite overloaded. This SEED
+does not propose a "method chaining" API (e.g. ``.AndThen([](..) { ... }``), nor
+is creating reference-counted, blocking handles to the output of other threads
+a la ``std::future``.
+
+Where this SEED refers to ``Future`` types (e.g. ``SendFuture`` in the summary
+example), it means only a type which offers a ``Poll(Context&)`` method and
+return some ``MaybeReady<T>`` value. This common pattern can be used to build
+various asynchronous state machines which optionally return a value upon
+completion.
+
+---------------------------------------------
+Usage In The Rust Ecosystem Shows Feasability
+---------------------------------------------
+The ``Poll``-based ``Task`` approach suggested here is similar to the one
+adopted by Rust's
+`Future type <https://doc.rust-lang.org/stable/std/future/trait.Future.html>`_.
+The ``Task`` class in this SEED is analogous to Rust's ``Future<Output = ()>``
+type. This model has proven usable on small environments without dynamic allocation.
+
+Due to compiler limitations, Rust's ``async fn`` language feature will often
+generate ``Future`` s which suffer from code size issues. However,
+manual implementations of Rust's ``Future`` trait (not using ``async fn``) do
+not have this issue.
+
+We believe the success of Rust's ``Poll``-based ``Future`` type demonstrates
+that the approach taken in this SEED can meet the needs of Pigweed users.
+
+---------
+Code Size
+---------
+`Some experiments have been done
+<https://pigweed-review.googlesource.com/c/pigweed/experimental/+/154570>`_
+to compare the size of the code generated by
+a ``Poll``-based approach with code generated with the existing ``pw::async``
+APIs. These experiments have so far found that the ``Poll``-based approach
+creates binaries with smaller code size due to an increased opportunity for
+inlining, static dispatch, and a smaller number of separate ``Task`` objects.
+
+The experimental ``pw_async_bench`` examples show that the ``Poll``-based
+approach offers more than 2kB of savings on a small ``Socket``-like example.
+
+------------------------
+The ``pw::async`` Facade
+------------------------
+This SEED proposes changing ``Dispatcher`` from a virtual base into a
+platform-specific concrete type.
+
+The existing ``pw::async::Dispatcher`` class is ``virtual`` in order to support
+use of an alternative ``Dispatcher`` implementation in tests. However, this
+approach assumes that ``Task`` s are capable of running on arbitrary
+implementations of the ``Dispatcher`` virtual interface. In practice, this is
+not the case.
+
+Different platforms will use different native ``Dispatcher`` waiting primitives
+including ``epoll``, ``kqueue``, IOCP, Fuchsia's ``libasync``/``zx_port``, and
+lower-level waiting primitives such as Zephyr's RTIO queue.
+
+Each of these primitives is strongly coupled with native async events, such as
+IO or buffer readiness. In order to support ``Dispatcher``-native IO events,
+IO objects must be able to guarantee that they are running on a compatible
+``Dispatcher``. In Pigweed, this can be accomplished through the use of the
+facade pattern.
+
+The facade patterns allows for concrete, platform-dependent definitions of the
+``Task``, ``Context``, ``Waker``, and ``Dispatcher`` types. This allows these
+objects to interact with one another as necessary to implement fast scheduling
+with minimal in-memory or code size overhead.
+
+This approach enables storing platform-specific per- ``Task`` scheduling details
+inline with the ``Task`` itself, enabling zero-allocation ``Task`` scheduling
+without the need for additional resource pools.
+
+This also allows for native integration with platform-specific I/O primitives
+including ``epoll``, ``kqueue``, IOCP, and others, but also lower-level
+waiting primitives such as Zephyr's RTIO queue.
+
+Testing
+=======
+Moving ``Dispatcher`` to a non-virtual facade means that the previous approach
+of testing with a ``FakeDispatcher`` would require a separate toolchain in
+order to provide a different instantiation of the ``Dispatcher`` type. However,
+we can adopt a simpler approach: the ``Dispatcher`` type can offer minimial
+testing primitives natively:
+
+.. code-block:: cpp
+
+ class Dispatcher {
+ public:
+ ...
+
+ /// Runs tasks until none are able to make immediate progress.
+ ///
+ /// Returns whether a ``Task`` was run.
+ bool RunUntilStalled();
+
+ /// Enable mock time, initializing the mock timer to some "zero"-like
+ /// value.
+ void InitializeMockTime();
+
+ /// Advances the mock timer forwards by ``duration``.
+ void AdvanceMockTime(chrono::SystemClock::duration duration);
+ };
+
+These primitives are sufficient for testing with mock time. They allow
+test authors to avoid deadlocks, timeouts, or race conditions.
+
+Downsides of Built-in Testing Functions
+---------------------------------------
+Requiring concrete ``Dispatcher`` types to include the testing functions above
+means that the production ``Dispatcher`` implementations will have code in them
+that is only needed for testing.
+
+However, these additions are minimal: mocking time introduces a single branch
+for each timer access, which is still likely to be more efficient than the
+virtual function call that was required under the previous model.
+
+Advantages of Built-in Testing Functions
+----------------------------------------
+Testing with a "real" ``Dispatcher`` implementation ensures that:
+
+* All ``pw::async`` platforms provide support for testing
+* The ``Dispatcher`` used for testing will support the same I/O operations and
+ features provided by the production ``Dispatcher``
+* Tests will run under conditions as-close-to-production as possible. This will
+ allow catching bugs that are caused by the interaction of the code and the
+ particular ``Dispatcher`` on which it runs.
+
+Enabling Dynamic ``Task`` Lifetimes
+===================================
+While some ``Task`` s may be static, others may not be. For these, we need a
+mechanism to ensure that:
+
+* ``Task`` resources are not destroyed while ``Waker`` s that may post them
+ to a ``Dispatcher`` remain.
+* ``Task`` resources are not destroyed while the ``Task`` itself is running
+ or is queued to run.
+
+In order to enable this, platforms should clear all ``Waker`` s referencing a
+``Task`` when the ``Task`` completes: that ``Task`` will make no further
+progress, so ``Wake`` ing it serves no purpose.
+
+Once all ``Waker`` s have been cleared and the ``Task`` has finished running
+on the ``Dispatcher``, the ``Dispatcher`` should call that ``Task`` s
+``Cleanup`` function so that the ``Task`` can free any associated dynamic
+resources. During this ``Cleanup`` function, no other resources of ``Task``
+may be accessed by the application author until the ``Task`` has been
+re-initialized. If the memory associated with the ``Task`` is to be reused,
+the ``Task`` object itself must be reinitialized by invoking the ``Init``
+function.
+
+.. code-block:: cpp
+
+ class Task {
+ public:
+ ...
+ void Init();
+ virtual void Cleanup();
+ ...
+ };
+
+This allows downstream ``Task`` inheritors to implement dynamic free-ing of
+``Task`` resources, while also allowing the ``Dispatcher`` implementation the
+opportunity to clean up its own resources stored inside of the ``Task`` base
+class.
+
+Waker
+=====
+``Waker`` s will at first only be created via the ``Dispatcher``
+implementation, via cloning, or by the null constructor. Later on, the API may
+be expanded to allow for waking sub-tasks. The necessity of this at Pigweed's
+scale has not yet been determined.
+
+Timer
+=====
+``pw::async`` will additionally provide a ``Timer`` type. A ``Timer`` can be
+``Poll``'d by a ``Task`` in order to determine if a certain amount of time has
+passed. This can be used to implement timeouts or to schedule work.
+
+One possible ``Timer`` API would be as follows:
+
+.. code-block:: cpp
+
+ class Timer {
+ public:
+ Timer(Context&, chrono::SystemClock::time_point deadline);
+ Timer(Context&, chrono::SystemClock::duration delay);
+ pw::MaybeReady<Complete> Poll(Context&);
+ ...
+ };
+
+In order to enable this, the ``Dispatcher`` base class will include the
+following functions which implementations should use to trigger timers:
+
+.. code-block:: cpp
+
+ class DispatcherBase {
+ public:
+ ...
+ protected:
+ /// Returns the time of the earliest timer currently scheduled to fire.
+ std::optional<chrono::SystemClock::time_point> EarliestTimerExpiry();
+
+ /// Marks all ``Timer`` s with a time before ``time_point`` as complete,
+ /// and awakens any associated tasks.
+ ///
+ /// Returns whether any ``Timer`` objects were marked complete.
+ bool AwakenTimersUpTo(chrono::SystemClock::time_point);
+
+ /// Invoked when a new earliest ``Timer`` is created.
+ ///
+ /// ``Dispatcher`` implementations can override this to receive
+ /// notifications when a new timer is added.
+ virtual void NewEarliestTimer();
+ ...
+ };
+
+---------------------
+C++ Coroutine Support
+---------------------
+The informed ``Poll`` approach is well-suited to
+`C++20's coroutines <https://en.cppreference.com/w/cpp/language/coroutines>`_.
+Coroutines using the ``co_await`` and ``co_return`` expressions can
+automatically create and wait on ``Task`` types, whose base class will
+implement the ``std::coroutine_traits`` interface on C++20 and later.
+
+Dynamic Allocation
+==================
+Note that C++ coroutines allocate their state dynamically using
+``operator new``, and therefore are not usable on systems in which dynamic
+allocation is not available or where recovery from allocation failure is
+required.
+
+------------
+Rust Interop
+------------
+Rust uses a similar informed ``Poll`` model for its ``Future`` trait. This
+allows ``pw::async`` code to invoke Rust-based ``Future`` s by creating a
+Rust ``Waker`` which invokes the C++ ``Waker``, and performing cross-language
+``Poll`` ing.
+
+Rust support is not currently planned for the initial version of ``pw::async``,
+but will likely come in the future as Pigweed support for Rust expands.
+
+------------------------------------------------
+Support for Traditional Callback-Style Codebases
+------------------------------------------------
+One concern is interop with codebases which adopt a more traditional
+callback-driven design, such as the one currently supported by ``pw::async``.
+These models will continue to be supported under the new design, and can be
+modeled as a ``Task`` which runs a single function when ``Poll`` ed.
+
+---------
+Migration
+---------
+For ease of implementation and in order to ensure a smooth transition, this API
+will initially live alongside the current ``pw::async`` interface. This API
+will first be tested with one or more trial usages in order to stabilize the
+interface and ensure its suitability for Pigweed users.
+
+Following that, the previous ``pw::async`` implementation will be deprecated.
+A shim will be provided to allow users of the previous API to easily migrate
+their code onto the new ``pw::async`` implementation. After migrating to the
+new implementation, users can gradually transition to the new ``Poll``-based
+APIs as-desired. It will be possible to intermix legacy-style and
+``Poll``-based async code within the same dispatcher loop, allowing legacy
+codebases to adopt the ``Poll``-based model for new subsystems.
diff --git a/seed/0113-bazel-cc-toolchain-api.rst b/seed/0113-bazel-cc-toolchain-api.rst
new file mode 100644
index 000000000..758e32350
--- /dev/null
+++ b/seed/0113-bazel-cc-toolchain-api.rst
@@ -0,0 +1,724 @@
+.. _seed-0113:
+
+===========================================
+0113: Add modular Bazel C/C++ toolchain API
+===========================================
+.. seed::
+ :number: 0113
+ :name: Add modular Bazel C/C++ toolchain API
+ :status: Accepted
+ :proposal_date: 2023-09-28
+ :cl: 173453
+
+-------
+Summary
+-------
+This SEED proposes custom Starlark rules for declaring C/C++ toolchains in
+Bazel, and scalable patterns for sharing modular components of C/C++ toolchain
+definitions.
+
+----------
+Motivation
+----------
+There is a lot of boilerplate involved in standing up a Bazel C/C++ toolchain.
+While a good portion of the verbosity of specifying a new toolchain is
+important, necessary machinery, nearly as much suffers from one or more of the
+following problems:
+
+- **Underdocumented patterns**: The
+ `create_cc_toolchain_config_info() <https://bazel.build/rules/lib/toplevel/cc_common#create_cc_toolchain_config_info>`_
+ method has an attribute called ``target_cpu`` that doesn't have an associated
+ list of expected values, which suggests the argument is for bookkeeping
+ purposes and doesn't affect Bazel behavior. The reality is this argument
+ *does* have expected values that change behavior, but these are undocumented
+ (see `this GitHub issue <https://github.com/bazelbuild/bazel/issues/19353>`_
+ for more information).
+
+- **Not inherently modular**: Bazel expects the overwhelming majority of a
+ C/C++ toolchain to be specified as part of a call to
+ ``create_cc_toolchain_config_info()``. Because this is a Starlark method,
+ there's a lot of flexibility with how you construct a toolchain config, but
+ very little by way of existing patterns for creating something that is
+ testable, sharable, or in other ways modular. The existing
+ `tutorial for creating a C/C++ toolchain <https://bazel.build/tutorials/ccp-toolchain-config#configuring_the_c_toolchain>`_
+ illustrates expanding out the toolchain definition as a no-argument Starlark
+ rule.
+
+As Pigweed fully embraces multi-platform builds, it is critical for both
+Pigweed and users of Pigweed that it is easy to stand up custom toolchain
+definitions that work for the relevant hardware project-specific code/libraries.
+
+This SEED seeks to address the shortcomings in Bazel C/C++ toolchain
+declaration by establishing patterns and providing custom build rules that
+are owned and maintained by Pigweed.
+
+--------
+Proposal
+--------
+1. Introduce rules for defining C/C++ toolchains
+================================================
+In an effort to improve the experience of defining a C/C++ toolchain, Pigweed
+will introduce new Bazel rules that allow toolchains to share common boilerplate
+without hindering power-user functionality.
+
+While this work centers around an improved experience for defining a toolchain
+via ``create_cc_toolchain_config_info()``, other build rules will be introduced
+for closely related aspects of the toolchain definition process.
+
+An example of what these rules would look like in practice is as follows:
+
+.. code-block::
+
+ # A tool that can be used by various build actions.
+ pw_cc_tool(
+ name = "clang_tool",
+ path = "@cipd_llvm_toolchain//:bin/clang",
+ additional_files = [
+ "@cipd_llvm_toolchain//:all",
+ ],
+ )
+
+ # A mapping of a tool to actions, with flag sets that define behaviors.
+ pw_cc_action_config(
+ name = "clang",
+ actions = ALL_ASM_ACTIONS + ALL_C_COMPILER_ACTIONS,
+ tools = [
+ ":clang_tool",
+ ],
+ flag_sets = [
+ # Most flags should NOT end up here. Only unconditional flags that
+ # should ALWAYS be bound to this tool (e.g. static library
+ # workaround fix for macOS).
+ "@pw_toolchain//flag_sets:generate_depfile",
+ ],
+ )
+
+ # A trivial flag to be consumed by a C/C++ toolchain.
+ pw_cc_flag_set(
+ name = "werror",
+ actions = ALL_COMPILE_ACTIONS,
+ flags = [
+ "-Werror",
+ ],
+ )
+
+ # A list of flags that can be added to a toolchain configuration.
+ pw_cc_flag_set(
+ name = "user_compile_options",
+ actions = ALL_COMPILE_ACTIONS,
+ flag_groups = [
+ ":user_compile_options_flags",
+ ]
+ )
+
+ # A more complex compiler flag that requires template expansion.
+ pw_cc_flag_group(
+ name = "user_compile_options_flags",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # The underlying definition of a complete C/C++ toolchain.
+ pw_cc_toolchain(
+ name = "host_toolchain_linux",
+ action_configs = [
+ ":clang",
+ ":clang++",
+ # ...
+ ],
+ additional_files = ":linux_sysroot_files",
+ action_config_flag_sets = [
+ "@pw_toolchain//flag_sets:no_canonical_prefixes",
+ ":user_compile_options",
+ ":werror",
+ ],
+ features = [
+ "@pw_toolchain//features:c++17",
+ ],
+ target_cpu = "x86_64",
+ target_system_name = "x86_64-unknown-linux-gnu",
+ toolchain_identifier = "host-toolchain-linux",
+ )
+
+ # Toolchain resolution parameters for the above C/C++ toolchain.
+ toolchain(
+ name = "host_cc_toolchain_linux",
+ exec_compatible_with = [
+ "@platforms//os:linux",
+ ],
+ target_compatible_with = [
+ "@platforms//os:linux",
+ ],
+ toolchain = ":host_toolchain_linux",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+ )
+
+2. Provide standard toolchain building-blocks
+=============================================
+Pigweed will build out a repository of sharable instantiations of the
+aforementioned custom rules to give projects the resources they need to quickly
+and easily assemble toolchains for desktop and embedded targets. This includes,
+but is not limited to:
+
+- Rules that define tool sets for common toolchains (LLVM/clang, GNU/gcc).
+- Fully specified, modular
+ `features <https://bazel.build/docs/cc-toolchain-config-reference#features>`_.
+- Common flag sets that users may want to apply directly to their toolchains.
+ (enabling/disabling warnings, C++ standard version, etc.)
+- Platform/architecture support rules, including host OS SDK integrations
+ (Xcode, Windows SDK) and architecture-specific flag sets.
+
+These components will help establish patterns that will make it significantly
+easier for Pigweed users (and Bazel users at large) to define their own
+toolchains.
+
+---------------------
+Problem investigation
+---------------------
+This section explores previous work, and details why existing solutions don't
+meet Pigweed's needs.
+
+bazelembedded/rules_cc_toolchain
+================================
+The `rules_cc_toolchain <https://github.com/bazelembedded/rules_cc_toolchain>`_
+as part of the larger bazelembedded suite was actually the initial foundation
+of Pigweed's Bazel build. While this served as a very good initial foundation,
+it didn't provide the flexibility needed to easily stand up additional
+toolchains in ways that gave downstream projects sufficient control over the
+flags, libraries, tools, and sysroot.
+
+To work around the limited configurability of toolchain flags, Pigweed employed
+the following workarounds:
+
+#. Place ``copts`` and ``linkopts`` in ``.bazelrc``: This was problematic
+ because ``.bazelrc`` is not intrinsically shared with or propagated to
+ downstream users of Pigweed. Also, flags here are unilaterally applied
+ without OS-specific considerations.
+#. Attach flags to build targets with custom wrappers: This approach
+ intrinsically requires the existence of the ``pw_cc_library``, which
+ introduces difficulty around consistent interoperability with other Bazel
+ projects (among other issues detailed in
+ `b/267498492 <https://issues.pigweed.dev/issues/267498492>`_).
+
+Some other issues encountered when working with this solution include:
+
+- These rules intended to be modular, but in practice were relatively tightly
+ coupled.
+- Transitive dependencies throughout the toolchain definition process resulted
+ in some hard-to-debug issues (see
+ `this pull request <https://github.com/bazelembedded/rules_cc_toolchain/pull/39>`_
+ and `b/254518544 <https://issues.pigweed.dev/issues/254518544>`_.
+
+bazelembedded/modular_cc_toolchains
+===================================
+The `modular_cc_toolchains <https://github.com/bazelembedded/modular_cc_toolchains>`_
+repository is a new attempt as part of the bazelembedded suite at providing
+truly modular toolchain rules. The proposed direction is much more in-line
+with the needs of Pigweed, but at the moment the repository exists as an
+initial draft of ideas rather than a complete implementation.
+
+This repository greatly inspired Pigweed's initial prototype for modular
+toolchains, but diverges significantly from the underlying Bazel C/C++
+toolchain building-blocks. If this work was already complete and
+well-established, it probably would have satisfied some of Pigweed's key needs.
+
+lowRISC/crt
+===========
+The `compiler repository toolkit <https://github.com/lowRISC/crt>`_ is another
+scalable approach at toolchains. This repository strives to be an all-in-one
+repository for embedded toolchains, and does a very good job at providing
+scalable models for establishing toolchains. This repository is relatively
+monolithic, though, and doesn't necessarily address the concern of quickly
+and easily standing up custom toolchains. Instead, it's more suited towards
+contributing new one-size-fits-all toolchains to ``crt`` directly.
+
+Android's toolchain
+===================
+Android's Bazel-based build has invested heavily in toolchains, but they're
+very tightly coupled to the use cases of Android. For example,
+`this <https://cs.android.com/android/platform/superproject/main/+/main:build/bazel/toolchains/clang/host/linux-x86/cc_toolchain_features.bzl;l=375-389;drc=097d710c349758fc2732497fe5c3d1b0a32fa4a8>`_ binds ``-fstrict-aliasing`` to a condition based on the target architecture.
+These toolchains scale for the purpose of Android, but unfortunately are
+inherently not modular or reusable outside of that context.
+
+Due to the sheer amount of investment in these toolchains, though, they serve
+as a good reference for building out a complete toolchain in Bazel.
+
+Pigweed's modular Bazel toolchain prototype
+===========================================
+As part of an exploratory phase of getting toolchains set up for Linux and
+macOS,
+`an initial prototype <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/157634>`_
+for modular Bazel toolchains was drafted and deployed to Pigweed. This work
+introduced two key build rules: ``pw_cc_toolchain_feature`` and
+``pw_cc_toolchain``. With both of these rules, it’s possible to instantiate a
+vast array of toolchain variants without writing a single line of Starlark. A
+few examples of these building blocks in action are provided below.
+
+.. code-block::
+
+ # pw_cc_toolchain example taken from https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain/host_clang/BUILD.bazel;l=113-143;drc=7df1768d915fe11dae05751f70f143e60acfb17a.
+
+ pw_cc_toolchain(
+ name = "host_toolchain_linux",
+ abi_libc_version = "unknown",
+ abi_version = "unknown",
+ all_files = ":all_linux_files",
+ ar = "@llvm_toolchain//:bin/llvm-ar",
+
+ # TODO: b/305737273 - Globbing all files for every action has a
+ # performance hit, make these more granular.
+ ar_files = ":all_linux_files",
+ as_files = ":all_linux_files",
+ compiler = "unknown",
+ compiler_files = ":all_linux_files",
+ coverage_files = ":all_linux_files",
+ cpp = "@llvm_toolchain//:bin/clang++",
+ dwp_files = ":all_linux_files",
+ feature_deps = [
+ ":linux_sysroot",
+ "@pw_toolchain//features:no_canonical_prefixes",
+ ],
+ gcc = "@llvm_toolchain//:bin/clang",
+ gcov = "@llvm_toolchain//:bin/llvm-cov",
+ host_system_name = "unknown",
+ ld = "@llvm_toolchain//:bin/clang++",
+ linker_files = ":all_linux_files",
+ objcopy_files = ":all_linux_files",
+ strip = "@llvm_toolchain//:bin/llvm-strip",
+ strip_files = ":all_linux_files",
+ supports_param_files = 0,
+ target_cpu = "unknown",
+ target_libc = "unknown",
+ target_system_name = "unknown",
+ toolchain_identifier = "host-toolchain-linux",
+ )
+
+ # pw_cc_toolchain_feature examples taken from https://cs.opensource.google/pigweed/pigweed/+/main:pw_toolchain_bazel/features/BUILD.bazel;l=21-34;drc=f96fd31675d136bd37a7f3840102cb256d555cea.
+
+ # Disables linking of the default C++ standard library to allow linking of a
+ # different version.
+ pw_cc_toolchain_feature(
+ name = "no_default_cpp_stdlib",
+ linkopts = ["-nostdlib++"],
+ )
+
+ # Prevent relative paths from being converted to absolute paths.
+ # Note: This initial prototype made this a feature, but it should instead
+ # exist as a flag_set.
+ pw_cc_toolchain_feature(
+ name = "no_canonical_prefixes",
+ copts = [
+ "-no-canonical-prefixes",
+ ],
+ )
+
+What’s worth noting is that the ``pw_cc_toolchain_feature`` build rule looks
+very similar to a GN ``config``. This was no mistake, and was an attempt to
+substantially reduce the boiler plate for creating new sharable compiler flag
+groups.
+
+Unfortunately, it quickly became apparent that this approach limited control
+over the underlying toolchain definition creation process. In order to support
+``always_link`` on macOS, a custom logic and flags had to be directly baked into
+the rule used to declare toolchains
+(`relevant change <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168614/17/pw_toolchain_bazel/cc_toolchain/private/cc_toolchain.bzl>`_).
+While workarounds like this should be possible, the fact that this had to be
+upstreamed internally to ``pw_cc_toolchain`` exposed limitations in the
+abstraction patterns that were established. Such limitations could preclude
+some project from using ``pw_cc_toolchain`` at all.
+
+---------------
+Detailed design
+---------------
+The core design proposal is to transform the providers used by
+``cc_common.create_cc_toolchain_config_info()`` into build rules. The approach
+has been prototyped
+`here <https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/168351/1>`_,
+and retains API compatibility with the initial prototype as a proof-of-concept.
+
+One core pattern established by this design is transforming content that would
+typically live as Starlark to instead live in build files. This is done to
+make it easier to read and reference existing work.
+
+Implementation requirements
+===========================
+Compatibility with native C/C++ rules
+-------------------------------------
+The core of Pigweed's toolchain build rules will rely on the providers
+defined as part of Bazel's
+`rules_cc <https://github.com/bazelbuild/rules_cc/blob/main/cc/cc_toolchain_config_lib.bzl>`_. This means that the new rules can interop with
+existing work that directly uses these toolchain primitives. It also provides
+a clear path for migrating existing toolchains piece-by-piece (which may be
+written completely in Starlark).
+
+Any extensions beyond the existing providers (e.g. specifying
+``additional_files`` on a ``pw_cc_tool``) must happen parallel to existing
+providers so that rules that consume the ``cc_toolchain_config_lib`` providers
+can work with vanilla providers.
+
+Compatibility with Bazel rules ecosystem
+----------------------------------------
+In following with the larger Bazel rules ecosystem, the toolchain building
+blocks will be designed such that they can be used independently from Pigweed.
+This allows this work to be used for non-embedded projects, and reduces the
+overhead for standing up a custom Bazel C/C++ toolchain in any arbitrary
+project.
+
+Initially, the work will live as ``pw_toolchain_bazel`` in the main Pigweed
+repository to facilitate testing. This module must not depend on any other
+aspects of Pigweed. As the toolchain rules mature, they will eventually be
+available as a separate repository to match the modularity patterns used by
+the larger Bazel rules ecosystem.
+
+Introduce ``pw_cc_flag_set`` and ``pw_cc_flag_group``
+=====================================================
+The majority of build flags would be expressed as ``pw_cc_flag_set`` and
+``pw_cc_flag_group`` pairs.
+
+.. code-block::
+
+ # A simple flag_set with a single flag.
+ pw_cc_flag_set(
+ name = "werror",
+ # Only applies to C/C++ compile actions (i.e. no assemble/link/ar).
+ actions = ALL_CPP_COMPILER_ACTIONS + ALL_C_COMPILER_ACTIONS,
+ flags = [
+ "-Werror",
+ ],
+ )
+
+ # A flag_group that potentially expands to multiple flags.
+ pw_cc_flag_group(
+ name = "user_compile_options_flags",
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ )
+
+ # A flag_set that relies on a non-trivial or non-constant expression of
+ # flags.
+ pw_cc_flag_set(
+ name = "user_compile_options",
+ actions = ALL_COMPILE_ACTIONS,
+ flag_groups = [
+ ":user_compile_options_flags",
+ ]
+ )
+
+These closely mimic the API of ``cc_toolchain_config_lib.flag_set()`` and
+``cc_toolchain_config_lib.flag_group()``, with the following exceptions:
+
+**pw_cc_flag_set**
+
+*Added*
+
+- ``flags`` (added): Express a constant, trivial list of flags. If this is
+ specified, ``flag_groups`` may not be specified. This eliminates the need
+ for specifying a corresponding ``pw_cc_flag_group`` for every
+ ``pw_cc_flag_set`` for most flags.
+
+**pw_cc_flag_group**
+
+*Removed*
+
+- ``expand_if_true``\, ``expand_if_false``\, ``expand_if_equal``\: More complex
+ rules that rely on these should live as custom Starlark rules that provide a
+ ``FlagGroupInfo``\, or ``FlagSetInfo`` (depending on which is more ergonomic
+ to express the intent). See :ref:`pw_cc_flag_set-exceptions` below for an
+ example that illustrates how express more complex ``flag_group``\s that rely
+ on these attributes.
+
+Application of flags
+--------------------
+Flags can be applied to a toolchain in three ways. This section attempts to
+provide initial guidance for where flags should be applied, though it's likely
+better practices will evolve as this work sees more use. For the latest
+guidance, please consult the official documentation when it rolls out to
+``pw_toolchain_bazel``.
+
+Flags unconditionally applied to a toolchain
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The majority of flags fall into this category. Architecture flags,
+globally-applied warnings, global defines, and other similar flags should be
+applied in the ``action_config_flag_sets`` attribute of a ``pw_cc_toolchain``
+(see :ref:`pw_cc_toolchain-toolchain-declarations` for more information). Each
+``pw_cc_flag_set`` (or other rule that provides a ``FlagSetInfo`` provider)
+listed in ``action_config_flag_sets`` is unconditionally applied to every tool
+that matches the ``actions`` listed in the flag set.
+
+.. _application-of-flags-feature-flags:
+
+Feature flags
+~~~~~~~~~~~~~
+Flag sets applied as features may or may not be enabled even if they are listed
+in the ``features`` attribute of a ``pw_cc_toolchain``. The
+`official Bazel documentation on features <https://bazel.build/docs/cc-toolchain-config-reference#features>`_
+provides some good guidance on when features should be employed. To summarize,
+features should be used when either they should be controllable by users
+invoking the build, or if they affect build behavior beyond simply
+adding/removing flags (e.g. by introducing additional build actions).
+
+Flags unconditionally applied to a tool
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+These flags are flags that are bound to a particular tool. These are not
+expressed as part of a ``pw_cc_toolchain``, and are instead bound to a
+``pw_cc_action_config``. This means that the flag set is unconditionally
+applied to every user of that action config. These kinds of flag applications
+should be reserved for flags required to assemble a working set of tools (such
+as generating a depfile, or adding support for static library link handling
+:ref:`as illustrated below <pw_cc_flag_set-exceptions>`).
+
+Flag application order
+~~~~~~~~~~~~~~~~~~~~~~
+When invoking the underlying tools, the intended order of flags is as follows:
+
+#. Flags listed in the ``flag_sets`` list of a ``pw_cc_action_config``.
+#. Flags listed in ``action_config_flag_sets`` of a ``pw_cc_toolchain``.
+#. Flags listed in ``features`` of a ``pw_cc_toolchain``.
+
+These lists are intended to be sensitive to ordering, earlier items in the lists
+should appear in the final tool invocation flags before later items in the list.
+
+As transitive dependencies between features/flags are not supported as part of
+this proposal, exact traversal of transitive flag dependencies will be left
+to be decided if/when that feature is introduced. This proposal suggests
+postorder handling of flags as the most intuitive order.
+
+.. _pw_cc_flag_set-exceptions:
+
+Exceptions
+----------
+Some flags are too complex to be nicely expressed in a Bazel build file. These
+flag sets or flag groups will need to be expressed in Starlark as custom rules.
+Fortunately, this will interop well with simpler flag sets since the underlying
+providers are all the same.
+
+**Example**
+
+In a Starlark file (e.g. ``//tools/llvm/llvm_ar_patch.bzl``), the required
+``flag_set`` can be defined:
+
+.. code-block::
+
+ # Starlark rules in a .bzl file for a relatively complicated workaround for
+ # what would normally be inherently managed by Bazel internally.
+ # TODO: b/297413805 - Remove this implementation.
+
+ def _pw_cc_static_libs_to_link_impl():
+ """Returns a flag_set provider that sets up static libraries to link."""
+ return flag_set(
+ actions = [
+ ACTION_NAMES.cpp_link_static_library,
+ ],
+ flag_groups = [
+ flag_group(
+ expand_if_available = "libraries_to_link",
+ iterate_over = "libraries_to_link",
+ flag_groups = [
+ flag_group(
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "object_file",
+ ),
+ flags = ["%{libraries_to_link.name}"],
+ ),
+ flag_group(
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "object_file_group",
+ ),
+ flags = ["%{libraries_to_link.object_files}"],
+ iterate_over = "libraries_to_link.object_files",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ pw_cc_static_libs_to_link = rule(
+ implementation = _pw_cc_static_libs_to_link_impl,
+ provides = [FlagSetInfo],
+ )
+
+And then in the ``BUILD.bazel`` file, the rules would be used as if they
+were a ``pw_cc_flag_set``:
+
+.. code-block::
+
+ load(
+ "@pw_toolchain//tools/llvm:llvm_ar_patch.bzl",
+ "pw_cc_static_libs_to_link"
+ )
+
+ pw_cc_static_libs_to_link(
+ name = "static_library_action_flags",
+ )
+
+ pw_cc_action_config(
+ name = "llvm_ar",
+ actions = ACTION_NAMES.cpp_link_static_library,
+ tools = [
+ ":llvm_ar_tool",
+ ],
+ flag_sets = [
+ ":static_library_action_flags",
+ ],
+ )
+
+Introduce ``pw_cc_feature`` and ``pw_cc_feature_set``
+=====================================================
+These types are just permutations of the ``cc_toolchain_config_lib.feature()``
+and ``cc_toolchain_config_lib.with_feature_set()`` API. For guidance on when
+these should be used, see
+:ref:`application of feature flags <application-of-flags-feature-flags>`.
+
+.. code-block::
+
+ pw_cc_feature_set(
+ name = "static_pie_requirements",
+ with_features = ["pie"],
+ # If this doesn't work when certain features are enabled, they should
+ # be specified as ``without_features``.
+ )
+
+ pw_cc_feature(
+ name = "static_pie",
+ flag_sets = [
+ "//flag_sets:static_pie",
+ ],
+ implies = ["static_link_flag"],
+ requires = [
+ ":static_pie_requirements",
+ ],
+ )
+
+Introduce ``pw_cc_action_config`` and ``pw_cc_tool``
+====================================================
+These are closely related to the ``ActionConfigInfo`` and ``ToolInfo``
+providers, but allow additional files to be attached and a list of actions to
+be attached rather than a single action.
+
+.. code-block::
+
+ pw_cc_tool(
+ name = "clang_tool",
+ path = "@llvm_toolchain//:bin/clang",
+ additional_files = [
+ "@llvm_toolchain//:all",
+ ],
+ )
+
+ pw_cc_action_config(
+ name = "clang",
+ actions = ALL_ASM_ACTIONS + ALL_C_COMPILER_ACTIONS,
+ tools = [
+ ":clang_tool",
+ ],
+ flag_sets = [
+ # Most flags should NOT end up here. Only unconditional flags that
+ # should ALWAYS be bound to this tool (e.g. static library
+ # workaround fix for macOS).
+ "//flag_sets:generate_depfile",
+ ],
+ )
+
+.. _pw_cc_toolchain-toolchain-declarations:
+
+Toolchain declarations
+======================
+In following with the other proposed rules, ``pw_cc_toolchain`` largely
+follows the API of ``cc_common.create_cc_toolchain_config_info()``. Most of the
+attributes are logically passed through, with the following exceptions:
+
+- **action_config_flag_sets**: Flag sets to apply to action configs. Since flag
+ sets are intrinsically bound to actions, there’s no need to divide them at
+ this level.
+- **additional_files**: Now that tools can spec out required files, those
+ should be propagated and mostly managed internally. The ``\*_files`` members
+ will still be available, but shouldn’t see much use. additional_files is like
+ “all_files”, but applies to all action_configs.
+
+.. code-block::
+
+ pw_cc_toolchain(
+ name = "host_toolchain_linux",
+ abi_libc_version = "unknown", # We should consider how to move this out in the future.
+ abi_version = "unknown",
+ action_configs = [
+ "@llvm_toolchain//tools:clang",
+ "@llvm_toolchain//tools:clang++",
+ "@llvm_toolchain//tools:lld",
+ "@llvm_toolchain//tools:llvm_ar",
+ "@llvm_toolchain//tools:llvm_cov",
+ "@llvm_toolchain//tools:llvm_strip",
+ ],
+ additional_files = ":linux_sysroot_files",
+ action_config_flag_sets = [
+ ":linux_sysroot",
+ "@pw_toolchain//flag_collections:strict_warnings",
+ "@pw_toolchain//flag_sets:no_canonical_prefixes",
+ ],
+ features = [
+ "@pw_toolchain//features:c++17",
+ ],
+ host_system_name = "unknown",
+ supports_param_files = 0, # Seems like this should be attached to a pw_cc_action_config...
+ target_cpu = "unknown",
+ target_libc = "unknown",
+ target_system_name = "unknown",
+ toolchain_identifier = "host-toolchain-linux",
+ cxx_builtin_include_directories = [
+ "%package(@llvm_toolchain//)%/include/x86_64-unknown-linux-gnu/c++/v1",
+ "%package(@llvm_toolchain//)%/include/c++/v1",
+ "%package(@llvm_toolchain//)%/lib/clang/17/include",
+ "%sysroot%/usr/local/include",
+ "%sysroot%/usr/include/x86_64-linux-gnu",
+ "%sysroot%/usr/include",
+ ],
+ )
+
+------------
+Alternatives
+------------
+Improve Bazel's native C/C++ toolchain rules
+============================================
+Improving Bazel's native rules for defining C/C++ toolchains is out of the
+scope of Pigweed's work. Changing the underlying toolchain API as Bazel
+understands it is a massive undertaking from the perspective of migrating
+existing code. We hope that the custom rule effort can help guide future
+decisions when it comes to toolchain scalability and maintainability.
+
+----------
+Next steps
+----------
+Rust toolchain interop
+======================
+Pigweed's Rust toolchains have some interoperability concerns and requirements.
+The extend of this needs to be thoroughly investigated as a next step to ensure
+that the Rust/C/C++ toolchain experience is relatively unified and ergonomic.
+
+More maintainable ``cxx_builtin_include_directories``
+=====================================================
+In the future, it would be nice to have a more sharable solution for managing
+``cxx_builtin_include_directories`` on a ``pw_cc_toolchain``. This could
+plausibly be done by allowing ``pw_cc_flag_set`` to express
+``cxx_builtin_include_directories`` so they can be propagated back up to the
+``pw_cc_toolchain``.
+
+Feature name collision guidance
+===============================
+Features support relatively complex relationships among each other, but
+traditionally rely on string names to express these relationships rather than
+labels. This introduces significant ambiguity, as it's possible for multiple
+features to use the same logical name so long as they aren't both employed in
+the same toolchain. In practice, the only way to tell what features will end up
+enabled is to manually unpack what features a toolchain pulls in, and
+cross-reference it against the output of
+`--experimental_save_feature_state <https://bazel.build/reference/command-line-reference#flag--experimental_save_feature_state>`_.
+
+One potential solution to this problem is to add a mechanism for expressing
+features as labels, which will allow relationships to be expressed more
+concretely, and help prevent unintended naming collisions. This would not
+replace the ability to express relationships with features not accessible via
+labels, but rather live alongside it.
diff --git a/seed/BUILD.gn b/seed/BUILD.gn
index 76a95b901..62aa25808 100644
--- a/seed/BUILD.gn
+++ b/seed/BUILD.gn
@@ -28,7 +28,10 @@ pw_doc_group("docs") {
":0107",
":0108",
":0109",
+ ":0110",
":0111",
+ ":0112",
+ ":0113",
]
}
@@ -69,6 +72,18 @@ pw_doc_group("0109") {
sources = [ "0109-comms-buffers.rst" ]
}
+pw_doc_group("0110") {
+ sources = [ "0110-memory-allocation-interfaces.rst" ]
+}
+
pw_doc_group("0111") {
sources = [ "0111-build-systems.rst" ]
}
+
+pw_doc_group("0112") {
+ sources = [ "0112-async-poll.rst" ]
+}
+
+pw_doc_group("0113") {
+ sources = [ "0113-bazel-cc-toolchain-api.rst" ]
+}
diff --git a/targets/BUILD.bazel b/targets/BUILD.bazel
index 2a7fb6ba4..66a1ccd5e 100644
--- a/targets/BUILD.bazel
+++ b/targets/BUILD.bazel
@@ -14,7 +14,8 @@
# Bazel build flags.
#
-# See for how these flags are used.
+# See https://pigweed.dev/build_system.html#facades-and-backends-tutorial for
+# how these flags are used.
#
# Please keep this list sorted by name, i.e. the following command should yield
# no output:
diff --git a/targets/arduino/target_toolchains.gni b/targets/arduino/target_toolchains.gni
index 3b74b8b96..82ef602ed 100644
--- a/targets/arduino/target_toolchains.gni
+++ b/targets/arduino/target_toolchains.gni
@@ -14,6 +14,7 @@
import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/defaults.gni")
import("$dir_pw_sys_io/backend.gni")
import("$dir_pw_toolchain/arm_gcc/toolchains.gni")
@@ -65,10 +66,10 @@ _toolchain_properties = {
final_binary_extension = ".elf"
}
-_target_default_configs = [
- "$dir_pw_toolchain/arm_gcc:enable_float_printf",
- "$dir_pigweed/targets/arduino:arduino_build",
-]
+_target_default_configs = pigweed_default_configs + [
+ "$dir_pw_toolchain/arm_gcc:enable_float_printf",
+ "$dir_pigweed/targets/arduino:arduino_build",
+ ]
pw_target_toolchain_arduino = {
_excluded_members = [
diff --git a/targets/docs/BUILD.gn b/targets/docs/BUILD.gn
index e6c723a56..eb9a000b1 100644
--- a/targets/docs/BUILD.gn
+++ b/targets/docs/BUILD.gn
@@ -44,6 +44,10 @@ generate_toolchain("docs") {
"defaults",
"name",
])
+
+ # Docs assume output binary location, and don't support extensions.
+ final_binary_extension = ""
+
defaults = {
forward_variables_from(_base_toolchain.defaults, "*")
diff --git a/targets/host/target_docs.rst b/targets/host/target_docs.rst
index 266ce71d1..99826a03b 100644
--- a/targets/host/target_docs.rst
+++ b/targets/host/target_docs.rst
@@ -49,14 +49,12 @@ downstream projects.
Toolchains for other C++ standards
==================================
-Most Pigweed code requires C++17, but a few modules, such as ``pw_tokenizer``,
-work with C++14. All Pigweed code is compatible with C++20. Pigweed defines
-toolchains for testing with C++14 and C++20.
+Pigweed code requires C++17 or newer and is fully compatible with C++20. Pigweed
+defines a toolchain for testing with C++20.
-* ``pw_strict_host_clang_debug_cpp14`` -- Builds with ``-std=c++14``.
* ``pw_strict_host_clang_size_optimized_cpp20`` -- Builds with ``-std=c++20``.
-These toolchains are only permitted for use in upstream pigweed, but downstream
+This toolchains is only permitted for use in upstream pigweed, but downstream
users may create similar toolchains as needed.
--------
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index b53e0f679..4d6b3e7db 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -38,7 +38,7 @@ _host_common = {
pw_perf_test_MAIN_FUNCTION = "$dir_pw_perf_test:log_perf_handler_main"
# Configure backend for assert facade.
- pw_assert_BACKEND = "$dir_pw_assert_basic"
+ pw_assert_BACKEND = "$dir_pw_assert:print_and_abort_check_backend"
pw_assert_LITE_BACKEND = "$dir_pw_assert:print_and_abort_assert_backend"
# Configure backend for async facade.
@@ -435,30 +435,6 @@ pw_internal_host_toolchains = [
}
},
{
- name = "pw_strict_host_clang_debug_cpp14"
- _toolchain_base = pw_toolchain_host_clang.debug
- forward_variables_from(_toolchain_base, "*", _excluded_members)
- defaults = {
- forward_variables_from(_toolchain_base.defaults, "*")
- forward_variables_from(_host_common, "*")
- forward_variables_from(_pigweed_internal, "*")
- forward_variables_from(_os_specific_config, "*")
- default_configs += _internal_clang_default_configs
-
- # Set the C++ standard to C++14 instead of the default (C++17).
- pw_toolchain_CXX_STANDARD = pw_toolchain_STANDARD.CXX14
-
- # Do not do negative compilation testing with C++14 since the code may
- # fail to compile for different reasons than in C++17 or newer.
- pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = false
-
- # Select C++14-compatible backends.
- pw_assert_BACKEND = "$dir_pw_assert:print_and_abort_check_backend"
- pw_log_BACKEND = "$dir_pw_log_null"
- pw_unit_test_MAIN = "$dir_pw_unit_test:printf_main"
- }
- },
- {
name = "pw_strict_host_clang_size_optimized_cpp20"
_toolchain_base = pw_toolchain_host_clang.size_optimized
forward_variables_from(_toolchain_base, "*", _excluded_members)
diff --git a/targets/stm32f429i_disc1_stm32cube/BUILD.bazel b/targets/stm32f429i_disc1_stm32cube/BUILD.bazel
index 7f4217403..44109fa46 100644
--- a/targets/stm32f429i_disc1_stm32cube/BUILD.bazel
+++ b/targets/stm32f429i_disc1_stm32cube/BUILD.bazel
@@ -16,6 +16,7 @@ load(
"//pw_build:pigweed.bzl",
"pw_cc_binary",
"pw_cc_library",
+ "pw_linker_script",
)
package(default_visibility = ["//visibility:public"])
@@ -44,40 +45,72 @@ constraint_value(
constraint_setting = "//third_party/freertos:freertos_config_setting",
)
-# TODO: b/261506064 - Additional constraint values for configuring stm32cube
-# need to be added here, once constraint settings for stm32cube are defined.
+# TODO: b/301334234 - Set the flags currently in the stm32f429i config in
+# .bazelrc using this platform, once that's supported.
platform(
name = "platform",
constraint_values = [
- "@pw_toolchain//constraints/arm_mcpu:cortex-m4",
- "//pw_build/constraints/rtos:freertos",
":freertos_config_cv",
+ "//pw_build/constraints/rtos:freertos",
+ "//pw_interrupt_cortex_m:backend",
+ "//pw_sys_io_stm32cube:backend",
"@freertos//:port_ARM_CM4F",
+ "@freertos//:disable_task_statics",
"@platforms//cpu:armv7e-m",
+ "@pw_toolchain//constraints/arm_mcpu:cortex-m4",
],
)
pw_cc_library(
+ name = "hal_config",
+ hdrs = [
+ "config/stm32f4xx_hal_conf.h",
+ ],
+ includes = ["config"],
+)
+
+pw_cc_library(
name = "pre_init",
srcs = [
"boot.cc",
"vector_table.c",
],
- hdrs = [
- "config/stm32f4xx_hal_conf.h",
- ],
+ copts = ["-Wno-return-type"],
+ defines = ["PW_MALLOC_ACTIVE=1"],
target_compatible_with = [":freertos_config_cv"],
deps = [
":freertos_config",
+ "//pw_assert",
"//pw_boot",
"//pw_boot_cortex_m",
"//pw_malloc",
"//pw_preprocessor",
"//pw_string",
"//pw_sys_io_stm32cube",
+ "//pw_system:init",
"//third_party/stm32cube",
"@freertos",
],
+ alwayslink = 1,
+)
+
+pw_linker_script(
+ name = "linker_script",
+ defines = [
+ "PW_BOOT_FLASH_BEGIN=0x08000200",
+ "PW_BOOT_FLASH_SIZE=2048K",
+
+ # TODO(b/235348465): Currently "pw_tokenizer/detokenize_test" requires at
+ # least 6K bytes in heap when using pw_malloc_freelist. The heap size
+ # required for tests should be investigated.
+ "PW_BOOT_HEAP_SIZE=7K",
+ "PW_BOOT_MIN_STACK_SIZE=1K",
+ "PW_BOOT_RAM_BEGIN=0x20000000",
+ "PW_BOOT_RAM_SIZE=192K",
+ "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
+ "PW_BOOT_VECTOR_TABLE_SIZE=512",
+ ],
+ linker_script = "//pw_boot_cortex_m:basic_cortex_m.ld",
)
pw_cc_binary(
diff --git a/targets/stm32f429i_disc1_stm32cube/BUILD.gn b/targets/stm32f429i_disc1_stm32cube/BUILD.gn
index 440882816..87a07f88e 100644
--- a/targets/stm32f429i_disc1_stm32cube/BUILD.gn
+++ b/targets/stm32f429i_disc1_stm32cube/BUILD.gn
@@ -19,6 +19,7 @@ import("$dir_pw_docgen/docs.gni")
import("$dir_pw_malloc/backend.gni")
import("$dir_pw_system/system_target.gni")
import("$dir_pw_third_party/stm32cube/stm32cube.gni")
+import("$dir_pw_toolchain/arm_clang/toolchains.gni")
import("$dir_pw_toolchain/generate_toolchain.gni")
config("pw_malloc_active") {
@@ -63,37 +64,51 @@ if (current_toolchain != default_toolchain) {
}
}
+common_link_deps =
+ [ "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:pre_init" ]
+
+common_build_args = {
+ pw_log_BACKEND = dir_pw_log_tokenized
+ pw_log_tokenized_HANDLER_BACKEND = "$dir_pw_system:log_backend.impl"
+ pw_third_party_freertos_CONFIG = "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f4xx_freertos_config"
+ pw_third_party_freertos_PORT = "$dir_pw_third_party/freertos:arm_cm4f"
+ pw_sys_io_BACKEND = dir_pw_sys_io_stm32cube
+ dir_pw_third_party_stm32cube = dir_pw_third_party_stm32cube_f4
+ pw_third_party_stm32cube_PRODUCT = "STM32F429xx"
+ pw_third_party_stm32cube_CONFIG =
+ "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f4xx_hal_config"
+ pw_third_party_stm32cube_CORE_INIT = ""
+ pw_boot_cortex_m_LINK_CONFIG_DEFINES = [
+ "PW_BOOT_FLASH_BEGIN=0x08000200",
+ "PW_BOOT_FLASH_SIZE=2048K",
+
+ # TODO: b/235348465 - Currently "pw_tokenizer/detokenize_test" requires at
+ # least 6K bytes in heap when using pw_malloc_freelist. The heap size
+ # required for tests should be investigated.
+ "PW_BOOT_HEAP_SIZE=7K",
+ "PW_BOOT_MIN_STACK_SIZE=1K",
+ "PW_BOOT_RAM_BEGIN=0x20000000",
+ "PW_BOOT_RAM_SIZE=192K",
+ "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
+ "PW_BOOT_VECTOR_TABLE_SIZE=512",
+ ]
+}
+
pw_system_target("stm32f429i_disc1_stm32cube") {
cpu = PW_SYSTEM_CPU.CORTEX_M4F
scheduler = PW_SYSTEM_SCHEDULER.FREERTOS
- link_deps = [ "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:pre_init" ]
- build_args = {
- pw_log_BACKEND = dir_pw_log_tokenized
- pw_log_tokenized_HANDLER_BACKEND = "$dir_pw_system:log_backend.impl"
- pw_third_party_freertos_CONFIG = "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f4xx_freertos_config"
- pw_third_party_freertos_PORT = "$dir_pw_third_party/freertos:arm_cm4f"
- pw_sys_io_BACKEND = dir_pw_sys_io_stm32cube
- dir_pw_third_party_stm32cube = dir_pw_third_party_stm32cube_f4
- pw_third_party_stm32cube_PRODUCT = "STM32F429xx"
- pw_third_party_stm32cube_CONFIG =
- "$dir_pigweed/targets/stm32f429i_disc1_stm32cube:stm32f4xx_hal_config"
- pw_third_party_stm32cube_CORE_INIT = ""
- pw_boot_cortex_m_LINK_CONFIG_DEFINES = [
- "PW_BOOT_FLASH_BEGIN=0x08000200",
- "PW_BOOT_FLASH_SIZE=2048K",
+ link_deps = common_link_deps
+ build_args = common_build_args
+}
+
+pw_system_target("stm32f429i_disc1_stm32cube_clang") {
+ cpu = PW_SYSTEM_CPU.CORTEX_M4F
+ scheduler = PW_SYSTEM_SCHEDULER.FREERTOS
+ system_toolchain = pw_toolchain_arm_clang
- # TODO: b/235348465 - Currently "pw_tokenizer/detokenize_test" requires at
- # least 6K bytes in heap when using pw_malloc_freelist. The heap size
- # required for tests should be investigated.
- "PW_BOOT_HEAP_SIZE=7K",
- "PW_BOOT_MIN_STACK_SIZE=1K",
- "PW_BOOT_RAM_BEGIN=0x20000000",
- "PW_BOOT_RAM_SIZE=192K",
- "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
- "PW_BOOT_VECTOR_TABLE_SIZE=512",
- ]
- }
+ link_deps = common_link_deps
+ build_args = common_build_args
}
pw_doc_group("target_docs") {
diff --git a/targets/stm32f429i_disc1_stm32cube/boot.cc b/targets/stm32f429i_disc1_stm32cube/boot.cc
index 8f00947bf..adca712db 100644
--- a/targets/stm32f429i_disc1_stm32cube/boot.cc
+++ b/targets/stm32f429i_disc1_stm32cube/boot.cc
@@ -17,6 +17,7 @@
#include <array>
#include "FreeRTOS.h"
+#include "pw_assert/check.h"
#include "pw_boot_cortex_m/boot.h"
#include "pw_malloc/malloc.h"
#include "pw_preprocessor/compiler.h"
diff --git a/targets/stm32f429i_disc1_stm32cube/config/stm32f4xx_hal_conf.h b/targets/stm32f429i_disc1_stm32cube/config/stm32f4xx_hal_conf.h
index a3f2e8f49..f7d2c082f 100644
--- a/targets/stm32f429i_disc1_stm32cube/config/stm32f4xx_hal_conf.h
+++ b/targets/stm32f429i_disc1_stm32cube/config/stm32f4xx_hal_conf.h
@@ -14,6 +14,8 @@
#pragma once
+#include <stdint.h>
+
/* Clock setup */
#define HSI_VALUE 16000000U
#define LSI_VALUE 32000U
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index 85cef78d9..73acfe1d9 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -50,6 +50,9 @@ if (pw_third_party_boringssl_ALIAS != "") {
# The ARM assembly code is only for cortex-A.
"OPENSSL_NO_ASM",
+ # socklen_t is not defined
+ "OPENSSL_NO_SOCK",
+
# Disable assert, which may additionally link in unwanted binaries via
# argument evaluation.
"NDEBUG",
diff --git a/third_party/llvm_builtins/BUILD.gn b/third_party/llvm_builtins/BUILD.gn
new file mode 100644
index 000000000..d38f26ffe
--- /dev/null
+++ b/third_party/llvm_builtins/BUILD.gn
@@ -0,0 +1,260 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/error.gni")
+import("$dir_pw_build/target_types.gni")
+
+declare_args() {
+ # This should be set to the path of the llvm compiler-rt/builtins directory.
+ dir_pw_third_party_llvm_builtins = ""
+
+ # Points to a pw_source_set that enumerates builtins specific to
+ # the current toolchain.
+ pw_third_party_llvm_builtins_TARGET_BUILTINS = ""
+}
+
+config("no-warnings") {
+ cflags = [
+ "-Wno-double-promotion",
+ "-Wno-undef",
+ "-Wno-unused-parameter",
+ "-Wno-strict-prototypes",
+ ]
+}
+
+files = [
+ "absvdi2.c",
+ "absvsi2.c",
+ "absvti2.c",
+ "adddf3.c",
+ "addsf3.c",
+ "addtf3.c",
+ "addvdi3.c",
+ "addvsi3.c",
+ "addvti3.c",
+ "ashldi3.c",
+ "ashlti3.c",
+ "ashrdi3.c",
+ "ashrti3.c",
+ "clzti2.c",
+ "cmpdi2.c",
+ "cmpti2.c",
+ "comparedf2.c",
+ "ctzdi2.c",
+ "ctzsi2.c",
+ "ctzti2.c",
+ "divdc3.c",
+ "divdf3.c",
+ "divdi3.c",
+ "divmoddi4.c",
+ "divsc3.c",
+ "divsf3.c",
+ "divtc3.c",
+ "divtf3.c",
+ "divti3.c",
+ "extendhfsf2.c",
+ "extendsfdf2.c",
+ "ffsdi2.c",
+ "ffssi2.c",
+ "ffsti2.c",
+ "fixdfdi.c",
+ "fixdfsi.c",
+ "fixdfti.c",
+ "fixsfdi.c",
+ "fixsfsi.c",
+ "fixsfti.c",
+ "fixunsdfdi.c",
+ "fixunsdfsi.c",
+ "fixunsdfti.c",
+ "fixunssfdi.c",
+ "fixunssfsi.c",
+ "fixunssfti.c",
+ "floatdidf.c",
+ "floatdisf.c",
+ "floatsidf.c",
+ "floatsisf.c",
+ "floattidf.c",
+ "floattisf.c",
+ "floatundidf.c",
+ "floatundisf.c",
+ "floatunsidf.c",
+ "floatunsisf.c",
+ "floatuntidf.c",
+ "floatuntisf.c",
+ "int_util.c",
+ "lshrdi3.c",
+ "lshrti3.c",
+ "moddi3.c",
+ "modti3.c",
+ "muldc3.c",
+ "muldf3.c",
+ "muldi3.c",
+ "mulodi4.c",
+ "mulosi4.c",
+ "muloti4.c",
+ "mulsc3.c",
+ "mulsf3.c",
+ "multf3.c",
+ "multi3.c",
+ "mulvdi3.c",
+ "mulvsi3.c",
+ "mulvti3.c",
+ "negdf2.c",
+ "negdi2.c",
+ "negsf2.c",
+ "negti2.c",
+ "negvdi2.c",
+ "negvsi2.c",
+ "negvti2.c",
+ "os_version_check.c",
+ "paritydi2.c",
+ "paritysi2.c",
+ "parityti2.c",
+ "popcountdi2.c",
+ "popcountsi2.c",
+ "popcountti2.c",
+ "powidf2.c",
+ "powisf2.c",
+ "powitf2.c",
+ "subdf3.c",
+ "subsf3.c",
+ "subtf3.c",
+ "subvdi3.c",
+ "subvsi3.c",
+ "subvti3.c",
+ "trampoline_setup.c",
+ "truncdfhf2.c",
+ "truncdfsf2.c",
+ "truncsfhf2.c",
+ "ucmpdi2.c",
+ "ucmpti2.c",
+ "udivdi3.c",
+ "udivmoddi4.c",
+ "udivmodti4.c",
+ "udivti3.c",
+ "umoddi3.c",
+ "umodti3.c",
+]
+
+pw_source_set("arm_builtins") {
+ configs = [ ":no-warnings" ]
+ remove_configs = [ "//pw_build:extra_strict_warnings" ]
+
+ sources = []
+
+ arm_files = [
+ "arm/aeabi_cdcmp.S",
+ "arm/aeabi_cdcmpeq_check_nan.c",
+ "arm/aeabi_cfcmp.S",
+ "arm/aeabi_cfcmpeq_check_nan.c",
+ "arm/aeabi_dcmp.S",
+ "arm/aeabi_div0.c",
+ "arm/aeabi_drsub.c",
+ "arm/aeabi_fcmp.S",
+ "arm/aeabi_frsub.c",
+ "arm/aeabi_idivmod.S",
+ "arm/aeabi_ldivmod.S",
+ "arm/aeabi_memcmp.S",
+ "arm/aeabi_memcpy.S",
+ "arm/aeabi_memmove.S",
+ "arm/aeabi_memset.S",
+ "arm/aeabi_uidivmod.S",
+ "arm/aeabi_uldivmod.S",
+ "arm/bswapdi2.S",
+ "arm/bswapsi2.S",
+ "arm/clzdi2.S",
+ "arm/clzsi2.S",
+ "arm/comparesf2.S",
+ "arm/divmodsi4.S",
+ "arm/divsi3.S",
+ "arm/fp_mode.c",
+ "arm/modsi3.S",
+ "arm/switch16.S",
+ "arm/switch32.S",
+ "arm/switch8.S",
+ "arm/switchu8.S",
+ "arm/sync_fetch_and_add_4.S",
+ "arm/sync_fetch_and_add_8.S",
+ "arm/sync_fetch_and_and_4.S",
+ "arm/sync_fetch_and_and_8.S",
+ "arm/sync_fetch_and_max_4.S",
+ "arm/sync_fetch_and_max_8.S",
+ "arm/sync_fetch_and_min_4.S",
+ "arm/sync_fetch_and_min_8.S",
+ "arm/sync_fetch_and_nand_4.S",
+ "arm/sync_fetch_and_nand_8.S",
+ "arm/sync_fetch_and_or_4.S",
+ "arm/sync_fetch_and_or_8.S",
+ "arm/sync_fetch_and_sub_4.S",
+ "arm/sync_fetch_and_sub_8.S",
+ "arm/sync_fetch_and_umax_4.S",
+ "arm/sync_fetch_and_umax_8.S",
+ "arm/sync_fetch_and_umin_4.S",
+ "arm/sync_fetch_and_umin_8.S",
+ "arm/sync_fetch_and_xor_4.S",
+ "arm/sync_fetch_and_xor_8.S",
+ "arm/sync_synchronize.S",
+ "arm/udivmodsi4.S",
+ "arm/udivsi3.S",
+ "arm/umodsi3.S",
+ ]
+
+ if (dir_pw_third_party_llvm_builtins != "") {
+ foreach(file, arm_files) {
+ sources += [ "$dir_pw_third_party_llvm_builtins/$file" ]
+ }
+ } else {
+ not_needed([ "arm_files" ])
+ }
+}
+
+# This list includes the cpu's for which their is a `:${current_cpu}_builtins`
+# target.
+_default_supported_cpus = [ "arm" ]
+_current_cpu_is_known = _default_supported_cpus + [ current_cpu ] -
+ [ current_cpu ] != _default_supported_cpus
+
+pw_static_library("llvm_builtins") {
+ add_global_link_deps = false
+
+ configs = [ ":no-warnings" ]
+ remove_configs = [ "//pw_build:extra_strict_warnings" ]
+
+ sources = []
+
+ if (dir_pw_third_party_llvm_builtins != "") {
+ foreach(file, files) {
+ sources += [ "$dir_pw_third_party_llvm_builtins/$file" ]
+ }
+ } else {
+ not_needed([ "files" ])
+ }
+
+ if (pw_third_party_llvm_builtins_TARGET_BUILTINS != "") {
+ deps = [ pw_third_party_llvm_builtins_TARGET_BUILTINS ]
+ } else if (_current_cpu_is_known) {
+ deps = [ ":${current_cpu}_builtins" ]
+ } else {
+ deps = [ ":unknown_cpu" ]
+ }
+}
+
+if (pw_third_party_llvm_builtins_TARGET_BUILTINS == "" &&
+ !_current_cpu_is_known) {
+ pw_error("unknown_cpu") {
+ message = "Tried to build $dir_pw_third_party_llvm_builtins:llvm_builtins, but pw_third_party_llvm_builtins_TARGET_BUILTINS was not set and `current_cpu=\"$current_cpu\"` does not have well-known builtins set up."
+ }
+}
diff --git a/third_party/llvm_libc/llvm_libc.gni b/third_party/llvm_libc/llvm_libc.gni
index 0636df794..fea250900 100644
--- a/third_party/llvm_libc/llvm_libc.gni
+++ b/third_party/llvm_libc/llvm_libc.gni
@@ -109,7 +109,7 @@ template("pw_libc_source_set") {
}
}
- pw_test("${source_set_target_name}.test") {
+ pw_test("${source_set_target_name}_tests") {
_dir = "$dir_pw_third_party_llvm_libc/test/src/$source_set_target_name"
# This might not be used if all test functions are in no_test_functions
diff --git a/third_party/nanopb/BUILD.gn b/third_party/nanopb/BUILD.gn
index a79221a69..56c0cc70e 100644
--- a/third_party/nanopb/BUILD.gn
+++ b/third_party/nanopb/BUILD.gn
@@ -60,8 +60,56 @@ if (dir_pw_third_party_nanopb != "") {
sources = [ "generate_nanopb_proto.py" ]
pylintrc = "$dir_pigweed/.pylintrc"
mypy_ini = "$dir_pigweed/.mypy.ini"
+
+ # Path to protoc binary. This variable is relative to the build directory,
+ # so it must be rebased as a source-tree-absolute path.
+ _protoc_binary_path =
+ "//" +
+ rebase_path(pw_protobuf_compiler_PROTOC_BINARY, "//", root_build_dir)
+
+ if (host_os == "win") {
+ if (get_path_info(_protoc_binary_path, "extension") != "exe" &&
+ get_path_info(_protoc_binary_path, "extension") != "bat") {
+ _protoc_binary_path += ".exe"
+ }
+ }
+
+ inputs = [
+ "$dir_pw_third_party_nanopb/pb.h",
+ "$dir_pw_third_party_nanopb/pb_common.h",
+ "$dir_pw_third_party_nanopb/pb_decode.h",
+ "$dir_pw_third_party_nanopb/pb_encode.h",
+ "$dir_pw_third_party_nanopb/pb_common.c",
+ "$dir_pw_third_party_nanopb/pb_decode.c",
+ "$dir_pw_third_party_nanopb/pb_encode.c",
+ "$dir_pw_third_party_nanopb/generator/nanopb_generator.py",
+ "$dir_pw_third_party_nanopb/generator/proto/google/protobuf",
+ "$dir_pw_third_party_nanopb/generator/proto/google/protobuf/descriptor.proto",
+ "$dir_pw_third_party_nanopb/generator/proto/__init__.py",
+ "$dir_pw_third_party_nanopb/generator/proto/nanopb.proto",
+ "$dir_pw_third_party_nanopb/generator/proto/_utils.py",
+ "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb",
+ "$dir_pw_third_party_nanopb/generator/nanopb_generator.py2",
+ "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb-py2",
+ "$dir_pw_third_party_nanopb/generator/protoc",
+ "$dir_pw_third_party_nanopb/generator/protoc-gen-nanopb.bat",
+ "$dir_pw_third_party_nanopb/generator/protoc.bat",
+ _protoc_binary_path,
+ ]
action = {
- args = [ rebase_path(dir_pw_third_party_nanopb, root_build_dir) ]
+ args = [
+ "--nanopb-root=" +
+ rebase_path(dir_pw_third_party_nanopb, root_build_dir),
+ "--protoc-binary=" + pw_protobuf_compiler_PROTOC_BINARY,
+ ]
+ if (pw_third_party_nanopb_AGGRESSIVE_NANOPB_PB2_REGEN) {
+ args += [ "--aggressive-regen" ]
+ }
+
+ # Nanopb writes a file to:
+ # $dir_pw_third_party_nanopb/generator/proto/nanopb_pb2.py
+ # Since this is in the source tree, we can't track it as an output. 😒
+ # Use a stamp instead.
stamp = true
}
}
diff --git a/third_party/nanopb/CMakeLists.txt b/third_party/nanopb/CMakeLists.txt
index c8461a1d0..b3868f800 100644
--- a/third_party/nanopb/CMakeLists.txt
+++ b/third_party/nanopb/CMakeLists.txt
@@ -50,7 +50,32 @@ pw_proto_library(pw_third_party.nanopb.proto
# Generates nanopb_pb2.py, which is needed to compile protobufs with Nanopb.
add_custom_command(
COMMAND
- python3 "${CMAKE_CURRENT_LIST_DIR}/generate_nanopb_proto.py" "${dir_pw_third_party_nanopb}"
+ python3
+ "${CMAKE_CURRENT_LIST_DIR}/generate_nanopb_proto.py"
+ --nanopb-root "${dir_pw_third_party_nanopb}"
+ --protoc-binary "$ENV{PW_PIGWEED_CIPD_INSTALL_DIR}/bin/protoc"
+ DEPENDS
+ "${CMAKE_CURRENT_LIST_DIR}/generate_nanopb_proto.py"
+ "${dir_pw_third_party_nanopb}/pb.h"
+ "${dir_pw_third_party_nanopb}/pb_common.h"
+ "${dir_pw_third_party_nanopb}/pb_decode.h"
+ "${dir_pw_third_party_nanopb}/pb_encode.h"
+ "${dir_pw_third_party_nanopb}/pb_common.c"
+ "${dir_pw_third_party_nanopb}/pb_decode.c"
+ "${dir_pw_third_party_nanopb}/pb_encode.c"
+ "${dir_pw_third_party_nanopb}/generator/nanopb_generator.py"
+ "${dir_pw_third_party_nanopb}/generator/proto/google/protobuf"
+ "${dir_pw_third_party_nanopb}/generator/proto/google/protobuf/descriptor.proto"
+ "${dir_pw_third_party_nanopb}/generator/proto/__init__.py"
+ "${dir_pw_third_party_nanopb}/generator/proto/nanopb.proto"
+ "${dir_pw_third_party_nanopb}/generator/proto/_utils.py"
+ "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb"
+ "${dir_pw_third_party_nanopb}/generator/nanopb_generator.py2"
+ "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb-py2"
+ "${dir_pw_third_party_nanopb}/generator/protoc"
+ "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb.bat"
+ "${dir_pw_third_party_nanopb}/generator/protoc.bat"
+ "$ENV{PW_PIGWEED_CIPD_INSTALL_DIR}/bin/protoc"
OUTPUT
"${dir_pw_third_party_nanopb}/generator/proto/nanopb_pb2.py"
)
diff --git a/third_party/nanopb/generate_nanopb_proto.py b/third_party/nanopb/generate_nanopb_proto.py
index 2934d0635..fc83df024 100644
--- a/third_party/nanopb/generate_nanopb_proto.py
+++ b/third_party/nanopb/generate_nanopb_proto.py
@@ -27,14 +27,28 @@ that nanopb_pb2.py is guaranteed to exist before they need it.
import argparse
import importlib.util
from pathlib import Path
+import os
import sys
-def generate_nanopb_proto(root: Path) -> None:
- sys.path.append(str(root / 'generator'))
+def generate_nanopb_proto(
+ nanopb_root: Path, protoc_binary: Path, aggressive_regen: bool
+) -> None:
+ generated_nanopb_pb2 = nanopb_root / 'generator' / 'proto' / 'nanopb_pb2.py'
+
+ # If protoc was updated, ensure the file is regenerated.
+ if generated_nanopb_pb2.is_file():
+ if (
+ aggressive_regen
+ or protoc_binary.stat().st_mtime
+ > generated_nanopb_pb2.stat().st_mtime
+ ):
+ os.remove(generated_nanopb_pb2)
+
+ sys.path.append(str(nanopb_root / 'generator'))
spec = importlib.util.spec_from_file_location(
- 'proto', root / 'generator' / 'proto' / '__init__.py'
+ 'proto', nanopb_root / 'generator' / 'proto' / '__init__.py'
)
assert spec is not None
proto_module = importlib.util.module_from_spec(spec)
@@ -43,7 +57,26 @@ def generate_nanopb_proto(root: Path) -> None:
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('root', type=Path, help='Nanopb root')
+ parser.add_argument(
+ '--nanopb-root',
+ type=Path,
+ help='Nanopb root',
+ )
+ parser.add_argument(
+ '--protoc-binary',
+ type=Path,
+ help='Protoc binary path',
+ )
+ parser.add_argument(
+ '--aggressive-regen',
+ action="store_true",
+ default=False,
+ help=(
+ 'If true, always regenerates nanopb_pb2.py when this script is '
+ 'run. If this is false, the file is only regenerated when the '
+ 'protoc binary is updated'
+ ),
+ )
return parser.parse_args()
diff --git a/third_party/nanopb/nanopb.gni b/third_party/nanopb/nanopb.gni
index 110debab8..69dee4818 100644
--- a/third_party/nanopb/nanopb.gni
+++ b/third_party/nanopb/nanopb.gni
@@ -25,4 +25,13 @@ declare_args() {
# 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_third_party_nanopb_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
+
+ # Regenerates `$dir_pw_third_party_nanopb/generator/proto/nanopb_pb2.py`
+ # whenever the `generate_nanopb_proto` action is run. If this is set to false,
+ # the file will only be regenerated if `protoc` is newer than the generated
+ # `nanopb_pb2.py`.
+ #
+ # Aggressive regeneration is NOT safe if this build will run in parallel with
+ # other build systems that try to read `nanopb_pb2.py`.
+ pw_third_party_nanopb_AGGRESSIVE_NANOPB_PB2_REGEN = true
}
diff --git a/third_party/stm32cube/BUILD.bazel b/third_party/stm32cube/BUILD.bazel
index 81a3ff78c..94e3537f4 100644
--- a/third_party/stm32cube/BUILD.bazel
+++ b/third_party/stm32cube/BUILD.bazel
@@ -21,10 +21,24 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"])
+# NOTE: To depend on this target, you must set the STM32CUBE_HEADER
+# preprocessor variable, perhaps using copts associated with the target
+# platform. See the module documentation for details.
pw_cc_library(
name = "stm32cube",
hdrs = [
"public/stm32cube/init.h",
"public/stm32cube/stm32cube.h",
],
+ includes = ["public"],
+ deps = [
+ ":hal_driver",
+ ],
+)
+
+# This label_flag introduces a layer of indirection useful when building a
+# project that requires more than one STM32Cube MCU Package.
+label_flag(
+ name = "hal_driver",
+ build_setting_default = "@hal_driver",
)
diff --git a/third_party/stm32cube/BUILD.gn b/third_party/stm32cube/BUILD.gn
index da8c1373e..cdfde7393 100644
--- a/third_party/stm32cube/BUILD.gn
+++ b/third_party/stm32cube/BUILD.gn
@@ -117,7 +117,7 @@ if (dir_pw_third_party_stm32cube == "") {
inputs = [ "$dir_pw_third_party_stm32cube/hal_driver/Inc/${files.family}_hal_conf_template.h" ]
}
- config("flags") {
+ config("header_flags") {
cflags = [ "-Wno-unused-parameter" ]
cflags_c = [
"-Wno-redundant-decls",
@@ -126,6 +126,8 @@ if (dir_pw_third_party_stm32cube == "") {
"-Wno-implicit-function-declaration",
"-Wno-switch-enum",
]
+
+ # TODO: b/301262374 - Provide a better way to detect the compiler type.
if (get_path_info(pw_toolchain_SCOPE.cc, "file") == "clang") {
cflags += [ "-Wno-deprecated-volatile" ]
cflags_c += [ "-Wno-parentheses-equality" ]
@@ -146,6 +148,14 @@ if (dir_pw_third_party_stm32cube == "") {
visibility = [ ":*" ]
}
+ config("sources_flags") {
+ if (get_path_info(pw_toolchain_SCOPE.cc, "file") == "clang") {
+ cflags_c = [ "-Wno-unused-but-set-variable" ]
+ }
+
+ visibility = [ ":*" ]
+ }
+
config("public_include_paths") {
include_dirs = files.include_dirs
include_dirs += [ "public" ]
@@ -156,7 +166,7 @@ if (dir_pw_third_party_stm32cube == "") {
# this. If you just want to depend on the hal, depend on stm32cube directly.
pw_source_set("stm32cube_headers") {
public_configs = [
- ":flags",
+ ":header_flags",
":public_include_paths",
]
public = [
@@ -172,6 +182,7 @@ if (dir_pw_third_party_stm32cube == "") {
}
pw_source_set("stm32cube") {
+ configs = [ ":sources_flags" ]
public_deps = [ ":stm32cube_headers" ]
sources = files.sources
deps = [
diff --git a/pw_trace_tokenized/py/setup.cfg b/third_party/stm32cube/cmsis_core.BUILD.bazel
index 98038d397..083434f80 100644
--- a/pw_trace_tokenized/py/setup.cfg
+++ b/third_party/stm32cube/cmsis_core.BUILD.bazel
@@ -1,4 +1,4 @@
-# Copyright 2021 The Pigweed Authors
+# Copyright 2023 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -11,19 +11,20 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-[metadata]
-name = pw_trace_tokenized
-version = 0.0.1
-author = Pigweed Authors
-author_email = pigweed-developers@googlegroups.com
-description = pw_trace backend to tokenize trace events
-[options]
-packages = find:
-zip_safe = False
-install_requires =
- pyserial>=3.5,<4.0
- types-pyserial>=3.5,<4.0
+# BUILD.bazel file for cmsis_core.
-[options.package_data]
-pw_trace_tokenized = py.typed
+# buildifier: disable=module-docstring
+cc_library(
+ name = "cmsis_core",
+ hdrs = glob(
+ [
+ "Include/*.h",
+ "Include/DSP/Include/*.h",
+ ],
+ ),
+ includes = [
+ "Include",
+ "Include/DSP/Include",
+ ],
+)
diff --git a/third_party/stm32cube/cmsis_device.BUILD.bazel b/third_party/stm32cube/cmsis_device.BUILD.bazel
new file mode 100644
index 000000000..9911787cf
--- /dev/null
+++ b/third_party/stm32cube/cmsis_device.BUILD.bazel
@@ -0,0 +1,46 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# BUILD.bazel file for cmsis_device_xxx.
+
+# buildifier: disable=module-docstring
+# Should point to the corresponding cmsis_core library. Hidden behind a label
+# flag so that it can be overriden in projects that build for more than one
+# family of STM processors.
+label_flag(
+ name = "cmsis_core",
+ build_setting_default = "@cmsis_core",
+)
+
+cc_library(
+ name = "default_cmsis_init",
+ srcs = glob(["Source/Templates/system_*.c"]),
+ deps = [":cmsis_device"],
+)
+
+cc_library(
+ name = "cmsis_device",
+ hdrs = glob(
+ [
+ "Include/*.h",
+ ],
+ ),
+ includes = ["Include"],
+ defines = [
+ "__ARMCC_VERSION=0",
+ ],
+ deps = [
+ ":cmsis_core",
+ ],
+)
diff --git a/third_party/stm32cube/stm32_hal_driver.BUILD.bazel b/third_party/stm32cube/stm32_hal_driver.BUILD.bazel
new file mode 100644
index 000000000..8bceedfce
--- /dev/null
+++ b/third_party/stm32cube/stm32_hal_driver.BUILD.bazel
@@ -0,0 +1,117 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# BUILD.bazel file for stm32xxx_hal_driver.
+
+# buildifier: disable=module-docstring
+# Must point to a cc_library exposing a header named stm32f4xx_hal_conf.h (or
+# similar for other families) that contains the HAL configuration
+label_flag(
+ name = "hal_config",
+ build_setting_default = ":unspecified",
+)
+
+# May point to a non-default implementation of timebase.
+label_flag(
+ name = "timebase",
+ build_setting_default = ":default_timebase",
+)
+
+# Should point to the corresponding cmsis_device library. Hidden behind a label
+# flag so that it can be overriden in projects that build for more than one
+# family of STM processors.
+label_flag(
+ name = "cmsis_device",
+ build_setting_default = "@cmsis_device",
+)
+
+label_flag(
+ name = "cmsis_init",
+ build_setting_default = "@cmsis_device//:default_cmsis_init",
+)
+
+# Special target used as a default value of the hal_config label_flag. It's not
+# compatible with any platform: To use Pigweed's STM32Cube integration, you
+# must provide a hal_config.
+cc_library(
+ name = "unspecified",
+ target_compatible_with = ["@platforms//:incompatible"],
+)
+
+cc_library(
+ name = "default_timebase",
+ srcs = glob(["Src/*_hal_timebase_tim_template.c"]),
+ deps = [":hal_driver_without_timebase"],
+)
+
+_DISABLED_WARNINGS = [
+ "-Wno-unused-parameter",
+ "-Wno-redundant-decls",
+ "-Wno-sign-compare",
+ "-Wno-undef",
+ "-Wno-implicit-function-declaration",
+ "-Wno-switch-enum",
+]
+
+cc_library(
+ name = "hal_driver",
+ deps = [
+ ":hal_driver_without_timebase",
+ ":timebase",
+ ],
+ copts = _DISABLED_WARNINGS,
+)
+
+cc_library(
+ name = "hal_driver_without_timebase",
+ srcs = glob(
+ [
+ "Src/*.c",
+ "Src/Legacy/*.c",
+ ],
+ exclude = ["Src/*_template.c"],
+ ),
+ deps = [
+ ":cmsis_device",
+ ":cmsis_init",
+ ":hal_headers",
+ ],
+ copts = _DISABLED_WARNINGS,
+)
+
+cc_library(
+ name = "hal_headers",
+ hdrs = glob(
+ [
+ "Inc/*.h",
+ "Inc/Legacy/*.h",
+ ],
+ exclude = [
+ # Excluded because implementers may want to override this template.
+ "Inc/*_hal_conf_template.h",
+ ],
+ ),
+ includes = [
+ "Inc",
+ "Inc/Legacy",
+ ],
+ deps = [
+ ":cmsis_device",
+ ":hal_config",
+ ],
+ defines = [
+ "USE_HAL_DRIVER",
+ ],
+ copts = _DISABLED_WARNINGS,
+)