aboutsummaryrefslogtreecommitdiff
path: root/pw_build/facade.gni
blob: d95764670fbc2d9f4829c08b6c83fae57ab1954e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 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" ]
    }
  }
}