# 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/python_action.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. # # To avoid circular dependencies, pw_facade creates two targets: # # - $target_name: the public-facing pw_source_set # - $target_name.facade: target used by the backend to avoid circular # dependencies # # 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. # # Example facade: # # # Creates ":module_name" and ":module_name.facade" GN targets. # pw_facade("module_name") { # backend = dir_module_name_backend # public_deps = [ # ":module_api_layer" # ] # } # # Accepts the standard pw_source_set args with the following additions: # # - backend: the dependency that implements this facade (a GN variable) # template("pw_facade") { assert(defined(invoker.backend), "pw_facade requires a reference to a backend variable for the facade") _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" ] } } # For backwards compatibility, provide a _facade version of the name. group(target_name + "_facade") { public_deps = [ ":$_facade_name" ] } # A facade's headers are split into a separate target to avoid a circular # dependency between the facade and the backend. # # For example, the following targets: # # 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 = [ ":facade" ] # sources = [ "bar.cc" ] # } # # Create the following dependency graph: # # facade <-. # ^ \ # | \ # | \ # foo ------> foo_backend_bar # _facade_vars = [ "public_configs", "public_deps", "public", ] pw_source_set(_facade_name) { forward_variables_from(invoker, _facade_vars) } if (invoker.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. _main_target_name = target_name pw_python_action(_main_target_name + ".NO_BACKEND_SET") { stamp = true script = "$dir_pw_build/py/pw_build/null_backend.py" args = [ _main_target_name ] not_needed(invoker, "*") } } # 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 = [ "backend" ] + _facade_vars 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 += [ ":$_main_target_name" + ".NO_BACKEND_SET" ] } } }