xref: /aosp_15_r20/external/executorch/shim/xplat/executorch/build/runtime_wrapper.bzl (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1"""Common macros to build Executorch runtime targets in both fbcode and xplat.
2
3For directories that contain code which must be built for both fbcode and xplat,
4the expected pattern is to create:
5
6- A `targets.bzl` file that uses the macros in this file
7  (`runtime_wrapper.bzl`) to define a function named `define_common_targets()`.
8  This function should define all of the build targets in terms of rules in the
9  `runtime` struct below.
10
11  The `targets.bzl` file must load this file from xplat (not fbcode), like
12    load("@fbsource//xplat/executorch/build:runtime_wrapper.bzl", "runtime")
13  to avoid a problematic dependency from xplat -> fbcode when building in xplat.
14
15- A TARGETS file and a BUILD file which both contain:
16
17    load(":targets.bzl", "define_common_targets")
18    define_common_targets()
19
20If a given directory also needs to define a fbcode-only build target as well
21as the common targets, it should define that rule directly in the TARGETS file
22below the call to `define_common_targets()`. Similar for xplat-only build
23targets and BUCK files.
24
25Note that fbcode-only directories do not need to use these wrappers, and can
26use TARGETS files normally. Same for xplat-only directories and BUCK files.
27"""
28
29load(":env_interface.bzl", "env")
30load(":selects.bzl", "selects")
31
32def is_xplat():
33    return env.is_xplat()
34
35def struct_to_json(x):
36    return env.struct_to_json(struct(**x))
37
38def get_default_executorch_platforms():
39    return env.default_platforms
40
41def _patch_executorch_references(targets, use_static_deps = False):
42    """Patches up references to "//executorch/..." in lists of build targets.
43
44    References to targets under `executorch` (in
45    deps/exported_deps/visibility/etc.) must be specified as `//executorch/...`
46    in the targets.bzl file. When building for xplat, rewrite them as
47    `//xplat/executorch/...`.
48
49    Args:
50        targets: A list of build target strings to fix up. Not modified in
51            place.
52        use_static_deps: Whether this target should depend on static executorch
53            targets when building in xplat.
54
55    Returns:
56        The possibly-different list of targets.
57    """
58    if not targets:
59        return targets
60    out_targets = []
61    for target in targets:
62        if target.startswith("//xplat/executorch"):
63            fail("References to executorch build targets must use " +
64                 "`//executorch`, not `//xplat/executorch`")
65
66        # TODO(larryliu0820): it's confusing that we only apply "static" patch to target_needs_patch. We need to clean this up.
67        if use_static_deps and env.target_needs_patch(target) and not target.endswith("..."):
68            target = target + "_static"
69
70        # Change the name of target reference to satisfy the build environment.
71        # This needs to happen after any other target_needs_patch calls, because
72        # it can modify the prefix of the target.
73        if env.target_needs_patch(target):
74            target = env.patch_target_for_env(target)
75
76        out_targets.append(target)
77    return out_targets
78
79def _patch_build_mode_flags(kwargs):
80    """Applies modifications to the `compiler_flags` kwargs based on build mode.
81
82    Args:
83        kwargs: The `kwargs` parameter from a rule.
84
85    Returns:
86        The possibly-modified `kwargs` parameter for chaining.
87    """
88    build_mode = native.read_config("fbcode", "build_mode_test_label", "")
89    flags = []
90
91    # Base build modes.
92    if build_mode.startswith("dev"):
93        flags.append("-D__ET_BUILD_MODE_DEV=1")
94    elif build_mode.startswith("opt"):
95        flags.append("-D__ET_BUILD_MODE_OPT=1")
96    elif build_mode.startswith("dbgo"):
97        flags.append("-D__ET_BUILD_MODE_DBGO=1")
98    elif build_mode.startswith("dbg"):
99        flags.append("-D__ET_BUILD_MODE_DBG=1")
100
101    # Build mode extensions.
102    if "-cov" in build_mode:
103        flags.append("-D__ET_BUILD_MODE_COV=1")
104    elif "-asan" in build_mode:
105        flags.append("-D__ET_BUILD_MODE_ASAN=1")
106    elif "-tsan" in build_mode:
107        flags.append("-D__ET_BUILD_MODE_TSAN=1")
108    elif "-ubsan" in build_mode:
109        flags.append("-D__ET_BUILD_MODE_UBSAN=1")
110    elif "-lto" in build_mode:
111        flags.append("-D__ET_BUILD_MODE_LTO=1")
112
113    if "compiler_flags" not in kwargs:
114        kwargs["compiler_flags"] = []
115
116    # kwargs["compiler_flags"].extend(flags) or kwargs["compiler_flags"] += would
117    # fail if kwargs["compiler_flags"] is Immutable (ex: the default argument of
118    # a Buck macro)
119    kwargs["compiler_flags"] = kwargs["compiler_flags"] + flags
120
121    return kwargs
122
123def _patch_test_compiler_flags(kwargs):
124    if "compiler_flags" not in kwargs:
125        kwargs["compiler_flags"] = []
126
127    # Required globally by all c++ tests.
128    kwargs["compiler_flags"].extend([
129        "-std=c++17",
130    ])
131
132    # Relaxing some constraints for tests
133    kwargs["compiler_flags"].extend([
134        "-Wno-missing-prototypes",
135        "-Wno-unused-variable",
136        "-Wno-error",
137    ])
138    return kwargs
139
140def _external_dep_location(name):
141    """Returns the target path for the specified external_dep name.
142
143    Only for use in genrule `$(location )`-like strings; e.g.,
144
145        "cmd $(location " + runtime.external_dep_location("functions-yaml") + ")"
146
147    Can only be used with external_deps names that map to exactly one target.
148    """
149    targets = env.resolve_external_dep(name)
150    if type(targets) == type(None) or len(targets) != 1:
151        fail("Could not resolve external_dep {}: saw {}".format(name, targets))
152    return targets[0]
153
154def _resolve_external_deps(kwargs):
155    """Converts `[exported_]external_deps` entries into real deps if necessary."""
156    for prefix in ("exported_", ""):
157        external_deps = kwargs.pop(prefix + "external_deps", [])
158        remaining_deps = []
159        for dep in external_deps:
160            targets = env.resolve_external_dep(dep)
161            if targets == env.EXTERNAL_DEP_FALLTHROUGH:
162                # Unhandled external_deps remain on the external_deps list of
163                # the target.
164                remaining_deps.append(dep)
165            else:
166                # Add the real targets that the external_deps entry refers to.
167                if (prefix + "deps") not in kwargs:
168                    kwargs[prefix + "deps"] = []
169
170                # Do not use extend because kwargs[prefix + "deps"] may be a select.
171                kwargs[prefix + "deps"] += targets
172        if remaining_deps:
173            kwargs[prefix + "external_deps"] = remaining_deps
174
175def _patch_kwargs_common(kwargs):
176    """Applies modifications to kwargs for all rule types.
177
178    Returns the possibly-modified `kwargs` parameter for chaining.
179    """
180    env.remove_unsupported_kwargs(kwargs)
181
182    # Be careful about dependencies on executorch targets for now, so that we
183    # don't pick up unexpected clients while things are still in flux.
184    if not kwargs.pop("_is_external_target", False):
185        for target in kwargs.get("visibility", []):
186            if not (target.startswith("//executorch") or target.startswith("@")):
187                fail("Please manage all external visibility using the " +
188                     "EXECUTORCH_CLIENTS list in " +
189                     "//executorch/build/fb/clients.bzl. " +
190                     "Found external visibility target \"{}\".".format(target))
191    else:
192        kwargs.pop("_is_external_target", None)
193
194    # Convert `[exported_]external_deps` entries into real deps if necessary.
195    _resolve_external_deps(kwargs)
196
197    # TODO(T158275165): Remove this once everyone uses external_deps
198    # Append repo-specific deps.
199    for dep_type in ("deps", "exported_deps"):
200        env.patch_deps(kwargs, dep_type)
201
202    # Patch up references to "//executorch/..." in lists of build targets,
203    # if necessary.
204    use_static_deps = kwargs.pop("use_static_deps", False)
205    for dep_type in ("deps", "exported_deps", "visibility"):
206        if kwargs.get(dep_type):
207            # deps may contain select() elements, dicts that map names to lists
208            # of targets. selects.apply() will run the provided function on all
209            # lists of targets in the provided object, but can also handle a
210            # simple list. See also
211            # https://www.internalfb.com/intern/qa/152401/what-is-a-select-in-buck
212            kwargs[dep_type] = selects.apply(
213                obj = kwargs.get(dep_type),
214                function = native.partial(_patch_executorch_references, use_static_deps = use_static_deps),
215            )
216
217    # Make all targets private by default, like in xplat.
218    if "visibility" not in kwargs:
219        kwargs["visibility"] = []
220
221    # If we see certain strings in the "visibility" list, expand them.
222    if "@EXECUTORCH_CLIENTS" in kwargs["visibility"]:
223        # See env.executorch_clients for this list.
224        kwargs["visibility"].remove("@EXECUTORCH_CLIENTS")
225        kwargs["visibility"].extend(env.executorch_clients)
226
227    return kwargs
228
229def _patch_kwargs_cxx(kwargs):
230    env.patch_platforms(kwargs)
231    env.remove_platform_specific_args(kwargs)
232    return _patch_kwargs_common(kwargs)
233
234def _cxx_library_common(*args, **kwargs):
235    _patch_kwargs_cxx(kwargs)
236    _patch_build_mode_flags(kwargs)
237
238    env.patch_platform_build_mode_flags(kwargs)
239    env.patch_headers(kwargs)
240    env.patch_pp_flags(kwargs)
241    env.patch_cxx_compiler_flags(kwargs)
242    env.patch_force_static(kwargs)
243
244    env.cxx_library(*args, **kwargs)
245
246def _cxx_library(*args, **kwargs):
247    define_static_target = kwargs.pop("define_static_target", True)
248
249    # Determine linkage for this binary based on its children.
250    kwargs["preferred_linkage"] = "any"
251    _cxx_library_common(*args, **kwargs)
252
253    # Optionally add a statically linked library target.
254    if define_static_target:
255        kwargs["name"] += "_static"
256        kwargs["preferred_linkage"] = "static"
257        kwargs["use_static_deps"] = True
258        _cxx_library_common(*args, **kwargs)
259
260def _cxx_binary_helper(*args, **kwargs):
261    _patch_kwargs_cxx(kwargs)
262    _patch_build_mode_flags(kwargs)
263    env.patch_platform_build_mode_flags(kwargs)
264    env.patch_cxx_compiler_flags(kwargs)
265
266    env.cxx_binary(*args, **kwargs)
267
268def _cxx_binary(*args, **kwargs):
269    define_static_target = kwargs.pop("define_static_target", True)
270    _cxx_binary_helper(*args, **kwargs)
271    if define_static_target:
272        kwargs["name"] += "_static"
273        kwargs["use_static_deps"] = True
274        _cxx_binary_helper(*args, **kwargs)
275
276def _cxx_test(*args, **kwargs):
277    # Inject test utils library.
278    if "deps" not in kwargs:
279        kwargs["deps"] = []
280    kwargs["deps"].append("//executorch/test/utils:utils")
281
282    _patch_kwargs_cxx(kwargs)
283    _patch_build_mode_flags(kwargs)
284    _patch_test_compiler_flags(kwargs)
285
286    env.patch_platform_build_mode_flags(kwargs)
287    env.cxx_test(*args, **kwargs)
288
289def _cxx_python_extension(*args, **kwargs):
290    _patch_kwargs_common(kwargs)
291    kwargs["srcs"] = _patch_executorch_references(kwargs["srcs"])
292    if "types" in kwargs:
293        kwargs["types"] = _patch_executorch_references(kwargs["types"])
294    env.cxx_python_extension(*args, **kwargs)
295
296def _export_file(*args, **kwargs):
297    _patch_kwargs_common(kwargs)
298    env.export_file(*args, **kwargs)
299
300def _filegroup(*args, **kwargs):
301    _patch_kwargs_common(kwargs)
302    env.filegroup(*args, **kwargs)
303
304def _command_alias(*args, **kwargs):
305    _patch_kwargs_common(kwargs)
306    env.command_alias(*args, **kwargs)
307
308def _genrule(*args, **kwargs):
309    _patch_kwargs_common(kwargs)
310    env.patch_platforms(kwargs)
311    if kwargs.get("cmd"):
312        kwargs["cmd"] = env.patch_executorch_genrule_cmd(
313            kwargs.get("cmd"),
314            kwargs.pop("macros_only", True),
315        )
316
317    # Really no difference between static and non-static in genrule,
318    # only to satisfy static build requirement. This is bad but works for now.
319    define_static_target = kwargs.pop("define_static_target", True)
320    env.genrule(*args, **kwargs)
321    if define_static_target:
322        kwargs["name"] += "_static"
323        env.genrule(*args, **kwargs)
324
325def _python_library(*args, **kwargs):
326    _patch_kwargs_common(kwargs)
327    env.python_library(*args, **kwargs)
328
329def _python_binary(*args, **kwargs):
330    _patch_kwargs_common(kwargs)
331    env.python_binary(*args, **kwargs)
332
333def _python_test(*args, **kwargs):
334    _patch_kwargs_common(kwargs)
335    env.python_test(*args, **kwargs)
336
337def get_oss_build_kwargs():
338    if env.is_oss:
339        return {
340            "link_style": "static",
341            "linker_flags": [
342                # platform/system.h uses dladdr() on mac and linux
343                "-ldl",
344            ],
345        }
346    return {}
347
348# Names in this struct should match the standard Buck rule names if possible:
349# see the "Build Rules" section in the sidebar of
350# https://buck.build/concept/build_rule.html.
351runtime = struct(
352    command_alias = _command_alias,
353    cxx_binary = _cxx_binary,
354    cxx_library = _cxx_library,
355    cxx_python_extension = _cxx_python_extension,
356    cxx_test = _cxx_test,
357    export_file = _export_file,
358    external_dep_location = _external_dep_location,
359    filegroup = _filegroup,
360    genrule = _genrule,
361    is_oss = env.is_oss,
362    python_binary = _python_binary,
363    python_library = _python_library,
364    python_test = _python_test,
365)
366