Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor CrateInfo construction #2161

Merged
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
3 changes: 2 additions & 1 deletion docs/flatten.md
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ A toolchain for [rustfmt](https://rust-lang.github.io/rustfmt/)
<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_data</a>, <a href="#CrateInfo-compile_data_targets">compile_data_targets</a>, <a href="#CrateInfo-data">data</a>, <a href="#CrateInfo-deps">deps</a>, <a href="#CrateInfo-edition">edition</a>, <a href="#CrateInfo-is_test">is_test</a>, <a href="#CrateInfo-metadata">metadata</a>, <a href="#CrateInfo-name">name</a>,
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>,
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>, <a href="#CrateInfo-_rustc_env_attr">_rustc_env_attr</a>)
</pre>

A provider containing general Crate information.
Expand Down Expand Up @@ -1478,6 +1478,7 @@ A provider containing general Crate information.
| <a id="CrateInfo-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
| <a id="CrateInfo-type"></a>type | str: The type of this crate (see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit)). |
| <a id="CrateInfo-wrapped_crate_type"></a>wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the <code>rust_test::crate</code> attribute) |
| <a id="CrateInfo-_rustc_env_attr"></a>_rustc_env_attr | Dict[String, String]: Additional <code>"key": "value"</code> environment variables to set for rustc. |


<a id="DepInfo"></a>
Expand Down
3 changes: 2 additions & 1 deletion docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<pre>
CrateInfo(<a href="#CrateInfo-aliases">aliases</a>, <a href="#CrateInfo-compile_data">compile_data</a>, <a href="#CrateInfo-compile_data_targets">compile_data_targets</a>, <a href="#CrateInfo-data">data</a>, <a href="#CrateInfo-deps">deps</a>, <a href="#CrateInfo-edition">edition</a>, <a href="#CrateInfo-is_test">is_test</a>, <a href="#CrateInfo-metadata">metadata</a>, <a href="#CrateInfo-name">name</a>,
<a href="#CrateInfo-output">output</a>, <a href="#CrateInfo-owner">owner</a>, <a href="#CrateInfo-proc_macro_deps">proc_macro_deps</a>, <a href="#CrateInfo-root">root</a>, <a href="#CrateInfo-rustc_env">rustc_env</a>, <a href="#CrateInfo-rustc_env_files">rustc_env_files</a>, <a href="#CrateInfo-srcs">srcs</a>, <a href="#CrateInfo-type">type</a>,
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>)
<a href="#CrateInfo-wrapped_crate_type">wrapped_crate_type</a>, <a href="#CrateInfo-_rustc_env_attr">_rustc_env_attr</a>)
</pre>

A provider containing general Crate information.
Expand Down Expand Up @@ -40,6 +40,7 @@ A provider containing general Crate information.
| <a id="CrateInfo-srcs"></a>srcs | depset[File]: All source Files that are part of the crate. |
| <a id="CrateInfo-type"></a>type | str: The type of this crate (see [rustc --crate-type](https://doc.rust-lang.org/rustc/command-line-arguments.html#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit)). |
| <a id="CrateInfo-wrapped_crate_type"></a>wrapped_crate_type | str, optional: The original crate type for targets generated using a previously defined crate (typically tests using the <code>rust_test::crate</code> attribute) |
| <a id="CrateInfo-_rustc_env_attr"></a>_rustc_env_attr | Dict[String, String]: Additional <code>"key": "value"</code> environment variables to set for rustc. |


<a id="DepInfo"></a>
Expand Down
1 change: 1 addition & 0 deletions proto/prost/private/prost.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ def _compile_rust(ctx, attr, crate_name, src, deps, edition):
edition = edition,
is_test = False,
rustc_env = {},
_rustc_env_attr = {},
compile_data = depset([]),
compile_data_targets = depset([]),
owner = ctx.label,
Expand Down
1 change: 1 addition & 0 deletions proto/protobuf/proto.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def _rust_proto_compile(protos, descriptor_sets, imports, crate_name, ctx, is_gr
metadata = rust_metadata,
edition = proto_toolchain.edition,
rustc_env = {},
_rustc_env_attr = {},
is_test = False,
compile_data = depset([target.files for target in getattr(ctx.attr, "compile_data", [])]),
compile_data_targets = depset(getattr(ctx.attr, "compile_data", [])),
Expand Down
1 change: 1 addition & 0 deletions rust/private/clippy.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def _clippy_aspect_impl(target, ctx):
build_env_files = build_env_files,
build_flags_files = build_flags_files,
emit = ["dep-info", "metadata"],
skip_expanding_rustc_env = True,
)

if crate_info.is_test:
Expand Down
2 changes: 2 additions & 0 deletions rust/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ CrateInfo = provider(
"str, optional: The original crate type for targets generated using a previously defined " +
"crate (typically tests using the `rust_test::crate` attribute)"
),
# TODO: Remove `_rustc_env_attr` after refactoring rust_test to only rely on rustc_env
"_rustc_env_attr": "Dict[String, String]: Additional `\"key\": \"value\"` environment variables to set for rustc.",
},
)

Expand Down
201 changes: 31 additions & 170 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,22 @@

"""Rust rule implementations"""

load("@bazel_skylib//lib:paths.bzl", "paths")
load("//rust/private:common.bzl", "rust_common")
load("//rust/private:providers.bzl", "BuildInfo")
load("//rust/private:rustc.bzl", "rustc_compile_action")
load(
"//rust/private:utils.bzl",
"can_build_metadata",
"compute_crate_name",
"crate_root_src",
"create_crate_info_dict",
"dedent",
"determine_output_hash",
"expand_dict_value_locations",
"find_toolchain",
"get_edition",
"get_import_macro_deps",
"transform_deps",
"transform_sources",
)
# TODO(marco): Separate each rule into its own file.

Expand Down Expand Up @@ -66,129 +67,6 @@ def _assert_correct_dep_mapping(ctx):
),
)

def _determine_lib_name(name, crate_type, toolchain, lib_hash = None):
"""See https://github.com/bazelbuild/rules_rust/issues/405

Args:
name (str): The name of the current target
crate_type (str): The `crate_type`
toolchain (rust_toolchain): The current `rust_toolchain`
lib_hash (str, optional): The hashed crate root path

Returns:
str: A unique library name
"""
extension = None
prefix = ""
if crate_type in ("dylib", "cdylib", "proc-macro"):
extension = toolchain.dylib_ext
elif crate_type == "staticlib":
extension = toolchain.staticlib_ext
elif crate_type in ("lib", "rlib"):
# All platforms produce 'rlib' here
extension = ".rlib"
prefix = "lib"
elif crate_type == "bin":
fail("crate_type of 'bin' was detected in a rust_library. Please compile " +
"this crate as a rust_binary instead.")

if not extension:
fail(("Unknown crate_type: {}. If this is a cargo-supported crate type, " +
"please file an issue!").format(crate_type))

prefix = "lib"
if toolchain.target_triple and toolchain.target_os == "windows" and crate_type not in ("lib", "rlib"):
prefix = ""
if toolchain.target_arch == "wasm32" and crate_type == "cdylib":
prefix = ""

return "{prefix}{name}{lib_hash}{extension}".format(
prefix = prefix,
name = name,
lib_hash = "-" + lib_hash if lib_hash else "",
extension = extension,
)

def get_edition(attr, toolchain, label):
"""Returns the Rust edition from either the current rule's attirbutes or the current `rust_toolchain`

Args:
attr (struct): The current rule's attributes
toolchain (rust_toolchain): The `rust_toolchain` for the current target
label (Label): The label of the target being built

Returns:
str: The target Rust edition
"""
if getattr(attr, "edition"):
return attr.edition
elif not toolchain.default_edition:
fail("Attribute `edition` is required for {}.".format(label))
else:
return toolchain.default_edition

def _symlink_for_non_generated_source(ctx, src_file, package_root):
"""Creates and returns a symlink for non-generated source files.

This rule uses the full path to the source files and the rule directory to compute
the relative paths. This is needed, instead of using `short_path`, because of non-generated
source files in external repositories possibly returning relative paths depending on the
current version of Bazel.

Args:
ctx (struct): The current rule's context.
src_file (File): The source file.
package_root (File): The full path to the directory containing the current rule.

Returns:
File: The created symlink if a non-generated file, or the file itself.
"""

if src_file.is_source or src_file.root.path != ctx.bin_dir.path:
src_short_path = paths.relativize(src_file.path, src_file.root.path)
src_symlink = ctx.actions.declare_file(paths.relativize(src_short_path, package_root))
ctx.actions.symlink(
output = src_symlink,
target_file = src_file,
progress_message = "Creating symlink to source file: {}".format(src_file.path),
)
return src_symlink
else:
return src_file

def _transform_sources(ctx, srcs, crate_root):
"""Creates symlinks of the source files if needed.

Rustc assumes that the source files are located next to the crate root.
In case of a mix between generated and non-generated source files, this
we violate this assumption, as part of the sources will be located under
bazel-out/... . In order to allow for targets that contain both generated
and non-generated source files, we generate symlinks for all non-generated
files.

Args:
ctx (struct): The current rule's context.
srcs (List[File]): The sources listed in the `srcs` attribute
crate_root (File): The file specified in the `crate_root` attribute,
if it exists, otherwise None

Returns:
Tuple(List[File], File): The transformed srcs and crate_root
"""
has_generated_sources = len([src for src in srcs if not src.is_source]) > 0

if not has_generated_sources:
return srcs, crate_root

package_root = paths.dirname(paths.join(ctx.label.workspace_root, ctx.build_file_path))
generated_sources = [_symlink_for_non_generated_source(ctx, src, package_root) for src in srcs if src != crate_root]
generated_root = crate_root
if crate_root:
generated_root = _symlink_for_non_generated_source(ctx, crate_root, package_root)
generated_sources.append(generated_root)

return generated_sources, generated_root

def _rust_library_impl(ctx):
"""The implementation of the `rust_library` rule.

Expand Down Expand Up @@ -265,7 +143,7 @@ def _rust_library_common(ctx, crate_type):
crate_root = getattr(ctx.file, "crate_root", None)
if not crate_root:
crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_type)
srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
_, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)

# Determine unique hash for this rlib.
# Note that we don't include a hash for `cdylib` and `staticlib` since they are meant to be consumed externally
Expand All @@ -277,49 +155,13 @@ def _rust_library_common(ctx, crate_type):
else:
output_hash = determine_output_hash(crate_root, ctx.label)

crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
rust_lib_name = _determine_lib_name(
crate_name,
crate_type,
toolchain,
output_hash,
)
rust_lib = ctx.actions.declare_file(rust_lib_name)

rust_metadata = None
if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
rust_metadata = ctx.actions.declare_file(
paths.replace_extension(rust_lib_name, ".rmeta"),
sibling = rust_lib,
)

deps = transform_deps(ctx.attr.deps)
proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))

return rustc_compile_action(
ctx = ctx,
attr = ctx.attr,
toolchain = toolchain,
crate_info = rust_common.create_crate_info(
name = crate_name,
type = crate_type,
root = crate_root,
srcs = depset(srcs),
deps = depset(deps),
proc_macro_deps = depset(proc_macro_deps),
aliases = ctx.attr.aliases,
output = rust_lib,
metadata = rust_metadata,
edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
is_test = False,
data = depset(ctx.files.data),
compile_data = depset(ctx.files.compile_data),
compile_data_targets = depset(ctx.attr.compile_data),
owner = ctx.label,
),
output_hash = output_hash,
crate_type = crate_type,
create_crate_info_callback = create_crate_info_dict,
)

def _rust_binary_impl(ctx):
Expand All @@ -343,7 +185,7 @@ def _rust_binary_impl(ctx):
crate_root = getattr(ctx.file, "crate_root", None)
if not crate_root:
crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, ctx.attr.crate_type)
srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)

return rustc_compile_action(
ctx = ctx,
Expand All @@ -359,6 +201,7 @@ def _rust_binary_impl(ctx):
aliases = ctx.attr.aliases,
output = output,
edition = get_edition(ctx.attr, toolchain, ctx.label),
_rustc_env_attr = ctx.attr.rustc_env,
rustc_env = ctx.attr.rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
is_test = False,
Expand Down Expand Up @@ -399,7 +242,7 @@ def _rust_test_impl(ctx):
),
)

srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
srcs, crate_root = transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))

# Optionally join compile data
if crate.compile_data:
Expand All @@ -411,8 +254,16 @@ def _rust_test_impl(ctx):
else:
compile_data_targets = depset(ctx.attr.compile_data)
rustc_env_files = ctx.files.rustc_env_files + crate.rustc_env_files
rustc_env = dict(crate.rustc_env)
rustc_env.update(**ctx.attr.rustc_env)

rustc_env = dict(crate._rustc_env_attr)

# crate.rustc_env is already expanded upstream in rust_library rule implementation
data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list()
rustc_env.update(expand_dict_value_locations(
ctx,
ctx.attr.rustc_env,
data_paths,
))

# Build the test binary using the dependency's srcs.
crate_info = rust_common.create_crate_info(
Expand All @@ -426,6 +277,7 @@ def _rust_test_impl(ctx):
output = output,
edition = crate.edition,
rustc_env = rustc_env,
_rustc_env_attr = ctx.attr.rustc_env,
rustc_env_files = rustc_env_files,
is_test = True,
compile_data = compile_data,
Expand All @@ -439,7 +291,7 @@ def _rust_test_impl(ctx):
if not crate_root:
crate_root_type = "lib" if ctx.attr.use_libtest_harness else "bin"
crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_root_type)
srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
srcs, crate_root = transform_sources(ctx, ctx.files.srcs, crate_root)

output_hash = determine_output_hash(crate_root, ctx.label)
output = ctx.actions.declare_file(
Expand All @@ -450,6 +302,13 @@ def _rust_test_impl(ctx):
),
)

data_paths = depset(direct = getattr(ctx.attr, "data", [])).to_list()
rustc_env = expand_dict_value_locations(
ctx,
ctx.attr.rustc_env,
data_paths,
)

# Target is a standalone crate. Build the test binary as its own crate.
crate_info = rust_common.create_crate_info(
name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name),
Expand All @@ -461,7 +320,8 @@ def _rust_test_impl(ctx):
aliases = ctx.attr.aliases,
output = output,
edition = get_edition(ctx.attr, toolchain, ctx.label),
rustc_env = ctx.attr.rustc_env,
rustc_env = rustc_env,
_rustc_env_attr = ctx.attr.rustc_env,
rustc_env_files = ctx.files.rustc_env_files,
is_test = True,
compile_data = depset(ctx.files.compile_data),
Expand All @@ -475,6 +335,7 @@ def _rust_test_impl(ctx):
toolchain = toolchain,
crate_info = crate_info,
rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"],
skip_expanding_rustc_env = True,
)
data = getattr(ctx.attr, "data", [])

Expand Down
2 changes: 1 addition & 1 deletion rust/private/rust_analyzer.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def _rust_analyzer_aspect_impl(target, ctx):
rust_analyzer_info = RustAnalyzerInfo(
crate = crate_info,
cfgs = cfgs,
env = getattr(ctx.rule.attr, "rustc_env", {}),
env = crate_info.rustc_env,
deps = dep_infos,
crate_specs = depset(direct = [crate_spec], transitive = [dep.crate_specs for dep in dep_infos]),
proc_macro_dylib_path = find_proc_macro_dylib_path(toolchain, target),
Expand Down
Loading