Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ tasks:
test_targets: *default_linux_targets
coverage_targets: *default_linux_targets
post_shell_commands: *coverage_validation_post_shell_commands
run_targets:
- //test:build_script_path_mapping_test_binary
linux_docs:
name: Docs
platform: ubuntu2204
Expand Down
22 changes: 20 additions & 2 deletions cargo/private/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("//rust:defs.bzl", "rust_binary")

config_setting(
name = "compilation_mode_dbg",
values = {"compilation_mode": "dbg"},
visibility = ["//visibility:public"],
)

config_setting(
name = "compilation_mode_fastbuild",
values = {"compilation_mode": "fastbuild"},
visibility = ["//visibility:public"],
)

config_setting(
name = "compilation_mode_opt",
values = {"compilation_mode": "opt"},
visibility = ["//visibility:public"],
)

rust_binary(
name = "copy_file",
srcs = ["copy_file.rs"],
Expand All @@ -10,9 +28,9 @@ rust_binary(

bzl_library(
name = "bzl_lib",
srcs = glob(["**/*.bzl"]),
visibility = ["//visibility:public"],
deps = [
"//rust:bzl_lib",
],
srcs = glob(["**/*.bzl"]),
visibility = ["//visibility:public"],
)
58 changes: 34 additions & 24 deletions cargo/private/cargo_build_script.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("//rust:defs.bzl", "rust_common")
load("//rust:rust_common.bzl", "BuildInfo", "CrateGroupInfo", "DepInfo")
load("//rust:rust_common.bzl", "BuildInfo", "DepInfo")

# buildifier: disable=bzl-visibility
load(
Expand Down Expand Up @@ -356,6 +356,7 @@ def _cargo_build_script_impl(ctx):
list: A list containing a BuildInfo provider
"""
script = ctx.executable.script
script_target = ctx.attr.script[0] if type(ctx.attr.script) == "list" else ctx.attr.script
toolchain = find_toolchain(ctx)
out_dir = ctx.actions.declare_directory(ctx.label.name + ".out_dir")
env_out = ctx.actions.declare_file(ctx.label.name + ".env")
Expand All @@ -382,7 +383,7 @@ def _cargo_build_script_impl(ctx):
# https://github.com/bazelbuild/bazel/issues/15486
runfiles_dir, runfiles_inputs, runfiles_args = _create_runfiles_dir(
ctx = ctx,
script = ctx.attr.script,
script = script_target,
data_runfiles = ctx.attr.data_runfiles,
retain_list = ctx.attr._cargo_manifest_dir_filename_suffixes_to_retain[BuildSettingInfo].value,
workspace_name = workspace_name,
Expand Down Expand Up @@ -594,20 +595,8 @@ def _cargo_build_script_impl(ctx):
for dep_build_info in dep[rust_common.dep_info].transitive_build_infos.to_list():
build_script_inputs.append(dep_build_info.out_dir)

for dep in ctx.attr.deps:
dep_infos = []
if DepInfo in dep:
dep_infos = [dep[DepInfo]]
else:
dep_infos = [
dep_variant_info.dep_info
for dep_variant_info in dep[CrateGroupInfo].dep_variant_infos.to_list()
if dep_variant_info.dep_info
]

for dep_info in dep_infos:
for dep_build_info in dep_info.transitive_build_infos.to_list():
build_script_inputs.append(dep_build_info.out_dir)
for dep_build_info in script_target[DepInfo].transitive_build_infos.to_list():
build_script_inputs.append(dep_build_info.out_dir)

experimental_symlink_execroot = ctx.attr._experimental_symlink_execroot[BuildSettingInfo].value or \
_feature_enabled(ctx, "symlink-exec-root")
Expand All @@ -632,7 +621,7 @@ def _cargo_build_script_impl(ctx):
runfiles_dir,
] + extra_output,
tools = [
ctx.attr.script[DefaultInfo].files_to_run,
script_target[DefaultInfo].files_to_run,
tools,
],
inputs = depset(build_script_inputs, transitive = [runfiles_inputs]),
Expand Down Expand Up @@ -663,6 +652,27 @@ def _cargo_build_script_impl(ctx):
),
]

_COMPILATION_MODE = "//command_line_option:compilation_mode"

def _cargo_build_script_compilation_mode_transition_impl(_settings, attr):
return {
_COMPILATION_MODE: attr.target_compilation_mode,
}

_cargo_build_script_compilation_mode_transition = transition(
implementation = _cargo_build_script_compilation_mode_transition_impl,
inputs = [],
outputs = [_COMPILATION_MODE],
)

def _get_cargo_build_script_cfg():
build_script_cfg = config.exec()
if hasattr(build_script_cfg, "and_then"):
build_script_cfg = build_script_cfg.and_then(_cargo_build_script_compilation_mode_transition)
return build_script_cfg

_cargo_build_script_cfg = _get_cargo_build_script_cfg()

cargo_build_script = rule(
doc = (
"A rule for running a crate's `build.rs` files to generate build information " +
Expand Down Expand Up @@ -709,11 +719,6 @@ cargo_build_script = rule(
cfg = "target",
executable = True,
),
"deps": attr.label_list(
doc = "The Rust build-dependencies of the crate",
providers = [[DepInfo], [CrateGroupInfo]],
cfg = "exec",
),
"link_deps": attr.label_list(
doc = dedent("""\
The subset of the Rust (normal) dependencies of the crate that
Expand Down Expand Up @@ -741,14 +746,19 @@ cargo_build_script = rule(
doc = "The binary script to run, generally a `rust_binary` target.",
executable = True,
mandatory = True,
cfg = "exec",
providers = [rust_common.crate_info],
cfg = _cargo_build_script_cfg,
providers = [rust_common.crate_info, DepInfo],
),
"tools": attr.label_list(
doc = "Tools required by the build script.",
allow_files = True,
cfg = "exec",
),
"target_compilation_mode": attr.string(
doc = "The parent target's compilation mode.",
mandatory = True,
values = ["dbg", "fastbuild", "opt"],
),
"use_default_shell_env": attr.int(
doc = dedent("""\
Whether or not to include the default shell environment for the build
Expand Down
8 changes: 7 additions & 1 deletion cargo/private/cargo_build_script_wrapper.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ load(
)
load("//rust:defs.bzl", "rust_binary")

_TARGET_COMPILATION_MODE = select({
str(Label("//cargo/private:compilation_mode_dbg")): "dbg",
str(Label("//cargo/private:compilation_mode_fastbuild")): "fastbuild",
str(Label("//cargo/private:compilation_mode_opt")): "opt",
})

def cargo_build_script(
*,
name,
Expand Down Expand Up @@ -213,14 +219,14 @@ def cargo_build_script(
data_runfiles = ":{}-".format(name),
data = data,
tools = tools,
target_compilation_mode = _TARGET_COMPILATION_MODE,
crate_features = crate_features,
version = version,
allow_build_script_to_detect_nonhermetic_paths = allow_build_script_to_detect_nonhermetic_paths,
build_script_env = build_script_env,
build_script_env_files = build_script_env_files,
use_default_shell_env = sanitized_use_default_shell_env,
links = links,
deps = deps,
link_deps = link_deps,
rustc_flags = rustc_flags,
visibility = visibility,
Expand Down
26 changes: 23 additions & 3 deletions cargo/tests/unit/build_script_deps/build_script_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ DepActionsInfo = provider(
def _collect_dep_actions_aspect_impl(target, ctx):
actions = []
actions.extend(target.actions)
for dep in ctx.rule.attr.deps:
actions.extend(dep[DepActionsInfo].actions)
attr_name = "script" if hasattr(ctx.rule.attr, "script") else "deps"
if hasattr(ctx.rule.attr, attr_name):
deps = getattr(ctx.rule.attr, attr_name)
if type(deps) != "list":
deps = [deps]
for dep in deps:
if DepActionsInfo in dep:
actions.extend(dep[DepActionsInfo].actions)
return [DepActionsInfo(actions = actions)]

collect_dep_actions_aspect = aspect(
implementation = _collect_dep_actions_aspect_impl,
attr_aspects = ["deps"],
attr_aspects = ["deps", "script"],
)

def _outputs_contain(outputs, substring):
Expand Down Expand Up @@ -47,6 +53,14 @@ def _build_script_deps_test_impl(ctx):
("-exec-" in rlib_output.path) or ("-exec/bin/" in rlib_output.path),
"Expected rlib output to be in an exec configuration, but got: {}".format(rlib_output.path),
)
if hasattr(config.exec(), "and_then"):
asserts.true(
env,
"--codegen=opt-level=0" in build_script_deps_action.argv,
"Expected build script dependencies to use fastbuild compilation mode, but got: {}".format(
build_script_deps_action.argv,
),
)
return analysistest.end(env)

build_script_deps_test = analysistest.make(
Expand Down Expand Up @@ -78,6 +92,12 @@ def build_script_test_suite(name):
target_under_test = ":build_script_deps_in_exec_mode",
)

if hasattr(config.exec(), "and_then"):
native.filegroup(
name = "transition_and_then_supported",
tags = ["manual"],
)

native.test_suite(
name = name,
tests = ["build_script_deps_in_exec_mode_test"],
Expand Down
6 changes: 6 additions & 0 deletions test/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")

sh_binary(
name = "build_script_path_mapping_test_binary",
srcs = ["build_script_path_mapping_test.sh"],
data = ["build_script_path_mapping_test.py"],
)

sh_binary(
name = "query_test_binary",
srcs = ["query_test_binary.sh"],
Expand Down
137 changes: 137 additions & 0 deletions test/build_script_path_mapping_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python3

import json
import sys


CRATE_NAME_ARGUMENT = "--crate-name=dep_of_a_build_script"


def path_for_fragment(path_fragments, fragment_id):
labels = []
while fragment_id:
fragment = path_fragments[fragment_id]
labels.append(fragment["label"])
fragment_id = fragment.get("parentId")
return "/".join(reversed(labels))


def artifact_paths(action_graph, artifact_ids):
artifacts = {artifact["id"]: artifact for artifact in action_graph["artifacts"]}
path_fragments = {
fragment["id"]: fragment
for fragment in action_graph["pathFragments"]
}
return sorted(
path_for_fragment(path_fragments, artifacts[artifact_id]["pathFragmentId"])
for artifact_id in artifact_ids
)


def strip_config_segment(path):
segments = path.split("/")
if len(segments) > 2 and segments[0] in ("bazel-out", "blaze-out"):
segments[1] = "cfg"
return "/".join(segments)


def input_paths(action_graph, dep_set_ids):
dep_sets = {dep_set["id"]: dep_set for dep_set in action_graph["depSetOfFiles"]}
artifact_ids = set()
pending = list(dep_set_ids)
while pending:
dep_set = dep_sets[pending.pop()]
artifact_ids.update(dep_set.get("directArtifactIds", []))
pending.extend(dep_set.get("transitiveDepSetIds", []))
return artifact_paths(action_graph, artifact_ids)


def normalized_action(action_graph, action):
# aquery's actionKey preserves the unmodified configuration segments. Compare
# the fields used to construct the path-mapped spawn instead.
return {
"arguments": action.get("arguments", []),
"environmentVariables": sorted(
action.get("environmentVariables", []),
key=lambda variable: (variable["key"], variable.get("value", "")),
),
"executionInfo": sorted(
action.get("executionInfo", []),
key=lambda entry: (entry["key"], entry.get("value", "")),
),
"executionPlatform": action.get("executionPlatform"),
"inputs": sorted(
strip_config_segment(path)
for path in input_paths(action_graph, action.get("inputDepSetIds", []))
),
"outputs": sorted(
strip_config_segment(path)
for path in artifact_paths(action_graph, action.get("outputIds", []))
),
"paramFiles": action.get("paramFiles", []),
}


def process_wrapper_inputs(action_graph, action):
return [
path
for path in input_paths(action_graph, action.get("inputDepSetIds", []))
if path.endswith("/util/process_wrapper/process_wrapper")
]


def fail(message, details=None):
if details is not None:
message += "\n" + json.dumps(details, indent=2, sort_keys=True)
raise AssertionError(message)


def main():
action_graph = json.load(sys.stdin)
crate_actions = [
action
for action in action_graph.get("actions", [])
if CRATE_NAME_ARGUMENT in action.get("arguments", [])
]
# rust_binary adds each dependency to deps and proc_macro_deps so filter_deps
# can select the dependency with the required provider. The proc_macro_deps
# action remains in aquery but is not an input to the build script binary.
actions = [
action
for action in crate_actions
if "--codegen=opt-level=0" in action.get("arguments", [])
]
if len(actions) != 2:
fail("Expected two fastbuild dep_of_a_build_script Rustc actions", crate_actions)

configuration_ids = {action.get("configurationId") for action in actions}
if len(configuration_ids) != 2:
fail("Expected target and exec configurations", actions)

normalized_actions = [
normalized_action(action_graph, action)
for action in actions
]
execution_info = normalized_actions[0]["executionInfo"]
if not any(entry["key"] == "supports-path-mapping" for entry in execution_info):
fail("Expected supports-path-mapping execution info", execution_info)

if normalized_actions[0] != normalized_actions[1]:
fail(
"Expected identical path-mapped target and build-script dependency spawns",
normalized_actions,
)

process_wrappers = [
process_wrapper_inputs(action_graph, action)
for action in actions
]
if process_wrappers[0] != process_wrappers[1]:
fail(
"Expected target and build-script dependencies to use the same process_wrapper",
process_wrappers,
)


if __name__ == "__main__":
main()
Loading
Loading