aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVinh Tran <vinhdaitran@google.com>2023-10-06 17:18:35 -0400
committerGitHub <noreply@github.com>2023-10-06 23:18:35 +0200
commit8b548d2432d3958e69093c7a0eb5d5550e067347 (patch)
treeaa0501642e785d285aed75f904a360b998a7145c
parenta4587571817d3e920f78c877e4a372f73dd38062 (diff)
downloadbazelbuild-rules_rust-8b548d2432d3958e69093c7a0eb5d5550e067347.tar.gz
Refactor CrateInfo construction (#2161)
## Description This PR addresses https://github.com/bazelbuild/rules_rust/issues/2013 to ensure rust-analyzer can access all the env vars the rust rules pass to `rustc` at compile time. This is a large refactoring in almost all the rules and aspects so I aim this PR to just fix `rust_library` rule. The follow-up PR will address the remaining rules and aspects. ## Summary * Create `create_crate_info_dict` function in `rust/private/utils.bzl` to create a mutable dict repsenting CrateInfo * Move `_determine_lib_name`, `get_edition`, `_transform_sources` functions to `rust/private/utils.bzl` to avoid cyclic dependency when `create_crate_info_dict` function use these * Introduce optional `create_crate_info_callback` attribute to `rustc_compile_action` to allow creating `CrateInfo` inside `rustc_compile_action`. This optional attribute allows scoping the refactoring in this PR to just `rust_library` rule. * Introduce optional `skip_expanding_rustc_env` attribute to `rustc_compile_action` to skip expanding `rustc_env` attr. This is useful for `clippy` aspect and `rust_test` rule because the `CrateInfo` provided from the depended `rust_library` already expands all the env vars before returning the provider downstream. ## Notes * `rustc_env_attr` is a temporary field in `CrateInfo` and needed by the rules and aspects not migrated to create `CrateInfo` in `rustc_compile_action` yet. It will be remove in the next PR after `CrateInfo.rustc_env` is always expanded. * `create_crate_info_callback` will be removed from `rustc_compile_action` after all `CrateInfo`s are created inside `rustc_compile_action`. --------- Co-authored-by: scentini <rosica@google.com>
-rw-r--r--docs/flatten.md3
-rw-r--r--docs/providers.md3
-rw-r--r--proto/prost/private/prost.bzl1
-rw-r--r--proto/protobuf/proto.bzl1
-rw-r--r--rust/private/clippy.bzl1
-rw-r--r--rust/private/providers.bzl2
-rw-r--r--rust/private/rust.bzl201
-rw-r--r--rust/private/rust_analyzer.bzl2
-rw-r--r--rust/private/rustc.bzl52
-rw-r--r--rust/private/rustdoc.bzl2
-rw-r--r--rust/private/rustdoc_test.bzl1
-rw-r--r--rust/private/utils.bzl189
-rw-r--r--test/unit/consistent_crate_name/with_modified_crate_name.bzl1
-rw-r--r--test/unit/force_all_deps_direct/generator.bzl1
-rw-r--r--test/unit/pipelined_compilation/wrap.bzl1
15 files changed, 279 insertions, 182 deletions
diff --git a/docs/flatten.md b/docs/flatten.md
index 30411043..330d743b 100644
--- a/docs/flatten.md
+++ b/docs/flatten.md
@@ -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.
@@ -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>
diff --git a/docs/providers.md b/docs/providers.md
index c97ad9bc..e553ffb9 100644
--- a/docs/providers.md
+++ b/docs/providers.md
@@ -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.
@@ -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>
diff --git a/proto/prost/private/prost.bzl b/proto/prost/private/prost.bzl
index 6cd3d522..b513b154 100644
--- a/proto/prost/private/prost.bzl
+++ b/proto/prost/private/prost.bzl
@@ -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,
diff --git a/proto/protobuf/proto.bzl b/proto/protobuf/proto.bzl
index ca2df8a2..2fcc1db5 100644
--- a/proto/protobuf/proto.bzl
+++ b/proto/protobuf/proto.bzl
@@ -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", [])),
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index 0f1f35ab..d124c71f 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -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:
diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl
index e4db9c9f..b2fced74 100644
--- a/rust/private/providers.bzl
+++ b/rust/private/providers.bzl
@@ -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.",
},
)
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 014d8d0d..d736fc69 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -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.
@@ -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.
@@ -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
@@ -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):
@@ -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,
@@ -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,
@@ -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:
@@ -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(
@@ -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,
@@ -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(
@@ -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),
@@ -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),
@@ -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", [])
diff --git a/rust/private/rust_analyzer.bzl b/rust/private/rust_analyzer.bzl
index 3eea6c08..323944db 100644
--- a/rust/private/rust_analyzer.bzl
+++ b/rust/private/rust_analyzer.bzl
@@ -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),
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 501e1403..6ae6f30c 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -787,7 +787,8 @@ def construct_arguments(
remap_path_prefix = "",
use_json_output = False,
build_metadata = False,
- force_depend_on_objects = False):
+ force_depend_on_objects = False,
+ skip_expanding_rustc_env = False):
"""Builds an Args object containing common rustc flags
Args:
@@ -817,6 +818,7 @@ def construct_arguments(
use_json_output (bool): Have rustc emit json and process_wrapper parse json messages to output rendered output.
build_metadata (bool): Generate CLI arguments for building *only* .rmeta files. This requires use_json_output.
force_depend_on_objects (bool): Force using `.rlib` object files instead of metadata (`.rmeta`) files even if they are available.
+ skip_expanding_rustc_env (bool): Whether to skip expanding CrateInfo.rustc_env_attr
Returns:
tuple: A tuple of the following items
@@ -1027,11 +1029,14 @@ def construct_arguments(
env.update(toolchain.env)
# Update environment with user provided variables.
- env.update(expand_dict_value_locations(
- ctx,
- crate_info.rustc_env,
- data_paths,
- ))
+ if skip_expanding_rustc_env:
+ env.update(crate_info.rustc_env)
+ else:
+ env.update(expand_dict_value_locations(
+ ctx,
+ crate_info._rustc_env_attr,
+ data_paths,
+ ))
# Ensure the sysroot is set for the target platform
env["SYSROOT"] = toolchain.sysroot
@@ -1079,21 +1084,29 @@ def rustc_compile_action(
ctx,
attr,
toolchain,
- crate_info,
- output_hash = None,
rust_flags = [],
- force_all_deps_direct = False):
+ crate_type = None,
+ crate_info = None,
+ output_hash = None,
+ force_all_deps_direct = False,
+ # TODO: Remove create_crate_info_callback and skip_expanding_rustc_env attributes
+ # after all CrateInfo structs are constructed in rustc_compile_action
+ create_crate_info_callback = None,
+ skip_expanding_rustc_env = False):
"""Create and run a rustc compile action based on the current rule's attributes
Args:
ctx (ctx): The rule's context object
attr (struct): Attributes to use for the rust compile action
toolchain (rust_toolchain): The current `rust_toolchain`
+ crate_type: TODO
crate_info (CrateInfo): The CrateInfo provider for the current target.
output_hash (str, optional): The hashed path of the crate root. Defaults to None.
rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
+ create_crate_info_callback: A callback to construct a mutable dict for constructor CrateInfo
+ skip_expanding_rustc_env (bool, optional): Whether to expand CrateInfo.rustc_env
Returns:
list: A list of the following providers:
@@ -1101,6 +1114,18 @@ def rustc_compile_action(
- (DepInfo): The transitive dependencies of this crate.
- (DefaultInfo): The output file for this crate, and its runfiles.
"""
+ # TODO: Remove create_crate_info_callback after all rustc_compile_action callers migrate to
+ # removing CrateInfo construction before `rust_compile_action
+
+ crate_info_dict = None
+ if create_crate_info_callback != None:
+ if ctx == None or toolchain == None or crate_type == None or crate_info != None:
+ fail("FAIL", ctx, toolchain, crate_type)
+ crate_info_dict = create_crate_info_callback(ctx, toolchain, crate_type)
+
+ if crate_info_dict != None:
+ crate_info = rust_common.create_crate_info(**crate_info_dict)
+
build_metadata = getattr(crate_info, "metadata", None)
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
@@ -1181,6 +1206,7 @@ def rustc_compile_action(
force_all_deps_direct = force_all_deps_direct,
stamp = stamp,
use_json_output = bool(build_metadata),
+ skip_expanding_rustc_env = skip_expanding_rustc_env,
)
args_metadata = None
@@ -1210,6 +1236,8 @@ def rustc_compile_action(
)
env = dict(ctx.configuration.default_shell_env)
+
+ # this is the final list of env vars
env.update(env_from_args)
if hasattr(attr, "version") and attr.version != "0.0.0":
@@ -1421,6 +1449,12 @@ def rustc_compile_action(
),
]
+ if crate_info_dict != None:
+ crate_info_dict.update({
+ "rustc_env": env,
+ })
+ crate_info = rust_common.create_crate_info(**crate_info_dict)
+
if crate_info.type in ["staticlib", "cdylib"]:
# These rules are not supposed to be depended on by other rust targets, and
# as such they shouldn't provide a CrateInfo. However, one may still want to
diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl
index f7d9985e..dac24b3e 100644
--- a/rust/private/rustdoc.bzl
+++ b/rust/private/rustdoc.bzl
@@ -39,6 +39,7 @@ def _strip_crate_info_output(crate_info):
output = None,
metadata = None,
edition = crate_info.edition,
+ _rustc_env_attr = crate_info._rustc_env_attr,
rustc_env = crate_info.rustc_env,
rustc_env_files = crate_info.rustc_env_files,
is_test = crate_info.is_test,
@@ -125,6 +126,7 @@ def rustdoc_compile_action(
remap_path_prefix = None,
rustdoc = True,
force_depend_on_objects = is_test,
+ skip_expanding_rustc_env = True,
)
# Because rustdoc tests compile tests outside of the sandbox, the sysroot
diff --git a/rust/private/rustdoc_test.bzl b/rust/private/rustdoc_test.bzl
index 79b0f4dd..f580c27a 100644
--- a/rust/private/rustdoc_test.bzl
+++ b/rust/private/rustdoc_test.bzl
@@ -122,6 +122,7 @@ def _rust_doc_test_impl(ctx):
output = crate.output,
edition = crate.edition,
rustc_env = crate.rustc_env,
+ _rustc_env_attr = crate._rustc_env_attr,
rustc_env_files = crate.rustc_env_files,
is_test = True,
compile_data = crate.compile_data,
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index 879e2b62..ddee229b 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -14,6 +14,7 @@
"""Utility functions not specific to the rust toolchain."""
+load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
load(":providers.bzl", "BuildInfo", "CrateGroupInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
@@ -725,3 +726,191 @@ def _shortest_src_with_basename(srcs, basename):
if not shortest or len(f.dirname) < len(shortest.dirname):
shortest = f
return shortest
+
+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 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 get_edition(attr, toolchain, label):
+ """Returns the Rust edition from either the current rule's attributes 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 create_crate_info_dict(ctx, toolchain, crate_type):
+ """Creates a mutable dict() representing CrateInfo provider
+
+ create_crate_info_dict is a *temporary* solution until create_crate_info is completely moved into
+ rustc_compile_action function.
+
+ The function is currently used as a callback to support constructing CrateInfo in rustc_compile_action
+ to ensure `CrateInfo.rustc_env` is fully loaded with all the env vars passed to rustc.
+
+ Args:
+ ctx (struct): The current rule's context
+ toolchain (toolchain): The rust toolchain
+ crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
+
+ Returns:
+ File: The created symlink if a non-generated file, or the file itself.
+ """
+ crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
+ 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)
+
+ if crate_type in ["cdylib", "staticlib"]:
+ output_hash = None
+ else:
+ output_hash = determine_output_hash(crate_root, ctx.label)
+
+ deps = transform_deps(ctx.attr.deps)
+ proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
+ 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,
+ )
+
+ return dict(
+ 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,
+ _rustc_env_attr = ctx.attr.rustc_env,
+ )
diff --git a/test/unit/consistent_crate_name/with_modified_crate_name.bzl b/test/unit/consistent_crate_name/with_modified_crate_name.bzl
index f06ddab5..335ec128 100644
--- a/test/unit/consistent_crate_name/with_modified_crate_name.bzl
+++ b/test/unit/consistent_crate_name/with_modified_crate_name.bzl
@@ -49,6 +49,7 @@ def _with_modified_crate_name_impl(ctx):
compile_data = depset([]),
compile_data_targets = depset([]),
rustc_env = {},
+ _rustc_env_attr = {},
is_test = False,
),
output_hash = output_hash,
diff --git a/test/unit/force_all_deps_direct/generator.bzl b/test/unit/force_all_deps_direct/generator.bzl
index 571b47e8..f56d09c2 100644
--- a/test/unit/force_all_deps_direct/generator.bzl
+++ b/test/unit/force_all_deps_direct/generator.bzl
@@ -66,6 +66,7 @@ EOF
compile_data = depset([]),
compile_data_targets = depset([]),
rustc_env = {},
+ _rustc_env_attr = {},
is_test = False,
),
output_hash = output_hash,
diff --git a/test/unit/pipelined_compilation/wrap.bzl b/test/unit/pipelined_compilation/wrap.bzl
index 8c09b4bc..b00f761a 100644
--- a/test/unit/pipelined_compilation/wrap.bzl
+++ b/test/unit/pipelined_compilation/wrap.bzl
@@ -76,6 +76,7 @@ EOF
compile_data = depset([]),
compile_data_targets = depset([]),
rustc_env = {},
+ _rustc_env_attr = {},
is_test = False,
),
output_hash = output_hash,