pw_build: pw_facade require_link_deps arg
- Make the pw_build_LINK_DEPS check in pw_assert a generic feature in
pw_facade.
- Use "impl" instead of "deps" for the pw_assert dependencies and
restructure the impl / backend split.
Change-Id: I75c0f7e67b3b97bfe333760897223ab4601649c0
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/43980
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
diff --git a/pw_assert/BUILD.gn b/pw_assert/BUILD.gn
index 0362da6..7196831 100644
--- a/pw_assert/BUILD.gn
+++ b/pw_assert/BUILD.gn
@@ -60,6 +60,9 @@
"public/pw_assert/short.h",
]
public_deps = [ dir_pw_preprocessor ]
+
+ # TODO(pwbug/372): Update projects to properly list pw_assert:impl.
+ # require_link_deps = [ ":impl" ]
}
# Provide "pw_assert/assert.h" in its own source set, so it can be used without
@@ -87,39 +90,18 @@
# circular dependencies. This target collects dependencies from the backend that
# cannot be used because they would cause circular deps.
#
-# This group ("$dir_pw_assert:deps") must listed in pw_build_LINK_DEPS if
+# This group ("$dir_pw_assert:impl") must listed in pw_build_LINK_DEPS if
# pw_assert_BACKEND is set.
#
-# pw_assert backends must provide their own "deps" group that collects their
-# actual dependencies. The backend "deps" group may be empty.
-group("deps") {
+# pw_assert backends must provide their own "impl" target that collects their
+# actual dependencies. The backend "impl" group may be empty if everything can
+# go directly in the backend target without causing circular dependencies.
+group("impl") {
public_deps = []
if (pw_assert_BACKEND != "") {
- public_deps += [ get_label_info(pw_assert_BACKEND, "dir") + ":deps" ]
-
- # Make sure this target is listed in pw_build_LINK_DEPS. This
- # ensures these dependencies are available during linking, even if nothing
- # else in the build depends on them.
- _deps_label = get_label_info(":$target_name", "label_no_toolchain")
- _deps_is_in_link_dependencies = false
-
- foreach(label, pw_build_LINK_DEPS) {
- if (get_label_info(label, "label_no_toolchain") == _deps_label) {
- _deps_is_in_link_dependencies = true
- }
- }
-
- # TODO(pwbug/372): Update projects with pw_assert to link the :deps target.
- _disable_this_check_for_now = true
- not_needed([
- "_deps_is_in_link_dependencies",
- "_deps_label",
- ])
-
- assert(_disable_this_check_for_now || _deps_is_in_link_dependencies,
- "\$dir_pw_assert:$target_name must be listed in " +
- "pw_build_LINK_DEPS when pw_assert_BACKEND is set")
+ public_deps +=
+ [ get_label_info(pw_assert_BACKEND, "label_no_toolchain") + ".impl" ]
}
}
diff --git a/pw_assert/docs.rst b/pw_assert/docs.rst
index cd36d07..9a03d64 100644
--- a/pw_assert/docs.rst
+++ b/pw_assert/docs.rst
@@ -481,17 +481,21 @@
this, assert backends may avoid declaring explicit dependencies, instead relying
on include paths to access header files.
-In GN, the ``pw_assert`` backend's true dependencies are made available through
-the ``$dir_pw_assert:deps`` group. When ``pw_assert_BACKEND`` is set,
-``$dir_pw_assert:deps`` must be listed in the ``pw_build_LINK_DEPS`` variable.
-See :ref:`module-pw_build-link-deps`.
+In GN, the ``pw_assert`` backend's full implementation with true dependencies is
+made available through the ``$dir_pw_assert:impl`` group. When
+``pw_assert_BACKEND`` is set, ``$dir_pw_assert:impl`` must be listed in the
+``pw_build_LINK_DEPS`` variable. See :ref:`module-pw_build-link-deps`.
-If necessary, ``pw_assert`` backends can access dependencies from include paths
-rather than GN ``deps``. In this case, the may disable GN header checking with
-``check_includes = false``. The true build dependencies must be listed in a
-``deps`` group, which the ``pw_assert`` facade depends on. The ``deps`` group
-may be empty if the backend can use its dependencies directly without causing
-circular dependencies.
+In the ``pw_assert``, the backend's full implementation is placed in the
+``$pw_assert_BACKEND.impl`` target. ``$dir_pw_assert:impl`` depends on this
+backend target. The ``$pw_assert_BACKEND.impl`` target may be an empty group if
+the backend target can use its dependencies directly without causing circular
+dependencies.
+
+In order to break dependency cycles, the ``pw_assert_BACKEND`` target may need
+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_assert-backend_api:
@@ -582,11 +586,13 @@
Backend build targets
---------------------
-In GN, the backend must provide a ``deps`` build target in the same directory as
-the backend target. The ``deps`` target contains the backend's dependencies that
-could result in a dependency cycle. In the simplest case, it can be an empty
-group. Circular dependencies are a common problem with ``pw_assert`` because it
-is so widely used. See :ref:`module-pw_assert-circular-deps`.
+In GN, the backend must provide a ``pw_assert.impl`` build target in the same
+directory as the backend target. If the main backend target's dependencies would
+cause dependency cycles, the actual backend implementation with its full
+dependencies is placed in the ``pw_assert.impl`` target. If this is not
+necessary, ``pw_assert.impl`` can be an empty group. Circular dependencies are a
+common problem with ``pw_assert`` because it is so widely used. See
+:ref:`module-pw_assert-circular-deps`.
--------------------------
Frequently asked questions
diff --git a/pw_assert_basic/BUILD.gn b/pw_assert_basic/BUILD.gn
index c2e48d1..34125d9 100644
--- a/pw_assert_basic/BUILD.gn
+++ b/pw_assert_basic/BUILD.gn
@@ -32,7 +32,8 @@
# These assert backend deps that might cause circular dependencies, since
# pw_assert is so ubiquitous. These deps are kept separate so they can be
# depended on from elsewhere.
-group("deps") {
+# TODO(pwbug/372): Reorganize this.
+group("pw_assert_basic.impl") {
public_deps = [
dir_pw_string,
dir_pw_sys_io,
diff --git a/pw_assert_log/BUILD.gn b/pw_assert_log/BUILD.gn
index f6726b0..46ce4c8 100644
--- a/pw_assert_log/BUILD.gn
+++ b/pw_assert_log/BUILD.gn
@@ -46,9 +46,10 @@
sources = [ "assert_log.cc" ]
}
+# TODO(pwbug/372): Reorganize this.
# pw_assert_log doesn't have deps with potential circular dependencies, so
# "deps" can be empty.
-group("deps") {
+group("pw_assert_log.impl") {
}
pw_doc_group("docs") {
diff --git a/pw_build/docs.rst b/pw_build/docs.rst
index ad43bff..a5d38da 100644
--- a/pw_build/docs.rst
+++ b/pw_build/docs.rst
@@ -125,6 +125,14 @@
public = [ "public/pw_foo/foo.h" ]
}
+Low-level facades like ``pw_assert`` cannot express all of their dependencies
+due to the potential for dependency cycles. Facades with this issue may require
+backends to place their implementations in a separate build target to be listed
+in ``pw_build_LINK_DEPS`` (see :ref:`module-pw_build-link-deps`). The
+``require_link_deps`` variable in ``pw_facade`` asserts that all specified build
+targets are present in ``pw_build_LINK_DEPS`` if the facade's backend variable
+is set.
+
.. _module-pw_build-python-action:
pw_python_action
diff --git a/pw_build/facade.gni b/pw_build/facade.gni
index 4cb422e..0b188a1 100644
--- a/pw_build/facade.gni
+++ b/pw_build/facade.gni
@@ -72,6 +72,9 @@
# include headers from the facade itself. If the facade doesn't expose any
# headers, it's basically the same as just depending directly on the build
# arg that `backend` is set to.
+# require_link_deps: A list of build targets that must be included in
+# pw_build_LINK_DEPS if this facade is used. This mechanism is used to
+# avoid circular dependencies in low-level facades like pw_assert.
#
template("pw_facade") {
assert(defined(invoker.backend),
@@ -99,7 +102,7 @@
"public",
]
pw_source_set(_facade_name) {
- forward_variables_from(invoker, _facade_vars)
+ forward_variables_from(invoker, _facade_vars, [ "require_link_deps" ])
}
if (invoker.backend == "") {
@@ -140,7 +143,10 @@
# correctly expressed for gn check.
pw_source_set(target_name) {
# The main library contains everything else specified in the template.
- _ignore_vars = [ "backend" ] + _facade_vars
+ _ignore_vars = _facade_vars + [
+ "backend",
+ "require_link_deps",
+ ]
forward_variables_from(invoker, "*", _ignore_vars)
public_deps = [ ":$_facade_name" ]
@@ -153,4 +159,28 @@
public_deps += [ ":$target_name.NO_BACKEND_SET" ]
}
}
+
+ if (defined(invoker.require_link_deps) && invoker.backend != "") {
+ # Check that the specified labels are listed in pw_build_LINK_DEPS. This
+ # ensures these dependencies are available during linking, even if nothing
+ # else in the build depends on them.
+ foreach(label, invoker.require_link_deps) {
+ _required_dep = get_label_info(label, "label_no_toolchain")
+ _dep_is_in_link_dependencies = false
+
+ foreach(link_dep, pw_build_LINK_DEPS) {
+ if (get_label_info(link_dep, "label_no_toolchain") == _required_dep) {
+ _dep_is_in_link_dependencies = true
+ }
+ }
+
+ _facade_name = get_label_info(":$target_name", "label_no_toolchain")
+ assert(_dep_is_in_link_dependencies,
+ "$_required_dep must be listed in the pw_build_LINK_DEPS build " +
+ "arg when the $_facade_name facade is in use. Please update " +
+ "your toolchain configuration.")
+ }
+ } else {
+ not_needed(invoker, [ "require_link_deps" ])
+ }
}
diff --git a/targets/arduino/target_toolchains.gni b/targets/arduino/target_toolchains.gni
index 0ee8322..167c185 100644
--- a/targets/arduino/target_toolchains.gni
+++ b/targets/arduino/target_toolchains.gni
@@ -51,7 +51,7 @@
"$dir_pigweed/targets/arduino:system_rpc_server"
pw_arduino_build_INIT_BACKEND = "$dir_pigweed/targets/arduino:pre_init"
- pw_build_LINK_DEPS = [ "$dir_pw_assert:deps" ]
+ pw_build_LINK_DEPS = [ "$dir_pw_assert:impl" ]
current_cpu = "arm"
current_os = ""
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index f0fa57e..5103a5b 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -65,7 +65,7 @@
pw_thread_THREAD_BACKEND = "$dir_pw_thread_stl:thread"
pw_build_LINK_DEPS = [] # Explicit list overwrite required by GN
- pw_build_LINK_DEPS = [ "$dir_pw_assert:deps" ]
+ pw_build_LINK_DEPS = [ "$dir_pw_assert:impl" ]
# Specify builtin GN variables.
current_os = host_os
diff --git a/targets/lm3s6965evb-qemu/target_toolchains.gni b/targets/lm3s6965evb-qemu/target_toolchains.gni
index dc18b3f..3d2f120 100644
--- a/targets/lm3s6965evb-qemu/target_toolchains.gni
+++ b/targets/lm3s6965evb-qemu/target_toolchains.gni
@@ -61,7 +61,7 @@
"PW_BOOT_VECTOR_TABLE_SIZE=512",
]
- pw_build_LINK_DEPS = [ "$dir_pw_assert:deps" ]
+ pw_build_LINK_DEPS = [ "$dir_pw_assert:impl" ]
current_cpu = "arm"
current_os = ""
diff --git a/targets/stm32f429i-disc1/target_toolchains.gni b/targets/stm32f429i-disc1/target_toolchains.gni
index 6c690b8..6acf09c 100644
--- a/targets/stm32f429i-disc1/target_toolchains.gni
+++ b/targets/stm32f429i-disc1/target_toolchains.gni
@@ -73,7 +73,7 @@
"PW_BOOT_VECTOR_TABLE_SIZE=512",
]
- pw_build_LINK_DEPS = [ "$dir_pw_assert:deps" ]
+ pw_build_LINK_DEPS = [ "$dir_pw_assert:impl" ]
current_cpu = "arm"
current_os = ""