diff --git a/docs/flatten.md b/docs/flatten.md index 84a2af509f..0cee286bc1 100644 --- a/docs/flatten.md +++ b/docs/flatten.md @@ -1110,9 +1110,9 @@ Run the test with `bazel build //hello_lib:hello_lib_test`.
 rust_toolchain(name, allocator_library, binary_ext, cargo, clippy_driver, debug_info,
-               default_edition, dylib_ext, exec_triple, opt_level, os, rust_doc, rust_lib, rust_std,
-               rustc, rustc_lib, rustc_srcs, rustfmt, staticlib_ext, stdlib_linkflags, target_json,
-               target_triple)
+               default_edition, dylib_ext, exec_triple, llvm_tools, opt_level, os, rust_doc, rust_lib,
+               rust_std, rustc, rustc_lib, rustc_srcs, rustfmt, staticlib_ext, stdlib_linkflags,
+               target_json, target_triple)
 
Declares a Rust toolchain for use. @@ -1170,6 +1170,7 @@ See @rules_rust//rust:repositories.bzl for examples of defining the @rust_cpuX r | default_edition | The edition to use for rust_* rules that don't specify an edition. | String | optional | "2018" | | dylib_ext | The extension for dynamic libraries created from rustc. | String | required | | | exec_triple | The platform triple for the toolchains execution environment. For more details see: https://docs.bazel.build/versions/master/skylark/rules.html#configurations | String | required | | +| llvm_tools | LLVM tools that are shipped with the Rust toolchain. | List of labels | optional | [] | | opt_level | Rustc optimization levels. | Dictionary: String -> String | optional | {"dbg": "0", "fastbuild": "0", "opt": "3"} | | os | The operating system for the current toolchain | String | required | | | rust_doc | The location of the rustdoc binary. Can be a direct source or a filegroup containing one item. | Label | required | | diff --git a/docs/rust_repositories.md b/docs/rust_repositories.md index d912ba7759..a5f4e9ac8e 100644 --- a/docs/rust_repositories.md +++ b/docs/rust_repositories.md @@ -35,9 +35,9 @@ A dedicated filegroup-like rule for Rust stdlib artifacts.
 rust_toolchain(name, allocator_library, binary_ext, cargo, clippy_driver, debug_info,
-               default_edition, dylib_ext, exec_triple, opt_level, os, rust_doc, rust_lib, rust_std,
-               rustc, rustc_lib, rustc_srcs, rustfmt, staticlib_ext, stdlib_linkflags, target_json,
-               target_triple)
+               default_edition, dylib_ext, exec_triple, llvm_tools, opt_level, os, rust_doc, rust_lib,
+               rust_std, rustc, rustc_lib, rustc_srcs, rustfmt, staticlib_ext, stdlib_linkflags,
+               target_json, target_triple)
 
Declares a Rust toolchain for use. @@ -95,6 +95,7 @@ See @rules_rust//rust:repositories.bzl for examples of defining the @rust_cpuX r | default_edition | The edition to use for rust_* rules that don't specify an edition. | String | optional | "2018" | | dylib_ext | The extension for dynamic libraries created from rustc. | String | required | | | exec_triple | The platform triple for the toolchains execution environment. For more details see: https://docs.bazel.build/versions/master/skylark/rules.html#configurations | String | required | | +| llvm_tools | LLVM tools that are shipped with the Rust toolchain. | List of labels | optional | [] | | opt_level | Rustc optimization levels. | Dictionary: String -> String | optional | {"dbg": "0", "fastbuild": "0", "opt": "3"} | | os | The operating system for the current toolchain | String | required | | | rust_doc | The location of the rustdoc binary. Can be a direct source or a filegroup containing one item. | Label | required | | diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 869a7953c4..1fd17dfede 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -380,19 +380,16 @@ def collect_inputs( nolinkstamp_compile_inputs = depset( getattr(files, "data", []) + - [toolchain.rustc] + - toolchain.crosstool_files + ([build_info.rustc_env, build_info.flags] if build_info else []) + ([toolchain.target_json] if toolchain.target_json else []) + ([] if linker_script == None else [linker_script]), transitive = [ - toolchain.rustc_lib, - toolchain.rust_std, linker_depset, crate_info.srcs, dep_info.transitive_crate_outputs, depset(additional_transitive_inputs), crate_info.compile_data, + toolchain.all_files, ], ) @@ -654,7 +651,7 @@ def construct_arguments( data_paths, )) - # Set the SYSROOT to the directory of the rust_std files passed to the toolchain + # Ensure the sysroot is set for the target platform env["SYSROOT"] = toolchain.sysroot # extra_rustc_flags apply to the target configuration, not the exec configuration. diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index 59bf404cd4..89fefefb6b 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -119,6 +119,17 @@ def rustdoc_compile_action( force_link = True, ) + # Because rustdoc tests compile tests outside of the sandbox, the sysroot + # must be updated to the `short_path` equivilant as it will now be + # a part of runfiles. + if is_test: + if "SYSROOT" in env: + env.update({"SYSROOT": "${{pwd}}/{}".format(toolchain.sysroot_short_path)}) + + # `rustdoc` does not support the SYSROOT environment variable. To account + # for this, the flag must be explicitly passed to the `rustdoc` binary. + args.rustc_flags.add("--sysroot=${{pwd}}/{}".format(toolchain.sysroot_short_path)) + return struct( executable = ctx.executable._process_wrapper, inputs = depset([crate_info.output], transitive = [compile_inputs]), diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index b5237b17f5..8577f3dad9 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -210,6 +210,173 @@ def _make_libstd_and_allocator_ccinfo(ctx, rust_std, allocator_library): ) return None +def _symlink_sysroot_tree(ctx, name, target): + """Generate a set of symlinks to files from another target + + Args: + ctx (ctx): The toolchain's context object + name (str): The name of the sysroot directory (typically `ctx.label.name`) + target (Target): A target owning files to symlink + + Returns: + depset[File]: A depset of the generated symlink files + """ + tree_files = [] + for file in target.files.to_list(): + # Parse the path to the file relative to the workspace root so a + # symlink matching this path can be created within the sysroot. + + # The code blow attempts to parse any workspace names out of the + # path. For local targets, this code is a noop. + if target.label.workspace_root: + file_path = file.path.split(target.label.workspace_root, 1)[-1] + else: + file_path = file.path + + symlink = ctx.actions.declare_file("{}/{}".format(name, file_path.lstrip("/"))) + + ctx.actions.symlink( + output = symlink, + target_file = file, + ) + + tree_files.append(symlink) + + return depset(tree_files) + +def _symlink_sysroot_bin(ctx, name, directory, target): + """Crete a symlink to a target file. + + Args: + ctx (ctx): The rule's context object + name (str): A common name for the output directory + directory (str): The directory under `name` to put the file in + target (File): A File object to symlink to + + Returns: + File: A newly generated symlink file + """ + symlink = ctx.actions.declare_file("{}/{}/{}".format( + name, + directory, + target.basename, + )) + + ctx.actions.symlink( + output = symlink, + target_file = target, + is_executable = True, + ) + + return symlink + +def _generate_sysroot( + ctx, + rustc, + rustdoc, + rustc_lib, + cargo = None, + clippy = None, + llvm_tools = None, + rust_std = None, + rustfmt = None): + """Generate a rust sysroot from collection of toolchain components + + Args: + ctx (ctx): A context object from a `rust_toolchain` rule. + rustc (File): The path to a `rustc` executable. + rustdoc (File): The path to a `rustdoc` executable. + rustc_lib (Target): A collection of Files containing dependencies of `rustc`. + cargo (File, optional): The path to a `cargo` executable. + clippy (File, optional): The path to a `clippy-driver` executable. + llvm_tools (Target, optional): A collection of llvm tools used by `rustc`. + rust_std (Target, optional): A collection of Files containing Rust standard library components. + rustfmt (File, optional): The path to a `rustfmt` executable. + + Returns: + struct: A struct of generated files representing the new sysroot + """ + name = ctx.label.name + + # Define runfiles + direct_files = [] + transitive_file_sets = [] + + # Rustc + sysroot_rustc = _symlink_sysroot_bin(ctx, name, "bin", rustc) + direct_files.extend([sysroot_rustc, rustc]) + + # Rustc dependencies + sysroot_rustc_lib = None + if rustc_lib: + sysroot_rustc_lib = _symlink_sysroot_tree(ctx, name, rustc_lib) + transitive_file_sets.extend([sysroot_rustc_lib, rustc_lib.files]) + + # Rustdoc + sysroot_rustdoc = _symlink_sysroot_bin(ctx, name, "bin", rustdoc) + direct_files.extend([sysroot_rustdoc, rustdoc]) + + # Clippy + sysroot_clippy = None + if clippy: + sysroot_clippy = _symlink_sysroot_bin(ctx, name, "bin", clippy) + direct_files.extend([sysroot_clippy, clippy]) + + # Cargo + sysroot_cargo = None + if cargo: + sysroot_cargo = _symlink_sysroot_bin(ctx, name, "bin", cargo) + direct_files.extend([sysroot_cargo, cargo]) + + # Rustfmt + sysroot_rustfmt = None + if rustfmt: + sysroot_rustfmt = _symlink_sysroot_bin(ctx, name, "bin", rustfmt) + direct_files.extend([sysroot_rustfmt, rustfmt]) + + # Llvm tools + sysroot_llvm_tools = None + if llvm_tools: + sysroot_llvm_tools = _symlink_sysroot_tree(ctx, name, llvm_tools) + transitive_file_sets.extend([sysroot_llvm_tools, llvm_tools.files]) + + # Rust standard library + sysroot_rust_std = None + if rust_std: + sysroot_rust_std = _symlink_sysroot_tree(ctx, name, rust_std) + transitive_file_sets.extend([sysroot_rust_std, rust_std.files]) + + # Declare a file in the root of the sysroot to make locating the sysroot easy + sysroot_anchor = ctx.actions.declare_file("{}/rust.sysroot".format(name)) + ctx.actions.write( + output = sysroot_anchor, + content = "\n".join([ + "cargo: {}".format(cargo), + "clippy: {}".format(clippy), + "llvm_tools: {}".format(llvm_tools), + "rust_std: {}".format(rust_std), + "rustc_lib: {}".format(rustc_lib), + "rustc: {}".format(rustc), + "rustdoc: {}".format(rustdoc), + "rustfmt: {}".format(rustfmt), + ]), + ) + + # Create a depset of all sysroot files (symlinks and their real paths) + all_files = depset(direct_files, transitive = transitive_file_sets) + + return struct( + all_files = all_files, + cargo = sysroot_cargo, + clippy = sysroot_clippy, + rust_std = sysroot_rust_std, + rustc = sysroot_rustc, + rustc_lib = sysroot_rustc_lib, + rustdoc = sysroot_rustdoc, + rustfmt = sysroot_rustfmt, + sysroot_anchor = sysroot_anchor, + ) + def _rust_toolchain_impl(ctx): """The rust_toolchain implementation @@ -243,6 +410,18 @@ def _rust_toolchain_impl(ctx): else: rust_std = ctx.attr.rust_std + sysroot = _generate_sysroot( + ctx = ctx, + rustc = ctx.file.rustc, + rustdoc = ctx.file.rust_doc, + rustc_lib = ctx.attr.rustc_lib, + rust_std = rust_std, + rustfmt = ctx.file.rustfmt, + clippy = ctx.file.clippy_driver, + cargo = ctx.file.cargo, + llvm_tools = ctx.attr.llvm_tools, + ) + expanded_stdlib_linkflags = [] for flag in ctx.attr.stdlib_linkflags: expanded_stdlib_linkflags.append( @@ -265,30 +444,26 @@ def _rust_toolchain_impl(ctx): linking_context = linking_context, ) - # In cases where the toolchain uses the Rust standard library, calculate sysroot path - sysroot_path = None - rust_std_files_list = [] - if rust_std: - # Calculate the rustc sysroot path by using a file from the rust-std bundle - rust_std_files_list = rust_std.files.to_list() - if not rust_std_files_list: - fail("The `rust_std` cannot be represented by an empty list") - sysroot_path = rust_std_files_list[0].dirname + # Determine the path and short_path of the sysroot + sysroot_path = sysroot.sysroot_anchor.dirname + sysroot_short_path, _, _ = sysroot.sysroot_anchor.short_path.rpartition("/") toolchain = platform_common.ToolchainInfo( - rustc = ctx.file.rustc, - rust_doc = ctx.file.rust_doc, - rustfmt = ctx.file.rustfmt, - cargo = ctx.file.cargo, - clippy_driver = ctx.file.clippy_driver, + all_files = sysroot.all_files, + rustc = sysroot.rustc, + rust_doc = sysroot.rustdoc, + rustfmt = sysroot.rustfmt, + cargo = sysroot.cargo, + clippy_driver = sysroot.clippy, target_json = ctx.file.target_json, target_flag_value = ctx.file.target_json.path if ctx.file.target_json else ctx.attr.target_triple, - rustc_lib = depset(ctx.files.rustc_lib), + rustc_lib = sysroot.rustc_lib, rustc_srcs = ctx.attr.rustc_srcs, - rust_std = rust_std.files, - rust_std_paths = depset([file.dirname for file in rust_std_files_list]), - rust_lib = rust_std.files, # `rust_lib` is deprecated and only exists for legacy support. + rust_std = sysroot.rust_std, + rust_std_paths = depset([file.dirname for file in sysroot.rust_std.to_list()]), + rust_lib = sysroot.rust_std, # `rust_lib` is deprecated and only exists for legacy support. sysroot = sysroot_path, + sysroot_short_path = sysroot_short_path, binary_ext = ctx.attr.binary_ext, staticlib_ext = ctx.attr.staticlib_ext, dylib_ext = ctx.attr.dylib_ext, @@ -355,6 +530,10 @@ rust_toolchain = rule( ), mandatory = True, ), + "llvm_tools": attr.label_list( + doc = "LLVM tools that are shipped with the Rust toolchain.", + allow_files = True, + ), "opt_level": attr.string_dict( doc = "Rustc optimization levels.", default = {