Merge branch 'main' into rules-based-toolchain-example
diff --git a/cc/common/BUILD b/cc/common/BUILD
index 2af8526..fdb3921 100644
--- a/cc/common/BUILD
+++ b/cc/common/BUILD
@@ -25,6 +25,24 @@
name = "cc_helper_bzl",
srcs = ["cc_helper.bzl"],
visibility = ["//visibility:public"],
+ deps = [":visibility_bzl"],
+)
+
+bzl_library(
+ name = "cc_debug_helper_bzl",
+ srcs = ["cc_debug_helper.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":cc_helper_bzl",
+ ":visibility_bzl",
+ "//cc:find_cc_toolchain_bzl",
+ ],
+)
+
+bzl_library(
+ name = "visibility_bzl",
+ srcs = ["visibility.bzl"],
+ visibility = ["//visibility:private"],
)
filegroup(
diff --git a/cc/common/cc_debug_helper.bzl b/cc/common/cc_debug_helper.bzl
new file mode 100644
index 0000000..d16e925
--- /dev/null
+++ b/cc/common/cc_debug_helper.bzl
@@ -0,0 +1,181 @@
+# 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.
+"""Utilities for creating cc debug package information outputs"""
+
+load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE")
+load(":cc_helper.bzl", "linker_mode")
+load(":visibility.bzl", "INTERNAL_VISIBILITY")
+
+visibility(INTERNAL_VISIBILITY)
+
+def create_debug_packager_actions(
+ ctx,
+ cc_toolchain,
+ dwp_output,
+ *,
+ cc_compilation_outputs,
+ cc_debug_context,
+ linking_mode,
+ use_pic = True,
+ lto_artifacts = []):
+ """Creates intermediate and final dwp creation action(s)
+
+ Args:
+ ctx: (RuleContext) the rule context
+ cc_toolchain: (CcToolchainInfo) the cc toolchain
+ dwp_output: (File) the output of the final dwp action
+ cc_compilation_outputs: (CcCompilationOutputs)
+ cc_debug_context: (DebugContext)
+ linking_mode: (str) See cc_helper.bzl%linker_mode
+ use_pic: (bool)
+ lto_artifacts: ([CcLtoBackendArtifacts])
+ """
+ dwo_files = _collect_transitive_dwo_artifacts(
+ cc_compilation_outputs,
+ cc_debug_context,
+ linking_mode,
+ use_pic,
+ lto_artifacts,
+ )
+
+ # No inputs? Just generate a trivially empty .dwp.
+ #
+ # Note this condition automatically triggers for any build where fission is disabled.
+ # Because rules referencing .dwp targets may be invoked with or without fission, we need
+ # to support .dwp generation even when fission is disabled. Since no actual functionality
+ # is expected then, an empty file is appropriate.
+ dwo_files_list = dwo_files.to_list()
+ if len(dwo_files_list) == 0:
+ ctx.actions.write(dwp_output, "", False)
+ return
+
+ # We apply a hierarchical action structure to limit the maximum number of inputs to any
+ # single action.
+ #
+ # While the dwp tool consumes .dwo files, it can also consume intermediate .dwp files,
+ # allowing us to split a large input set into smaller batches of arbitrary size and order.
+ # Aside from the parallelism performance benefits this offers, this also reduces input
+ # size requirements: if a.dwo, b.dwo, c.dwo, and e.dwo are each 1 KB files, we can apply
+ # two intermediate actions DWP(a.dwo, b.dwo) --> i1.dwp and DWP(c.dwo, e.dwo) --> i2.dwp.
+ # When we then apply the final action DWP(i1.dwp, i2.dwp) --> finalOutput.dwp, the inputs
+ # to this action will usually total far less than 4 KB.
+ #
+ # The actions form an n-ary tree with n == MAX_INPUTS_PER_DWP_ACTION. The tree is fuller
+ # at the leaves than the root, but that both increases parallelism and reduces the final
+ # action's input size.
+ packager = _create_intermediate_dwp_packagers(ctx, dwp_output, cc_toolchain, cc_toolchain._dwp_files, dwo_files_list, 1)
+ packager["outputs"].append(dwp_output)
+ packager["arguments"].add("-o", dwp_output)
+ ctx.actions.run(
+ mnemonic = "CcGenerateDwp",
+ tools = packager["tools"],
+ executable = packager["executable"],
+ toolchain = CC_TOOLCHAIN_TYPE,
+ arguments = [packager["arguments"]],
+ inputs = packager["inputs"],
+ outputs = packager["outputs"],
+ )
+
+def _collect_transitive_dwo_artifacts(cc_compilation_outputs, cc_debug_context, linking_mode, use_pic, lto_backend_artifacts):
+ dwo_files = []
+ transitive_dwo_files = depset()
+ if use_pic:
+ dwo_files.extend(cc_compilation_outputs.pic_dwo_files())
+ else:
+ dwo_files.extend(cc_compilation_outputs.dwo_files())
+
+ if lto_backend_artifacts != None:
+ for lto_backend_artifact in lto_backend_artifacts:
+ if lto_backend_artifact.dwo_file() != None:
+ dwo_files.append(lto_backend_artifact.dwo_file())
+
+ if linking_mode != linker_mode.LINKING_DYNAMIC:
+ if use_pic:
+ transitive_dwo_files = cc_debug_context.pic_files
+ else:
+ transitive_dwo_files = cc_debug_context.files
+ return depset(dwo_files, transitive = [transitive_dwo_files])
+
+def _get_intermediate_dwp_file(ctx, dwp_output, order_number):
+ output_path = dwp_output.short_path
+
+ # Since it is a dwp_output we can assume that it always
+ # ends with .dwp suffix, because it is declared so in outputs
+ # attribute.
+ extension_stripped_output_path = output_path[0:len(output_path) - 4]
+ intermediate_path = extension_stripped_output_path + "-" + str(order_number) + ".dwp"
+
+ return ctx.actions.declare_file("_dwps/" + intermediate_path)
+
+def _create_intermediate_dwp_packagers(ctx, dwp_output, cc_toolchain, dwp_files, dwo_files, intermediate_dwp_count):
+ intermediate_outputs = dwo_files
+
+ # This long loop is a substitution for recursion, which is not currently supported in Starlark.
+ for _ in range(2147483647):
+ packagers = []
+ current_packager = _new_dwp_action(ctx, cc_toolchain, dwp_files)
+ inputs_for_current_packager = 0
+
+ # Step 1: generate our batches. We currently break into arbitrary batches of fixed maximum
+ # input counts, but we can always apply more intelligent heuristics if the need arises.
+ for dwo_file in intermediate_outputs:
+ if inputs_for_current_packager == 100:
+ packagers.append(current_packager)
+ current_packager = _new_dwp_action(ctx, cc_toolchain, dwp_files)
+ inputs_for_current_packager = 0
+ current_packager["inputs"].append(dwo_file)
+
+ # add_all expands all directories to their contained files, see
+ # https://bazel.build/rules/lib/builtins/Args#add_all. add doesn't
+ # do that, so using add_all on the one-item list here allows us to
+ # find dwo files in directories.
+ current_packager["arguments"].add_all([dwo_file])
+ inputs_for_current_packager += 1
+
+ packagers.append(current_packager)
+
+ # Step 2: given the batches, create the actions.
+ if len(packagers) > 1:
+ # If we have multiple batches, make them all intermediate actions, then pipe their outputs
+ # into an additional level.
+ intermediate_outputs = []
+ for packager in packagers:
+ intermediate_output = _get_intermediate_dwp_file(ctx, dwp_output, intermediate_dwp_count)
+ intermediate_dwp_count += 1
+ packager["outputs"].append(intermediate_output)
+ packager["arguments"].add("-o", intermediate_output)
+ ctx.actions.run(
+ mnemonic = "CcGenerateIntermediateDwp",
+ tools = packager["tools"],
+ executable = packager["executable"],
+ toolchain = CC_TOOLCHAIN_TYPE,
+ arguments = [packager["arguments"]],
+ inputs = packager["inputs"],
+ outputs = packager["outputs"],
+ )
+ intermediate_outputs.append(intermediate_output)
+ else:
+ return packagers[0]
+
+ # This is to fix buildifier errors, even though we should never reach this part of the code.
+ return None
+
+def _new_dwp_action(ctx, cc_toolchain, dwp_tools):
+ return {
+ "arguments": ctx.actions.args(),
+ "executable": cc_toolchain._tool_paths.get("dwp", None),
+ "inputs": [],
+ "outputs": [],
+ "tools": dwp_tools,
+ }
diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl
index 8b161a1..35f6812 100644
--- a/cc/common/cc_helper.bzl
+++ b/cc/common/cc_helper.bzl
@@ -11,10 +11,82 @@
# 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.
-
"""Utility functions for C++ rules."""
-# LINT.IfChange
+load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE")
+load(":cc_common.bzl", "cc_common")
+load(":visibility.bzl", "INTERNAL_VISIBILITY")
+
+visibility(INTERNAL_VISIBILITY)
+
+# LINT.IfChange(linker_mode)
+linker_mode = struct(
+ LINKING_DYNAMIC = "dynamic_linking_mode",
+ LINKING_STATIC = "static_linking_mode",
+)
+# LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl:linker_mode)
+
+# LINT.IfChange(forked_exports)
+def _get_static_mode_params_for_dynamic_library_libraries(libs):
+ linker_inputs = []
+ for lib in libs.to_list():
+ if lib.pic_static_library:
+ linker_inputs.append(lib.pic_static_library)
+ elif lib.static_library:
+ linker_inputs.append(lib.static_library)
+ elif lib.interface_library:
+ linker_inputs.append(lib.interface_library)
+ else:
+ linker_inputs.append(lib.dynamic_library)
+ return linker_inputs
+
+def _create_strip_action(ctx, cc_toolchain, cpp_config, input, output, feature_configuration):
+ if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "no_stripping"):
+ ctx.actions.symlink(
+ output = output,
+ target_file = input,
+ progress_message = "Symlinking original binary as stripped binary",
+ )
+ return
+
+ if not cc_common.action_is_enabled(feature_configuration = feature_configuration, action_name = "strip"):
+ fail("Expected action_config for 'strip' to be configured.")
+
+ variables = cc_common.create_compile_variables(
+ cc_toolchain = cc_toolchain,
+ feature_configuration = feature_configuration,
+ output_file = output.path,
+ input_file = input.path,
+ strip_opts = cpp_config.strip_opts(),
+ )
+ command_line = cc_common.get_memory_inefficient_command_line(
+ feature_configuration = feature_configuration,
+ action_name = "strip",
+ variables = variables,
+ )
+ env = cc_common.get_environment_variables(
+ feature_configuration = feature_configuration,
+ action_name = "strip",
+ variables = variables,
+ )
+ execution_info = {}
+ for execution_requirement in cc_common.get_tool_requirement_for_action(feature_configuration = feature_configuration, action_name = "strip"):
+ execution_info[execution_requirement] = ""
+ ctx.actions.run(
+ inputs = depset(
+ direct = [input],
+ transitive = [cc_toolchain._strip_files],
+ ),
+ outputs = [output],
+ use_default_shell_env = True,
+ env = env,
+ executable = cc_common.get_tool_for_action(feature_configuration = feature_configuration, action_name = "strip"),
+ toolchain = CC_TOOLCHAIN_TYPE,
+ execution_requirements = execution_info,
+ progress_message = "Stripping {} for {}".format(output.short_path, ctx.label),
+ mnemonic = "CcStrip",
+ arguments = command_line,
+ )
def _lookup_var(ctx, additional_vars, var):
expanded_make_var_ctx = ctx.var.get(var)
@@ -195,9 +267,29 @@
if force_token or len(token) > 0:
options.append("".join(token))
+def _should_use_pic(ctx, cc_toolchain, feature_configuration):
+ """Whether to use pic files
+
+ Args:
+ ctx: (RuleContext)
+ cc_toolchain: (CcToolchainInfo)
+ feature_configuration: (FeatureConfiguration)
+
+ Returns:
+ (bool)
+ """
+ return ctx.fragments.cpp.force_pic() or (
+ cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) and (
+ ctx.var["COMPILATION_MODE"] != "opt" or
+ cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "prefer_pic_for_opt_binaries")
+ )
+ )
+
cc_helper = struct(
+ create_strip_action = _create_strip_action,
get_expanded_env = _get_expanded_env,
+ get_static_mode_params_for_dynamic_library_libraries = _get_static_mode_params_for_dynamic_library_libraries,
+ should_use_pic = _should_use_pic,
tokenize = _tokenize,
)
-
-# LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl)
+# LINT.ThenChange(https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl:forked_exports)
diff --git a/cc/common/visibility.bzl b/cc/common/visibility.bzl
new file mode 100644
index 0000000..dfbc8ce
--- /dev/null
+++ b/cc/common/visibility.bzl
@@ -0,0 +1,3 @@
+"""Bzl load visibility package specs"""
+
+INTERNAL_VISIBILITY = ["//cc/..."]
diff --git a/cc/toolchains/args/runtime_library_search_directories/BUILD b/cc/toolchains/args/runtime_library_search_directories/BUILD
index a6bc392..2b980bc 100644
--- a/cc/toolchains/args/runtime_library_search_directories/BUILD
+++ b/cc/toolchains/args/runtime_library_search_directories/BUILD
@@ -77,7 +77,7 @@
"-Xlinker",
] + select({
"@platforms//os:macos": ["@loader_path/{search_path}"],
- "//conditions:default": ["$EXEC_ORIGIN/{search_path}"],
+ "//conditions:default": ["$ORIGIN/{search_path}"],
}),
format = {
"search_path": "//cc/toolchains/variables:runtime_library_search_directories",
@@ -113,7 +113,7 @@
"-Xlinker",
] + select({
"@platforms//os:macos": ["@loader_path/{search_path}"],
- "//conditions:default": ["$EXEC_ORIGIN/{search_path}"],
+ "//conditions:default": ["$ORIGIN/{search_path}"],
}),
format = {
"search_path": "//cc/toolchains/variables:runtime_library_search_directories",
diff --git a/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/runtime_library_search_directories.textproto b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/runtime_library_search_directories.textproto
index 1c69c14..8618b47 100644
--- a/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/runtime_library_search_directories.textproto
+++ b/tests/rule_based_toolchain/legacy_features_as_args/goldens/unix/runtime_library_search_directories.textproto
@@ -21,7 +21,7 @@
flags: "-Xlinker"
flags: "-rpath"
flags: "-Xlinker"
- flags: "$EXEC_ORIGIN/%{runtime_library_search_directories}"
+ flags: "$ORIGIN/%{runtime_library_search_directories}"
}
iterate_over: "runtime_library_search_directories"
}
@@ -43,7 +43,7 @@
flags: "-Xlinker"
flags: "-rpath"
flags: "-Xlinker"
- flags: "$EXEC_ORIGIN/%{runtime_library_search_directories}"
+ flags: "$ORIGIN/%{runtime_library_search_directories}"
iterate_over: "runtime_library_search_directories"
}
}