aboutsummaryrefslogtreecommitdiff
path: root/pw_build/facade.gni
blob: a5a51593ad2340cb87189451fd0a4b7209b4cf6a (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
# 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.
# gn-format disable
import("//build_overrides/pigweed.gni")

import("$dir_pw_build/python_script.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.
#
# Example facade:
#
#   pw_facade("module_name") {
#     backend = dir_module_name_backend
#     public_deps = [
#       ":module_api_layer"
#     ]
#   }
#
# Args:
#  - backend: the dependency that implements this facade
#  - facade_name: (optional) The name to use for the facade target on which the
#        backend depends. Only required when a module defines multiple facades.
#        Defaults to "facade".
#
template("pw_facade") {
  assert(defined(invoker.backend),
         "pw_facade requires a reference to a backend variable for the facade")

  if (defined(invoker.facade_name)) {
    _facade_name = invoker.facade_name
  } else {
    _facade_name = "facade"
  }

  # 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_script(_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" ]
    }
  }
}