From 8734929514db11d41a1bbb923a71c6fa722cc102 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Thu, 24 Sep 2026 12:37:53 -0700 Subject: [PATCH 01/61] Remove release tests, add check for green commit (#898) We don't want to block on env differences from buildkite to github actions. This makes sure the commit the release being cut from is green. Closes #898 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/898 from keith:ks/remove-release-tests-add-check-for-green-commit 2c6c094a91bc26734c3c2d224ee02d2a19a498e1 PiperOrigin-RevId: 987685459 Change-Id: I85ab425de0d086b49a8410ad224b079b7859aa13 --- .github/workflows/release.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) 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 }} From 327ab4a6aadabc9637e129b336f1408f725c153f Mon Sep 17 00:00:00 2001 From: armandomontanez Date: Tue, 29 Sep 2026 00:36:43 -0700 Subject: [PATCH 02/61] Rename INTERNAL_VISIBILITY to PUBLIC_IF_NOT_GOOGLE (#910) Renames INTERNAL_VISIBILITY to better clarify the intent of the variable, and communicate to both Bazel and Google-internal users what this variable implies. Also restricts the visibilty of the visibility.bzl file to hopefully prevent hyrum's law from taking too strong a hold on this non-private file. Closes #910 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/910 from armandomontanez:armando/add-visibility-comment 2d87d58e9e49b18475e55d7669d0edf315f2b6b6 PiperOrigin-RevId: 990112200 Change-Id: I55c5a5acf45634125d2e5e6cede0b6efc8704d7c --- cc/common/BUILD | 13 +++---------- cc/common/cc_debug_helper.bzl | 4 ++-- cc/common/cc_helper.bzl | 4 ++-- cc/common/cc_helper_internal.bzl | 2 +- cc/private/BUILD | 6 ++++++ cc/private/cc_info.bzl | 2 +- cc/private/link/create_extra_link_time_library.bzl | 2 +- cc/{common => private}/visibility.bzl | 6 +++++- 8 files changed, 21 insertions(+), 18 deletions(-) rename cc/{common => private}/visibility.bzl (91%) 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..92bb753c9 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", 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/private/BUILD b/cc/private/BUILD index 9959fecee..02c25bd26 100644 --- a/cc/private/BUILD +++ b/cc/private/BUILD @@ -124,3 +124,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_info.bzl b/cc/private/cc_info.bzl index 6b3dcbeda..76eca9572 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( diff --git a/cc/private/link/create_extra_link_time_library.bzl b/cc/private/link/create_extra_link_time_library.bzl index 74165962d..27a3d05db 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. 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 = [] From 1f56261526d8f76f895221612c87f074fcbfe6b0 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 29 Sep 2026 23:14:40 -0700 Subject: [PATCH 03/61] Fix cc_shared_library locations with rules_python on windows (#909) rules_python switched to using --enable_runfiles on windows which revealed a bug that we weren't copying dependent dylibs for cc_shared_library as we do for cc_binary. Closes #909 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/909 from keith:ks/fix-cc_shared_library-locations-with-rules_python-on-windows 674b12566bde94a0b967f24b7ff04f1e834506a5 PiperOrigin-RevId: 990763525 Change-Id: I8fe763ae5623bf54e0cc90d78a002110c6b2383b --- MODULE.bazel | 4 +--- cc/common/cc_helper.bzl | 16 ++++++++++++++++ cc/private/rules_impl/cc_binary_impl.bzl | 17 +---------------- .../rules_impl/cc_shared_library_impl.bzl | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index f0077955f..420705b60 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -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/common/cc_helper.bzl b/cc/common/cc_helper.bzl index 92bb753c9..a8d1ea502 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -240,6 +240,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(): @@ -1226,6 +1241,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/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index a329ae28d..fc4c2b23c 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -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 @@ -731,7 +716,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 diff --git a/cc/private/rules_impl/cc_shared_library_impl.bzl b/cc/private/rules_impl/cc_shared_library_impl.bzl index b241381c4..44abc8a13 100644 --- a/cc/private/rules_impl/cc_shared_library_impl.bzl +++ b/cc/private/rules_impl/cc_shared_library_impl.bzl @@ -736,6 +736,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 From 2251f97531cb75c1caf6b1abf3ecece45909ee94 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 30 Sep 2026 14:08:21 -0700 Subject: [PATCH 04/61] Use --remote_download_all on windows (#915) This temporarily works around a bazel bug where windows bwtb produces invalid results https://github.com/bazelbuild/bazel/pull/31423 Closes #915 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/915 from keith:ks/use-remote_download_all-on-windows 76062580a930200f239b64414e1330a15948b8c4 PiperOrigin-RevId: 991211475 Change-Id: I27559e370ba55834abd365983375b299780a1ee5 --- .bazelrc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.bazelrc b/.bazelrc index 34bed5573..e1b2f6f4a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,7 @@ +common --enable_platform_specific_config +# TODO: Remove https://github.com/bazelbuild/bazel/pull/31423 +common:windows --remote_download_all + # Required by googletest 1.17. common --cxxopt=-std=c++17 test --test_env=PATH=/bin:/usr/bin:/usr/local/bin From f0a3fa7aa8250f733dc598021f069e7d32072b68 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 30 Sep 2026 23:09:05 -0700 Subject: [PATCH 05/61] Bump apple_support dep (#903) We switched the default toolchain to a rules based one moving forward. This also required a integration test change since previously the rules based toolchain files weren't in the integration test inputs, but now they are required. Closes #903 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/903 from keith:ks/bump-apple_support-dep 554150404db1a2bf78b64eac348fa9ee64559feb PiperOrigin-RevId: 991449012 Change-Id: I54fbdb4a8e726e03186340f4ec27e33a9ac8aad3 --- MODULE.bazel | 2 +- cc/toolchains/args/BUILD | 27 +++++++++++++++++++ cc/toolchains/args/compile_flags/BUILD | 6 +++++ cc/toolchains/args/compiler_input_flags/BUILD | 6 +++++ .../args/compiler_output_flags/BUILD | 6 +++++ cc/toolchains/args/def_file/BUILD | 6 +++++ cc/toolchains/args/dependency_file/BUILD | 6 +++++ .../args/external_include_paths/BUILD | 6 +++++ cc/toolchains/args/fission_flags/BUILD | 6 +++++ cc/toolchains/args/generate_linkmap/BUILD | 6 +++++ cc/toolchains/args/include_flags/BUILD | 6 +++++ cc/toolchains/args/layering_check/BUILD | 6 +++++ cc/toolchains/args/libraries_to_link/BUILD | 9 +++++++ .../args/libraries_to_link/private/BUILD | 9 +++++++ .../args/library_search_directories/BUILD | 6 +++++ cc/toolchains/args/link_flags/BUILD | 6 +++++ cc/toolchains/args/linker_param_file/BUILD | 6 +++++ cc/toolchains/args/linkstamp_flags/BUILD | 6 +++++ cc/toolchains/args/objc_arc_flags/BUILD | 6 +++++ cc/toolchains/args/pic_flags/BUILD | 6 +++++ cc/toolchains/args/preprocessor_defines/BUILD | 6 +++++ cc/toolchains/args/random_seed/BUILD | 6 +++++ .../runtime_library_search_directories/BUILD | 6 +++++ cc/toolchains/args/sanitize_pwd/BUILD | 6 +++++ cc/toolchains/args/shared_flag/BUILD | 6 +++++ cc/toolchains/args/soname_flags/BUILD | 6 +++++ cc/toolchains/args/static_libgcc/BUILD | 6 +++++ cc/toolchains/args/strip_debug_symbols/BUILD | 6 +++++ cc/toolchains/args/strip_flags/BUILD | 6 +++++ cc/toolchains/args/thin_lto/BUILD | 6 +++++ 30 files changed, 202 insertions(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 420705b60..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") diff --git a/cc/toolchains/args/BUILD b/cc/toolchains/args/BUILD index 9625c3d9e..440a79864 100644 --- a/cc/toolchains/args/BUILD +++ b/cc/toolchains/args/BUILD @@ -61,5 +61,32 @@ 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/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..07235fe21 100644 --- a/cc/toolchains/args/compiler_input_flags/BUILD +++ b/cc/toolchains/args/compiler_input_flags/BUILD @@ -32,3 +32,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..b7350c402 100644 --- a/cc/toolchains/args/compiler_output_flags/BUILD +++ b/cc/toolchains/args/compiler_output_flags/BUILD @@ -45,3 +45,9 @@ cc_args( 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/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/library_search_directories/BUILD b/cc/toolchains/args/library_search_directories/BUILD index 1ed8b1487..d3f468230 100644 --- a/cc/toolchains/args/library_search_directories/BUILD +++ b/cc/toolchains/args/library_search_directories/BUILD @@ -16,3 +16,9 @@ cc_args( iterate_over = "//cc/toolchains/variables:library_search_directories", requires_not_none = "//cc/toolchains/variables:library_search_directories", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/link_flags/BUILD b/cc/toolchains/args/link_flags/BUILD index 58d38a31f..e57c2747a 100644 --- a/cc/toolchains/args/link_flags/BUILD +++ b/cc/toolchains/args/link_flags/BUILD @@ -58,3 +58,9 @@ cc_args( format = {"output_execpath": "//cc/toolchains/variables:output_execpath"}, requires_not_none = "//cc/toolchains/variables:output_execpath", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/linker_param_file/BUILD b/cc/toolchains/args/linker_param_file/BUILD index b9d4096df..ab50ec440 100644 --- a/cc/toolchains/args/linker_param_file/BUILD +++ b/cc/toolchains/args/linker_param_file/BUILD @@ -27,3 +27,9 @@ cc_args( format = {"param_file": "//cc/toolchains/variables:linker_param_file"}, requires_not_none = "//cc/toolchains/variables:linker_param_file", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/linkstamp_flags/BUILD b/cc/toolchains/args/linkstamp_flags/BUILD index a2e2bf06c..689026950 100644 --- a/cc/toolchains/args/linkstamp_flags/BUILD +++ b/cc/toolchains/args/linkstamp_flags/BUILD @@ -16,3 +16,9 @@ cc_args( iterate_over = "//cc/toolchains/variables:linkstamp_paths", requires_not_none = "//cc/toolchains/variables:linkstamp_paths", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/objc_arc_flags/BUILD b/cc/toolchains/args/objc_arc_flags/BUILD index 06d44c3b8..0262ac2a6 100644 --- a/cc/toolchains/args/objc_arc_flags/BUILD +++ b/cc/toolchains/args/objc_arc_flags/BUILD @@ -24,3 +24,9 @@ cc_args( args = ["-fno-objc-arc"], requires_not_none = "//cc/toolchains/variables:no_objc_arc", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/pic_flags/BUILD b/cc/toolchains/args/pic_flags/BUILD index 4ff86da6c..c0621da1d 100644 --- a/cc/toolchains/args/pic_flags/BUILD +++ b/cc/toolchains/args/pic_flags/BUILD @@ -42,3 +42,9 @@ cc_args( requires_not_none = "//cc/toolchains/variables:force_pic", visibility = ["//:__subpackages__"], ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/preprocessor_defines/BUILD b/cc/toolchains/args/preprocessor_defines/BUILD index d7c3267dd..e32268ef3 100644 --- a/cc/toolchains/args/preprocessor_defines/BUILD +++ b/cc/toolchains/args/preprocessor_defines/BUILD @@ -16,3 +16,9 @@ cc_args( format = {"preprocessor_defines": "//cc/toolchains/variables:preprocessor_defines"}, iterate_over = "//cc/toolchains/variables:preprocessor_defines", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/random_seed/BUILD b/cc/toolchains/args/random_seed/BUILD index 97f56a33f..0087d2f7f 100644 --- a/cc/toolchains/args/random_seed/BUILD +++ b/cc/toolchains/args/random_seed/BUILD @@ -16,3 +16,9 @@ cc_args( 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/runtime_library_search_directories/BUILD b/cc/toolchains/args/runtime_library_search_directories/BUILD index 5cfadaa9f..20cfaadcf 100644 --- a/cc/toolchains/args/runtime_library_search_directories/BUILD +++ b/cc/toolchains/args/runtime_library_search_directories/BUILD @@ -114,3 +114,9 @@ cc_nested_args( }, iterate_over = "//cc/toolchains/variables:runtime_library_search_directories", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/sanitize_pwd/BUILD b/cc/toolchains/args/sanitize_pwd/BUILD index a280c1751..cb7cbb307 100644 --- a/cc/toolchains/args/sanitize_pwd/BUILD +++ b/cc/toolchains/args/sanitize_pwd/BUILD @@ -22,3 +22,9 @@ cc_args( "//conditions:default": {"PWD": "/proc/self/cwd"}, }), ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/shared_flag/BUILD b/cc/toolchains/args/shared_flag/BUILD index b01c9eafe..7d6cf1a8f 100644 --- a/cc/toolchains/args/shared_flag/BUILD +++ b/cc/toolchains/args/shared_flag/BUILD @@ -16,3 +16,9 @@ cc_args( args = ["-shared"], visibility = ["//visibility:public"], ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/soname_flags/BUILD b/cc/toolchains/args/soname_flags/BUILD index 304cccecb..10e0baa7e 100644 --- a/cc/toolchains/args/soname_flags/BUILD +++ b/cc/toolchains/args/soname_flags/BUILD @@ -36,3 +36,9 @@ cc_args( target_compatible_with = ["@platforms//os:linux"], visibility = ["//visibility:public"], ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/static_libgcc/BUILD b/cc/toolchains/args/static_libgcc/BUILD index 422842750..bbdf50d19 100644 --- a/cc/toolchains/args/static_libgcc/BUILD +++ b/cc/toolchains/args/static_libgcc/BUILD @@ -20,3 +20,9 @@ cc_args( args = ["-static-libgcc"], requires_any_of = [":static_link_cpp_runtimes_enabled"], ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/strip_debug_symbols/BUILD b/cc/toolchains/args/strip_debug_symbols/BUILD index b94c5b19f..d5a4ab9b2 100644 --- a/cc/toolchains/args/strip_debug_symbols/BUILD +++ b/cc/toolchains/args/strip_debug_symbols/BUILD @@ -15,3 +15,9 @@ cc_args( args = ["-Wl,-S"], requires_not_none = "//cc/toolchains/variables:strip_debug_symbols", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/strip_flags/BUILD b/cc/toolchains/args/strip_flags/BUILD index e6c466834..f13961fae 100644 --- a/cc/toolchains/args/strip_flags/BUILD +++ b/cc/toolchains/args/strip_flags/BUILD @@ -66,3 +66,9 @@ cc_args( format = {"input_file": "//cc/toolchains/variables:input_file"}, requires_not_none = "//cc/toolchains/variables:input_file", ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) diff --git a/cc/toolchains/args/thin_lto/BUILD b/cc/toolchains/args/thin_lto/BUILD index 774be376a..b54bbdd84 100644 --- a/cc/toolchains/args/thin_lto/BUILD +++ b/cc/toolchains/args/thin_lto/BUILD @@ -178,3 +178,9 @@ cc_feature( feature_name = "thin_lto", visibility = ["//visibility:public"], ) + +filegroup( + name = "srcs", + srcs = ["BUILD"], + visibility = ["//cc/toolchains/args:__pkg__"], +) From 59bedc63681875c29b26d29b440f403fcdcb590d Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 30 Sep 2026 23:15:16 -0700 Subject: [PATCH 06/61] Set coverage_files from rules based toolchains (#904) Having actions for these is relatively new. Closes #904 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/904 from keith:ks/set-coverage_files-from-rules-based-toolchains 4af217544df574c860ce16cffd3e32139a5dce2d PiperOrigin-RevId: 991450972 Change-Id: I18d5c2b4a58822e90893b7539619e42c320b1afc --- cc/toolchains/legacy_file_group.bzl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cc/toolchains/legacy_file_group.bzl b/cc/toolchains/legacy_file_group.bzl index dc51569ff..48d289a69 100644 --- a/cc/toolchains/legacy_file_group.bzl +++ b/cc/toolchains/legacy_file_group.bzl @@ -32,8 +32,11 @@ LEGACY_FILE_GROUPS = { Label("//cc/toolchains/actions:cpp20_module_codegen"), Label("//cc/toolchains/actions:cpp_module_deps_scanning"), ], - # There are no actions listed for coverage and objcopy in action_names.bzl. - "coverage_files": [], + "coverage_files": [ + Label("//cc/toolchains/actions:gcov"), + Label("//cc/toolchains/actions:llvm_cov"), + Label("//cc/toolchains/actions:llvm_profdata"), + ], "dwp_files": [ Label("//cc/toolchains/actions:dwp"), ], From 20e8db3567d633a9c330049b8b701a5f86b092d6 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Thu, 1 Oct 2026 05:36:52 -0700 Subject: [PATCH 07/61] Use dict.items() for artifact-label pairs (#900) Replace `_map_to_list` with `dict.items()` in `cc_helper.bzl` and `objc_compilation_support.bzl`. Starlark's `dict.items()` already returns a list of key-value tuples in insertion order, so the helpers unnecessarily iterate over that list and recreate each tuple. Remove both helpers and return `artifact_label_map.items()` at all five call sites. Closes #900 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/900 from dzbarsky:codex/use-dict-items bef7e1ef9cfbbedac44a3668685089bd339a91c2 PiperOrigin-RevId: 991615000 Change-Id: I281e1f5634a85fff7e0a079e79d13a02b3b3f22a --- cc/common/cc_helper.bzl | 14 ++++---------- cc/private/rules_impl/objc_compilation_support.bzl | 8 +------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index a8d1ea502..d4bd27d58 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -1048,12 +1048,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. @@ -1081,13 +1075,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 @@ -1101,7 +1095,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): @@ -1114,7 +1108,7 @@ def _get_public_hdrs(ctx): if _check_file_extension(artifact, extensions.DISALLOWED_HDRS_FILES, 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", []) diff --git a/cc/private/rules_impl/objc_compilation_support.bzl b/cc/private/rules_impl/objc_compilation_support.bzl index 624ee8bfd..7c143e53a 100644 --- a/cc/private/rules_impl/objc_compilation_support.bzl +++ b/cc/private/rules_impl/objc_compilation_support.bzl @@ -282,13 +282,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, From 2e1b4ab4749320b3efb939e8a0311cc807eb9078 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Thu, 1 Oct 2026 10:21:40 -0700 Subject: [PATCH 08/61] Starlarkify cc_common.empty_variables (#806) Replace the Java-backed `cc_common.empty_variables()` call with `_cc_internal.cc_toolchain_variables(vars = {})`. Both implementations construct the same empty `CcToolchainVariables` representation, preserving compile, link, FDO, and toolchain feature expansion. Validation: `buildifier -mode=check cc/private/cc_common.bzl` and 66 passing C++ common, compile-variable, link-variable, and FDO analysis tests. Closes #806 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/806 from dzbarsky:codex/starlarkify-empty-variables 33dcb2f771b81da0ffae807d033471bea1fdbaa5 PiperOrigin-RevId: 991757889 Change-Id: Ifec667bdd7fbd5b7c852258e69d7f0198d2df1bd --- cc/private/cc_common.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cc/private/cc_common.bzl b/cc/private/cc_common.bzl index 8a289e3c7..4533f7fda 100644 --- a/cc/private/cc_common.bzl +++ b/cc/private/cc_common.bzl @@ -170,7 +170,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( *, From 785f33a454363e5f8625d7f0c2265f254e7371fc Mon Sep 17 00:00:00 2001 From: Googler Date: Thu, 1 Oct 2026 16:13:40 -0700 Subject: [PATCH 09/61] Support checking command-line copts in disallowed copts policies Enable cc_copts_policy to enforce disallowed flags on command-line compiler options (--copt, --cxxopt, --conlyopt) via verify_command_line = True. Taken over from unknown commit by trybka@. Startblock: * // Put other blockers before this line to avoid churning. * has lgtm * is approved * and then * all comments are resolved * and then *[] allows unknown commit PiperOrigin-RevId: 991964690 Change-Id: I04934ce74c252ad3a4c049ddcc6dad9b4463eb4d --- cc/common/cc_helper.bzl | 57 +++++++++++++++++------ tests/cc/common/disallowed_copts_test.bzl | 2 +- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index d4bd27d58..b7008b256 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -923,11 +923,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 @@ -945,17 +955,23 @@ def _verify_no_disallowed_copts( allowlist_target_label = getattr(info, "allowlist_target_label", None) error_message = getattr(info, "error_message", None) - # 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)) + # 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), + ]) + + for source, flags in flag_sources: + for flag in flags: + if _matches_disallowed_copt(flag, info.flags): + violations.append((flag, allowlist_target_label, error_message, source)) if violations: formatted_violations = [] - for flag, allowlist_target_label, error_message in violations: + for flag, allowlist_target_label, error_message, source in violations: allowlist_clause = ( " (target is not in the allowlist '{}')".format( allowlist_target_label, @@ -965,21 +981,22 @@ def _verify_no_disallowed_copts( ": {}".format(error_message) if error_message else "" ) formatted_violations.append( - "- Flag '{}'{}{}".format( + "- Flag '{}' in {}{}{}".format( flag, + source, allowlist_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( @@ -995,8 +1012,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) @@ -1005,6 +1033,7 @@ def _get_copts( expanded_attribute_copts, attr, disallowed_copts_infos = disallowed_copts_infos, + command_line_copts = command_line_copts, ) return expanded_attribute_copts diff --git a/tests/cc/common/disallowed_copts_test.bzl b/tests/cc/common/disallowed_copts_test.bzl index 1ad8441d1..6620454c5 100644 --- a/tests/cc/common/disallowed_copts_test.bzl +++ b/tests/cc/common/disallowed_copts_test.bzl @@ -19,7 +19,7 @@ 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' (target is not in the allowlist '//custom:toolchain_allowlist'): Custom warning policy guidance.", ), ) From 958e6a18282da5ffa7d6764cb7451ea16dabcc80 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 2 Oct 2026 07:03:53 -0700 Subject: [PATCH 10/61] Remove -std=c++17 from bazelrc (#918) This is the default in the toolchains now, this flag was invalid for windows so CI warned Closes #918 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/918 from keith:ks/remove-std-c-17-from-bazelrc 0829b78995a6faa0d3956f882d810e266c9a547d PiperOrigin-RevId: 992301854 Change-Id: I2364dc05bc0963edd8a23368af0da64ef46baeda --- .bazelrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.bazelrc b/.bazelrc index e1b2f6f4a..0d722cf12 100644 --- a/.bazelrc +++ b/.bazelrc @@ -2,6 +2,4 @@ common --enable_platform_specific_config # TODO: Remove https://github.com/bazelbuild/bazel/pull/31423 common:windows --remote_download_all -# Required by googletest 1.17. -common --cxxopt=-std=c++17 test --test_env=PATH=/bin:/usr/bin:/usr/local/bin From eaafbd34240f0d0968f25782385cf013f823ea69 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 2 Oct 2026 10:45:04 -0700 Subject: [PATCH 11/61] Add back grep-includes alias (#888) There's references to this from bazel https://github.com/bazelbuild/bazel/blob/5604b553b2a54152e5e4f41ef4b01a416b7bfd44/tools/cpp/BUILD.tools#L44 We should remove both but in the meantime some rulesets reference this Closes #888 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/888 from keith:ks/add-back-grep-includes-alias ebc21b3f0726853f80f0ebddb898bfd205277870 PiperOrigin-RevId: 992413533 Change-Id: Iaeaf5a245db5a7a8a5fc891d4c70324a4b57f868 --- cc/private/toolchain/BUILD | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cc/private/toolchain/BUILD b/cc/private/toolchain/BUILD index abd4e6b9d..e3c068058 100644 --- a/cc/private/toolchain/BUILD +++ b/cc/private/toolchain/BUILD @@ -58,6 +58,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 = [], From 8e81232886013da92481e0fe42b12b89d03c2924 Mon Sep 17 00:00:00 2001 From: Nicholas Levin Date: Mon, 5 Oct 2026 09:37:19 -0700 Subject: [PATCH 12/61] Automatic code cleanup. PiperOrigin-RevId: 993739069 Change-Id: I6fd5884126ba600529ff1904d8aac0438ca13e00 --- cc/private/rules_impl/objc_compilation_support.bzl | 2 -- cc/private/toolchain/unix_cc_toolchain_config.bzl | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cc/private/rules_impl/objc_compilation_support.bzl b/cc/private/rules_impl/objc_compilation_support.bzl index 7c143e53a..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: 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): From 213bf5578f7e5b71a5fb941f34f50edd3f44a2c5 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 5 Oct 2026 10:04:14 -0700 Subject: [PATCH 13/61] Use named arguments instead of comments (#906) Closes #906 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/906 from keith:ks/use-named-arguments-instead-of-comments a5169cbb54f0ef3ddcc336bd56fb44d5baa348c8 PiperOrigin-RevId: 993754957 Change-Id: I8963cdc410859edd985730f4d5bc1d35316a0acf --- cc/private/link/cc_linking_helper.bzl | 12 +++--- cc/private/link/cpp_link_action.bzl | 40 +++++++++---------- ...nking_context_from_compilation_outputs.bzl | 10 ++--- cc/private/link/link.bzl | 26 ++++++------ cc/private/link/link_build_variables.bzl | 20 +++++----- cc/private/link/lto_backends.bzl | 2 +- cc/private/link/lto_indexing_action.bzl | 16 ++++---- cc/private/rules_impl/cc_binary_impl.bzl | 2 +- 8 files changed, 64 insertions(+), 64 deletions(-) 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_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/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index fc4c2b23c..77695a33b 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, From 1dfaf0bdb22320df4e52db6662dd10f9c257f849 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 6 Oct 2026 04:50:20 -0700 Subject: [PATCH 14/61] Add docs for skip_virtual_includes (#920) Closes #920 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/920 from keith:ks/add-docs-for-skip_virtual_includes 0e834a4b430dc19aac0990a39f83174dfa5d05c3 PiperOrigin-RevId: 994315342 Change-Id: Iab7ea501f9c35ad434a11cec1ff31c73ce566222 --- docs/features.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/features.md b/docs/features.md index b12569d22..ef453cccb 100644 --- a/docs/features.md +++ b/docs/features.md @@ -538,6 +538,23 @@ on Windows to avoid long path issues. This feature must be enabled by default if desired. +### `skip_virtual_includes` + +A marker feature that avoids creating `_virtual_includes` symlinks when +headers only need a `strip_include_prefix` (without an +`include_prefix`). Instead, `rules_cc` adds the stripped directory to +the include paths directly and uses the original headers. This reduces +path lengths and can help avoid long path issues on Windows. + +The only downside of this feature is that if you disable sandboxing +non-declared headers can be included that are in the same directory as +the one being added to the include path. This would still be caught by +[`layering_check`](#layering_check) if enabled. + +See also [`shorten_virtual_includes`](#shorten_virtual_includes). + +This feature must be enabled if desired. + ### `static_link_cpp_runtimes` A marker feature used by `rules_cc` to determine if the toolchain From f96573d6e51487f8c313cd57bd498fe328a5a6ff Mon Sep 17 00:00:00 2001 From: Googler Date: Fri, 9 Oct 2026 09:28:15 -0700 Subject: [PATCH 15/61] Add a `dwo_files` output group to cc_binary and cc_test. Provide a `dwo_files` output group for debug builds with fission enabled so that separate DWARF (`.dwo`) files can be requested on-demand in build modes where intermediate compilation outputs are not materialized by default. PiperOrigin-RevId: 996578913 Change-Id: Iea1798051051edf66fa30f25417796d742c8dad2 --- cc/private/rules_impl/cc_binary_impl.bzl | 12 ++ tests/cc/common/debug_info_packaging_test.bzl | 197 +++++++++++++++++- 2 files changed, 199 insertions(+), 10 deletions(-) diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index 77695a33b..7f08ed98b 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -703,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. @@ -770,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 diff --git a/tests/cc/common/debug_info_packaging_test.bzl b/tests/cc/common/debug_info_packaging_test.bzl index 662b9c721..e7b1b1967 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,181 @@ def _test_dwo_input_set_dynamic_mode_impl(env, target): dwo_files.contains_exactly([expected_main]) +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, + ] + 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, ) From 9b9b7decbb1be6fee6af60a7d1972ae96108a326 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Fri, 9 Oct 2026 15:53:24 -0700 Subject: [PATCH 16/61] Keep tool paths of generated files relative to the execution root (#901) `_compute_tool_paths` prefixes every relative tool path with the package of the `cc_toolchain`, which turns exec-cfg paths into non-existent ones. With apple_support 2.8.4, which Bazel@HEAD now bundles and thus selects by default on macOS, `$(CC)` expands to the nonsensical ``` external/apple_support++apple_cc_configure_extension+local_config_apple_cc/bazel-out/darwin_arm64-opt-exec-ST-/bin/external/apple_support+/crosstool/wrapped_clang ``` instead of ``` bazel-out/darwin_arm64-opt-exec-ST-/bin/external/apple_support+/crosstool/wrapped_clang ``` This PR keeps tool paths that start with the output directory as they are. Closes #901 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/901 from fmeum:generated-tool-paths 103fab867e66d382e2461ef5ba91f547e33b64db PiperOrigin-RevId: 996808000 Change-Id: Ice1f696166553d98f893dd6c430a769f3b9e691e --- cc/cc_toolchain_config_lib.bzl | 3 +- .../cc_toolchain_provider_helper.bzl | 18 ++++- tests/generated_tool_path/BUILD.bazel | 61 ++++++++++++++++ .../generated_tool_path_test.bzl | 72 +++++++++++++++++++ 4 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 tests/generated_tool_path/BUILD.bazel create mode 100644 tests/generated_tool_path/generated_tool_path_test.bzl diff --git a/cc/cc_toolchain_config_lib.bzl b/cc/cc_toolchain_config_lib.bzl index 77bb554f9..798a19ff0 100644 --- a/cc/cc_toolchain_config_lib.bzl +++ b/cc/cc_toolchain_config_lib.bzl @@ -448,7 +448,8 @@ def tool_path(name, path): 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. diff --git a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl index 8d042e291..6495fe21f 100644 --- a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl +++ b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl @@ -58,13 +58,18 @@ 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 @@ -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 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", + ) From 770471b5f24adba5b599238ad35f5bf76c5de0f5 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Mon, 12 Oct 2026 10:39:38 -0700 Subject: [PATCH 17/61] Enable some more tests on macOS (#924) This just fixes minor incompatibilities Closes #924 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/924 from keith:ks/enable-cc_integration_test-on-macos 3f48e22cc303023c26ccbf5843fb5f12d9141094 PiperOrigin-RevId: 998012310 Change-Id: I41cd53581c97e9455208d5a1c63c585eebfcd843 --- .../cc/cc_static_library/test/BUILD | 1 + tests/integration/BUILD | 8 +--- tests/integration/cc_integration_test.sh | 43 +++++++------------ tests/integration/cc_shared_library_tests.sh | 21 ++++++--- 4 files changed, 32 insertions(+), 41 deletions(-) diff --git a/tests/builtins_bzl/cc/cc_static_library/test/BUILD b/tests/builtins_bzl/cc/cc_static_library/test/BUILD index 407853e7d..4da9022e6 100644 --- a/tests/builtins_bzl/cc/cc_static_library/test/BUILD +++ b/tests/builtins_bzl/cc/cc_static_library/test/BUILD @@ -84,6 +84,7 @@ sh_test( ], target_compatible_with = select({ "@platforms//os:linux": [], + "@platforms//os:macos": [], "//conditions:default": ["@platforms//:incompatible"], }), ) diff --git a/tests/integration/BUILD b/tests/integration/BUILD index 02a083ba5..a5c838417 100644 --- a/tests/integration/BUILD +++ b/tests/integration/BUILD @@ -21,7 +21,6 @@ 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 +39,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": [], }), @@ -67,6 +63,7 @@ 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, }), @@ -75,10 +72,7 @@ rules_cc_integration_test( 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..b46d7b5b0 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" < BUILD <<'EOF' load("@rules_cc//cc:cc_binary.bzl", "cc_binary") @@ -1831,19 +1826,15 @@ int main() { } EOF - export BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 export BAZEL_CXXOPTS=-DEXIT_CODE=0 bazel build //:main_c \ - --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \ --repo_env=BAZEL_CXXOPTS=-DEXIT_CODE=0 && fail "Expected C compilation to fail" bazel run //:main_cpp \ - --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \ --repo_env=BAZEL_CXXOPTS=-DEXIT_CODE=0 || fail "Expected C++ compilation to pass" } function test_bazel_conlyopts() { is_bazel || return 0 - is_darwin && return 0 cat > BUILD <<'EOF' load("@rules_cc//cc:cc_binary.bzl", "cc_binary") @@ -1869,13 +1860,10 @@ int main() { } EOF - export BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 export BAZEL_CONLYOPTS=-DEXIT_CODE=0 bazel build //:main_cpp \ - --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \ --repo_env=BAZEL_CONLYOPTS=-DEXIT_CODE=0 && fail "Expected C++ compilation to fail" bazel run //:main_c \ - --repo_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \ --repo_env=BAZEL_CONLYOPTS=-DEXIT_CODE=0 || fail "Expected C compilation to pass" } @@ -2173,8 +2161,7 @@ EOF } function test_parse_headers_clean() { - - mkdir pkg + mkdir -p pkg cat > pkg/BUILD <<'EOF' load("@rules_cc//cc:cc_library.bzl", "cc_library") package(features = ["parse_headers"]) diff --git a/tests/integration/cc_shared_library_tests.sh b/tests/integration/cc_shared_library_tests.sh index 437fce83e..24471cd70 100755 --- a/tests/integration/cc_shared_library_tests.sh +++ b/tests/integration/cc_shared_library_tests.sh @@ -104,13 +104,22 @@ EOF --experimental_cc_shared_library \ //foo:foo_shared >& "${TEST_log}" || fail "Expected build to succeed" - so_file="bazel-bin/foo/libfoo_shared.so" + local so_file="bazel-bin/foo/libfoo_shared.so" + local nm_flag="-D" + local symbol_prefix="_Z" + if is_darwin; then + so_file="bazel-bin/foo/libfoo_shared.dylib" + symbol_prefix="__Z" + # nm -g reads global symbols rather than the Mach-O export trie. This test + # assumes that defined global symbols are also exported. + nm_flag="-g" + fi - nm -D "$so_file" | grep -q "T _Z3foov" || fail "_Z3foov not linked statically" - nm -D "$so_file" | grep -q "T _Z4foo2v" || fail "_Z4foo2v not linked statically" - nm -D "$so_file" | grep -q "U _Z3barv" || fail "_Z3barv not linked dynamically" - nm -D "$so_file" | grep -q "baz" && fail "baz shoulod not be linked in anyway" - nm -D "$so_file" | grep -q "bar2" && fail "bar2 shoulod not be linked in anyway" + nm "$nm_flag" "$so_file" | grep -q "T ${symbol_prefix}3foov" || fail "${symbol_prefix}3foov not linked statically" + nm "$nm_flag" "$so_file" | grep -q "T ${symbol_prefix}4foo2v" || fail "${symbol_prefix}4foo2v not linked statically" + nm "$nm_flag" "$so_file" | grep -q "U ${symbol_prefix}3barv" || fail "${symbol_prefix}3barv not linked dynamically" + nm "$nm_flag" "$so_file" | grep -q "baz" && fail "baz shoulod not be linked in anyway" + nm "$nm_flag" "$so_file" | grep -q "bar2" && fail "bar2 shoulod not be linked in anyway" return 0 } From 14c39139c0ac30d0cdfa426502c49164ca6fb5c1 Mon Sep 17 00:00:00 2001 From: Googler Date: Thu, 15 Oct 2026 21:20:39 -0700 Subject: [PATCH 18/61] No public description Startblock: PiperOrigin-RevId: 1000590878 Change-Id: Iae8805f45f22d773b39697d676886f1b9e1258de --- .../compile/compile_build_variables.bzl | 30 ++++++++++++------- tests/cc/common/cc_binary_fdo_test.bzl | 13 ++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/cc/private/compile/compile_build_variables.bzl b/cc/private/compile/compile_build_variables.bzl index 34500702b..f8e7b98bf 100644 --- a/cc/private/compile/compile_build_variables.bzl +++ b/cc/private/compile/compile_build_variables.bzl @@ -479,17 +479,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 +525,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/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, From d1d1a95c5fbc87766cf455f96f56452ae99d5f41 Mon Sep 17 00:00:00 2001 From: Googler Date: Sun, 18 Oct 2026 20:17:49 -0700 Subject: [PATCH 19/61] Check disallowed copts before allowlist membership. _verify_no_disallowed_copts runs for every C++ target, once per copts, conlyopts and cxxopts attribute. It used to call allowlist.contains(ctx.label) first, for every policy. PackageGroupContents.containsPackage uses a hash set for exact //pkg entries but scans //pkg/... entries one by one, so a large prefix allowlist made the check expensive for every target, including the vast majority that use no disallowed flag. The flags are now matched first, and the allowlist is consulted only when a flag matched, at most once per policy. Violations and their messages are unchanged. This is needed before registering the -g0 debug info policy, whose allowlist has about 8k //pkg/... entries. Startblock: * // Put other blockers before this line to avoid churning. * has lgtm * is approved * and then * all comments are resolved * and then *[] allows unknown commit PiperOrigin-RevId: 1002043792 Change-Id: I68f80519dad93d597669a6aadfc71ef53f297719 --- cc/common/cc_helper.bzl | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index b7008b256..a8f7619f3 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -947,11 +947,6 @@ 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): - continue - allowlist_target_label = getattr(info, "allowlist_target_label", None) error_message = getattr(info, "error_message", None) @@ -964,10 +959,23 @@ def _verify_no_disallowed_copts( ("'--conlyopt' flag", command_line_copts.conlyopts), ]) - for source, flags in flag_sources: - for flag in flags: - if _matches_disallowed_copt(flag, info.flags): - violations.append((flag, allowlist_target_label, error_message, source)) + matches = [ + (flag, source) + for source, flags in flag_sources + for flag in flags + if _matches_disallowed_copt(flag, info.flags) + ] + if not matches: + continue + + # Check the allowlist only after a match: lookups scan "//pkg/..." + # entries linearly, and most targets use no disallowed flags. + allowlist = getattr(info, "allowlist", None) + if allowlist != None and allowlist.contains(ctx.label): + continue + + for flag, source in matches: + violations.append((flag, allowlist_target_label, error_message, source)) if violations: formatted_violations = [] From b50a797b7dea422992b49424440167cd60ef4574 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 19 Oct 2026 21:19:04 -0700 Subject: [PATCH 20/61] Fix label resolution for external repos (#896) `%package(...)%` resolved relative to rules_cc itself, not the location where the `cc_toolchain` was defined. For `cc_shared_library`, the existing initializer did resolve labels correctly, but failed to do so for `select` expressions. Closes #896 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/896 from fmeum:claude/package-paths-bzlmod-toolchain-w8lcdu 0468ba063b2cc44e5555b6787e1dcd33020600be PiperOrigin-RevId: 1002808922 Change-Id: Iea958478b558c0a5f80e6edd84bc13f20d6a9b98 --- cc/private/rules_impl/cc_shared_library.bzl | 23 ------------ .../rules_impl/cc_shared_library_impl.bzl | 6 +-- .../cc_toolchain_provider_helper.bzl | 6 +-- .../cc/cc_shared_library/test/BUILD | 13 +++++++ .../cc/cc_shared_library/test2/BUILD.bazel | 15 ++++++++ .../test2/toolchain_include_dirs/BUILD.bazel | 31 ++++++++++++++++ .../cc_toolchain_config.bzl | 23 ++++++++++++ tests/cc/common/BUILD | 3 ++ .../cc_toolchain_include_directories_test.bzl | 37 +++++++++++++++++++ 9 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/BUILD.bazel create mode 100644 tests/builtins_bzl/cc/cc_shared_library/test2/toolchain_include_dirs/cc_toolchain_config.bzl create mode 100644 tests/cc/common/cc_toolchain_include_directories_test.bzl 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 44abc8a13..5c8bce7a8 100644 --- a/cc/private/rules_impl/cc_shared_library_impl.bzl +++ b/cc/private/rules_impl/cc_shared_library_impl.bzl @@ -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 diff --git a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl index 6495fe21f..b0dea3f7f 100644 --- a/cc/private/rules_impl/cc_toolchain_provider_helper.bzl +++ b/cc/private/rules_impl/cc_toolchain_provider_helper.bzl @@ -73,7 +73,7 @@ def _compute_tool_paths(toolchain_config_info, crosstool_top_path, output_direct 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. @@ -102,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) @@ -234,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/tests/builtins_bzl/cc/cc_shared_library/test/BUILD b/tests/builtins_bzl/cc/cc_shared_library/test/BUILD index d6949cdfa..7d45fbfd8 100644 --- a/tests/builtins_bzl/cc/cc_shared_library/test/BUILD +++ b/tests/builtins_bzl/cc/cc_shared_library/test/BUILD @@ -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/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_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, + ) From ebd996fd37eaa710fd5d90e1bfd1bcddafcac067 Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 20 Oct 2026 01:43:20 -0700 Subject: [PATCH 21/61] Implement target allowlists in addition to package allowlists for disallowed copts policies. Implement the design changes specified in design_banned_warnings.md and design_modular_copts_policies.md: - Update CcDisallowedCoptsInfo to provide package_allowlist, target_allowlist, and policy_label. - Update cc_copts_policy to accept package_allowlist and target_allowlist. - Update tools/cpp/policy/banned_warnings/BUILD to use package_allowlist. - Update third_party/crosstool/v18/stable/crosstool_configuration.bzl and third_party/apple_crosstool/crosstool.bzl to forward package_allowlist, target_allowlist, and policy_label. - Update cc_helper.bzl to exempt targets matching entries in package_allowlist and target_allowlist, and emit "disallowed by ''" in violation messages. - Add and update unit tests in tools/cpp/policy/ and third_party/bazel_rules/rules_cc/tests/cc/common/. Startblock: - BTRGuardian allows unknown commit Startblock: * // Put other blockers before this line to avoid churning. * has lgtm * is approved * and then * all comments are resolved * and then *[] allows unknown commit PiperOrigin-RevId: 1002924230 Change-Id: I35d1aefd0e0ecc47c81fa45c62481d534ca95a3f --- cc/common/cc_helper.bzl | 36 ++++++++----- tests/cc/common/disallowed_copts_test.bzl | 23 ++++++++- tests/cc/testutil/cc_analysis_test.bzl | 4 ++ tests/cc/testutil/toolchains/BUILD | 51 ++++++++++++++++++- .../toolchains/cc_toolchain_config.bzl | 7 +-- 5 files changed, 100 insertions(+), 21 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index a8f7619f3..ad009a742 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -947,9 +947,6 @@ def _verify_no_disallowed_copts( if not hasattr(info, "flags") or not info.flags: continue - allowlist_target_label = getattr(info, "allowlist_target_label", None) - error_message = getattr(info, "error_message", None) - # 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: @@ -968,22 +965,33 @@ def _verify_no_disallowed_copts( if not matches: continue - # Check the allowlist only after a match: lookups scan "//pkg/..." - # entries linearly, and most targets use no disallowed flags. - allowlist = getattr(info, "allowlist", None) - if allowlist != None and allowlist.contains(ctx.label): + # 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 + 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, allowlist_target_label, error_message, source)) + violations.append((flag, policy_label, error_message, source)) if violations: formatted_violations = [] - for flag, allowlist_target_label, error_message, source 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 "" @@ -992,7 +1000,7 @@ def _verify_no_disallowed_copts( "- Flag '{}' in {}{}{}".format( flag, source, - allowlist_clause, + policy_clause, error_clause, ), ) diff --git a/tests/cc/common/disallowed_copts_test.bzl b/tests/cc/common/disallowed_copts_test.bzl index 6620454c5..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' in attribute 'copts' (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..4894971f8 100644 --- a/tests/cc/testutil/toolchains/BUILD +++ b/tests/cc/testutil/toolchains/BUILD @@ -449,7 +449,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 +496,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 +537,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/cc_toolchain_config.bzl b/tests/cc/testutil/toolchains/cc_toolchain_config.bzl index 3ed8bbb70..a9233474b 100644 --- a/tests/cc/testutil/toolchains/cc_toolchain_config.bzl +++ b/tests/cc/testutil/toolchains/cc_toolchain_config.bzl @@ -2106,11 +2106,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 +2157,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"), From f3daca77a07e26755be7aa4f15a837c53fa49950 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 20 Oct 2026 12:20:40 -0700 Subject: [PATCH 22/61] Add --check_direct_dependencies=off to bazelrc (#917) This silences noise that encourages us to bump our MVS dep versions which would affect users and we should only bump if we really need to Closes #917 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/917 from keith:ks/add-check_direct_dependencies-off-to-bazelrc d525a7d137f5801ddf1c351c122057646f546dcb PiperOrigin-RevId: 1003300828 Change-Id: I3f7cce6f2988e426dcd819b797b0a1a731fae4ea --- .bazelrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bazelrc b/.bazelrc index 0d722cf12..dd2b2bdb1 100644 --- a/.bazelrc +++ b/.bazelrc @@ -2,4 +2,6 @@ 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 From bf7ebfe7644dbb451011652303b1ce0268ecb2f0 Mon Sep 17 00:00:00 2001 From: PikachuHy Date: Tue, 20 Oct 2026 15:51:58 -0700 Subject: [PATCH 23/61] Apply cxx_flags to Windows module actions (#912) Closes #912 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/912 from PikachuHyA:msvc_support b3056e02f0c5575e20a0b7a4587e91776bd02de7 PiperOrigin-RevId: 1003457743 Change-Id: Ic6e1b559130a00b43c7b4c770abbc0f17ea2c8bc --- .../toolchain/windows_cc_toolchain_config.bzl | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/cc/private/toolchain/windows_cc_toolchain_config.bzl b/cc/private/toolchain/windows_cc_toolchain_config.bzl index 55b156daf..cd881f3be 100644 --- a/cc/private/toolchain/windows_cc_toolchain_config.bzl +++ b/cc/private/toolchain/windows_cc_toolchain_config.bzl @@ -607,15 +607,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 +1658,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, From f6a743ea0b99b2b6d370819c73620018c8be3bdd Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 21 Oct 2026 04:16:12 -0700 Subject: [PATCH 24/61] Update $(location to execpath (#891) Buildifier warns about this now Closes #891 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/891 from keith:ks/update-location-to-execpath e0d7ca93b2f90d5ab7aa7d333389ceabd45b7f19 PiperOrigin-RevId: 1003826865 Change-Id: I8de712ba8d978a26bccb0cc74bc9d8faab78daab --- tests/alwayslink_srcs/BUILD | 4 ++-- tests/builtins_bzl/cc/cc_shared_library/test/BUILD | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) 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 7d45fbfd8..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( From 4d19de1c63961e3e75ee69cbd986557ecfab5afe Mon Sep 17 00:00:00 2001 From: rdong8 Date: Wed, 21 Oct 2026 21:58:48 -0700 Subject: [PATCH 25/61] Fix cpp_modules + features=parse_headers/c=opt (#876) Combining `--features=cpp_modules` with either `--features=parse_headers` or `--compilation_mode opt` was broken, this seems to fix it. This adds an early exit copied from `_create_cc_compile_actions` that was missing in `_create_cc_compile_actions_with_cpp20_module_helper`: https://github.com/bazelbuild/rules_cc/blob/c1ba7a0d5a5f51f6539eb94f32ef8d9fedd0250d/cc/private/compile/compile.bzl#L1249-L1254 I tested the changes in [my personal monorepo](https://github.com/rdong8/monorepo) + added a test for `parse_headers`. This was found with the help of an LLM Closes #876 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/876 from rdong8:cpp-modules-fixes 77b47b724ab05a076039f011a7295a4edce04d33 PiperOrigin-RevId: 1004479640 Change-Id: I2d1d38ce1c85bf027d3749a78bb4b11eb531b80b --- cc/private/compile/compile.bzl | 6 +++++- tests/cc/bazel/cpp_modules_test.bzl | 32 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) 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/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, From 11976124184a79bfecaedf6ff5cbf2f17f815a21 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Thu, 22 Oct 2026 01:27:51 -0700 Subject: [PATCH 26/61] Exclude cpp_module_codegen from source_compile_actions (#897) Module codegen actions compile a precompiled module into an object file and never preprocess. Bazel's legacy `include_paths`, `includes` and `preprocessor_defines` features accordingly don't cover the `cpp_module_codegen` action, but their rules-based counterparts (`//cc/toolchains/args/include_flags` and `//cc/toolchains/args/preprocessor_defines`, as well as the corresponding variables) apply to `source_compile_actions`, which does. Every module codegen action thus receives the include paths and defines of compile actions, and Clang warns about each include path as an unused argument (`clang++: warning: argument unused during compilation: '-iquote .' [-Wunused-command-line-argument]`). This excludes the action from `source_compile_actions` like `lto_backend` already is, which restores parity with the legacy features. Closes #897 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/897 from fmeum:codegen-no-preprocessing-flags 1157d3784791939390992a0a840e1e91f935866d PiperOrigin-RevId: 1004579515 Change-Id: I51f7f91172ae614876080794a59278ca22faafcd --- cc/toolchains/actions/BUILD | 4 ++++ .../action_type_sets/goldens/source_compile_actions.txt | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) 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/tests/rule_based_toolchain/action_type_sets/goldens/source_compile_actions.txt b/tests/rule_based_toolchain/action_type_sets/goldens/source_compile_actions.txt index e039c97e7..c59b6f593 100644 --- a/tests/rule_based_toolchain/action_type_sets/goldens/source_compile_actions.txt +++ b/tests/rule_based_toolchain/action_type_sets/goldens/source_compile_actions.txt @@ -1,7 +1,6 @@ assemble c++-compile c++-header-parsing -c++-module-codegen c++-module-compile c-compile clif-match From 1bcbf518706ba9217906b9e98a9330b86d4b83ea Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 23 Oct 2026 05:59:15 -0700 Subject: [PATCH 27/61] Stop using deprecated aliases for rules_cc tools (#889) Closes #889 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/889 from keith:ks/stop-using-deprecated-aliases-for-rules_cc-tools 3efdcd367c46b12dd8da975d36cf78f1eff13adb PiperOrigin-RevId: 1005536693 Change-Id: I2347fc2c3b8706c7295bb329a41622cbe235df6b --- cc/private/rules_impl/cc_toolchain.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 providing cc_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", ), From 9cb3e09e617ce29ff1d794b98b7068e50f5f0d89 Mon Sep 17 00:00:00 2001 From: armandomontanez Date: Fri, 23 Oct 2026 08:11:27 -0700 Subject: [PATCH 28/61] Add lilygorsheneva to BCR maintainers list (#956) Closes #956 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/956 from armandomontanez:armando/add-lily-to-bcr-maintainer-template-list 3955c6d32ee8cd3d0ac2306e8b3c6d699cfeb8d0 PiperOrigin-RevId: 1005610780 Change-Id: I095ca0a09cebf8225cac46f5bd61905afd22acc9 --- .bcr/metadata.template.json | 5 +++++ 1 file changed, 5 insertions(+) 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": [], From 5323f498387318739c9878cb87805aaf40d2723c Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 27 Oct 2026 06:31:05 -0700 Subject: [PATCH 29/61] Migrate C++ debug info packaging tests from Java to Starlark. Migrated 4 test cases from `DebugInfoPackagingTest.java` to `debug_info_packaging_test.bzl`: - `testEmptyInputsInFissionMode` - `testDwoInputSetNonPicMode` - `testDwoInputSetNoPicForBinariesMode` - `testDwoInputSetCustomMalloc` Deleted the corresponding test cases and unused imports from `DebugInfoPackagingTest.java`. PiperOrigin-RevId: 1008054896 Change-Id: I087cd39e40ec33855260c9f11c1a96d93429be12 --- tests/cc/common/debug_info_packaging_test.bzl | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/tests/cc/common/debug_info_packaging_test.bzl b/tests/cc/common/debug_info_packaging_test.bzl index e7b1b1967..3f70ed221 100644 --- a/tests/cc/common/debug_info_packaging_test.bzl +++ b/tests/cc/common/debug_info_packaging_test.bzl @@ -259,6 +259,176 @@ 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) @@ -422,6 +592,10 @@ def debug_info_packaging_tests(name): _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([ From 24f0847f1c8c29888a709d8ba3ef7b8485cba45a Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 27 Oct 2026 08:19:35 -0700 Subject: [PATCH 30/61] Add Bazel 10 RC testing to BazelCI:rules_cc PiperOrigin-RevId: 1008118178 Change-Id: I17b426c25c0bbbc82a26ab959dfacac6f653f201 --- .bazelci/presubmit.yml | 49 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 10f0c250c..f584a70c2 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -230,7 +230,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 +238,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 +247,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 +256,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 From 12f2354ae5df57ba3ec72bc6caf939895e34a588 Mon Sep 17 00:00:00 2001 From: Googler Date: Tue, 27 Oct 2026 08:59:56 -0700 Subject: [PATCH 31/61] Reenable coverage for windows_rolling on BazelCI:rules_cc PiperOrigin-RevId: 1008142697 Change-Id: I075a382f9443ca939a54ad1fd2ddf9fcad1b4b11 --- .bazelci/presubmit.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index f584a70c2..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: From cad4670c839c80e87e8a669e3ab5eee2ac7339b7 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 27 Oct 2026 13:13:10 -0700 Subject: [PATCH 32/61] Fix windows binaries with spaces (#923) `windows_quoting_for_param_files` must be enabled for msvc. It is now enable-able for all windows configurations Closes #923 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/923 from keith:ks/fix-windows-binaries-with-spaces f97cf0f935e69efa3e44ea4edeeb5a4bb46f9194 PiperOrigin-RevId: 1008324735 Change-Id: I5d95f05b1bbdf3bd4db07ec7850e375b95689c2f --- .../toolchain/windows_cc_toolchain_config.bzl | 7 ++- docs/features.md | 6 ++- tests/integration/BUILD | 15 ++++-- tests/integration/cc_linking_tests.sh | 52 +++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100755 tests/integration/cc_linking_tests.sh diff --git a/cc/private/toolchain/windows_cc_toolchain_config.bzl b/cc/private/toolchain/windows_cc_toolchain_config.bzl index cd881f3be..8d447af67 100644 --- a/cc/private/toolchain/windows_cc_toolchain_config.bzl +++ b/cc/private/toolchain/windows_cc_toolchain_config.bzl @@ -1723,6 +1723,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", @@ -1769,7 +1774,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/docs/features.md b/docs/features.md index ef453cccb..902be1853 100644 --- a/docs/features.md +++ b/docs/features.md @@ -255,7 +255,11 @@ This feature is off by default. Marker features to configure the quoting style of arguments in `@params` files. If neither are enabled, no quoting is applied. -These features must be enabled if desired. +`gcc_quoting_for_param_files` should be enabled by default for Unix like +toolchains. + +`windows_quoting_for_param_files` should be enabled by default for some +Windows toolchains, such as MSVC. ### `generate_submodules` diff --git a/tests/integration/BUILD b/tests/integration/BUILD index a5c838417..66b49f31f 100644 --- a/tests/integration/BUILD +++ b/tests/integration/BUILD @@ -9,12 +9,22 @@ 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( @@ -56,16 +66,13 @@ 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", ) diff --git a/tests/integration/cc_linking_tests.sh b/tests/integration/cc_linking_tests.sh new file mode 100755 index 000000000..4761542de --- /dev/null +++ b/tests/integration/cc_linking_tests.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +source "$(rlocation rules_cc/tests/test_utils.sh)" +source "$(rlocation rules_cc/tests/unittest.bash)" + +function test_linker_param_files_with_spaces() { + cat > BUILD <<'EOF' +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +cc_library( + name = "library with spaces", + srcs = ["source with spaces.cc"], +) + +cc_binary( + name = "binary with spaces", + srcs = ["main.cc"], + deps = [":library with spaces"], +) + +cc_binary( + name = "shared library with spaces", + srcs = ["source with spaces.cc"], + linkshared = True, +) +EOF + + cat > 'source with spaces.cc' <<'EOF' +#ifdef _WIN32 +__declspec(dllexport) +#endif +int value() { return 42; } +EOF + cat > main.cc <<'EOF' +int value(); +int main() { return value() == 42 ? 0 : 1; } +EOF + + # TODO: compiler_param_file is broken with spaces on Windows + bazel build --features=-compiler_param_file --min_param_file_size=0 \ + ':library with spaces' ':binary with spaces' ':shared library with spaces' \ + >& "$TEST_log" || fail "Build with spaces in response files failed" + local binary='bazel-bin/binary with spaces' + if is_windows; then + binary+='.exe' + fi + "$binary" >> "$TEST_log" || fail "Binary failed" +} + +run_suite "Integration tests for C++ linking" From cb13b255e6f3718e39f8d040ec4463a51e47b5cb Mon Sep 17 00:00:00 2001 From: Austin Schuh Date: Wed, 28 Oct 2026 06:17:16 -0700 Subject: [PATCH 33/61] Add rule-based args for C++20 modules (#927) #750 added the C++20 module actions and variables to the rule-based toolchain API, but no args that use them, so every rule-based toolchain that builds modules writes its own. unix_cc_toolchain_config has them as features, and this brings rule-based toolchains up to parity. Add //cc/toolchains/args/cpp_modules, with the cpp_modules feature Bazel requires before it builds module interfaces, and the clang args: the module map file, -x c++-module and -fmodule-output. Name the dependency scanner's output file in compiler_output_flags, where unix_cc_toolchain_config does. The args only apply to the module actions, so they join the legacy action config features; toolchains that map tools for those actions list cpp_modules as a known feature. Tested with goldens in legacy_features_as_args, which match the features of the same names in unix_cc_toolchain_config. End to end, toolchains_llvm's rule-based darwin toolchain builds and runs C++20 module tests with these args. Closes #927 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/927 from AustinSchuh:cpp20-module-feature f9fe5f6f32766bb40650e5cb31b410d04dc3cf70 PiperOrigin-RevId: 1008856146 Change-Id: I42710cf3bb520f384a9f17447b3542d80f0cbca8 --- cc/toolchains/args/BUILD | 2 + cc/toolchains/args/compiler_input_flags/BUILD | 19 +++- .../args/compiler_output_flags/BUILD | 11 +++ cc/toolchains/args/cpp_modules/BUILD | 56 ++++++++++++ .../legacy_features_as_args/BUILD | 27 ++++++ .../goldens/unix/compiler_input_flags.bzl | 57 ++++++++++++ .../goldens/unix/compiler_output_flags.bzl | 86 +++++++++++++++++++ .../goldens/unix/cpp20_module_flags.bzl | 36 ++++++++ 8 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 cc/toolchains/args/cpp_modules/BUILD create mode 100644 tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_input_flags.bzl create mode 100644 tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_output_flags.bzl create mode 100644 tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/cpp20_module_flags.bzl diff --git a/cc/toolchains/args/BUILD b/cc/toolchains/args/BUILD index 440a79864..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", @@ -64,6 +65,7 @@ filegroup( "//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", diff --git a/cc/toolchains/args/compiler_input_flags/BUILD b/cc/toolchains/args/compiler_input_flags/BUILD index 07235fe21..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"], diff --git a/cc/toolchains/args/compiler_output_flags/BUILD b/cc/toolchains/args/compiler_output_flags/BUILD index b7350c402..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", ], ) @@ -46,6 +47,16 @@ cc_args( 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"], 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/tests/rule_based_toolchain/legacy_features_as_args/BUILD b/tests/rule_based_toolchain/legacy_features_as_args/BUILD index e64f152af..055792630 100644 --- a/tests/rule_based_toolchain/legacy_features_as_args/BUILD +++ b/tests/rule_based_toolchain/legacy_features_as_args/BUILD @@ -5,6 +5,9 @@ load(":goldens/macos/libraries_to_link.bzl", _MACOS_LIBRARIES_TO_LINK = "GOLDEN" load(":goldens/macos/runtime_library_search_directories.bzl", _MACOS_RUNTIME_LIBRARY_SEARCH_DIRECTORIES = "GOLDEN") load(":goldens/macos/thin_lto.bzl", _MACOS_THIN_LTO = "GOLDEN") load(":goldens/unix/archiver_flags.bzl", _UNIX_ARCHIVER_FLAGS = "GOLDEN") +load(":goldens/unix/compiler_input_flags.bzl", _UNIX_COMPILER_INPUT_FLAGS = "GOLDEN") +load(":goldens/unix/compiler_output_flags.bzl", _UNIX_COMPILER_OUTPUT_FLAGS = "GOLDEN") +load(":goldens/unix/cpp20_module_flags.bzl", _UNIX_CPP20_MODULE_FLAGS = "GOLDEN") load(":goldens/unix/force_pic_flags.bzl", _UNIX_FORCE_PIC_FLAGS = "GOLDEN") load(":goldens/unix/libraries_to_link.bzl", _UNIX_LIBRARIES_TO_LINK = "GOLDEN") load(":goldens/unix/linker_param_file.bzl", _UNIX_LINKER_PARAM_FILE = "GOLDEN") @@ -155,3 +158,27 @@ compare_cc_feature_implementation( expected = _WINDOWS_THIN_LTO, platform = ":windows", ) + +# C++20 modules. Bazel's CppActionConfigs.java predates them, so these goldens +# match the features of the same names in unix_cc_toolchain_config. + +compare_cc_feature_implementation( + name = "cpp20_module_flags_unix_test", + actual_implementation = "//cc/toolchains/args/cpp_modules:feature", + expected = _UNIX_CPP20_MODULE_FLAGS, + platform = ":linux", +) + +compare_cc_feature_implementation( + name = "compiler_input_flags_unix_test", + actual_implementation = "//cc/toolchains/args/compiler_input_flags:feature", + expected = _UNIX_COMPILER_INPUT_FLAGS, + platform = ":linux", +) + +compare_cc_feature_implementation( + name = "compiler_output_flags_unix_test", + actual_implementation = "//cc/toolchains/args/compiler_output_flags:feature", + expected = _UNIX_COMPILER_OUTPUT_FLAGS, + platform = ":linux", +) diff --git a/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_input_flags.bzl b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_input_flags.bzl new file mode 100644 index 000000000..8126866ce --- /dev/null +++ b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_input_flags.bzl @@ -0,0 +1,57 @@ +# 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. +"""Expected compiler_input_flags legacy feature textproto on unix.""" + +visibility("private") + +GOLDEN = """enabled: false +flag_sets { + actions: "assemble" + actions: "c++-compile" + actions: "c++-module-codegen" + actions: "c++-module-compile" + actions: "c++-module-deps-scanning" + actions: "c++20-module-codegen" + actions: "c-compile" + actions: "clif-match" + actions: "linkstamp-compile" + actions: "lto-backend" + actions: "objc++-compile" + actions: "objc-compile" + actions: "preprocess-assemble" + flag_groups { + expand_if_available: "source_file" + flags: "-c" + flags: "%{source_file}" + } +} +flag_sets { + actions: "c++20-module-compile" + flag_groups { + expand_if_available: "source_file" + flags: "-x" + flags: "c++-module" + flags: "-c" + flags: "%{source_file}" + } +} +flag_sets { + actions: "c++-header-parsing" + flag_groups { + expand_if_available: "source_file" + flags: "%{source_file}" + } +} +name: "compiler_input_flags" +""" diff --git a/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_output_flags.bzl b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_output_flags.bzl new file mode 100644 index 000000000..377244e2a --- /dev/null +++ b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/compiler_output_flags.bzl @@ -0,0 +1,86 @@ +# 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. +"""Expected compiler_output_flags legacy feature textproto on unix.""" + +visibility("private") + +GOLDEN = """enabled: false +env_sets { + actions: "c++-module-deps-scanning" + env_entries { + expand_if_available: "output_file" + key: "DEPS_SCANNER_OUTPUT_FILE" + value: "%{output_file}" + } +} +flag_sets { + actions: "assemble" + actions: "c++-compile" + actions: "c++-header-parsing" + actions: "c++-module-codegen" + actions: "c++-module-compile" + actions: "c-compile" + actions: "clif-match" + actions: "linkstamp-compile" + actions: "lto-backend" + actions: "objc++-compile" + actions: "objc-compile" + actions: "preprocess-assemble" + flag_groups { + expand_if_available: "output_assembly_file" + flags: "-S" + } +} +flag_sets { + actions: "assemble" + actions: "c++-compile" + actions: "c++-header-parsing" + actions: "c++-module-codegen" + actions: "c++-module-compile" + actions: "c-compile" + actions: "clif-match" + actions: "linkstamp-compile" + actions: "lto-backend" + actions: "objc++-compile" + actions: "objc-compile" + actions: "preprocess-assemble" + flag_groups { + expand_if_available: "output_preprocess_file" + flags: "-E" + } +} +flag_sets { + actions: "assemble" + actions: "c++-compile" + actions: "c++-header-parsing" + actions: "c++-module-codegen" + actions: "c++-module-compile" + actions: "c++-module-deps-scanning" + actions: "c++20-module-codegen" + actions: "c++20-module-compile" + actions: "c-compile" + actions: "clif-match" + actions: "linkstamp-compile" + actions: "lto-backend" + actions: "objc++-compile" + actions: "objc-compile" + actions: "preprocess-assemble" + flag_groups { + expand_if_available: "output_file" + flags: "-o" + flags: "%{output_file}" + } +} +name: "compiler_output_flags" +""" diff --git a/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/cpp20_module_flags.bzl b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/cpp20_module_flags.bzl new file mode 100644 index 000000000..3af6099f7 --- /dev/null +++ b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/cpp20_module_flags.bzl @@ -0,0 +1,36 @@ +# 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. +"""Expected cpp20_module_flags legacy feature textproto on unix.""" + +visibility("private") + +GOLDEN = """enabled: false +flag_sets { + actions: "c++-compile" + actions: "c++20-module-codegen" + actions: "c++20-module-compile" + flag_groups { + expand_if_available: "cpp_module_modmap_file" + flags: "@%{cpp_module_modmap_file}" + } +} +flag_sets { + actions: "c++20-module-compile" + flag_groups { + expand_if_available: "cpp_module_output_file" + flags: "-fmodule-output=%{cpp_module_output_file}" + } +} +name: "cpp20_module_flags" +""" From b368e9c5a908a7ab128d69bdf40a6648d508c0d8 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Wed, 28 Oct 2026 06:20:27 -0700 Subject: [PATCH 34/61] Add linker directory -B flag when using lld or gold (#943) When local C++ toolchain autoconfiguration detects `lld` or `gold` for GCC (or when Clang does not resolve to an absolute linker binary path), it adds `-fuse-ld=lld` or `-fuse-ld=gold`. However, if the linker binary resides in a directory other than the compiler directory (for instance, in an LLVM directory that was on PATH during repository configuration but is not in the default action PATH), GCC's `collect2` fails with: `collect2: fatal error: cannot find 'ld'` under strict action environments. This change looks up `ld.` (or ``) and appends `-B` to ensure the compiler can locate the selected linker executable during action execution. Fixes bazelbuild/bazel#21334 Closes #943 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/943 from pefoley2:main bec34c09b15531f375920703675386e1483a5488 PiperOrigin-RevId: 1008857598 Change-Id: Iea2bcf99061cedab6b1b321699f9ba4e0e307e9a --- cc/private/toolchain/unix_cc_configure.bzl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index 65ac6ce9d..c025a8fcd 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -561,7 +561,13 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): if not gold_or_lld_linker_path: 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_string(str(ld_path.dirname))) + elif not gold_or_lld_linker_path.startswith("/"): + ld_binary = repository_ctx.which("ld." + gold_or_lld_linker_path) or repository_ctx.which(gold_or_lld_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_string(str(ld_path.dirname))) force_linker_flags = [] if gold_or_lld_linker_path: force_linker_flags.append("-fuse-ld=" + gold_or_lld_linker_path) From df732a095ddb6d552fcb8165468557922b08b8b3 Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 28 Oct 2026 06:54:35 -0700 Subject: [PATCH 35/61] Starlarkify _cc_internal.create_cc_compile_action_template(). Replace _cc_internal.create_cc_compile_action_template() with ctx.actions.map_directory() and _cc_internal.create_cc_compile_action(). PiperOrigin-RevId: 1008874841 Change-Id: Id7c325eb90c290ee6a8d2fdd4107d8a08273fc97 --- MODULE.bazel | 2 +- cc/common/cc_helper_internal.bzl | 9 + cc/private/compile/cc_compilation_helper.bzl | 11 + cc/private/compile/compile.bzl | 11 +- .../compile/compile_action_templates.bzl | 274 ++++++++++++++++-- .../compile/compile_build_variables.bzl | 32 ++ 6 files changed, 305 insertions(+), 34 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 6d274608c..36a2373a1 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,7 +5,7 @@ module( ) bazel_dep(name = "apple_support", version = "2.9.1") -bazel_dep(name = "bazel_features", version = "1.50.0") +bazel_dep(name = "bazel_features", version = "1.54.1") bazel_dep(name = "bazel_skylib", version = "1.8.0") bazel_dep(name = "platforms", version = "0.0.10") bazel_dep(name = "protobuf", version = "27.0", repo_name = "com_google_protobuf") diff --git a/cc/common/cc_helper_internal.bzl b/cc/common/cc_helper_internal.bzl index 318049ad2..a2e88c30f 100644 --- a/cc/common/cc_helper_internal.bzl +++ b/cc/common/cc_helper_internal.bzl @@ -202,6 +202,14 @@ _CC_AND_OBJC.extend(_CC_HEADER) _CC_AND_OBJC.extend(_ASSEMBLER) _CC_AND_OBJC.extend(_ASSEMBLER_WITH_C_PREPROCESSOR) +_CC_AND_OBJC_SOURCE = [] +_CC_AND_OBJC_SOURCE.extend(_CC_SOURCE) +_CC_AND_OBJC_SOURCE.extend(_C_SOURCE) +_CC_AND_OBJC_SOURCE.extend(_OBJC_SOURCE) +_CC_AND_OBJC_SOURCE.extend(_OBJCPP_SOURCE) +_CC_AND_OBJC_SOURCE.extend(_ASSEMBLER) +_CC_AND_OBJC_SOURCE.extend(_ASSEMBLER_WITH_C_PREPROCESSOR) + _DISALLOWED_HDRS_FILES = [] _DISALLOWED_HDRS_FILES.extend(_ARCHIVE) _DISALLOWED_HDRS_FILES.extend(_PIC_ARCHIVE) @@ -232,6 +240,7 @@ extensions = struct( OBJECT_FILE = _OBJECT_FILE, PIC_OBJECT_FILE = _PIC_OBJECT_FILE, CC_AND_OBJC = _CC_AND_OBJC, + CC_AND_OBJC_SOURCE = _CC_AND_OBJC_SOURCE, DISALLOWED_HDRS_FILES = _DISALLOWED_HDRS_FILES, # Also includes VERSIONED_SHARED_LIBRARY files. CPP_MODULE = _CPP_MODULE, CPP_MODULE_MAP = _CPP_MODULE_MAP, diff --git a/cc/private/compile/cc_compilation_helper.bzl b/cc/private/compile/cc_compilation_helper.bzl index 2dac4ad3b..18563bf0d 100644 --- a/cc/private/compile/cc_compilation_helper.bzl +++ b/cc/private/compile/cc_compilation_helper.bzl @@ -16,6 +16,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load( "//cc/common:cc_helper_internal.bzl", + "extensions", "get_relative_path", "package_source_root", "repository_exec_path", @@ -722,6 +723,16 @@ def dotd_files_enabled(language, cpp_configuration, feature_configuration): not feature_configuration.is_enabled(feature_names.NO_DOTD_FILE) ) +# buildifier: disable=function-docstring +def use_dotd_file(feature_configuration, source_file): + extension = "." + source_file.extension if source_file.extension else "" + header_discover_required = extension not in (extensions.ASSEMBLER + extensions.CPP_MODULE) + use_header_modules = ( + feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES) and + extension in (extensions.CC_SOURCE + extensions.CC_HEADER + extensions.CPP_MODULE_MAP) + ) + return header_discover_required and not use_header_modules + # buildifier: disable=function-docstring def serialized_diagnostics_file_enabled(feature_configuration): return feature_configuration.is_enabled(feature_names.SERIALIZED_DIAGNOSTICS_FILE) diff --git a/cc/private/compile/compile.bzl b/cc/private/compile/compile.bzl index 2643b2aa3..d8040a51c 100644 --- a/cc/private/compile/compile.bzl +++ b/cc/private/compile/compile.bzl @@ -41,7 +41,7 @@ load( "get_module_map_name", ) load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") -load("//cc/private/compile:cc_compilation_helper.bzl", "cc_compilation_helper", "dotd_files_enabled", "serialized_diagnostics_file_enabled") +load("//cc/private/compile:cc_compilation_helper.bzl", "cc_compilation_helper", "dotd_files_enabled", "serialized_diagnostics_file_enabled", _use_dotd_file = "use_dotd_file") load("//cc/private/compile:cc_compilation_outputs.bzl", "create_compilation_outputs_internal") load("//cc/private/compile:compile_action_templates.bzl", "create_compile_action_templates") load( @@ -2167,15 +2167,6 @@ def _get_compile_output_file(ctx, label, *, output_name, configuration): ) return file -def _use_dotd_file(feature_configuration, source_file): - extension = "." + source_file.extension if source_file.extension else "" - header_discover_required = extension not in (extensions.ASSEMBLER + extensions.CPP_MODULE) - use_header_modules = ( - feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES) and - extension in extensions.CC_SOURCE + extensions.CC_HEADER + extensions.CPP_MODULE_MAP - ) - return header_discover_required and not use_header_modules - def _calculate_output_name_map_by_type(sources, prefix_dir): return ( _calculate_output_name_map( diff --git a/cc/private/compile/compile_action_templates.bzl b/cc/private/compile/compile_action_templates.bzl index 1744c759f..0c11b46f1 100644 --- a/cc/private/compile/compile_action_templates.bzl +++ b/cc/private/compile/compile_action_templates.bzl @@ -13,18 +13,25 @@ # limitations under the License. """Tree artifact compilation actions""" +load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_skylib//lib:paths.bzl", "paths") load( "//cc/common:cc_helper_internal.bzl", "CPP_SOURCE_TYPE_HEADER", "CPP_SOURCE_TYPE_SOURCE", + "extensions", artifact_category = "artifact_category_names", ) load("//cc/common:feature_names.bzl", "feature_names") load("//cc/common:semantics.bzl", cc_semantics = "semantics") load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") -load("//cc/private/compile:cc_compilation_helper.bzl", "dotd_files_enabled", "serialized_diagnostics_file_enabled") -load("//cc/private/compile:compile_build_variables.bzl", "get_copts", "get_specific_compile_build_variables") +load("//cc/private/compile:cc_compilation_helper.bzl", "dotd_files_enabled", "serialized_diagnostics_file_enabled", "use_dotd_file") +load( + "//cc/private/compile:compile_build_variables.bzl", + "get_copts", + "get_specific_compile_build_variables", + "get_template_added_build_variables", +) # buildifier: disable=function-docstring def create_compile_action_templates( @@ -193,29 +200,250 @@ def _create_compile_action_template( ) if lto_output_enabled: outputs["lto_compilation_context"][output_dir] = (lto_index_tree_artifact, all_copts) - _cc_internal.create_cc_compile_action_template( - action_construction_context = action_construction_context, - cc_compilation_context = cc_compilation_context, - cc_toolchain = cc_toolchain, - configuration = configuration, - feature_configuration = feature_configuration, - compile_build_variables = _cc_internal.combine_cc_toolchain_variables( - common_compile_build_variables, - specific_compile_build_variables, + + if bazel_features.cc._map_directory_supports_cc_compile: + _starlark_create_cc_compile_action_template( + action_construction_context = action_construction_context, + cc_compilation_context = cc_compilation_context, + cc_toolchain = cc_toolchain, + configuration = configuration, + feature_configuration = feature_configuration, + compile_build_variables = _cc_internal.combine_cc_toolchain_variables( + common_compile_build_variables, + specific_compile_build_variables, + ), + source_dir = source_dir, + additional_compilation_inputs = additional_compilation_inputs, + additional_include_scanning_roots = additional_include_scanning_roots, + use_pic = use_pic, + output_categories = output_categories, + output_dir = output_dir, + dotd_tree_artifact = dotd_tree_artifact, + diagnostics_tree_artifact = diagnostics_tree_artifact, + lto_indexing_tree_artifact = lto_index_tree_artifact, + needs_include_validation = cc_semantics.needs_include_validation(language), + toolchain_type = cc_semantics.toolchain, + ) + else: + _cc_internal.create_cc_compile_action_template( + action_construction_context = action_construction_context, + cc_compilation_context = cc_compilation_context, + cc_toolchain = cc_toolchain, + configuration = configuration, + feature_configuration = feature_configuration, + compile_build_variables = _cc_internal.combine_cc_toolchain_variables( + common_compile_build_variables, + specific_compile_build_variables, + ), + source = source_dir, + additional_compilation_inputs = additional_compilation_inputs, + additional_include_scanning_roots = additional_include_scanning_roots, + use_pic = use_pic, + output_categories = output_categories, + output_files = output_dir, + dotd_tree_artifact = dotd_tree_artifact, + diagnostics_tree_artifact = diagnostics_tree_artifact, + lto_indexing_tree_artifact = lto_index_tree_artifact, + needs_include_validation = cc_semantics.needs_include_validation(language), + toolchain_type = cc_semantics.toolchain, + ) + outputs[outputs_key].append(output_dir) + +def _starlark_create_cc_compile_action_template( + *, + action_construction_context, + cc_compilation_context, + cc_toolchain, + configuration, + feature_configuration, + compile_build_variables, + source_dir, + additional_compilation_inputs, + additional_include_scanning_roots, + use_pic, + output_categories, + output_dir, + dotd_tree_artifact, + diagnostics_tree_artifact, + lto_indexing_tree_artifact, + needs_include_validation, + toolchain_type): + # The setting of additional_inputs is important to prevent build failures stating that a file + # "wasn't declared as input of map_directory()". This happens if an artifact's metadata is + # pruned because no action declared to consume it - an "orphaned" file. + # This is esp. true for cc_compilation_context.headers: + # Even though cc_compilation_context is passed into map_directory() via additional_params, + # the headers within it must also be added to additional_inputs so that map_directory() can + # declare it is consuming these files, and thus their metadata is not pruned. + # See b/527759856 for more details. + additional_inputs = { + "headers": ( + depset(additional_compilation_inputs, transitive = [cc_compilation_context.headers]) if additional_compilation_inputs else cc_compilation_context.headers ), - source = source_dir, - additional_compilation_inputs = additional_compilation_inputs, - additional_include_scanning_roots = additional_include_scanning_roots, - use_pic = use_pic, - output_categories = output_categories, - output_files = output_dir, - dotd_tree_artifact = dotd_tree_artifact, - diagnostics_tree_artifact = diagnostics_tree_artifact, - lto_indexing_tree_artifact = lto_index_tree_artifact, - needs_include_validation = cc_semantics.needs_include_validation(language), - toolchain_type = cc_semantics.toolchain, + "direct_module_maps": cc_compilation_context._direct_module_maps, + "non_code_inputs": cc_compilation_context._non_code_inputs, + } + if cc_compilation_context._module_map and cc_compilation_context._module_map.file: + additional_inputs["module_map"] = cc_compilation_context._module_map.file + if feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES): + additional_inputs["transitive_modules"] = ( + cc_compilation_context._transitive_pic_modules if use_pic else cc_compilation_context._transitive_modules + ) + separate_module = ( + cc_compilation_context._header_info.separate_pic_module if use_pic else cc_compilation_context._header_info.separate_module + ) + if separate_module: + additional_inputs["separate_module"] = separate_module + + tools = { + "compiler_files": cc_toolchain._compiler_files, + "compiler_files_without_includes": cc_toolchain._compiler_files_without_includes, + } + if cc_toolchain._grep_includes: + tools["grep_includes"] = cc_toolchain._grep_includes + + additional_params = dict( + cc_compilation_context = cc_compilation_context, # struct + cc_toolchain = cc_toolchain, # struct + configuration = configuration, # BuildConfigurationApi + feature_configuration = feature_configuration, # FeatureConfigurationApi + compile_build_variables = compile_build_variables, # CcToolchainVariablesApi + additional_compilation_inputs = additional_compilation_inputs, # list of File + additional_include_scanning_roots = additional_include_scanning_roots, # list of File + use_pic = use_pic, # boolean + output_categories = output_categories, # list of strings + dotd_tree_artifact = dotd_tree_artifact, # Artifact|NoneType + diagnostics_tree_artifact = diagnostics_tree_artifact, # Artifact|NoneType + lto_indexing_tree_artifact = lto_indexing_tree_artifact, # Artifact|NoneType + needs_include_validation = needs_include_validation, # boolean + toolchain_type = toolchain_type, # string + should_compile_headers = feature_configuration.is_enabled("parse_headers"), # boolean ) - outputs[outputs_key].append(output_dir) + + output_directories = {"out_dir": output_dir} + if dotd_tree_artifact: + output_directories["dotd_out_dir"] = dotd_tree_artifact + if diagnostics_tree_artifact: + output_directories["diagnostics_out_dir"] = diagnostics_tree_artifact + if lto_indexing_tree_artifact: + output_directories["lto_indexing_out_dir"] = lto_indexing_tree_artifact + + action_construction_context.actions.map_directory( + implementation = _template_impl, + input_directories = {"in_dir": source_dir}, + output_directories = output_directories, + additional_inputs = additional_inputs, + additional_params = additional_params, + mnemonic = "CcCompileActionTemplate", + tools = tools, + toolchain = cc_semantics.toolchain, + ) + +def _template_impl( + template_ctx, + input_directories, + output_directories, + additional_inputs, # @unused + tools, # @unused + additional_params): + # additional_inputs isn't used inside _template_impl() because the contained files are covered + # by input discovery via include scanning. + # But additional_inputs must be passed into map_directory() so the inputs are know to bazel and + # their metadata isn't pruned as orphaned input files. + in_dir = input_directories["in_dir"] + out_dir = output_directories["out_dir"] + + source_files = [] + private_headers = [] + + for input_file in in_dir.children: + extension_with_dot = "." + input_file.extension + is_header = extension_with_dot in extensions.CC_HEADER + is_textual_include = extension_with_dot in extensions.CC_TEXTUAL_INCLUDE + is_source = extension_with_dot in extensions.CC_AND_OBJC_SOURCE + + if is_header: + private_headers.append(input_file) + if is_source or (is_header and additional_params["should_compile_headers"] and not is_textual_include): + source_files.append(input_file) + elif not is_header: + fail(("Artifact '%s' expanded from the directory artifact '%s' is neither header nor source file." + + " input_file.extension = %s") % (input_file.path, in_dir.directory.path, input_file.extension)) + + private_headers_depset = depset(direct = private_headers) + + for source_file in source_files: + output_file_name = _compute_output_file_name( + source_file, + additional_params["output_categories"], + additional_params["cc_toolchain"], + ) + output_file = template_ctx.declare_file(output_file_name, directory = out_dir) + dotd_file = None + if ( + additional_params["dotd_tree_artifact"] and + use_dotd_file(additional_params["feature_configuration"], source_file) + ): + dotd_file = template_ctx.declare_file( + output_file_name + ".d", + directory = additional_params["dotd_tree_artifact"], + ) + diagnostics_file = None + if additional_params["diagnostics_tree_artifact"]: + diagnostics_file = template_ctx.declare_file( + output_file_name + ".dia", + directory = additional_params["diagnostics_tree_artifact"], + ) + lto_indexing_file = None + if additional_params["lto_indexing_tree_artifact"]: + lto_index_file_name = paths.replace_extension(output_file_name, ".indexing.o") + lto_indexing_file = template_ctx.declare_file( + lto_index_file_name, + directory = additional_params["lto_indexing_tree_artifact"], + ) + + added_build_variables = get_template_added_build_variables( + source_file = source_file, + output_file = output_file, + dotd_file = dotd_file, + diagnostics_file = diagnostics_file, + lto_indexing_file = lto_indexing_file, + ) + + compile_build_variables = _cc_internal.combine_cc_toolchain_variables( + additional_params["compile_build_variables"], + added_build_variables, + ) + + _cc_internal.create_cc_compile_action( + action_construction_context = template_ctx, + cc_compilation_context = additional_params["cc_compilation_context"], + cc_toolchain = additional_params["cc_toolchain"], + configuration = additional_params["configuration"], + feature_configuration = additional_params["feature_configuration"], + additional_compilation_inputs = additional_params["additional_compilation_inputs"], + additional_include_scanning_roots = additional_params["additional_include_scanning_roots"], + source = source_file, + output_file = output_file, + diagnostics_file = diagnostics_file, + dotd_file = dotd_file, + use_pic = additional_params["use_pic"], + lto_indexing_file = lto_indexing_file, + compile_build_variables = compile_build_variables, + needs_include_validation = additional_params["needs_include_validation"], + toolchain_type = additional_params["toolchain_type"], + additional_prunable_headers = private_headers_depset, + ) + +def _compute_output_file_name(input_file, output_categories, cc_toolchain): + output_file_name = paths.split_extension(input_file.tree_relative_path)[0] + for category in output_categories: + output_file_name = _cc_internal.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = category, + output_name = output_file_name, + ) + return output_file_name def _declare_compile_output_tree_artifact( ctx, diff --git a/cc/private/compile/compile_build_variables.bzl b/cc/private/compile/compile_build_variables.bzl index f8e7b98bf..1448335b4 100644 --- a/cc/private/compile/compile_build_variables.bzl +++ b/cc/private/compile/compile_build_variables.bzl @@ -639,4 +639,36 @@ def _compute_all_linkstamp_defines( ) return _stamps_to_defines(stamps) +def get_template_added_build_variables( + source_file, + output_file, + dotd_file = None, + diagnostics_file = None, + lto_indexing_file = None): + """Returns variables for a compile action template. + + These variables are a function of the specific source file being compiled. + + Args: + source_file: (File) The source file to be compiled. + output_file: (File) The output object file. + dotd_file: (File) The dependency file, enables .d file generation. + diagnostics_file: (File) The serialized diagnostics file, enables .dia file generation. + lto_indexing_file: (File) The minimized LTO indexing bitcode file. + + Returns: + (CcToolchainVariables) containing the variables for the template. + """ + added_build_variables = { + _VARS.SOURCE_FILE: source_file, + _VARS.OUTPUT_FILE: output_file, + } + if dotd_file: + added_build_variables[_VARS.DEPENDENCY_FILE] = dotd_file + if diagnostics_file: + added_build_variables[_VARS.SERIALIZED_DIAGNOSTICS_FILE] = diagnostics_file + if lto_indexing_file: + added_build_variables[_VARS.LTO_INDEXING_BITCODE_FILE] = lto_indexing_file + return _cc_internal.cc_toolchain_variables(vars = added_build_variables) + COMPILE_BUILD_VARIABLES = _VARS From d310c2afc462665b1db53c9fb951c236842f803c Mon Sep 17 00:00:00 2001 From: gztong666 Date: Wed, 28 Oct 2026 07:59:54 -0700 Subject: [PATCH 36/61] Preserve LLVM coverage runtime object paths (#905) ## Description Preserve each LLVM runtime object path as a single command-line argument when collecting C++ coverage. This moves the fix proposed in bazelbuild/bazel#31297 to the script now maintained in rules_cc, as requested in the review there. ## Motivation The script concatenates `-object` options and paths into a string, then expands it unquoted. Paths containing whitespace are split before `llvm-cov export` receives them. A Bash array preserves each argument. A regression test exercises a path containing spaces and checks the order and boundaries of two objects. The test target lives under `tests/coverage` so that downstream users of the production `cc/coverage` package do not need the development-only `rules_shell` dependency. ## Tests - `bash -n cc/coverage/collect_cc_coverage.sh tests/coverage/collect_cc_coverage_test.sh` - `TEST_SRCDIR="$PWD" TEST_WORKSPACE=. TEST_TMPDIR="$PWD" bash tests/coverage/collect_cc_coverage_test.sh` Both pass locally. The Bazel test target `//tests/coverage:collect_cc_coverage_test` and downstream integration tests await the updated CI run. RELNOTES: None ### Related issue Fixes bazelbuild/bazel#31348 Closes #905 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/905 from gztong666:fix/llvm-coverage-object-paths 34377548442660af1ccec64476c79205f18b31c0 PiperOrigin-RevId: 1008911556 Change-Id: Id48d8414d6951ebecd6ee889d45f5d6535b40931 --- cc/coverage/collect_cc_coverage.sh | 6 +-- tests/coverage/BUILD.bazel | 9 ++++ tests/coverage/collect_cc_coverage_test.sh | 59 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 tests/coverage/BUILD.bazel create mode 100755 tests/coverage/collect_cc_coverage_test.sh 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/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" From 2ec2e09cd3cc7d77945d09b852c2696cd671eb00 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Wed, 28 Oct 2026 09:21:20 -0700 Subject: [PATCH 37/61] Let integration tests reuse the pkg directory of earlier tests (#953) Tests in the same shard of `cc_integration_test` share the scratch workspace, and some leave a `pkg` directory behind. For example, `test_cc_toolchain_asan_feature` does so on macOS, where it assumes libasan is installed. Four tests still created it with a plain `mkdir pkg`, so they failed with `mkdir: pkg: File exists` whenever a test like that ran before them in the same shard. Which tests share a shard depends on the number of tests, so adding an unrelated test can trigger this. That happened in #948, where `test_env_inherit_cc_test` started failing on macOS. This uses `mkdir -p pkg` in those tests, like the rest of the file. Closes #953 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/953 from chrisburr:integration-test-mkdir-p 2d8bb3ee20f8189cf215226c5cf8ca9dc105499c PiperOrigin-RevId: 1008964294 Change-Id: Idfd3b277f5d93684c5033d5916889382544fed07 --- tests/integration/cc_integration_test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/cc_integration_test.sh b/tests/integration/cc_integration_test.sh index b46d7b5b0..4b7e39672 100755 --- a/tests/integration/cc_integration_test.sh +++ b/tests/integration/cc_integration_test.sh @@ -1476,7 +1476,7 @@ EOF function test_env_inherit_cc_test() { - mkdir pkg + mkdir -p pkg cat > pkg/BUILD < pkg/BUILD < pkg/BUILD < pkg/BUILD < Date: Wed, 28 Oct 2026 11:07:37 -0700 Subject: [PATCH 38/61] Use the DLL's file name in the DEF file generated by cc_shared_library (#950) `cc_shared_library` generates its DEF file (with `windows_export_all_symbols` or a toolchain-provided `generate_def_file` action) with a `LIBRARY` statement that is the target name. The linker records that name in the import library, so for a target `foo_shared` with `shared_lib_name = "foo1.dll"`, dependents import `foo_shared.dll`, which doesn't exist at runtime. The same happens without `shared_lib_name` on toolchains whose DLL name pattern adds a prefix, e.g. `libfoo_shared.dll` on MinGW. This passes the actual output file name instead, which is what `cc_binary(linkshared = True)` already does. Checked with lld-link: a DEF file with `LIBRARY foo_shared` linked with `/out:foo1.dll` yields an import library whose members reference `foo_shared.dll`, while a DEF file with the matching name (or without `LIBRARY`) yields references to `foo1.dll`. With this change, the `DefParser` action for the example above receives `foo1.dll` instead of `foo_shared_named`. Found while looking into bazelbuild/bazel#31951. Closes #950 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/950 from fmeum:csl-def-library-name 1a7076148051df4adeef28b674a0ce733c35dda7 PiperOrigin-RevId: 1009039396 Change-Id: I6405d69e0b6158b9f3f445c6eadeb878ef57110b --- cc/private/rules_impl/cc_shared_library_impl.bzl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cc/private/rules_impl/cc_shared_library_impl.bzl b/cc/private/rules_impl/cc_shared_library_impl.bzl index 5c8bce7a8..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") @@ -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, ) From 221626445982f29a9c437d1227997d50175c21ef Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 28 Oct 2026 16:26:57 -0700 Subject: [PATCH 39/61] Name MinGW DLLs `lib.dll` (#951) The MinGW toolchain config only overrides the name pattern for executables, so dynamic libraries (`cc_shared_library` outputs and `cc_library`'s own DLLs) fall back to `lib.so`. lld in MinGW mode doesn't search for `.so` files when resolving `-l`, so linking anything against such a library fails with `unable to find library -l`. With a `lib` + `.dll` pattern, which is MinGW's naming convention, `-l` resolves directly to the DLL. Closes #951 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/951 from fmeum:mingw-dll-artifact-name 86c9c245b6bd6139b5c28669c4d998e8f46c3690 PiperOrigin-RevId: 1009255440 Change-Id: Ibd47bd0ddf38eecc3ff9ebe9b3643bba1139bacc --- cc/private/toolchain/windows_cc_toolchain_config.bzl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cc/private/toolchain/windows_cc_toolchain_config.bzl b/cc/private/toolchain/windows_cc_toolchain_config.bzl index 8d447af67..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): From 28acac02a118f94eddf73f2d81eed4d4a70e4157 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 28 Oct 2026 16:29:00 -0700 Subject: [PATCH 40/61] Fallback to finding clang-scan-deps on PATH (#887) Fixes https://github.com/bazelbuild/rules_cc/issues/553 Closes #887 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/887 from keith:ks/fallback-to-finding-clang-scan-deps-on-path c86791860687004fec6110ebcf78870cb46b6851 PiperOrigin-RevId: 1009256727 Change-Id: Ia8f5499e00e2eab199ab69ce5c82414482902508 --- cc/private/toolchain/unix_cc_configure.bzl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index c025a8fcd..4297f37c1 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -513,10 +513,11 @@ 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", deps_scanner) repository_ctx.template( "deps_scanner_wrapper.sh", paths[deps_scanner_wrapper_src], From 45a967f1136b2ff982fa89236d23d63844ad9b4e Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Wed, 28 Oct 2026 18:01:29 -0700 Subject: [PATCH 41/61] Avoid unnecessary versioned library checks in cc_helper (#871) `_get_public_hdrs` now checks `extensions.DISALLOWED_HDRS_FILES` through a dictionary, avoiding repeated linear membership scans. `_check_file_extension` calls `is_versioned_shared_library_extension_valid` only when `file.extension` begins with a digit, as required by versioned `.so` and `.dylib` names. Validation: `buildifier -mode=check cc/common/cc_helper.bzl`; `bazel test //tests/cc/common:cc_library_configured_target_tests //tests/cc/common:cc_import_configured_target_tests` (19 tests passed). Closes #871 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/871 from dzbarsky:codex/filter-versioned-extension-checks 6a42930bb2523f9996cfb9025d0e8a5d14f472b2 PiperOrigin-RevId: 1009305857 Change-Id: If70e16a8d9cfd1c99299d5390658887e6f2f3b94 --- cc/common/cc_helper.bzl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index ad009a742..d77c5a6dd 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -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) @@ -378,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: @@ -1150,7 +1155,7 @@ 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 artifact_label_map.items() From 880db0ab1b6d2d08dd078ca912931b36bb2434e3 Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 28 Oct 2026 21:33:12 -0700 Subject: [PATCH 42/61] Automated rollback of commit df732a095ddb6d552fcb8165468557922b08b8b3. *** Reason for rollback *** Pure rollback *** Original change description *** Starlarkify _cc_internal.create_cc_compile_action_template(). Replace _cc_internal.create_cc_compile_action_template() with ctx.actions.map_directory() and _cc_internal.create_cc_compile_action(). PiperOrigin-RevId: 1009396286 Change-Id: I9ca9ca710f2eb1a6f091ea108abca2a88859a2c6 --- MODULE.bazel | 2 +- cc/common/cc_helper_internal.bzl | 9 - cc/private/compile/cc_compilation_helper.bzl | 11 - cc/private/compile/compile.bzl | 11 +- .../compile/compile_action_templates.bzl | 274 ++---------------- .../compile/compile_build_variables.bzl | 32 -- 6 files changed, 34 insertions(+), 305 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 36a2373a1..6d274608c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,7 +5,7 @@ module( ) bazel_dep(name = "apple_support", version = "2.9.1") -bazel_dep(name = "bazel_features", version = "1.54.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") bazel_dep(name = "protobuf", version = "27.0", repo_name = "com_google_protobuf") diff --git a/cc/common/cc_helper_internal.bzl b/cc/common/cc_helper_internal.bzl index a2e88c30f..318049ad2 100644 --- a/cc/common/cc_helper_internal.bzl +++ b/cc/common/cc_helper_internal.bzl @@ -202,14 +202,6 @@ _CC_AND_OBJC.extend(_CC_HEADER) _CC_AND_OBJC.extend(_ASSEMBLER) _CC_AND_OBJC.extend(_ASSEMBLER_WITH_C_PREPROCESSOR) -_CC_AND_OBJC_SOURCE = [] -_CC_AND_OBJC_SOURCE.extend(_CC_SOURCE) -_CC_AND_OBJC_SOURCE.extend(_C_SOURCE) -_CC_AND_OBJC_SOURCE.extend(_OBJC_SOURCE) -_CC_AND_OBJC_SOURCE.extend(_OBJCPP_SOURCE) -_CC_AND_OBJC_SOURCE.extend(_ASSEMBLER) -_CC_AND_OBJC_SOURCE.extend(_ASSEMBLER_WITH_C_PREPROCESSOR) - _DISALLOWED_HDRS_FILES = [] _DISALLOWED_HDRS_FILES.extend(_ARCHIVE) _DISALLOWED_HDRS_FILES.extend(_PIC_ARCHIVE) @@ -240,7 +232,6 @@ extensions = struct( OBJECT_FILE = _OBJECT_FILE, PIC_OBJECT_FILE = _PIC_OBJECT_FILE, CC_AND_OBJC = _CC_AND_OBJC, - CC_AND_OBJC_SOURCE = _CC_AND_OBJC_SOURCE, DISALLOWED_HDRS_FILES = _DISALLOWED_HDRS_FILES, # Also includes VERSIONED_SHARED_LIBRARY files. CPP_MODULE = _CPP_MODULE, CPP_MODULE_MAP = _CPP_MODULE_MAP, diff --git a/cc/private/compile/cc_compilation_helper.bzl b/cc/private/compile/cc_compilation_helper.bzl index 18563bf0d..2dac4ad3b 100644 --- a/cc/private/compile/cc_compilation_helper.bzl +++ b/cc/private/compile/cc_compilation_helper.bzl @@ -16,7 +16,6 @@ load("@bazel_skylib//lib:paths.bzl", "paths") load( "//cc/common:cc_helper_internal.bzl", - "extensions", "get_relative_path", "package_source_root", "repository_exec_path", @@ -723,16 +722,6 @@ def dotd_files_enabled(language, cpp_configuration, feature_configuration): not feature_configuration.is_enabled(feature_names.NO_DOTD_FILE) ) -# buildifier: disable=function-docstring -def use_dotd_file(feature_configuration, source_file): - extension = "." + source_file.extension if source_file.extension else "" - header_discover_required = extension not in (extensions.ASSEMBLER + extensions.CPP_MODULE) - use_header_modules = ( - feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES) and - extension in (extensions.CC_SOURCE + extensions.CC_HEADER + extensions.CPP_MODULE_MAP) - ) - return header_discover_required and not use_header_modules - # buildifier: disable=function-docstring def serialized_diagnostics_file_enabled(feature_configuration): return feature_configuration.is_enabled(feature_names.SERIALIZED_DIAGNOSTICS_FILE) diff --git a/cc/private/compile/compile.bzl b/cc/private/compile/compile.bzl index d8040a51c..2643b2aa3 100644 --- a/cc/private/compile/compile.bzl +++ b/cc/private/compile/compile.bzl @@ -41,7 +41,7 @@ load( "get_module_map_name", ) load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") -load("//cc/private/compile:cc_compilation_helper.bzl", "cc_compilation_helper", "dotd_files_enabled", "serialized_diagnostics_file_enabled", _use_dotd_file = "use_dotd_file") +load("//cc/private/compile:cc_compilation_helper.bzl", "cc_compilation_helper", "dotd_files_enabled", "serialized_diagnostics_file_enabled") load("//cc/private/compile:cc_compilation_outputs.bzl", "create_compilation_outputs_internal") load("//cc/private/compile:compile_action_templates.bzl", "create_compile_action_templates") load( @@ -2167,6 +2167,15 @@ def _get_compile_output_file(ctx, label, *, output_name, configuration): ) return file +def _use_dotd_file(feature_configuration, source_file): + extension = "." + source_file.extension if source_file.extension else "" + header_discover_required = extension not in (extensions.ASSEMBLER + extensions.CPP_MODULE) + use_header_modules = ( + feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES) and + extension in extensions.CC_SOURCE + extensions.CC_HEADER + extensions.CPP_MODULE_MAP + ) + return header_discover_required and not use_header_modules + def _calculate_output_name_map_by_type(sources, prefix_dir): return ( _calculate_output_name_map( diff --git a/cc/private/compile/compile_action_templates.bzl b/cc/private/compile/compile_action_templates.bzl index 0c11b46f1..1744c759f 100644 --- a/cc/private/compile/compile_action_templates.bzl +++ b/cc/private/compile/compile_action_templates.bzl @@ -13,25 +13,18 @@ # limitations under the License. """Tree artifact compilation actions""" -load("@bazel_features//:features.bzl", "bazel_features") load("@bazel_skylib//lib:paths.bzl", "paths") load( "//cc/common:cc_helper_internal.bzl", "CPP_SOURCE_TYPE_HEADER", "CPP_SOURCE_TYPE_SOURCE", - "extensions", artifact_category = "artifact_category_names", ) load("//cc/common:feature_names.bzl", "feature_names") load("//cc/common:semantics.bzl", cc_semantics = "semantics") load("//cc/private:cc_internal.bzl", _cc_internal = "cc_internal") -load("//cc/private/compile:cc_compilation_helper.bzl", "dotd_files_enabled", "serialized_diagnostics_file_enabled", "use_dotd_file") -load( - "//cc/private/compile:compile_build_variables.bzl", - "get_copts", - "get_specific_compile_build_variables", - "get_template_added_build_variables", -) +load("//cc/private/compile:cc_compilation_helper.bzl", "dotd_files_enabled", "serialized_diagnostics_file_enabled") +load("//cc/private/compile:compile_build_variables.bzl", "get_copts", "get_specific_compile_build_variables") # buildifier: disable=function-docstring def create_compile_action_templates( @@ -200,250 +193,29 @@ def _create_compile_action_template( ) if lto_output_enabled: outputs["lto_compilation_context"][output_dir] = (lto_index_tree_artifact, all_copts) - - if bazel_features.cc._map_directory_supports_cc_compile: - _starlark_create_cc_compile_action_template( - action_construction_context = action_construction_context, - cc_compilation_context = cc_compilation_context, - cc_toolchain = cc_toolchain, - configuration = configuration, - feature_configuration = feature_configuration, - compile_build_variables = _cc_internal.combine_cc_toolchain_variables( - common_compile_build_variables, - specific_compile_build_variables, - ), - source_dir = source_dir, - additional_compilation_inputs = additional_compilation_inputs, - additional_include_scanning_roots = additional_include_scanning_roots, - use_pic = use_pic, - output_categories = output_categories, - output_dir = output_dir, - dotd_tree_artifact = dotd_tree_artifact, - diagnostics_tree_artifact = diagnostics_tree_artifact, - lto_indexing_tree_artifact = lto_index_tree_artifact, - needs_include_validation = cc_semantics.needs_include_validation(language), - toolchain_type = cc_semantics.toolchain, - ) - else: - _cc_internal.create_cc_compile_action_template( - action_construction_context = action_construction_context, - cc_compilation_context = cc_compilation_context, - cc_toolchain = cc_toolchain, - configuration = configuration, - feature_configuration = feature_configuration, - compile_build_variables = _cc_internal.combine_cc_toolchain_variables( - common_compile_build_variables, - specific_compile_build_variables, - ), - source = source_dir, - additional_compilation_inputs = additional_compilation_inputs, - additional_include_scanning_roots = additional_include_scanning_roots, - use_pic = use_pic, - output_categories = output_categories, - output_files = output_dir, - dotd_tree_artifact = dotd_tree_artifact, - diagnostics_tree_artifact = diagnostics_tree_artifact, - lto_indexing_tree_artifact = lto_index_tree_artifact, - needs_include_validation = cc_semantics.needs_include_validation(language), - toolchain_type = cc_semantics.toolchain, - ) - outputs[outputs_key].append(output_dir) - -def _starlark_create_cc_compile_action_template( - *, - action_construction_context, - cc_compilation_context, - cc_toolchain, - configuration, - feature_configuration, - compile_build_variables, - source_dir, - additional_compilation_inputs, - additional_include_scanning_roots, - use_pic, - output_categories, - output_dir, - dotd_tree_artifact, - diagnostics_tree_artifact, - lto_indexing_tree_artifact, - needs_include_validation, - toolchain_type): - # The setting of additional_inputs is important to prevent build failures stating that a file - # "wasn't declared as input of map_directory()". This happens if an artifact's metadata is - # pruned because no action declared to consume it - an "orphaned" file. - # This is esp. true for cc_compilation_context.headers: - # Even though cc_compilation_context is passed into map_directory() via additional_params, - # the headers within it must also be added to additional_inputs so that map_directory() can - # declare it is consuming these files, and thus their metadata is not pruned. - # See b/527759856 for more details. - additional_inputs = { - "headers": ( - depset(additional_compilation_inputs, transitive = [cc_compilation_context.headers]) if additional_compilation_inputs else cc_compilation_context.headers + _cc_internal.create_cc_compile_action_template( + action_construction_context = action_construction_context, + cc_compilation_context = cc_compilation_context, + cc_toolchain = cc_toolchain, + configuration = configuration, + feature_configuration = feature_configuration, + compile_build_variables = _cc_internal.combine_cc_toolchain_variables( + common_compile_build_variables, + specific_compile_build_variables, ), - "direct_module_maps": cc_compilation_context._direct_module_maps, - "non_code_inputs": cc_compilation_context._non_code_inputs, - } - if cc_compilation_context._module_map and cc_compilation_context._module_map.file: - additional_inputs["module_map"] = cc_compilation_context._module_map.file - if feature_configuration.is_enabled(feature_names.USE_HEADER_MODULES): - additional_inputs["transitive_modules"] = ( - cc_compilation_context._transitive_pic_modules if use_pic else cc_compilation_context._transitive_modules - ) - separate_module = ( - cc_compilation_context._header_info.separate_pic_module if use_pic else cc_compilation_context._header_info.separate_module - ) - if separate_module: - additional_inputs["separate_module"] = separate_module - - tools = { - "compiler_files": cc_toolchain._compiler_files, - "compiler_files_without_includes": cc_toolchain._compiler_files_without_includes, - } - if cc_toolchain._grep_includes: - tools["grep_includes"] = cc_toolchain._grep_includes - - additional_params = dict( - cc_compilation_context = cc_compilation_context, # struct - cc_toolchain = cc_toolchain, # struct - configuration = configuration, # BuildConfigurationApi - feature_configuration = feature_configuration, # FeatureConfigurationApi - compile_build_variables = compile_build_variables, # CcToolchainVariablesApi - additional_compilation_inputs = additional_compilation_inputs, # list of File - additional_include_scanning_roots = additional_include_scanning_roots, # list of File - use_pic = use_pic, # boolean - output_categories = output_categories, # list of strings - dotd_tree_artifact = dotd_tree_artifact, # Artifact|NoneType - diagnostics_tree_artifact = diagnostics_tree_artifact, # Artifact|NoneType - lto_indexing_tree_artifact = lto_indexing_tree_artifact, # Artifact|NoneType - needs_include_validation = needs_include_validation, # boolean - toolchain_type = toolchain_type, # string - should_compile_headers = feature_configuration.is_enabled("parse_headers"), # boolean - ) - - output_directories = {"out_dir": output_dir} - if dotd_tree_artifact: - output_directories["dotd_out_dir"] = dotd_tree_artifact - if diagnostics_tree_artifact: - output_directories["diagnostics_out_dir"] = diagnostics_tree_artifact - if lto_indexing_tree_artifact: - output_directories["lto_indexing_out_dir"] = lto_indexing_tree_artifact - - action_construction_context.actions.map_directory( - implementation = _template_impl, - input_directories = {"in_dir": source_dir}, - output_directories = output_directories, - additional_inputs = additional_inputs, - additional_params = additional_params, - mnemonic = "CcCompileActionTemplate", - tools = tools, - toolchain = cc_semantics.toolchain, + source = source_dir, + additional_compilation_inputs = additional_compilation_inputs, + additional_include_scanning_roots = additional_include_scanning_roots, + use_pic = use_pic, + output_categories = output_categories, + output_files = output_dir, + dotd_tree_artifact = dotd_tree_artifact, + diagnostics_tree_artifact = diagnostics_tree_artifact, + lto_indexing_tree_artifact = lto_index_tree_artifact, + needs_include_validation = cc_semantics.needs_include_validation(language), + toolchain_type = cc_semantics.toolchain, ) - -def _template_impl( - template_ctx, - input_directories, - output_directories, - additional_inputs, # @unused - tools, # @unused - additional_params): - # additional_inputs isn't used inside _template_impl() because the contained files are covered - # by input discovery via include scanning. - # But additional_inputs must be passed into map_directory() so the inputs are know to bazel and - # their metadata isn't pruned as orphaned input files. - in_dir = input_directories["in_dir"] - out_dir = output_directories["out_dir"] - - source_files = [] - private_headers = [] - - for input_file in in_dir.children: - extension_with_dot = "." + input_file.extension - is_header = extension_with_dot in extensions.CC_HEADER - is_textual_include = extension_with_dot in extensions.CC_TEXTUAL_INCLUDE - is_source = extension_with_dot in extensions.CC_AND_OBJC_SOURCE - - if is_header: - private_headers.append(input_file) - if is_source or (is_header and additional_params["should_compile_headers"] and not is_textual_include): - source_files.append(input_file) - elif not is_header: - fail(("Artifact '%s' expanded from the directory artifact '%s' is neither header nor source file." + - " input_file.extension = %s") % (input_file.path, in_dir.directory.path, input_file.extension)) - - private_headers_depset = depset(direct = private_headers) - - for source_file in source_files: - output_file_name = _compute_output_file_name( - source_file, - additional_params["output_categories"], - additional_params["cc_toolchain"], - ) - output_file = template_ctx.declare_file(output_file_name, directory = out_dir) - dotd_file = None - if ( - additional_params["dotd_tree_artifact"] and - use_dotd_file(additional_params["feature_configuration"], source_file) - ): - dotd_file = template_ctx.declare_file( - output_file_name + ".d", - directory = additional_params["dotd_tree_artifact"], - ) - diagnostics_file = None - if additional_params["diagnostics_tree_artifact"]: - diagnostics_file = template_ctx.declare_file( - output_file_name + ".dia", - directory = additional_params["diagnostics_tree_artifact"], - ) - lto_indexing_file = None - if additional_params["lto_indexing_tree_artifact"]: - lto_index_file_name = paths.replace_extension(output_file_name, ".indexing.o") - lto_indexing_file = template_ctx.declare_file( - lto_index_file_name, - directory = additional_params["lto_indexing_tree_artifact"], - ) - - added_build_variables = get_template_added_build_variables( - source_file = source_file, - output_file = output_file, - dotd_file = dotd_file, - diagnostics_file = diagnostics_file, - lto_indexing_file = lto_indexing_file, - ) - - compile_build_variables = _cc_internal.combine_cc_toolchain_variables( - additional_params["compile_build_variables"], - added_build_variables, - ) - - _cc_internal.create_cc_compile_action( - action_construction_context = template_ctx, - cc_compilation_context = additional_params["cc_compilation_context"], - cc_toolchain = additional_params["cc_toolchain"], - configuration = additional_params["configuration"], - feature_configuration = additional_params["feature_configuration"], - additional_compilation_inputs = additional_params["additional_compilation_inputs"], - additional_include_scanning_roots = additional_params["additional_include_scanning_roots"], - source = source_file, - output_file = output_file, - diagnostics_file = diagnostics_file, - dotd_file = dotd_file, - use_pic = additional_params["use_pic"], - lto_indexing_file = lto_indexing_file, - compile_build_variables = compile_build_variables, - needs_include_validation = additional_params["needs_include_validation"], - toolchain_type = additional_params["toolchain_type"], - additional_prunable_headers = private_headers_depset, - ) - -def _compute_output_file_name(input_file, output_categories, cc_toolchain): - output_file_name = paths.split_extension(input_file.tree_relative_path)[0] - for category in output_categories: - output_file_name = _cc_internal.get_artifact_name_for_category( - cc_toolchain = cc_toolchain, - category = category, - output_name = output_file_name, - ) - return output_file_name + outputs[outputs_key].append(output_dir) def _declare_compile_output_tree_artifact( ctx, diff --git a/cc/private/compile/compile_build_variables.bzl b/cc/private/compile/compile_build_variables.bzl index 1448335b4..f8e7b98bf 100644 --- a/cc/private/compile/compile_build_variables.bzl +++ b/cc/private/compile/compile_build_variables.bzl @@ -639,36 +639,4 @@ def _compute_all_linkstamp_defines( ) return _stamps_to_defines(stamps) -def get_template_added_build_variables( - source_file, - output_file, - dotd_file = None, - diagnostics_file = None, - lto_indexing_file = None): - """Returns variables for a compile action template. - - These variables are a function of the specific source file being compiled. - - Args: - source_file: (File) The source file to be compiled. - output_file: (File) The output object file. - dotd_file: (File) The dependency file, enables .d file generation. - diagnostics_file: (File) The serialized diagnostics file, enables .dia file generation. - lto_indexing_file: (File) The minimized LTO indexing bitcode file. - - Returns: - (CcToolchainVariables) containing the variables for the template. - """ - added_build_variables = { - _VARS.SOURCE_FILE: source_file, - _VARS.OUTPUT_FILE: output_file, - } - if dotd_file: - added_build_variables[_VARS.DEPENDENCY_FILE] = dotd_file - if diagnostics_file: - added_build_variables[_VARS.SERIALIZED_DIAGNOSTICS_FILE] = diagnostics_file - if lto_indexing_file: - added_build_variables[_VARS.LTO_INDEXING_BITCODE_FILE] = lto_indexing_file - return _cc_internal.cc_toolchain_variables(vars = added_build_variables) - COMPILE_BUILD_VARIABLES = _VARS From 1448f8bd0b4bcca0a55c8366bb6a16fe23afd736 Mon Sep 17 00:00:00 2001 From: Googler CA Date: Thu, 29 Oct 2026 02:50:29 -0700 Subject: [PATCH 43/61] Add analysis tests for cc_shared_library DEF file DLL name generation. unknown commit updates `cc_shared_library` to pass the output DLL's basename to `DefParser` when generating a Windows DEF file, but does not include automated tests for the default toolchain naming or `shared_lib_name` overrides. Expose a `dynamic_library_pattern` build setting flag on the mock C++ toolchain configuration so analysis tests can override the `dynamic_library` artifact name prefix and extension. Add Starlark analysis tests verifying that `DefParser` receives the expected DLL basename and that `CppLink` consumes the generated `.gen.def` file across the default toolchain pattern, MinGW and MSVC `dynamic_library` patterns, an explicit `shared_lib_name` override, and a `shared_lib_name` containing a subdirectory path. PiperOrigin-RevId: 1009544025 Change-Id: I404f52ac101d8ffd6baf163caf1ca275b4d67d15 --- .../cc_binary_configured_target_tests.bzl | 104 ++++++++++++++++++ tests/cc/testutil/toolchains/BUILD | 5 + .../toolchains/cc_toolchain_config.bzl | 4 + 3 files changed, 113 insertions(+) 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/testutil/toolchains/BUILD b/tests/cc/testutil/toolchains/BUILD index 4894971f8..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) diff --git a/tests/cc/testutil/toolchains/cc_toolchain_config.bzl b/tests/cc/testutil/toolchains/cc_toolchain_config.bzl index a9233474b..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 = [] @@ -2167,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, From 599653fd53e54daa13d800745bcb7fc8851a41b8 Mon Sep 17 00:00:00 2001 From: Googler CA Date: Thu, 29 Oct 2026 06:57:31 -0700 Subject: [PATCH 44/61] Avoid redundant `linker_inputs` depset flattening during `cc_binary` runfiles collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_collect_runfiles` previously flattened the transitive `linker_inputs` depset of every direct `deps` target twice—returning an empty list when linking statically without LCOV coverage—while `ctx.runfiles(collect_default = True)` already merges `default_runfiles` from `deps`, `data`, and C++ runtimes. Skipping static `deps` `linker_inputs` traversal when coverage is disabled, deduplicating `data` dynamic library collection, and keeping runtime library artifacts as depsets avoids quadratic `direct_deps * transitive_deps` Starlark iteration and list allocations during `cc_binary` analysis. PiperOrigin-RevId: 1009656955 Change-Id: I107c0e3d1bf25e9186f84dcf8d3363a77513f044 --- cc/common/cc_helper.bzl | 24 +++------ cc/private/rules_impl/cc_binary_impl.bzl | 62 ++++++++++++------------ 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index d77c5a6dd..7aca7809c 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -257,27 +257,17 @@ def _create_dynamic_libraries_copy_actions(ctx, binary, dynamic_libraries_for_ru 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(): - libraries.extend(linker_input.libraries) - dynamic_libraries_for_runtime = [] - for library in libraries: - artifact = _get_dynamic_library_for_runtime_or_none(library, linking_statically) - if artifact != None: - dynamic_libraries_for_runtime.append(artifact) + for linker_input in cc_linking_context.linker_inputs.to_list(): + for library in linker_input.libraries: + if library.dynamic_library == None: + continue + if linking_statically and (library.static_library != None or library.pic_static_library != None): + continue + dynamic_libraries_for_runtime.append(library.dynamic_library) return dynamic_libraries_for_runtime -def _get_dynamic_library_for_runtime_or_none(library, linking_statically): - if library.dynamic_library == None: - return None - - if linking_statically and (library.static_library != None or library.pic_static_library != None): - return None - - return library.dynamic_library - def _collect_native_cc_libraries(deps, libraries): _CcNativeLibraryInfo = cc_common.get_cc_native_library_info_provider() transitive_libraries = [ diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index 7f08ed98b..bb5c43a9d 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -129,68 +129,65 @@ def _add_transitive_info_providers(ctx, cc_toolchain, cpp_config, feature_config output_groups["_validation"] = compilation_context.validation_artifacts return (cc_info, instrumented_files_provider, output_groups) -def _collect_runfiles(ctx, feature_configuration, cc_toolchain, linking_context, libraries, cc_library_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately): +def _collect_runfiles(ctx, feature_configuration, cc_toolchain, cpp_config, linking_context, libraries, cc_library_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately): # TODO(b/198254254): Add Legacyexternalrunfiles if necessary. + collect_coverage_objects = cpp_config.generate_llvm_lcov() runtime_objects_for_coverage = [] builder_artifacts = [] - builder_transitive_artifacts = [] + builder_transitive_artifacts = [transitive_artifacts] - runtime_libraries = _collect_runtime_libraries(ctx, linking_context, linking_mode != linker_mode.LINKING_DYNAMIC) - builder = ctx.runfiles(transitive_files = runtime_libraries, collect_default = True) + linking_statically = linking_mode != linker_mode.LINKING_DYNAMIC + if not linking_statically or collect_coverage_objects: + runtime_libraries = _collect_runtime_libraries(ctx, linking_context, linking_statically) + builder_transitive_artifacts.append(runtime_libraries) + if collect_coverage_objects: + runtime_objects_for_coverage.extend(runtime_libraries.to_list()) + builder = ctx.runfiles(collect_default = True) - runtime_objects_for_coverage.extend(runtime_libraries.to_list()) dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime(True, libraries) - runtime_objects_for_coverage.extend(dynamic_libraries_for_runtime) + if collect_coverage_objects: + runtime_objects_for_coverage.extend(dynamic_libraries_for_runtime) - builder_transitive_artifacts.extend(transitive_artifacts.to_list()) builder_artifacts.extend(dynamic_libraries_for_runtime) - runfiles_is_static = [] - runfiles_is_not_static = [] + runfiles_data = [] for transitive_info_collection in ctx.attr.data: - runfiles_is_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, True))) - runfiles_is_not_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, False))) - runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, True).to_list()) - runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, False).to_list()) + data_dynamic_libs = _runfiles_function(transitive_info_collection, False) + runfiles_data.append(ctx.runfiles(transitive_files = data_dynamic_libs)) + if collect_coverage_objects: + runtime_objects_for_coverage.extend(data_dynamic_libs.to_list()) for dynamic_dep in ctx.attr.dynamic_deps: builder = builder.merge(dynamic_dep[DefaultInfo].default_runfiles) - builder = builder.merge_all(runfiles_is_static + runfiles_is_not_static) + builder = builder.merge_all(runfiles_data) if linking_mode == linker_mode.LINKING_DYNAMIC: dynamic_runtime_lib = cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration) - dynamic_runtime_lib_list = dynamic_runtime_lib.to_list() - builder_transitive_artifacts.extend(dynamic_runtime_lib_list) - runtime_objects_for_coverage.extend(dynamic_runtime_lib_list) + builder_transitive_artifacts.append(dynamic_runtime_lib) + if collect_coverage_objects: + runtime_objects_for_coverage.extend(dynamic_runtime_lib.to_list()) if link_compile_output_separately: if cc_library_linking_outputs != None and cc_library_linking_outputs.library_to_link != None and cc_library_linking_outputs.library_to_link.dynamic_library != None: builder_artifacts.append(cc_library_linking_outputs.library_to_link.dynamic_library) - runtime_objects_for_coverage.append(cc_library_linking_outputs.library_to_link.dynamic_library) + if collect_coverage_objects: + runtime_objects_for_coverage.append(cc_library_linking_outputs.library_to_link.dynamic_library) builder = builder.merge_all([ _default_runfiles_function(ctx, runtime) for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx)) - ] + [ - ctx.runfiles(transitive_files = _runfiles_function(runtime, linking_mode != linker_mode.LINKING_DYNAMIC)) - for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx)) ]) - return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(builder_transitive_artifacts))), runtime_objects_for_coverage) - -def _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically): - if library_to_link.dynamic_library == None: - return None - if link_statically and (library_to_link.static_library != None or library_to_link.pic_static_library != None): - return None - return library_to_link.dynamic_library + return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(transitive = builder_transitive_artifacts))), runtime_objects_for_coverage) def _get_dynamic_libraries_for_runtime(link_statically, libraries): dynamic_libraries_for_runtime = [] for library_to_link in libraries: - artifact = _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically) - if artifact != None: - dynamic_libraries_for_runtime.append(artifact) + if library_to_link.dynamic_library == None: + continue + if link_statically and (library_to_link.static_library != None or library_to_link.pic_static_library != None): + continue + dynamic_libraries_for_runtime.append(library_to_link.dynamic_library) return dynamic_libraries_for_runtime def _get_providers(ctx): @@ -740,6 +737,7 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): ctx, feature_configuration, cc_toolchain, + cpp_config, deps_cc_linking_context, libraries, cc_linking_outputs, From 0726c5d09cc89181c4a33ec675e7dbaef004bd25 Mon Sep 17 00:00:00 2001 From: jke-jumptrading <163859533+jke-jumptrading@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:28:49 -0700 Subject: [PATCH 45/61] Fix cc_static_library opt mode duplicate archive symbols (#930) From the bazel documentation: > The resulting static library contains the object files of the targets listed in deps as well as their transitive dependencies, with preference given to PIC objects. This is currently not true as cc_static_archive will archive both the -fPIC and non -fPIC artifacts, leading to duplicate symbol errors. This proposes a fix to match the specification again. Closes #930 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/930 from jke-jumptrading:fix-static-lib-pic-dedup eeb58e0e425a23828b97cffd0603e17b4d69cafd PiperOrigin-RevId: 1009967005 Change-Id: I24cba3a97048f0f25d34156180574d1d02ecdf2d --- cc/private/rules_impl/cc_static_library.bzl | 4 +- .../cc/cc_static_library/test/BUILD | 8 +++- .../cc_static_library/test/starlark_tests.bzl | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) 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/tests/builtins_bzl/cc/cc_static_library/test/BUILD b/tests/builtins_bzl/cc/cc_static_library/test/BUILD index 4da9022e6..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( 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 ], ) From bfb93210a6c4749405f8bcea20d909ce2c49b1f2 Mon Sep 17 00:00:00 2001 From: Luke Zarko Date: Thu, 29 Oct 2026 19:48:20 -0700 Subject: [PATCH 46/61] No public description PiperOrigin-RevId: 1010161975 Change-Id: I8cbbc519fa3a1b4bd30dd4c60af37170e58b5813 --- cc/private/cc_common.bzl | 21 ++++++++++++------- .../compile/compile_build_variables.bzl | 9 +++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cc/private/cc_common.bzl b/cc/private/cc_common.bzl index 4533f7fda..aa06fdd1c 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") @@ -637,19 +638,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/compile/compile_build_variables.bzl b/cc/private/compile/compile_build_variables.bzl index f8e7b98bf..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 = {} From 2695f06af94f3838c28e41f224dae07b18421496 Mon Sep 17 00:00:00 2001 From: Googler Date: Thu, 29 Oct 2026 21:37:30 -0700 Subject: [PATCH 47/61] Automated rollback of commit 599653fd53e54daa13d800745bcb7fc8851a41b8. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *** Reason for rollback *** Breaks Google Beam builds, for example: ``` dev_config_cli: error while loading shared libraries: libsystemd.so: cannot open shared object file: No such file or directory ``` *** Original change description *** Avoid redundant `linker_inputs` depset flattening during `cc_binary` runfiles collection `_collect_runfiles` previously flattened the transitive `linker_inputs` depset of every direct `deps` target twice—returning an empty list when linking statically without LCOV coverage—while `ctx.runfiles(collect_default = True)` already merges `default_runfiles` from `deps`, `data`, and C++ runtimes. Skipping static `deps` `linker_inputs` traversal when coverage is disabled, deduplicating `data` dynami... *** Startblock: * // Put other blockers before this line to avoid churning. * has lgtm * is approved * and then * all comments are resolved * and then *[] allows unknown commit PiperOrigin-RevId: 1010209277 Change-Id: I24eaf238ba592d1ec2b9541f6f4e3934dabbe56b --- cc/common/cc_helper.bzl | 24 ++++++--- cc/private/rules_impl/cc_binary_impl.bzl | 62 ++++++++++++------------ 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index 7aca7809c..d77c5a6dd 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -257,17 +257,27 @@ def _create_dynamic_libraries_copy_actions(ctx, binary, dynamic_libraries_for_ru return depset(result) def _get_dynamic_libraries_for_runtime(cc_linking_context, linking_statically): - dynamic_libraries_for_runtime = [] + libraries = [] for linker_input in cc_linking_context.linker_inputs.to_list(): - for library in linker_input.libraries: - if library.dynamic_library == None: - continue - if linking_statically and (library.static_library != None or library.pic_static_library != None): - continue - dynamic_libraries_for_runtime.append(library.dynamic_library) + libraries.extend(linker_input.libraries) + + dynamic_libraries_for_runtime = [] + for library in libraries: + artifact = _get_dynamic_library_for_runtime_or_none(library, linking_statically) + if artifact != None: + dynamic_libraries_for_runtime.append(artifact) return dynamic_libraries_for_runtime +def _get_dynamic_library_for_runtime_or_none(library, linking_statically): + if library.dynamic_library == None: + return None + + if linking_statically and (library.static_library != None or library.pic_static_library != None): + return None + + return library.dynamic_library + def _collect_native_cc_libraries(deps, libraries): _CcNativeLibraryInfo = cc_common.get_cc_native_library_info_provider() transitive_libraries = [ diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index bb5c43a9d..7f08ed98b 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -129,65 +129,68 @@ def _add_transitive_info_providers(ctx, cc_toolchain, cpp_config, feature_config output_groups["_validation"] = compilation_context.validation_artifacts return (cc_info, instrumented_files_provider, output_groups) -def _collect_runfiles(ctx, feature_configuration, cc_toolchain, cpp_config, linking_context, libraries, cc_library_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately): +def _collect_runfiles(ctx, feature_configuration, cc_toolchain, linking_context, libraries, cc_library_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately): # TODO(b/198254254): Add Legacyexternalrunfiles if necessary. - collect_coverage_objects = cpp_config.generate_llvm_lcov() runtime_objects_for_coverage = [] builder_artifacts = [] - builder_transitive_artifacts = [transitive_artifacts] + builder_transitive_artifacts = [] - linking_statically = linking_mode != linker_mode.LINKING_DYNAMIC - if not linking_statically or collect_coverage_objects: - runtime_libraries = _collect_runtime_libraries(ctx, linking_context, linking_statically) - builder_transitive_artifacts.append(runtime_libraries) - if collect_coverage_objects: - runtime_objects_for_coverage.extend(runtime_libraries.to_list()) - builder = ctx.runfiles(collect_default = True) + runtime_libraries = _collect_runtime_libraries(ctx, linking_context, linking_mode != linker_mode.LINKING_DYNAMIC) + builder = ctx.runfiles(transitive_files = runtime_libraries, collect_default = True) + runtime_objects_for_coverage.extend(runtime_libraries.to_list()) dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime(True, libraries) - if collect_coverage_objects: - runtime_objects_for_coverage.extend(dynamic_libraries_for_runtime) + runtime_objects_for_coverage.extend(dynamic_libraries_for_runtime) + builder_transitive_artifacts.extend(transitive_artifacts.to_list()) builder_artifacts.extend(dynamic_libraries_for_runtime) - runfiles_data = [] + runfiles_is_static = [] + runfiles_is_not_static = [] for transitive_info_collection in ctx.attr.data: - data_dynamic_libs = _runfiles_function(transitive_info_collection, False) - runfiles_data.append(ctx.runfiles(transitive_files = data_dynamic_libs)) - if collect_coverage_objects: - runtime_objects_for_coverage.extend(data_dynamic_libs.to_list()) + runfiles_is_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, True))) + runfiles_is_not_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, False))) + runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, True).to_list()) + runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, False).to_list()) for dynamic_dep in ctx.attr.dynamic_deps: builder = builder.merge(dynamic_dep[DefaultInfo].default_runfiles) - builder = builder.merge_all(runfiles_data) + builder = builder.merge_all(runfiles_is_static + runfiles_is_not_static) if linking_mode == linker_mode.LINKING_DYNAMIC: dynamic_runtime_lib = cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration) - builder_transitive_artifacts.append(dynamic_runtime_lib) - if collect_coverage_objects: - runtime_objects_for_coverage.extend(dynamic_runtime_lib.to_list()) + dynamic_runtime_lib_list = dynamic_runtime_lib.to_list() + builder_transitive_artifacts.extend(dynamic_runtime_lib_list) + runtime_objects_for_coverage.extend(dynamic_runtime_lib_list) if link_compile_output_separately: if cc_library_linking_outputs != None and cc_library_linking_outputs.library_to_link != None and cc_library_linking_outputs.library_to_link.dynamic_library != None: builder_artifacts.append(cc_library_linking_outputs.library_to_link.dynamic_library) - if collect_coverage_objects: - runtime_objects_for_coverage.append(cc_library_linking_outputs.library_to_link.dynamic_library) + runtime_objects_for_coverage.append(cc_library_linking_outputs.library_to_link.dynamic_library) builder = builder.merge_all([ _default_runfiles_function(ctx, runtime) for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx)) + ] + [ + ctx.runfiles(transitive_files = _runfiles_function(runtime, linking_mode != linker_mode.LINKING_DYNAMIC)) + for runtime in cc_helper.get_cc_runtimes(ctx, _is_link_shared(ctx)) ]) - return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(transitive = builder_transitive_artifacts))), runtime_objects_for_coverage) + return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(builder_transitive_artifacts))), runtime_objects_for_coverage) + +def _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically): + if library_to_link.dynamic_library == None: + return None + if link_statically and (library_to_link.static_library != None or library_to_link.pic_static_library != None): + return None + return library_to_link.dynamic_library def _get_dynamic_libraries_for_runtime(link_statically, libraries): dynamic_libraries_for_runtime = [] for library_to_link in libraries: - if library_to_link.dynamic_library == None: - continue - if link_statically and (library_to_link.static_library != None or library_to_link.pic_static_library != None): - continue - dynamic_libraries_for_runtime.append(library_to_link.dynamic_library) + artifact = _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically) + if artifact != None: + dynamic_libraries_for_runtime.append(artifact) return dynamic_libraries_for_runtime def _get_providers(ctx): @@ -737,7 +740,6 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): ctx, feature_configuration, cc_toolchain, - cpp_config, deps_cc_linking_context, libraries, cc_linking_outputs, From ef8ed36b2cdb97aba7526aedf124a66e656440d2 Mon Sep 17 00:00:00 2001 From: Synchronization Acknowledgement <77421532+sin-ack@users.noreply.github.com> Date: Thu, 29 Oct 2026 22:53:46 -0700 Subject: [PATCH 48/61] Minor fixes for documentation x-refs (#938) See individual commits for details. Closes #938 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/938 from sin-ack:linkopts-docs-fix c414826849d11351d2893462cb560b249df93cfc PiperOrigin-RevId: 1010244192 Change-Id: I218ccf5d06eb27572777533609e89b18d76c06c3 --- cc/private/rules_impl/attrs.bzl | 4 ++-- cc/private/rules_impl/cc_library.bzl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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.
  • @@ -311,7 +311,7 @@ Whether to encode build information into the binary. Possible values:
  • 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_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 @@ the malloc 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.

From fc6b090285458e8564a32d5bdce999d9e4a28790 Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Sat, 31 Oct 2026 21:59:26 -0700 Subject: [PATCH 49/61] Validate extra link-time callbacks with Starlark Args (#827) Validate extra link-time library callbacks with `ctx.actions.args().add_all([], map_each = callback)` immediately before invocation. `Args.add_all` performs the same top-level-function check as `_cc_internal.check_toplevel`, so the C++ Java helper is no longer needed. Validation: 88 C++ common and binary analysis tests passed, including new tests for top-level and nested callbacks. Closes #827 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/827 from dzbarsky:codex/starlarkify-check-toplevel 73c8487ad9c4432e6558d78e4c97db5ef95140e9 PiperOrigin-RevId: 1011439742 Change-Id: Id543c4e4dd18385b288e1129be95641b70d2560c --- .../link/create_extra_link_time_library.bzl | 19 ++- tests/cc/common/cc_common_test.bzl | 132 ++++++++++++++++++ 2 files changed, 147 insertions(+), 4 deletions(-) diff --git a/cc/private/link/create_extra_link_time_library.bzl b/cc/private/link/create_extra_link_time_library.bzl index 27a3d05db..344a83fe8 100644 --- a/cc/private/link/create_extra_link_time_library.bzl +++ b/cc/private/link/create_extra_link_time_library.bzl @@ -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/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, From f042218fdb682a7361dc1f868dc350508e2b965e Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 2 Nov 2026 02:05:12 -0800 Subject: [PATCH 50/61] Use --ld-path= for the autoconfigured Clang linker (#935) When Clang is the compiler, the autoconfigured toolchain passes the linker's absolute path through `-fuse-ld=`, e.g. `-fuse-ld=/usr/bin/ld.lld`. Clang 12 deprecated `-fuse-ld=` in favor of `--ld-path=` (https://reviews.llvm.org/D83015) because the `-fuse-ld=` value is the linker flavor, which a path loses. For example, on Darwin `-fuse-ld=/path/to/ld64.lld` makes the driver pass ld64's version-dependent options to lld. Pass `-fuse-ld=lld --ld-path=/usr/bin/ld.lld` (or the gold equivalent) when Clang accepts `--ld-path=`, and fall back to `-fuse-ld=` otherwise. Fixes #934 Closes #935 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/935 from MaskRay:ld-path 4245e780980665cd3aa2d699bad594ae3e3b6f50 PiperOrigin-RevId: 1012001045 Change-Id: I59ab45762d04dd9be202c2515135b60d21519ef4 --- cc/private/toolchain/unix_cc_configure.bzl | 35 +++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index 4297f37c1..50918132d 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -216,7 +216,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_flags(repository_ctx, cc, linker, is_clang): """Checks if a given linker is supported by the C compiler. Args: @@ -226,7 +226,7 @@ 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. + List of flags selecting the linker, or None if linker couldn't be found. """ result = repository_ctx.execute([ cc, @@ -245,7 +245,7 @@ def _find_linker_path(repository_ctx, cc, linker, is_clang): return None if not is_clang: - return linker + return ["-fuse-ld=" + linker] # Extract linker path from: # /usr/bin/clang ... @@ -254,9 +254,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 ["-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 ["-fuse-ld=" + linker_path] + return flags def _add_compiler_option_if_supported(repository_ctx, cc, option): """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option.""" @@ -548,9 +561,9 @@ 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) + gold_or_lld_linker_flags = ( + _find_linker_flags(repository_ctx, cc, "lld", is_clang) or + _find_linker_flags(repository_ctx, cc, "gold", is_clang) ) cc_path = repository_ctx.path(cc) if not str(cc_path).startswith(str(repository_ctx.path(".")) + "/"): @@ -559,7 +572,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): else: # cc is inside the repository, don't set -B. bin_search_flags = [] - if not gold_or_lld_linker_path: + if not gold_or_lld_linker_flags: ld_path = repository_ctx.path(tool_paths["ld"]) if ld_path.dirname != cc_path.dirname: bin_search_flags.append("-B" + escape_string(str(ld_path.dirname))) @@ -570,8 +583,8 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): if ld_path.dirname != cc_path.dirname: bin_search_flags.append("-B" + escape_string(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 gold_or_lld_linker_flags: + force_linker_flags.extend(gold_or_lld_linker_flags) # TODO: It's unclear why these flags aren't added on macOS. if bin_search_flags and not darwin: From c7c3941624839d5456ca775d2283dd3d10faddb5 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 10:03:32 -0800 Subject: [PATCH 51/61] Return the Unix linker path and compiler flags (#971) Since f042218fd, `_find_linker_flags` returns compiler flags, but `configure_unix_toolchain` still reads `gold_or_lld_linker_path` to find the linker directory. Bazel rejects the undefined name when it loads `@local_config_cc`. Return the linker name or path alongside its flags. Use the name to find the directory for `-B` and pass the flags to the compiler. Prepared by Codex on behalf of @tamird. Closes #971 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/971 from tamird:fix-autoconfigured-linker-path 80a7dadff32e6769116badabe312dba19dd9e2fb PiperOrigin-RevId: 1013101108 Change-Id: I0a2874f0aaef98a58cc9c4da24df72e0dbcf1cb4 --- cc/private/toolchain/unix_cc_configure.bzl | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index 50918132d..a1fbd9607 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -216,7 +216,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_flags(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 +226,8 @@ def _find_linker_flags(repository_ctx, cc, linker, is_clang): is_clang: whether the compiler is known to be clang Returns: - List of flags selecting the linker, 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 +246,7 @@ def _find_linker_flags(repository_ctx, cc, linker, is_clang): return None if not is_clang: - return ["-fuse-ld=" + linker] + return struct(path = linker, flags = ["-fuse-ld=" + linker]) # Extract linker path from: # /usr/bin/clang ... @@ -254,7 +255,7 @@ def _find_linker_flags(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 ["-fuse-ld=" + linker] + return struct(path = linker, flags = ["-fuse-ld=" + linker]) linker_command = invocations[-1] linker_path = linker_command.strip().split(" ")[0].strip("\"'") @@ -268,8 +269,8 @@ def _find_linker_flags(repository_ctx, cc, linker, is_clang): "/dev/null", ] + flags) if result.return_code != 0: - return ["-fuse-ld=" + linker_path] - return flags + 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.""" @@ -561,9 +562,9 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): False, ), ":") - gold_or_lld_linker_flags = ( - _find_linker_flags(repository_ctx, cc, "lld", is_clang) or - _find_linker_flags(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(".")) + "/"): @@ -572,19 +573,19 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): else: # cc is inside the repository, don't set -B. bin_search_flags = [] - if not gold_or_lld_linker_flags: + if not linker: ld_path = repository_ctx.path(tool_paths["ld"]) if ld_path.dirname != cc_path.dirname: bin_search_flags.append("-B" + escape_string(str(ld_path.dirname))) - elif not gold_or_lld_linker_path.startswith("/"): - ld_binary = repository_ctx.which("ld." + gold_or_lld_linker_path) or repository_ctx.which(gold_or_lld_linker_path) + 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_string(str(ld_path.dirname))) force_linker_flags = [] - if gold_or_lld_linker_flags: - force_linker_flags.extend(gold_or_lld_linker_flags) + 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: From f3c588271884d3904c21349705e896031f459528 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 3 Nov 2026 10:26:25 -0800 Subject: [PATCH 52/61] Don't resolve cc_configure template labels to paths (#944) `cc_configure` resolved all of its labels with `repository_ctx.path` up front (`resolve_labels`) to avoid expensive restarts of the repo rule. Since Bazel 7.1, repo rules run on worker threads by default (`--experimental_worker_for_repo_fetching=auto`), so missing dependencies no longer restart them. Resolving a label to a path makes the repo containing the file available on disk. With Bazel's `--experimental_remote_repo_contents_cache`, `rules_cc` is otherwise only kept in memory, so this downloads all of its files before the C++ toolchain can be configured (bazelbuild/bazel#31005). In Bazel's own CI with the cache enabled (bazelbuild/bazel#31755), this took 0.8 s per job on Linux and 2.1 s on Windows arm64, on the critical path of every build. This change: * passes templates to `repository_ctx.template` by label, which only reads the template itself with bazelbuild/bazel#31493, * copies the toolchain config `.bzl` files (and the BSD `BUILD` file and the MSYS error script) into the generated repo instead of symlinking them, so that they keep their names (e.g. `@local_config_cc//:cc_toolchain_config.bzl`, which is loaded by other projects) without requiring `rules_cc` on disk, * copies `generate_system_module_map.sh` into the generated repo before running it and deletes it afterwards. `resolve_labels` is no longer used, but is kept since `lib_cc_configure.bzl` is loaded by other projects. Closes #944 COPYBARA_INTEGRATE_REVIEW=https://github.com/bazelbuild/rules_cc/pull/944 from fmeum:cc-configure-no-eager-paths 2a44513f53d1676713b25b5c0c06da1444665517 PiperOrigin-RevId: 1013118324 Change-Id: I340540da6e7404ecbb404058fffca9a65caf59a5 --- cc/private/toolchain/cc_configure.bzl | 31 ++------- cc/private/toolchain/lib_cc_configure.bzl | 25 ++++--- cc/private/toolchain/unix_cc_configure.bzl | 42 +++++------- cc/private/toolchain/windows_cc_configure.bzl | 65 +++++++++---------- 4 files changed, 69 insertions(+), 94 deletions(-) diff --git a/cc/private/toolchain/cc_configure.bzl b/cc/private/toolchain/cc_configure.bzl index c2bf06aaa..1d254af8b 100644 --- a/cc/private/toolchain/cc_configure.bzl +++ b/cc/private/toolchain/cc_configure.bzl @@ -14,11 +14,7 @@ """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") @@ -52,13 +48,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: @@ -89,28 +82,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). diff --git a/cc/private/toolchain/lib_cc_configure.bzl b/cc/private/toolchain/lib_cc_configure.bzl index 464700387..32a22ac9b 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,6 +29,19 @@ 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): + """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_string(arg): """Escape percent sign (%) in the string so it can appear in the Crosstool.""" if arg != None: diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index a1fbd9607..ce1282e7b 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -19,11 +19,11 @@ load( "auto_configure_fail", "auto_configure_warning", "auto_configure_warning_maybe", + "copy_file", "escape_string", "execute", "get_env_var", "get_starlark_list", - "resolve_labels", "split_escaped", "which", "write_builtin_include_directory_paths", @@ -35,8 +35,13 @@ def _uniq(iterable): unique_elements = {element: None for element in iterable} return unique_elements.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): + # 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. @@ -396,25 +401,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", ) @@ -501,7 +496,7 @@ 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"]))), # Certain weak symbols are otherwise listed with type T in the output of nm on macOS. @@ -516,7 +511,7 @@ 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)), @@ -534,7 +529,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): deps_scanner = which(repository_ctx, "clang-scan-deps", 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), @@ -682,7 +677,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: @@ -704,7 +698,7 @@ 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( diff --git a/cc/private/toolchain/windows_cc_configure.bzl b/cc/private/toolchain/windows_cc_configure.bzl index 235ebbf76..790d41c1f 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", + "copy_file", "escape_string", "execute", "get_starlark_list", - "resolve_labels", "split_escaped", "write_builtin_include_directory_paths", ) @@ -665,7 +665,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 +673,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 +689,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}, ) @@ -788,7 +788,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 +797,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 +813,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 +865,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 + "}"], }, @@ -923,26 +923,19 @@ def configure_windows_toolchain(repository_ctx): 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 +947,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, ) From 818408bd9179ccb7c3f4d4b8e9d246ff36ddcb3d Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 29 Oct 2026 12:55:40 -0400 Subject: [PATCH 53/61] Describe C++ toolchain helpers and providers Give toolchain configuration, provider collectors and rule helpers typed inputs and results. Keep simple signatures in their implementations; represent provider fields and generic functions in companion interfaces. --- cc/cc_import.bzl | 1 + cc/cc_postmark.bzl | 8 +- cc/cc_postmark_initializers.bzli | 4 + cc/cc_shared_library.bzl | 1 + cc/cc_toolchain_config_lib.bzl | 62 ++-- cc/cc_toolchain_config_lib.bzli | 201 ++++++++++++ cc/extensions.bzl | 8 +- cc/private/cc_common.bzl | 4 +- cc/private/cc_info.bzl | 2 +- cc/private/objc_info.bzl | 2 + cc/private/objc_info.bzli | 19 ++ cc/private/paths.bzl | 1 + cc/private/rules_impl/cc_binary_impl.bzl | 4 +- .../rules_impl/failing_cc_proto_library.bzl | 1 + .../rules_impl/fdo/fdo_prefetch_hints.bzl | 1 + cc/private/rules_impl/fdo/fdo_profile.bzl | 1 + cc/private/rules_impl/fdo/memprof_profile.bzl | 1 + .../rules_impl/fdo/propeller_optimize.bzl | 1 + cc/private/rules_impl/native_cc_common.bzl | 4 +- .../objc_compilation_artifacts_info.bzl | 5 +- .../objc_compilation_artifacts_info.bzli | 47 +++ .../objc_compilation_context_info.bzli | 35 +++ cc/private/toolchain/cc_configure.bzl | 6 + cc/private/toolchain/lib_cc_configure.bzl | 28 +- cc/private/toolchain/lib_cc_configure.bzli | 44 +++ cc/private/toolchain/unix_cc_configure.bzl | 172 +++++----- cc/private/toolchain/unix_cc_configure.bzli | 84 +++++ cc/private/toolchain/windows_cc_configure.bzl | 85 +++-- .../toolchain_config/legacy_features.bzl | 6 +- .../toolchain_config/legacy_features.bzli | 39 +++ cc/repositories.bzl | 6 +- cc/starlark_doc_extract_helper.bzl | 1 + cc/system_library.bzl | 11 +- cc/system_library.bzli | 27 ++ cc/toolchains/actions.bzl | 2 + cc/toolchains/args.bzl | 3 +- .../private/library_link_args.bzl | 2 + cc/toolchains/args_list.bzl | 1 + cc/toolchains/artifacts.bzl | 1 + cc/toolchains/cc_toolchain_info.bzli | 294 ++++++++++++++++++ cc/toolchains/directory_tool.bzli | 17 + cc/toolchains/feature.bzl | 1 + cc/toolchains/feature_constraint.bzl | 1 + cc/toolchains/feature_set.bzl | 1 + cc/toolchains/impl/cc_artifact_category.bzl | 1 + cc/toolchains/impl/collect.bzl | 29 +- cc/toolchains/impl/collect.bzli | 11 + cc/toolchains/impl/external_feature.bzl | 3 + cc/toolchains/impl/label_utils.bzl | 1 + cc/toolchains/impl/legacy_converter.bzl | 8 +- cc/toolchains/impl/legacy_converter.bzli | 25 ++ cc/toolchains/impl/libc_version.bzl | 1 + cc/toolchains/impl/markdown_helpers.bzl | 2 + cc/toolchains/impl/nested_args.bzl | 22 +- cc/toolchains/impl/nested_args.bzli | 45 +++ cc/toolchains/impl/variables.bzl | 24 +- cc/toolchains/impl/variables.bzli | 49 +++ cc/toolchains/legacy_file_group.bzli | 12 + cc/toolchains/legacy_tool.bzl | 1 + cc/toolchains/make_variable.bzl | 1 + cc/toolchains/mutually_exclusive_category.bzl | 1 + cc/toolchains/nested_args.bzli | 23 ++ cc/toolchains/tool.bzl | 9 +- cc/toolchains/tool.bzli | 34 ++ cc/toolchains/tool_capability.bzl | 3 + cc/toolchains/tool_map.bzl | 4 +- cc/toolchains/tool_map.bzli | 12 + cc/use_cc_toolchain.bzl | 1 + local_bazel.bzl | 3 + .../toolchains/additional_toolchains.bzl | 1 + .../toolchains/additional_toolchains.bzli | 3 + .../action_type_set_diff_test.bzl | 1 + .../action_type_set_diff_test.bzli | 5 + .../args/make_var_provider.bzl | 1 + .../features/features_test.bzl | 2 + tests/rule_based_toolchain/helpers.bzl | 1 + tests/rule_based_toolchain/nested_args/BUILD | 8 + .../nested_args/nested_args_test.bzl | 8 + .../tool/execution_requirements.bzl | 1 + .../tool/execution_requirements.bzli | 3 + tests/rule_based_toolchain/tool/tool_test.bzl | 9 + .../variables/variables_test.bzl | 1 + 82 files changed, 1393 insertions(+), 221 deletions(-) create mode 100644 cc/cc_postmark_initializers.bzli create mode 100644 cc/cc_toolchain_config_lib.bzli create mode 100644 cc/private/objc_info.bzli create mode 100644 cc/private/rules_impl/objc_compilation_artifacts_info.bzli create mode 100644 cc/private/rules_impl/objc_compilation_context_info.bzli create mode 100644 cc/private/toolchain/lib_cc_configure.bzli create mode 100644 cc/private/toolchain/unix_cc_configure.bzli create mode 100644 cc/private/toolchain_config/legacy_features.bzli create mode 100644 cc/system_library.bzli create mode 100644 cc/toolchains/cc_toolchain_info.bzli create mode 100644 cc/toolchains/directory_tool.bzli create mode 100644 cc/toolchains/impl/collect.bzli create mode 100644 cc/toolchains/impl/legacy_converter.bzli create mode 100644 cc/toolchains/impl/nested_args.bzli create mode 100644 cc/toolchains/impl/variables.bzli create mode 100644 cc/toolchains/legacy_file_group.bzli create mode 100644 cc/toolchains/nested_args.bzli create mode 100644 cc/toolchains/tool.bzli create mode 100644 cc/toolchains/tool_map.bzli create mode 100644 tests/cc/testutil/toolchains/additional_toolchains.bzli create mode 100644 tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli create mode 100644 tests/rule_based_toolchain/tool/execution_requirements.bzli diff --git a/cc/cc_import.bzl b/cc/cc_import.bzl index 176243646..2292ef1f2 100644 --- a/cc/cc_import.bzl +++ b/cc/cc_import.bzl @@ -16,4 +16,5 @@ load("@cc_compatibility_proxy//:proxy.bzl", _cc_import = "cc_import") 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..28c225706 100644 --- a/cc/cc_postmark.bzl +++ b/cc/cc_postmark.bzl @@ -14,20 +14,18 @@ """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 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..fa7ae1b89 --- /dev/null +++ b/cc/cc_postmark_initializers.bzli @@ -0,0 +1,4 @@ +"""Contracts for the PostMark initializer compatibility layer.""" + +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..d66fea01e 100644 --- a/cc/cc_shared_library.bzl +++ b/cc/cc_shared_library.bzl @@ -16,4 +16,5 @@ load("@cc_compatibility_proxy//:proxy.bzl", _cc_shared_library = "cc_shared_library") 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 798a19ff0..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,6 +457,7 @@ ToolPathInfo = provider( ) def tool_path(name, path): + # type: (str, str) -> ToolPathInfo """ Tool locations. Args: @@ -475,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: @@ -507,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, @@ -536,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 @@ -566,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, @@ -589,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: @@ -617,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..62bcd0067 --- /dev/null +++ b/cc/cc_toolchain_config_lib.bzli @@ -0,0 +1,201 @@ +# 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. +""" + +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/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/cc_common.bzl b/cc/private/cc_common.bzl index aa06fdd1c..ea80a731a 100644 --- a/cc/private/cc_common.bzl +++ b/cc/private/cc_common.bzl @@ -252,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 = [], diff --git a/cc/private/cc_info.bzl b/cc/private/cc_info.bzl index 76eca9572..7d6a5999f 100644 --- a/cc/private/cc_info.bzl +++ b/cc/private/cc_info.bzl @@ -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/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..fb43100f4 --- /dev/null +++ b/cc/private/objc_info.bzli @@ -0,0 +1,19 @@ +"""Objective-C compilation and linking information.""" + +# 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/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index 7f08ed98b..152f5f6f0 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -815,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/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/toolchain/cc_configure.bzl b/cc/private/toolchain/cc_configure.bzl index 1d254af8b..fa28a5d81 100644 --- a/cc/private/toolchain/cc_configure.bzl +++ b/cc/private/toolchain/cc_configure.bzl @@ -19,6 +19,9 @@ 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": @@ -35,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: @@ -71,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: @@ -163,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 32a22ac9b..241adae99 100644 --- a/cc/private/toolchain/lib_cc_configure.bzl +++ b/cc/private/toolchain/lib_cc_configure.bzl @@ -30,6 +30,7 @@ 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 @@ -42,14 +43,21 @@ def copy_file(repository_ctx, src, dest): """ 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: @@ -80,7 +88,7 @@ def split_escaped(string, delimiter): i = 0 result = [] - accumulator = [] + accumulator = [] # type: list[str] length = len(string) delimiter_length = len(delimiter) @@ -111,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" @@ -142,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! @@ -175,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, @@ -228,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: @@ -267,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..82430005c --- /dev/null +++ b/cc/private/toolchain/lib_cc_configure.bzli @@ -0,0 +1,44 @@ +"""Type contracts for C++ toolchain configuration helpers.""" + +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 ce1282e7b..c093cdc73 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -17,10 +17,9 @@ load( ":lib_cc_configure.bzl", "auto_configure_fail", - "auto_configure_warning", "auto_configure_warning_maybe", "copy_file", - "escape_string", + "escape_percent", "execute", "get_env_var", "get_starlark_list", @@ -32,10 +31,10 @@ 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): + # 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")) @@ -83,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. @@ -120,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: @@ -159,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): @@ -307,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: @@ -325,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", @@ -365,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] @@ -386,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: @@ -421,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 = [] @@ -486,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", @@ -498,10 +483,10 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): "validate_static_library.sh", 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" @@ -513,8 +498,8 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): "cc_wrapper.sh", 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 = ( @@ -526,14 +511,14 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): if sibling_deps_scanner.exists: deps_scanner = str(sibling_deps_scanner) else: - deps_scanner = which(repository_ctx, "clang-scan-deps", deps_scanner) + deps_scanner = which(repository_ctx, "clang-scan-deps") or deps_scanner repository_ctx.template( "deps_scanner_wrapper.sh", 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)), }, ) @@ -564,10 +549,10 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): 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 = [] + bin_search_flags = [] # type: list[string] if not linker: ld_path = repository_ctx.path(tool_paths["ld"]) if ld_path.dirname != cc_path.dirname: @@ -577,7 +562,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): if ld_binary: ld_path = repository_ctx.path(ld_binary) if ld_path.dirname != cc_path.dirname: - bin_search_flags.append("-B" + escape_string(str(ld_path.dirname))) + bin_search_flags.append("-B" + escape_percent(str(ld_path.dirname))) force_linker_flags = [] if linker: force_linker_flags.extend(linker.flags) @@ -701,13 +686,13 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): 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", @@ -743,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), @@ -758,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", @@ -775,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, @@ -832,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..8243cb829 --- /dev/null +++ b/cc/private/toolchain/unix_cc_configure.bzli @@ -0,0 +1,84 @@ +"""Contracts for discovering and configuring a local Unix C++ toolchain.""" + +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/windows_cc_configure.bzl b/cc/private/toolchain/windows_cc_configure.bzl index 790d41c1f..f37e7f179 100644 --- a/cc/private/toolchain/windows_cc_configure.bzl +++ b/cc/private/toolchain/windows_cc_configure.bzl @@ -20,7 +20,7 @@ load( "auto_configure_warning", "auto_configure_warning_maybe", "copy_file", - "escape_string", + "escape_percent", "execute", "get_starlark_list", "split_escaped", @@ -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.""" @@ -715,8 +717,8 @@ def _get_msvc_vars(repository_ctx, target_arch = "x64", msvc_vars_x64 = None): 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, target_arch = "x64", msvc_vars_x64 = None): 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"], @@ -878,33 +880,17 @@ def _get_msvc_deps_scanner_vars(repository_ctx, template_vars, target_arch = "x6 "%{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,12 +898,13 @@ 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: 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..c7fed2788 --- /dev/null +++ b/cc/private/toolchain_config/legacy_features.bzli @@ -0,0 +1,39 @@ +# 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.""" + +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/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..55460b24d 100644 --- a/cc/starlark_doc_extract_helper.bzl +++ b/cc/starlark_doc_extract_helper.bzl @@ -3,6 +3,7 @@ visibility("//cc/...") 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..f0cdbee71 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, @@ -180,7 +189,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/system_library.bzli b/cc/system_library.bzli new file mode 100644 index 000000000..090f53d7a --- /dev/null +++ b/cc/system_library.bzli @@ -0,0 +1,27 @@ +# 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. +"""System library and header discovery.""" + +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: repository_ctx[_Attrs]) -> repo_metadata | None: ... 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/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/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, @@ -50,6 +51,7 @@ def apple_force_load_library_args(name, variable): ) def library_link_args(name, library_type, from_variable, iterate_over_variable = False): + # type: (str, str | select[str], str | Label, bool) -> None """A helper for declaring a library to link. For most platforms, this expands something akin to the following: diff --git a/cc/toolchains/args_list.bzl b/cc/toolchains/args_list.bzl index 0747acba7..654ea85b0 100644 --- a/cc/toolchains/args_list.bzl +++ b/cc/toolchains/args_list.bzl @@ -20,6 +20,7 @@ load( load(":cc_toolchain_info.bzl", "ArgsListInfo") def _cc_args_list_impl(ctx): + # type: (...) -> list[ArgsListInfo] return [collect_args_lists(ctx.attr.args, ctx.label)] cc_args_list = rule( diff --git a/cc/toolchains/artifacts.bzl b/cc/toolchains/artifacts.bzl index 20e6b84c1..af8d62134 100644 --- a/cc/toolchains/artifacts.bzl +++ b/cc/toolchains/artifacts.bzl @@ -19,6 +19,7 @@ load(":cc_toolchain_info.bzl", "ArtifactCategoryInfo", "ArtifactNamePatternInfo" visibility("public") def _cc_artifact_name_pattern_impl(ctx): + # type: (...) -> list[ArtifactNamePatternInfo] return [ ArtifactNamePatternInfo( label = ctx.label, diff --git a/cc/toolchains/cc_toolchain_info.bzli b/cc/toolchains/cc_toolchain_info.bzli new file mode 100644 index 000000000..fc8129b31 --- /dev/null +++ b/cc/toolchains/cc_toolchain_info.bzli @@ -0,0 +1,294 @@ +"""Provider values shared by rule-based toolchain implementations.""" + +load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo") +load("//cc:cc_toolchain_config_lib.bzli", "FlagGroupInfo") + +type TypeRequirement = struct[{"msg": str, "valid_types": list[str], "after_option_unwrap": bool}] +type ActionArgs = struct[{"action": ActionTypeInfo, "args": Sequence[ArgsInfo], "files": depset[File]}] +# TODO(https://github.com/bazelbuild/bazel/issues/31770): annotate the toolchain variable dictionaries +# in Starlark once Bazel supports dictionaries with named, typed keys. +type LeafVariableType = dict[{"name": Literal["unknown", "void", "string", "bool", "file", "directory"], "repr": str}] +type ElementVariableType = dict[{"name": Literal["option", "list"], "repr": str, "elements": VariableType}] +type StructVariableType = dict[{"name": Literal["struct"], "repr": str, "kv": dict[str, VariableType]}] +type VariableType = LeafVariableType | ElementVariableType | StructVariableType + +def ActionTypeInfo( + *, + label: Label, + name: str, +) -> record[{ + "label": Label, + "name": str, +}]: ... + +def ActionTypeSetInfo( + *, + label: Label, + actions: depset[ActionTypeInfo], +) -> record[{ + "label": Label, + "actions": depset[ActionTypeInfo], +}]: ... + +def ArtifactCategoryInfo( + *, + label: Label, + name: str, +) -> record[{ + "label": Label, + "name": str, +}]: ... + +def ArtifactNamePatternInfo( + *, + label: Label, + category: ArtifactCategoryInfo, + prefix: str, + extension: str, +) -> record[{ + "label": Label, + "category": ArtifactCategoryInfo, + "prefix": str, + "extension": str, +}]: ... + +def VariableInfo( + *, + name: str, + label: Label, + actions: depset[ActionTypeInfo] | None, + type: VariableType, +) -> record[{ + "name": str, + "label": Label, + "actions": depset[ActionTypeInfo] | None, + "type": VariableType, +}]: ... + +def BuiltinVariablesInfo( + *, + variables: dict[str, VariableInfo], +) -> record[{ + "variables": dict[str, VariableInfo], +}]: ... + +def NestedArgsInfo( + *, + label: Label, + nested: Sequence[NestedArgsInfo], + iterate_over: str | None, + files: depset[File], + requires_types: dict[str, list[TypeRequirement]], + legacy_flag_group: FlagGroupInfo, + unwrap_options: list[str], +) -> record[{ + "label": Label, + "nested": Sequence[NestedArgsInfo], + "iterate_over": str | None, + "files": depset[File], + "requires_types": dict[str, list[TypeRequirement]], + "legacy_flag_group": FlagGroupInfo, + "unwrap_options": list[str], +}]: ... + +def EnvInfo( + *, + label: Label, + entries: dict[str, str], + requires_not_none: str | None, +) -> record[{ + "label": Label, + "entries": dict[str, str], + "requires_not_none": str | None, +}]: ... + +def ArgsInfo( + *, + label: Label, + actions: depset[ActionTypeInfo], + requires_any_of: Sequence[FeatureConstraintInfo], + nested: NestedArgsInfo | None, + files: depset[File], + env: EnvInfo, + allowlist_include_directories: depset[DirectoryInfo], + allowlist_absolute_include_directories: depset[str], +) -> record[{ + "label": Label, + "actions": depset[ActionTypeInfo], + "requires_any_of": Sequence[FeatureConstraintInfo], + "nested": NestedArgsInfo | None, + "files": depset[File], + "env": EnvInfo, + "allowlist_include_directories": depset[DirectoryInfo], + "allowlist_absolute_include_directories": depset[str], +}]: ... + +def ArgsListInfo( + *, + label: Label, + args: Sequence[ArgsInfo], + files: depset[File], + by_action: Sequence[ActionArgs], + allowlist_include_directories: depset[DirectoryInfo], + allowlist_absolute_include_directories: depset[str], +) -> record[{ + "label": Label, + "args": Sequence[ArgsInfo], + "files": depset[File], + "by_action": Sequence[ActionArgs], + "allowlist_include_directories": depset[DirectoryInfo], + "allowlist_absolute_include_directories": depset[str], +}]: ... + +def FeatureInfo( + *, + label: Label, + name: str, + enabled: bool, + args: ArgsListInfo, + implies: depset[FeatureInfo], + requires_any_of: Sequence[FeatureSetInfo], + mutually_exclusive: Sequence[MutuallyExclusiveCategoryInfo], + external: bool, + overridable: bool, + overrides: FeatureInfo | None, + allowlist_include_directories: depset[DirectoryInfo], + allowlist_absolute_include_directories: depset[str], +) -> record[{ + "label": Label, + "name": str, + "enabled": bool, + "args": ArgsListInfo, + "implies": depset[FeatureInfo], + "requires_any_of": Sequence[FeatureSetInfo], + "mutually_exclusive": Sequence[MutuallyExclusiveCategoryInfo], + "external": bool, + "overridable": bool, + "overrides": FeatureInfo | None, + "allowlist_include_directories": depset[DirectoryInfo], + "allowlist_absolute_include_directories": depset[str], +}]: ... + +def FeatureSetInfo( + *, + label: Label, + features: depset[FeatureInfo], +) -> record[{ + "label": Label, + "features": depset[FeatureInfo], +}]: ... + +def FeatureConstraintInfo( + *, + label: Label, + all_of: depset[FeatureInfo], + none_of: depset[FeatureInfo], +) -> record[{ + "label": Label, + "all_of": depset[FeatureInfo], + "none_of": depset[FeatureInfo], +}]: ... + +def MakeVariableInfo( + *, + label: Label, + key: str, + value: str, +) -> record[{ + "label": Label, + "key": str, + "value": str, +}]: ... + +def ExecutionRequirementsInfo( + *, + label: Label, + requirements: Sequence[str], +) -> record[{ + "label": Label, + "requirements": Sequence[str], +}]: ... + +def MutuallyExclusiveCategoryInfo( + *, + label: Label, + name: str, +) -> record[{ + "label": Label, + "name": str, +}]: ... + +def ToolInfo( + *, + label: Label, + exe: File, + runfiles: runfiles, + execution_requirements: Sequence[str], + env: dict[str, str], + allowlist_include_directories: depset[DirectoryInfo], + capabilities: Sequence[ToolCapabilityInfo], +) -> record[{ + "label": Label, + "exe": File, + "runfiles": runfiles, + "execution_requirements": Sequence[str], + "env": dict[str, str], + "allowlist_include_directories": depset[DirectoryInfo], + "capabilities": Sequence[ToolCapabilityInfo], +}]: ... + +def ToolCapabilityInfo( + *, + label: Label, + feature: FeatureInfo, +) -> record[{ + "label": Label, + "feature": FeatureInfo, +}]: ... + +def ToolConfigInfo( + *, + label: Label, + configs: dict[ActionTypeInfo, ToolInfo], +) -> record[{ + "label": Label, + "configs": dict[ActionTypeInfo, ToolInfo], +}]: ... + +def LegacyToolInfo( + *, + label: Label, + name: str, + path: str, +) -> record[{ + "label": Label, + "name": str, + "path": str, +}]: ... + +def ToolchainConfigInfo( + *, + label: Label, + features: Sequence[FeatureInfo], + enabled_features: Sequence[FeatureInfo], + tool_map: ToolConfigInfo, + args: ArgsListInfo, + artifact_name_patterns: Sequence[ArtifactNamePatternInfo], + make_variables: Sequence[MakeVariableInfo], + files: dict[ActionTypeInfo, depset[File]], + allowlist_include_directories: depset[DirectoryInfo], + allowlist_absolute_include_directories: depset[str], + legacy_tools: Sequence[LegacyToolInfo], +) -> record[{ + "label": Label, + "features": Sequence[FeatureInfo], + "enabled_features": Sequence[FeatureInfo], + "tool_map": ToolConfigInfo, + "args": ArgsListInfo, + "artifact_name_patterns": Sequence[ArtifactNamePatternInfo], + "make_variables": Sequence[MakeVariableInfo], + "files": dict[ActionTypeInfo, depset[File]], + "allowlist_include_directories": depset[DirectoryInfo], + "allowlist_absolute_include_directories": depset[str], + "legacy_tools": Sequence[LegacyToolInfo], +}]: ... diff --git a/cc/toolchains/directory_tool.bzli b/cc/toolchains/directory_tool.bzli new file mode 100644 index 000000000..9804c209f --- /dev/null +++ b/cc/toolchains/directory_tool.bzli @@ -0,0 +1,17 @@ +"""Tools extracted from directories.""" + +load("//cc/toolchains:tool.bzl", _cc_tool = "cc_tool") + +type _Strings = Collection[str] | depset[str] +type _StringsInput = _Strings | select[_Strings | None] | None +type _Bool = bool | Literal[0, 1] + +def cc_directory_tool( + name: str, + directory: str | Label | select[str | Label | None], + executable: str, + data: _StringsInput = ..., + exclude: _StringsInput = ..., + allow_empty: _Bool | select[_Bool | None] | None = ..., + **kwargs: Unpack[KeywordArgs[_cc_tool, "name", "src", "data"]], +) -> None: ... diff --git a/cc/toolchains/feature.bzl b/cc/toolchains/feature.bzl index 713b2a102..29f63097d 100644 --- a/cc/toolchains/feature.bzl +++ b/cc/toolchains/feature.bzl @@ -29,6 +29,7 @@ load( ) def _cc_feature_impl(ctx): + # type: (...) -> list[FeatureInfo | FeatureSetInfo | FeatureConstraintInfo | MutuallyExclusiveCategoryInfo] if bool(ctx.attr.feature_name) == (ctx.attr.overrides != None): fail("Exactly one of 'feature_name' and 'overrides' are required") diff --git a/cc/toolchains/feature_constraint.bzl b/cc/toolchains/feature_constraint.bzl index 8a3d60f8d..b08bae7ed 100644 --- a/cc/toolchains/feature_constraint.bzl +++ b/cc/toolchains/feature_constraint.bzl @@ -25,6 +25,7 @@ load( ) def _cc_feature_constraint_impl(ctx): + # type: (...) -> list[FeatureConstraintInfo] if ctx.attr.features: fail("Features is a reserved attribute in bazel. Use the attributes `all_of` and `none_of` for feature constraints") all_of = collect_provider(ctx.attr.all_of, FeatureConstraintInfo) diff --git a/cc/toolchains/feature_set.bzl b/cc/toolchains/feature_set.bzl index c4a075613..db8b1f765 100644 --- a/cc/toolchains/feature_set.bzl +++ b/cc/toolchains/feature_set.bzl @@ -21,6 +21,7 @@ load( ) def _cc_feature_set_impl(ctx): + # type: (...) -> list[FeatureSetInfo | FeatureConstraintInfo] if ctx.attr.features: fail("Features is a reserved attribute in bazel. cc_feature_set takes `all_of` instead.") features = collect_features(ctx.attr.all_of) diff --git a/cc/toolchains/impl/cc_artifact_category.bzl b/cc/toolchains/impl/cc_artifact_category.bzl index b53a18b9f..680e3e876 100644 --- a/cc/toolchains/impl/cc_artifact_category.bzl +++ b/cc/toolchains/impl/cc_artifact_category.bzl @@ -21,6 +21,7 @@ visibility([ ]) def _cc_artifact_category_impl(ctx): + # type: (...) -> list[ArtifactCategoryInfo] return [ ArtifactCategoryInfo( label = ctx.label, diff --git a/cc/toolchains/impl/collect.bzl b/cc/toolchains/impl/collect.bzl index eb020b0b5..f1f06f04b 100644 --- a/cc/toolchains/impl/collect.bzl +++ b/cc/toolchains/impl/collect.bzl @@ -39,6 +39,7 @@ def collect_provider(targets, provider): return [target[provider] for target in targets] def collect_defaultinfo(targets): + # type: (Sequence[Target]) -> list[DefaultInfo] """Collects DefaultInfo from a label list. Args: @@ -48,21 +49,28 @@ def collect_defaultinfo(targets): """ return collect_provider(targets, DefaultInfo) -def _make_collector(provider, field): +def _make_collector(field): def collector(targets, direct = [], transitive = []): + # type: (Sequence[Target], Sequence[T], list[depset[T]]) -> depset[T] + # TODO(https://github.com/bazelbuild/bazel/issues/32021): make this an + # annotation once supported Bazel versions have element-typed depsets, + # source type parameters, and admit Target values in sequences. + # Target admission: https://github.com/bazelbuild/bazel/issues/32024 + # The first issue covers only the depset requirement. # Avoid mutating what was passed in. transitive = transitive[:] - for value in collect_provider(targets, provider): - transitive.append(getattr(value, field)) + for target in targets: + transitive.append(field(target)) return depset(direct = direct, transitive = transitive) return collector -collect_action_types = _make_collector(ActionTypeSetInfo, "actions") -collect_features = _make_collector(FeatureSetInfo, "features") -collect_files = _make_collector(DefaultInfo, "files") +collect_action_types = _make_collector(lambda target: target[ActionTypeSetInfo].actions) +collect_features = _make_collector(lambda target: target[FeatureSetInfo].features) +collect_files = _make_collector(lambda target: target.files) def collect_data(ctx, targets): + # type: (ctx[object, object], Sequence[Target]) -> runfiles """Collects from a 'data' attribute. This is distinguished from collect_files by the fact that data attributes @@ -83,6 +91,7 @@ def collect_data(ctx, targets): ]) def collect_tools(ctx, targets, fail = fail): + # type: (ctx[object, object], Sequence[Target], Callable[[str], None]) -> list[ToolInfo] """Collects tools from a label_list. Each entry in the label list may either be a cc_tool or a binary. @@ -117,6 +126,7 @@ def collect_tools(ctx, targets, fail = fail): return tools def collect_args_lists(targets, label): + # type: (Sequence[Target], Label) -> ArgsListInfo """Collects a label_list of ArgsListInfo into a single ArgsListInfo Args: @@ -127,16 +137,19 @@ def collect_args_lists(targets, label): together. """ args = [] - by_action = {} + by_action = {} # type: dict[ActionTypeInfo, _ActionAccumulator] transitive_files = [] for target in targets: args_list = target[ArgsListInfo] args.extend(args_list.args) transitive_files.extend([args_info.files for args_info in args_list.args]) for value in args_list.by_action: + action_args = [] # type: list[ArgsInfo] + + action_files = [] # type: list[depset[File]] out = by_action.setdefault( value.action, - struct(args = [], transitive_files = [], action = value.action), + struct(args = action_args, transitive_files = action_files, action = value.action), ) out.args.extend(value.args) out.transitive_files.append(value.files) diff --git a/cc/toolchains/impl/collect.bzli b/cc/toolchains/impl/collect.bzli new file mode 100644 index 000000000..4943c37d4 --- /dev/null +++ b/cc/toolchains/impl/collect.bzli @@ -0,0 +1,11 @@ +"""Typed collections of toolchain provider values.""" + +load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ArgsInfo", "ArgsListInfo", "ToolInfo") + +type _ActionAccumulator = struct[{"action": ActionTypeInfo, "args": list[ArgsInfo], "transitive_files": list[depset[File]]}] + +class _Collector[T](Protocol): + def __call__(self, targets: Sequence[Target], direct: Sequence[T] = ..., transitive: list[depset[T]] = ...) -> depset[T]: ... + +def collect_provider[P](targets: Sequence[Target], provider: Provider[P]) -> list[P]: ... +def _make_collector[T](field: Callable[[Target], depset[T]]) -> _Collector[T]: ... diff --git a/cc/toolchains/impl/external_feature.bzl b/cc/toolchains/impl/external_feature.bzl index 6aa3b026a..42fcf7b58 100644 --- a/cc/toolchains/impl/external_feature.bzl +++ b/cc/toolchains/impl/external_feature.bzl @@ -27,6 +27,7 @@ visibility([ ]) def _cc_external_feature_impl(ctx): + # type: (...) -> list[FeatureInfo | FeatureSetInfo | FeatureConstraintInfo] feature = FeatureInfo( label = ctx.label, name = ctx.attr.feature_name, @@ -36,6 +37,8 @@ def _cc_external_feature_impl(ctx): args = (), files = depset([]), by_action = (), + allowlist_include_directories = depset(), + allowlist_absolute_include_directories = depset(), ), implies = depset([]), requires_any_of = (), diff --git a/cc/toolchains/impl/label_utils.bzl b/cc/toolchains/impl/label_utils.bzl index 690bcec8b..dfa0ecfff 100644 --- a/cc/toolchains/impl/label_utils.bzl +++ b/cc/toolchains/impl/label_utils.bzl @@ -14,6 +14,7 @@ """Helpers for passing possibly duplicated labels through rule attributes.""" def deduplicate_label_list(name, labels): + # type: (str, Sequence[str | Label]) -> struct[{'labels': list[Label], 'indexes': list[int]}] """Deduplicates a label list while preserving the original indexes. Args: diff --git a/cc/toolchains/impl/legacy_converter.bzl b/cc/toolchains/impl/legacy_converter.bzl index d0378911a..02fa8bd3b 100644 --- a/cc/toolchains/impl/legacy_converter.bzl +++ b/cc/toolchains/impl/legacy_converter.bzl @@ -155,7 +155,7 @@ def convert_capability(capability): def _convert_tool_map(tool_map, args_by_action): action_configs = [] - caps = {} + caps = {} # type: dict[ToolCapabilityInfo, None] for action_type, tool in tool_map.configs.items(): action_args = args_by_action.get(action_type.name, default = None) flag_sets = action_args.flag_sets if action_args != None else [] @@ -194,9 +194,11 @@ def convert_toolchain(toolchain): # For that reason, there's additional logic in this function to ensure that the args are # attached to the action configs directly, as that is the only way to ensure the correct # ordering. - args_by_action = {} + args_by_action = {} # type: dict[str, LegacyArgs] for a in toolchain.args.by_action: - args = args_by_action.setdefault(a.action.name, struct(flag_sets = [], env_sets = [])) + flag_sets = [] # type: list[FlagSetInfo] + env_sets = [] # type: list[EnvSetInfo] + args = args_by_action.setdefault(a.action.name, struct(flag_sets = flag_sets, env_sets = env_sets)) new_args = _convert_args_sequence(a.args, strip_actions = True) args.flag_sets.extend(new_args.flag_sets) args.env_sets.extend(new_args.env_sets) diff --git a/cc/toolchains/impl/legacy_converter.bzli b/cc/toolchains/impl/legacy_converter.bzli new file mode 100644 index 000000000..facd81898 --- /dev/null +++ b/cc/toolchains/impl/legacy_converter.bzli @@ -0,0 +1,25 @@ +"""Conversion from rule-based toolchain providers to legacy configuration values.""" + +load("//cc:cc_toolchain_config_lib.bzli", "ActionConfigInfo", "ArtifactNamePatternInfo", "EnvSetInfo", "FeatureInfo", "FlagSetInfo", "MakeVariableInfo", "ToolInfo", "ToolPathInfo", "WithFeatureSetInfo") +load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ArgsInfo", "FeatureConstraintInfo", "ToolConfigInfo", "ToolCapabilityInfo", "ToolchainConfigInfo", NewFeatureInfo = "FeatureInfo", NewToolInfo = "ToolInfo") + +type LegacyArgs = struct[{"flag_sets": list[FlagSetInfo], "env_sets": list[EnvSetInfo]}] +type LegacyToolchain = struct[{ + "features": list[FeatureInfo], + "action_configs": list[ActionConfigInfo], + "cxx_builtin_include_directories": list[str], + "artifact_name_patterns": list[ArtifactNamePatternInfo], + "make_variables": list[MakeVariableInfo], + "tool_paths": list[ToolPathInfo], +}] + +def _convert_actions(actions: depset[ActionTypeInfo]) -> list[str]: ... +def convert_feature_constraint(constraint: FeatureConstraintInfo) -> WithFeatureSetInfo: ... +def convert_args(args: ArgsInfo, strip_actions: bool = ...) -> LegacyArgs: ... +def _convert_args_sequence(args_sequence: Sequence[ArgsInfo], strip_actions: bool = ...) -> LegacyArgs: ... +def _convert_tool_env(tool: NewToolInfo, action_name: str) -> EnvSetInfo | None: ... +def convert_feature(feature: NewFeatureInfo, enabled: bool = ...) -> FeatureInfo | None: ... +def convert_tool(tool: NewToolInfo) -> ToolInfo: ... +def convert_capability(capability: NewFeatureInfo) -> FeatureInfo: ... +def _convert_tool_map(tool_map: ToolConfigInfo, args_by_action: dict[str, LegacyArgs]) -> tuple[list[ActionConfigInfo], list[FeatureInfo]]: ... +def convert_toolchain(toolchain: ToolchainConfigInfo) -> LegacyToolchain: ... diff --git a/cc/toolchains/impl/libc_version.bzl b/cc/toolchains/impl/libc_version.bzl index c3f8ef659..55c65da38 100644 --- a/cc/toolchains/impl/libc_version.bzl +++ b/cc/toolchains/impl/libc_version.bzl @@ -20,6 +20,7 @@ visibility([ ]) def libc_without_version(libc): + # type: (str) -> str """Strips a version suffix from a target_libc value. Args: diff --git a/cc/toolchains/impl/markdown_helpers.bzl b/cc/toolchains/impl/markdown_helpers.bzl index 1ae401f99..ee9d78bc6 100644 --- a/cc/toolchains/impl/markdown_helpers.bzl +++ b/cc/toolchains/impl/markdown_helpers.bzl @@ -14,6 +14,7 @@ """A few small helpers for working with Markdown.""" def markdown_link(link_text, href): + # type: (str, str) -> str """Creates a markdown link. Args: @@ -26,6 +27,7 @@ def markdown_link(link_text, href): return "[" + link_text + "](" + href + ")" def xref_substitutions(match_text_patterns): + # type: (Mapping[str, str]) -> dict[str, str] """Creates a dictionary of substitutions for use for linkification of text. Example: diff --git a/cc/toolchains/impl/nested_args.bzl b/cc/toolchains/impl/nested_args.bzl index 8b523ccdb..c559c58a1 100644 --- a/cc/toolchains/impl/nested_args.bzl +++ b/cc/toolchains/impl/nested_args.bzl @@ -82,6 +82,7 @@ directory as additional files. } def _var(target): + # type: (Target | None) -> str | None if target == None: return None return target[VariableInfo].name @@ -301,17 +302,19 @@ def nested_args_provider( ) def _escape(s): + # type: (str) -> str return s.replace("%", "%%") _SUPPORTED_BUILD_SETTING_TYPES = ["string", "bool", "int", "Label"] -def _format_build_setting(value, label, fail = fail): +def _format_build_setting(value, label, report_error = fail): if type(value) in _SUPPORTED_BUILD_SETTING_TYPES: return _escape(str(value)) - fail("%s had an unsupported build setting type %s. Only string, bool, int, or Label values may be formatted." % (label, type(value))) + report_error("%s had an unsupported build setting type %s. Only string, bool, int, or Label values may be formatted." % (label, type(value))) + return "" -def _format_target(target, fail = fail): +def _format_target(target, report_error = fail): # NOTE: This is for use with ctx.var where we don't have a variable, but we # also don't want to allow arbitrary strings. See args.bzl for usage. if hasattr(target, "__raw_string"): @@ -322,7 +325,7 @@ def _format_target(target, fail = fail): return _format_build_setting( target[BuildSettingInfo].value, target.label, - fail = fail, + report_error = report_error, ) path = None @@ -333,9 +336,10 @@ def _format_target(target, fail = fail): if len(files) == 1: path = files[0].path if path == None: - fail("%s should be either a variable, a directory, or a single file." % target.label) + report_error("%s should be either a variable, a directory, or a single file." % target.label) + return "" if "}" in path: - fail("The path %r of target %s used with 'format' cannot contain } because it would interfere with the formatting." % ( + report_error("The path %r of target %s used with 'format' cannot contain } because it would interfere with the formatting." % ( path, target.label, )) @@ -373,7 +377,7 @@ def _format_string(arg, format, used_vars, fail = fail): else: used_vars[variable] = None has_format = True - out.append(_format_target(format[variable], fail = fail)) + out.append(_format_target(format[variable], report_error = fail)) upto += len(variable) + 2 elif arg[upto] == "}": @@ -400,7 +404,7 @@ def format_list(args, format, must_use = [], fail = fail): A string defined to be compatible with flag groups. """ formatted = [] - used_vars = {} + used_vars = {} # type: dict[str, None] for arg in args: formatted.append(_format_string(arg, format, used_vars, fail)) @@ -427,7 +431,7 @@ def format_dict_values(env, format, must_use = [], fail = fail): The environment variables with values defined to be compatible with flag groups. """ formatted = {} - used_vars = {} + used_vars = {} # type: dict[str, None] for key, value in env.items(): formatted[key] = _format_string(value, format, used_vars, fail) diff --git a/cc/toolchains/impl/nested_args.bzli b/cc/toolchains/impl/nested_args.bzli new file mode 100644 index 000000000..e2b6fb6b8 --- /dev/null +++ b/cc/toolchains/impl/nested_args.bzli @@ -0,0 +1,45 @@ +"""Formatting and validation of nested toolchain arguments.""" + +load("//cc/toolchains:cc_toolchain_info.bzli", "NestedArgsInfo") + +type _Fail = Callable[[str], None] +type _RawString = struct[{"__raw_string": str}] +type _Format = Mapping[str, Target | _RawString] +type _Attrs[T: Target] = struct[{ + "args": list[str], + "format": Mapping[T, str], + "nested": Sequence[Target], + "data": Sequence[Target], + "iterate_over": Target | None, + "requires_not_none": Target | None, + "requires_none": Target | None, + "requires_true": Target | None, + "requires_false": Target | None, + "requires_equal": Target | None, + "requires_equal_value": str, +}, ...] + +def nested_args_provider_from_ctx[T: Target](ctx: ctx[object, _Attrs[T]], maybe_used_vars: Sequence[str] = ...) -> NestedArgsInfo: ... +def nested_args_provider[T: Target]( + *, + label: Label, + args: Sequence[str] = ..., + nested: Sequence[NestedArgsInfo] = ..., + format: Mapping[T, str] = ..., + files: depset[File] = ..., + iterate_over: Target | None = ..., + requires_not_none: str | None = ..., + requires_none: str | None = ..., + requires_true: str | None = ..., + requires_false: str | None = ..., + requires_equal: str | None = ..., + requires_equal_value: str = ..., + maybe_used_vars: Sequence[str] = ..., + make_variables: Mapping[str, str] = ..., + fail: _Fail = ..., +) -> NestedArgsInfo: ... +def _format_build_setting(value: object, label: Label, report_error: _Fail = ...) -> str: ... +def _format_target(target: Target | _RawString, report_error: _Fail = ...) -> str: ... +def _format_string(arg: str, format: _Format, used_vars: dict[str, None], fail: _Fail = ...) -> str: ... +def format_list(args: Collection[str], format: _Format, must_use: Sequence[str] = ..., fail: _Fail = ...) -> tuple[list[str], list[str]]: ... +def format_dict_values(env: Mapping[str, str], format: _Format, must_use: Sequence[str] = ..., fail: _Fail = ...) -> tuple[dict[str, str], list[str]]: ... diff --git a/cc/toolchains/impl/variables.bzl b/cc/toolchains/impl/variables.bzl index aab365df0..11f28a5c8 100644 --- a/cc/toolchains/impl/variables.bzl +++ b/cc/toolchains/impl/variables.bzl @@ -49,11 +49,16 @@ types = struct( ), ) +def _decode_variable_type(encoded): + # cc_variable is the sole writer of the private rule's JSON attribute. + return json.decode(encoded) # ty: ignore[non-static-type, unsound-return-statement] + def _cc_variable_impl(ctx): + # type: (...) -> list[VariableInfo] return [VariableInfo( name = ctx.label.name, label = ctx.label, - type = json.decode(ctx.attr.type), + type = _decode_variable_type(ctx.attr.type), actions = collect_action_types(ctx.attr.actions) if ctx.attr.actions else None, )] @@ -66,7 +71,7 @@ _cc_variable = rule( provides = [VariableInfo], ) -def cc_variable(name, type, **kwargs): +def cc_variable(name, type, *, actions = [], **kwargs): """Exposes a toolchain variable to use in toolchain argument expansions. This internal rule exposes [toolchain variables](https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables) @@ -92,11 +97,13 @@ def cc_variable(name, type, **kwargs): name: (str) The name of the outer variable, and the rule. 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). + 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. """ - _cc_variable(name = name, type = json.encode(type), **kwargs) + _cc_variable(name = name, type = json.encode(type), actions = actions, **kwargs) def _cc_builtin_variables_impl(ctx): + # type: (...) -> list[BuiltinVariablesInfo] return [BuiltinVariablesInfo(variables = { variable.name: variable for variable in collect_provider(ctx.attr.srcs, VariableInfo) @@ -137,12 +144,13 @@ def get_type(*, name, variables, overrides, actions, args_label, nested_label, f # buildifier: disable=unreachable return types.void - if variables[outer].actions != None: - valid_actions = variables[outer].actions.to_list() + variable = variables[outer] + if variable.actions != None: + valid_actions = variable.actions.to_list() for action in actions: if action not in valid_actions: fail("The variable {var} is inaccessible from the action {action}. This is required because it is referenced in {nested_label}, which is included by {args_label}, which references that action".format( - var = variables[outer].label, + var = variable.label, nested_label = nested_label, args_label = args_label, action = action.label, @@ -151,7 +159,7 @@ def get_type(*, name, variables, overrides, actions, args_label, nested_label, f # buildifier: disable=unreachable return types.void - type = overrides.get(outer, variables[outer].type) + type = overrides.get(outer, variable.type) parent = outer for part in name.split(".")[1:]: @@ -176,7 +184,7 @@ def get_type(*, name, variables, overrides, actions, args_label, nested_label, f # buildifier: disable=unreachable return types.void - type = overrides.get(full, type["kv"][part]) + type = overrides[full] if full in overrides else type["kv"][part] parent = full return type diff --git a/cc/toolchains/impl/variables.bzli b/cc/toolchains/impl/variables.bzli new file mode 100644 index 000000000..566b3ab12 --- /dev/null +++ b/cc/toolchains/impl/variables.bzli @@ -0,0 +1,49 @@ +"""Toolchain variable descriptors.""" + +load( + "//cc/toolchains:cc_toolchain_info.bzli", + "ActionTypeInfo", + "ElementVariableType", + "LeafVariableType", + "StructVariableType", + "VariableInfo", + "VariableType", +) + +class _StructFactory(Protocol): + def __call__(self, **fields: VariableType) -> StructVariableType: ... + +types: struct[{ + "unknown": LeafVariableType, + "void": LeafVariableType, + "string": LeafVariableType, + "bool": LeafVariableType, + "file": LeafVariableType, + "directory": LeafVariableType, + "option": Callable[[VariableType], ElementVariableType], + "list": Callable[[VariableType], ElementVariableType], + "struct": _StructFactory, +}] + +type _Labels = Collection[Label | str] | depset[Label | str] | None + +def _decode_variable_type(encoded: str) -> VariableType: ... + +def cc_variable( + name: str, + type: VariableType, + *, + actions: _Labels | select[_Labels] = ..., + **kwargs: Unpack[CommonRuleAttrs], +) -> None: ... + +def get_type( + *, + name: str, + variables: Mapping[str, VariableInfo], + overrides: Mapping[str, VariableType], + actions: Collection[ActionTypeInfo], + args_label: Label, + nested_label: Label, + fail: Callable[[str], object], +) -> VariableType: ... diff --git a/cc/toolchains/legacy_file_group.bzli b/cc/toolchains/legacy_file_group.bzli new file mode 100644 index 000000000..7268adcbb --- /dev/null +++ b/cc/toolchains/legacy_file_group.bzli @@ -0,0 +1,12 @@ +"""Action labels grouped by the legacy attributes of cc_toolchain.""" + +LEGACY_FILE_GROUPS: dict[{ + "ar_files": list[Label], + "as_files": list[Label], + "compiler_files": list[Label], + "coverage_files": list[Label], + "dwp_files": list[Label], + "linker_files": list[Label], + "objcopy_files": list[Label], + "strip_files": list[Label], +}] diff --git a/cc/toolchains/legacy_tool.bzl b/cc/toolchains/legacy_tool.bzl index 4ab9f8611..629730a51 100644 --- a/cc/toolchains/legacy_tool.bzl +++ b/cc/toolchains/legacy_tool.bzl @@ -16,6 +16,7 @@ load(":cc_toolchain_info.bzl", "LegacyToolInfo") def _cc_legacy_tool_impl(ctx): + # type: (...) -> list[LegacyToolInfo] return [LegacyToolInfo( label = ctx.label, name = ctx.attr.tool_name, diff --git a/cc/toolchains/make_variable.bzl b/cc/toolchains/make_variable.bzl index 754dd768b..a1e54917c 100644 --- a/cc/toolchains/make_variable.bzl +++ b/cc/toolchains/make_variable.bzl @@ -19,6 +19,7 @@ load(":cc_toolchain_info.bzl", "MakeVariableInfo") visibility("public") def _cc_make_variable_impl(ctx): + # type: (...) -> list[MakeVariableInfo] return [ MakeVariableInfo( label = ctx.label, diff --git a/cc/toolchains/mutually_exclusive_category.bzl b/cc/toolchains/mutually_exclusive_category.bzl index f83b55499..d6fe831b4 100644 --- a/cc/toolchains/mutually_exclusive_category.bzl +++ b/cc/toolchains/mutually_exclusive_category.bzl @@ -16,6 +16,7 @@ load(":cc_toolchain_info.bzl", "MutuallyExclusiveCategoryInfo") def _cc_mutually_exclusive_category_impl(ctx): + # type: (...) -> list[MutuallyExclusiveCategoryInfo] return [MutuallyExclusiveCategoryInfo( label = ctx.label, name = str(ctx.label), diff --git a/cc/toolchains/nested_args.bzli b/cc/toolchains/nested_args.bzli new file mode 100644 index 000000000..7eac8bda5 --- /dev/null +++ b/cc/toolchains/nested_args.bzli @@ -0,0 +1,23 @@ +"""Nested command-line arguments.""" + +type _Label = str | Label +type _LabelInput = _Label | select[_Label | None] | None +type _ListInput[T] = Collection[T] | depset[T] | select[Collection[T] | depset[T] | None] | None +type _StringInput = str | select[str | None] | None + +def cc_nested_args( + *, + name: str, + args: _ListInput[str] = ..., + data: _ListInput[_Label] = ..., + format: Mapping[str, _Label] = ..., + iterate_over: _LabelInput = ..., + nested: _ListInput[_Label] = ..., + requires_not_none: _LabelInput = ..., + requires_none: _LabelInput = ..., + requires_true: _LabelInput = ..., + requires_false: _LabelInput = ..., + requires_equal: _LabelInput = ..., + requires_equal_value: _StringInput = ..., + **kwargs: Unpack[CommonRuleAttrs], +) -> None: ... diff --git a/cc/toolchains/tool.bzl b/cc/toolchains/tool.bzl index 246d54c55..3fb958fd4 100644 --- a/cc/toolchains/tool.bzl +++ b/cc/toolchains/tool.bzl @@ -33,7 +33,7 @@ def _cc_tool_impl(ctx): else: fail("Expected cc_tool's src attribute to be either an executable or a single file") - format_targets = {} + format_targets = {} # type: dict[str, Target] for i in range(len(ctx.attr.format_keys)): format_targets[ctx.attr.format_keys[i]] = ctx.attr.format_values[ctx.attr.format_value_indexes[i]] env, _ = format_dict_values( @@ -45,7 +45,9 @@ def _cc_tool_impl(ctx): for info in collect_provider(ctx.attr.execution_requirements, ExecutionRequirementsInfo): execution_requirements.extend(info.requirements) - runfiles = collect_data(ctx, ctx.attr.data + [ctx.attr.src]) + inputs = list(ctx.attr.data) + inputs.append(ctx.attr.src) + runfiles = collect_data(ctx, inputs) tool = ToolInfo( label = ctx.label, exe = exe, @@ -81,6 +83,7 @@ _cc_tool = rule( attrs = { "src": attr.label( allow_files = True, + mandatory = True, doc = """The underlying binary that this tool represents. Usually just a single prebuilt (eg. @toolchain//:bin/clang), but may be any @@ -188,7 +191,7 @@ cc_tool( def cc_tool( *, name, - src = None, + src, data = None, allowlist_include_directories = None, env = None, diff --git a/cc/toolchains/tool.bzli b/cc/toolchains/tool.bzli new file mode 100644 index 000000000..6cfceca24 --- /dev/null +++ b/cc/toolchains/tool.bzli @@ -0,0 +1,34 @@ +"""Tools, their execution inputs, and forwarded rule attributes.""" + +load("//cc/toolchains:cc_toolchain_info.bzli", "ToolInfo") + +type _LabelInput = str | Label | select[str | Label | None] +type _Labels = Collection[str | Label] | depset[str | Label] +type _LabelsInput = _Labels | select[_Labels | None] | None +type _EnvironmentInput = Mapping[str, str] | select[Mapping[str, str] | None] | None +type _Attrs = struct[{ + "src": Target, + "data": Sequence[Target], + "format_keys": list[str], + "format_values": Sequence[Target], + "format_value_indexes": list[int], + "env": dict[str, str], + "execution_requirements": Sequence[Target], + "tags": list[str], + "allowlist_include_directories": Sequence[Target], + "capabilities": Sequence[Target], +}, ...] + +def _cc_tool_impl(ctx: ctx[object, _Attrs]) -> list[ToolInfo | DefaultInfo]: ... +def cc_tool( + *, + name: str, + src: _LabelInput, + data: _LabelsInput = ..., + allowlist_include_directories: _LabelsInput = ..., + env: _EnvironmentInput = ..., + execution_requirements: _LabelsInput = ..., + format: Mapping[str, str | Label] = ..., + capabilities: _LabelsInput = ..., + **kwargs: Unpack[CommonExecutableRuleAttrs], +) -> None: ... diff --git a/cc/toolchains/tool_capability.bzl b/cc/toolchains/tool_capability.bzl index 3b811a87f..2dec70b27 100644 --- a/cc/toolchains/tool_capability.bzl +++ b/cc/toolchains/tool_capability.bzl @@ -22,6 +22,7 @@ load( ) def _cc_tool_capability_impl(ctx): + # type: (...) -> list[ToolCapabilityInfo | FeatureConstraintInfo] ft = FeatureInfo( name = ctx.attr.feature_name or ctx.label.name, label = ctx.label, @@ -32,6 +33,7 @@ def _cc_tool_capability_impl(ctx): files = depset(), by_action = (), allowlist_include_directories = depset(), + allowlist_absolute_include_directories = depset(), ), implies = depset(), requires_any_of = (), @@ -43,6 +45,7 @@ def _cc_tool_capability_impl(ctx): overridable = True, overrides = None, allowlist_include_directories = depset(), + allowlist_absolute_include_directories = depset(), ) return [ ToolCapabilityInfo(label = ctx.label, feature = ft), diff --git a/cc/toolchains/tool_map.bzl b/cc/toolchains/tool_map.bzl index 6b133e14d..c8c5aece9 100644 --- a/cc/toolchains/tool_map.bzl +++ b/cc/toolchains/tool_map.bzl @@ -30,8 +30,8 @@ def _cc_tool_map_impl(ctx): tools = collect_tools(ctx, ctx.attr.tools) action_sets = collect_provider(ctx.attr.actions, ActionTypeSetInfo) - action_to_tool = {} - action_to_as = {} + action_to_tool = {} # type: dict[ActionTypeInfo, ToolInfo] + action_to_as = {} # type: dict[ActionTypeInfo, ActionTypeSetInfo] for i in range(len(action_sets)): action_set = action_sets[i] tool = tools[ctx.attr.tool_index_for_action[i]] diff --git a/cc/toolchains/tool_map.bzli b/cc/toolchains/tool_map.bzli new file mode 100644 index 000000000..91a62af81 --- /dev/null +++ b/cc/toolchains/tool_map.bzli @@ -0,0 +1,12 @@ +"""The association between toolchain actions and executable tools.""" + +load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ActionTypeSetInfo", "ToolConfigInfo", "ToolInfo") + +type _Attrs = struct[{ + "actions": Sequence[Target], + "tools": Sequence[Target], + "tool_index_for_action": list[int], +}, ...] + +def _cc_tool_map_impl(ctx: ctx[object, _Attrs]) -> list[ToolConfigInfo]: ... +def cc_tool_map[K: str | Label](name: str, tools: Mapping[K, str | Label], **kwargs: Unpack[CommonRuleAttrs]) -> None: ... diff --git a/cc/use_cc_toolchain.bzl b/cc/use_cc_toolchain.bzl index 8e271105a..a7eee9731 100644 --- a/cc/use_cc_toolchain.bzl +++ b/cc/use_cc_toolchain.bzl @@ -60,6 +60,7 @@ CC_TOOLCHAIN_ATTRS = { } def use_cc_toolchain(mandatory = True): + # type: (bool) -> list[toolchain_type] """ Helper to depend on the cc toolchain. 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/tests/cc/testutil/toolchains/additional_toolchains.bzl b/tests/cc/testutil/toolchains/additional_toolchains.bzl index 1a8320cb7..ea7c8419f 100644 --- a/tests/cc/testutil/toolchains/additional_toolchains.bzl +++ b/tests/cc/testutil/toolchains/additional_toolchains.bzl @@ -1,6 +1,7 @@ """Additional mock toolchains besides the mock C++ toolchain.""" def mock_additional_toolchains(): + # type: () -> None pass ADDITIONAL_MOCK_TOOLCHAINS = [] diff --git a/tests/cc/testutil/toolchains/additional_toolchains.bzli b/tests/cc/testutil/toolchains/additional_toolchains.bzli new file mode 100644 index 000000000..ce7ebffe8 --- /dev/null +++ b/tests/cc/testutil/toolchains/additional_toolchains.bzli @@ -0,0 +1,3 @@ +"""Additional mock toolchain labels used by the C++ analysis tests.""" + +ADDITIONAL_MOCK_TOOLCHAINS: list[str] diff --git a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl index d9a695de1..4d4b25653 100644 --- a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl +++ b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl @@ -20,6 +20,7 @@ load("//cc/toolchains/impl:collect.bzl", "collect_action_types") visibility("private") def _generate_action_type_set_impl(ctx): + # type: (...) -> DefaultInfo out = ctx.actions.declare_file(ctx.attr.output.name) actions = sorted([ action.name diff --git a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli new file mode 100644 index 000000000..54f9273eb --- /dev/null +++ b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli @@ -0,0 +1,5 @@ +"""Action set golden file test declarations.""" + +type _LabelInput = str | Label | select[str | Label] + +def action_type_set_diff_test(name: str, action_type_set: _LabelInput, expected: _LabelInput) -> None: ... diff --git a/tests/rule_based_toolchain/args/make_var_provider.bzl b/tests/rule_based_toolchain/args/make_var_provider.bzl index 740387397..9f5d0d76a 100644 --- a/tests/rule_based_toolchain/args/make_var_provider.bzl +++ b/tests/rule_based_toolchain/args/make_var_provider.bzl @@ -1,6 +1,7 @@ """Helper rule for providing TemplateVariableInfo in tests.""" def _make_var_provider_impl(ctx): + # type: (...) -> list[TemplateVariableInfo] return [platform_common.TemplateVariableInfo(ctx.attr.variables)] make_var_provider = rule( diff --git a/tests/rule_based_toolchain/features/features_test.bzl b/tests/rule_based_toolchain/features/features_test.bzl index a0d479afa..67f9787bb 100644 --- a/tests/rule_based_toolchain/features/features_test.bzl +++ b/tests/rule_based_toolchain/features/features_test.bzl @@ -143,6 +143,8 @@ def _external_feature_is_a_feature_test(env, targets): FeatureInfo, ) external_feature.name().equals("builtin_feature") + external_feature.args().allowlist_include_directories().contains_exactly([]) + external_feature.args().allowlist_absolute_include_directories().contains_exactly([]) # It's not a string, but we don't have a factory for the type. env.expect.that_str(convert_feature(external_feature.actual)).equals(None) diff --git a/tests/rule_based_toolchain/helpers.bzl b/tests/rule_based_toolchain/helpers.bzl index d0901a310..bffe8ca1b 100644 --- a/tests/rule_based_toolchain/helpers.bzl +++ b/tests/rule_based_toolchain/helpers.bzl @@ -18,6 +18,7 @@ load("@bazel_features//:features.bzl", "bazel_features") visibility("//tests/rule_based_toolchain/...") def path_pattern(p): + # type: (str) -> str """Wraps a path with %{path:...} when supported by Bazel. Mirrors the behavior of cc/toolchains/impl/nested_args.bzl's _format_target. diff --git a/tests/rule_based_toolchain/nested_args/BUILD b/tests/rule_based_toolchain/nested_args/BUILD index 180453741..5bd2c15e3 100644 --- a/tests/rule_based_toolchain/nested_args/BUILD +++ b/tests/rule_based_toolchain/nested_args/BUILD @@ -1,3 +1,4 @@ +load("@bazel_skylib//rules:common_settings.bzl", "string_list_setting") load("//cc/toolchains:nested_args.bzl", "cc_nested_args") load("//cc/toolchains/impl:variables.bzl", "cc_variable", "types") load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite") @@ -31,6 +32,13 @@ cc_nested_args( toolchains = [":make_vars"], ) +filegroup(name = "empty_files") + +string_list_setting( + name = "unsupported_setting", + build_setting_default = ["unsupported"], +) + analysis_test_suite( name = "test_suite", targets = TARGETS, diff --git a/tests/rule_based_toolchain/nested_args/nested_args_test.bzl b/tests/rule_based_toolchain/nested_args/nested_args_test.bzl index 319a64fce..ba4d9a047 100644 --- a/tests/rule_based_toolchain/nested_args/nested_args_test.bzl +++ b/tests/rule_based_toolchain/nested_args/nested_args_test.bzl @@ -122,6 +122,12 @@ def _format_args_test(env, targets): "file", ]) + for target, message in [ + (targets.empty_files, "should be either a variable, a directory, or a single file"), + (targets.unsupported_setting, "had an unsupported build setting type list"), + ]: + _expect_that_formatted(env, ["{value}"], {"value": target}).err().contains(message) + res = _expect_that_formatted( env, ["{var}", "{var}"], @@ -276,6 +282,8 @@ def _nested_make_vars_test(env, targets): nested.legacy_flag_group().equals(flag_group(flags = ["--path=/usr/local", "-DFOO"])) TARGETS = [ + ":empty_files", + ":unsupported_setting", ":foo", ":my_list", ":nested_with_make_vars", diff --git a/tests/rule_based_toolchain/tool/execution_requirements.bzl b/tests/rule_based_toolchain/tool/execution_requirements.bzl index 28be39a30..1be64ee9b 100644 --- a/tests/rule_based_toolchain/tool/execution_requirements.bzl +++ b/tests/rule_based_toolchain/tool/execution_requirements.bzl @@ -18,6 +18,7 @@ load("//cc/toolchains:providers.bzl", "ExecutionRequirementsInfo") visibility("private") def _test_execution_requirements_impl(ctx): + # type: (...) -> list[ExecutionRequirementsInfo] return [ ExecutionRequirementsInfo( label = ctx.label, diff --git a/tests/rule_based_toolchain/tool/execution_requirements.bzli b/tests/rule_based_toolchain/tool/execution_requirements.bzli new file mode 100644 index 000000000..4384459a0 --- /dev/null +++ b/tests/rule_based_toolchain/tool/execution_requirements.bzli @@ -0,0 +1,3 @@ +"""Execution requirements supplied to the toolchain tests.""" + +load("//cc/toolchains:providers.bzl", "ExecutionRequirementsInfo") diff --git a/tests/rule_based_toolchain/tool/tool_test.bzl b/tests/rule_based_toolchain/tool/tool_test.bzl index f79565703..ae5f3de16 100644 --- a/tests/rule_based_toolchain/tool/tool_test.bzl +++ b/tests/rule_based_toolchain/tool/tool_test.bzl @@ -58,6 +58,14 @@ def _wrapped_tool_includes_runfiles_test(env, targets): _BIN, ]) +def _tool_capability_has_no_include_directories_test(env, targets): + feature = env.expect.that_value( + value = targets.tool[ToolInfo].capabilities[0].feature, + factory = subjects.FeatureInfo, + ) + feature.allowlist_absolute_include_directories().contains_exactly([]) + feature.args().allowlist_absolute_include_directories().contains_exactly([]) + def _tool_with_allowlist_include_directories_test(env, targets): tool = env.expect.that_target(targets.tool_with_allowlist_include_directories).provider(ToolInfo) tool.allowlist_include_directories().contains_exactly([_TOOL_DIRECTORY]) @@ -145,6 +153,7 @@ TESTS = { "tool_test": lambda env, targets: _tool_test(env, targets.tool), "directory_tool_test": lambda env, targets: _tool_test(env, targets.directory_tool), "wrapped_tool_includes_runfiles_test": _wrapped_tool_includes_runfiles_test, + "tool_capability_has_no_include_directories_test": _tool_capability_has_no_include_directories_test, "collect_tools_collects_tools_test": _collect_tools_collects_tools_test, "collect_tools_collects_binaries_test": _collect_tools_collects_binaries_test, "collect_tools_collects_single_files_test": _collect_tools_collects_single_files_test, diff --git a/tests/rule_based_toolchain/variables/variables_test.bzl b/tests/rule_based_toolchain/variables/variables_test.bzl index 98a64fd38..cd947c2f1 100644 --- a/tests/rule_based_toolchain/variables/variables_test.bzl +++ b/tests/rule_based_toolchain/variables/variables_test.bzl @@ -89,6 +89,7 @@ str_list expect_type("struct.unknown").err().equals("""Unable to find "unknown" in "struct", which had the following attributes: nested_str: string nested_str_list: List[string]""") + expect_type("struct.unknown", overrides = {"struct.unknown": types.string}).ok().equals(types.string) expect_type("struct", actions = [c_compile]).ok() expect_type("struct", actions = [c_compile, cpp_compile]).err().equals( From cb691505207dd1b88717c43afd2704e9e2acb220 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 29 Oct 2026 12:55:40 -0400 Subject: [PATCH 54/61] Check and distribute rules_cc type contracts Export the interfaces for dependent repositories. Run sty on the implementations and report findings across the remaining Starlark files. --- .github/workflows/sty.yaml | 102 ++++++++++++++++++++++++++++++ BUILD | 6 +- MODULE.bazel | 7 ++ cc/BUILD | 4 ++ cc/private/BUILD | 3 + cc/private/rules_impl/BUILD | 3 + cc/private/toolchain/BUILD | 5 ++ cc/private/toolchain_config/BUILD | 3 + cc/toolchains/BUILD | 12 ++++ cc/toolchains/impl/BUILD | 10 +++ docs/toolchain_api.md | 7 +- stubs.toml | 22 +++++++ sty.toml | 22 +++++++ 13 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/sty.yaml create mode 100644 stubs.toml create mode 100644 sty.toml diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml new file mode 100644 index 000000000..d4db35f21 --- /dev/null +++ b/.github/workflows/sty.yaml @@ -0,0 +1,102 @@ +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@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - name: List maintained Starlark implementations + run: git ls-files -- '*.bzl' > "$RUNNER_TEMP/sty-files" + - uses: tamird/sty@2ab74c830937d3ca85165ca0e9a02a42400ad9e5 + with: + audit-loads: true + bazel-version: '9.3.0' + advisory-files-from: ${{ runner.temp }}/sty-files + 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: | + 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/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/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/propeller_optimize.bzl + cc/toolchains/providers.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 6d274608c..d6b1394a9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -53,3 +53,10 @@ use_repo(compat, "cc_compatibility_proxy") local_bazel_import = use_repo_rule("//:local_bazel.bzl", "local_bazel_import") local_bazel_import(name = "local_bazel") + +# Use provider contracts while bazelbuild/bazel-skylib#641 is in review. +git_override( + module_name = "bazel_skylib", + commit = "98d84dda6a754a8ee2fb99397cbdb690f98f56a0", + remote = "https://github.com/tamird/bazel-skylib.git", +) diff --git a/cc/BUILD b/cc/BUILD index 615b43002..d9c897f69 100644 --- a/cc/BUILD +++ b/cc/BUILD @@ -30,6 +30,9 @@ filegroup( ) exports_files([ + "cc_postmark_initializers.bzli", + "cc_toolchain_config_lib.bzli", + "system_library.bzli", "defs.bzl", "action_names.bzl", "system_library.bzl", @@ -74,6 +77,7 @@ filegroup( testonly = 1, srcs = glob([ "**/*.bzl", + "**/*.bzli", "**/BUILD", ]) + [ "//cc/common:srcs", diff --git a/cc/private/BUILD b/cc/private/BUILD index 02c25bd26..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", 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/toolchain/BUILD b/cc/private/toolchain/BUILD index e3c068058..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") 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/toolchains/BUILD b/cc/toolchains/BUILD index e9ac62b03..2d40c8c2d 100644 --- a/cc/toolchains/BUILD +++ b/cc/toolchains/BUILD @@ -14,6 +14,17 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +exports_files( + [ + "cc_toolchain_info.bzli", + "directory_tool.bzli", + "nested_args.bzli", + "tool.bzli", + "tool_map.bzli", + ], + visibility = ["//visibility:public"], +) + bzl_library( name = "toolchain_rules", srcs = glob(["*.bzl"]), @@ -44,6 +55,7 @@ filegroup( name = "srcs", srcs = glob([ "**/*.bzl", + "**/*.bzli", "**/BUILD", ]) + [ "//cc/toolchains/actions:srcs", diff --git a/cc/toolchains/impl/BUILD b/cc/toolchains/impl/BUILD index 98d2dd716..673005103 100644 --- a/cc/toolchains/impl/BUILD +++ b/cc/toolchains/impl/BUILD @@ -12,6 +12,16 @@ exports_files( visibility = ["//docs:__pkg__"], ) +exports_files( + [ + "collect.bzli", + "legacy_converter.bzli", + "nested_args.bzli", + "variables.bzli", + ], + visibility = ["//visibility:public"], +) + bzl_library( name = "toolchain_impl_rules", srcs = glob(["*.bzl"]), diff --git a/docs/toolchain_api.md b/docs/toolchain_api.md index bd03ee925..914d15ff4 100644 --- a/docs/toolchain_api.md +++ b/docs/toolchain_api.md @@ -611,7 +611,7 @@ For more extensive examples, see the usages here: | Name | Description | Default Value | | :------------- | :------------- | :------------- | | name | (str) The name of the target. | none | -| actions | (List[Label]) A list of labels of [`cc_action_type`](#cc_action_type) or [`cc_action_type_set`](#cc_action_type_set) rules that dictate which actions these arguments should be applied to. | `None` | +| actions | (List[Label]) A list of labels of [`cc_action_type`](#cc_action_type) or [`cc_action_type_set`](#cc_action_type_set) rules that dictate which actions these arguments should be applied to. | none | | allowlist_include_directories | (List[Label]) A list of include paths that are implied by using this rule. These must point to a skylib [directory](https://github.com/bazelbuild/bazel-skylib/tree/main/docs/directory_doc.md#directory) or [subdirectory](https://github.com/bazelbuild/bazel-skylib/tree/main/docs/directory_subdirectory_doc.md#subdirectory) rule. Some flags (e.g. --sysroot) imply certain include paths are available despite not explicitly specifying a normal include path flag (`-I`, `-isystem`, etc.). Bazel checks that all included headers are properly provided by a dependency or allowlisted through this mechanism.

As a rule of thumb, only use this if Bazel is complaining about absolute paths in your toolchain and you've ensured that the toolchain is compiling with the `-no-canonical-prefixes` and/or `-fno-canonical-system-headers` arguments.

These files are not automatically passed to each action. If they need to be, add them to 'data' as well.

This can help work around errors like: `the source file 'main.c' includes the following non-builtin files with absolute paths (if these are builtin files, make sure these paths are in your toolchain)`. | `None` | | allowlist_absolute_include_directories | (List[str]) Allowlists absolute include directories, preventing Bazel from emitting errors when an #include of local system files in the directory occurs. Be careful when adding directories to this list, as it inherently causes leaks in hermeticity. Prefer to reserve use of this for cases like Xcode, where the conventional expectation is to allowlist well-known system-absolute include paths rather than redistributing the SDK. | `None` | | args | (List[str]) The command-line arguments that are applied by using this rule. This is mutually exclusive with [nested](#cc_args-nested). | `None` | @@ -722,7 +722,7 @@ cc_tool( | Name | Description | Default Value | | :------------- | :------------- | :------------- | | name | (str) The name of the target. | none | -| src | (Label) The underlying binary that this tool represents. Usually just a single prebuilt (e.g. @toolchain//:bin/clang), but may be any executable label. | `None` | +| src | (Label) The underlying binary that this tool represents. Usually just a single prebuilt (e.g. @toolchain//:bin/clang), but may be any executable label. | none | | data | (List[Label]) Additional files that are required for this tool to run. Frequently, clang and gcc require additional files to execute as they often shell out to other binaries (e.g. `cc1`). | `None` | | allowlist_include_directories | (List[Label]) Include paths implied by using this tool. Compilers may include a set of built-in headers that are implicitly available unless flags like `-nostdinc` are provided. Bazel checks that all included headers are properly provided by a dependency or allowlisted through this mechanism.

As a rule of thumb, only use this if Bazel is complaining about absolute paths in your toolchain and you've ensured that the toolchain is compiling with the `-no-canonical-prefixes` and/or `-fno-canonical-system-headers` arguments.

These files are not automatically passed to each action. If they need to be, add them to 'data' as well.

This can help work around errors like: `the source file 'main.c' includes the following non-builtin files with absolute paths (if these are builtin files, make sure these paths are in your toolchain)`. | `None` | | env | (Dict[str, str]) Environment variables to apply when running this tool. Format expansion is performed on values using `format`. | `None` | @@ -872,7 +872,7 @@ Generated rules:

 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/stubs.toml b/stubs.toml new file mode 100644 index 000000000..07af7ad57 --- /dev/null +++ b/stubs.toml @@ -0,0 +1,22 @@ +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/system_library.bzl" = "cc/system_library.bzli" +"cc/toolchains/cc_toolchain_info.bzl" = "cc/toolchains/cc_toolchain_info.bzli" +"cc/toolchains/directory_tool.bzl" = "cc/toolchains/directory_tool.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/legacy_file_group.bzl" = "cc/toolchains/legacy_file_group.bzli" +"cc/toolchains/nested_args.bzl" = "cc/toolchains/nested_args.bzli" +"cc/toolchains/tool.bzl" = "cc/toolchains/tool.bzli" +"cc/toolchains/tool_map.bzl" = "cc/toolchains/tool_map.bzli" diff --git a/sty.toml b/sty.toml new file mode 100644 index 000000000..262be1c77 --- /dev/null +++ b/sty.toml @@ -0,0 +1,22 @@ +[analysis] +strict-generic-narrowing = true +require-valid-suppressions = true + +[rules] +missing-parameter-annotation = "error" +missing-return-annotation = "error" +missing-type-argument = "error" +unsound-assignment = "error" +unsound-return-statement = "error" +explicit-any = "error" +non-static-type = "error" +blanket-ignore-comment = "error" +possibly-unresolved-reference = "error" +possibly-missing-attribute = "error" +division-by-zero = "error" + +[[stub-packages]] +manifest = "stubs.toml" + +[[stub-packages]] +manifest = "@bazel_skylib//:stubs.toml" From 8dadf5c2e03cb5c6575097c43a9762a6e3bb64bc Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 09:49:54 -0500 Subject: [PATCH 55/61] Keep toolchain contracts beside their source Move four companion declarations into the implementations they describe. Keep the same strict sources and let the shared action select the complete advisory inventory. --- .github/workflows/sty.yaml | 6 ++---- cc/private/toolchain/unix_cc_configure.bzl | 4 ++-- cc/toolchains/legacy_file_group.bzl | 12 ++++++++++++ cc/toolchains/legacy_file_group.bzli | 12 ------------ stubs.toml | 1 - .../cc/testutil/toolchains/additional_toolchains.bzl | 4 +++- .../testutil/toolchains/additional_toolchains.bzli | 3 --- .../action_type_sets/action_type_set_diff_test.bzl | 5 +++++ .../action_type_sets/action_type_set_diff_test.bzli | 5 ----- .../tool/execution_requirements.bzli | 3 --- 10 files changed, 24 insertions(+), 31 deletions(-) delete mode 100644 cc/toolchains/legacy_file_group.bzli delete mode 100644 tests/cc/testutil/toolchains/additional_toolchains.bzli delete mode 100644 tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli delete mode 100644 tests/rule_based_toolchain/tool/execution_requirements.bzli diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml index d4db35f21..6a1e3930f 100644 --- a/.github/workflows/sty.yaml +++ b/.github/workflows/sty.yaml @@ -20,13 +20,11 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false - - name: List maintained Starlark implementations - run: git ls-files -- '*.bzl' > "$RUNNER_TEMP/sty-files" - - uses: tamird/sty@2ab74c830937d3ca85165ca0e9a02a42400ad9e5 + - uses: tamird/sty@d400a00807b3c7f7b2ac82bf1e1e6a16db0290e3 with: audit-loads: true bazel-version: '9.3.0' - advisory-files-from: ${{ runner.temp }}/sty-files + 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: | diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index c093cdc73..89615b709 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -552,11 +552,11 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): bin_search_flags = ["-B" + escape_percent(str(cc_path.dirname))] else: # cc is inside the repository, don't set -B. - bin_search_flags = [] # type: list[string] + 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" + escape_string(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: diff --git a/cc/toolchains/legacy_file_group.bzl b/cc/toolchains/legacy_file_group.bzl index 48d289a69..a1f184417 100644 --- a/cc/toolchains/legacy_file_group.bzl +++ b/cc/toolchains/legacy_file_group.bzl @@ -16,6 +16,18 @@ # Taken from https://bazel.build/docs/cc-toolchain-config-reference#actions # TODO: This is best-effort. Update this with the correct file groups once we # work out what actions correspond to what file groups. +# TODO(https://github.com/bazelbuild/bazel/issues/31770): Use native key types +# once all supported Bazel versions can express them. +# type: dict[{ +# "ar_files": list[Label], +# "as_files": list[Label], +# "compiler_files": list[Label], +# "coverage_files": list[Label], +# "dwp_files": list[Label], +# "linker_files": list[Label], +# "objcopy_files": list[Label], +# "strip_files": list[Label], +# }] LEGACY_FILE_GROUPS = { "ar_files": [ Label("//cc/toolchains/actions:ar_actions"), diff --git a/cc/toolchains/legacy_file_group.bzli b/cc/toolchains/legacy_file_group.bzli deleted file mode 100644 index 7268adcbb..000000000 --- a/cc/toolchains/legacy_file_group.bzli +++ /dev/null @@ -1,12 +0,0 @@ -"""Action labels grouped by the legacy attributes of cc_toolchain.""" - -LEGACY_FILE_GROUPS: dict[{ - "ar_files": list[Label], - "as_files": list[Label], - "compiler_files": list[Label], - "coverage_files": list[Label], - "dwp_files": list[Label], - "linker_files": list[Label], - "objcopy_files": list[Label], - "strip_files": list[Label], -}] diff --git a/stubs.toml b/stubs.toml index 07af7ad57..b3a24c8c6 100644 --- a/stubs.toml +++ b/stubs.toml @@ -16,7 +16,6 @@ format-version = 1 "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/legacy_file_group.bzl" = "cc/toolchains/legacy_file_group.bzli" "cc/toolchains/nested_args.bzl" = "cc/toolchains/nested_args.bzli" "cc/toolchains/tool.bzl" = "cc/toolchains/tool.bzli" "cc/toolchains/tool_map.bzl" = "cc/toolchains/tool_map.bzli" diff --git a/tests/cc/testutil/toolchains/additional_toolchains.bzl b/tests/cc/testutil/toolchains/additional_toolchains.bzl index ea7c8419f..0b2f7c67d 100644 --- a/tests/cc/testutil/toolchains/additional_toolchains.bzl +++ b/tests/cc/testutil/toolchains/additional_toolchains.bzl @@ -4,4 +4,6 @@ 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/additional_toolchains.bzli b/tests/cc/testutil/toolchains/additional_toolchains.bzli deleted file mode 100644 index ce7ebffe8..000000000 --- a/tests/cc/testutil/toolchains/additional_toolchains.bzli +++ /dev/null @@ -1,3 +0,0 @@ -"""Additional mock toolchain labels used by the C++ analysis tests.""" - -ADDITIONAL_MOCK_TOOLCHAINS: list[str] diff --git a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl index 4d4b25653..59aadcfe9 100644 --- a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl +++ b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzl @@ -40,7 +40,12 @@ _generate_action_type_set = rule( }, ) +# TODO(https://github.com/bazelbuild/bazel/pull/30373): Use native type syntax +# when Bazel 8.8 is the minimum supported version. +# type _LabelInput = str | Label | select[str | Label] + def action_type_set_diff_test(name, action_type_set, expected): + # type: (str, _LabelInput, _LabelInput) -> None output_filename = name + ".actual.txt" _generate_action_type_set( name = name + "_actual", diff --git a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli b/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli deleted file mode 100644 index 54f9273eb..000000000 --- a/tests/rule_based_toolchain/action_type_sets/action_type_set_diff_test.bzli +++ /dev/null @@ -1,5 +0,0 @@ -"""Action set golden file test declarations.""" - -type _LabelInput = str | Label | select[str | Label] - -def action_type_set_diff_test(name: str, action_type_set: _LabelInput, expected: _LabelInput) -> None: ... diff --git a/tests/rule_based_toolchain/tool/execution_requirements.bzli b/tests/rule_based_toolchain/tool/execution_requirements.bzli deleted file mode 100644 index 4384459a0..000000000 --- a/tests/rule_based_toolchain/tool/execution_requirements.bzli +++ /dev/null @@ -1,3 +0,0 @@ -"""Execution requirements supplied to the toolchain tests.""" - -load("//cc/toolchains:providers.bzl", "ExecutionRequirementsInfo") From 8eec445be9f4ad4c6904946e0b0a10a3c4712410 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 10:29:17 -0500 Subject: [PATCH 56/61] Move tool and repository types into source Declare each macro's inputs at its definition. Derive the directory tool's forwarded keywords from cc_tool so both macros share one contract. Check the repository rule at its implementation. Remove four companion interfaces and their registrations. Document which generic binders and cross-module types the others require. --- .github/workflows/sty.yaml | 4 +++ cc/BUILD | 1 - cc/cc_postmark_initializers.bzli | 3 ++ cc/cc_toolchain_config_lib.bzli | 3 ++ cc/private/objc_info.bzli | 3 ++ cc/private/toolchain/lib_cc_configure.bzli | 3 ++ cc/private/toolchain/unix_cc_configure.bzli | 3 ++ .../toolchain_config/legacy_features.bzli | 3 ++ cc/system_library.bzl | 14 ++++++++ cc/system_library.bzli | 27 --------------- cc/toolchains/BUILD | 3 -- cc/toolchains/directory_tool.bzl | 23 ++++++++++++- cc/toolchains/directory_tool.bzli | 17 ---------- cc/toolchains/impl/collect.bzli | 3 ++ cc/toolchains/impl/legacy_converter.bzli | 3 ++ cc/toolchains/impl/nested_args.bzli | 3 ++ cc/toolchains/impl/variables.bzli | 3 ++ cc/toolchains/nested_args.bzl | 22 ++++++++++++ cc/toolchains/nested_args.bzli | 23 ------------- cc/toolchains/tool.bzl | 30 ++++++++++++++++ cc/toolchains/tool.bzli | 34 ------------------- cc/toolchains/tool_map.bzli | 3 ++ stubs.toml | 4 --- 23 files changed, 125 insertions(+), 110 deletions(-) delete mode 100644 cc/system_library.bzli delete mode 100644 cc/toolchains/directory_tool.bzli delete mode 100644 cc/toolchains/nested_args.bzli delete mode 100644 cc/toolchains/tool.bzli diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml index 6a1e3930f..505e15ad6 100644 --- a/.github/workflows/sty.yaml +++ b/.github/workflows/sty.yaml @@ -56,6 +56,7 @@ jobs: 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 @@ -66,6 +67,7 @@ jobs: 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 @@ -84,8 +86,10 @@ jobs: 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 diff --git a/cc/BUILD b/cc/BUILD index d9c897f69..1b8a32c65 100644 --- a/cc/BUILD +++ b/cc/BUILD @@ -32,7 +32,6 @@ filegroup( exports_files([ "cc_postmark_initializers.bzli", "cc_toolchain_config_lib.bzli", - "system_library.bzli", "defs.bzl", "action_names.bzl", "system_library.bzl", diff --git a/cc/cc_postmark_initializers.bzli b/cc/cc_postmark_initializers.bzli index fa7ae1b89..39f75ad5c 100644 --- a/cc/cc_postmark_initializers.bzli +++ b/cc/cc_postmark_initializers.bzli @@ -1,4 +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_toolchain_config_lib.bzli b/cc/cc_toolchain_config_lib.bzli index 62bcd0067..6ceb58f41 100644 --- a/cc/cc_toolchain_config_lib.bzli +++ b/cc/cc_toolchain_config_lib.bzli @@ -16,6 +16,9 @@ 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( diff --git a/cc/private/objc_info.bzli b/cc/private/objc_info.bzli index fb43100f4..55be7d23b 100644 --- a/cc/private/objc_info.bzli +++ b/cc/private/objc_info.bzli @@ -1,5 +1,8 @@ """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] = ..., diff --git a/cc/private/toolchain/lib_cc_configure.bzli b/cc/private/toolchain/lib_cc_configure.bzli index 82430005c..621ec3f45 100644 --- a/cc/private/toolchain/lib_cc_configure.bzli +++ b/cc/private/toolchain/lib_cc_configure.bzli @@ -1,5 +1,8 @@ """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]( diff --git a/cc/private/toolchain/unix_cc_configure.bzli b/cc/private/toolchain/unix_cc_configure.bzli index 8243cb829..fc4e3078e 100644 --- a/cc/private/toolchain/unix_cc_configure.bzli +++ b/cc/private/toolchain/unix_cc_configure.bzli @@ -1,5 +1,8 @@ """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]: ... diff --git a/cc/private/toolchain_config/legacy_features.bzli b/cc/private/toolchain_config/legacy_features.bzli index c7fed2788..f6ff42b45 100644 --- a/cc/private/toolchain_config/legacy_features.bzli +++ b/cc/private/toolchain_config/legacy_features.bzli @@ -13,6 +13,9 @@ # 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 diff --git a/cc/system_library.bzl b/cc/system_library.bzl index f0cdbee71..12c720321 100644 --- a/cc/system_library.bzl +++ b/cc/system_library.bzl @@ -136,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 diff --git a/cc/system_library.bzli b/cc/system_library.bzli deleted file mode 100644 index 090f53d7a..000000000 --- a/cc/system_library.bzli +++ /dev/null @@ -1,27 +0,0 @@ -# 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. -"""System library and header discovery.""" - -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: repository_ctx[_Attrs]) -> repo_metadata | None: ... diff --git a/cc/toolchains/BUILD b/cc/toolchains/BUILD index 2d40c8c2d..ba57417d3 100644 --- a/cc/toolchains/BUILD +++ b/cc/toolchains/BUILD @@ -17,9 +17,6 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") exports_files( [ "cc_toolchain_info.bzli", - "directory_tool.bzli", - "nested_args.bzli", - "tool.bzli", "tool_map.bzli", ], visibility = ["//visibility:public"], diff --git a/cc/toolchains/directory_tool.bzl b/cc/toolchains/directory_tool.bzl index b4572e737..76391cae2 100644 --- a/cc/toolchains/directory_tool.bzl +++ b/cc/toolchains/directory_tool.bzl @@ -16,7 +16,28 @@ load("@bazel_skylib//rules/directory:glob.bzl", "directory_glob") load(":tool.bzl", "cc_tool") -def cc_directory_tool(name, directory, executable, data = [], exclude = [], allow_empty = False, **kwargs): +# TODO(https://github.com/bazelbuild/bazel/issues/32343): Type **kwargs on the +# declaration when Bazel can refer to the remaining keywords of cc_tool. +# type _Strings = Collection[str] | depset[str] +# type _StringsInput = _Strings | select[_Strings | None] | None +# type _Bool = bool | Literal[0, 1] + +def cc_directory_tool( + # type: str + name, + # type: str | Label | select[str | Label | None] + directory, + # type: str + executable, + # type: _StringsInput + data = [], + # type: _StringsInput + exclude = [], + # type: _Bool | select[_Bool | None] | None + allow_empty = False, + # type: Unpack[KeywordArgs[cc_tool, "name", "src", "data"]] + **kwargs): + # type: (...) -> None """A tool extracted from a directory. Args: diff --git a/cc/toolchains/directory_tool.bzli b/cc/toolchains/directory_tool.bzli deleted file mode 100644 index 9804c209f..000000000 --- a/cc/toolchains/directory_tool.bzli +++ /dev/null @@ -1,17 +0,0 @@ -"""Tools extracted from directories.""" - -load("//cc/toolchains:tool.bzl", _cc_tool = "cc_tool") - -type _Strings = Collection[str] | depset[str] -type _StringsInput = _Strings | select[_Strings | None] | None -type _Bool = bool | Literal[0, 1] - -def cc_directory_tool( - name: str, - directory: str | Label | select[str | Label | None], - executable: str, - data: _StringsInput = ..., - exclude: _StringsInput = ..., - allow_empty: _Bool | select[_Bool | None] | None = ..., - **kwargs: Unpack[KeywordArgs[_cc_tool, "name", "src", "data"]], -) -> None: ... diff --git a/cc/toolchains/impl/collect.bzli b/cc/toolchains/impl/collect.bzli index 4943c37d4..ac74ac156 100644 --- a/cc/toolchains/impl/collect.bzli +++ b/cc/toolchains/impl/collect.bzli @@ -1,5 +1,8 @@ """Typed collections of toolchain provider values.""" +# TODO(https://github.com/bazelbuild/starlark/pull/362): Move collect_provider into source when a function declaration can bind +# P to its provider and result. A comment cannot bind P or declare the collector protocol. + load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ArgsInfo", "ArgsListInfo", "ToolInfo") type _ActionAccumulator = struct[{"action": ActionTypeInfo, "args": list[ArgsInfo], "transitive_files": list[depset[File]]}] diff --git a/cc/toolchains/impl/legacy_converter.bzli b/cc/toolchains/impl/legacy_converter.bzli index facd81898..732f727cf 100644 --- a/cc/toolchains/impl/legacy_converter.bzli +++ b/cc/toolchains/impl/legacy_converter.bzli @@ -1,5 +1,8 @@ """Conversion from rule-based toolchain providers to legacy configuration values.""" +# TODO(https://github.com/bazelbuild/bazel/issues/32207): Move these signatures into source when the native provider declarations +# expose their field types. The converter imports both sets of types from interfaces. + load("//cc:cc_toolchain_config_lib.bzli", "ActionConfigInfo", "ArtifactNamePatternInfo", "EnvSetInfo", "FeatureInfo", "FlagSetInfo", "MakeVariableInfo", "ToolInfo", "ToolPathInfo", "WithFeatureSetInfo") load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ArgsInfo", "FeatureConstraintInfo", "ToolConfigInfo", "ToolCapabilityInfo", "ToolchainConfigInfo", NewFeatureInfo = "FeatureInfo", NewToolInfo = "ToolInfo") diff --git a/cc/toolchains/impl/nested_args.bzli b/cc/toolchains/impl/nested_args.bzli index e2b6fb6b8..44e9b0469 100644 --- a/cc/toolchains/impl/nested_args.bzli +++ b/cc/toolchains/impl/nested_args.bzli @@ -1,5 +1,8 @@ """Formatting and validation of nested toolchain arguments.""" +# TODO(https://github.com/bazelbuild/starlark/pull/362): Move the nested-argument signatures into source when functions can bind +# the target type used by their attribute schema. A source comment cannot bind T. + load("//cc/toolchains:cc_toolchain_info.bzli", "NestedArgsInfo") type _Fail = Callable[[str], None] diff --git a/cc/toolchains/impl/variables.bzli b/cc/toolchains/impl/variables.bzli index 566b3ab12..b0f269512 100644 --- a/cc/toolchains/impl/variables.bzli +++ b/cc/toolchains/impl/variables.bzli @@ -1,5 +1,8 @@ """Toolchain variable descriptors.""" +# TODO(https://github.com/bazelbuild/bazel/issues/32044): Move the descriptors into source when variable types can refer to their +# recursive element type there. The factory also accepts named fields through its callable protocol. + load( "//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", diff --git a/cc/toolchains/nested_args.bzl b/cc/toolchains/nested_args.bzl index d81dd998b..6a86dccdc 100644 --- a/cc/toolchains/nested_args.bzl +++ b/cc/toolchains/nested_args.bzl @@ -41,21 +41,43 @@ Examples: """, ) +# TODO(https://github.com/bazelbuild/bazel/issues/32343): Annotate **kwargs in the +# declaration when Bazel can type the common rule attributes forwarded here. +# type _Label = str | Label +# type _LabelInput = _Label | select[_Label | None] | None +# type _StringListInput = Collection[str] | depset[str] | select[Collection[str] | depset[str] | None] | None +# type _LabelListInput = Collection[_Label] | depset[_Label] | select[Collection[_Label] | depset[_Label] | None] | None +# type _StringInput = str | select[str | None] | None + def cc_nested_args( *, + # type: str name, + # type: _StringListInput args = None, + # type: _LabelListInput data = None, + # type: Mapping[str, _Label] format = {}, + # type: _LabelInput iterate_over = None, + # type: _LabelListInput nested = None, + # type: _LabelInput requires_not_none = None, + # type: _LabelInput requires_none = None, + # type: _LabelInput requires_true = None, + # type: _LabelInput requires_false = None, + # type: _LabelInput requires_equal = None, + # type: _StringInput requires_equal_value = None, + # type: Unpack[CommonRuleAttrs] **kwargs): + # type: (...) -> None """Nested arguments for use in more complex `cc_args` expansions. While this rule is very similar in shape to `cc_args`, it is intended to be used as a diff --git a/cc/toolchains/nested_args.bzli b/cc/toolchains/nested_args.bzli deleted file mode 100644 index 7eac8bda5..000000000 --- a/cc/toolchains/nested_args.bzli +++ /dev/null @@ -1,23 +0,0 @@ -"""Nested command-line arguments.""" - -type _Label = str | Label -type _LabelInput = _Label | select[_Label | None] | None -type _ListInput[T] = Collection[T] | depset[T] | select[Collection[T] | depset[T] | None] | None -type _StringInput = str | select[str | None] | None - -def cc_nested_args( - *, - name: str, - args: _ListInput[str] = ..., - data: _ListInput[_Label] = ..., - format: Mapping[str, _Label] = ..., - iterate_over: _LabelInput = ..., - nested: _ListInput[_Label] = ..., - requires_not_none: _LabelInput = ..., - requires_none: _LabelInput = ..., - requires_true: _LabelInput = ..., - requires_false: _LabelInput = ..., - requires_equal: _LabelInput = ..., - requires_equal_value: _StringInput = ..., - **kwargs: Unpack[CommonRuleAttrs], -) -> None: ... diff --git a/cc/toolchains/tool.bzl b/cc/toolchains/tool.bzl index 3fb958fd4..419dd437a 100644 --- a/cc/toolchains/tool.bzl +++ b/cc/toolchains/tool.bzl @@ -24,7 +24,27 @@ load( "ToolInfo", ) +# TODO(https://github.com/bazelbuild/bazel/issues/32343): Annotate **kwargs in the +# declaration when Bazel can type the forwarded executable-rule attributes. +# type _LabelInput = str | Label | select[str | Label | None] +# type _Labels = Collection[str | Label] | depset[str | Label] +# type _LabelsInput = _Labels | select[_Labels | None] | None +# type _EnvironmentInput = Mapping[str, str] | select[Mapping[str, str] | None] | None +# type _Attrs = struct[{ +# "src": Target, +# "data": Sequence[Target], +# "format_keys": list[str], +# "format_values": Sequence[Target], +# "format_value_indexes": list[int], +# "env": dict[str, str], +# "execution_requirements": Sequence[Target], +# "tags": list[str], +# "allowlist_include_directories": Sequence[Target], +# "capabilities": Sequence[Target], +# }, ...] + def _cc_tool_impl(ctx): + # type: (ctx[object, _Attrs]) -> list[ToolInfo | DefaultInfo] exe_info = ctx.attr.src[DefaultInfo] if exe_info.files_to_run != None and exe_info.files_to_run.executable != None: exe = exe_info.files_to_run.executable @@ -190,15 +210,25 @@ cc_tool( def cc_tool( *, + # type: str name, + # type: _LabelInput src, + # type: _LabelsInput data = None, + # type: _LabelsInput allowlist_include_directories = None, + # type: _EnvironmentInput env = None, + # type: _LabelsInput execution_requirements = None, + # type: Mapping[str, str | Label] format = {}, + # type: _LabelsInput capabilities = None, + # type: Unpack[CommonExecutableRuleAttrs] **kwargs): + # type: (...) -> None """Declares a tool for use by toolchain actions. `cc_tool` rules are used in a `cc_tool_map` rule to ensure all files and diff --git a/cc/toolchains/tool.bzli b/cc/toolchains/tool.bzli deleted file mode 100644 index 6cfceca24..000000000 --- a/cc/toolchains/tool.bzli +++ /dev/null @@ -1,34 +0,0 @@ -"""Tools, their execution inputs, and forwarded rule attributes.""" - -load("//cc/toolchains:cc_toolchain_info.bzli", "ToolInfo") - -type _LabelInput = str | Label | select[str | Label | None] -type _Labels = Collection[str | Label] | depset[str | Label] -type _LabelsInput = _Labels | select[_Labels | None] | None -type _EnvironmentInput = Mapping[str, str] | select[Mapping[str, str] | None] | None -type _Attrs = struct[{ - "src": Target, - "data": Sequence[Target], - "format_keys": list[str], - "format_values": Sequence[Target], - "format_value_indexes": list[int], - "env": dict[str, str], - "execution_requirements": Sequence[Target], - "tags": list[str], - "allowlist_include_directories": Sequence[Target], - "capabilities": Sequence[Target], -}, ...] - -def _cc_tool_impl(ctx: ctx[object, _Attrs]) -> list[ToolInfo | DefaultInfo]: ... -def cc_tool( - *, - name: str, - src: _LabelInput, - data: _LabelsInput = ..., - allowlist_include_directories: _LabelsInput = ..., - env: _EnvironmentInput = ..., - execution_requirements: _LabelsInput = ..., - format: Mapping[str, str | Label] = ..., - capabilities: _LabelsInput = ..., - **kwargs: Unpack[CommonExecutableRuleAttrs], -) -> None: ... diff --git a/cc/toolchains/tool_map.bzli b/cc/toolchains/tool_map.bzli index 91a62af81..fd7997cfc 100644 --- a/cc/toolchains/tool_map.bzli +++ b/cc/toolchains/tool_map.bzli @@ -1,5 +1,8 @@ """The association between toolchain actions and executable tools.""" +# TODO(https://github.com/bazelbuild/starlark/pull/362): Move cc_tool_map into source when its signature can bind K to the +# key type of the caller’s tools dictionary. A signature comment cannot bind K. + load("//cc/toolchains:cc_toolchain_info.bzli", "ActionTypeInfo", "ActionTypeSetInfo", "ToolConfigInfo", "ToolInfo") type _Attrs = struct[{ diff --git a/stubs.toml b/stubs.toml index b3a24c8c6..f1b4eff0f 100644 --- a/stubs.toml +++ b/stubs.toml @@ -9,13 +9,9 @@ format-version = 1 "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/system_library.bzl" = "cc/system_library.bzli" "cc/toolchains/cc_toolchain_info.bzl" = "cc/toolchains/cc_toolchain_info.bzli" -"cc/toolchains/directory_tool.bzl" = "cc/toolchains/directory_tool.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/nested_args.bzl" = "cc/toolchains/nested_args.bzli" -"cc/toolchains/tool.bzl" = "cc/toolchains/tool.bzli" "cc/toolchains/tool_map.bzl" = "cc/toolchains/tool_map.bzli" From d3c9dc8362636beffc2588141a29f12a25ffba79 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 10:38:38 -0500 Subject: [PATCH 57/61] Document the type source for forwarded keywords Link the three wrapper signatures to the Bazel proposal for deriving keyword arguments from a wrapped rule. --- cc/cc_import.bzl | 2 ++ cc/cc_shared_library.bzl | 2 ++ cc/starlark_doc_extract_helper.bzl | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cc/cc_import.bzl b/cc/cc_import.bzl index 2292ef1f2..822250a9f 100644 --- a/cc/cc_import.bzl +++ b/cc/cc_import.bzl @@ -15,6 +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_shared_library.bzl b/cc/cc_shared_library.bzl index d66fea01e..fe1580b36 100644 --- a/cc/cc_shared_library.bzl +++ b/cc/cc_shared_library.bzl @@ -15,6 +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/starlark_doc_extract_helper.bzl b/cc/starlark_doc_extract_helper.bzl index 55460b24d..fc96c726d 100644 --- a/cc/starlark_doc_extract_helper.bzl +++ b/cc/starlark_doc_extract_helper.bzl @@ -2,6 +2,8 @@ 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 From 99a0753cdf7e9ade5809fc1a065334df75fc0bab Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 10:50:56 -0500 Subject: [PATCH 58/61] Explain the source annotation limits Identify the native types each exceptional comment needs. Document the language boundary beside its use. --- cc/cc_postmark.bzl | 3 +++ sty.toml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cc/cc_postmark.bzl b/cc/cc_postmark.bzl index 28c225706..eed942ead 100644 --- a/cc/cc_postmark.bzl +++ b/cc/cc_postmark.bzl @@ -24,6 +24,9 @@ def _add_postmark_action( # 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 diff --git a/sty.toml b/sty.toml index 262be1c77..cb4dce718 100644 --- a/sty.toml +++ b/sty.toml @@ -1,3 +1,6 @@ +# 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 From 826ec97ddd6542ce1501df62c4ee83ff965b8738 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 10:50:57 -0500 Subject: [PATCH 59/61] Reference sty by release tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the repository’s release-tag convention for external actions. Use the same checkout and checker revisions as the previous workflow. --- .github/workflows/sty.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml index 505e15ad6..325c68eba 100644 --- a/.github/workflows/sty.yaml +++ b/.github/workflows/sty.yaml @@ -17,10 +17,10 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v4 with: persist-credentials: false - - uses: tamird/sty@d400a00807b3c7f7b2ac82bf1e1e6a16db0290e3 + - uses: tamird/sty@sty-action-v0.2.0 with: audit-loads: true bazel-version: '9.3.0' From 4f9860e0da3e473e9f5a54976e35b97395180684 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 12:57:51 -0500 Subject: [PATCH 60/61] Select the strict sty rules as a group Set one severity for the eleven strict diagnostics. Let the pinned checker define the group so an upgrade can extend it without edits to the project configuration. --- sty.toml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/sty.toml b/sty.toml index cb4dce718..40cc7da5f 100644 --- a/sty.toml +++ b/sty.toml @@ -6,17 +6,7 @@ strict-generic-narrowing = true require-valid-suppressions = true [rules] -missing-parameter-annotation = "error" -missing-return-annotation = "error" -missing-type-argument = "error" -unsound-assignment = "error" -unsound-return-statement = "error" -explicit-any = "error" -non-static-type = "error" -blanket-ignore-comment = "error" -possibly-unresolved-reference = "error" -possibly-missing-attribute = "error" -division-by-zero = "error" +strict = "error" [[stub-packages]] manifest = "stubs.toml" From bbec9dfcba1604fa7c42810edec3b617a62f872f Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 3 Nov 2026 13:40:16 -0500 Subject: [PATCH 61/61] Check rules_cc against released Skylib Since 60dc620ac, the root module replaces Skylib with a fork to get type information. Bazel also uses that fork for ordinary builds. Declare the build macro, path helpers and two providers against Skylib 1.9.0. Check their signatures against the installed sources. Report incomplete types in three files from the release while checking their contracts and callers as errors. --- .github/workflows/sty.yaml | 6 ++++- MODULE.bazel | 7 ------ stubs/bazel_skylib/bzl_library.bzli | 5 ++++ stubs/bazel_skylib/common_settings.bzli | 3 +++ stubs/bazel_skylib/directory_providers.bzli | 26 +++++++++++++++++++++ stubs/bazel_skylib/paths.bzli | 12 ++++++++++ stubs/bazel_skylib/stubs.toml | 12 ++++++++++ sty.toml | 23 +++++++++++++++++- 8 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 stubs/bazel_skylib/bzl_library.bzli create mode 100644 stubs/bazel_skylib/common_settings.bzli create mode 100644 stubs/bazel_skylib/directory_providers.bzli create mode 100644 stubs/bazel_skylib/paths.bzli create mode 100644 stubs/bazel_skylib/stubs.toml diff --git a/.github/workflows/sty.yaml b/.github/workflows/sty.yaml index 325c68eba..50fff9056 100644 --- a/.github/workflows/sty.yaml +++ b/.github/workflows/sty.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: tamird/sty@sty-action-v0.2.0 + - uses: tamird/sty@95e2b424a7f52fa0b0946da98eb2cc328e9b4107 with: audit-loads: true bazel-version: '9.3.0' @@ -28,6 +28,10 @@ jobs: 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 diff --git a/MODULE.bazel b/MODULE.bazel index d6b1394a9..6d274608c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -53,10 +53,3 @@ use_repo(compat, "cc_compatibility_proxy") local_bazel_import = use_repo_rule("//:local_bazel.bzl", "local_bazel_import") local_bazel_import(name = "local_bazel") - -# Use provider contracts while bazelbuild/bazel-skylib#641 is in review. -git_override( - module_name = "bazel_skylib", - commit = "98d84dda6a754a8ee2fb99397cbdb690f98f56a0", - remote = "https://github.com/tamird/bazel-skylib.git", -) 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 index 40cc7da5f..0edcbec44 100644 --- a/sty.toml +++ b/sty.toml @@ -8,8 +8,29 @@ 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 = "@bazel_skylib//:stubs.toml" +manifest = "stubs/bazel_skylib/stubs.toml"