diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 10f0c250c..2d6fdb67c 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -147,9 +147,8 @@ tasks: build_targets: *build_targets test_targets: *test_targets test_flags: *windows_test_flags - # TODO(pzembrod): reenable once Commit #53428ca hits Bazel ROLLING - should be within 08/2026 - # coverage_flags: *windows_coverage_flags - # coverage_targets: *coverage_targets + coverage_flags: *windows_coverage_flags + coverage_targets: *coverage_targets # Bazel 7 ubuntu_bazel_7: @@ -230,7 +229,7 @@ tasks: coverage_targets: *test_targets_bazel_less_than_9 # Bazel 9 - ubuntu: + ubuntu_bazel_9: name: Ubuntu (Bazel 9) bazel: 9.x platform: ubuntu2404 @@ -238,7 +237,7 @@ tasks: test_targets: *test_targets coverage_flags: *common_coverage_flags coverage_targets: *coverage_targets - macos_intel: + macos_intel_bazel_9: name: MacOS Intel (Bazel 9) bazel: 9.x platform: macos @@ -247,7 +246,7 @@ tasks: test_flags: *macos_test_flags coverage_flags: *common_coverage_flags coverage_targets: *coverage_targets - macos: + macos_bazel_9: name: MacOS (Bazel 9) bazel: 9.x platform: macos_arm64 @@ -256,9 +255,50 @@ tasks: test_flags: *macos_test_flags coverage_flags: *common_coverage_flags coverage_targets: *coverage_targets - windows: + windows_bazel_9: name: Windows (Bazel 9) bazel: 9.x + platform: windows + build_targets: *build_targets + test_targets: *test_targets + test_flags: *windows_test_flags + coverage_flags: *windows_coverage_flags + coverage_targets: *test_targets + +# Bazel 10 + ubuntu_bazel_10: + name: Ubuntu (Bazel 10) + bazel: 10.* # TODO(pzembrod): Change to 10.x after 10.0.0 is released. Same for other jobs. + platform: ubuntu2404 + build_targets: *build_targets + test_targets: *test_targets + coverage_flags: *common_coverage_flags + coverage_targets: *coverage_targets + macos_intel_bazel_10: + name: MacOS Intel (Bazel 10) + bazel: 10.* + platform: macos + environment: + ANDROID_HOME: "" + ANDROID_NDK_HOME: "" + build_targets: *build_targets + test_targets: *test_targets + test_flags: *macos_test_flags + coverage_flags: *common_coverage_flags + coverage_targets: *coverage_targets + macos_bazel_10: + name: MacOS (Bazel 10) + bazel: 10.* + platform: macos_arm64 + build_targets: *build_targets + test_targets: *test_targets + test_flags: *macos_test_flags + coverage_flags: *common_coverage_flags + coverage_targets: *coverage_targets + windows_bazel_10: + name: Windows (Bazel 10) + bazel: 10.* + platform: windows build_targets: *build_targets test_targets: *test_targets test_flags: *windows_test_flags diff --git a/.bazelrc b/.bazelrc index 34bed5573..dd2b2bdb1 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,7 @@ -# Required by googletest 1.17. -common --cxxopt=-std=c++17 +common --enable_platform_specific_config +# TODO: Remove https://github.com/bazelbuild/bazel/pull/31423 +common:windows --remote_download_all + +common --check_direct_dependencies=off + test --test_env=PATH=/bin:/usr/bin:/usr/local/bin diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json index 1bdf20711..a51b320ad 100644 --- a/.bcr/metadata.template.json +++ b/.bcr/metadata.template.json @@ -10,6 +10,11 @@ "email": "pzembrod@google.com", "github": "pzembrod", "name": "Philip Zembrod" + }, + { + "email": "bgorshenev@google.com", + "github": "lilygorsheneva", + "name": "Lily Gorsheneva" } ], "versions": [], diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 47fba6122..cf7edd610 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,9 +23,29 @@ permissions: attestations: write contents: write jobs: + check-commit-status: + name: Check commit status + runs-on: ubuntu-latest + permissions: + statuses: read + steps: + - name: Require a green commit + env: + GH_TOKEN: ${{ github.token }} + run: | + status="$(gh api \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/status" \ + --jq '.state')" + if [[ "${status}" != "success" ]]; then + echo "::error::Commit ${GITHUB_SHA} is not green (combined status: ${status})." + exit 1 + fi release: + needs: check-commit-status uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.2.3 with: + bazel_test_command: "true" prerelease: false release_files: rules_cc-*.tar.gz tag_name: ${{ inputs.tag_name || github.ref_name }} diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml new file mode 100644 index 000000000..50fff9056 --- /dev/null +++ b/.github/workflows/sty.yaml @@ -0,0 +1,108 @@ +name: Starlark types + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + sty: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: tamird/sty@95e2b424a7f52fa0b0946da98eb2cc328e9b4107 + with: + audit-loads: true + bazel-version: '9.3.0' + advisory-pathspecs: '*.bzl' + advisory-bazel-load-context: '@cross_repo_test//toolchain_include_dirs:toolchain_config' + advisory-require-bazel-load-context: tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/cc_toolchain_config.bzl + files: | + @bazel_skylib//:bzl_library.bzl + @bazel_skylib//lib:paths.bzl + @bazel_skylib//rules:common_settings.bzl + @bazel_skylib//rules/directory:providers.bzl + cc/BUILD + cc/action_names.bzl + cc/cc_import.bzl + cc/cc_postmark.bzl + cc/cc_rules_extension_support.bzl + cc/cc_shared_library.bzl + cc/common/BUILD + cc/common/cc_shared_library_info.bzl + cc/common/debug_package_info.bzl + cc/common/feature_names.bzl + cc/common/objc_info.bzl + cc/extensions.bzl + cc/private/cc_shared_library_hint_info.bzl + cc/private/cc_shared_library_info.bzl + cc/private/debug_package_info.bzl + cc/private/graph_node_info.bzl + cc/private/paths.bzl + cc/private/rules_impl/failing_cc_proto_library.bzl + cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl + cc/private/rules_impl/fdo/fdo_profile.bzl + cc/private/rules_impl/fdo/memprof_profile.bzl + cc/private/rules_impl/fdo/propeller_optimize.bzl + cc/private/rules_impl/native_cc_common.bzl + cc/private/rules_impl/objc_compilation_context_info.bzl + cc/private/toolchain/cc_configure.bzl + cc/private/toolchain/unix_cc_configure.bzl + cc/repositories.bzl + cc/starlark_doc_extract_helper.bzl + cc/system_library.bzl + cc/toolchains/actions.bzl + cc/toolchains/actions/BUILD + cc/toolchains/args/BUILD + cc/toolchains/args/libraries_to_link/private/library_link_args.bzl + cc/toolchains/args_list.bzl + cc/toolchains/artifacts.bzl + cc/toolchains/capabilities/BUILD + cc/toolchains/cc_toolchain.bzl + cc/toolchains/cc_toolchain_alias.bzl + cc/toolchains/cc_toolchain_config_info.bzl + cc/toolchains/directory_tool.bzl + cc/toolchains/fdo_prefetch_hints.bzl + cc/toolchains/fdo_profile.bzl + cc/toolchains/feature.bzl + cc/toolchains/feature_constraint.bzl + cc/toolchains/feature_injection.bzl + cc/toolchains/feature_set.bzl + cc/toolchains/features/BUILD + cc/toolchains/features/legacy/BUILD + cc/toolchains/impl/cc_artifact_category.bzl + cc/toolchains/impl/external_feature.bzl + cc/toolchains/impl/label_utils.bzl + cc/toolchains/impl/libc_version.bzl + cc/toolchains/impl/markdown_helpers.bzl + cc/toolchains/legacy_file_group.bzl + cc/toolchains/legacy_tool.bzl + cc/toolchains/make_variable.bzl + cc/toolchains/memprof_profile.bzl + cc/toolchains/mutually_exclusive_category.bzl + cc/toolchains/nested_args.bzl + cc/toolchains/propeller_optimize.bzl + cc/toolchains/providers.bzl + cc/toolchains/tool.bzl + cc/toolchains/tool_capability.bzl + cc/toolchains/variables/BUILD + cc/use_cc_toolchain.bzl + local_bazel.bzl + tests/cc/testutil/toolchains/additional_toolchains.bzl + tests/cc/testutil/toolchains/features.bzl + tests/load_from_macro/tags.bzl + tests/rule_based_toolchain/action_type_sets + tests/rule_based_toolchain/args/make_var_provider.bzl + tests/rule_based_toolchain/helpers.bzl + tests/rule_based_toolchain/legacy_features_as_args/goldens + tests/rule_based_toolchain/tool/execution_requirements.bzl diff --git a/BUILD b/BUILD index 278e2f585..449b30347 100644 --- a/BUILD +++ b/BUILD @@ -4,7 +4,10 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) -exports_files(["LICENSE"]) +exports_files([ + "LICENSE", + "stubs.toml", +]) exports_files(["copy.bara.sky"]) @@ -51,6 +54,7 @@ filegroup( srcs = [ "BUILD", "MODULE.bazel", + "stubs.toml", "//cc:srcs", ], visibility = ["//:__subpackages__"], diff --git a/MODULE.bazel b/MODULE.bazel index f0077955f..6d274608c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,7 +4,7 @@ module( compatibility_level = 1, ) -bazel_dep(name = "apple_support", version = "2.8.0") +bazel_dep(name = "apple_support", version = "2.9.1") bazel_dep(name = "bazel_features", version = "1.50.0") bazel_dep(name = "bazel_skylib", version = "1.8.0") bazel_dep(name = "platforms", version = "0.0.10") @@ -19,9 +19,7 @@ register_toolchains( ) bazel_dep(name = "rules_shell", version = "0.2.0", dev_dependency = True) - -bazel_dep(name = "rules_python", version = "1.7.0") - +bazel_dep(name = "rules_python", version = "2.0.0", dev_dependency = True) bazel_dep(name = "googletest", version = "1.17.0", dev_dependency = True) single_version_override( module_name = "googletest", diff --git a/cc/BUILD b/cc/BUILD index 615b43002..1b8a32c65 100644 --- a/cc/BUILD +++ b/cc/BUILD @@ -30,6 +30,8 @@ filegroup( ) exports_files([ + "cc_postmark_initializers.bzli", + "cc_toolchain_config_lib.bzli", "defs.bzl", "action_names.bzl", "system_library.bzl", @@ -74,6 +76,7 @@ filegroup( testonly = 1, srcs = glob([ "**/*.bzl", + "**/*.bzli", "**/BUILD", ]) + [ "//cc/common:srcs", diff --git a/cc/cc_import.bzl b/cc/cc_import.bzl index 176243646..822250a9f 100644 --- a/cc/cc_import.bzl +++ b/cc/cc_import.bzl @@ -15,5 +15,8 @@ load("@cc_compatibility_proxy//:proxy.bzl", _cc_import = "cc_import") +# TODO(https://github.com/bazelbuild/bazel/issues/32343): Annotate **kwargs +# when Bazel can derive them from the wrapped cc_import rule. def cc_import(**kwargs): + # type: (**Unpack[KeywordArgs[_cc_import]]) -> None _cc_import(**kwargs) diff --git a/cc/cc_postmark.bzl b/cc/cc_postmark.bzl index f9bf9272c..eed942ead 100644 --- a/cc/cc_postmark.bzl +++ b/cc/cc_postmark.bzl @@ -14,20 +14,21 @@ """PostMark is not implemented in Bazel.""" -def _get_postmark_attrs(): - return {} - -def _postmark_initializer(**kwargs): - return kwargs +load("//cc:cc_postmark_initializers.bzl", _get_postmark_attrs = "get_postmark_attrs", _postmark_initializer = "postmark_initializer") def _add_postmark_action( ctx, # @unused cc_toolchain, # @unused binary, # @unused output_binary_for_linking): # @unused + # type: (object, object, object, object) -> None pass +# The public toolchain never enables PostMark. A broader bool makes the +# cc_binary branch that passes internal-only action keywords reachable. +# TODO: Use bool when the public implementation supports that action. def _get_use_postmark(ctx): # @unused + # type: (object) -> Literal[False] return False postmark = struct( diff --git a/cc/cc_postmark_initializers.bzli b/cc/cc_postmark_initializers.bzli new file mode 100644 index 000000000..39f75ad5c --- /dev/null +++ b/cc/cc_postmark_initializers.bzli @@ -0,0 +1,7 @@ +"""Contracts for the PostMark initializer compatibility layer.""" + +# TODO(https://github.com/bazelbuild/starlark/pull/362): Move the initializers into source when supported Bazel versions can bind +# K and V on the function. A signature comment cannot bind these type parameters. + +def _get_postmark_attrs[K, V]() -> dict[K, V]: ... +def _postmark_initializer[T](**kwargs: T) -> dict[str, T]: ... diff --git a/cc/cc_shared_library.bzl b/cc/cc_shared_library.bzl index 2cbc47f83..fe1580b36 100644 --- a/cc/cc_shared_library.bzl +++ b/cc/cc_shared_library.bzl @@ -15,5 +15,8 @@ load("@cc_compatibility_proxy//:proxy.bzl", _cc_shared_library = "cc_shared_library") +# TODO(https://github.com/bazelbuild/bazel/issues/32343): Annotate **kwargs +# when Bazel can derive them from the wrapped cc_shared_library rule. def cc_shared_library(**kwargs): + # type: (**Unpack[KeywordArgs[_cc_shared_library]]) -> None _cc_shared_library(**kwargs) diff --git a/cc/cc_toolchain_config_lib.bzl b/cc/cc_toolchain_config_lib.bzl index 77bb554f9..8627534ab 100644 --- a/cc/cc_toolchain_config_lib.bzl +++ b/cc/cc_toolchain_config_lib.bzl @@ -14,30 +14,35 @@ """ A library of functions creating structs for CcToolchainConfigInfo.""" def _check_is_none(obj, parameter_name, method_name): + # type: (object, str, str) -> None if obj != None: fail("{} parameter of {} should be None, found {}." .format(parameter_name, method_name, type(obj))) def _check_is_none_or_right_type(obj, obj_of_right_type, parameter_name, method_name): + # type: (object, object, str, str) -> None if obj != None: _check_same_type(obj, obj_of_right_type, parameter_name, method_name) def _check_right_type(obj, expected_type, parameter_name, method_name): + # type: (object, str, str, str) -> None if type(obj) != expected_type: fail("{} parameter of {} should be a {}, found {}." .format(parameter_name, method_name, expected_type, type(obj))) def _check_same_type(obj, obj_of_right_type, parameter_name, method_name): + # type: (object, object, str, str) -> None _check_right_type(obj, type(obj_of_right_type), parameter_name, method_name) def _check_is_nonempty_string(obj, parameter_name, method_name): + # type: (object, str, str) -> None _check_same_type(obj, "", parameter_name, method_name) if obj == "": fail("{} parameter of {} must be a nonempty string." .format(parameter_name, method_name)) def _check_is_nonempty_list(obj, parameter_name, method_name): - _check_same_type(obj, [], parameter_name, method_name) + _check_right_type(obj, "list", parameter_name, method_name) if len(obj) == 0: fail("{} parameter of {} must be a nonempty list." .format(parameter_name, method_name)) @@ -48,6 +53,7 @@ EnvEntryInfo = provider( ) def env_entry(key, value, expand_if_available = None): + # type: (str, str, str | None) -> EnvEntryInfo """ A key/value pair to be added as an environment variable. The returned EnvEntry provider finds its use in EnvSet creation through @@ -81,6 +87,7 @@ VariableWithValueInfo = provider( ) def variable_with_value(name, value): + # type: (str, str) -> VariableWithValueInfo """ Represents equality check between a variable and a certain value. The returned provider finds its use through flag_group.expand_if_equal, @@ -108,6 +115,7 @@ MakeVariableInfo = provider( ) def make_variable(name, value): + # type: (str, str) -> MakeVariableInfo """ A make variable that is made accessible to rules.""" _check_is_nonempty_string(name, "name", "make_variable") _check_is_nonempty_string(value, "value", "make_variable") @@ -123,6 +131,7 @@ FeatureSetInfo = provider( ) def feature_set(features = []): + # type: (list[str]) -> FeatureSetInfo """ A set of features. Used to support logical 'and' when specifying feature requirements in a @@ -134,7 +143,7 @@ def feature_set(features = []): Returns: A FeatureSetInfo provider. """ - _check_same_type(features, [], "features", "feature_set") + _check_right_type(features, "list", "features", "feature_set") return FeatureSetInfo(features = features, type_name = "feature_set") WithFeatureSetInfo = provider( @@ -143,6 +152,7 @@ WithFeatureSetInfo = provider( ) def with_feature_set(features = [], not_features = []): + # type: (list[str], list[str]) -> WithFeatureSetInfo """ A set of positive and negative features. This stanza will evaluate to true when every 'feature' is enabled, and @@ -155,8 +165,8 @@ def with_feature_set(features = [], not_features = []): Returns: A WithFeatureSetInfo provider. """ - _check_same_type(features, [], "features", "with_feature_set") - _check_same_type(not_features, [], "not_features", "with_feature_set") + _check_right_type(features, "list", "features", "with_feature_set") + _check_right_type(not_features, "list", "not_features", "with_feature_set") return WithFeatureSetInfo( features = features, not_features = not_features, @@ -169,6 +179,7 @@ EnvSetInfo = provider( ) def env_set(actions, env_entries = [], with_features = []): + # type: (list[str], list[EnvEntryInfo], list[WithFeatureSetInfo]) -> EnvSetInfo """ Groups a set of environment variables to apply for certain actions. EnvSet providers are passed to feature() and action_config(), to be applied to @@ -190,8 +201,8 @@ def env_set(actions, env_entries = [], with_features = []): An EnvSetInfo provider. """ _check_is_nonempty_list(actions, "actions", "env_set") - _check_same_type(env_entries, [], "env_entries", "env_set") - _check_same_type(with_features, [], "with_features", "env_set") + _check_right_type(env_entries, "list", "env_entries", "env_set") + _check_right_type(with_features, "list", "with_features", "env_set") return EnvSetInfo( actions = actions, env_entries = env_entries, @@ -223,6 +234,7 @@ def flag_group( expand_if_true = None, expand_if_false = None, expand_if_equal = None): + # type: (list[str], list[FlagGroupInfo], str | None, str | None, str | None, str | None, str | None, VariableWithValueInfo | None) -> FlagGroupInfo """ A group of flags. Supports parametrization via variable expansion. To expand a variable of list type, flag_group has to be annotated with @@ -292,8 +304,8 @@ def flag_group( A FlagGroupInfo provider. """ - _check_same_type(flags, [], "flags", "flag_group") - _check_same_type(flag_groups, [], "flag_groups", "flag_group") + _check_right_type(flags, "list", "flags", "flag_group") + _check_right_type(flag_groups, "list", "flag_groups", "flag_group") if len(flags) > 0 and len(flag_groups) > 0: fail("flag_group must not contain both a flag and another flag_group.") if len(flags) == 0 and len(flag_groups) == 0: @@ -335,6 +347,7 @@ def flag_set( actions = [], with_features = [], flag_groups = []): + # type: (list[str], list[WithFeatureSetInfo], list[FlagGroupInfo]) -> FlagSetInfo """ A set of flags to be expanded in the command line for specific actions. Args: @@ -351,9 +364,9 @@ def flag_set( Returns: A FlagSetInfo provider. """ - _check_same_type(actions, [], "actions", "flag_set") - _check_same_type(with_features, [], "with_features", "flag_set") - _check_same_type(flag_groups, [], "flag_groups", "flag_set") + _check_right_type(actions, "list", "actions", "flag_set") + _check_right_type(with_features, "list", "with_features", "flag_set") + _check_right_type(flag_groups, "list", "flag_groups", "flag_set") return FlagSetInfo( actions = actions, with_features = with_features, @@ -383,6 +396,7 @@ def feature( requires = [], implies = [], provides = []): + # type: (str, bool, list[FlagSetInfo], list[EnvSetInfo], list[FeatureSetInfo], list[str], list[str]) -> FeatureInfo """ Contains all flag specifications for one feature. Args: @@ -421,11 +435,11 @@ def feature( A FeatureInfo provider. """ _check_same_type(enabled, True, "enabled", "feature") - _check_same_type(flag_sets, [], "flag_sets", "feature") - _check_same_type(env_sets, [], "env_sets", "feature") - _check_same_type(requires, [], "requires", "feature") - _check_same_type(provides, [], "provides", "feature") - _check_same_type(implies, [], "implies", "feature") + _check_right_type(flag_sets, "list", "flag_sets", "feature") + _check_right_type(env_sets, "list", "env_sets", "feature") + _check_right_type(requires, "list", "requires", "feature") + _check_right_type(provides, "list", "provides", "feature") + _check_right_type(implies, "list", "implies", "feature") return FeatureInfo( name = name, enabled = enabled, @@ -443,12 +457,14 @@ ToolPathInfo = provider( ) def tool_path(name, path): + # type: (str, str) -> ToolPathInfo """ Tool locations. Args: name: Name of the tool. path: Location of the tool; Can be absolute path (in case of non hermetic - toolchain), or path relative to the cc_toolchain's package. + toolchain), a path relative to the cc_toolchain's package, or the path of a + generated file. Returns: A ToolPathInfo provider. @@ -474,6 +490,7 @@ ToolInfo = provider( ) def tool(path = None, with_features = [], execution_requirements = [], tool = None): + # type: (str | None, list[WithFeatureSetInfo], list[str], File | None) -> ToolInfo """ Describes a tool associated with a crosstool action config. Args: @@ -506,8 +523,8 @@ def tool(path = None, with_features = [], execution_requirements = [], tool = No _check_is_none(path, "path", "tool") _check_right_type(tool, "File", "tool", "tool") - _check_same_type(with_features, [], "with_features", "tool") - _check_same_type(execution_requirements, [], "execution_requirements", "tool") + _check_right_type(with_features, "list", "with_features", "tool") + _check_right_type(execution_requirements, "list", "execution_requirements", "tool") return ToolInfo( path = path, tool = tool, @@ -535,6 +552,7 @@ def action_config( tools = [], flag_sets = [], implies = []): + # type: (str, bool, list[ToolInfo], list[FlagSetInfo], list[str]) -> ActionConfigInfo """ Configuration of a Bazel action. An action config corresponds to a Bazel action, and allows selection of @@ -565,9 +583,9 @@ def action_config( """ _check_is_nonempty_string(action_name, "name", "action_config") _check_same_type(enabled, True, "enabled", "action_config") - _check_same_type(tools, [], "tools", "action_config") - _check_same_type(flag_sets, [], "flag_sets", "action_config") - _check_same_type(implies, [], "implies", "action_config") + _check_right_type(tools, "list", "tools", "action_config") + _check_right_type(flag_sets, "list", "flag_sets", "action_config") + _check_right_type(implies, "list", "implies", "action_config") return ActionConfigInfo( action_name = action_name, enabled = enabled, @@ -588,6 +606,7 @@ ArtifactNamePatternInfo = provider( ) def artifact_name_pattern(category_name, prefix, extension): + # type: (str, str | None, str | None) -> ArtifactNamePatternInfo """ The name for an artifact of a given category of input or output artifacts to an action. Args: @@ -616,7 +635,9 @@ def artifact_name_pattern(category_name, prefix, extension): ) def _is_gcc_compiler(compiler): + # type: (str) -> bool return "gcc" in compiler def get_profile_correction_flags(ctx): + # type: (ctx[object, struct[{'compiler': NotRequired[str]}, ...]]) -> list[str] return ["-fprofile-correction"] if hasattr(ctx.attr, "compiler") and _is_gcc_compiler(ctx.attr.compiler) else [] diff --git a/cc/cc_toolchain_config_lib.bzli b/cc/cc_toolchain_config_lib.bzli new file mode 100644 index 000000000..6ceb58f41 --- /dev/null +++ b/cc/cc_toolchain_config_lib.bzli @@ -0,0 +1,204 @@ +# Copyright 2026 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. +"""Contracts for complete provider values produced by the configuration helpers. + +ActionConfigInfo also accepts the optional legacy config_name field. +""" + +# TODO(https://github.com/bazelbuild/bazel/issues/32207): Declare the constructor and field types on the providers when Bazel +# can express their field schemas. Callers use their result types across modules. + +def _check_is_nonempty_list[T](obj: list[T], parameter_name: str, method_name: str) -> None: ... + +def EnvEntryInfo( + *, + key: str, + value: str, + expand_if_available: str | None, + type_name: Literal["env_entry"], +) -> record[{ + "key": str, + "value": str, + "expand_if_available": str | None, + "type_name": Literal["env_entry"], +}]: ... + +def VariableWithValueInfo( + *, + name: str, + value: str, + type_name: Literal["variable_with_value"], +) -> record[{ + "name": str, + "value": str, + "type_name": Literal["variable_with_value"], +}]: ... + +def MakeVariableInfo( + *, + name: str, + value: str, + type_name: Literal["make_variable"], +) -> record[{ + "name": str, + "value": str, + "type_name": Literal["make_variable"], +}]: ... + +def FeatureSetInfo( + *, + features: list[str], + type_name: Literal["feature_set"], +) -> record[{ + "features": list[str], + "type_name": Literal["feature_set"], +}]: ... + +def WithFeatureSetInfo( + *, + features: list[str], + not_features: list[str], + type_name: Literal["with_feature_set"], +) -> record[{ + "features": list[str], + "not_features": list[str], + "type_name": Literal["with_feature_set"], +}]: ... + +def EnvSetInfo( + *, + actions: list[str], + env_entries: list[EnvEntryInfo], + with_features: list[WithFeatureSetInfo], + type_name: Literal["env_set"], +) -> record[{ + "actions": list[str], + "env_entries": list[EnvEntryInfo], + "with_features": list[WithFeatureSetInfo], + "type_name": Literal["env_set"], +}]: ... + +def FlagGroupInfo( + *, + flags: list[str], + flag_groups: list[FlagGroupInfo], + iterate_over: str | None, + expand_if_available: str | None, + expand_if_not_available: str | None, + expand_if_true: str | None, + expand_if_false: str | None, + expand_if_equal: VariableWithValueInfo | None, + type_name: Literal["flag_group"], +) -> record[{ + "flags": list[str], + "flag_groups": list[FlagGroupInfo], + "iterate_over": str | None, + "expand_if_available": str | None, + "expand_if_not_available": str | None, + "expand_if_true": str | None, + "expand_if_false": str | None, + "expand_if_equal": VariableWithValueInfo | None, + "type_name": Literal["flag_group"], +}]: ... + +def FlagSetInfo( + *, + actions: list[str], + with_features: list[WithFeatureSetInfo], + flag_groups: list[FlagGroupInfo], + type_name: Literal["flag_set"], +) -> record[{ + "actions": list[str], + "with_features": list[WithFeatureSetInfo], + "flag_groups": list[FlagGroupInfo], + "type_name": Literal["flag_set"], +}]: ... + +def FeatureInfo( + *, + name: str, + enabled: bool, + flag_sets: list[FlagSetInfo], + env_sets: list[EnvSetInfo], + requires: list[FeatureSetInfo], + implies: list[str], + provides: list[str], + type_name: Literal["feature"], +) -> record[{ + "name": str, + "enabled": bool, + "flag_sets": list[FlagSetInfo], + "env_sets": list[EnvSetInfo], + "requires": list[FeatureSetInfo], + "implies": list[str], + "provides": list[str], + "type_name": Literal["feature"], +}]: ... + +def ToolPathInfo( + *, + name: str, + path: str, + type_name: Literal["tool_path"], +) -> record[{ + "name": str, + "path": str, + "type_name": Literal["tool_path"], +}]: ... + +def ToolInfo( + *, + path: str | None, + tool: File | None, + with_features: list[WithFeatureSetInfo], + execution_requirements: list[str], + type_name: Literal["tool"], +) -> record[{ + "path": str | None, + "tool": File | None, + "with_features": list[WithFeatureSetInfo], + "execution_requirements": list[str], + "type_name": Literal["tool"], +}]: ... + +def ActionConfigInfo( + *, + action_name: str, + enabled: bool, + tools: list[ToolInfo], + flag_sets: list[FlagSetInfo], + implies: list[str], + type_name: Literal["action_config"], + config_name: str = ..., +) -> record[{ + "action_name": str, + "enabled": bool, + "tools": list[ToolInfo], + "flag_sets": list[FlagSetInfo], + "implies": list[str], + "type_name": Literal["action_config"], +}, ...]: ... + +def ArtifactNamePatternInfo( + *, + category_name: str, + prefix: str | None, + extension: str | None, + type_name: Literal["artifact_name_pattern"], +) -> record[{ + "category_name": str, + "prefix": str | None, + "extension": str | None, + "type_name": Literal["artifact_name_pattern"], +}]: ... diff --git a/cc/common/BUILD b/cc/common/BUILD index 97755ad6e..db5d44f45 100644 --- a/cc/common/BUILD +++ b/cc/common/BUILD @@ -34,13 +34,13 @@ bzl_library( "debug_package_info.bzl", "objc_info.bzl", "semantics.bzl", - "visibility.bzl", ], visibility = ["//visibility:public"], deps = [ ":feature_names_bzl", "//cc/private:cc_internal_bzl", "//cc/private:paths_bzl", + "//cc/private:visibility_bzl", "//cc/private/rules_impl:native_cc_common_bzl", "@bazel_skylib//lib:paths", "@cc_compatibility_proxy//:symbols_bzl", @@ -54,9 +54,9 @@ bzl_library( deps = [ ":cc_helper_internal_bzl", ":common", - ":visibility_bzl", "//cc:find_cc_toolchain_bzl", "//cc/private:paths_bzl", + "//cc/private:visibility_bzl", "//cc/private/rules_impl:objc_common", ], ) @@ -93,17 +93,11 @@ bzl_library( visibility = ["//visibility:public"], deps = [ ":cc_helper_bzl", - ":visibility_bzl", "//cc:find_cc_toolchain_bzl", + "//cc/private:visibility_bzl", ], ) -bzl_library( - name = "visibility_bzl", - srcs = ["visibility.bzl"], - visibility = ["//visibility:private"], -) - filegroup( name = "srcs", srcs = glob([ @@ -126,7 +120,6 @@ filegroup( exclude = [ "cc_helper.bzl", "cc_debug_helper.bzl", - "visibility.bzl", "semantics.bzl", "cc_helper_internal.bzl", "cc_shared_library_info.bzl", diff --git a/cc/common/cc_debug_helper.bzl b/cc/common/cc_debug_helper.bzl index 7caf81c80..d740fa8a2 100644 --- a/cc/common/cc_debug_helper.bzl +++ b/cc/common/cc_debug_helper.bzl @@ -15,11 +15,11 @@ load("//cc:action_names.bzl", "ACTION_NAMES") load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE") +load("//cc/private:visibility.bzl", "PUBLIC_IF_NOT_GOOGLE") load(":cc_common.bzl", "cc_common") load(":cc_helper.bzl", "linker_mode") -load(":visibility.bzl", "INTERNAL_VISIBILITY") -visibility(INTERNAL_VISIBILITY) +visibility(PUBLIC_IF_NOT_GOOGLE) def create_debug_packager_actions( ctx, diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index c849c6c46..d77c5a6dd 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -17,6 +17,7 @@ load("//cc:action_names.bzl", "ACTION_NAMES") load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE") load("//cc/common:feature_names.bzl", "feature_names") load("//cc/private:paths.bzl", "is_path_absolute") +load("//cc/private:visibility.bzl", "PUBLIC_IF_NOT_GOOGLE") load("//cc/private/rules_impl:objc_common.bzl", "objc_common") load(":cc_common.bzl", "cc_common") load( @@ -35,9 +36,8 @@ load( _should_stamp = "should_stamp", ) load(":cc_info.bzl", "CcInfo") -load(":visibility.bzl", "INTERNAL_VISIBILITY") -visibility(INTERNAL_VISIBILITY) +visibility(PUBLIC_IF_NOT_GOOGLE) linker_mode = struct( LINKING_DYNAMIC = "dynamic_linking_mode", @@ -58,6 +58,7 @@ _PIC_ARCHIVE_EXTENSIONS = tuple(extensions.PIC_ARCHIVE) _ARCHIVE_EXTENSIONS = tuple(extensions.ARCHIVE) _ALWAYSLINK_PIC_LIBRARY_EXTENSIONS = tuple(extensions.ALWAYSLINK_PIC_LIBRARY) _ALWAYSLINK_LIBRARY_EXTENSIONS = tuple(extensions.ALWAYSLINK_LIBRARY) +_DISALLOWED_HDRS_EXTENSIONS = {extension: True for extension in extensions.DISALLOWED_HDRS_FILES} def _rule_error(msg): fail(msg) @@ -240,6 +241,21 @@ def _collect_library_hidden_top_level_artifacts( return depset(transitive = artifacts_to_force_builder) +def _create_dynamic_libraries_copy_actions(ctx, binary, dynamic_libraries_for_runtime): + result = [] + for lib in depset(dynamic_libraries_for_runtime).to_list(): + # If the binary and the DLL are not in the same directory, copy the DLL + # to the binary's directory. + if lib.dirname != binary.dirname: + copy = ctx.actions.declare_file(lib.basename, sibling = binary) + ctx.actions.symlink(output = copy, target_file = lib, progress_message = "Copying Execution Dynamic Library") + result.append(copy) + else: + # If the library is already in the same directory as the binary, we don't need to copy it, + # but we still add it to the result. + result.append(lib) + return depset(result) + def _get_dynamic_libraries_for_runtime(cc_linking_context, linking_statically): libraries = [] for linker_input in cc_linking_context.linker_inputs.to_list(): @@ -363,9 +379,13 @@ def _build_precompiled_files(ctx): ) def _check_file_extension(file, allowed_extensions, allow_versioned_shared_libraries): - extension = "." + file.extension - return extension in allowed_extensions or \ - (allow_versioned_shared_libraries and is_versioned_shared_library_extension_valid(file.path)) + extension = file.extension + + # The final extension of a versioned .so or .dylib starts with a digit. + return "." + extension in allowed_extensions or \ + (allow_versioned_shared_libraries and len(extension) > 0 and + extension[0].isdigit() and + is_versioned_shared_library_extension_valid(file.path)) def _check_file_extensions(attr_values, allowed_extensions, attr_name, label, rule_name, allow_versioned_shared_libraries): for attr_value in attr_values: @@ -908,11 +928,21 @@ def _expand_make_variables_for_copts(ctx, tokenization, unexpanded_tokens, addit tokens.append(_expand(ctx, token, additional_make_variable_substitutions, targets = targets)) return tokens +def _matches_disallowed_copt(flag, disallowed_flags): + """Returns True if flag equals a disallowed flag or has a disallowed prefix.""" + for disallowed in disallowed_flags: + if flag == disallowed or ( + disallowed.endswith("=") and flag.startswith(disallowed) + ): + return True + return False + def _verify_no_disallowed_copts( ctx, copts, attr_name, - disallowed_copts_infos = []): + disallowed_copts_infos = [], + command_line_copts = None): """Fails the build if copts contain any disallowed compiler flags.""" if not disallowed_copts_infos: return @@ -922,49 +952,72 @@ def _verify_no_disallowed_copts( if not hasattr(info, "flags") or not info.flags: continue - # Check if target is exempt under this policy's allowlist - allowlist = getattr(info, "allowlist", None) - if allowlist != None and allowlist.contains(ctx.label): + # Pair each set of flags with a description of where it came from. + flag_sources = [("attribute '{}'".format(attr_name), copts)] + if getattr(info, "verify_command_line", False) and command_line_copts: + flag_sources.extend([ + ("'--copt' flag", command_line_copts.copts), + ("'--cxxopt' flag", command_line_copts.cxxopts), + ("'--conlyopt' flag", command_line_copts.conlyopts), + ]) + + matches = [ + (flag, source) + for source, flags in flag_sources + for flag in flags + if _matches_disallowed_copt(flag, info.flags) + ] + if not matches: continue - allowlist_target_label = getattr(info, "allowlist_target_label", None) - error_message = getattr(info, "error_message", None) + # Check the allowlists only after a match: package allowlist lookups + # scan "//pkg/..." entries linearly, and most targets use no disallowed + # flags. + package_allowlist = getattr(info, "package_allowlist", getattr(info, "allowlist", None)) + if package_allowlist != None and package_allowlist.contains(ctx.label): + continue - # Check each expanded flag against disallowed rules - for flag in copts: - for disallowed in info.flags: - if flag == disallowed or ( - disallowed.endswith("=") and flag.startswith(disallowed) - ): - violations.append((flag, allowlist_target_label, error_message)) + target_allowlist = getattr(info, "target_allowlist", None) + + if target_allowlist != None and ( + str(ctx.label) in target_allowlist or + str(ctx.label).lstrip("@") in target_allowlist + ): + continue + + policy_label = getattr(info, "policy_label", getattr(info, "allowlist_target_label", None)) + error_message = getattr(info, "error_message", None) + for flag, source in matches: + violations.append((flag, policy_label, error_message, source)) if violations: formatted_violations = [] - for flag, allowlist_target_label, error_message in violations: - allowlist_clause = ( - " (target is not in the allowlist '{}')".format( - allowlist_target_label, - ) if allowlist_target_label else "" + for flag, policy_label, error_message, source in violations: + policy_clause = ( + " disallowed by '{}'".format( + policy_label, + ) if policy_label else "" ) error_clause = ( ": {}".format(error_message) if error_message else "" ) formatted_violations.append( - "- Flag '{}'{}{}".format( + "- Flag '{}' in {}{}{}".format( flag, - allowlist_clause, + source, + policy_clause, error_clause, ), ) fail( """ -[DISALLOWED COPTS ERROR] Target '{}' specifies disallowed compiler flag(s) in attribute '{}': +[DISALLOWED COPTS ERROR] Target '{}' specifies disallowed compiler flag(s): {} """.format( ctx.label, - attr_name, "\n ".join(formatted_violations), ), + stack_trace = False, ) def _get_copts( @@ -980,8 +1033,19 @@ def _get_copts( requested_features = ctx.features attribute_copts = getattr(ctx.attr, attr) - # If we wanted to include command-line specified flags, we could add - # ctx.fragments.cpp.copts (and cxxopts/conlyopts) here. + # Only read the command-line options when a policy asks for them; this runs + # for every C++ target, once per copts-like attribute. + command_line_copts = None + for info in disallowed_copts_infos: + if getattr(info, "verify_command_line", False): + cpp_fragment = ctx.fragments.cpp + command_line_copts = struct( + copts = cpp_fragment.copts, + cxxopts = cpp_fragment.cxxopts, + conlyopts = cpp_fragment.conlyopts, + ) + break + tokenization = not (feature_configuration.is_enabled(feature_names.NO_COPTS_TOKENIZATION) or feature_names.NO_COPTS_TOKENIZATION in requested_features) expanded_attribute_copts = _expand_make_variables_for_copts(ctx, tokenization, attribute_copts, additional_make_variable_substitutions) @@ -990,6 +1054,7 @@ def _get_copts( expanded_attribute_copts, attr, disallowed_copts_infos = disallowed_copts_infos, + command_line_copts = command_line_copts, ) return expanded_attribute_copts @@ -1033,12 +1098,6 @@ def _defines(ctx, additional_make_variable_substitutions): def _local_defines(ctx, additional_make_variable_substitutions): return _defines_attribute(ctx, additional_make_variable_substitutions, "local_defines", getattr(ctx.attr, "additional_compiler_inputs", [])) -def _map_to_list(m): - result = [] - for k, v in m.items(): - result.append((k, v)) - return result - def _calculate_artifact_label_map(attr_list, attr_name): """ Converts a label_list attribute into a list of (Artifact, Label) tuples. @@ -1066,13 +1125,13 @@ def _get_srcs(ctx): if not hasattr(ctx.attr, "srcs"): return [] artifact_label_map = _calculate_artifact_label_map(ctx.attr.srcs, "srcs") - return _map_to_list(artifact_label_map) + return artifact_label_map.items() def _get_cpp_module_interfaces(ctx): if not hasattr(ctx.attr, "module_interfaces"): return [] artifact_label_map = _calculate_artifact_label_map(ctx.attr.module_interfaces, "module_interfaces") - return _map_to_list(artifact_label_map) + return artifact_label_map.items() # Returns a list of (Artifact, Label) tuples. Each tuple represents an input source # file and the label of the rule that generates it (or the label of the source file itself if it @@ -1086,7 +1145,7 @@ def _get_private_hdrs(ctx): for artifact in src[DefaultInfo].files.to_list(): if "." + artifact.extension in extensions.CC_HEADER: artifact_label_map[artifact] = src.label - return _map_to_list(artifact_label_map) + return artifact_label_map.items() # Returns the files from headers and does some checks. def _get_public_hdrs(ctx): @@ -1096,10 +1155,10 @@ def _get_public_hdrs(ctx): for hdr in ctx.attr.hdrs: if DefaultInfo in hdr: for artifact in hdr[DefaultInfo].files.to_list(): - if _check_file_extension(artifact, extensions.DISALLOWED_HDRS_FILES, True): + if _check_file_extension(artifact, _DISALLOWED_HDRS_EXTENSIONS, True): continue artifact_label_map[artifact] = hdr.label - return _map_to_list(artifact_label_map) + return artifact_label_map.items() def _linkopts(ctx, additional_make_variable_substitutions, cc_toolchain): linkopts = getattr(ctx.attr, "linkopts", []) @@ -1226,6 +1285,7 @@ cc_helper = struct( get_coverage_environment = _get_coverage_environment, create_cc_instrumented_files_info = _create_cc_instrumented_files_info, get_dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime, + create_dynamic_libraries_copy_actions = _create_dynamic_libraries_copy_actions, build_output_groups_for_emitting_compile_providers = _build_output_groups_for_emitting_compile_providers, merge_cc_debug_contexts = _merge_cc_debug_contexts, get_providers = _get_providers, diff --git a/cc/common/cc_helper_internal.bzl b/cc/common/cc_helper_internal.bzl index 57922e98d..318049ad2 100644 --- a/cc/common/cc_helper_internal.bzl +++ b/cc/common/cc_helper_internal.bzl @@ -19,8 +19,8 @@ Only use those within C++ implementation. The others need to go through cc_commo load("@bazel_skylib//lib:paths.bzl", "paths") load("//cc/common:feature_names.bzl", "feature_names") -load("//cc/common:visibility.bzl", _CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES = "CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES", _PRIVATE_STARLARKIFICATION_ALLOWLIST = "PRIVATE_STARLARKIFICATION_ALLOWLIST", _check_private_api = "check_private_api", _wrap_with_check_private_api = "wrap_with_check_private_api") load("//cc/private:paths.bzl", "is_path_absolute") +load("//cc/private:visibility.bzl", _CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES = "CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES", _PRIVATE_STARLARKIFICATION_ALLOWLIST = "PRIVATE_STARLARKIFICATION_ALLOWLIST", _check_private_api = "check_private_api", _wrap_with_check_private_api = "wrap_with_check_private_api") check_private_api = _check_private_api wrap_with_check_private_api = _wrap_with_check_private_api diff --git a/cc/coverage/collect_cc_coverage.sh b/cc/coverage/collect_cc_coverage.sh index 577310932..44c929d6d 100755 --- a/cc/coverage/collect_cc_coverage.sh +++ b/cc/coverage/collect_cc_coverage.sh @@ -76,18 +76,18 @@ function llvm_coverage_lcov() { "${LLVM_PROFDATA}" merge -output "${output_file}.data" \ "${COVERAGE_DIR}"/*.profraw - local object_param="" + local object_params=() while read -r line; do if [[ ${line: -24} == "runtime_objects_list.txt" ]]; then while read -r line_runtime_object; do - object_param+=" -object ${line_runtime_object}" + object_params+=(-object "${line_runtime_object}") done < "${line}" fi done < "${COVERAGE_MANIFEST}" "${LLVM_COV}" export -instr-profile "${output_file}.data" -format=lcov \ -ignore-filename-regex='^/tmp/.+' \ - ${object_param} | sed 's#/proc/self/cwd/##' > "${output_file}" + "${object_params[@]}" | sed 's#/proc/self/cwd/##' > "${output_file}" } function llvm_coverage_profdata() { diff --git a/cc/extensions.bzl b/cc/extensions.bzl index 2f3078a73..1356faf35 100644 --- a/cc/extensions.bzl +++ b/cc/extensions.bzl @@ -19,7 +19,9 @@ load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_features//private:util.bzl", _bazel_version_ge = "ge") load("//cc/private/toolchain:cc_configure.bzl", "cc_autoconf", "cc_autoconf_toolchains") +# TODO(https://github.com/bazelbuild/bazel/issues/32020): use a source annotation once Bazel exposes these context and metadata types. def _cc_configure_extension_impl(ctx): + # type: (...) -> extension_metadata | None cc_autoconf_toolchains(name = "local_config_cc_toolchains") cc_autoconf(name = "local_config_cc") if bazel_features.external_deps.extension_metadata_has_reproducible: @@ -30,7 +32,9 @@ def _cc_configure_extension_impl(ctx): cc_configure_extension = module_extension(implementation = _cc_configure_extension_impl) def _compatibility_proxy_repo_impl(rctx): - if _bazel_version_ge("9.0.0-pre.20250911"): + # type: (...) -> repo_metadata | None + # TODO(https://github.com/bazelbuild/rules_cc/pull/960): use the typed public feature flag. + if _bazel_version_ge("9.0.0-pre.20250911"): # ty: ignore[non-static-type] rctx.file( "BUILD", """ @@ -181,9 +185,11 @@ _compatibility_proxy_repo_rule = repository_rule( ) def compatibility_proxy_repo(): + # type: () -> None _compatibility_proxy_repo_rule(name = "cc_compatibility_proxy") def _compat_proxy_impl(module_ctx): + # type: (...) -> extension_metadata | None compatibility_proxy_repo() # module_ctx.extension_metadata has the paramater `reproducible` as of Bazel 7.1.0. We can't diff --git a/cc/private/BUILD b/cc/private/BUILD index 9959fecee..14f2af0d3 100644 --- a/cc/private/BUILD +++ b/cc/private/BUILD @@ -14,6 +14,8 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +exports_files(["objc_info.bzli"]) + filegroup( name = "bzl_srcs", srcs = glob([ @@ -34,6 +36,7 @@ filegroup( "**/BUILD", "*.bzl", ]) + [ + "objc_info.bzli", "//cc/private/bazel7:BUILD", "//cc/private/compile:srcs", "//cc/private/link:srcs", @@ -124,3 +127,9 @@ bzl_library( srcs = ["objc_info.bzl"], visibility = ["//cc:__subpackages__"], ) + +bzl_library( + name = "visibility_bzl", + srcs = ["visibility.bzl"], + visibility = ["//cc:__subpackages__"], +) diff --git a/cc/private/cc_common.bzl b/cc/private/cc_common.bzl index 8a289e3c7..ea80a731a 100644 --- a/cc/private/cc_common.bzl +++ b/cc/private/cc_common.bzl @@ -18,6 +18,7 @@ load( _CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES = "CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES", _PRIVATE_STARLARKIFICATION_ALLOWLIST = "PRIVATE_STARLARKIFICATION_ALLOWLIST", ) +load("//cc/common:semantics.bzl", _starlark_cc_semantics = "semantics") load("//cc/private:cc_info.bzl", "CcNativeLibraryInfo", "create_compilation_context", "create_debug_context", "create_linking_context", "create_module_map", "merge_cc_infos", "merge_compilation_contexts", "merge_debug_context", "merge_linking_contexts") load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") load("//cc/private:cc_launcher_info.bzl", "CcLauncherInfo") @@ -170,7 +171,7 @@ def _get_environment_variables(*, feature_configuration, action_name, variables) return _cc_common_internal.get_environment_variables(feature_configuration = feature_configuration, action_name = action_name, variables = variables) def _empty_variables(): - return _cc_common_internal.empty_variables() + return _cc_internal.cc_toolchain_variables(vars = {}) def _create_library_to_link( *, @@ -251,7 +252,9 @@ def _create_compilation_context( system_includes = None, includes = None, quote_includes = None, - framework_includes = None, + # TODO(https://github.com/bazelbuild/bazel/issues/32021): make this an + # annotation when supported Bazel versions expose element-typed depsets. + framework_includes = None, # type: depset[str] | None defines = None, local_defines = None, direct_textual_headers = [], @@ -637,19 +640,25 @@ def _create_compile_action( action_name, compilation_context, additional_inputs = None, - additional_outputs = []): + additional_outputs = [], + use_pic = False): _cc_internal.check_private_api(allowlist = _CREATE_COMPILE_ACTION_API_ALLOWLISTED_PACKAGES) - return _cc_common_internal.create_compile_action( - actions = actions, + ctx = _cc_internal.actions2ctx_cheat(actions) + return _cc_internal.create_cc_compile_action( + action_construction_context = ctx, + cc_compilation_context = compilation_context, cc_toolchain = cc_toolchain, + configuration = ctx.configuration, feature_configuration = feature_configuration, - source_file = source_file, + source = source_file, + additional_compilation_inputs_set = additional_inputs, output_file = output_file, - variables = variables, + use_pic = use_pic, + compile_build_variables = variables, action_name = action_name, - compilation_context = compilation_context, - additional_inputs = additional_inputs, additional_outputs = additional_outputs, + needs_include_validation = True, + toolchain_type = _starlark_cc_semantics.toolchain, ) def _implementation_deps_allowed_by_allowlist(*, ctx): diff --git a/cc/private/cc_info.bzl b/cc/private/cc_info.bzl index 6b3dcbeda..7d6a5999f 100644 --- a/cc/private/cc_info.bzl +++ b/cc/private/cc_info.bzl @@ -18,8 +18,8 @@ This file is extremely widely loaded, do not load others unless absolutely neces """ load("@bazel_skylib//lib:paths.bzl", "paths") -load("//cc/common:visibility.bzl", "check_private_api") load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") +load("//cc/private:visibility.bzl", "check_private_api") load("//cc/private/link:create_extra_link_time_library.bzl", "create_extra_link_time_libraries", "merge_extra_link_time_libraries") CcCompilationContextInfo = provider( @@ -320,7 +320,7 @@ def merge_debug_context(debug_contexts = []): def _create_cc_info( *, - compilation_context = None, + compilation_context = None, # type: CcCompilationContextInfo | None linking_context = None, debug_context = None, cc_native_library_info = None): diff --git a/cc/private/compile/compile.bzl b/cc/private/compile/compile.bzl index af93e1eb6..2643b2aa3 100644 --- a/cc/private/compile/compile.bzl +++ b/cc/private/compile/compile.bzl @@ -875,8 +875,12 @@ def _create_cc_compile_actions_with_cpp20_module_helper( all_module_files = depset(direct_module_files, transitive = [transitive_module_files]) for cpp_source in compilation_unit_sources.values(): source_artifact = cpp_source.file - output_name = output_name_map[source_artifact] + source_type = cpp_source.type source_label = cpp_source.label + if not _cc_internal.is_tree_artifact(source_artifact) and source_type == CPP_SOURCE_TYPE_HEADER: + continue + + output_name = output_name_map[source_artifact] bitcode_output = feature_configuration.is_enabled(feature_names.THIN_LTO) and (("." + source_artifact.extension) in LTO_SOURCE_EXTENSIONS) if use_pic: diff --git a/cc/private/compile/compile_build_variables.bzl b/cc/private/compile/compile_build_variables.bzl index 34500702b..5091c8a52 100644 --- a/cc/private/compile/compile_build_variables.bzl +++ b/cc/private/compile/compile_build_variables.bzl @@ -108,7 +108,9 @@ def create_compile_variables( add_legacy_cxx_options = False, # buildifier: disable=unused-variable variables_extension = {}, strip_opts = _UNBOUND, - input_file = _UNBOUND): + input_file = _UNBOUND, + cpp_module_map = None, + direct_module_maps = depset()): """Returns variables used for compilation actions. Args: @@ -138,6 +140,9 @@ def create_compile_variables( variables_extension: A dictionary of additional variables used by compile actions. strip_opts: (Private API) input_file: (Private API) + cpp_module_map: Optional CppModuleMap of the target being compiled, used to populate + module_name and module_map_file build variables when module_maps is enabled. + direct_module_maps: Depset of direct dependency module map files. Returns: (CcToolchainVariables) common compile build variables @@ -167,6 +172,8 @@ def create_compile_variables( system_include_dirs = system_include_directories or depset(), framework_include_dirs = framework_include_directories or depset(), defines = preprocessor_defines or depset(), + cpp_module_map = cpp_module_map if feature_configuration.is_enabled(feature_names.MODULE_MAPS) else None, + direct_module_maps = direct_module_maps, ) additional_build_variables = {} @@ -479,17 +486,25 @@ def _setup_fdo_build_variables( if (branch_fdo_profile and auxiliary_fdo_inputs_list and - ( - feature_configuration.is_enabled(feature_names.AUTOFDO) or - feature_configuration.is_enabled(feature_names.XBINARYFDO) or - ( - feature_configuration.is_enabled(feature_names.FDO_OPTIMIZE) and - branch_fdo_profile.branch_fdo_mode in ("llvm_fdo", "llvm_cs_fdo") - ) - )): + _uses_branch_fdo_profile(branch_fdo_profile.branch_fdo_mode, feature_configuration)): variables["fdo_profile_path"] = branch_fdo_profile.profile_artifact.path return variables +def _uses_branch_fdo_profile(branch_fdo_mode, feature_configuration): + """Returns whether the enabled features consume the branch FDO profile. + + The profile is only passed to compile actions (as `fdo_profile_path` and as + an input) when the toolchain feature that reads it is enabled. + """ + if feature_configuration.is_enabled(feature_names.AUTOFDO): + return True + if feature_configuration.is_enabled(feature_names.XBINARYFDO): + return True + if (feature_configuration.is_enabled(feature_names.FDO_OPTIMIZE) and + branch_fdo_mode in ("llvm_fdo", "llvm_cs_fdo")): + return True + return False + def _get_auxiliary_fdo_inputs_list( cc_toolchain, fdo_context, @@ -517,8 +532,10 @@ def _get_auxiliary_fdo_inputs_list( branch_fdo_profile = getattr(fdo_context, "branch_fdo_profile", None) - # If --fdo_optimize was not specified, we don't have any additional inputs. - if branch_fdo_profile: + # Only add the profile when the enabled features read it, matching the + # condition under which `fdo_profile_path` is set. + if (branch_fdo_profile and + _uses_branch_fdo_profile(branch_fdo_profile.branch_fdo_mode, feature_configuration)): auxiliary_inputs.append(branch_fdo_profile.profile_artifact) return auxiliary_inputs diff --git a/cc/private/link/cc_linking_helper.bzl b/cc/private/link/cc_linking_helper.bzl index 808a34f42..42746b195 100644 --- a/cc/private/link/cc_linking_helper.bzl +++ b/cc/private/link/cc_linking_helper.bzl @@ -590,9 +590,9 @@ def _create_no_pic_and_pic_static_libs_actions( actions, name, static_link_type, - False, # use_pic - cc_toolchain, - link_action_kwargs, + use_pic = False, + cc_toolchain = cc_toolchain, + link_action_kwargs = link_action_kwargs, ) pic_library_to_link = {} @@ -609,9 +609,9 @@ def _create_no_pic_and_pic_static_libs_actions( actions, name, link_target_type_used_for_naming, - True, # use_pic - cc_toolchain, - link_action_kwargs, + use_pic = True, + cc_toolchain = cc_toolchain, + link_action_kwargs = link_action_kwargs, ) return no_pic_library_to_link | pic_library_to_link diff --git a/cc/private/link/cpp_link_action.bzl b/cc/private/link/cpp_link_action.bzl index 5e48f5632..3f3569ef1 100644 --- a/cc/private/link/cpp_link_action.bzl +++ b/cc/private/link/cpp_link_action.bzl @@ -212,31 +212,31 @@ def link_action( stamping, feature_configuration, cc_toolchain, - "Linking %{output}", # progress_message + progress_message = "Linking %{output}", # Inputs: - object_file_inputs, - non_code_inputs, - libraries_to_link, - linkstamp_map, - linkstamp_object_artifacts, - linkstamp_object_file_inputs, - user_link_flags, + object_file_inputs = object_file_inputs, + non_code_inputs = non_code_inputs, + libraries_to_link = libraries_to_link, + linkstamp_map = linkstamp_map, + linkstamp_object_artifacts = linkstamp_object_artifacts, + linkstamp_object_file_inputs = linkstamp_object_file_inputs, + user_link_flags = user_link_flags, # Custom user input files and variables: - additional_linker_inputs, - build_variables, + additional_linker_inputs = additional_linker_inputs, + additional_build_variables = build_variables, # Outputs: - output, - interface_output, - dynamic_library_solib_symlink_output, - action_outputs, + output = output, + interface_output = interface_output, + dynamic_library_solib_symlink_output = dynamic_library_solib_symlink_output, + action_outputs = action_outputs, # Originating from private APIs: - use_test_only_flags, - whole_archive, - native_deps, - additional_linkstamp_defines, + use_test_only_flags = use_test_only_flags, + whole_archive = whole_archive, + native_deps = native_deps, + additional_linkstamp_defines = additional_linkstamp_defines, # LTO: - lto_mapping, - allow_lto_indexing, + lto_mapping = lto_mapping, + allow_lto_indexing = allow_lto_indexing, ) return output_library, interface_output_library diff --git a/cc/private/link/create_extra_link_time_library.bzl b/cc/private/link/create_extra_link_time_library.bzl index 74165962d..344a83fe8 100644 --- a/cc/private/link/create_extra_link_time_library.bzl +++ b/cc/private/link/create_extra_link_time_library.bzl @@ -22,8 +22,8 @@ This file is extremely widely loaded (due to being used by CcInfo), do not load others unless absolutely necessary. """ -load("//cc/common:visibility.bzl", "check_private_api") load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") +load("//cc/private:visibility.bzl", "check_private_api") # An implementation of ExtraLinkTimeLibrary that uses functions and data passed in from Starlark. @@ -57,16 +57,21 @@ def create_extra_link_time_library(*, build_library_func, **kwargs): Any implementations must be immutable (and therefore thread-safe), because this is passed between rules and accessed in a multi-threaded context. + The callback is part of the key used to merge inputs from dependencies. Pass rule-specific + values through kwargs so dependencies can reuse the same callback. + Args: - build_library_func: A function that takes a rule context, static_mode, for_dynamic_library, - and kwargs, and returns a tuple of (linker_input, runtime_library): `tuple[LinkerInputInfo, - File]`. + build_library_func: A top-level Starlark function that takes a rule context, static_mode, + for_dynamic_library, and kwargs. It returns a struct with linker_input (a depset of linker + inputs), runtime_library (a depset of files), and optional additional_stamp_info. The + top-level requirement is checked when the library is built. **kwargs: Additional fields to pass to the build function. Returns: ExtraLinkTimeLibraryInfo. """ - _cc_internal.check_toplevel(build_library_func) + if type(build_library_func) != "function": + fail("build_library_func must be a Starlark function, got %s" % type(build_library_func)) return ExtraLinkTimeLibraryInfo( build_library_func = build_library_func, # Key to identify the "class" of a StarlarkDefinedLinkTimeLibrary. Uses the build function and @@ -173,6 +178,12 @@ def build_libraries(extra_libraries, ctx, static_mode, for_dynamic_library): transitive_runtime_libraries = [] additional_stamp_infos = [] for library in extra_libraries: + # Merging uses build_library_func as part of the key, so require a + # top-level def with the same identity across dependency analysis. + # Args.add_all normally uses this check to keep closures out of execution; + # here an empty, discarded Args validates the function without invoking it + # or registering an action. We call build_library_func below during analysis. + ctx.actions.args().add_all([], map_each = library.build_library_func) kwargs = {} for key in dir(library): if key not in ["build_library_func", "_key"]: diff --git a/cc/private/link/create_linking_context_from_compilation_outputs.bzl b/cc/private/link/create_linking_context_from_compilation_outputs.bzl index 13e492397..f8f1c6047 100644 --- a/cc/private/link/create_linking_context_from_compilation_outputs.bzl +++ b/cc/private/link/create_linking_context_from_compilation_outputs.bzl @@ -111,11 +111,11 @@ def create_linking_context_from_compilation_outputs( cc_toolchain, compilation_outputs, linking_contexts, - user_link_flags, # linkopts - stamp, - additional_inputs, # additional_linker_inputs - [], # linker_outputs - variables_extension, + linkopts = user_link_flags, + stamp = stamp, + additional_linker_inputs = additional_inputs, + linker_outputs = [], + variables_extension = variables_extension, alwayslink = alwayslink, test_only_target = test_only_target, linked_dll_name_suffix = linked_dll_name_suffix, diff --git a/cc/private/link/link.bzl b/cc/private/link/link.bzl index 822506fb4..993aee8df 100644 --- a/cc/private/link/link.bzl +++ b/cc/private/link/link.bzl @@ -174,21 +174,21 @@ def link( cc_toolchain, compilation_outputs, linking_contexts, - user_link_flags, # linkopts - stamp, - additional_inputs, - additional_outputs, # linker_outputs - variables_extension, - use_test_only_flags, - never_link, # neverlink - test_only_target, - whole_archive, - native_deps, - additional_linkstamp_defines, + linkopts = user_link_flags, + stamp = stamp, + additional_linker_inputs = additional_inputs, + linker_outputs = additional_outputs, + variables_extension = variables_extension, + use_test_only_flags = use_test_only_flags, + neverlink = never_link, + test_only_target = test_only_target, + whole_archive = whole_archive, + native_deps = native_deps, + additional_linkstamp_defines = additional_linkstamp_defines, # alwayslink may be deprecated but we're trying to replicate CcBinary as closely as possible for the moment. # TODO(b/331164666): remove alwayslink, the information is in static_link_type already - always_link, - main_output, # linker_output_artifact + alwayslink = always_link, + linker_output_artifact = main_output, emit_interface_shared_libraries = dynamic_link_type == LINK_TARGET_TYPE.DYNAMIC_LIBRARY and (emit_interface_shared_library or feature_configuration.is_enabled(feature_names.TARGETS_WINDOWS)), diff --git a/cc/private/link/link_build_variables.bzl b/cc/private/link/link_build_variables.bzl index 51c4337d6..2e13c38c5 100644 --- a/cc/private/link/link_build_variables.bzl +++ b/cc/private/link/link_build_variables.bzl @@ -155,16 +155,16 @@ def create_link_variables( vars = setup_common_linking_variables( cc_toolchain, feature_configuration, - [], # libraries_to_link - runtime_library_search_directories, - library_search_directories, - user_link_flags, - param_file, - is_using_linker, - is_linking_dynamic_library, - must_keep_debug, - use_test_only_flags, - is_static_linking_mode, + libraries_to_link = [], + runtime_library_search_directories = runtime_library_search_directories, + library_search_directories = library_search_directories, + user_link_flags = user_link_flags, + param_file = param_file, + is_using_linker = is_using_linker, + is_linking_dynamic_library = is_linking_dynamic_library, + must_keep_debug = must_keep_debug, + use_test_only_flags = use_test_only_flags, + is_static_linking_mode = is_static_linking_mode, ) # output exec path diff --git a/cc/private/link/lto_backends.bzl b/cc/private/link/lto_backends.bzl index 4d9629080..6481a31fb 100644 --- a/cc/private/link/lto_backends.bzl +++ b/cc/private/link/lto_backends.bzl @@ -411,7 +411,7 @@ def create_lto_backend_artifacts( all_bitcode_files, dwo_file, use_pic, - None, # bitcode_file_path + bitcode_file_path = None, ) return LtoBackendArtifactsInfo( diff --git a/cc/private/link/lto_indexing_action.bzl b/cc/private/link/lto_indexing_action.bzl index fd3d7b9d4..1685eb46c 100644 --- a/cc/private/link/lto_indexing_action.bzl +++ b/cc/private/link/lto_indexing_action.bzl @@ -263,14 +263,14 @@ def _lto_indexing_action( finalize_link_action( actions, - "CppLTOIndexing", # mnemonic - action_name, - link_type, - linking_mode, - False, # stamping (we don't have linkstamps, nothing to stamp) - feature_configuration, - cc_toolchain, - "LTO indexing %{output}", # progress_message + mnemonic = "CppLTOIndexing", + action_name = action_name, + link_type = link_type, + linking_mode = linking_mode, + stamping = False, # we don't have linkstamps, nothing to stamp + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + progress_message = "LTO indexing %{output}", # Inputs: object_file_inputs = object_file_inputs, libraries_to_link = libraries_to_link, diff --git a/cc/private/objc_info.bzl b/cc/private/objc_info.bzl index c17981b69..8531ae42b 100644 --- a/cc/private/objc_info.bzl +++ b/cc/private/objc_info.bzl @@ -14,6 +14,7 @@ # LINT.IfChange(forked_exports) """The provider ObjcInfo for ObjC rules. For more context see doc param of provider() call.""" +# TODO(https://github.com/bazelbuild/bazel/issues/32021): use a source annotation once depset element types are supported. def _objcinfo_init( j2objc_library = depset(), module_map = depset(), @@ -21,6 +22,7 @@ def _objcinfo_init( strict_include = depset(), umbrella_header = depset(), providers = []): # List of depended-on ObjcInfo providers + # type: (depset[File], depset[File], depset[File], depset[str], depset[File], Collection[ObjcInfo]) -> dict[{'direct_module_maps': list[File], 'direct_sources': list[File], 'j2objc_library': depset[File], 'module_map': depset[File], 'source': depset[File], 'strict_include': depset[str], 'umbrella_header': depset[File]}] direct_module_maps = module_map.to_list() direct_sources = source.to_list() diff --git a/cc/private/objc_info.bzli b/cc/private/objc_info.bzli new file mode 100644 index 000000000..55be7d23b --- /dev/null +++ b/cc/private/objc_info.bzli @@ -0,0 +1,22 @@ +"""Objective-C compilation and linking information.""" + +# TODO(https://github.com/bazelbuild/bazel/issues/32207): Move the field types into the provider declaration when Bazel can +# describe its result independently of the custom initializer inputs. + +# The provider and initializer need separate declarations; keep their signatures and fields in sync. +def ObjcInfo( + j2objc_library: depset[File] = ..., + module_map: depset[File] = ..., + source: depset[File] = ..., + strict_include: depset[str] = ..., + umbrella_header: depset[File] = ..., + providers: Collection[ObjcInfo] = ..., +) -> record[{ + "direct_module_maps": list[File], + "direct_sources": list[File], + "j2objc_library": depset[File], + "module_map": depset[File], + "source": depset[File], + "strict_include": depset[str], + "umbrella_header": depset[File], +}]: ... diff --git a/cc/private/paths.bzl b/cc/private/paths.bzl index 838f4ec13..60f55d106 100644 --- a/cc/private/paths.bzl +++ b/cc/private/paths.bzl @@ -14,6 +14,7 @@ """Paths utility functions.""" def is_path_absolute(path): + # type: (str) -> bool """Checks whether a path is absolute or not. Note that is does not take the execution platform into account. diff --git a/cc/private/rules_impl/BUILD b/cc/private/rules_impl/BUILD index 1e11a5b36..6f8e395d0 100644 --- a/cc/private/rules_impl/BUILD +++ b/cc/private/rules_impl/BUILD @@ -4,6 +4,8 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 +exports_files(["objc_compilation_context_info.bzli"]) + filegroup( name = "bzl_srcs", srcs = glob(["**/*.bzl"]) + [ @@ -15,6 +17,7 @@ filegroup( name = "srcs", srcs = glob([ "**/*.bzl", + "**/*.bzli", "**/BUILD", ]) + [ "//cc/private/rules_impl/fdo:srcs", diff --git a/cc/private/rules_impl/attrs.bzl b/cc/private/rules_impl/attrs.bzl index c74d42f0d..cd4cbc99c 100644 --- a/cc/private/rules_impl/attrs.bzl +++ b/cc/private/rules_impl/attrs.bzl @@ -301,7 +301,7 @@ Whether to encode build information into the binary. Possible values:
stamp = 1: Always stamp the build information into the binary, even in
- --nostamp builds. This
+ --nostamp builds. This
setting should be avoided, since it potentially kills remote caching for the
binary and any downstream actions that depend on it.
stamp = -1: Embedding of build information is controlled by the
- --[no]stamp flag.
+ --[no]stamp flag.
Stamped binaries are not rebuilt unless their dependencies change.
diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index a329ae28d..152f5f6f0 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -120,7 +120,7 @@ def _add_transitive_info_providers(ctx, cc_toolchain, cpp_config, feature_config cc_toolchain, feature_configuration, ctx, - False, # generate_hidden_top_level_group + generate_hidden_top_level_group = False, ) cc_info = CcInfo( compilation_context = compilation_context, @@ -178,21 +178,6 @@ def _collect_runfiles(ctx, feature_configuration, cc_toolchain, linking_context, return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(builder_transitive_artifacts))), runtime_objects_for_coverage) -def _create_dynamic_libraries_copy_actions(ctx, binary, dynamic_libraries_for_runtime): - result = [] - for lib in dynamic_libraries_for_runtime: - # If the binary and the DLL are not in the same directory, copy the DLL - # to the binary's directory. - if lib.dirname != binary.dirname: - copy = ctx.actions.declare_file(lib.basename, sibling = binary) - ctx.actions.symlink(output = copy, target_file = lib, progress_message = "Copying Execution Dynamic Library") - result.append(copy) - else: - # If the library is already in the same directory as the binary, we don't need to copy it, - # but we still add it to the result. - result.append(lib) - return depset(result) - def _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically): if library_to_link.dynamic_library == None: return None @@ -718,10 +703,15 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): dwp_exec_group = "cpp_dwp", ) explicit_dwp_file = dwp_file + materialize_dwo_files = False if not cc_helper.should_create_per_object_debug_info(feature_configuration, cpp_config): explicit_dwp_file = None elif cc_helper.should_create_test_dwp_for_statically_linked_test(ctx.attr._is_test, linking_mode, cpp_config): files_to_build_list.append(dwp_file) + elif is_dbg_build: + # If fission is enabled, and this is a debug build, and we are not creating a DWP file, + # materialize DWO files into their own output_group. + materialize_dwo_files = True # If the binary is linked dynamically and COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, collect # all the dynamic libraries we need at runtime. Then copy these libraries next to the binary. @@ -731,7 +721,7 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): libraries = [] for linker_input in linker_inputs: libraries.extend(linker_input.libraries) - copied_runtime_dynamic_libraries = _create_dynamic_libraries_copy_actions(ctx, binary, _get_dynamic_libraries_for_runtime(is_static_mode, libraries)) + copied_runtime_dynamic_libraries = cc_helper.create_dynamic_libraries_copy_actions(ctx, binary, _get_dynamic_libraries_for_runtime(is_static_mode, libraries)) # TODO(b/198254254)(bazel-team): Do we need to put original shared libraries (along with # mangled symlinks) into the RunfilesSupport object? It does not seem @@ -785,6 +775,13 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): if linkmap: output_groups["linkmap"] = depset([linkmap]) + if materialize_dwo_files and dwo_files: + output_groups["dwo_files"] = dwo_files + else: + # Some configurations always request dwo_files. + # If we do not create them, set an empty depset to avoid a warning. + output_groups["dwo_files"] = depset() + if cc_linking_outputs_binary_library != None: # For consistency and readability. library_to_link = cc_linking_outputs_binary_library @@ -818,12 +815,14 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): runfiles = runfiles, executable = binary, ) + + # TODO(https://github.com/bazelbuild/bazel/issues/32085): Use Bazel's type. result = [ cc_info, instrumented_files_provider, debug_package_info, OutputGroupInfo(**output_groups), - ] + ] # type: list[ProviderInstance] if cc_launcher_info != None: result.append(cc_launcher_info) return binary_info, result diff --git a/cc/private/rules_impl/cc_library.bzl b/cc/private/rules_impl/cc_library.bzl index 0bbb93094..5b8e7bb43 100755 --- a/cc/private/rules_impl/cc_library.bzl +++ b/cc/private/rules_impl/cc_library.bzl @@ -394,7 +394,7 @@ themalloc
attribute of cc_binary). Dependency
linkopts take precedence over dependent linkopts (i.e. dependency linkopts
appear later in the command line). Linkopts specified in
---linkopt
+--linkopt
take precedence over rule linkopts.
diff --git a/cc/private/rules_impl/cc_shared_library.bzl b/cc/private/rules_impl/cc_shared_library.bzl index 7df94c7f8..8566333d2 100644 --- a/cc/private/rules_impl/cc_shared_library.bzl +++ b/cc/private/rules_impl/cc_shared_library.bzl @@ -79,31 +79,8 @@ graph_structure_aspect = aspect( implementation = _graph_structure_aspect_impl, ) -def _cc_shared_library_initializer(**kwargs): - """Converts labels in exports_filter into canonical form relative to the current repository. - - This conversion can only be done in a macro as it requires access to the repository mapping of - the repository containing the cc_shared_library target. This mapping is automatically - applied to label attributes, but exports_filter is a list of strings attribute. - """ - if "exports_filter" not in kwargs: - return kwargs - - raw_exports_filter = kwargs["exports_filter"] - if type(raw_exports_filter) != type([]): - # TODO: Also canonicalize labels in selects once macros can operate on them. - # https://github.com/bazelbuild/bazel/issues/14157 - return kwargs - - canonical_exports_filter = [ - str(native.package_relative_label(s)) - for s in raw_exports_filter - ] - return kwargs | {"exports_filter": canonical_exports_filter} - cc_shared_library = rule( implementation = proxy, - initializer = _cc_shared_library_initializer, doc = """
It produces a shared library.
diff --git a/cc/private/rules_impl/cc_shared_library_impl.bzl b/cc/private/rules_impl/cc_shared_library_impl.bzl index b241381c4..58212ae28 100644 --- a/cc/private/rules_impl/cc_shared_library_impl.bzl +++ b/cc/private/rules_impl/cc_shared_library_impl.bzl @@ -17,7 +17,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load("//cc:find_cc_toolchain.bzl", "find_cc_toolchain") load("//cc/common:cc_common.bzl", "cc_common") -load("//cc/common:cc_helper.bzl", "cc_helper") +load("//cc/common:cc_helper.bzl", "artifact_category", "cc_helper") load("//cc/common:cc_info.bzl", "CcInfo") load("//cc/common:cc_shared_library_info.bzl", "CcSharedLibraryInfo") load("//cc/common:feature_names.bzl", "feature_names") @@ -293,9 +293,9 @@ def _check_if_target_under_path(value, pattern): return pattern.package == value.package and pattern.name == value.name -def _check_if_target_should_be_exported_with_filter(target, current_label, exports_filter): +def _check_if_target_should_be_exported_with_filter(ctx, target, exports_filter): for export_filter in exports_filter: - export_filter_label = current_label.relative(export_filter) + export_filter_label = ctx.package_relative_label(export_filter) if _check_if_target_under_path(target, export_filter_label): return True @@ -478,8 +478,8 @@ def _filter_inputs( linker_input, ) if _check_if_target_should_be_exported_with_filter( + ctx, linker_input.owner, - ctx.label, ctx.attr.exports_filter, ): exports[owner] = True @@ -690,12 +690,21 @@ def _cc_shared_library_impl(ctx): if library.objects != None: object_files.extend(library.objects) - generated_def_file = None + # The LIBRARY statement of the DEF file determines the DLL name that the import library + # refers to, so it has to match the actual output file. + if main_output: + dll_name = main_output.basename + else: + dll_name = paths.basename(cc_common.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = artifact_category.DYNAMIC_LIBRARY, + output_name = ctx.label.name, + )) generated_def_file = cc_helper.generate_def_file( ctx, ctx.file._def_parser, object_files, - ctx.label.name, + dll_name, cc_toolchain, feature_configuration, ) @@ -736,6 +745,25 @@ def _cc_shared_library_impl(ctx): for dep in ctx.attr.dynamic_deps: runfiles = runfiles.merge(dep[DefaultInfo].data_runfiles) + if feature_configuration.is_enabled(feature_names.COPY_DYNAMIC_LIBRARIES_TO_BINARY): + libraries_to_copy = [] + copy_linking_contexts = [linking_context] + runtimes_linking_contexts + copy_linking_contexts.append(_create_linker_context([ + dep.linker_input + for dep in merged_cc_shared_library_infos_list + ])) + for context in copy_linking_contexts: + libraries_to_copy.extend(cc_helper.get_dynamic_libraries_for_runtime( + context, + linking_statically = True, + )) + copied_runtime_libraries = cc_helper.create_dynamic_libraries_copy_actions( + ctx, + linking_outputs.library_to_link.dynamic_library, + libraries_to_copy, + ) + runfiles = runfiles.merge(ctx.runfiles(transitive_files = copied_runtime_libraries)) + precompiled_only_dynamic_libraries_runfiles = [] for precompiled_dynamic_library in precompiled_only_dynamic_libraries: # precompiled_dynamic_library.dynamic_library could be None if the library to link just contains diff --git a/cc/private/rules_impl/cc_static_library.bzl b/cc/private/rules_impl/cc_static_library.bzl index 041da2afb..7579307b6 100644 --- a/cc/private/rules_impl/cc_static_library.bzl +++ b/cc/private/rules_impl/cc_static_library.bzl @@ -40,8 +40,8 @@ def _flatten_and_get_objects(linker_inputs): for linker_input in linker_inputs.to_list(): for lib in linker_input.libraries: if lib._contains_objects: - transitive_objects.append(depset(lib.pic_objects)) - transitive_objects.append(depset(lib.objects)) + # Select on variant per library, preferring PIC. + transitive_objects.append(depset(lib.pic_objects or lib.objects)) return depset(transitive = transitive_objects, order = "topological") diff --git a/cc/private/rules_impl/cc_toolchain.bzl b/cc/private/rules_impl/cc_toolchain.bzl index ab55e6b35..7a622b536 100644 --- a/cc/private/rules_impl/cc_toolchain.bzl +++ b/cc/private/rules_impl/cc_toolchain.bzl @@ -356,12 +356,12 @@ The label of the rule providingcc_toolchain_config_info.""",
default = configuration_field(fragment = "cpp", name = "libc_top"),
),
"_interface_library_builder": attr.label(
- default = "@bazel_tools//tools/cpp:interface_library_builder",
+ default = "//cc/private/toolchain:interface_library_builder",
allow_single_file = True,
cfg = "exec",
),
"_link_dynamic_library_tool": attr.label(
- default = "@bazel_tools//tools/cpp:link_dynamic_library",
+ default = "//cc/private/toolchain:link_dynamic_library",
allow_single_file = True,
cfg = "exec",
),
diff --git a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl
index 8d042e291..b0dea3f7f 100644
--- a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl
+++ b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl
@@ -58,17 +58,22 @@ def _additional_make_variables(toolchain_make_vars):
make_vars.pop("CC_FLAGS", None)
return make_vars
-def _compute_tool_paths(toolchain_config_info, crosstool_top_path):
+def _compute_tool_paths(toolchain_config_info, crosstool_top_path, output_directory_prefix):
tool_paths_collector = {}
for tool in toolchain_config_info.tool_paths:
path_str = tool.path
if not paths.is_normalized(path_str):
fail("The include path '" + path_str + "' is not normalized.")
- tool_paths_collector[tool.name] = get_relative_path(crosstool_top_path, path_str)
+ if path_str.startswith(output_directory_prefix):
+ # The path of a file generated by the build, e.g. a compiler wrapper built in the
+ # exec configuration, is already relative to the execution root.
+ tool_paths_collector[tool.name] = path_str
+ else:
+ tool_paths_collector[tool.name] = get_relative_path(crosstool_top_path, path_str)
return tool_paths_collector
-def _resolve_include_dir(target_label, s, sysroot, crosstool_path):
+def _resolve_include_dir(ctx, s, sysroot, crosstool_path):
""" Resolve the given include directory.
If it starts with %sysroot%/, that part is replaced with the actual sysroot.
@@ -97,7 +102,7 @@ def _resolve_include_dir(target_label, s, sysroot, crosstool_path):
# This is necessary to avoid the hard work of parsing,
# and use an already existing API.
- dummy_label = target_label.relative(package + ":dummy_target")
+ dummy_label = ctx.package_relative_label(package + ":dummy_target")
repo_prefix = dummy_label.workspace_root
path_prefix = get_relative_path(repo_prefix, dummy_label.package)
path_start_index = package_end_index + len(_PACKAGE_END)
@@ -150,7 +155,14 @@ def get_cc_toolchain_provider(ctx, attributes):
attributes.cc_toolchain_label.package,
ctx.configuration.is_sibling_repository_layout(),
)
- tool_paths = _compute_tool_paths(toolchain_config_info, tools_directory)
+ bin_dir_path = ctx.bin_dir.path
+ tool_paths = _compute_tool_paths(
+ toolchain_config_info,
+ tools_directory,
+ # The first segment of the bin directory path, e.g. "bazel-out/", is shared by the output
+ # directories of all configurations.
+ bin_dir_path[:bin_dir_path.find("/") + 1],
+ )
toolchain_features = cc_common.cc_toolchain_features(toolchain_config_info = toolchain_config_info, tools_directory = tools_directory)
# action_is_enabled only returns true for action_configs that are in
@@ -222,7 +234,7 @@ def get_cc_toolchain_provider(ctx, attributes):
builtin_include_directories = []
for s in toolchain_config_info.cxx_builtin_include_directories:
- builtin_include_directories.append(_resolve_include_dir(ctx.label, s, sysroot, tools_directory))
+ builtin_include_directories.append(_resolve_include_dir(ctx, s, sysroot, tools_directory))
build_variables_dict = _get_cc_toolchain_vars(ctx.fragments.cpp, sysroot)
build_variables = cc_common.cc_toolchain_variables(vars = build_variables_dict)
diff --git a/cc/private/rules_impl/failing_cc_proto_library.bzl b/cc/private/rules_impl/failing_cc_proto_library.bzl
index 263c8a022..499c8d2cc 100644
--- a/cc/private/rules_impl/failing_cc_proto_library.bzl
+++ b/cc/private/rules_impl/failing_cc_proto_library.bzl
@@ -23,6 +23,7 @@ CC_PROTO_LIBRARY_DEPRECATION = (
)
def _impl(_ctx):
+ # type: (...) -> Never
fail(CC_PROTO_LIBRARY_DEPRECATION)
cc_proto_library = rule(
diff --git a/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl b/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl
index 6696b7b3d..c6e693073 100644
--- a/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl
+++ b/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl
@@ -20,6 +20,7 @@ FdoPrefetchHintsInfo = provider(
)
def _impl(ctx):
+ # type: (...) -> FdoPrefetchHintsInfo
return FdoPrefetchHintsInfo(artifact = ctx.file.profile)
fdo_prefetch_hints = rule(
diff --git a/cc/private/rules_impl/fdo/fdo_profile.bzl b/cc/private/rules_impl/fdo/fdo_profile.bzl
index d16e94dac..f2bfe2937 100644
--- a/cc/private/rules_impl/fdo/fdo_profile.bzl
+++ b/cc/private/rules_impl/fdo/fdo_profile.bzl
@@ -26,6 +26,7 @@ FdoProfileInfo = provider(
)
def _impl(ctx):
+ # type: (...) -> FdoProfileInfo
return FdoProfileInfo(
artifact = ctx.file.profile,
proto_profile_artifact = ctx.file.proto_profile,
diff --git a/cc/private/rules_impl/fdo/memprof_profile.bzl b/cc/private/rules_impl/fdo/memprof_profile.bzl
index 46248613c..3eb12f01a 100644
--- a/cc/private/rules_impl/fdo/memprof_profile.bzl
+++ b/cc/private/rules_impl/fdo/memprof_profile.bzl
@@ -20,6 +20,7 @@ MemProfProfileInfo = provider(
)
def _impl(ctx):
+ # type: (...) -> MemProfProfileInfo
return MemProfProfileInfo(
artifact = ctx.file.profile,
changelist = ctx.attr.changelist,
diff --git a/cc/private/rules_impl/fdo/propeller_optimize.bzl b/cc/private/rules_impl/fdo/propeller_optimize.bzl
index 3e3e8ce62..833aa535c 100644
--- a/cc/private/rules_impl/fdo/propeller_optimize.bzl
+++ b/cc/private/rules_impl/fdo/propeller_optimize.bzl
@@ -20,6 +20,7 @@ PropellerOptimizeInfo = provider(
)
def _impl(ctx):
+ # type: (...) -> PropellerOptimizeInfo
return PropellerOptimizeInfo(
cc_profile = ctx.file.cc_profile,
ld_profile = ctx.file.ld_profile,
diff --git a/cc/private/rules_impl/native_cc_common.bzl b/cc/private/rules_impl/native_cc_common.bzl
index 00bc8a5dc..75f18abcf 100644
--- a/cc/private/rules_impl/native_cc_common.bzl
+++ b/cc/private/rules_impl/native_cc_common.bzl
@@ -14,5 +14,7 @@
#
"""Native implementation of cc_common."""
+# Bazel's API metadata omits private members such as empty_variables and
+# legacy_cc_flags_make_variable_do_not_use.
# buildifier: disable=native-cc-common
-native_cc_common = cc_common
+native_cc_common = cc_common # ty: ignore[non-static-type]
diff --git a/cc/private/rules_impl/objc_compilation_artifacts_info.bzl b/cc/private/rules_impl/objc_compilation_artifacts_info.bzl
index bdb0e77ea..0b72d049f 100644
--- a/cc/private/rules_impl/objc_compilation_artifacts_info.bzl
+++ b/cc/private/rules_impl/objc_compilation_artifacts_info.bzl
@@ -53,12 +53,13 @@ def _compilation_artifacts_init(
# Note: the condition under which we set an archive artifact needs to match the condition for
# which we create the archive in compilation_support.bzl. In particular, if srcs are all
# headers, we don't generate an archive.
+ # TODO(https://github.com/bazelbuild/rules_cc/pull/967): handle omitted sources and artifacts.
if (
non_arc_srcs or
- [s for s in srcs if s.is_directory or
+ [s for s in srcs if s.is_directory or # ty: ignore[not-iterable]
s.extension in COMPILABLE_OR_PRECOMPILED_SRC_EXTENSIONS]
):
- archive = intermediate_artifacts.archive()
+ archive = intermediate_artifacts.archive() # ty: ignore[unresolved-attribute]
else:
archive = None
return {
diff --git a/cc/private/rules_impl/objc_compilation_artifacts_info.bzli b/cc/private/rules_impl/objc_compilation_artifacts_info.bzli
new file mode 100644
index 000000000..2d7748db4
--- /dev/null
+++ b/cc/private/rules_impl/objc_compilation_artifacts_info.bzli
@@ -0,0 +1,47 @@
+# Copyright 2024 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.
+
+"""Types for Objective-C compilation artifacts."""
+
+type _Files = list[File] | tuple[File, ...]
+type _ArchiveFactory = struct[{"archive": Callable[[], File]}, ...]
+# TODO(https://github.com/bazelbuild/bazel/issues/31770): annotate the Objective-C artifact dictionary
+# in Starlark once Bazel supports dictionaries with named, typed keys.
+type _Result = dict[{
+ "srcs": _Files | None,
+ "non_arc_srcs": _Files | None,
+ "additional_hdrs": _Files | None,
+ "archive": File | None,
+}]
+
+def _compilation_artifacts_init(
+ ctx: ctx[object, object] | None = ...,
+ srcs: _Files | None = ...,
+ non_arc_srcs: _Files | None = ...,
+ hdrs: _Files | None = ...,
+ intermediate_artifacts: _ArchiveFactory | None = ...,
+) -> _Result: ...
+
+def CompilationArtifactsInfo(
+ ctx: ctx[object, object] | None = ...,
+ srcs: _Files | None = ...,
+ non_arc_srcs: _Files | None = ...,
+ hdrs: _Files | None = ...,
+ intermediate_artifacts: _ArchiveFactory | None = ...,
+) -> record[{
+ "srcs": _Files | None,
+ "non_arc_srcs": _Files | None,
+ "additional_hdrs": _Files | None,
+ "archive": File | None,
+}]: ...
diff --git a/cc/private/rules_impl/objc_compilation_context_info.bzli b/cc/private/rules_impl/objc_compilation_context_info.bzli
new file mode 100644
index 000000000..9a417c6ce
--- /dev/null
+++ b/cc/private/rules_impl/objc_compilation_context_info.bzli
@@ -0,0 +1,35 @@
+"""Inputs and fields of the Objective-C compilation context provider."""
+
+class _StrictIncludes(Protocol):
+ @property
+ def strict_include(self) -> depset[str]: ...
+
+# TODO(https://github.com/bazelbuild/bazel/issues/31770): annotate the Objective-C provider initializer result
+# in Starlark once Bazel supports dictionaries with named, typed keys.
+type _Fields = dict[{
+ "defines": Sequence[str],
+ "public_hdrs": Sequence[File],
+ "public_textual_hdrs": Collection[File],
+ "private_hdrs": Collection[File],
+ "includes": Sequence[str],
+ "system_includes": Sequence[str],
+ "quote_includes": Sequence[str],
+ "direct_cc_compilation_contexts": Collection[CompilationContext],
+ "cc_compilation_contexts": list[CompilationContext],
+ "implementation_cc_compilation_contexts": Sequence[CompilationContext],
+ "strict_dependency_includes": list[str],
+}]
+
+def _objc_compilation_context_info_init(
+ defines: Sequence[str] = ...,
+ public_hdrs: Sequence[File] = ...,
+ public_textual_hdrs: Collection[File] = ...,
+ private_hdrs: Collection[File] = ...,
+ includes: Sequence[str] = ...,
+ system_includes: Sequence[str] = ...,
+ quote_includes: Sequence[str] = ...,
+ direct_cc_compilation_contexts: Collection[CompilationContext] = ...,
+ cc_compilation_contexts: list[CompilationContext] = ...,
+ implementation_cc_compilation_contexts: Sequence[CompilationContext] = ...,
+ providers: Collection[_StrictIncludes] = ...,
+) -> _Fields: ...
diff --git a/cc/private/rules_impl/objc_compilation_support.bzl b/cc/private/rules_impl/objc_compilation_support.bzl
index 624ee8bfd..46bcd8439 100644
--- a/cc/private/rules_impl/objc_compilation_support.bzl
+++ b/cc/private/rules_impl/objc_compilation_support.bzl
@@ -22,8 +22,6 @@ load(":objc_common.bzl", "objc_common")
load(":objc_compilation_artifacts_info.bzl", "CompilationArtifactsInfo")
load(":objc_intermediate_artifacts.bzl", "create_intermediate_artifacts")
-XcodeVersionInfo = apple_common.XcodeVersionConfig
-
def _build_variable_extensions(ctx, arc_enabled):
extensions = {}
if hasattr(ctx.attr, "pch") and ctx.attr.pch != None:
@@ -282,13 +280,7 @@ def _get_non_arc_srcs(ctx):
if DefaultInfo in src:
for artifact in src[DefaultInfo].files.to_list():
artifact_label_map[artifact] = src.label
- return _map_to_list(artifact_label_map)
-
-def _map_to_list(m):
- result = []
- for k, v in m.items():
- result.append((k, v))
- return result
+ return artifact_label_map.items()
def _cc_compile_and_link(
srcs,
diff --git a/cc/private/toolchain/BUILD b/cc/private/toolchain/BUILD
index abd4e6b9d..753bdab3d 100644
--- a/cc/private/toolchain/BUILD
+++ b/cc/private/toolchain/BUILD
@@ -21,6 +21,11 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
+exports_files([
+ "lib_cc_configure.bzli",
+ "unix_cc_configure.bzli",
+])
+
# It is frequently necessary to constrain platforms based on the cc compiler type.
constraint_setting(name = "cc_compiler")
@@ -58,6 +63,12 @@ cc_library(
name = "malloc",
)
+filegroup(
+ name = "grep-includes",
+ srcs = ["empty.cc"],
+ deprecation = "grep-includes is always empty, you must provide your own.",
+)
+
filegroup(
name = "empty",
srcs = [],
diff --git a/cc/private/toolchain/cc_configure.bzl b/cc/private/toolchain/cc_configure.bzl
index c2bf06aaa..fa28a5d81 100644
--- a/cc/private/toolchain/cc_configure.bzl
+++ b/cc/private/toolchain/cc_configure.bzl
@@ -14,15 +14,14 @@
"""Rules for configuring the C++ toolchain (experimental)."""
load("@bazel_features//:features.bzl", "bazel_features")
-load(
- ":lib_cc_configure.bzl",
- "get_cpu_value",
- "resolve_labels",
-)
+load(":lib_cc_configure.bzl", "copy_file", "get_cpu_value")
load(":unix_cc_configure.bzl", "configure_unix_toolchain")
load(":windows_cc_configure.bzl", "configure_windows_toolchain")
def _should_disable_toolchain(repository_ctx):
+ # type: (repository_ctx) -> bool
+ # TODO(https://github.com/bazelbuild/bazel/issues/32020): make this an
+ # annotation when supported Bazel versions expose a repository context type.
"""Returns true if the toolchain should be disabled based on environment variables."""
env = repository_ctx.os.environ
if env.get("BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN") == "1":
@@ -39,6 +38,7 @@ def _should_disable_toolchain(repository_ctx):
return False
def cc_autoconf_toolchains_impl(repository_ctx):
+ # type: (repository_ctx) -> repo_metadata | None
"""Generate BUILD file with 'toolchain' targets for the local host C++ toolchain.
Args:
@@ -52,13 +52,10 @@ def cc_autoconf_toolchains_impl(repository_ctx):
build_path = "@rules_cc//cc/private/toolchain:BUILD.windows_toolchains.tpl"
else:
build_path = "@rules_cc//cc/private/toolchain:BUILD.toolchains.tpl"
- paths = resolve_labels(repository_ctx, [
- build_path,
- ])
repository_ctx.template(
"BUILD",
- paths[build_path],
+ Label(build_path),
{"%{name}": get_cpu_value(repository_ctx)},
)
else:
@@ -78,6 +75,7 @@ cc_autoconf_toolchains = repository_rule(
)
def cc_autoconf_impl(repository_ctx, overriden_tools = dict()):
+ # type: (repository_ctx, Mapping[str, str | path]) -> repo_metadata | None
"""Generate BUILD file with 'cc_toolchain' targets for the local host C++ toolchain.
Args:
@@ -89,28 +87,18 @@ def cc_autoconf_impl(repository_ctx, overriden_tools = dict()):
"""
cpu_value = get_cpu_value(repository_ctx)
if _should_disable_toolchain(repository_ctx):
- paths = resolve_labels(repository_ctx, [
- "@rules_cc//cc/private/toolchain:BUILD.empty.tpl",
- "@rules_cc//cc/private/toolchain:empty_cc_toolchain_config.bzl",
- ])
- repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:empty_cc_toolchain_config.bzl"], "cc_toolchain_config.bzl")
- repository_ctx.template("BUILD", paths["@rules_cc//cc/private/toolchain:BUILD.empty.tpl"], {
+ copy_file(repository_ctx, Label("@rules_cc//cc/private/toolchain:empty_cc_toolchain_config.bzl"), "cc_toolchain_config.bzl")
+ repository_ctx.template("BUILD", Label("@rules_cc//cc/private/toolchain:BUILD.empty.tpl"), {
"%{cpu}": get_cpu_value(repository_ctx),
})
elif cpu_value == "freebsd" or cpu_value == "openbsd":
- paths = resolve_labels(repository_ctx, [
- "@rules_cc//cc/private/toolchain:BUILD.static.bsd",
- "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl",
- "@rules_cc//cc/private/toolchain:bsd_cc_toolchain_config.bzl",
- ])
-
# This is defaulting to a static crosstool. We should eventually
# autoconfigure this platform too. Theoretically, FreeBSD and OpenBSD
# should be straightforward to add but we cannot run them in a Docker
# container so skipping until we have proper tests for these platforms.
- repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"], "armeabi_cc_toolchain_config.bzl")
- repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:bsd_cc_toolchain_config.bzl"], "cc_toolchain_config.bzl")
- repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:BUILD.static.bsd"], "BUILD")
+ copy_file(repository_ctx, Label("@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"), "armeabi_cc_toolchain_config.bzl")
+ copy_file(repository_ctx, Label("@rules_cc//cc/private/toolchain:bsd_cc_toolchain_config.bzl"), "cc_toolchain_config.bzl")
+ copy_file(repository_ctx, Label("@rules_cc//cc/private/toolchain:BUILD.static.bsd"), "BUILD")
elif cpu_value in ["x64_windows", "arm64_windows"]:
# TODO(ibiryukov): overriden_tools are only supported in configure_unix_toolchain.
# We might want to add that to Windows too(at least for msys toolchain).
@@ -180,6 +168,7 @@ cc_autoconf = repository_rule(
# buildifier: disable=unnamed-macro
def cc_configure():
+ # type: () -> None
"""A C++ configuration rules that generate the crosstool file."""
cc_autoconf_toolchains(name = "local_config_cc_toolchains")
cc_autoconf(name = "local_config_cc")
diff --git a/cc/private/toolchain/lib_cc_configure.bzl b/cc/private/toolchain/lib_cc_configure.bzl
index 464700387..241adae99 100644
--- a/cc/private/toolchain/lib_cc_configure.bzl
+++ b/cc/private/toolchain/lib_cc_configure.bzl
@@ -17,16 +17,8 @@
def resolve_labels(repository_ctx, labels):
"""Resolves a collection of labels to their paths.
- Label resolution can cause the evaluation of Starlark functions to restart.
- For functions with side-effects (like the auto-configuration functions, which
- inspect the system and touch the file system), such restarts are costly.
- We cannot avoid the restarts, but we can minimize their penalty by resolving
- all labels upfront.
-
- Among other things, doing less work on restarts can cut analysis times by
- several seconds and may also prevent tickling kernel conditions that cause
- build failures. See https://github.com/bazelbuild/bazel/issues/5196 for
- more details.
+ Deprecated: This function is no longer used by rules_cc and will be removed
+ in the future. Pass labels to repository_ctx methods directly instead.
Args:
repository_ctx: The context with which to resolve the labels.
@@ -37,14 +29,35 @@ def resolve_labels(repository_ctx, labels):
"""
return dict([(label, repository_ctx.path(Label(label))) for label in labels])
+def copy_file(repository_ctx, src, dest):
+ # type: (repository_ctx, Label, str) -> None
+ """Copies a file into the repository.
+
+ Unlike repository_ctx.symlink, this doesn't require the repository
+ containing the file to be present on disk.
+
+ Args:
+ repository_ctx: The repository context.
+ src: The Label of the file to copy.
+ dest: The path of the copy relative to the repository root.
+ """
+ repository_ctx.template(dest, src, executable = False)
+
+def escape_percent(value):
+ # type: (str) -> str
+ """Escape percent signs in a known string for the Crosstool."""
+ return value.replace("%", "%%")
+
def escape_string(arg):
+ # type: (object) -> str | None
"""Escape percent sign (%) in the string so it can appear in the Crosstool."""
if arg != None:
- return str(arg).replace("%", "%%")
+ return escape_percent(str(arg))
else:
return None
def split_escaped(string, delimiter):
+ # type: (str, str) -> list[str]
"""Split string on the delimiter unless %-escaped.
Examples:
@@ -75,7 +88,7 @@ def split_escaped(string, delimiter):
i = 0
result = []
- accumulator = []
+ accumulator = [] # type: list[str]
length = len(string)
delimiter_length = len(delimiter)
@@ -106,12 +119,14 @@ def split_escaped(string, delimiter):
return result
def auto_configure_fail(msg):
+ # type: (object) -> Never
"""Output failure message when auto configuration fails."""
red = "\033[0;31m"
no_color = "\033[0m"
fail("\n%sAuto-Configuration Error:%s %s\n" % (red, no_color, msg))
def auto_configure_warning(msg):
+ # type: (object) -> None
"""Output warning message during auto configuration."""
yellow = "\033[1;33m"
no_color = "\033[0m"
@@ -137,8 +152,7 @@ def get_env_var(repository_ctx, name, default = None, enable_warning = True):
if enable_warning:
auto_configure_warning("'%s' environment variable is not set, using '%s' as default" % (name, default))
return default
- auto_configure_fail("'%s' environment variable is not set" % name)
- return None
+ return auto_configure_fail("'%s' environment variable is not set" % name)
def which(repository_ctx, cmd, default = None):
"""A wrapper around repository_ctx.which() to provide a fallback value. Doesn't %-escape the value!
@@ -170,8 +184,7 @@ def which_cmd(repository_ctx, cmd, default = None):
if default != None:
auto_configure_warning("Cannot find %s in PATH, using '%s' as default.\nPATH=%s" % (cmd, default, path))
return default
- auto_configure_fail("Cannot find %s in PATH, please make sure %s is installed and add its directory in PATH.\nPATH=%s" % (cmd, cmd, path))
- return str(result)
+ return auto_configure_fail("Cannot find %s in PATH, please make sure %s is installed and add its directory in PATH.\nPATH=%s" % (cmd, cmd, path))
def execute(
repository_ctx,
@@ -223,6 +236,9 @@ def execute(
return stripped_stdout
def get_cpu_value(repository_ctx):
+ # type: (repository_ctx) -> str
+ # TODO(https://github.com/bazelbuild/bazel/issues/32020): make this an
+ # annotation when supported Bazel versions expose a repository context type.
"""Compute the cpu_value based on the OS name. Doesn't %-escape the result!
Args:
@@ -262,26 +278,31 @@ def get_cpu_value(repository_ctx):
return "k8" if arch in ["amd64", "x86_64", "x64"] else "piii"
def is_cc_configure_debug(repository_ctx):
+ # type: (repository_ctx) -> bool
"""Returns True if CC_CONFIGURE_DEBUG is set to 1."""
env = repository_ctx.os.environ
return "CC_CONFIGURE_DEBUG" in env and env["CC_CONFIGURE_DEBUG"] == "1"
def build_flags(flags):
+ # type: (Collection[str]) -> str
"""Convert `flags` to a string of flag fields."""
return "\n".join([" flag: '" + flag + "'" for flag in flags])
def get_starlark_list(values):
+ # type: (Collection[str]) -> str
"""Convert a list of string into a string that can be passed to a rule attribute."""
if not values:
return ""
return "\"" + "\",\n \"".join(values) + "\""
def auto_configure_warning_maybe(repository_ctx, msg):
+ # type: (repository_ctx, object) -> None
"""Output warning message when CC_CONFIGURE_DEBUG is enabled."""
if is_cc_configure_debug(repository_ctx):
auto_configure_warning(msg)
def write_builtin_include_directory_paths(repository_ctx, cc, directories, file_suffix = ""):
+ # type: (repository_ctx, object, Collection[str], str) -> None
"""Generate output file named 'builtin_include_directory_paths' in the root of the repository."""
if get_env_var(repository_ctx, "BAZEL_IGNORE_SYSTEM_HEADERS_VERSIONS", "0", False) == "1":
repository_ctx.file(
diff --git a/cc/private/toolchain/lib_cc_configure.bzli b/cc/private/toolchain/lib_cc_configure.bzli
new file mode 100644
index 000000000..621ec3f45
--- /dev/null
+++ b/cc/private/toolchain/lib_cc_configure.bzli
@@ -0,0 +1,47 @@
+"""Type contracts for C++ toolchain configuration helpers."""
+
+# TODO(https://github.com/bazelbuild/starlark/pull/362): Move get_env_var and which into source when the supported syntax
+# can bind a type parameter for the caller’s default. Comments cannot bind T.
+
+def resolve_labels[T: str | Label](repository_ctx: repository_ctx, labels: Collection[T]) -> dict[T, path]: ...
+
+def get_env_var[T = Never](
+ repository_ctx: repository_ctx,
+ name: str,
+ default: T | None = ...,
+ enable_warning: bool = ...,
+) -> str | T: ...
+
+def which[T = Never](
+ repository_ctx: repository_ctx,
+ cmd: str,
+ default: T | None = ...,
+) -> str | T | None: ...
+
+def which_cmd[T = Never](
+ repository_ctx: repository_ctx,
+ cmd: str,
+ default: T | None = ...,
+) -> str | T: ...
+
+class _EnvironmentSchema[V](TypedDict, extra_items=ReadOnly[V]): ...
+
+type _Environment[K: str, V] = dict[K, V] | _EnvironmentSchema[V]
+
+# The wrapper forwards its environment to Bazel. Derive the accepted value
+# type from the repository context, since it varies across Bazel releases.
+class _Executable[K: str, V](Protocol):
+ def execute(
+ self,
+ __arguments: Sequence[str | Label | path],
+ *,
+ environment: _Environment[K, V] = ...,
+ ) -> exec_result: ...
+
+def execute[K: str = str, V = str](
+ repository_ctx: Intersection[repository_ctx, _Executable[K, V]],
+ command: Sequence[str | Label | path],
+ environment: _Environment[K, V] | None = ...,
+ expect_failure: bool = ...,
+ expect_empty_output: bool = ...,
+) -> str: ...
diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl
index 65ac6ce9d..89615b709 100644
--- a/cc/private/toolchain/unix_cc_configure.bzl
+++ b/cc/private/toolchain/unix_cc_configure.bzl
@@ -17,13 +17,12 @@
load(
":lib_cc_configure.bzl",
"auto_configure_fail",
- "auto_configure_warning",
"auto_configure_warning_maybe",
- "escape_string",
+ "copy_file",
+ "escape_percent",
"execute",
"get_env_var",
"get_starlark_list",
- "resolve_labels",
"split_escaped",
"which",
"write_builtin_include_directory_paths",
@@ -32,11 +31,16 @@ load(
def _uniq(iterable):
"""Remove duplicates from a list."""
- unique_elements = {element: None for element in iterable}
- return unique_elements.keys()
+ return {element: None for element in iterable}.keys()
-def _generate_system_module_map(repository_ctx, dirs, script_path):
- return execute(repository_ctx, [script_path] + dirs)
+def _generate_system_module_map(repository_ctx, dirs):
+ # type: (repository_ctx, list[str]) -> str
+ # The script has to be present on disk to be run.
+ script = "generate_system_module_map.sh"
+ repository_ctx.template(script, Label("@rules_cc//cc/private/toolchain:generate_system_module_map.sh"))
+ module_map = execute(repository_ctx, [repository_ctx.path(script)] + dirs)
+ repository_ctx.delete(script)
+ return module_map
def _prepare_include_path(repo_ctx, path):
"""Resolve include path before outputting it into the crosstool.
@@ -78,7 +82,7 @@ def _get_target_libc(repository_ctx, cc, darwin, compile_opts):
return "macosx"
libc = get_env_var(repository_ctx, "BAZEL_TARGET_LIBC", "", False)
if libc:
- return escape_string(libc)
+ return escape_percent(libc)
# Preprocessing any standard library header defines macros that identify
# most implementations.
@@ -115,33 +119,38 @@ def _find_tool(repository_ctx, tool, overridden_tools):
return overridden_tools[tool]
return which(repository_ctx, tool, "/usr/bin/" + tool)
+# TODO(https://github.com/bazelbuild/bazel/issues/32020): use a source annotation once Bazel exposes these context and metadata types.
def _get_tool_paths(repository_ctx, overridden_tools):
+ # type: (repository_ctx, Mapping[str, str | path | None]) -> dict[str, str]
"""Compute the %-escaped path to the various tools"""
- return dict({
- k: escape_string(_find_tool(repository_ctx, k, overridden_tools))
- for k in [
- "ar",
- "ld",
- "llvm-cov",
- "llvm-profdata",
- "cpp",
- "gcc",
- "dwp",
- "gcov",
- "nm",
- "objcopy",
- "objdump",
- "strip",
- "c++filt",
- ]
- }.items())
+ paths = {}
+ for name in [
+ "ar",
+ "ld",
+ "llvm-cov",
+ "llvm-profdata",
+ "cpp",
+ "gcc",
+ "dwp",
+ "gcov",
+ "nm",
+ "objcopy",
+ "objdump",
+ "strip",
+ "c++filt",
+ ]:
+ tool = _find_tool(repository_ctx, name, overridden_tools)
+ if tool != None:
+ paths[name] = escape_percent(str(tool))
+ return paths
def _escaped_cplus_include_paths(repository_ctx):
+ # type: (repository_ctx) -> list[str]
"""Use ${CPLUS_INCLUDE_PATH} to compute the %-escaped list of flags for cxxflag."""
if "CPLUS_INCLUDE_PATH" in repository_ctx.os.environ:
result = []
for p in repository_ctx.os.environ["CPLUS_INCLUDE_PATH"].split(":"):
- p = escape_string(str(repository_ctx.path(p))) # Normalize the path
+ p = escape_percent(str(repository_ctx.path(p))) # Normalize the path
result.append("-I" + p)
return result
else:
@@ -154,6 +163,7 @@ _OSX_FRAMEWORK_SUFFIX = " (framework directory)"
_OSX_FRAMEWORK_SUFFIX_LEN = len(_OSX_FRAMEWORK_SUFFIX)
def _cxx_inc_convert(path):
+ # type: (str) -> str
"""Convert path returned by cc -E xc++ in a complete path. Doesn't %-escape the path!"""
path = path.strip()
if path.endswith(_OSX_FRAMEWORK_SUFFIX):
@@ -216,7 +226,7 @@ def _is_linker_option_supported(repository_ctx, cc, force_linker_flags, option,
def _is_oso_prefix_supported(repository_ctx, ld):
return repository_ctx.execute([ld, "-v", "-oso_prefix", "."]).return_code == 0
-def _find_linker_path(repository_ctx, cc, linker, is_clang):
+def _find_linker(repository_ctx, cc, linker, is_clang):
"""Checks if a given linker is supported by the C compiler.
Args:
@@ -226,7 +236,8 @@ def _find_linker_path(repository_ctx, cc, linker, is_clang):
is_clang: whether the compiler is known to be clang
Returns:
- String to put as value to -fuse-ld= flag, or None if linker couldn't be found.
+ A struct with the linker name or path and the flags that select it, or
+ None if the linker couldn't be found.
"""
result = repository_ctx.execute([
cc,
@@ -245,7 +256,7 @@ def _find_linker_path(repository_ctx, cc, linker, is_clang):
return None
if not is_clang:
- return linker
+ return struct(path = linker, flags = ["-fuse-ld=" + linker])
# Extract linker path from:
# /usr/bin/clang ...
@@ -254,9 +265,22 @@ def _find_linker_path(repository_ctx, cc, linker, is_clang):
# https://github.com/llvm/llvm-project/blob/85c78274358717e4d5d019a801decba5c1add484/clang/lib/Driver/Job.cpp#L207-L209
invocations = [line for line in result.stderr.splitlines() if line.startswith(" \"")]
if not invocations:
- return linker
+ return struct(path = linker, flags = ["-fuse-ld=" + linker])
linker_command = invocations[-1]
- return linker_command.strip().split(" ")[0].strip("\"'")
+ linker_path = linker_command.strip().split(" ")[0].strip("\"'")
+
+ # Clang uses the -fuse-ld= value as the linker flavor. --ld-path= (Clang 12+)
+ # specifies the path without losing the flavor.
+ flags = ["-fuse-ld=" + linker, "--ld-path=" + linker_path]
+ result = repository_ctx.execute([
+ cc,
+ str(repository_ctx.path("tools/cpp/empty.cc")),
+ "-o",
+ "/dev/null",
+ ] + flags)
+ if result.return_code != 0:
+ return struct(path = linker_path, flags = ["-fuse-ld=" + linker_path])
+ return struct(path = linker_path, flags = flags)
def _add_compiler_option_if_supported(repository_ctx, cc, option):
"""Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
@@ -288,6 +312,7 @@ def _get_no_canonical_prefixes_opt(repository_ctx, cc):
return opt
def get_env(repository_ctx):
+ # type: (repository_ctx) -> str
"""Convert the environment in a list of export if in Homebrew. Doesn't %-escape the result!
Args:
@@ -306,6 +331,7 @@ def get_env(repository_ctx):
return ""
def _coverage_flags(repository_ctx, darwin):
+ # type: (repository_ctx, bool) -> tuple[str, str]
use_llvm_cov = "1" == get_env_var(
repository_ctx,
"BAZEL_USE_LLVM_NATIVE_COVERAGE",
@@ -346,8 +372,8 @@ def _get_compiler_name(repository_ctx, cc):
return "gcc"
return "compiler"
-def _find_generic(repository_ctx, name, env_name, overridden_tools, warn = False, silent = False):
- """Find a generic C++ toolchain tool. Doesn't %-escape the result."""
+def _find_generic(repository_ctx, name, env_name, overridden_tools, on_missing = auto_configure_fail):
+ """Return the tool without %-escaping it, or call on_missing with the lookup error."""
if name in overridden_tools:
return overridden_tools[name]
@@ -367,14 +393,17 @@ def _find_generic(repository_ctx, name, env_name, overridden_tools, warn = False
if result == None:
msg = ("Cannot find %s or %s%s; either correct your path or set the %s" +
" environment variable") % (name, env_name, env_value_with_paren, env_name)
- if warn:
- if not silent:
- auto_configure_warning(msg)
- else:
- auto_configure_fail(msg)
+ return on_missing(msg)
return result
+def _allow_missing_tool(_message):
+ # type: (str) -> None
+ return None
+
def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
+ # type: (repository_ctx, str, Mapping[str, str | path]) -> None
+ # TODO(https://github.com/bazelbuild/bazel/issues/32020): make this an
+ # annotation when supported Bazel versions expose a repository context type.
"""Configure C++ toolchain on Unix platforms.
Args:
@@ -382,25 +411,15 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
cpu_value: The CPU value.
overridden_tools: A dictionary of overridden tools.
"""
- paths = resolve_labels(repository_ctx, [
- "@rules_cc//cc/private/toolchain:BUILD.tpl",
- "@rules_cc//cc/private/toolchain:generate_system_module_map.sh",
- "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl",
- "@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl",
- "@rules_cc//cc/private/toolchain:linux_cc_wrapper.sh.tpl",
- "@rules_cc//cc/private/toolchain:validate_static_library.sh.tpl",
- "@rules_cc//cc/private/toolchain:osx_cc_wrapper.sh.tpl",
- "@rules_cc//cc/private/toolchain:clang_deps_scanner_wrapper.sh.tpl",
- "@rules_cc//cc/private/toolchain:gcc_deps_scanner_wrapper.sh.tpl",
- ])
-
- repository_ctx.symlink(
- paths["@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl"],
+ copy_file(
+ repository_ctx,
+ Label("@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl"),
"cc_toolchain_config.bzl",
)
- repository_ctx.symlink(
- paths["@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"],
+ copy_file(
+ repository_ctx,
+ Label("@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"),
"armeabi_cc_toolchain_config.bzl",
)
@@ -412,62 +431,37 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
cc = _find_generic(repository_ctx, "gcc", "CC", overridden_tools)
is_clang = _is_clang(repository_ctx, cc)
- overridden_tools = dict(overridden_tools)
- overridden_tools["gcc"] = cc
- overridden_tools["gcov"] = _find_generic(
- repository_ctx,
- "gcov",
- "GCOV",
- overridden_tools,
- warn = True,
- silent = True,
- )
- overridden_tools["llvm-cov"] = _find_generic(
- repository_ctx,
- "llvm-cov",
- "BAZEL_LLVM_COV",
- overridden_tools,
- warn = True,
- silent = True,
- )
- overridden_tools["llvm-profdata"] = _find_generic(
- repository_ctx,
- "llvm-profdata",
- "BAZEL_LLVM_PROFDATA",
- overridden_tools,
- warn = True,
- silent = True,
- )
- overridden_tools["ar"] = _find_generic(
- repository_ctx,
- "ar",
- "AR",
- overridden_tools,
- warn = True,
- silent = True,
- )
+ optional_tools = {
+ name: _find_generic(repository_ctx, name, env, overridden_tools, on_missing = _allow_missing_tool)
+ for name, env in [
+ ("gcov", "GCOV"),
+ ("llvm-cov", "BAZEL_LLVM_COV"),
+ ("llvm-profdata", "BAZEL_LLVM_PROFDATA"),
+ ("ar", "AR"),
+ ]
+ }
+ tool_overrides = dict(overridden_tools) | {"gcc": cc} | optional_tools
libtool = _find_generic(
repository_ctx,
"libtool",
"LIBTOOL",
- overridden_tools,
- warn = not darwin,
- silent = True,
+ tool_overrides,
+ on_missing = auto_configure_fail if darwin else _allow_missing_tool,
)
if darwin:
- overridden_tools["gcc"] = "cc_wrapper.sh"
+ tool_overrides["gcc"] = "cc_wrapper.sh"
xcrun = repository_ctx.which("xcrun")
if xcrun:
for tool_name in ["llvm-cov", "llvm-profdata"]:
- if overridden_tools.get(tool_name) == None:
+ if tool_overrides.get(tool_name) == None:
xcrun_result = repository_ctx.execute([xcrun, "--find", tool_name])
if xcrun_result.return_code == 0:
- overridden_tools[tool_name] = xcrun_result.stdout.strip()
+ tool_overrides[tool_name] = xcrun_result.stdout.strip()
auto_configure_warning_maybe(repository_ctx, "CC used: " + str(cc))
- tool_paths = _get_tool_paths(repository_ctx, overridden_tools)
+ tool_paths = _get_tool_paths(repository_ctx, tool_overrides)
if libtool:
- tool_paths["libtool"] = escape_string(libtool)
+ tool_paths["libtool"] = escape_percent(str(libtool))
tool_paths["cpp-module-deps-scanner"] = "deps_scanner_wrapper.sh"
toolchain_features = []
@@ -477,7 +471,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
# The parse_header tool needs to be a wrapper around the compiler as it has
# to touch the output file.
tool_paths["parse_headers"] = "cc_wrapper.sh"
- cc_toolchain_identifier = escape_string(get_env_var(
+ cc_toolchain_identifier = escape_percent(get_env_var(
repository_ctx,
"CC_TOOLCHAIN_NAME",
"local",
@@ -487,12 +481,12 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
if "nm" in tool_paths and "c++filt" in tool_paths:
repository_ctx.template(
"validate_static_library.sh",
- paths["@rules_cc//cc/private/toolchain:validate_static_library.sh.tpl"],
+ Label("@rules_cc//cc/private/toolchain:validate_static_library.sh.tpl"),
{
- "%{c++filt}": escape_string(str(repository_ctx.path(tool_paths["c++filt"]))),
+ "%{c++filt}": escape_percent(str(repository_ctx.path(tool_paths["c++filt"]))),
# Certain weak symbols are otherwise listed with type T in the output of nm on macOS.
"%{nm_extra_args}": "--no-weak" if darwin else "",
- "%{nm}": escape_string(str(repository_ctx.path(tool_paths["nm"]))),
+ "%{nm}": escape_percent(str(repository_ctx.path(tool_paths["nm"]))),
},
)
tool_paths["validate_static_library"] = "validate_static_library.sh"
@@ -502,10 +496,10 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
)
repository_ctx.template(
"cc_wrapper.sh",
- paths[cc_wrapper_src],
+ Label(cc_wrapper_src),
{
- "%{cc}": escape_string(str(cc)),
- "%{env}": escape_string(get_env(repository_ctx)),
+ "%{cc}": escape_percent(str(cc)),
+ "%{env}": escape_percent(get_env(repository_ctx)),
},
)
deps_scanner_wrapper_src = (
@@ -513,17 +507,18 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
)
deps_scanner = "cpp-module-deps-scanner_not_found"
if is_clang:
- cc_str = str(cc)
- path_arr = cc_str.split("/")[:-1]
- path_arr.append("clang-scan-deps")
- deps_scanner = "/".join(path_arr)
+ sibling_deps_scanner = repository_ctx.path(cc).dirname.get_child("clang-scan-deps")
+ if sibling_deps_scanner.exists:
+ deps_scanner = str(sibling_deps_scanner)
+ else:
+ deps_scanner = which(repository_ctx, "clang-scan-deps") or deps_scanner
repository_ctx.template(
"deps_scanner_wrapper.sh",
- paths[deps_scanner_wrapper_src],
+ Label(deps_scanner_wrapper_src),
{
- "%{cc}": escape_string(str(cc)),
- "%{deps_scanner}": escape_string(deps_scanner),
- "%{env}": escape_string(get_env(repository_ctx)),
+ "%{cc}": escape_percent(str(cc)),
+ "%{deps_scanner}": escape_percent(deps_scanner),
+ "%{env}": escape_percent(get_env(repository_ctx)),
},
)
@@ -547,24 +542,30 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
False,
), ":")
- gold_or_lld_linker_path = (
- _find_linker_path(repository_ctx, cc, "lld", is_clang) or
- _find_linker_path(repository_ctx, cc, "gold", is_clang)
+ linker = (
+ _find_linker(repository_ctx, cc, "lld", is_clang) or
+ _find_linker(repository_ctx, cc, "gold", is_clang)
)
cc_path = repository_ctx.path(cc)
if not str(cc_path).startswith(str(repository_ctx.path(".")) + "/"):
# cc is outside the repository, set -B
- bin_search_flags = ["-B" + escape_string(str(cc_path.dirname))]
+ bin_search_flags = ["-B" + escape_percent(str(cc_path.dirname))]
else:
# cc is inside the repository, don't set -B.
- bin_search_flags = []
- if not gold_or_lld_linker_path:
+ bin_search_flags = [] # type: list[str]
+ if not linker:
ld_path = repository_ctx.path(tool_paths["ld"])
if ld_path.dirname != cc_path.dirname:
- bin_search_flags.append("-B" + str(ld_path.dirname))
+ bin_search_flags.append("-B" + escape_percent(str(ld_path.dirname)))
+ elif not linker.path.startswith("/"):
+ ld_binary = repository_ctx.which("ld." + linker.path) or repository_ctx.which(linker.path)
+ if ld_binary:
+ ld_path = repository_ctx.path(ld_binary)
+ if ld_path.dirname != cc_path.dirname:
+ bin_search_flags.append("-B" + escape_percent(str(ld_path.dirname)))
force_linker_flags = []
- if gold_or_lld_linker_path:
- force_linker_flags.append("-fuse-ld=" + gold_or_lld_linker_path)
+ if linker:
+ force_linker_flags.extend(linker.flags)
# TODO: It's unclear why these flags aren't added on macOS.
if bin_search_flags and not darwin:
@@ -661,7 +662,6 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
repository_ctx.file("module.modulemap", _generate_system_module_map(
repository_ctx,
builtin_include_directories,
- paths["@rules_cc//cc/private/toolchain:generate_system_module_map.sh"],
))
extra_flags_per_feature = {}
if is_clang:
@@ -683,16 +683,16 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
write_builtin_include_directory_paths(repository_ctx, cc, builtin_include_directories)
repository_ctx.template(
"BUILD",
- paths["@rules_cc//cc/private/toolchain:BUILD.tpl"],
+ Label("@rules_cc//cc/private/toolchain:BUILD.tpl"),
# @unsorted-dict-items
{
- "%{abi_libc_version}": escape_string(get_env_var(
+ "%{abi_libc_version}": escape_percent(get_env_var(
repository_ctx,
"ABI_LIBC_VERSION",
"local",
False,
)),
- "%{abi_version}": escape_string(get_env_var(
+ "%{abi_version}": escape_percent(get_env_var(
repository_ctx,
"ABI_VERSION",
"local",
@@ -728,7 +728,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
"-fno-omit-frame-pointer",
],
),
- "%{compiler}": escape_string(get_env_var(
+ "%{compiler}": escape_percent(get_env_var(
repository_ctx,
"BAZEL_COMPILER",
_get_compiler_name(repository_ctx, cc),
@@ -743,7 +743,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
"%{fastbuild_compile_flags}": get_starlark_list([]),
"%{dbg_compile_flags}": get_starlark_list(["-g"]),
"%{extra_flags_per_feature}": repr(extra_flags_per_feature),
- "%{host_system_name}": escape_string(get_env_var(
+ "%{host_system_name}": escape_percent(get_env_var(
repository_ctx,
"BAZEL_HOST_SYSTEM",
"local",
@@ -760,11 +760,10 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
) + (
[
"-headerpad_max_install_names",
- ] if darwin else [
+ ] if darwin else _add_compiler_option_if_supported(
# Gold linker only? Can we enable this by default?
# "-Wl,--warn-execstack",
# "-Wl,--detect-odr-violations"
- ] + _add_compiler_option_if_supported(
# Have gcc return the exit code from ld.
repository_ctx,
cc,
@@ -817,21 +816,21 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools):
"--start-lib",
) else "False",
"%{toolchain_features}": get_starlark_list(toolchain_features),
- "%{target_cpu}": escape_string(get_env_var(
+ "%{target_cpu}": escape_percent(get_env_var(
repository_ctx,
"BAZEL_TARGET_CPU",
cpu_value,
False,
)),
"%{target_libc}": _get_target_libc(repository_ctx, cc, darwin, all_compile_opts + conly_opts),
- "%{target_system_name}": escape_string(get_env_var(
+ "%{target_system_name}": escape_percent(get_env_var(
repository_ctx,
"BAZEL_TARGET_SYSTEM",
"local",
False,
)),
"%{tool_paths}": ",\n ".join(
- ['"%s": "%s"' % (k, v) for k, v in tool_paths.items() if v != None],
+ ['"%s": "%s"' % (k, v) for k, v in tool_paths.items()],
),
"%{unfiltered_compile_flags}": get_starlark_list(
_get_no_canonical_prefixes_opt(repository_ctx, cc) + [
diff --git a/cc/private/toolchain/unix_cc_configure.bzli b/cc/private/toolchain/unix_cc_configure.bzli
new file mode 100644
index 000000000..fc4e3078e
--- /dev/null
+++ b/cc/private/toolchain/unix_cc_configure.bzli
@@ -0,0 +1,87 @@
+"""Contracts for discovering and configuring a local Unix C++ toolchain."""
+
+# TODO(https://github.com/bazelbuild/starlark/pull/362): Move the helpers into source when its function syntax can bind T.
+# For example, _uniq returns the same item type that its caller supplies.
+
+type _Executable = str | Label | path
+
+def _uniq[T: Hashable](iterable: Collection[T]) -> list[T]: ...
+
+def _prepare_include_path(repo_ctx: repository_ctx, path: _Executable) -> str: ...
+
+def _get_target_libc(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ darwin: bool,
+ compile_opts: list[str],
+) -> str: ...
+
+def _find_tool[T](
+ repository_ctx: repository_ctx,
+ tool: str,
+ overridden_tools: Mapping[str, T],
+) -> str | T | None: ...
+
+def _get_cxx_include_directories(
+ repository_ctx: repository_ctx,
+ print_resource_dir_supported: bool,
+ cc: _Executable,
+ lang_flag: str,
+ additional_flags: list[str] = ...,
+) -> list[str]: ...
+
+def _is_compiler_option_supported(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ option: str,
+) -> bool: ...
+
+def _is_linker_option_supported(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ force_linker_flags: list[str],
+ option: str,
+ pattern: str,
+) -> bool: ...
+
+def _is_oso_prefix_supported(repository_ctx: repository_ctx, ld: _Executable) -> bool: ...
+
+def _find_linker(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ linker: str,
+ is_clang: bool,
+) -> struct[{"path": str, "flags": list[str]}] | None: ...
+
+def _add_compiler_option_if_supported(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ option: str,
+) -> list[str]: ...
+
+def _add_linker_option_if_supported(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+ force_linker_flags: list[str],
+ option: str,
+ pattern: str,
+) -> list[str]: ...
+
+def _get_no_canonical_prefixes_opt(
+ repository_ctx: repository_ctx,
+ cc: _Executable,
+) -> list[str]: ...
+
+def _is_clang(repository_ctx: repository_ctx, cc: _Executable) -> bool: ...
+
+def _is_gcc(repository_ctx: repository_ctx, cc: _Executable) -> bool: ...
+
+def _get_compiler_name(repository_ctx: repository_ctx, cc: _Executable) -> str: ...
+
+def _find_generic[Override, Missing = Never](
+ repository_ctx: repository_ctx,
+ name: str,
+ env_name: str,
+ overridden_tools: Mapping[str, Override],
+ on_missing: Callable[[str], Missing] = ...,
+) -> Override | Missing | str | path: ...
diff --git a/cc/private/toolchain/unix_cc_toolchain_config.bzl b/cc/private/toolchain/unix_cc_toolchain_config.bzl
index 4724887df..a77ac482b 100644
--- a/cc/private/toolchain/unix_cc_toolchain_config.bzl
+++ b/cc/private/toolchain/unix_cc_toolchain_config.bzl
@@ -13,6 +13,8 @@
# limitations under the License.
"""A Starlark cc_toolchain configuration rule"""
+load("@apple_support//xcode:providers.bzl", "XcodeVersionInfo")
+
#buildifier: disable=bzl-visibility
load("@bazel_features//private:util.bzl", "ge")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
@@ -44,7 +46,7 @@ def _cpp_module_extension(compiler):
return ".pcm"
def _target_os_version(ctx):
- xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ xcode_config = ctx.attr._xcode_config[XcodeVersionInfo]
return xcode_config.minimum_os_for_platform_type(apple_common.platform_type.macos)
def layering_check_features(compiler, extra_flags_per_feature, is_macos):
diff --git a/cc/private/toolchain/windows_cc_configure.bzl b/cc/private/toolchain/windows_cc_configure.bzl
index 235ebbf76..f37e7f179 100644
--- a/cc/private/toolchain/windows_cc_configure.bzl
+++ b/cc/private/toolchain/windows_cc_configure.bzl
@@ -19,10 +19,10 @@ load(
"auto_configure_fail",
"auto_configure_warning",
"auto_configure_warning_maybe",
- "escape_string",
+ "copy_file",
+ "escape_percent",
"execute",
"get_starlark_list",
- "resolve_labels",
"split_escaped",
"write_builtin_include_directory_paths",
)
@@ -30,25 +30,27 @@ load(
_targets_archs = {"arm": "amd64_arm", "arm64": "amd64_arm64", "x64": "amd64", "x86": "amd64_x86"}
_targets_lib_folder = {"arm": "arm", "arm64": "arm64", "x86": ""}
-def _lookup_env_var(env, name, default = None):
- """Lookup environment variable case-insensitve.
+def _lookup_env_var(env, name):
+ # type: (Mapping[str, str], str) -> tuple[str, str | None]
+ """Look up an environment variable case-insensitively.
If a matching (case-insensitive) entry is found in the env dict both
the key and the value are returned. The returned key might differ from
name in casing.
- If a matching key was found its value is returned otherwise
- the default is returned.
-
- Return a (key, value) tuple"""
+ If no entry matches, return (name, None).
+ """
for key, value in env.items():
if name.lower() == key.lower():
return (key, value)
- return (name, default)
+ return (name, None)
-def _get_env_var(repository_ctx, name, default = None):
+def _get_env_var(repository_ctx, name):
+ # type: (repository_ctx, str) -> str | None
+ # TODO(https://github.com/bazelbuild/bazel/issues/32020): make this an
+ # annotation when supported Bazel versions expose a repository context type.
"""Returns a value from an environment variable."""
- return _lookup_env_var(repository_ctx.os.environ, name, default)[1]
+ return _lookup_env_var(repository_ctx.os.environ, name)[1]
def _get_path_env_var(repository_ctx, name):
"""Returns a path from an environment variable.
@@ -91,7 +93,7 @@ def _get_escaped_windows_msys_starlark_content(repository_ctx, use_mingw = False
msys_root = tokens[0][:len(tokens[0]) - len("bin")]
prefix = "mingw64" if use_mingw else "usr"
- tool_path_prefix = escape_string(msys_root) + prefix
+ tool_path_prefix = escape_percent(msys_root) + prefix
tool_bin_path = tool_path_prefix + "/bin"
tool_path = {}
@@ -113,12 +115,12 @@ def _get_system_root(repository_ctx):
repository_ctx,
"SYSTEMROOT is not set, using default SYSTEMROOT=C:\\Windows",
)
- return escape_string(systemroot)
+ return escape_percent(systemroot)
def _add_system_root(repository_ctx, env):
"""Running VCVARSALL.BAT and VCVARSQUERYREGISTRY.BAT need %SYSTEMROOT%\\\\system32 in PATH."""
- env_key, env_value = _lookup_env_var(env, "PATH", default = "")
- env[env_key] = env_value + ";" + _get_system_root(repository_ctx) + "\\system32"
+ env_key, env_value = _lookup_env_var(env, "PATH")
+ env[env_key] = (env_value or "") + ";" + _get_system_root(repository_ctx) + "\\system32"
return env
def find_vc_path(repository_ctx):
@@ -343,7 +345,7 @@ def _get_vc_env_vars(repository_ctx, vc_path, msvc_vars_x64, target_arch):
env["INCLUDE"] = msvc_vars_x64["%{msvc_env_include_x64}"]
env["LIB"] = lib.replace("x64", target_arch)
- env["PATH"] = escape_string(tools_path.replace("\\", "\\\\")) + ";" + msvc_vars_x64["%{msvc_env_path_x64}"]
+ env["PATH"] = escape_percent(tools_path.replace("\\", "\\\\")) + ";" + msvc_vars_x64["%{msvc_env_path_x64}"]
return env
def setup_vc_env_vars(repository_ctx, vc_path, envvars = [], allow_empty = False, escape = True):
@@ -398,7 +400,7 @@ def setup_vc_env_vars(repository_ctx, vc_path, envvars = [], allow_empty = False
env_map = {}
for env in envs:
key, value = env.split("=", 1)
- env_map[key] = escape_string(value.replace("\\", "\\\\")) if escape else value
+ env_map[key] = escape_percent(value.replace("\\", "\\\\")) if escape else value
if not allow_empty:
_check_env_vars(env_map, cmd, expected = envvars)
@@ -445,7 +447,7 @@ def _get_vc_full_version(repository_ctx, vc_path):
def _get_winsdk_full_version(repository_ctx):
"""Return the value of BAZEL_WINSDK_FULL_VERSION if defined, otherwise an empty string."""
- return _get_env_var(repository_ctx, "BAZEL_WINSDK_FULL_VERSION", default = "")
+ return _get_env_var(repository_ctx, "BAZEL_WINSDK_FULL_VERSION") or ""
def _find_msvc_tools(repository_ctx, vc_path, target_arch = "x64"):
"""Find the exact paths of the build tools in MSVC for the given target. Doesn't %-escape the result."""
@@ -614,7 +616,7 @@ def find_llvm_tool(repository_ctx, llvm_path, tool):
def _use_clang_cl(repository_ctx):
"""Returns True if USE_CLANG_CL is set to 1."""
- return _get_env_var(repository_ctx, "USE_CLANG_CL", default = "0") == "1"
+ return _get_env_var(repository_ctx, "USE_CLANG_CL") == "1"
def _find_missing_llvm_tools(repository_ctx, llvm_path):
"""Check if any required tool is missing under given LLVM path."""
@@ -665,7 +667,7 @@ def _get_msys_mingw_vars(repository_ctx):
}
return msys_mingw_vars
-def _get_msvc_vars(repository_ctx, paths, target_arch = "x64", msvc_vars_x64 = None):
+def _get_msvc_vars(repository_ctx, target_arch = "x64", msvc_vars_x64 = None):
"""Get the variables we need to populate the MSVC toolchains."""
vc_path = find_vc_path(repository_ctx)
missing_tools = None
@@ -673,7 +675,7 @@ def _get_msvc_vars(repository_ctx, paths, target_arch = "x64", msvc_vars_x64 = N
if not vc_path:
repository_ctx.template(
"vc_installation_error_" + target_arch + ".bat",
- paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"],
+ Label("@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"),
{"%{vc_error_message}": ""},
)
else:
@@ -689,7 +691,7 @@ def _get_msvc_vars(repository_ctx, paths, target_arch = "x64", msvc_vars_x64 = N
])
repository_ctx.template(
"vc_installation_error_" + target_arch + ".bat",
- paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"],
+ Label("@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"),
{"%{vc_error_message}": message},
)
@@ -715,8 +717,8 @@ def _get_msvc_vars(repository_ctx, paths, target_arch = "x64", msvc_vars_x64 = N
env = _get_vc_env_vars(repository_ctx, vc_path, msvc_vars_x64, target_arch)
else:
env = setup_vc_env_vars(repository_ctx, vc_path)
- escaped_tmp_dir = escape_string(_get_temp_env(repository_ctx).replace("\\", "\\\\"))
- escaped_include_paths = escape_string(env["INCLUDE"])
+ escaped_tmp_dir = escape_percent(_get_temp_env(repository_ctx).replace("\\", "\\\\"))
+ escaped_include_paths = escape_percent(env["INCLUDE"])
build_tools = {}
llvm_path = ""
@@ -752,7 +754,7 @@ def _get_msvc_vars(repository_ctx, paths, target_arch = "x64", msvc_vars_x64 = N
clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\")
escaped_cxx_include_directories.append("\"%s\"" % clang_include_path)
clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\")
- env["LIB"] = escape_string(env["LIB"]) + ";" + clang_lib_path
+ env["LIB"] = escape_percent(env["LIB"]) + ";" + clang_lib_path
support_debug_fastlink = _is_support_debug_fastlink(repository_ctx, build_tools["LINK"])
write_builtin_include_directory_paths(repository_ctx, "msvc", escaped_cxx_include_directories, file_suffix = "_msvc")
@@ -775,8 +777,8 @@ Fix this by installing the English language pack for the Visual Studio installat
"%{msvc_env_tmp_" + target_arch + "}": escaped_tmp_dir,
"%{msvc_env_include_" + target_arch + "}": escaped_include_paths,
"%{msvc_cxx_builtin_include_directories_" + target_arch + "}": " " + ",\n ".join(escaped_cxx_include_directories),
- "%{msvc_env_path_" + target_arch + "}": escape_string(env["PATH"]),
- "%{msvc_env_lib_" + target_arch + "}": escape_string(env["LIB"]),
+ "%{msvc_env_path_" + target_arch + "}": escape_percent(env["PATH"]),
+ "%{msvc_env_lib_" + target_arch + "}": escape_percent(env["LIB"]),
"%{msvc_cl_path_" + target_arch + "}": build_tools["CL"],
"%{msvc_ml_path_" + target_arch + "}": build_tools.get("ML", "msvc_arm_toolchain_does_not_support_ml"),
"%{msvc_link_path_" + target_arch + "}": build_tools["LINK"],
@@ -788,7 +790,7 @@ Fix this by installing the English language pack for the Visual Studio installat
}
return msvc_vars
-def _get_clang_cl_vars(repository_ctx, paths, msvc_vars, target_arch):
+def _get_clang_cl_vars(repository_ctx, msvc_vars, target_arch):
"""Get the variables we need to populate the clang-cl toolchains."""
llvm_path = find_llvm_path(repository_ctx)
error_script = None
@@ -797,7 +799,7 @@ def _get_clang_cl_vars(repository_ctx, paths, msvc_vars, target_arch):
elif not llvm_path:
repository_ctx.template(
"clang_installation_error.bat",
- paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"],
+ Label("@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"),
{"%{clang_error_message}": ""},
)
error_script = "clang_installation_error.bat"
@@ -813,7 +815,7 @@ def _get_clang_cl_vars(repository_ctx, paths, msvc_vars, target_arch):
])
repository_ctx.template(
"clang_installation_error.bat",
- paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"],
+ Label("@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"),
{"%{clang_error_message}": message},
)
error_script = "clang_installation_error.bat"
@@ -865,10 +867,10 @@ def _get_clang_cl_vars(repository_ctx, paths, msvc_vars, target_arch):
}
return clang_cl_vars
-def _get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, target_arch = "x64"):
+def _get_msvc_deps_scanner_vars(repository_ctx, template_vars, target_arch = "x64"):
repository_ctx.template(
"msvc_deps_scanner_wrapper_" + target_arch + ".bat",
- paths["@rules_cc//cc/private/toolchain:msvc_deps_scanner_wrapper.bat.tpl"],
+ Label("@rules_cc//cc/private/toolchain:msvc_deps_scanner_wrapper.bat.tpl"),
{
"%{cc}": template_vars["%{msvc_cl_path_" + target_arch + "}"],
},
@@ -878,33 +880,17 @@ def _get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, target_arc
"%{msvc_deps_scanner_wrapper_path_" + target_arch + "}": "msvc_deps_scanner_wrapper_" + target_arch + ".bat",
}
+def _get_env_options(repository_ctx, name):
+ # type: (repository_ctx, str) -> list[str]
+ return split_escaped(_get_env_var(repository_ctx, name) or "", ":")
+
def _get_copts(repository_ctx):
"""Get the variables we need to populate the msys/mingw toolchains."""
- conly_opts = split_escaped(_get_env_var(
- repository_ctx,
- "BAZEL_CONLYOPTS",
- "",
- ), ":")
- all_compile_opts = split_escaped(_get_env_var(
- repository_ctx,
- "BAZEL_COPTS",
- "",
- ), ":")
- cxx_opts = split_escaped(_get_env_var(
- repository_ctx,
- "BAZEL_CXXOPTS",
- "",
- ), ":")
- link_opts = split_escaped(_get_env_var(
- repository_ctx,
- "BAZEL_LINKOPTS",
- "",
- ), ":")
- win32_winnt_opts = _get_env_var(
- repository_ctx,
- "BAZEL_WIN32_WINNT",
- None,
- )
+ conly_opts = _get_env_options(repository_ctx, "BAZEL_CONLYOPTS")
+ all_compile_opts = _get_env_options(repository_ctx, "BAZEL_COPTS")
+ cxx_opts = _get_env_options(repository_ctx, "BAZEL_CXXOPTS")
+ link_opts = _get_env_options(repository_ctx, "BAZEL_LINKOPTS")
+ win32_winnt_opts = _get_env_var(repository_ctx, "BAZEL_WIN32_WINNT")
copts_vars = {
"%{all_compile_flags}": get_starlark_list(all_compile_opts),
"%{conly_flags}": get_starlark_list(conly_opts),
@@ -912,37 +898,31 @@ def _get_copts(repository_ctx):
"%{link_flags}": get_starlark_list(link_opts),
# None means the user didn't provide the override.
# Fall back to the default in that case.
- "%{win32_winnt_flag}": ' win32_winnt_flag = "' + escape_string(win32_winnt_opts) + '", ' if win32_winnt_opts != None else "",
+ "%{win32_winnt_flag}": ' win32_winnt_flag = "' + escape_percent(win32_winnt_opts) + '", ' if win32_winnt_opts != None else "",
}
return copts_vars
def configure_windows_toolchain(repository_ctx):
+ # type: (repository_ctx) -> None
"""Configure C++ toolchain on Windows.
Args:
repository_ctx: The repository context.
"""
- paths = resolve_labels(repository_ctx, [
- "@rules_cc//cc/private/toolchain:BUILD.windows.tpl",
- "@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl",
- "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl",
- "@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl",
- "@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat",
- "@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl",
- "@rules_cc//cc/private/toolchain:msvc_deps_scanner_wrapper.bat.tpl",
- ])
-
- repository_ctx.symlink(
- paths["@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl"],
+ copy_file(
+ repository_ctx,
+ Label("@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl"),
"windows_cc_toolchain_config.bzl",
)
- repository_ctx.symlink(
- paths["@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"],
+ copy_file(
+ repository_ctx,
+ Label("@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"),
"armeabi_cc_toolchain_config.bzl",
)
- repository_ctx.symlink(
- paths["@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat"],
+ copy_file(
+ repository_ctx,
+ Label("@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat"),
"msys_gcc_installation_error.bat",
)
@@ -954,23 +934,23 @@ def configure_windows_toolchain(repository_ctx):
# Set outside _get_msvc_vars so the error-stub configs it returns early for
# are labelled the same as the working ones.
template_vars["%{compiler}"] = "clang-cl" if _use_clang_cl(repository_ctx) else "msvc-cl"
- msvc_vars_x64 = _get_msvc_vars(repository_ctx, paths, "x64")
+ msvc_vars_x64 = _get_msvc_vars(repository_ctx, "x64")
template_vars.update(msvc_vars_x64)
- template_vars.update(_get_clang_cl_vars(repository_ctx, paths, msvc_vars_x64, "x64"))
+ template_vars.update(_get_clang_cl_vars(repository_ctx, msvc_vars_x64, "x64"))
template_vars.update(_get_msys_mingw_vars(repository_ctx))
template_vars.update(_get_copts(repository_ctx))
- template_vars.update(_get_msvc_vars(repository_ctx, paths, "x86", msvc_vars_x64))
- template_vars.update(_get_msvc_vars(repository_ctx, paths, "arm", msvc_vars_x64))
- msvc_vars_arm64 = _get_msvc_vars(repository_ctx, paths, "arm64", msvc_vars_x64)
+ template_vars.update(_get_msvc_vars(repository_ctx, "x86", msvc_vars_x64))
+ template_vars.update(_get_msvc_vars(repository_ctx, "arm", msvc_vars_x64))
+ msvc_vars_arm64 = _get_msvc_vars(repository_ctx, "arm64", msvc_vars_x64)
template_vars.update(msvc_vars_arm64)
- template_vars.update(_get_clang_cl_vars(repository_ctx, paths, msvc_vars_arm64, "arm64"))
+ template_vars.update(_get_clang_cl_vars(repository_ctx, msvc_vars_arm64, "arm64"))
- template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, "x64"))
- template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, "x86"))
- template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, "arm"))
- template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, paths, template_vars, "arm64"))
+ template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, template_vars, "x64"))
+ template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, template_vars, "x86"))
+ template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, template_vars, "arm"))
+ template_vars.update(_get_msvc_deps_scanner_vars(repository_ctx, template_vars, "arm64"))
repository_ctx.template(
"BUILD",
- paths["@rules_cc//cc/private/toolchain:BUILD.windows.tpl"],
+ Label("@rules_cc//cc/private/toolchain:BUILD.windows.tpl"),
template_vars,
)
diff --git a/cc/private/toolchain/windows_cc_toolchain_config.bzl b/cc/private/toolchain/windows_cc_toolchain_config.bzl
index 55b156daf..521a3428d 100644
--- a/cc/private/toolchain/windows_cc_toolchain_config.bzl
+++ b/cc/private/toolchain/windows_cc_toolchain_config.bzl
@@ -177,6 +177,11 @@ def _impl(ctx):
prefix = "",
extension = ".exe",
),
+ artifact_name_pattern(
+ category_name = "dynamic_library",
+ prefix = "lib",
+ extension = ".dll",
+ ),
]
if _use_msvc_toolchain(ctx):
@@ -607,15 +612,7 @@ def _impl(ctx):
] if ctx.attr.conly_flags else []),
),
flag_set(
- actions = [
- ACTION_NAMES.linkstamp_compile,
- ACTION_NAMES.cpp_compile,
- ACTION_NAMES.cpp_header_parsing,
- ACTION_NAMES.cpp_module_compile,
- ACTION_NAMES.cpp_module_codegen,
- ACTION_NAMES.lto_backend,
- ACTION_NAMES.clif_match,
- ],
+ actions = all_cpp_compile_actions + [ACTION_NAMES.lto_backend],
flag_groups = ([
flag_group(
flags = ctx.attr.cxx_flags,
@@ -1666,15 +1663,7 @@ def _impl(ctx):
] if ctx.attr.conly_flags else []),
),
flag_set(
- actions = [
- ACTION_NAMES.linkstamp_compile,
- ACTION_NAMES.cpp_compile,
- ACTION_NAMES.cpp_header_parsing,
- ACTION_NAMES.cpp_module_compile,
- ACTION_NAMES.cpp_module_codegen,
- ACTION_NAMES.lto_backend,
- ACTION_NAMES.clif_match,
- ],
+ actions = all_cpp_compile_actions + [ACTION_NAMES.lto_backend],
flag_groups = ([
flag_group(
flags = ctx.attr.cxx_flags,
@@ -1739,6 +1728,11 @@ def _impl(ctx):
if "dumpbin" in ctx.attr.tool_paths:
make_variables.append(make_variable(name = "DUMPBIN", value = ctx.attr.tool_paths["dumpbin"]))
+ windows_quoting_for_param_files_feature = feature(
+ name = "windows_quoting_for_param_files",
+ enabled = _use_msvc_toolchain(ctx),
+ )
+
# Tell bazel we support C++ modules now
cpp_modules_feature = feature(
name = "cpp_modules",
@@ -1785,7 +1779,7 @@ def _impl(ctx):
],
enabled = True,
)
- features.extend([cpp_modules_feature, cpp_module_modmap_file_feature, cpp20_module_compile_flags_feature])
+ features.extend([windows_quoting_for_param_files_feature, cpp_modules_feature, cpp_module_modmap_file_feature, cpp20_module_compile_flags_feature])
extra_rules_based_features = depset(ctx.attr.extra_enabled_features + ctx.attr.extra_known_features)
features.extend([convert_feature(extra_feature[FeatureInfo], enabled = extra_feature in ctx.attr.extra_enabled_features) for extra_feature in extra_rules_based_features.to_list()])
diff --git a/cc/private/toolchain_config/BUILD b/cc/private/toolchain_config/BUILD
index eb5a48d6e..ccfa6c62a 100644
--- a/cc/private/toolchain_config/BUILD
+++ b/cc/private/toolchain_config/BUILD
@@ -14,6 +14,8 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+exports_files(["legacy_features.bzli"])
+
bzl_library(
name = "toolchain_config_bzl",
srcs = glob(["*.bzl"]),
@@ -41,6 +43,7 @@ filegroup(
name = "srcs",
srcs = glob([
"**/*.bzl",
+ "**/*.bzli",
"**/BUILD",
]),
visibility = ["//visibility:public"],
diff --git a/cc/private/toolchain_config/legacy_features.bzl b/cc/private/toolchain_config/legacy_features.bzl
index aa5b88a8a..bd7263d93 100644
--- a/cc/private/toolchain_config/legacy_features.bzl
+++ b/cc/private/toolchain_config/legacy_features.bzl
@@ -27,6 +27,8 @@ load(
"with_feature_set",
)
+_NO_FLAGS = [] # type: list[str]
+
def get_legacy_features(ctx, platform, existing_feature_names, linker_tool_path):
"""The features added to all legacy toolchains
@@ -622,7 +624,7 @@ def get_legacy_features(ctx, platform, existing_feature_names, linker_tool_path)
expand_if_available = "output_execpath",
flags = _platform_specific_value(
platform,
- linux = [],
+ linux = _NO_FLAGS,
mac = ["-o"],
) + [
"%{output_execpath}",
@@ -1371,7 +1373,7 @@ def get_legacy_action_configs(
flags = ["-S"] + _platform_specific_value(
platform,
linux = ["-p"],
- mac = [],
+ mac = _NO_FLAGS,
) + ["-o", "%{output_file}"],
),
flag_group(
diff --git a/cc/private/toolchain_config/legacy_features.bzli b/cc/private/toolchain_config/legacy_features.bzli
new file mode 100644
index 000000000..f6ff42b45
--- /dev/null
+++ b/cc/private/toolchain_config/legacy_features.bzli
@@ -0,0 +1,42 @@
+# Copyright 2026 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.
+"""Legacy feature and action configuration builders."""
+
+# TODO(https://github.com/bazelbuild/starlark/pull/362): Move _platform_specific_value into source when its signature can bind
+# L and M separately to preserve the two platforms’ possible result types.
+
+load("//cc:cc_toolchain_config_lib.bzl", "ActionConfigInfo", "FeatureInfo")
+
+type _ExistingNames = Collection[str] | str
+
+type _Platform = Literal["linux", "mac"]
+
+def get_legacy_features(
+ ctx: ctx[object, struct[{"compiler": NotRequired[str]}, ...]],
+ platform: _Platform,
+ existing_feature_names: _ExistingNames,
+ linker_tool_path: str,
+) -> list[FeatureInfo]: ...
+
+def get_features_to_appear_last(existing_feature_names: _ExistingNames) -> list[FeatureInfo]: ...
+
+def get_legacy_action_configs(
+ platform: _Platform,
+ gcc_tool_path: str,
+ ar_tool_path: str,
+ strip_tool_path: str,
+ existing_action_config_names: _ExistingNames,
+) -> list[ActionConfigInfo]: ...
+
+def _platform_specific_value[L, M](platform: str, *, linux: L, mac: M) -> L | M: ...
diff --git a/cc/common/visibility.bzl b/cc/private/visibility.bzl
similarity index 91%
rename from cc/common/visibility.bzl
rename to cc/private/visibility.bzl
index 4ce7ed032..5eb0c6edc 100644
--- a/cc/common/visibility.bzl
+++ b/cc/private/visibility.bzl
@@ -3,6 +3,8 @@
# buildifier: disable=bzl-visibility
load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal")
+visibility("//cc/...")
+
def check_private_api():
_cc_internal.check_private_api(allowlist = PRIVATE_STARLARKIFICATION_ALLOWLIST, depth = 2)
@@ -21,7 +23,9 @@ def wrap_with_check_private_api(symbol):
return callback
-INTERNAL_VISIBILITY = ["public"]
+# Things that are publicly visible in the open-source Bazel world, but have
+# restricted visibility within Google's monorepo.
+PUBLIC_IF_NOT_GOOGLE = ["public"]
PRIVATE_RULES_VISIBILITY_FOR_BZL = []
diff --git a/cc/repositories.bzl b/cc/repositories.bzl
index 3ff7dbccd..07a2437b0 100644
--- a/cc/repositories.bzl
+++ b/cc/repositories.bzl
@@ -3,8 +3,10 @@
load("//cc/private/toolchain:cc_configure.bzl", "cc_configure")
def rules_cc_dependencies():
+ # type: () -> None
pass
# buildifier: disable=unnamed-macro
-def rules_cc_toolchains(*args):
- cc_configure(*args)
+def rules_cc_toolchains():
+ # type: () -> None
+ cc_configure()
diff --git a/cc/starlark_doc_extract_helper.bzl b/cc/starlark_doc_extract_helper.bzl
index 6c145f304..fc96c726d 100644
--- a/cc/starlark_doc_extract_helper.bzl
+++ b/cc/starlark_doc_extract_helper.bzl
@@ -2,7 +2,10 @@
visibility("//cc/...")
+# TODO(https://github.com/bazelbuild/bazel/issues/32343): Annotate **kwargs
+# when Bazel can derive them from the wrapped starlark_doc_extract rule.
def starlark_doc_extract_helper(**kwargs):
+ # type: (**Unpack[KeywordArgs[native.starlark_doc_extract]]) -> None
"""Creates a starlark_doc_extract target if the native rule is available
Args:
diff --git a/cc/system_library.bzl b/cc/system_library.bzl
index 68477dace..12c720321 100644
--- a/cc/system_library.bzl
+++ b/cc/system_library.bzl
@@ -8,13 +8,16 @@ ENV_VAR_SEPARATOR = ","
ENV_VAR_ASSIGNMENT = "="
def _make_flags(flag_values, flag):
+ # type: (Collection[str] | None, str) -> str
flags = []
if flag_values:
for s in flag_values:
flags.append(flag + s)
return " ".join(flags)
+# TODO(https://github.com/bazelbuild/bazel/issues/32020): use a source annotation once Bazel exposes these context and metadata types.
def _split_env_var(repo_ctx, var_name):
+ # type: (repository_ctx, str) -> dict[str, list[str]]
value = repo_ctx.os.environ.get(var_name)
if value:
assignments = value.split(ENV_VAR_SEPARATOR)
@@ -36,12 +39,15 @@ def _split_env_var(repo_ctx, var_name):
return {}
def _get_list_from_env_var(repo_ctx, var_name, key):
+ # type: (repository_ctx, str, str) -> list[str]
return _split_env_var(repo_ctx, var_name).get(key, default = [])
def _execute_bash(repo_ctx, cmd):
+ # type: (repository_ctx, str) -> str
return repo_ctx.execute(["/usr/bin/env", "bash", "-c", cmd]).stdout.strip("\n")
def _find_linker(repo_ctx):
+ # type: (repository_ctx) -> str
ld = _execute_bash(repo_ctx, "which ld")
lld = _execute_bash(repo_ctx, "which lld")
if ld:
@@ -52,6 +58,7 @@ def _find_linker(repo_ctx):
fail("No linker found")
def _find_compiler(repo_ctx):
+ # type: (repository_ctx) -> str
gcc = _execute_bash(repo_ctx, "which g++")
clang = _execute_bash(repo_ctx, "which clang++")
if gcc:
@@ -62,6 +69,7 @@ def _find_compiler(repo_ctx):
fail("No compiler found")
def _find_lib_path(repo_ctx, lib_name, archive_names, lib_path_hints):
+ # type: (repository_ctx, str, Collection[str], list[str]) -> tuple[str, str] | tuple[None, None]
override_paths = _get_list_from_env_var(
repo_ctx,
BAZEL_LIB_OVERRIDE_PATHS_ENV_VAR,
@@ -96,6 +104,7 @@ def _find_lib_path(repo_ctx, lib_name, archive_names, lib_path_hints):
return (None, None)
def _find_header_path(repo_ctx, lib_name, header_name, includes):
+ # type: (repository_ctx, str, str, list[str]) -> str | None
override_paths = _get_list_from_env_var(
repo_ctx,
BAZEL_INCLUDE_OVERRIDE_PATHS_ENV_VAR,
@@ -127,7 +136,21 @@ def _find_header_path(repo_ctx, lib_name, header_name, includes):
return result
return None
+# TODO(https://github.com/bazelbuild/bazel/issues/32020): Use a native repository
+# context annotation for these attributes when supported Bazel versions expose it.
+# type _Attrs = struct[{
+# "name": str,
+# "includes": list[str],
+# "hdrs": list[str],
+# "optional_hdrs": list[str],
+# "deps": list[str],
+# "lib_path_hints": list[str],
+# "static_lib_names": list[str],
+# "shared_lib_names": list[str],
+# }, ...]
+
def _system_library_impl(repo_ctx):
+ # type: (repository_ctx[_Attrs]) -> repo_metadata | None
repo_name = repo_ctx.attr.name.split("+")[-1]
includes = repo_ctx.attr.includes
hdrs = repo_ctx.attr.hdrs
@@ -180,7 +203,7 @@ def _system_library_impl(repo_ctx):
# The problem is that the quote-includes are assumed to be
# in the same directory as the header they are included from -
# they have no subdir prefix ("SDL2/") in their paths
- include_subdirs = {}
+ include_subdirs = {} # type: dict[str, None]
for hdr in hdr_names:
path_segments = hdr.split("/")
path_segments.pop()
diff --git a/cc/toolchains/BUILD b/cc/toolchains/BUILD
index e9ac62b03..ba57417d3 100644
--- a/cc/toolchains/BUILD
+++ b/cc/toolchains/BUILD
@@ -14,6 +14,14 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+exports_files(
+ [
+ "cc_toolchain_info.bzli",
+ "tool_map.bzli",
+ ],
+ visibility = ["//visibility:public"],
+)
+
bzl_library(
name = "toolchain_rules",
srcs = glob(["*.bzl"]),
@@ -44,6 +52,7 @@ filegroup(
name = "srcs",
srcs = glob([
"**/*.bzl",
+ "**/*.bzli",
"**/BUILD",
]) + [
"//cc/toolchains/actions:srcs",
diff --git a/cc/toolchains/actions.bzl b/cc/toolchains/actions.bzl
index 9a8928e5b..99e23f0a6 100644
--- a/cc/toolchains/actions.bzl
+++ b/cc/toolchains/actions.bzl
@@ -19,6 +19,7 @@ load(":cc_toolchain_info.bzl", "ActionTypeInfo", "ActionTypeSetInfo")
visibility("public")
def _cc_action_type_impl(ctx):
+ # type: (...) -> list[ActionTypeInfo | ActionTypeSetInfo]
action_type = ActionTypeInfo(label = ctx.label, name = ctx.attr.action_name)
return [
action_type,
@@ -62,6 +63,7 @@ cc_action_type(
)
def _cc_action_type_set_impl(ctx):
+ # type: (...) -> list[ActionTypeSetInfo]
if not ctx.attr.actions and not ctx.attr.allow_empty:
fail("Each cc_action_type_set must contain at least one action type.")
actions = collect_action_types(ctx.attr.actions)
diff --git a/cc/toolchains/actions/BUILD b/cc/toolchains/actions/BUILD
index 80cac26b2..cbf939535 100644
--- a/cc/toolchains/actions/BUILD
+++ b/cc/toolchains/actions/BUILD
@@ -269,6 +269,10 @@ cc_action_type_set(
excludes = [
# `lto_backend` actions do not instantiate the full set of build variables.
":lto_backend",
+ # `cpp_module_codegen` actions compile a precompiled module and never
+ # preprocess, so include paths, defines and other preprocessing flags
+ # don't apply to them and only make Clang warn about unused arguments.
+ ":cpp_module_codegen",
],
)
diff --git a/cc/toolchains/args.bzl b/cc/toolchains/args.bzl
index b07e54ec4..0c18c30de 100644
--- a/cc/toolchains/args.bzl
+++ b/cc/toolchains/args.bzl
@@ -164,6 +164,7 @@ Roughly equivalent to ctx.actions.args()
Examples:
cc_args(
name = "warnings_as_errors",
+ actions = ["//cc/toolchains/actions:compile_actions"],
args = ["-Werror"],
)
""",
@@ -172,7 +173,7 @@ Examples:
def cc_args(
*,
name,
- actions = None,
+ actions,
allowlist_include_directories = None,
allowlist_absolute_include_directories = None,
args = None,
diff --git a/cc/toolchains/args/BUILD b/cc/toolchains/args/BUILD
index 9625c3d9e..3edc7f41a 100644
--- a/cc/toolchains/args/BUILD
+++ b/cc/toolchains/args/BUILD
@@ -36,6 +36,7 @@ cc_feature_set(
"//cc/toolchains/args/linkstamp_flags:feature",
"//cc/toolchains/args/library_search_directories:feature",
"//cc/toolchains/args/dependency_file:feature",
+ "//cc/toolchains/args/cpp_modules:feature",
# NOTE: This should come below default flags so user flags can override them
"//cc/toolchains/args/compile_flags:feature",
@@ -61,5 +62,33 @@ filegroup(
name = "srcs",
srcs = glob(["**"]) + [
"//cc/toolchains/args/archiver_flags:srcs",
+ "//cc/toolchains/args/compile_flags:srcs",
+ "//cc/toolchains/args/compiler_input_flags:srcs",
+ "//cc/toolchains/args/compiler_output_flags:srcs",
+ "//cc/toolchains/args/cpp_modules:srcs",
+ "//cc/toolchains/args/def_file:srcs",
+ "//cc/toolchains/args/dependency_file:srcs",
+ "//cc/toolchains/args/external_include_paths:srcs",
+ "//cc/toolchains/args/fission_flags:srcs",
+ "//cc/toolchains/args/generate_linkmap:srcs",
+ "//cc/toolchains/args/include_flags:srcs",
+ "//cc/toolchains/args/layering_check:srcs",
+ "//cc/toolchains/args/libraries_to_link:srcs",
+ "//cc/toolchains/args/library_search_directories:srcs",
+ "//cc/toolchains/args/link_flags:srcs",
+ "//cc/toolchains/args/linker_param_file:srcs",
+ "//cc/toolchains/args/linkstamp_flags:srcs",
+ "//cc/toolchains/args/objc_arc_flags:srcs",
+ "//cc/toolchains/args/pic_flags:srcs",
+ "//cc/toolchains/args/preprocessor_defines:srcs",
+ "//cc/toolchains/args/random_seed:srcs",
+ "//cc/toolchains/args/runtime_library_search_directories:srcs",
+ "//cc/toolchains/args/sanitize_pwd:srcs",
+ "//cc/toolchains/args/shared_flag:srcs",
+ "//cc/toolchains/args/soname_flags:srcs",
+ "//cc/toolchains/args/static_libgcc:srcs",
+ "//cc/toolchains/args/strip_debug_symbols:srcs",
+ "//cc/toolchains/args/strip_flags:srcs",
+ "//cc/toolchains/args/thin_lto:srcs",
],
)
diff --git a/cc/toolchains/args/compile_flags/BUILD b/cc/toolchains/args/compile_flags/BUILD
index 123892aaa..aa285c379 100644
--- a/cc/toolchains/args/compile_flags/BUILD
+++ b/cc/toolchains/args/compile_flags/BUILD
@@ -62,3 +62,9 @@ cc_args(
iterate_over = "//cc/toolchains/variables:unfiltered_compile_flags",
requires_not_none = "//cc/toolchains/variables:unfiltered_compile_flags",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/compiler_input_flags/BUILD b/cc/toolchains/args/compiler_input_flags/BUILD
index 9bfe6a48b..d4a39d0c1 100644
--- a/cc/toolchains/args/compiler_input_flags/BUILD
+++ b/cc/toolchains/args/compiler_input_flags/BUILD
@@ -5,6 +5,7 @@ cc_feature(
name = "feature",
args = [
":flags",
+ ":module_interface_args",
":header_parsing_args",
],
overrides = "//cc/toolchains/features/legacy:compiler_input_flags",
@@ -15,7 +16,8 @@ cc_args(
name = "flags",
actions = [
"//cc/toolchains/actions:compile_actions_without_header_parsing",
- "//cc/toolchains/actions:cpp20_module_actions",
+ "//cc/toolchains/actions:cpp20_module_codegen",
+ "//cc/toolchains/actions:cpp_module_deps_scanning",
],
args = [
"-c",
@@ -25,6 +27,21 @@ cc_args(
requires_not_none = "//cc/toolchains/variables:source_file",
)
+# A C++20 module interface unit compiles as one whatever its extension. `-x`
+# only applies to the inputs after it, so it goes right before the source.
+cc_args(
+ name = "module_interface_args",
+ actions = ["//cc/toolchains/actions:cpp20_module_compile"],
+ args = [
+ "-x",
+ "c++-module",
+ "-c",
+ "{source_file}",
+ ],
+ format = {"source_file": "//cc/toolchains/variables:source_file"},
+ requires_not_none = "//cc/toolchains/variables:source_file",
+)
+
cc_args(
name = "header_parsing_args",
actions = ["//cc/toolchains/actions:cpp_header_parsing"],
@@ -32,3 +49,9 @@ cc_args(
format = {"source_file": "//cc/toolchains/variables:source_file"},
requires_not_none = "//cc/toolchains/variables:source_file",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/compiler_output_flags/BUILD b/cc/toolchains/args/compiler_output_flags/BUILD
index 068d31085..d6bd17e90 100644
--- a/cc/toolchains/args/compiler_output_flags/BUILD
+++ b/cc/toolchains/args/compiler_output_flags/BUILD
@@ -15,6 +15,7 @@ cc_args_list(
":output_assembly_flags",
":output_preprocess_flags",
":output_flags",
+ ":deps_scanner_output_env",
],
)
@@ -45,3 +46,19 @@ cc_args(
format = {"output_file": "//cc/toolchains/variables:output_file"},
requires_not_none = "//cc/toolchains/variables:output_file",
)
+
+# The C++20 module dependency scanner writes its P1689 output to the file this
+# names; the output_flags above name the scanned compile's object.
+cc_args(
+ name = "deps_scanner_output_env",
+ actions = ["//cc/toolchains/actions:cpp_module_deps_scanning"],
+ env = {"DEPS_SCANNER_OUTPUT_FILE": "{output_file}"},
+ format = {"output_file": "//cc/toolchains/variables:output_file"},
+ requires_not_none = "//cc/toolchains/variables:output_file",
+)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/cpp_modules/BUILD b/cc/toolchains/args/cpp_modules/BUILD
new file mode 100644
index 000000000..6269e5fd9
--- /dev/null
+++ b/cc/toolchains/args/cpp_modules/BUILD
@@ -0,0 +1,56 @@
+load("//cc/toolchains:args.bzl", "cc_args")
+load("//cc/toolchains:feature.bzl", "cc_feature")
+
+# The args C++20 modules need from a clang toolchain, matching
+# unix_cc_toolchain_config. Bazel builds module interfaces only once the
+# toolchain knows the `cpp_modules` feature and the build enables it, so
+# toolchains that map tools for the C++20 module actions should list
+# `:cpp_modules` in their known features and `:feature` in their enabled ones.
+# compiler_input_flags marks a module interface unit as one, since `-x` only
+# applies to the inputs after it.
+
+package(default_visibility = ["//visibility:public"])
+
+cc_feature(
+ name = "cpp_modules",
+ feature_name = "cpp_modules",
+)
+
+cc_feature(
+ name = "feature",
+ args = [
+ ":modmap_file_args",
+ ":module_output_args",
+ ],
+ feature_name = "cpp20_module_flags",
+)
+
+# The module map file names the BMI of each module a source imports.
+cc_args(
+ name = "modmap_file_args",
+ actions = [
+ "//cc/toolchains/actions:cpp_compile",
+ "//cc/toolchains/actions:cpp20_module_compile",
+ "//cc/toolchains/actions:cpp20_module_codegen",
+ ],
+ args = ["@{cpp_module_modmap_file}"],
+ format = {"cpp_module_modmap_file": "//cc/toolchains/variables:cpp_module_modmap_file"},
+ requires_not_none = "//cc/toolchains/variables:cpp_module_modmap_file",
+ visibility = ["//visibility:private"],
+)
+
+# A module interface unit writes its BMI where Bazel declared it.
+cc_args(
+ name = "module_output_args",
+ actions = ["//cc/toolchains/actions:cpp20_module_compile"],
+ args = ["-fmodule-output={cpp_module_output_file}"],
+ format = {"cpp_module_output_file": "//cc/toolchains/variables:cpp_module_output_file"},
+ requires_not_none = "//cc/toolchains/variables:cpp_module_output_file",
+ visibility = ["//visibility:private"],
+)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/def_file/BUILD b/cc/toolchains/args/def_file/BUILD
index 0b6811f95..6449b22f3 100644
--- a/cc/toolchains/args/def_file/BUILD
+++ b/cc/toolchains/args/def_file/BUILD
@@ -16,3 +16,9 @@ cc_args(
format = {"def_file_path": "//cc/toolchains/variables:def_file_path"},
requires_not_none = "//cc/toolchains/variables:def_file_path",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/dependency_file/BUILD b/cc/toolchains/args/dependency_file/BUILD
index 812092b6e..05ac6052c 100644
--- a/cc/toolchains/args/dependency_file/BUILD
+++ b/cc/toolchains/args/dependency_file/BUILD
@@ -22,3 +22,9 @@ cc_args(
format = {"dependency_file": "//cc/toolchains/variables:dependency_file"},
requires_not_none = "//cc/toolchains/variables:dependency_file",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/external_include_paths/BUILD b/cc/toolchains/args/external_include_paths/BUILD
index de2214f3d..f2dfcfab8 100644
--- a/cc/toolchains/args/external_include_paths/BUILD
+++ b/cc/toolchains/args/external_include_paths/BUILD
@@ -20,3 +20,9 @@ cc_nested_args(
},
iterate_over = "//cc/toolchains/variables:external_include_paths",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/fission_flags/BUILD b/cc/toolchains/args/fission_flags/BUILD
index 8ceb501c0..f91cdd9b2 100644
--- a/cc/toolchains/args/fission_flags/BUILD
+++ b/cc/toolchains/args/fission_flags/BUILD
@@ -39,3 +39,9 @@ cc_args(
],
requires_not_none = "//cc/toolchains/variables:per_object_debug_info_file",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/generate_linkmap/BUILD b/cc/toolchains/args/generate_linkmap/BUILD
index b44ecf1bb..27f07a98a 100644
--- a/cc/toolchains/args/generate_linkmap/BUILD
+++ b/cc/toolchains/args/generate_linkmap/BUILD
@@ -20,3 +20,9 @@ cc_args(
requires_not_none = "//cc/toolchains/variables:output_execpath",
visibility = ["//visibility:private"],
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/include_flags/BUILD b/cc/toolchains/args/include_flags/BUILD
index 1ee790e26..b6d76ad92 100644
--- a/cc/toolchains/args/include_flags/BUILD
+++ b/cc/toolchains/args/include_flags/BUILD
@@ -81,3 +81,9 @@ cc_args(
format = {"framework_include_paths": "//cc/toolchains/variables:framework_include_paths"},
iterate_over = "//cc/toolchains/variables:framework_include_paths",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/layering_check/BUILD b/cc/toolchains/args/layering_check/BUILD
index a7672deef..327aaad6e 100644
--- a/cc/toolchains/args/layering_check/BUILD
+++ b/cc/toolchains/args/layering_check/BUILD
@@ -95,3 +95,9 @@ cc_args(
iterate_over = "@rules_cc//cc/toolchains/variables:dependent_module_map_files",
requires_not_none = "@rules_cc//cc/toolchains/variables:dependent_module_map_files",
)
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/libraries_to_link/BUILD b/cc/toolchains/args/libraries_to_link/BUILD
index ebfec9a44..8228f3588 100644
--- a/cc/toolchains/args/libraries_to_link/BUILD
+++ b/cc/toolchains/args/libraries_to_link/BUILD
@@ -180,3 +180,12 @@ cc_nested_args(
requires_equal = "//cc/toolchains/variables:libraries_to_link.type",
requires_equal_value = "versioned_dynamic_library",
)
+
+filegroup(
+ name = "srcs",
+ srcs = [
+ "BUILD",
+ "//cc/toolchains/args/libraries_to_link/private:srcs",
+ ],
+ visibility = ["//cc/toolchains/args:__pkg__"],
+)
diff --git a/cc/toolchains/args/libraries_to_link/private/BUILD b/cc/toolchains/args/libraries_to_link/private/BUILD
index ecb36c3e0..e0b10e88f 100644
--- a/cc/toolchains/args/libraries_to_link/private/BUILD
+++ b/cc/toolchains/args/libraries_to_link/private/BUILD
@@ -1 +1,10 @@
package(default_visibility = ["//visibility:private"])
+
+filegroup(
+ name = "srcs",
+ srcs = [
+ "BUILD",
+ "library_link_args.bzl",
+ ],
+ visibility = ["//cc/toolchains/args/libraries_to_link:__pkg__"],
+)
diff --git a/cc/toolchains/args/libraries_to_link/private/library_link_args.bzl b/cc/toolchains/args/libraries_to_link/private/library_link_args.bzl
index 85e65814d..bc47b4a7e 100644
--- a/cc/toolchains/args/libraries_to_link/private/library_link_args.bzl
+++ b/cc/toolchains/args/libraries_to_link/private/library_link_args.bzl
@@ -16,6 +16,7 @@
load("//cc/toolchains:nested_args.bzl", "cc_nested_args")
def apple_force_load_library_args(name, variable):
+ # type: (str, str | Label) -> None
"""A helper for declaring -force_load argument expansion for a library.
This creates an argument expansion that will expand to -Wl,-force_load,
load("@rules_cc//cc/toolchains/impl:documented_api.bzl", "cc_variable")
-cc_variable(name, type, **kwargs)
+cc_variable(name, type, *, actions, **kwargs)
Exposes a toolchain variable to use in toolchain argument expansions.
@@ -904,6 +904,7 @@ cc_variable(
| :------------- | :------------- | :------------- |
| name | (str) The name of the outer variable, and the rule. | none |
| type | The type of the variable, constructed using `types` factory in [@rules_cc//cc/toolchains/impl:variables.bzl](https://github.com/bazelbuild/rules_cc/tree/main/cc/toolchains/impl/variables.bzl). | none |
+| actions | The actions for which the variable is available. | `[]` |
| kwargs | [common attributes](https://bazel.build/reference/be/common-definitions#common-attributes) that should be applied to this rule. | none |
diff --git a/local_bazel.bzl b/local_bazel.bzl
index ab937b932..78140ca7c 100644
--- a/local_bazel.bzl
+++ b/local_bazel.bzl
@@ -15,6 +15,9 @@
"""Repository rule for importing the copy of Bazel on the user's PATH."""
def _local_bazel_import_impl(repository_ctx):
+ # type: (...) -> repo_metadata | None
+ # TODO(https://github.com/bazelbuild/bazel/issues/32020): make this an
+ # annotation when supported Bazel versions expose metadata types.
if "windows" in repository_ctx.os.name.lower():
bazel_real = "bazel-real.exe"
bazel_name = "bazel.exe"
diff --git a/stubs.toml b/stubs.toml
new file mode 100644
index 000000000..f1b4eff0f
--- /dev/null
+++ b/stubs.toml
@@ -0,0 +1,17 @@
+format-version = 1
+
+[files]
+"cc/cc_postmark_initializers.bzl" = "cc/cc_postmark_initializers.bzli"
+"cc/cc_toolchain_config_lib.bzl" = "cc/cc_toolchain_config_lib.bzli"
+"cc/private/objc_info.bzl" = "cc/private/objc_info.bzli"
+"cc/private/rules_impl/objc_compilation_artifacts_info.bzl" = "cc/private/rules_impl/objc_compilation_artifacts_info.bzli"
+"cc/private/rules_impl/objc_compilation_context_info.bzl" = "cc/private/rules_impl/objc_compilation_context_info.bzli"
+"cc/private/toolchain/lib_cc_configure.bzl" = "cc/private/toolchain/lib_cc_configure.bzli"
+"cc/private/toolchain/unix_cc_configure.bzl" = "cc/private/toolchain/unix_cc_configure.bzli"
+"cc/private/toolchain_config/legacy_features.bzl" = "cc/private/toolchain_config/legacy_features.bzli"
+"cc/toolchains/cc_toolchain_info.bzl" = "cc/toolchains/cc_toolchain_info.bzli"
+"cc/toolchains/impl/collect.bzl" = "cc/toolchains/impl/collect.bzli"
+"cc/toolchains/impl/legacy_converter.bzl" = "cc/toolchains/impl/legacy_converter.bzli"
+"cc/toolchains/impl/nested_args.bzl" = "cc/toolchains/impl/nested_args.bzli"
+"cc/toolchains/impl/variables.bzl" = "cc/toolchains/impl/variables.bzli"
+"cc/toolchains/tool_map.bzl" = "cc/toolchains/tool_map.bzli"
diff --git a/stubs/bazel_skylib/bzl_library.bzli b/stubs/bazel_skylib/bzl_library.bzli
new file mode 100644
index 000000000..5e3bda8fd
--- /dev/null
+++ b/stubs/bazel_skylib/bzl_library.bzli
@@ -0,0 +1,5 @@
+# TODO(https://github.com/bazelbuild/bazel-skylib/pull/641): Remove this stub
+# when the selected Skylib release types the wrapper's forwarded rule attributes.
+load("@bazel_skylib//rules/private:bzl_library.bzl", _rule = "bzl_library")
+
+def bzl_library(name: str, **kwargs: Unpack[KeywordArgs[_rule, "name"]]) -> None: ...
diff --git a/stubs/bazel_skylib/common_settings.bzli b/stubs/bazel_skylib/common_settings.bzli
new file mode 100644
index 000000000..c98968090
--- /dev/null
+++ b/stubs/bazel_skylib/common_settings.bzli
@@ -0,0 +1,3 @@
+# TODO(https://github.com/bazelbuild/bazel-skylib/pull/641): Remove this stub
+# when the selected Skylib release types BuildSettingInfo.value.
+def BuildSettingInfo[T = object](*, value: T) -> record[{"value": T}]: ...
diff --git a/stubs/bazel_skylib/directory_providers.bzli b/stubs/bazel_skylib/directory_providers.bzli
new file mode 100644
index 000000000..94538f239
--- /dev/null
+++ b/stubs/bazel_skylib/directory_providers.bzli
@@ -0,0 +1,26 @@
+# TODO(https://github.com/bazelbuild/bazel-skylib/pull/641): Remove this stub
+# when the selected Skylib release types DirectoryInfo.
+class _Glob(Protocol):
+ def __call__(self, include: Sequence[str], exclude: Sequence[str] = ..., allow_empty: bool = ...) -> depset[File]: ...
+
+type _Fields = dict[{
+ "entries": dict[str, File | DirectoryInfo],
+ "transitive_files": depset[File],
+ "path": str,
+ "human_readable": str,
+ "get_path": Callable[[str], File | DirectoryInfo],
+ "get_file": Callable[[str], File],
+ "get_subdirectory": Callable[[str], DirectoryInfo],
+ "glob": _Glob,
+}]
+
+def DirectoryInfo(**kwargs: Unpack[_Fields]) -> record[{
+ "entries": dict[str, File | DirectoryInfo],
+ "transitive_files": depset[File],
+ "path": str,
+ "human_readable": str,
+ "get_path": Callable[[str], File | DirectoryInfo],
+ "get_file": Callable[[str], File],
+ "get_subdirectory": Callable[[str], DirectoryInfo],
+ "glob": _Glob,
+}]: ...
diff --git a/stubs/bazel_skylib/paths.bzli b/stubs/bazel_skylib/paths.bzli
new file mode 100644
index 000000000..ec6d79705
--- /dev/null
+++ b/stubs/bazel_skylib/paths.bzli
@@ -0,0 +1,12 @@
+# TODO(https://github.com/bazelbuild/bazel-skylib/pull/641): Remove this stub
+# when the selected Skylib release types the functions it exports through paths.
+def _basename(p: str) -> str: ...
+def _dirname(p: str) -> str: ...
+def _is_absolute(path: str) -> bool: ...
+def _join(path: str, *others: str) -> str: ...
+def _normalize(path: str) -> str: ...
+def _is_normalized(str: str, look_for_same_level_references: bool = ...) -> bool: ...
+def _relativize(path: str, start: str) -> str: ...
+def _replace_extension(p: str, new_extension: str) -> str: ...
+def _split_extension(p: str) -> tuple[str, str]: ...
+def _starts_with(path_a: str, path_b: str) -> bool: ...
diff --git a/stubs/bazel_skylib/stubs.toml b/stubs/bazel_skylib/stubs.toml
new file mode 100644
index 000000000..36b7a33c9
--- /dev/null
+++ b/stubs/bazel_skylib/stubs.toml
@@ -0,0 +1,12 @@
+format-version = 1
+
+[source]
+repository = "@bazel_skylib"
+module = "bazel_skylib"
+versions = ["1.9.0"]
+
+[files]
+"bzl_library.bzl" = "bzl_library.bzli"
+"lib/paths.bzl" = "paths.bzli"
+"rules/common_settings.bzl" = "common_settings.bzli"
+"rules/directory/providers.bzl" = "directory_providers.bzli"
diff --git a/sty.toml b/sty.toml
new file mode 100644
index 000000000..0edcbec44
--- /dev/null
+++ b/sty.toml
@@ -0,0 +1,36 @@
+# TODO(https://github.com/bazelbuild/bazel/pull/30373): Move ordinary signatures
+# into function declarations once the minimum Bazel version supports annotation
+# syntax. Bazel 7 must still be able to load these files.
+[analysis]
+strict-generic-narrowing = true
+require-valid-suppressions = true
+
+[rules]
+strict = "error"
+
+# The released provider modules also contain unannotated private helpers.
+# Check the provider contracts and report incomplete types in those helpers.
+[[overrides]]
+include = [
+ "@bazel_skylib//rules:common_settings.bzl",
+ "@bazel_skylib//rules/directory:providers.bzl",
+]
+
+[overrides.rules]
+missing-parameter-annotation = "warn"
+missing-return-annotation = "warn"
+non-static-type = "warn"
+
+# _relativize assigns [] to start_segments; Skylib 1.9.0 leaves its element
+# type unknown. Keep checking the function's inputs and return type.
+[[overrides]]
+include = ["@bazel_skylib//lib:paths.bzl"]
+
+[overrides.rules]
+non-static-type = "warn"
+
+[[stub-packages]]
+manifest = "stubs.toml"
+
+[[stub-packages]]
+manifest = "stubs/bazel_skylib/stubs.toml"
diff --git a/tests/alwayslink_srcs/BUILD b/tests/alwayslink_srcs/BUILD
index 833912012..44015de27 100644
--- a/tests/alwayslink_srcs/BUILD
+++ b/tests/alwayslink_srcs/BUILD
@@ -39,7 +39,7 @@ genrule(
name = "copy_registerer_archive_unix",
srcs = [":registerer_lib"],
outs = ["libregisterer_copied.a"],
- cmd = "for f in $(locations :registerer_lib); do case \"$$f\" in *.a) cp \"$$f\" $@;; esac; done",
+ cmd = "for f in $(execpaths :registerer_lib); do case \"$$f\" in *.a) cp \"$$f\" $@;; esac; done",
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
@@ -50,7 +50,7 @@ genrule(
name = "copy_registerer_archive_windows",
srcs = [":registerer_lib"],
outs = ["registerer_copied.lib"],
- cmd = "for f in $(locations :registerer_lib); do case \"$$f\" in *.lib) cp \"$$f\" $@;; esac; done",
+ cmd = "for f in $(execpaths :registerer_lib); do case \"$$f\" in *.lib) cp \"$$f\" $@;; esac; done",
target_compatible_with = ["@platforms//os:windows"],
)
diff --git a/tests/builtins_bzl/cc/cc_shared_library/test/BUILD b/tests/builtins_bzl/cc/cc_shared_library/test/BUILD
index d6949cdfa..e6129a9c4 100644
--- a/tests/builtins_bzl/cc/cc_shared_library/test/BUILD
+++ b/tests/builtins_bzl/cc/cc_shared_library/test/BUILD
@@ -135,8 +135,8 @@ cc_shared_library(
user_link_flags = select({
"@platforms//os:linux": [
"-Wl,-rpath,kittens",
- "-Wl,--version-script=$(location :foo.lds)",
- "-Wl,--script=$(location :additional_script.txt)",
+ "-Wl,--version-script=$(execpath :foo.lds)",
+ "-Wl,--script=$(execpath :additional_script.txt)",
"-Wl,--no-gc-sections",
],
"//conditions:default": [],
@@ -272,7 +272,7 @@ cc_shared_library(
features = ["windows_export_all_symbols"],
user_link_flags = select({
"@platforms//os:linux": [
- "-Wl,--version-script=$(location :bar.lds)",
+ "-Wl,--version-script=$(execpath :bar.lds)",
],
"//conditions:default": [],
}),
@@ -399,7 +399,7 @@ genrule(
name = "check_file_named_correctly",
srcs = ["just_main_output_renamed"],
outs = ["renamed_so_file_copy.so"],
- cmd = "cp $$(dirname $(location :just_main_output_renamed))/renamed_so_file.so $@",
+ cmd = "cp $$(dirname $(execpath :just_main_output_renamed))/renamed_so_file.so $@",
)
filegroup(
@@ -552,6 +552,19 @@ exports_test(
],
)
+exports_test(
+ name = "external_select_exports_filter_exports_test",
+ bazel_only = True,
+ # target = "external_export_so", # copybara-comment-this-out-please
+ target = "@cross_repo_test//:bar_so", # copybara-uncomment-this-please
+ targets_that_should_be_claimed_to_be_exported = [
+ # buildifier: disable=canonical-repository
+ "@@test_repo+//:bar", # copybara-uncomment-this-please
+ # buildifier: disable=canonical-repository
+ "@@test_repo+//:bar_wrapper", # copybara-uncomment-this-please
+ ],
+)
+
pdb_test(
name = "pdb_test",
target = ":foo_so",
diff --git a/tests/builtins_bzl/cc/cc_shared_library/test2/BUILD.bazel b/tests/builtins_bzl/cc/cc_shared_library/test2/BUILD.bazel
index 88a92df68..133dff65f 100644
--- a/tests/builtins_bzl/cc/cc_shared_library/test2/BUILD.bazel
+++ b/tests/builtins_bzl/cc/cc_shared_library/test2/BUILD.bazel
@@ -1,4 +1,5 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
+load("@rules_cc//cc:cc_shared_library.bzl", "cc_shared_library")
licenses(["notice"])
@@ -8,3 +9,17 @@ cc_library(
hdrs = ["bar.h"],
visibility = ["//visibility:public"],
)
+
+cc_library(
+ name = "bar_wrapper",
+ visibility = ["//visibility:public"],
+ deps = [":bar"],
+)
+
+cc_shared_library(
+ name = "bar_so",
+ exports_filter = select({"//conditions:default": ["@test_repo//:__pkg__"]}),
+ tags = ["manual"],
+ visibility = ["//visibility:public"],
+ deps = [":bar_wrapper"],
+)
diff --git a/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/BUILD.bazel b/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/BUILD.bazel
new file mode 100644
index 000000000..b5605a2b0
--- /dev/null
+++ b/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/BUILD.bazel
@@ -0,0 +1,31 @@
+load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain")
+load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+
+package(default_visibility = ["//visibility:public"])
+
+filegroup(name = "empty")
+
+cc_toolchain_config(
+ name = "toolchain_config",
+ cxx_builtin_include_directories = [
+ "%package(@test_repo//toolchain_include_dirs)%/include",
+ "%package(//toolchain_include_dirs)%/other",
+ "%package(@rules_cc//cc)%",
+ ],
+ tags = ["manual"],
+)
+
+cc_toolchain(
+ name = "toolchain",
+ all_files = ":empty",
+ ar_files = ":empty",
+ as_files = ":empty",
+ compiler_files = ":empty",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ tags = ["manual"],
+ toolchain_config = ":toolchain_config",
+ toolchain_identifier = "test-toolchain",
+)
diff --git a/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/cc_toolchain_config.bzl b/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/cc_toolchain_config.bzl
new file mode 100644
index 000000000..2b2c922f0
--- /dev/null
+++ b/tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/cc_toolchain_config.bzl
@@ -0,0 +1,23 @@
+"""A minimal cc_toolchain_config rule for testing cxx_builtin_include_directories."""
+
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
+load("@rules_cc//cc/toolchains:cc_toolchain_config_info.bzl", "CcToolchainConfigInfo")
+
+def _impl(ctx):
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = "test-toolchain",
+ compiler = "test-compiler",
+ target_system_name = "local",
+ target_cpu = "k8",
+ target_libc = "unknown",
+ cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories,
+ )
+
+cc_toolchain_config = rule(
+ implementation = _impl,
+ attrs = {
+ "cxx_builtin_include_directories": attr.string_list(),
+ },
+ provides = [CcToolchainConfigInfo],
+)
diff --git a/tests/builtins_bzl/cc/cc_static_library/test/BUILD b/tests/builtins_bzl/cc/cc_static_library/test/BUILD
index 407853e7d..8c01a4cae 100644
--- a/tests/builtins_bzl/cc/cc_static_library/test/BUILD
+++ b/tests/builtins_bzl/cc/cc_static_library/test/BUILD
@@ -34,9 +34,15 @@ cc_library(
linkstatic = True,
)
+filegroup(
+ name = "lib_only_static",
+ srcs = [":lib_only"],
+ output_group = "archive",
+)
+
cc_import(
name = "lib_only_static_lib",
- static_library = ":lib_only",
+ static_library = ":lib_only_static",
)
cc_import(
@@ -84,6 +90,7 @@ sh_test(
],
target_compatible_with = select({
"@platforms//os:linux": [],
+ "@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
diff --git a/tests/builtins_bzl/cc/cc_static_library/test/starlark_tests.bzl b/tests/builtins_bzl/cc/cc_static_library/test/starlark_tests.bzl
index 3cba57383..f73ef0af3 100644
--- a/tests/builtins_bzl/cc/cc_static_library/test/starlark_tests.bzl
+++ b/tests/builtins_bzl/cc/cc_static_library/test/starlark_tests.bzl
@@ -19,6 +19,8 @@ load("@rules_testing//lib:util.bzl", "util")
load("//cc:cc_import.bzl", "cc_import")
load("//cc:cc_library.bzl", "cc_library")
load("//cc:cc_static_library.bzl", "cc_static_library")
+load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test")
+load("//tests/cc/testutil/toolchains:features.bzl", "FEATURE_NAMES")
load(":mock_toolchain.bzl", "mock_cc_toolchain")
def _set_up_subject(name):
@@ -233,6 +235,40 @@ def _test_validation_disabled_impl(env, target):
env.expect.that_collection(dir(target[OutputGroupInfo])).contains_none_of(["_validation"])
+#buildifier: disable=unused-variable
+def _test_opt_archives_one_object_per_source(name):
+ util.helper_target(
+ cc_library,
+ name = name + "_lib",
+ srcs = ["file.cc"],
+ )
+ util.helper_target(
+ cc_static_library,
+ name = name + "_subject",
+ deps = [name + "_lib"],
+ )
+
+ # With supports_pic in opt mode, cc_library compiles both a PIC and a
+ # non-PIC object for every source.
+ cc_analysis_test(
+ name = name,
+ impl = _test_opt_archives_one_object_per_source_impl,
+ target = name + "_subject",
+ test_features = [FEATURE_NAMES.supports_pic],
+ config_settings = {
+ "//command_line_option:compilation_mode": "opt",
+ },
+ )
+
+def _test_opt_archives_one_object_per_source_impl(env, target):
+ action = env.expect.that_target(target).action_named("CppTransitiveArchive")
+ objects = [
+ f.basename
+ for f in action.actual.inputs.to_list()
+ if f.extension == "o"
+ ]
+ env.expect.that_collection(objects).contains_exactly(["file.pic.o"])
+
def analysis_test_suite(name):
test_suite(
name = name,
@@ -241,5 +277,6 @@ def analysis_test_suite(name):
_test_output_groups,
_test_validation_enabled,
_test_validation_disabled,
+ _test_opt_archives_one_object_per_source, # copybara-uncomment-this-please
],
)
diff --git a/tests/cc/bazel/cpp_modules_test.bzl b/tests/cc/bazel/cpp_modules_test.bzl
index ce1c50d14..948659221 100644
--- a/tests/cc/bazel/cpp_modules_test.bzl
+++ b/tests/cc/bazel/cpp_modules_test.bzl
@@ -18,6 +18,37 @@ def _setup_with_features_target(name, rule_fn, suffix):
module_interfaces = [name + "/foo.cppm"],
)
+def _setup_with_parse_headers_target(name, rule_fn, suffix):
+ util.empty_file(name + "/bar.cc")
+ util.empty_file(name + "/bar.h")
+ util.helper_target(
+ rule_fn,
+ name = name + suffix,
+ srcs = [name + "/bar.cc"],
+ hdrs = [name + "/bar.h"],
+ features = ["parse_headers"],
+ )
+
+def _test_cpp_modules_cc_library_with_parse_headers(name, **kwargs):
+ _setup_with_parse_headers_target(name, cc_library, "_lib")
+ cc_analysis_test(
+ name = name,
+ target = name + "_lib",
+ impl = _test_cpp_modules_with_parse_headers_impl,
+ test_features = ["cpp_modules"],
+ with_action_configs = [
+ ACTION_NAMES.cpp_module_deps_scanning,
+ ACTION_NAMES.cpp20_module_compile,
+ ],
+ config_settings = {
+ "//command_line_option:experimental_cpp_modules": True,
+ },
+ **kwargs
+ )
+
+def _test_cpp_modules_with_parse_headers_impl(env, target):
+ env.expect.that_target(target).action_named("CppCompile")
+
def _test_cpp_modules_cc_library_configuration_with_features(name, **kwargs):
_setup_with_features_target(name, cc_library, "_lib")
cc_analysis_test(
@@ -166,6 +197,7 @@ def cpp_modules_tests(name, **kwargs):
_test_cpp_modules_cc_library_configuration_with_features,
_test_cpp_modules_cc_binary_configuration_with_features,
_test_cpp_modules_cc_test_configuration_with_features,
+ _test_cpp_modules_cc_library_with_parse_headers,
_test_same_module_interfaces_file_in_cc_library_twice,
_test_same_module_interfaces_file_in_cc_binary_twice,
_test_same_module_interfaces_file_in_cc_test_twice,
diff --git a/tests/cc/common/BUILD b/tests/cc/common/BUILD
index 94535da60..cc4dda4ff 100644
--- a/tests/cc/common/BUILD
+++ b/tests/cc/common/BUILD
@@ -12,6 +12,7 @@ load(":cc_link_action_test.bzl", "cc_link_action_tests")
load(":cc_linkstamp_compile_test.bzl", "cc_linkstamp_compile_tests")
load(":cc_objc_library_configured_target_tests.bzl", "cc_objc_library_configured_target_tests")
load(":cc_output_groups_test.bzl", "cc_output_groups_tests")
+load(":cc_toolchain_include_directories_test.bzl", "cc_toolchain_include_directories_tests")
load(":compile_build_variables_tests.bzl", "compile_build_variables_tests")
load(":cpp_modules_test.bzl", "cpp_modules_tests")
load(":debug_info_packaging_test.bzl", "debug_info_packaging_tests")
@@ -55,3 +56,5 @@ cc_link_action_tests(name = "cc_link_action_tests")
cc_linkstamp_compile_tests(name = "cc_linkstamp_compile_tests")
cc_binary_fdo_tests(name = "cc_binary_fdo_tests")
+
+cc_toolchain_include_directories_tests(name = "cc_toolchain_include_directories_tests")
diff --git a/tests/cc/common/cc_binary_configured_target_tests.bzl b/tests/cc/common/cc_binary_configured_target_tests.bzl
index fdde1aeae..75c9ec6d3 100644
--- a/tests/cc/common/cc_binary_configured_target_tests.bzl
+++ b/tests/cc/common/cc_binary_configured_target_tests.bzl
@@ -337,6 +337,105 @@ def _test_generated_def_file_uses_toolchain_action_impl(env, target):
matching.contains("def_parser_tool"),
)
+def _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name,
+ shared_lib_name = None,
+ dynamic_library_pattern = None,
+ **kwargs):
+ util.helper_target(
+ cc_library,
+ name = name + "/foo",
+ srcs = ["foo.cc"],
+ )
+ shared_lib_kwargs = {}
+ if shared_lib_name != None:
+ shared_lib_kwargs["shared_lib_name"] = shared_lib_name
+ util.helper_target(
+ cc_shared_library,
+ name = name + "/foo_shared",
+ deps = [name + "/foo"],
+ **shared_lib_kwargs
+ )
+ config_settings = {}
+ if dynamic_library_pattern != None:
+ config_settings[str(Label("//tests/cc/testutil/toolchains:dynamic_library_pattern"))] = dynamic_library_pattern
+ cc_analysis_test(
+ name = name,
+ impl = _cc_shared_library_generated_def_file_test_impl,
+ target = name + "/foo_shared",
+ test_features = [
+ FEATURE_NAMES.copy_dynamic_libraries_to_binary,
+ FEATURE_NAMES.targets_windows,
+ FEATURE_NAMES.windows_export_all_symbols,
+ ],
+ with_action_configs = [ACTION_NAMES.generate_def_file],
+ config_settings = config_settings,
+ attrs = {
+ "expected_dll_name": attr.string(),
+ },
+ attr_values = {
+ "expected_dll_name": expected_dll_name,
+ },
+ **kwargs
+ )
+
+def _cc_shared_library_generated_def_file_test_impl(env, target):
+ def_file_short_path = "{}/{}.gen.def".format(target.label.package, target.label.name)
+ def_action = _generated_def_file_action(env, target)
+ def_action.argv().contains_at_least_predicates([
+ matching.contains("def_parser_tool"),
+ matching.str_endswith(def_file_short_path),
+ matching.equals_wrapper(env.ctx.attr.expected_dll_name),
+ ]).in_order()
+ env.expect.that_target(target).output_group("def_file").contains_exactly([
+ "{package}/{name}.gen.def",
+ ])
+ link_action = env.expect.that_target(target).action_named("CppLink")
+ link_action.inputs().contains("{package}/{name}.gen.def")
+ link_action.outputs().contains_predicate(
+ matching.file_basename_equals(env.ctx.attr.expected_dll_name),
+ )
+
+def _test_cc_shared_library_generated_def_file_default_dll_name(name, **kwargs):
+ _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name = "libfoo_shared.so",
+ **kwargs
+ )
+
+def _test_cc_shared_library_generated_def_file_mingw_dll_pattern(name, **kwargs):
+ _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name = "libfoo_shared.dll",
+ dynamic_library_pattern = ["lib", ".dll"],
+ **kwargs
+ )
+
+def _test_cc_shared_library_generated_def_file_msvc_dll_pattern(name, **kwargs):
+ _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name = "foo_shared.dll",
+ dynamic_library_pattern = ["", ".dll"],
+ **kwargs
+ )
+
+def _test_cc_shared_library_generated_def_file_shared_lib_name(name, **kwargs):
+ _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name = "custom_foo.pyd",
+ shared_lib_name = "custom_foo.pyd",
+ **kwargs
+ )
+
+def _test_cc_shared_library_generated_def_file_shared_lib_name_with_subdir(name, **kwargs):
+ _cc_shared_library_generated_def_file_test(
+ name,
+ expected_dll_name = "custom_foo.dll",
+ shared_lib_name = "subdir/custom_foo.dll",
+ **kwargs
+ )
+
def _test_duplicate_linkopts(name, **kwargs):
util.helper_target(
cc_library,
@@ -2088,6 +2187,11 @@ def cc_binary_configured_target_tests(name):
_test_system_include_paths_reclassifies_local_includes_without_propagation,
_test_generated_def_file_uses_toolchain_action, # copybara-uncomment-this-please
_test_generated_def_file_uses_default_tool, # copybara-uncomment-this-please
+ _test_cc_shared_library_generated_def_file_default_dll_name,
+ _test_cc_shared_library_generated_def_file_mingw_dll_pattern,
+ _test_cc_shared_library_generated_def_file_msvc_dll_pattern,
+ _test_cc_shared_library_generated_def_file_shared_lib_name,
+ _test_cc_shared_library_generated_def_file_shared_lib_name_with_subdir,
_test_linker_toolchain_feature,
_test_link_staticness_binary_static_dynamic_mode_default,
_test_link_staticness_hello_test_dynamic_mode_default,
diff --git a/tests/cc/common/cc_binary_fdo_test.bzl b/tests/cc/common/cc_binary_fdo_test.bzl
index 4b64aef65..6f4fb9b40 100644
--- a/tests/cc/common/cc_binary_fdo_test.bzl
+++ b/tests/cc/common/cc_binary_fdo_test.bzl
@@ -14,6 +14,7 @@
"""Tests for cc_binary with FDO."""
+load("@bazel_features//:features.bzl", "bazel_features")
load("@rules_testing//lib:analysis_test.bzl", "test_suite")
load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", "util")
@@ -27,6 +28,12 @@ def _test_fdo_profile_action_graph(name, **kwargs):
name = name + "/foo",
srcs = ["foo.cc"],
)
+ util.helper_target(
+ cc_binary,
+ name = name + "/foo_no_fdo",
+ srcs = ["foo.cc"],
+ features = ["-fdo_optimize"],
+ )
util.helper_target(
fdo_profile,
@@ -46,6 +53,7 @@ def _test_fdo_profile_action_graph(name, **kwargs):
impl = _test_fdo_profile_action_graph_impl,
targets = {
"foo": name + "/foo",
+ "foo_no_fdo": name + "/foo_no_fdo",
"gen_profraw": name + "/gen_profraw",
"compiler": select({
"@platforms//os:macos": "//tests/cc/testutil/toolchains:cc-compiler-macos-compiler",
@@ -101,6 +109,11 @@ def _test_fdo_profile_action_graph_impl(env, targets):
# these targets so just assert short_path
assert_compile_action.inputs().contains(profdata.short_path)
+ if bazel_features.cc.cc_common_is_in_rules_cc:
+ env.expect.that_target(targets.foo_no_fdo).action_generating(
+ "{package}/_objs/{name}/foo.o",
+ ).inputs().not_contains(profdata.short_path)
+
def cc_binary_fdo_tests(name):
test_suite(
name = name,
diff --git a/tests/cc/common/cc_common_test.bzl b/tests/cc/common/cc_common_test.bzl
index 15af5008b..39be3c329 100644
--- a/tests/cc/common/cc_common_test.bzl
+++ b/tests/cc/common/cc_common_test.bzl
@@ -7,6 +7,7 @@ load("@rules_testing//lib:util.bzl", "TestingAspectInfo", "util")
load("//cc:cc_binary.bzl", "cc_binary")
load("//cc:cc_library.bzl", "cc_library")
load("//cc/common:cc_info.bzl", "CcInfo")
+load("//cc/private/link:create_extra_link_time_library.bzl", "build_libraries", "create_extra_link_time_libraries", "create_extra_link_time_library", "merge_extra_link_time_libraries") # buildifier: disable=bzl-visibility
load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test")
load("//tests/cc/testutil:cc_info_subject.bzl", "cc_info_subject")
@@ -707,6 +708,133 @@ def _test_alwayslink_yields_lo_impl(env, target):
files = [f.basename for f in target[DefaultInfo].files.to_list()]
env.expect.that_collection(files).contains("libalways_link.lo")
+# buildifier: disable=provider-params
+_ExtraLinkTimeLibraryTestInfo = provider(fields = ["stamp_infos"])
+
+def _build_extra_link_time_library(_ctx, _static_mode, _for_dynamic_library, inputs):
+ return struct(
+ linker_input = depset(),
+ runtime_library = depset(),
+ additional_stamp_info = inputs.to_list(),
+ )
+
+def _top_level_extra_link_time_library_impl(ctx):
+ first = create_extra_link_time_library(
+ build_library_func = _build_extra_link_time_library,
+ inputs = depset(["first"]),
+ )
+ second = create_extra_link_time_library(
+ build_library_func = _build_extra_link_time_library,
+ inputs = depset(["second"]),
+ )
+ libraries = merge_extra_link_time_libraries([
+ create_extra_link_time_libraries(first),
+ create_extra_link_time_libraries(second),
+ ])
+ result = build_libraries(libraries.libraries, ctx, False, False)
+ return [_ExtraLinkTimeLibraryTestInfo(stamp_infos = result.additional_stamp_infos)]
+
+_top_level_extra_link_time_library = rule(
+ implementation = _top_level_extra_link_time_library_impl,
+)
+
+def _nested_extra_link_time_library_impl(ctx):
+ def nested_build_library(_ctx, _static_mode, _for_dynamic_library):
+ # buildifier: disable=rule-impl-returns
+ return struct(
+ linker_input = depset(),
+ runtime_library = depset(),
+ )
+
+ library = create_extra_link_time_library(
+ build_library_func = nested_build_library,
+ )
+ build_libraries([library], ctx, False, False)
+ return []
+
+_nested_extra_link_time_library = rule(
+ implementation = _nested_extra_link_time_library_impl,
+)
+
+def _invalid_extra_link_time_library_impl(ctx):
+ create_extra_link_time_library(build_library_func = struct if ctx.attr.builtin else None)
+ return []
+
+_invalid_extra_link_time_library = rule(
+ implementation = _invalid_extra_link_time_library_impl,
+ attrs = {"builtin": attr.bool()},
+)
+
+def _test_top_level_extra_link_time_library(name):
+ util.helper_target(
+ _top_level_extra_link_time_library,
+ name = name + "/library",
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_top_level_extra_link_time_library_impl,
+ target = name + "/library",
+ )
+
+def _test_top_level_extra_link_time_library_impl(env, target):
+ env.expect.that_target(target).failures().contains_exactly([])
+ stamp_infos = target[_ExtraLinkTimeLibraryTestInfo].stamp_infos
+ env.expect.that_collection(stamp_infos).has_size(1)
+ env.expect.that_collection(stamp_infos[0]).contains_exactly(["first", "second"])
+
+def _test_nested_extra_link_time_library(name):
+ util.helper_target(
+ _nested_extra_link_time_library,
+ name = name + "/library",
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_nested_extra_link_time_library_impl,
+ target = name + "/library",
+ expect_failure = True,
+ )
+
+def _test_nested_extra_link_time_library_impl(env, target):
+ env.expect.that_target(target).failures().contains_predicate(
+ matching.custom(
+ "contains 'must be declared by a top-level def statement'",
+ lambda message: "must be declared by a top-level def statement" in message,
+ ),
+ )
+
+def _test_builtin_extra_link_time_library(name):
+ util.helper_target(
+ _invalid_extra_link_time_library,
+ name = name + "/library",
+ builtin = True,
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_invalid_extra_link_time_library_impl,
+ target = name + "/library",
+ expect_failure = True,
+ )
+
+def _test_none_extra_link_time_library(name):
+ util.helper_target(
+ _invalid_extra_link_time_library,
+ name = name + "/library",
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_invalid_extra_link_time_library_impl,
+ target = name + "/library",
+ expect_failure = True,
+ )
+
+def _test_invalid_extra_link_time_library_impl(env, target):
+ env.expect.that_target(target).failures().contains_predicate(
+ matching.custom(
+ "contains 'build_library_func must be a Starlark function'",
+ lambda message: "build_library_func must be a Starlark function" in message,
+ ),
+ )
+
def cc_common_tests(name):
tests = [
_test_same_cc_file_twice,
@@ -732,6 +860,10 @@ def cc_common_tests(name):
]
if bazel_features.cc.cc_common_is_in_rules_cc:
tests.extend([
+ _test_top_level_extra_link_time_library,
+ _test_nested_extra_link_time_library,
+ _test_builtin_extra_link_time_library,
+ _test_none_extra_link_time_library,
_test_strip_include_prefix_uses_virtual_includes_by_default,
_test_strip_include_prefix_no_virtual_includes_when_enabled,
_test_strip_include_prefix_with_include_prefix_uses_virtual_includes,
diff --git a/tests/cc/common/cc_toolchain_include_directories_test.bzl b/tests/cc/common/cc_toolchain_include_directories_test.bzl
new file mode 100644
index 000000000..fdfa88756
--- /dev/null
+++ b/tests/cc/common/cc_toolchain_include_directories_test.bzl
@@ -0,0 +1,37 @@
+"""Tests for the resolution of cxx_builtin_include_directories by cc_toolchain."""
+
+load("@bazel_features//:features.bzl", "bazel_features")
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@rules_testing//lib:analysis_test.bzl", "test_suite")
+load("//cc/common:cc_common.bzl", "cc_common")
+load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test")
+
+# buildifier: disable=unused-variable
+def _test_package_include_directories_use_toolchain_repo_mapping(name):
+ cc_analysis_test(
+ name = name,
+ impl = _test_package_include_directories_use_toolchain_repo_mapping_impl,
+ target = "@cross_repo_test//toolchain_include_dirs:toolchain",
+ )
+
+def _test_package_include_directories_use_toolchain_repo_mapping_impl(env, target):
+ test_repo_root = Label("@cross_repo_test//toolchain_include_dirs:toolchain").workspace_root
+ rules_cc_root = Label("//cc:BUILD").workspace_root
+ env.expect.that_collection(
+ target[cc_common.CcToolchainInfo].built_in_include_directories,
+ ).contains_exactly([
+ paths.join(test_repo_root, "toolchain_include_dirs/include"),
+ paths.join(test_repo_root, "toolchain_include_dirs/other"),
+ paths.join(rules_cc_root, "cc"),
+ ]).in_order()
+
+def cc_toolchain_include_directories_tests(name):
+ tests = []
+ if bazel_features.cc.cc_common_is_in_rules_cc:
+ tests.append(_test_package_include_directories_use_toolchain_repo_mapping) # copybara-uncomment-this-please
+ # pass # copybara-comment-this-out-please
+
+ test_suite(
+ name = name,
+ tests = tests,
+ )
diff --git a/tests/cc/common/debug_info_packaging_test.bzl b/tests/cc/common/debug_info_packaging_test.bzl
index 662b9c721..3f70ed221 100644
--- a/tests/cc/common/debug_info_packaging_test.bzl
+++ b/tests/cc/common/debug_info_packaging_test.bzl
@@ -1,10 +1,12 @@
"""Tests for debug info packaging (Fission)."""
+load("@bazel_features//:features.bzl", "bazel_features")
load("@rules_testing//lib:analysis_test.bzl", "test_suite")
load("@rules_testing//lib:truth.bzl", "subjects")
load("@rules_testing//lib:util.bzl", "util")
load("//cc:cc_binary.bzl", "cc_binary")
load("//cc:cc_library.bzl", "cc_library")
+load("//cc:cc_test.bzl", "cc_test")
load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test")
load("//tests/cc/testutil/toolchains:features.bzl", "FEATURE_NAMES")
@@ -116,7 +118,7 @@ def _test_dwp_action_structure_impl(env, target):
)
dwo_files.contains_exactly([expected_dwo])
-def _create_dwo_input_set_tree(name, linkstatic):
+def _create_dwo_input_set_tree(name, linkstatic, rule_under_test = cc_binary):
util.empty_file(name + "_lib1.cc")
util.empty_file(name + "_lib2.cc")
util.empty_file(name + "_lib_pregen.o")
@@ -143,7 +145,7 @@ def _create_dwo_input_set_tree(name, linkstatic):
name = name + "_empty_lib",
)
util.helper_target(
- cc_binary,
+ rule_under_test,
name = name + "_binary",
srcs = [
name + "_main.cc",
@@ -169,6 +171,15 @@ def _get_dwo_files_subject(env, target, dwp_path):
dwo_files.append(sf.short_path)
return env.expect.that_collection(dwo_files)
+def _get_dwo_files_output_group_subject(env, target):
+ package_prefix = target.label.package + "/"
+ dwo_files = [
+ f.short_path
+ for f in target[OutputGroupInfo].dwo_files.to_list()
+ if f.short_path.startswith(package_prefix)
+ ]
+ return env.expect.that_collection(dwo_files)
+
def _test_dwo_input_set_pic_mode(name):
"""Tests that the set of input .dwo files is as expected.
@@ -248,15 +259,355 @@ def _test_dwo_input_set_dynamic_mode_impl(env, target):
dwo_files.contains_exactly([expected_main])
+def _test_empty_inputs_in_fission_mode(name):
+ """Tests that a fission build with no .dwo inputs from the target package doesn't contribute any .dwo files.
+
+ Note: When there are zero .dwo inputs overall (as in OSS Bazel, where _stl defaults to None),
+ the .dwp action is optimized to an empty FileWrite. When implicit runtime dependencies such as
+ _stl contribute non-package .dwo files, CcGenerateDwp is created and we verify no package-scoped
+ .dwo files are included.
+ """
+ util.helper_target(
+ cc_library,
+ name = name + "_test_malloc",
+ srcs = [name + "_test_malloc_prebuilt.o"],
+ )
+ util.helper_target(
+ cc_library,
+ name = name + "_empty_lib",
+ )
+ util.helper_target(
+ cc_binary,
+ name = name + "_fission",
+ srcs = [name + "_prebuilt.o"],
+ link_extra_lib = name + "_empty_lib", # copybara:strip
+ malloc = name + "_test_malloc",
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_empty_inputs_in_fission_mode_impl,
+ target = name + "_fission",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_empty_inputs_in_fission_mode_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ action = env.expect.that_target(target).action_generating(dwp_path)
+
+ mnemonic = action.actual.mnemonic
+ if mnemonic == "CcGenerateDwp":
+ # Implicit runtime dependencies (such as _stl) can transitively contribute
+ # non-package .dwo inputs, causing CcGenerateDwp to be emitted.
+ dwo_files = _get_dwo_files_subject(env, target, dwp_path)
+ dwo_files.is_empty()
+ elif mnemonic == "FileWrite":
+ # Zero .dwo inputs overall: DWP generation is optimized to an empty FileWrite.
+ action.content().equals("")
+ else:
+ env.fail("Expected DWP action mnemonic to be 'CcGenerateDwp' or 'FileWrite', but got '%s'" % mnemonic)
+
+def _test_dwo_input_set_non_pic_mode(name):
+ """Same as _test_dwo_input_set_pic_mode, but for non-pic-mode linking.
+
+ That means .o files are linked in over their .pic.o counterparts and the
+ .dwo inputs should reflect this change.
+ """
+ _create_dwo_input_set_tree(name, linkstatic = True)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_input_set_non_pic_mode_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:fission": "yes",
+ },
+ )
+
+def _test_dwo_input_set_non_pic_mode_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ dwo_files = _get_dwo_files_subject(env, target, dwp_path)
+
+ pkg = target.label.package
+ prefix = target.label.name.replace("_binary", "")
+ lib_target = prefix + "_lib"
+ malloc_target = prefix + "_test_malloc"
+ bin_target = prefix + "_binary"
+
+ expected_templates = [
+ "{pkg}/_objs/{lib}/{prefix}_lib1.dwo",
+ "{pkg}/_objs/{lib}/{prefix}_lib2.dwo",
+ "{pkg}/_objs/{malloc}/{prefix}_test_malloc.dwo",
+ "{pkg}/_objs/{bin}/{prefix}_main.dwo",
+ ]
+ expected = [t.format(pkg = pkg, lib = lib_target, malloc = malloc_target, bin = bin_target, prefix = prefix) for t in expected_templates]
+ dwo_files.contains_exactly(expected)
+
+def _test_dwo_input_set_no_pic_for_binaries_mode(name):
+ """Same as _test_dwo_input_set_pic_mode, but for pic-mode configurations where binaries are specially linked without pic.
+
+ See CppConfiguration#usePicForBinaries
+ """
+ _create_dwo_input_set_tree(name, linkstatic = True)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_input_set_no_pic_for_binaries_mode_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:fission": "yes",
+ "//command_line_option:compilation_mode": "opt",
+ },
+ )
+
+def _test_dwo_input_set_no_pic_for_binaries_mode_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ dwo_files = _get_dwo_files_subject(env, target, dwp_path)
+
+ pkg = target.label.package
+ prefix = target.label.name.replace("_binary", "")
+ lib_target = prefix + "_lib"
+ malloc_target = prefix + "_test_malloc"
+ bin_target = prefix + "_binary"
+
+ expected_templates = [
+ "{pkg}/_objs/{lib}/{prefix}_lib1.dwo",
+ "{pkg}/_objs/{lib}/{prefix}_lib2.dwo",
+ "{pkg}/_objs/{malloc}/{prefix}_test_malloc.dwo",
+ "{pkg}/_objs/{bin}/{prefix}_main.dwo",
+ ]
+ expected = [t.format(pkg = pkg, lib = lib_target, malloc = malloc_target, bin = bin_target, prefix = prefix) for t in expected_templates]
+ dwo_files.contains_exactly(expected)
+
+def _create_custom_malloc_tree(name):
+ _create_dwo_input_set_tree(name, linkstatic = True)
+ util.helper_target(
+ cc_library,
+ name = name + "_custom_malloc",
+ srcs = [name + "_custom_malloc.cc"],
+ )
+
+def _test_dwo_input_set_custom_malloc(name):
+ """Tests that when --custom_malloc overrides a rule's default malloc, the custom malloc is the one that makes it into the input set."""
+ _create_custom_malloc_tree(name)
+ custom_malloc_label = Label("//{}:{}_custom_malloc".format(native.package_name(), name))
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_input_set_custom_malloc_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ "//command_line_option:custom_malloc": custom_malloc_label,
+ },
+ )
+
+def _test_dwo_input_set_custom_malloc_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ dwo_files = _get_dwo_files_subject(env, target, dwp_path)
+
+ pkg = target.label.package
+ prefix = target.label.name.replace("_binary", "")
+ lib_target = prefix + "_lib"
+ malloc_target = prefix + "_custom_malloc"
+ bin_target = prefix + "_binary"
+
+ expected_templates = [
+ "{pkg}/_objs/{lib}/{prefix}_lib1.pic.dwo",
+ "{pkg}/_objs/{lib}/{prefix}_lib2.pic.dwo",
+ "{pkg}/_objs/{malloc}/{prefix}_custom_malloc.pic.dwo",
+ "{pkg}/_objs/{bin}/{prefix}_main.pic.dwo",
+ ]
+ expected = [t.format(pkg = pkg, lib = lib_target, malloc = malloc_target, bin = bin_target, prefix = prefix) for t in expected_templates]
+ dwo_files.contains_exactly(expected)
+
+def _test_dwo_files_output_group_dbg_static_mode(name):
+ """Tests that the dwo_files output group contains direct and transitive .dwo files in dbg static mode."""
+ _create_dwo_input_set_tree(name, linkstatic = True)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_dbg_static_mode_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_dbg_static_mode_impl(env, target):
+ dwo_files = _get_dwo_files_output_group_subject(env, target)
+
+ pkg = target.label.package
+ prefix = target.label.name.replace("_binary", "")
+ lib_target = prefix + "_lib"
+ malloc_target = prefix + "_test_malloc"
+ bin_target = prefix + "_binary"
+
+ expected = [
+ "{pkg}/_objs/{lib}/{prefix}_lib1.pic.dwo".format(pkg = pkg, lib = lib_target, prefix = prefix),
+ "{pkg}/_objs/{lib}/{prefix}_lib2.pic.dwo".format(pkg = pkg, lib = lib_target, prefix = prefix),
+ "{pkg}/_objs/{malloc}/{prefix}_test_malloc.pic.dwo".format(pkg = pkg, malloc = malloc_target, prefix = prefix),
+ "{pkg}/_objs/{bin}/{prefix}_main.pic.dwo".format(pkg = pkg, bin = bin_target, prefix = prefix),
+ ]
+ dwo_files.contains_exactly(expected)
+
+def _test_dwo_files_output_group_dbg_dynamic_mode(name):
+ """Tests that the dwo_files output group contains only direct .dwo files in dbg dynamic mode."""
+ _create_dwo_input_set_tree(name, linkstatic = False)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_dbg_dynamic_mode_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_dbg_dynamic_mode_impl(env, target):
+ dwo_files = _get_dwo_files_output_group_subject(env, target)
+
+ pkg = target.label.package
+ prefix = target.label.name.replace("_binary", "")
+ bin_target = prefix + "_binary"
+ expected_main = "{pkg}/_objs/{bin}/{prefix}_main.pic.dwo".format(pkg = pkg, bin = bin_target, prefix = prefix)
+
+ dwo_files.contains_exactly([expected_main])
+
+def _test_dwo_files_output_group_fission_disabled(name):
+ """Tests that the dwo_files output group is empty when fission is disabled."""
+ _create_fission_binary(name)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_empty_impl,
+ target = name + "_fission",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "no",
+ },
+ )
+
+def _test_dwo_files_output_group_non_dbg_mode(name):
+ """Tests that the dwo_files output group is empty in non-dbg builds even when fission is enabled."""
+ _create_fission_binary(name)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_empty_impl,
+ target = name + "_fission",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:compilation_mode": "opt",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_empty_impl(env, target):
+ env.expect.that_target(target).output_group("dwo_files").is_empty()
+
+def _test_dwo_files_output_group_cc_test_static_with_build_test_dwp(name):
+ """Tests that a statically linked cc_test with --build_test_dwp builds a .dwp and leaves dwo_files empty."""
+ _create_dwo_input_set_tree(name, linkstatic = True, rule_under_test = cc_test)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_cc_test_static_with_build_test_dwp_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:build_test_dwp": True,
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_cc_test_static_with_build_test_dwp_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ env.expect.that_target(target).default_outputs().contains(dwp_path)
+ env.expect.that_target(target).output_group("dwo_files").is_empty()
+
+def _test_dwo_files_output_group_cc_test_static_without_build_test_dwp(name):
+ """Tests that a statically linked cc_test with --nobuild_test_dwp populates the dwo_files output group."""
+ _create_dwo_input_set_tree(name, linkstatic = True, rule_under_test = cc_test)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_cc_test_static_without_build_test_dwp_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:build_test_dwp": False,
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_cc_test_static_without_build_test_dwp_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ env.expect.that_target(target).default_outputs().not_contains(dwp_path)
+ _test_dwo_files_output_group_dbg_static_mode_impl(env, target)
+
+def _test_dwo_files_output_group_cc_test_dynamic_with_build_test_dwp(name):
+ """Tests that a dynamically linked cc_test populates the dwo_files output group even with --build_test_dwp."""
+ _create_dwo_input_set_tree(name, linkstatic = False, rule_under_test = cc_test)
+ cc_analysis_test(
+ name = name,
+ impl = _test_dwo_files_output_group_cc_test_dynamic_with_build_test_dwp_impl,
+ target = name + "_binary",
+ with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info],
+ config_settings = {
+ "//command_line_option:build_test_dwp": True,
+ "//command_line_option:compilation_mode": "dbg",
+ "//command_line_option:fission": "yes",
+ "//command_line_option:force_pic": True,
+ },
+ )
+
+def _test_dwo_files_output_group_cc_test_dynamic_with_build_test_dwp_impl(env, target):
+ executable = target[DefaultInfo].files_to_run.executable
+ dwp_path = executable.short_path + ".dwp"
+ env.expect.that_target(target).default_outputs().not_contains(dwp_path)
+ _test_dwo_files_output_group_dbg_dynamic_mode_impl(env, target)
+
def debug_info_packaging_tests(name):
+ tests = [
+ _test_dwp_output_names,
+ _test_dwp_builds_require_fission_mode_disabled,
+ _test_dwp_builds_require_fission_mode_enabled,
+ _test_dwp_action_structure,
+ _test_dwo_input_set_pic_mode,
+ _test_dwo_input_set_dynamic_mode,
+ _test_empty_inputs_in_fission_mode,
+ _test_dwo_input_set_non_pic_mode,
+ _test_dwo_input_set_no_pic_for_binaries_mode,
+ _test_dwo_input_set_custom_malloc,
+ ]
+ if bazel_features.cc.cc_common_is_in_rules_cc:
+ tests.extend([
+ _test_dwo_files_output_group_dbg_static_mode,
+ _test_dwo_files_output_group_dbg_dynamic_mode,
+ _test_dwo_files_output_group_fission_disabled,
+ _test_dwo_files_output_group_non_dbg_mode,
+ _test_dwo_files_output_group_cc_test_static_with_build_test_dwp,
+ _test_dwo_files_output_group_cc_test_static_without_build_test_dwp,
+ _test_dwo_files_output_group_cc_test_dynamic_with_build_test_dwp,
+ ])
test_suite(
name = name,
- tests = [
- _test_dwp_output_names,
- _test_dwp_builds_require_fission_mode_disabled,
- _test_dwp_builds_require_fission_mode_enabled,
- _test_dwp_action_structure,
- _test_dwo_input_set_pic_mode,
- _test_dwo_input_set_dynamic_mode,
- ],
+ tests = tests,
)
diff --git a/tests/cc/common/disallowed_copts_test.bzl b/tests/cc/common/disallowed_copts_test.bzl
index 1ad8441d1..6384acb3a 100644
--- a/tests/cc/common/disallowed_copts_test.bzl
+++ b/tests/cc/common/disallowed_copts_test.bzl
@@ -6,7 +6,7 @@ load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", "util")
load("//cc:cc_binary.bzl", "cc_binary")
load("//cc:cc_library.bzl", "cc_library")
-load("//tests/cc/testutil:cc_analysis_test.bzl", "MOCK_ALLOWLIST_TOOLCHAINS", "MOCK_EMPTY_ALLOWLIST_TOOLCHAINS", "cc_analysis_test")
+load("//tests/cc/testutil:cc_analysis_test.bzl", "MOCK_ALLOWLIST_TOOLCHAINS", "MOCK_EMPTY_ALLOWLIST_TOOLCHAINS", "MOCK_TARGET_ALLOWLIST_TOOLCHAINS", "cc_analysis_test")
def _test_pass_impl(_env, _target):
pass
@@ -19,7 +19,9 @@ def _test_fail_impl(env, target):
def _test_fail_empty_toolchain_allowlist_impl(env, target):
env.expect.that_target(target).failures().contains_predicate(
matching.contains(
- "- Flag '-Wno-error' (target is not in the allowlist '//custom:toolchain_allowlist'): Custom warning policy guidance.",
+ "- Flag '-Wno-error' in attribute 'copts' disallowed by '{}': Custom warning policy guidance.".format(
+ Label("//tests/cc/testutil/toolchains:k8-compiler_config_with_empty_allowlist"),
+ ),
),
)
@@ -111,6 +113,22 @@ def _test_no_copts_target_passes(name, **kwargs):
**kwargs
)
+def _test_disallowed_copt_in_toolchain_target_allowlist_passes(name, **kwargs):
+ util.helper_target(
+ cc_library,
+ name = name + "/test",
+ copts = ["-Wno-error"],
+ )
+ cc_analysis_test(
+ name = name,
+ impl = _test_pass_impl,
+ target = name + "/test",
+ config_settings = {
+ "//command_line_option:extra_toolchains": ",".join(MOCK_TARGET_ALLOWLIST_TOOLCHAINS),
+ },
+ **kwargs
+ )
+
def disallowed_copts_tests(name):
test_suite(
name = name,
@@ -118,6 +136,7 @@ def disallowed_copts_tests(name):
_test_disallowed_copt_fails,
_test_disallowed_copt_in_toolchain_allowlist_passes,
_test_disallowed_copt_cc_binary_in_toolchain_allowlist_passes,
+ _test_disallowed_copt_in_toolchain_target_allowlist_passes,
_test_disallowed_copt_with_empty_toolchain_allowlist_fails,
_test_valid_warning_passes,
_test_no_copts_target_passes,
diff --git a/tests/cc/testutil/cc_analysis_test.bzl b/tests/cc/testutil/cc_analysis_test.bzl
index 5557975f8..53fb8566c 100644
--- a/tests/cc/testutil/cc_analysis_test.bzl
+++ b/tests/cc/testutil/cc_analysis_test.bzl
@@ -16,6 +16,10 @@ MOCK_EMPTY_ALLOWLIST_TOOLCHAINS = [
"//tests/cc/testutil/toolchains:cc-toolchain-k8-with-empty-allowlist",
]
+MOCK_TARGET_ALLOWLIST_TOOLCHAINS = [
+ "//tests/cc/testutil/toolchains:cc-toolchain-k8-with-target-allowlist",
+]
+
def cc_analysis_test(name, with_features = None, test_features = [], with_action_configs = [], **kwargs):
"""Runs an analysis_test with the a mock C++ toolchain.
diff --git a/tests/cc/testutil/toolchains/BUILD b/tests/cc/testutil/toolchains/BUILD
index 3c750cdf1..0b65bf396 100644
--- a/tests/cc/testutil/toolchains/BUILD
+++ b/tests/cc/testutil/toolchains/BUILD
@@ -59,6 +59,11 @@ string_flag(
build_setting_default = "",
)
+string_list_flag(
+ name = "dynamic_library_pattern",
+ build_setting_default = [],
+)
+
# "everything" is a filegroup that the implementation of --grte_top (more specifically, the
# LibcTopLabelConverter in
# third_party/bazel/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java)
@@ -449,7 +454,6 @@ cc_toolchain_config(
abi_version = "local",
action_configs = ["c++-header-analysis"],
allowlist_provider = ":mock_allowlist",
- allowlist_target_label = "//custom:toolchain_allowlist",
artifact_name_patterns = {},
builtin_sysroot = "/usr/grte/v1",
cc_target_os = "",
@@ -497,7 +501,6 @@ cc_toolchain_config(
abi_version = "local",
action_configs = ["c++-header-analysis"],
allowlist_provider = ":empty_mock_allowlist",
- allowlist_target_label = "//custom:toolchain_allowlist",
artifact_name_patterns = {},
builtin_sysroot = "/usr/grte/v1",
cc_target_os = "",
@@ -539,3 +542,52 @@ toolchain(
toolchain = ":cc-compiler-k8-with-empty-allowlist",
toolchain_type = "//cc:toolchain_type",
)
+
+cc_toolchain_config(
+ name = "k8-compiler_config_with_target_allowlist",
+ abi_libc_version = "local",
+ abi_version = "local",
+ action_configs = ["c++-header-analysis"],
+ artifact_name_patterns = {},
+ builtin_sysroot = "/usr/grte/v1",
+ cc_target_os = "",
+ compiler = "compiler",
+ cpu = "k8",
+ cxx_builtin_include_directories = [],
+ feature_names = [],
+ host_system_name = "local",
+ make_variables = {},
+ target_allowlist = [
+ "//tests/cc/common:test_disallowed_copt_in_toolchain_target_allowlist_passes/test",
+ ],
+ target_libc = "local",
+ target_system_name = "local",
+ tool_paths = {},
+ toolchain_identifier = "mock-toolchain-k8",
+)
+
+cc_toolchain(
+ name = "cc-compiler-k8-with-target-allowlist",
+ all_files = ":every-file",
+ ar_files = "ar-k8",
+ as_files = "as-k8",
+ compiler_files = "compile-k8",
+ compiler_files_without_includes = "compile-without-includes-k8",
+ coverage_files = "coverage-file",
+ dwp_files = "dwp-k8",
+ licenses = ["unencumbered"],
+ linker_files = "link-k8",
+ module_map = "crosstool.cppmap",
+ objcopy_files = "objcopy-k8",
+ output_licenses = ["unencumbered"],
+ strip_files = ":every-file",
+ supports_header_parsing = 1,
+ toolchain_config = ":k8-compiler_config_with_target_allowlist",
+ toolchain_identifier = "mock-toolchain-k8",
+)
+
+toolchain(
+ name = "cc-toolchain-k8-with-target-allowlist",
+ toolchain = ":cc-compiler-k8-with-target-allowlist",
+ toolchain_type = "//cc:toolchain_type",
+)
diff --git a/tests/cc/testutil/toolchains/additional_toolchains.bzl b/tests/cc/testutil/toolchains/additional_toolchains.bzl
index 1a8320cb7..0b2f7c67d 100644
--- a/tests/cc/testutil/toolchains/additional_toolchains.bzl
+++ b/tests/cc/testutil/toolchains/additional_toolchains.bzl
@@ -1,6 +1,9 @@
"""Additional mock toolchains besides the mock C++ toolchain."""
def mock_additional_toolchains():
+ # type: () -> None
pass
-ADDITIONAL_MOCK_TOOLCHAINS = []
+# TODO(https://github.com/bazelbuild/bazel/pull/30373): Use a variable annotation
+# when Bazel 8.8 is the minimum supported version.
+ADDITIONAL_MOCK_TOOLCHAINS = [] # type: list[str]
diff --git a/tests/cc/testutil/toolchains/cc_toolchain_config.bzl b/tests/cc/testutil/toolchains/cc_toolchain_config.bzl
index 3ed8bbb70..60c332531 100644
--- a/tests/cc/testutil/toolchains/cc_toolchain_config.bzl
+++ b/tests/cc/testutil/toolchains/cc_toolchain_config.bzl
@@ -2057,6 +2057,9 @@ def _impl(ctx):
object_file_extension = ctx.attr._object_file_extension[BuildSettingInfo].value
if object_file_extension:
artifact_name_patterns.append(_get_artifact_name_pattern("object_file", "", object_file_extension))
+ dynamic_library_pattern = ctx.attr._dynamic_library_pattern[BuildSettingInfo].value
+ if dynamic_library_pattern:
+ artifact_name_patterns.append(_get_artifact_name_pattern("dynamic_library", dynamic_library_pattern[0], dynamic_library_pattern[1]))
action_configs = []
@@ -2106,11 +2109,12 @@ def _impl(ctx):
extra_args = {}
if bazel_features.cc.cc_common_is_in_rules_cc:
allowlist = ctx.attr.allowlist_provider[PackageSpecificationInfo] if ctx.attr.allowlist_provider != None else None
- allowlist_target_label = ctx.attr.allowlist_target_label or (str(ctx.attr.allowlist_provider.label) if ctx.attr.allowlist_provider else None)
extra_args["disallowed_copts_infos"] = [struct(
flags = ctx.attr.disallowed_copts,
+ package_allowlist = allowlist,
allowlist = allowlist,
- allowlist_target_label = allowlist_target_label,
+ target_allowlist = ctx.attr.target_allowlist if ctx.attr.target_allowlist else None,
+ policy_label = str(ctx.label),
error_message = ctx.attr.error_message or None,
)]
@@ -2156,7 +2160,7 @@ cc_toolchain_config = rule(
"artifact_name_patterns": attr.string_list_dict(),
"disallowed_copts": attr.string_list(default = ["-w", "-Wno-error"]),
"allowlist_provider": attr.label(providers = [PackageSpecificationInfo]),
- "allowlist_target_label": attr.string(),
+ "target_allowlist": attr.string_list(),
"error_message": attr.string(),
"cc_target_os": attr.string(),
"builtin_sysroot": attr.string(default = "/usr/grte/v1"),
@@ -2166,6 +2170,7 @@ cc_toolchain_config = rule(
"_with_features": attr.label(default = Label("//tests/cc/testutil/toolchains:with_features")),
"_with_action_configs": attr.label(default = Label("//tests/cc/testutil/toolchains:with_action_configs")),
"_object_file_extension": attr.label(default = Label("//tests/cc/testutil/toolchains:object_file_extension")),
+ "_dynamic_library_pattern": attr.label(default = Label("//tests/cc/testutil/toolchains:dynamic_library_pattern")),
},
provides = [CcToolchainConfigInfo],
executable = True,
diff --git a/tests/coverage/BUILD.bazel b/tests/coverage/BUILD.bazel
new file mode 100644
index 000000000..c7cc95327
--- /dev/null
+++ b/tests/coverage/BUILD.bazel
@@ -0,0 +1,9 @@
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
+sh_test(
+ name = "collect_cc_coverage_test",
+ size = "small",
+ srcs = ["collect_cc_coverage_test.sh"],
+ data = ["//cc/coverage:srcs"],
+ target_compatible_with = ["@platforms//os:linux"],
+)
diff --git a/tests/coverage/collect_cc_coverage_test.sh b/tests/coverage/collect_cc_coverage_test.sh
new file mode 100755
index 000000000..3f380038a
--- /dev/null
+++ b/tests/coverage/collect_cc_coverage_test.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+# Copyright 2026 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.
+
+set -euo pipefail
+
+readonly SCRIPT="${TEST_SRCDIR}/${TEST_WORKSPACE}/cc/coverage/collect_cc_coverage.sh"
+readonly WORK_DIR="$(mktemp -d "${TEST_TMPDIR:-/tmp}/cc-coverage-args.XXXXXXXX")"
+trap 'rm -rf "${WORK_DIR}"' EXIT
+
+mkdir -p "${WORK_DIR}/coverage" "${WORK_DIR}/object files"
+readonly OBJECT_WITH_SPACE="${WORK_DIR}/object files/runtime.o"
+readonly SECOND_OBJECT="${WORK_DIR}/second.o"
+touch "${WORK_DIR}/coverage/coverage.profraw" "${OBJECT_WITH_SPACE}" "${SECOND_OBJECT}"
+printf '%s\n' "${OBJECT_WITH_SPACE}" "${SECOND_OBJECT}" > "${WORK_DIR}/runtime_objects_list.txt"
+printf '%s\n' "${WORK_DIR}/runtime_objects_list.txt" > "${WORK_DIR}/manifest.txt"
+
+cat > "${WORK_DIR}/llvm-profdata" <<'EOF'
+#!/usr/bin/env bash
+set -euo pipefail
+[[ "$#" -eq 4 && "$1" == merge && "$2" == -output ]]
+touch "$3"
+EOF
+
+cat > "${WORK_DIR}/llvm-cov" <<'EOF'
+#!/usr/bin/env bash
+set -euo pipefail
+printf '%s\n' "$@" > "${LLVM_COV_ARGS}"
+printf 'TN:\n'
+EOF
+chmod +x "${WORK_DIR}/llvm-profdata" "${WORK_DIR}/llvm-cov"
+
+COVERAGE_DIR="${WORK_DIR}/coverage" \
+COVERAGE_MANIFEST="${WORK_DIR}/manifest.txt" \
+GENERATE_LLVM_LCOV=1 \
+LLVM_PROFDATA="${WORK_DIR}/llvm-profdata" \
+LLVM_COV="${WORK_DIR}/llvm-cov" \
+LLVM_COV_ARGS="${WORK_DIR}/llvm-cov.args" \
+ bash "${SCRIPT}"
+
+printf '%s\n' \
+ export \
+ -instr-profile "${WORK_DIR}/coverage/_cc_coverage.dat.data" \
+ -format=lcov \
+ '-ignore-filename-regex=^/tmp/.+' \
+ -object "${OBJECT_WITH_SPACE}" \
+ -object "${SECOND_OBJECT}" > "${WORK_DIR}/expected.args"
+diff -u "${WORK_DIR}/expected.args" "${WORK_DIR}/llvm-cov.args"
diff --git a/tests/generated_tool_path/BUILD.bazel b/tests/generated_tool_path/BUILD.bazel
new file mode 100644
index 000000000..e2eb86cc4
--- /dev/null
+++ b/tests/generated_tool_path/BUILD.bazel
@@ -0,0 +1,61 @@
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+load("//cc/toolchains:cc_toolchain.bzl", "cc_toolchain")
+load("//cc/toolchains:cc_toolchain_alias.bzl", "cc_toolchain_alias")
+load(":generated_tool_path_test.bzl", "generated_tool_cc_toolchain_config", "maybe_define_generated_tool_path_targets")
+
+package(default_visibility = ["//visibility:private"])
+
+constraint_setting(name = "toolchain_selector")
+
+constraint_value(
+ name = "use_test_toolchain",
+ constraint_setting = ":toolchain_selector",
+)
+
+platform(
+ name = "test_platform",
+ constraint_values = [":use_test_toolchain"],
+)
+
+filegroup(name = "empty")
+
+# A compiler wrapper generated by the build, as used by e.g. the Apple C++ toolchain.
+write_file(
+ name = "gcc",
+ out = "gcc.sh",
+ content = [
+ "#!/bin/sh",
+ "exit 0",
+ ],
+ is_executable = True,
+)
+
+generated_tool_cc_toolchain_config(
+ name = "config",
+ gcc = ":gcc",
+)
+
+cc_toolchain(
+ name = "cc_toolchain",
+ all_files = ":gcc",
+ ar_files = ":empty",
+ as_files = ":empty",
+ compiler_files = ":gcc",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ toolchain_config = ":config",
+ toolchain_identifier = "generated-tool-path-test",
+)
+
+toolchain(
+ name = "cc_toolchain_registration",
+ target_compatible_with = [":use_test_toolchain"],
+ toolchain = ":cc_toolchain",
+ toolchain_type = "//cc:toolchain_type",
+)
+
+cc_toolchain_alias(name = "current_cc_toolchain")
+
+maybe_define_generated_tool_path_targets()
diff --git a/tests/generated_tool_path/generated_tool_path_test.bzl b/tests/generated_tool_path/generated_tool_path_test.bzl
new file mode 100644
index 000000000..02b5784e5
--- /dev/null
+++ b/tests/generated_tool_path/generated_tool_path_test.bzl
@@ -0,0 +1,72 @@
+"""Analysis test for tool paths that refer to files generated by the build."""
+
+load("@bazel_features//:features.bzl", "bazel_features")
+load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
+load("//cc:cc_toolchain_config_lib.bzl", "tool_path") # buildifier: disable=deprecated-function
+load("//cc/common:cc_common.bzl", "cc_common")
+
+def _generated_tool_cc_toolchain_config_impl(ctx):
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = "generated-tool-path-test",
+ host_system_name = "local",
+ target_system_name = "local",
+ target_cpu = "k8",
+ target_libc = "local",
+ compiler = "compiler",
+ abi_version = "local",
+ abi_libc_version = "local",
+ tool_paths = [
+ # The path of a generated file is relative to the execution root rather than to the
+ # package of the toolchain.
+ tool_path(name = "gcc", path = ctx.file.gcc.path),
+ tool_path(name = "ar", path = "/usr/bin/ar"),
+ tool_path(name = "cpp", path = "/usr/bin/cpp"),
+ tool_path(name = "ld", path = "/usr/bin/ld"),
+ tool_path(name = "nm", path = "/usr/bin/nm"),
+ tool_path(name = "objdump", path = "/usr/bin/objdump"),
+ tool_path(name = "strip", path = "/usr/bin/strip"),
+ ],
+ )
+
+generated_tool_cc_toolchain_config = rule(
+ implementation = _generated_tool_cc_toolchain_config_impl,
+ attrs = {
+ "gcc": attr.label(
+ allow_single_file = True,
+ mandatory = True,
+ ),
+ },
+)
+
+def _generated_tool_path_test_impl(env, target):
+ all_files = target[cc_common.CcToolchainInfo].all_files.to_list()
+ gcc = [file for file in all_files if file.basename == "gcc.sh"]
+ env.expect.that_collection(gcc).has_size(1)
+ cc = target[platform_common.TemplateVariableInfo].variables["CC"]
+ env.expect.that_str(cc).equals(gcc[0].path)
+
+def generated_tool_path_test(name, target):
+ analysis_test(
+ name = name,
+ target = target,
+ impl = _generated_tool_path_test_impl,
+ config_settings = {
+ "//command_line_option:extra_toolchains": [
+ Label("//tests/generated_tool_path:cc_toolchain_registration"),
+ ],
+ "//command_line_option:platforms": [
+ Label("//tests/generated_tool_path:test_platform"),
+ ],
+ },
+ )
+
+def maybe_define_generated_tool_path_targets():
+ # cc_toolchain is implemented in rules_cc only for Bazel 9+. For older Bazel versions, the
+ # native rule is used.
+ if not bazel_features.cc.cc_common_is_in_rules_cc:
+ return
+ generated_tool_path_test(
+ name = "generated_tool_path_test",
+ target = ":current_cc_toolchain",
+ )
diff --git a/tests/integration/BUILD b/tests/integration/BUILD
index 02a083ba5..66b49f31f 100644
--- a/tests/integration/BUILD
+++ b/tests/integration/BUILD
@@ -9,19 +9,28 @@ exports_files(
visibility = ["//tests:__subpackages__"],
)
+VERSION_GATE = [] if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"] # copybara-uncomment-this-please
+# VERSION_GATE = ["@platforms//:incompatible"] # copybara-comment-this-out-please
+
rules_cc_integration_test(
name = "cc_tests",
shard_count = 4,
test_script = "cc_tests.sh",
)
+rules_cc_integration_test(
+ name = "cc_linking_tests",
+ tags = ["notap"],
+ target_compatible_with = [] if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"],
+ test_script = "cc_linking_tests.sh",
+)
+
# Tests migrated from bazelbuild/bazel/src/test/shell/bazel/cc_integration_test.sh
# In a separate file from cc_tests.sh to make migration clearer.
rules_cc_integration_test(
name = "cc_integration_test",
shard_count = 10,
target_compatible_with = select({
- "@platforms//os:macos": ["@platforms//:incompatible"],
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}) if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"],
@@ -40,10 +49,7 @@ rules_cc_integration_test(
rules_cc_integration_test(
name = "cc_shared_library_tests",
- # These could probably be made to work on all CI platforms,
- # but the default toolchains are not properly configured for it.
target_compatible_with = select({
- "@platforms//os:macos": ["@platforms//:incompatible"],
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
@@ -60,25 +66,20 @@ rules_cc_integration_test(
test_script = "fission_tests.sh",
)
-STATIC_LIBRARY_VERSION_GATE = [] if bazel_features.cc.cc_common_is_in_rules_cc else ["@platforms//:incompatible"] # copybara-uncomment-this-please
-# STATIC_LIBRARY_VERSION_GATE = ["@platforms//:incompatible"] # copybara-comment-this-out-please
-
rules_cc_integration_test(
name = "cc_static_library_failure_tests",
tags = ["notap"],
target_compatible_with = select({
+ # The default Apple toolchain does not provide symbol_check.
"@platforms//os:macos": ["@platforms//:incompatible"],
- "//conditions:default": STATIC_LIBRARY_VERSION_GATE,
+ "//conditions:default": VERSION_GATE,
}),
test_script = "cc_static_library_failure_tests.sh",
)
rules_cc_integration_test(
name = "cc_shared_library_toolchain_tests",
- # These could probably be made to work on all CI platforms,
- # but the default toolchains are not properly configured for it.
target_compatible_with = select({
- "@platforms//os:macos": ["@platforms//:incompatible"],
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [] if _features_ge("9.2.0") else ["@platforms//:incompatible"],
}),
diff --git a/tests/integration/cc_integration_test.sh b/tests/integration/cc_integration_test.sh
index 0945605a5..4b7e39672 100755
--- a/tests/integration/cc_integration_test.sh
+++ b/tests/integration/cc_integration_test.sh
@@ -324,6 +324,10 @@ def _cc_lib_impl(ctx):
language = "c++",
compilation_outputs = compilation_outputs,
linking_contexts = linking_contexts,
+ disallow_dynamic_library = not cc_common.is_enabled(
+ feature_configuration = feature_configuration,
+ feature_name = "supports_dynamic_linker",
+ ),
)
library = linking_outputs.library_to_link
files = []
@@ -789,13 +793,6 @@ EOF
function test_aspect_accessing_args_link_action_with_tree_artifact() {
is_bazel || return 0
- # This test assumes the presence of "nodeps" dynamic libraries, which do not
- # function on Apple platforms.
- if is_darwin; then
- return 0
- fi
-
-
local package="${FUNCNAME[0]}"
mkdir -p "${package}"
cat > "${package}/makes_tree_artifacts.sh" <