blob: 7ecb62c80ad372f51da261655fb5964c23ac7ab1 [file] [log] [blame]
# 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,
)