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