1 # Copyright 2019 The Pigweed Authors
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
7 # https://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
15 import("//build_overrides/pigweed.gni")
17 import("$dir_pw_build/python_action.gni")
18 import("$dir_pw_build/target_types.gni")
22 # A Pigweed facade is an API layer that has a single implementation it must
23 # link against. Typically this will be done by pointing a build arg like
24 # `pw_[module]_BACKEND` at a backend implementation for that module.
26 # pw_facade creates two targets:
28 # $target_name: The public-facing pw_source_set that provides the API and
29 # implementation (backend). Users of the facade should depend on this.
30 # $target_name.facade: A private source_set that provides ONLY the API. ONLY
31 # backends should depend on this.
33 # If the target name matches the directory name (e.g. //foo:foo), a ":facade"
34 # alias of the facade target (e.g. //foo:facade) is also provided. This avoids
35 # the need to repeat the directory name, for consistency with the main target.
37 # The facade's headers are split out into the *.facade target to avoid circular
38 # dependencies. Here's a concrete example to illustrate why this is needed:
40 # foo_BACKEND = "//foo:foo_backend_bar"
43 # backend = foo_BACKEND
44 # public = [ "foo.h" ]
45 # sources = [ "foo.cc" ]
48 # pw_source_set("foo_backend_bar") {
49 # deps = [ ":foo.facade" ]
50 # sources = [ "bar.cc" ]
53 # This creates the following dependency graph:
59 # foo ----------> foo_backend_bar
61 # This allows foo_backend_bar to include "foo.h". If you tried to directly
62 # depend on `foo` from `foo_backend_bar`, you'd get a dependency cycle error in
65 # Accepts the standard pw_source_set args with the following additions:
68 # backend: (required) The dependency that implements this facade (a GN
70 # public: (required) The headers exposed by this facade. A facade acts as a
71 # tool to break dependency cycles that come from the backend trying to
72 # include headers from the facade itself. If the facade doesn't expose any
73 # headers, it's basically the same as just depending directly on the build
74 # arg that `backend` is set to.
76 template("pw_facade") {
77 assert(defined(invoker.backend),
78 "pw_facade requires a reference to a backend variable for the facade")
79 assert(defined(invoker.public),
80 "If your facade does not explicitly expose an API that a backend " +
81 "must depend on, you can just directly depend on the build arg " +
82 "that the `backend` template argument would have been set to.")
84 _facade_name = "$target_name.facade"
86 if (get_path_info(get_label_info(":$target_name", "dir"), "name") ==
87 get_label_info(":$target_name", "name")) {
89 public_deps = [ ":$_facade_name" ]
98 pw_source_set(_facade_name) {
99 forward_variables_from(invoker, _facade_vars)
102 if (invoker.backend == "") {
103 # If backend is not set to anything, create a script that emits an error.
104 # This will be added as a data dependency to the actual target, so that
105 # attempting to build the facade without a backend fails with a relevant
107 _main_target_name = target_name
109 pw_python_action(_main_target_name + ".NO_BACKEND_SET") {
111 script = "$dir_pw_build/py/pw_build/null_backend.py"
112 args = [ _main_target_name ]
113 not_needed(invoker, "*")
117 # Create a target that defines the main facade library. Always emit this
118 # target, even if the backend isn't defined, so that the dependency graph is
119 # correctly expressed for gn check.
120 pw_source_set(target_name) {
121 # The main library contains everything else specified in the template.
122 _ignore_vars = [ "backend" ] + _facade_vars
123 forward_variables_from(invoker, "*", _ignore_vars)
125 public_deps = [ ":$_facade_name" ]
127 # If the backend is set, inject it as a dependency.
128 if (invoker.backend != "") {
129 public_deps += [ invoker.backend ]
131 # If the backend is not set, depend on the *.NO_BACKEND_SET target.
132 public_deps += [ ":$_main_target_name" + ".NO_BACKEND_SET" ]