| # Copyright 2022 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Fuchsia cc primitives. |
| |
| Drop in replacements for cc_binary and cc_test: |
| - fuchsia_cc_binary |
| - fuchsia_cc_test |
| |
| cc_binary & cc_test wrappers: |
| - fuchsia_wrap_cc_binary |
| - fuchsia_wrap_cc_test |
| """ |
| |
| load(":utils.bzl", "forward_providers", "get_runfiles", "is_lib", "make_resource_struct", "rule_variant", "rule_variants") |
| load(":fuchsia_select.bzl", "if_fuchsia") |
| load(":fuchsia_component.bzl", "fuchsia_test_component") |
| load(":fuchsia_component_manifest.bzl", "fuchsia_component_manifest") |
| load( |
| ":providers.bzl", |
| "FuchsiaComponentInfo", |
| "FuchsiaDebugSymbolInfo", |
| "FuchsiaDefaultComponentInfo", |
| "FuchsiaPackageResourcesInfo", |
| ) |
| |
| KNOWN_PROVIDERS = [ |
| CcInfo, |
| # This provider is generated by cc_binary/cc_test, but there's no way to |
| # load it. |
| # CcLauncherInfo, |
| DebugPackageInfo, |
| FuchsiaDebugSymbolInfo, |
| FuchsiaPackageResourcesInfo, |
| InstrumentedFilesInfo, |
| OutputGroupInfo, |
| ] |
| |
| _invalid_deps_message = """Missing or mismatched exact_cc_%s_deps. |
| Please factor out the deps of `%s` and pass them into **both targets**. |
| If there are no deps, assign an empty list and pass into into both.""" |
| |
| def _fuchsia_cc_impl(ctx): |
| # Expect exactly one binary to be generated by the native cc_* rule. |
| native_outputs = ctx.attr.native_target.files.to_list() |
| if len(native_outputs) != 1: |
| fail("Expected exactly 1 native output for %s, got %s" % (ctx.attr.native_target, native_outputs)) |
| |
| resources = [ |
| # The binary generated by the native cc_* rule. |
| make_resource_struct(src = native_outputs[0], dest = "bin/" + ctx.attr.bin_name), |
| ] |
| for data in ctx.attr.data: |
| resources.extend(data[FuchsiaPackageResourcesInfo].resources) |
| |
| for dep in ctx.attr.deps: |
| for file in dep.files.to_list(): |
| if is_lib(file): |
| resources.append(make_resource_struct(src = file, dest = "lib/" + file.basename)) |
| for file in get_runfiles(ctx.attr.native_target): |
| if is_lib(file): |
| resources.append(make_resource_struct(src = file, dest = "lib/" + file.basename)) |
| |
| # Forward CC providers along with metadata for packaging. |
| return forward_providers( |
| ctx, |
| ctx.attr.native_target, |
| rename_executable = ctx.attr._variant != "test" and ctx.attr.bin_name, |
| *KNOWN_PROVIDERS |
| ) + [ |
| ctx.attr.clang_debug_symbols[FuchsiaDebugSymbolInfo], |
| FuchsiaPackageResourcesInfo(resources = resources), |
| ] |
| |
| _fuchsia_cc_binary, _fuchsia_cc_test = rule_variants( |
| variants = ("executable", "test"), |
| implementation = _fuchsia_cc_impl, |
| doc = """Attaches fuchsia-specific metadata to native cc_* targets. |
| |
| This allows them to be directly included in fuchsia_component. |
| """, |
| attrs = { |
| "bin_name": attr.string( |
| doc = "The name of the executable to place under bin/.", |
| mandatory = True, |
| ), |
| "native_target": attr.label( |
| doc = "The underlying cc_* target.", |
| mandatory = True, |
| providers = [[CcInfo, DefaultInfo]], |
| ), |
| "clang_debug_symbols": attr.label( |
| doc = "Clang debug symbols.", |
| mandatory = True, |
| providers = [FuchsiaDebugSymbolInfo], |
| ), |
| "deps": attr.label_list( |
| doc = """The exact list of dependencies dep-ed on by native_target. |
| |
| We need these because we can't rely on `cc_binary`'s DefaultInfo |
| [run]files (Bazel does not handle static libraries correctly.) |
| See https://github.com/bazelbuild/bazel/issues/1920. |
| |
| Failure to provide the *exact list* of dependencies may result in a |
| runtime crash. |
| """, |
| providers = [[CcInfo, DefaultInfo]], |
| ), |
| "data": attr.label_list( |
| doc = "Packaged files needed by this target at runtime.", |
| providers = [FuchsiaPackageResourcesInfo], |
| ), |
| }, |
| ) |
| |
| # fuchsia_cc_binary build rules. |
| def fuchsia_wrap_cc_binary( |
| name = None, |
| bin_name = None, |
| cc_binary = None, |
| exact_cc_binary_deps = None, |
| sdk_root_label = "@fuchsia_sdk", |
| clang_root_label = "@fuchsia_clang", |
| **kwargs): |
| """Wrap a native cc_binary. |
| |
| The resulting target can be used as a dep in fuchsia_component. |
| |
| Args: |
| name: This target name. |
| bin_name: The filename to place under bin/. Defaults to name. |
| cc_binary: The existing cc_binary's target name. |
| exact_cc_binary_deps: The existing cc_binary's deps. This **ALWAYS MUST BE** |
| identical to cc_binary's deps to prevent runtime crashes. |
| We recommend factoring out cc_binary's deps and then referencing |
| them in cc_binary as well as fuchsia_wrap_cc_binary. |
| sdk_root_label: Optionally override the root label of the fuchsia sdk repo. |
| clang_root_label: Optionally override the root label of the fuchsia clang repo. |
| **kwargs: Arguments to forward to the fuchsia cc_binary wrapper. |
| """ |
| if exact_cc_binary_deps == None: |
| fail(_invalid_deps_message % ("binary", cc_binary)) |
| |
| _deps = exact_cc_binary_deps |
| _implicit_deps = [ |
| "%s//pkg/fdio" % sdk_root_label, |
| ] |
| for implicit_dep in _implicit_deps: |
| if implicit_dep not in _deps: |
| _deps.append(implicit_dep) |
| |
| _fuchsia_cc_binary( |
| name = name, |
| bin_name = bin_name if bin_name != None else name, |
| native_target = cc_binary, |
| clang_debug_symbols = "%s//:debug_symbols" % clang_root_label, |
| deps = _deps, |
| data = [ |
| "%s//pkg/sysroot:dist" % sdk_root_label, |
| "%s//:dist" % clang_root_label, |
| "%s//:runtime" % clang_root_label, |
| ], |
| **kwargs |
| ) |
| |
| def fuchsia_cc_binary( |
| name = None, |
| sdk_root_label = "@fuchsia_sdk", |
| clang_root_label = "@fuchsia_clang", |
| tags = None, |
| visibility = None, |
| **cc_binary_kwargs): |
| """A fuchsia-specific cc_binary drop-in replacement. |
| |
| The resulting target can be used as a dep in fuchsia_component. |
| |
| Args: |
| name: The target name. |
| sdk_root_label: Optionally override the root label of the fuchsia sdk repo. |
| clang_root_label: Optionally override the root label of the fuchsia clang repo. |
| tags: The tags of all generated targets. |
| visibility: The visibility of all generated targets. |
| **cc_binary_kwargs: Arguments to forward to `cc_binary`. |
| """ |
| native.cc_binary( |
| name = "_%s_native" % name, |
| tags = tags, |
| visibility = visibility, |
| **cc_binary_kwargs |
| ) |
| native.alias( |
| name = "%s_native" % name, |
| actual = "_%s_native" % name, |
| tags = tags, |
| visibility = visibility, |
| deprecation = "fuchsia_cc_binary supports direct execution now. Please use `:%s` instead." % name, |
| ) |
| fuchsia_wrap_cc_binary( |
| name = name, |
| cc_binary = "_%s_native" % name, |
| exact_cc_binary_deps = cc_binary_kwargs["deps"] if "deps" in cc_binary_kwargs else [], |
| sdk_root_label = sdk_root_label, |
| clang_root_label = clang_root_label, |
| tags = tags, |
| visibility = visibility, |
| ) |
| |
| # fuchsia_cc_test build rules. |
| def _fuchsia_cc_test_manifest_impl(ctx): |
| # Detect googletest. |
| is_gtest = False |
| for dep in ctx.attr.deps: |
| if dep.label.workspace_name == ctx.attr.googletest.label.workspace_name: |
| is_gtest = True |
| break |
| |
| # Write cml. |
| generated_cml = ctx.actions.declare_file("%s.cml" % ctx.label.name) |
| ctx.actions.expand_template( |
| template = ctx.attr._template_file.files.to_list()[0], |
| output = generated_cml, |
| substitutions = { |
| "{{RUNNER}}": "gtest_runner" if is_gtest else "elf_test_runner", |
| "{{BINARY}}": ctx.attr.test_binary_name, |
| }, |
| ) |
| |
| return [ |
| DefaultInfo(files = depset([generated_cml])), |
| ] |
| |
| _fuchsia_cc_test_manifest = rule( |
| implementation = _fuchsia_cc_test_manifest_impl, |
| doc = """Generates a stub cml file for a given cc_test-backed _fuchsia_cc. |
| |
| Detects whether gtest is included as a dependency. If it is, the cml file |
| will use gtest_runner. Otherwise it will use the elf_test_runner. |
| """, |
| attrs = { |
| "test_binary_name": attr.string( |
| doc = "The test binary's name.", |
| mandatory = True, |
| ), |
| "deps": attr.label_list( |
| doc = "The same deps passed into _fuchsia_cc.", |
| mandatory = True, |
| providers = [[CcInfo, DefaultInfo]], |
| ), |
| "googletest": attr.label( |
| doc = "Any googletest label.", |
| allow_single_file = True, |
| mandatory = True, |
| ), |
| "_template_file": attr.label( |
| doc = "The template cml file.", |
| default = "//fuchsia/private:templates/cc_test_manifest.cml", |
| allow_single_file = True, |
| ), |
| }, |
| ) |
| |
| def _add_default_component_info_for_test_impl(ctx): |
| return forward_providers(ctx, ctx.attr.base) + ([ |
| FuchsiaDefaultComponentInfo(component = ctx.attr.default_component), |
| ] if ctx.attr.default_component else []) |
| |
| _add_default_component_info_for_test = rule_variant( |
| variant = "test", |
| implementation = _add_default_component_info_for_test_impl, |
| doc = """Provides FuchsiaDefaultComponentInfo on top of _fuchsia_cc providers.""", |
| attrs = { |
| "base": attr.label( |
| doc = "The base _fuchsia_cc target.", |
| mandatory = True, |
| providers = [[CcInfo, FuchsiaPackageResourcesInfo]], |
| ), |
| "default_component": attr.label( |
| doc = "The default component target.", |
| providers = [FuchsiaComponentInfo], |
| ), |
| }, |
| ) |
| |
| def fuchsia_wrap_cc_test( |
| name = None, |
| cc_test = None, |
| exact_cc_test_deps = None, |
| sdk_root_label = "@fuchsia_sdk", |
| clang_root_label = "@fuchsia_clang", |
| googletest_root_label = "@com_google_googletest", |
| **kwargs): |
| """Wrap a native cc_test. |
| |
| The resulting target can be used as a dep in fuchsia_component. |
| |
| Args: |
| name: This target name. |
| cc_test: The existing cc_test's target name. |
| exact_cc_test_deps: The existing cc_test's deps. This **ALWAYS MUST BE** |
| identical to cc_test's deps to prevent runtime crashes. |
| We recommend factoring out cc_test's deps and then referencing |
| them in cc_test as well as fuchsia_wrap_cc_test. |
| sdk_root_label: Optionally override the root label of the fuchsia sdk repo. |
| clang_root_label: Optionally override the root label of the fuchsia clang repo. |
| googletest_root_label: Optionally override the root label of the googletest repo. |
| **kwargs: Arguments to forward to the fuchsia cc_test wrapper. |
| """ |
| if exact_cc_test_deps == None: |
| fail(_invalid_deps_message % ("test", cc_test)) |
| |
| _fuchsia_cc_test( |
| name = "%s_native_cc" % name, |
| bin_name = name, |
| native_target = cc_test, |
| clang_debug_symbols = "%s//:debug_symbols" % clang_root_label, |
| deps = exact_cc_test_deps, |
| data = [ |
| "%s//pkg/sysroot:dist" % sdk_root_label, |
| "%s//:dist" % clang_root_label, |
| "%s//:runtime" % clang_root_label, |
| ], |
| **kwargs |
| ) |
| |
| _fuchsia_cc_test_manifest( |
| name = "%s_autogen_cml" % name, |
| test_binary_name = name, |
| deps = exact_cc_test_deps, |
| googletest = "%s//:BUILD.bazel" % googletest_root_label, |
| **kwargs |
| ) |
| |
| # Generate a default component manifest. |
| fuchsia_component_manifest( |
| name = "%s_autogen_manifest" % name, |
| component_name = name, |
| src = ":%s_autogen_cml" % name, |
| **kwargs |
| ) |
| |
| # Generate the default component. |
| fuchsia_test_component( |
| name = "%s_autogen_component" % name, |
| content = { |
| ":%s_autogen_manifest" % name: "meta/%s.cm" % name, |
| }, |
| manifest = ":%s_autogen_manifest" % name, |
| deps = [":%s_native_cc" % name], |
| **kwargs |
| ) |
| |
| _add_default_component_info_for_test( |
| name = name, |
| base = ":%s_native_cc" % name, |
| default_component = if_fuchsia(":%s_autogen_component" % name, if_not = None), |
| testonly = True, |
| **kwargs |
| ) |
| |
| def fuchsia_cc_test( |
| name = None, |
| sdk_root_label = "@fuchsia_sdk", |
| clang_root_label = "@fuchsia_clang", |
| googletest_root_label = "@com_google_googletest", |
| tags = None, |
| visibility = None, |
| **cc_test_kwargs): |
| """A fuchsia-specific cc_test drop-in replacement. |
| |
| The resulting target can be used as a dep in fuchsia_component. |
| |
| Args: |
| name: The target name. |
| sdk_root_label: Optionally override the root label of the fuchsia sdk repo. |
| clang_root_label: Optionally override the root label of the fuchsia clang repo. |
| googletest_root_label: Optionally override the root label of the googletest repo. |
| tags: The tags of all generated targets. |
| visibility: The visibility of all generated targets. |
| **cc_test_kwargs: Arguments to forward to `cc_test`. |
| """ |
| native.cc_test( |
| name = "_%s_native" % name, |
| tags = tags, |
| visibility = visibility, |
| **cc_test_kwargs |
| ) |
| native.alias( |
| name = "%s_native" % name, |
| actual = "_%s_native" % name, |
| tags = tags, |
| visibility = visibility, |
| deprecation = "fuchsia_cc_test supports direct execution now. Please use `:%s` instead." % name, |
| ) |
| fuchsia_wrap_cc_test( |
| name = name, |
| cc_test = "_%s_native" % name, |
| exact_cc_test_deps = cc_test_kwargs["deps"] if "deps" in cc_test_kwargs else [], |
| sdk_root_label = sdk_root_label, |
| clang_root_label = clang_root_label, |
| googletest_root_label = googletest_root_label, |
| tags = tags, |
| visibility = visibility, |
| ) |