diff --git a/cc/toolchains/cc_coverage_config.bzl b/cc/toolchains/cc_coverage_config.bzl new file mode 100644 index 000000000..f86805760 --- /dev/null +++ b/cc/toolchains/cc_coverage_config.bzl @@ -0,0 +1,102 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# 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 +# +# http://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. + +"""Rules to configure cc coverage collection.""" + +load("//cc/toolchains/impl:collect.bzl", "collect_data") +load(":cc_toolchain_info.bzl", "CoverageConfigInfo") + +visibility("public") + +def _cc_coverage_config_impl(ctx): + exe_info = ctx.attr.src[DefaultInfo] + if exe_info.files_to_run != None and exe_info.files_to_run.executable != None: + exe = exe_info.files_to_run.executable + elif len(exe_info.files.to_list()) == 1: + exe = exe_info.files.to_list()[0] + else: + fail("Expected cc_coverage_config's src attribute to be either an executable or a single file") + + runfiles = collect_data(ctx, ctx.attr.data + [ctx.attr.src]) + config = CoverageConfigInfo( + label = ctx.label, + type = ctx.attr.type, + exe = exe, + runfiles = runfiles, + ) + + link = ctx.actions.declare_file(ctx.label.name) + ctx.actions.symlink( + output = link, + target_file = exe, + is_executable = True, + ) + return [ + config, + # This isn't required, but now we can do "bazel run ", which can + # be very helpful when debugging toolchains. + DefaultInfo( + files = depset([link]), + runfiles = runfiles, + executable = link, + ), + ] + +cc_coverage_config = rule( + implementation = _cc_coverage_config_impl, + attrs = { + "type": attr.string( + mandatory = True, + values = [ + "gcov", + "llvm-cov", + ], + doc = """ +The type of coverage this config is for (e.g., gcov). +""" + ), + "src": attr.label( + mandatory = True, + allow_files = True, + cfg = "exec", + doc = """ +The tool to collect coverage with. +""" + ), + "data": attr.label_list( + mandatory = False, + allow_files = True, + doc = """ +Additional files that are required for this coverage config to run. +""", + ), + }, + doc = """ +Defines the configuration to collect CC coverage. + +Example: +``` +load("//cc/toolchains:cc_coverage_config.bzl", "cc_coverage_config") + +cc_coverage_config( + name = "gcov", + type = "gcov", + src = "bin/gcov", +) +``` +""", + provides = [ + CoverageConfigInfo, + ], +) diff --git a/cc/toolchains/cc_toolchain_info.bzl b/cc/toolchains/cc_toolchain_info.bzl index 735f96f22..4da3aecd5 100644 --- a/cc/toolchains/cc_toolchain_info.bzl +++ b/cc/toolchains/cc_toolchain_info.bzl @@ -217,9 +217,21 @@ ToolchainConfigInfo = provider( "enabled_features": "(Sequence[FeatureInfo]) The features That are enabled by default for this toolchain", "tool_map": "(ToolConfigInfo) A provider mapping toolchain action types to tools.", "args": "(Sequence[ArgsInfo]) A list of arguments to be unconditionally applied to the toolchain.", - "artifact_name_patterns": "Sequence[ArtifactNamePatternInfo] A artifact name patterns for this toolchain", + "artifact_name_patterns": "Sequence[ArtifactNamePatternInfo] The artifact name patterns for this toolchain", + "coverage_config": "(CoverageConfigInfo) The coverage configuration for this toolchain.", "make_variables": "Sequence[MakeVariableInfo] Make variable substitutions for this toolchain", "files": "(dict[ActionTypeInfo, depset[File]]) Files required for the toolchain, keyed by the action type.", "allowlist_include_directories": "(depset[DirectoryInfo]) Built-in include directories implied by this toolchain's args and tools that should be allowlisted in Bazel's include checker", }, ) + +CoverageConfigInfo = provider( + doc = "A type of coverage (eg. gcov)", + # @unsorted-dict-items + fields = { + "label": "(Label) The label defining this provider. Place in error messages to simplify debugging", + "type": "(CoverageTypeInfo) A provider defining the type of coverage config", + "exe": "(File) The file corresponding to the coverage tool", + "runfiles": "(runfiles) The files required to run the coverage tool", + }, +) diff --git a/cc/toolchains/impl/toolchain_config.bzl b/cc/toolchains/impl/toolchain_config.bzl index 1f6efefe5..9f5849393 100644 --- a/cc/toolchains/impl/toolchain_config.bzl +++ b/cc/toolchains/impl/toolchain_config.bzl @@ -19,6 +19,7 @@ load( "ActionTypeSetInfo", "ArgsListInfo", "ArtifactNamePatternInfo", + "CoverageConfigInfo", "FeatureSetInfo", "MakeVariableInfo", "ToolConfigInfo", @@ -61,6 +62,7 @@ def _cc_toolchain_config_impl(ctx): tool_map = ctx.attr.tool_map, args = ctx.attr.args, artifact_name_patterns = ctx.attr.artifact_name_patterns, + coverage_config = ctx.attr.coverage_config, make_variables = ctx.attr.make_variables, ) @@ -110,6 +112,7 @@ cc_toolchain_config = rule( "known_features": attr.label_list(providers = [FeatureSetInfo]), "enabled_features": attr.label_list(providers = [FeatureSetInfo]), "artifact_name_patterns": attr.label_list(providers = [ArtifactNamePatternInfo]), + "coverage_config": attr.label_list(providers = [CoverageConfigInfo]), "make_variables": attr.label_list(providers = [MakeVariableInfo]), "_builtin_features": attr.label(default = "//cc/toolchains/features:all_builtin_features"), }, diff --git a/cc/toolchains/impl/toolchain_config_info.bzl b/cc/toolchains/impl/toolchain_config_info.bzl index 06a2c8f47..f1d057467 100644 --- a/cc/toolchains/impl/toolchain_config_info.bzl +++ b/cc/toolchains/impl/toolchain_config_info.bzl @@ -13,7 +13,7 @@ # limitations under the License. """Helper functions to create and validate a ToolchainConfigInfo.""" -load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo") +load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "CoverageConfigInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo") load(":args_utils.bzl", "get_action_type") load(":collect.bzl", "collect_args_lists", "collect_features") @@ -162,7 +162,7 @@ def _collect_make_variables(targets, fail): return make_variables.values() -def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], make_variables = [], tool_map = None, fail = fail): +def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], coverage_config = None, make_variables = [], tool_map = None, fail = fail): """Generates and validates a ToolchainConfigInfo from lists of labels. Args: @@ -214,6 +214,7 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg files = files, allowlist_include_directories = allowlist_include_directories, artifact_name_patterns = _collect_artifact_name_patterns(artifact_name_patterns, fail), + coverage_config = coverage_config[CoverageConfigInfo] if coverage_config else None, make_variables = _collect_make_variables(make_variables, fail), ) _validate_toolchain(toolchain_config, fail = fail) diff --git a/cc/toolchains/toolchain.bzl b/cc/toolchains/toolchain.bzl index af8d5229d..c06e9fc71 100644 --- a/cc/toolchains/toolchain.bzl +++ b/cc/toolchains/toolchain.bzl @@ -58,6 +58,7 @@ def cc_toolchain( tool_map = None, args = [], artifact_name_patterns = [], + coverage_config = None, make_variables = [], known_features = [], enabled_features = [], @@ -171,6 +172,7 @@ def cc_toolchain( tool_map = tool_map, args = args, artifact_name_patterns = artifact_name_patterns, + coverage_config = coverage_config, make_variables = make_variables, known_features = known_features, enabled_features = enabled_features,