|  | # Copyright 2019 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 a facade. | 
|  | # | 
|  | # A Pigweed facade is an API layer that has a single implementation it must | 
|  | # link against. Typically this will be done by pointing a build arg like | 
|  | # `pw_[module]_BACKEND` at a backend implementation for that module. | 
|  | # | 
|  | # pw_facade creates two targets: | 
|  | # | 
|  | #   $target_name: The public-facing pw_source_set that provides the API and | 
|  | #     implementation (backend). Users of the facade should depend on this. | 
|  | #   $target_name.facade: A private source_set that provides ONLY the API. ONLY | 
|  | #     backends should depend on this. | 
|  | # | 
|  | # If the target name matches the directory name (e.g. //foo:foo), a ":facade" | 
|  | # alias of the facade target (e.g. //foo:facade) is also provided. This avoids | 
|  | # the need to repeat the directory name, for consistency with the main target. | 
|  | # | 
|  | # The facade's headers are split out into the *.facade target to avoid circular | 
|  | # dependencies. Here's a concrete example to illustrate why this is needed: | 
|  | # | 
|  | #   foo_BACKEND = "//foo:foo_backend_bar" | 
|  | # | 
|  | #   pw_facade("foo") { | 
|  | #     backend = foo_BACKEND | 
|  | #     public = [ "foo.h" ] | 
|  | #     sources = [ "foo.cc" ] | 
|  | #   } | 
|  | # | 
|  | #   pw_source_set("foo_backend_bar") { | 
|  | #     deps = [ ":foo.facade" ] | 
|  | #     sources = [ "bar.cc" ] | 
|  | #   } | 
|  | # | 
|  | # This creates the following dependency graph: | 
|  | # | 
|  | #   foo.facade  <-. | 
|  | #    ^             \ | 
|  | #    |              \ | 
|  | #    |               \ | 
|  | #   foo  ---------->  foo_backend_bar | 
|  | # | 
|  | # This allows foo_backend_bar to include "foo.h". If you tried to directly | 
|  | # depend on `foo` from `foo_backend_bar`, you'd get a dependency cycle error in | 
|  | # GN. | 
|  | # | 
|  | # Accepts the standard pw_source_set args with the following additions: | 
|  | # | 
|  | # Args: | 
|  | #  backend: (required) The dependency that implements this facade (a GN | 
|  | #    variable) | 
|  | #  public: (required) The headers exposed by this facade. A facade acts as a | 
|  | #    tool to break dependency cycles that come from the backend trying to | 
|  | #    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), | 
|  | "pw_facade requires a reference to a backend variable for the facade") | 
|  | assert(defined(invoker.public), | 
|  | "If your facade does not explicitly expose an API that a backend " + | 
|  | "must depend on, you can just directly depend on the build arg " + | 
|  | "that the `backend` template argument would have been set to.") | 
|  |  | 
|  | _facade_name = "$target_name.facade" | 
|  |  | 
|  | if (get_path_info(get_label_info(":$target_name", "dir"), "name") == | 
|  | get_label_info(":$target_name", "name")) { | 
|  | group("facade") { | 
|  | public_deps = [ ":$_facade_name" ] | 
|  | } | 
|  | } | 
|  |  | 
|  | _facade_vars = [ | 
|  | # allow_circular_includes_from should very rarely be used, but when it is, | 
|  | # it only applies to headers, so should be in the .facade target. | 
|  | "allow_circular_includes_from", | 
|  | "public_configs", | 
|  | "public_deps", | 
|  | "public", | 
|  | ] | 
|  | pw_source_set(_facade_name) { | 
|  | forward_variables_from(invoker, _facade_vars, [ "require_link_deps" ]) | 
|  | } | 
|  |  | 
|  | if (invoker.backend == "") { | 
|  | # Try to guess the name of the facade's backend variable. | 
|  | _dir = get_path_info(get_label_info(":$target_name", "dir"), "name") | 
|  | if (target_name == _dir) { | 
|  | _varname = target_name + "_BACKEND" | 
|  | } else { | 
|  | # There is no way to capitalize this string in GN, so use <FACADE_NAME> | 
|  | # instead of the lowercase target name. | 
|  | _varname = _dir + "_<FACADE_NAME>_BACKEND" | 
|  | } | 
|  |  | 
|  | # If backend is not set to anything, create a script that emits an error. | 
|  | # This will be added as a data dependency to the actual target, so that | 
|  | # attempting to build the facade without a backend fails with a relevant | 
|  | # error message. | 
|  | pw_error(target_name + ".NO_BACKEND_SET") { | 
|  | _label = get_label_info(":${invoker.target_name}", "label_no_toolchain") | 
|  | message_lines = [ | 
|  | "Attempted to build the $_label facade with no backend.", | 
|  | "", | 
|  | "If you are using this facade, ensure you have configured a backend ", | 
|  | "properly. The build arg for the facade must be set to a valid ", | 
|  | "backend in the toolchain. For example, you may need to add a line ", | 
|  | "like the following to the toolchain's .gni file:", | 
|  | "", | 
|  | "  $_varname = \"//path/to/the:backend\"", | 
|  | "", | 
|  | "If you are NOT using this facade, this error may have been triggered ", | 
|  | "by trying to build all targets.", | 
|  | ] | 
|  | } | 
|  | } | 
|  |  | 
|  | # Create a target that defines the main facade library. Always emit this | 
|  | # target, even if the backend isn't defined, so that the dependency graph is | 
|  | # correctly expressed for gn check. | 
|  | pw_source_set(target_name) { | 
|  | # The main library contains everything else specified in the template. | 
|  | _ignore_vars = _facade_vars + [ | 
|  | "backend", | 
|  | "require_link_deps", | 
|  | ] | 
|  | forward_variables_from(invoker, "*", _ignore_vars) | 
|  |  | 
|  | public_deps = [ ":$_facade_name" ] | 
|  |  | 
|  | # If the backend is set, inject it as a dependency. | 
|  | if (invoker.backend != "") { | 
|  | public_deps += [ invoker.backend ] | 
|  | } else { | 
|  | # If the backend is not set, depend on the *.NO_BACKEND_SET target. | 
|  | 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" ]) | 
|  | } | 
|  | } |