diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index a08e71c25e..d46242ca28 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -36,6 +36,14 @@ default_windows_targets: &default_windows_targets - "//..." - "-//test/proto/..." - "-//test/unit/pipelined_compilation/..." +default_windows_no_runfiles_targets: &default_windows_no_runfiles_targets + - "--" # Allows negative patterns; hack for https://github.com/bazelbuild/continuous-integration/pull/245 + - "//..." + - "-//crate_universe/..." + - "-//test/proto/..." + - "-//test/unit/pipelined_compilation/..." + - "-//test/unit/rustdoc/..." + - "-//tools/runfiles/..." crate_universe_vendor_example_targets: &crate_universe_vendor_example_targets - "//vendor_external:crates_vendor" - "//vendor_local_manifests:crates_vendor" @@ -78,6 +86,11 @@ tasks: windows: build_targets: *default_windows_targets test_targets: *default_windows_targets + windows_no_runfiles: + build_flags: + - "--noenable_runfiles" + build_targets: *default_windows_no_runfiles_targets + test_targets: *default_windows_no_runfiles_targets ubuntu2004_split_coverage_postprocessing: name: Split Coverage Postprocessing platform: ubuntu2004 @@ -171,6 +184,15 @@ tasks: - "--config=clippy" build_targets: *default_windows_targets test_targets: *default_windows_targets + windows_no_runfiles_with_aspects: + name: No Runfiles With Aspects + platform: windows + build_flags: + - "--noenable_runfiles" + - "--config=rustfmt" + - "--config=clippy" + build_targets: *default_windows_no_runfiles_targets + test_targets: *default_windows_no_runfiles_targets windows_rolling_with_aspects: name: "Windows Rolling Bazel Version With Aspects" platform: windows @@ -610,7 +632,7 @@ tasks: environment: # This ndk version matches with rules_android_ndk repo's CI # https://github.com/bazelbuild/rules_android_ndk/blob/877c68ef34c9f3353028bf490d269230c1990483/.bazelci/presubmit.yml#L37 - # The ndk is installed by this script + # The ndk is installed by this script # https://github.com/bazelbuild/continuous-integration/blob/ba56013373821feadd9f2eaa6b81eb19528795f0/macos/mac-android.sh ANDROID_NDK_HOME: /opt/android-ndk-r25b android_examples_macos: @@ -625,7 +647,7 @@ tasks: environment: # This ndk version matches with rules_android_ndk repo's CI # https://github.com/bazelbuild/rules_android_ndk/blob/877c68ef34c9f3353028bf490d269230c1990483/.bazelci/presubmit.yml#L42 - # The ndk is installed by this script + # The ndk is installed by this script # https://github.com/bazelbuild/continuous-integration/blob/ba56013373821feadd9f2eaa6b81eb19528795f0/macos/mac-android.sh ANDROID_NDK_HOME: /Users/buildkite/android-ndk-r25b ios_examples: diff --git a/cargo/cargo_build_script_runner/lib.rs b/cargo/cargo_build_script_runner/lib.rs index 3739a1778d..c0c4ea4dc7 100644 --- a/cargo/cargo_build_script_runner/lib.rs +++ b/cargo/cargo_build_script_runner/lib.rs @@ -124,7 +124,9 @@ impl BuildScriptOutput { pub fn outputs_from_command( cmd: &mut Command, ) -> Result<(Vec, Output), Output> { - let child_output = cmd.output().expect("Unable to start binary"); + let child_output = cmd + .output() + .unwrap_or_else(|_| panic!("Unable to start command:\n{:#?}", cmd)); if child_output.status.success() { let reader = BufReader::new(child_output.stdout.as_slice()); let output = Self::outputs_from_reader(reader); diff --git a/cargo/private/BUILD.bazel b/cargo/private/BUILD.bazel index 6a1aab653c..2b7e6de7dd 100644 --- a/cargo/private/BUILD.bazel +++ b/cargo/private/BUILD.bazel @@ -1,7 +1,13 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load(":runfiles_enabled.bzl", "runfiles_enabled_build_setting") bzl_library( name = "bzl_lib", srcs = glob(["**/*.bzl"]), visibility = ["//:__subpackages__"], ) + +runfiles_enabled_build_setting( + name = "runfiles_enabled", + visibility = ["//visibility:public"], +) diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl index 56f64994af..16b6623693 100644 --- a/cargo/private/cargo_build_script.bzl +++ b/cargo/private/cargo_build_script.bzl @@ -23,6 +23,7 @@ load( "find_toolchain", _name_to_crate_name = "name_to_crate_name", ) +load(":runfiles_enabled.bzl", "is_runfiles_enabled", "runfiles_enabled_attr") # Reexport for cargo_build_script_wrapper.bzl name_to_crate_name = _name_to_crate_name @@ -198,6 +199,39 @@ def _feature_enabled(ctx, feature_name, default = False): return default +def _rlocationpath(file, workspace_name): + if file.short_path.startswith("../"): + return file.short_path[len("../"):] + + return "{}/{}".format(workspace_name, file.short_path) + +def _create_runfiles_dir(ctx, script): + runfiles_dir = ctx.actions.declare_directory("{}.cargo_runfiles".format(ctx.label.name)) + + workspace_name = ctx.label.workspace_name + if not workspace_name: + workspace_name = ctx.workspace_name + + def _runfiles_map(file): + return "{}={}".format(file.path, _rlocationpath(file, workspace_name)) + + runfiles = script[DefaultInfo].default_runfiles + + args = ctx.actions.args() + args.use_param_file("@%s", use_always = True) + args.add(runfiles_dir.path) + args.add_all(runfiles.files, map_each = _runfiles_map, allow_closure = True) + + ctx.actions.run( + mnemonic = "CargoBuildScriptRunfilesDir", + executable = ctx.executable._runfiles_maker, + arguments = [args], + inputs = runfiles.files, + outputs = [runfiles_dir], + ) + + return runfiles_dir + def _cargo_build_script_impl(ctx): """The implementation for the `cargo_build_script` rule. @@ -215,9 +249,20 @@ def _cargo_build_script_impl(ctx): flags_out = ctx.actions.declare_file(ctx.label.name + ".flags") link_flags = ctx.actions.declare_file(ctx.label.name + ".linkflags") link_search_paths = ctx.actions.declare_file(ctx.label.name + ".linksearchpaths") # rustc-link-search, propagated from transitive dependencies - manifest_dir = "%s.runfiles/%s/%s" % (script.path, ctx.label.workspace_name or ctx.workspace_name, ctx.label.package) compilation_mode_opt_level = get_compilation_mode_opts(ctx, toolchain).opt_level + build_script_inputs = [] + + workspace_name = ctx.label.workspace_name + if not workspace_name: + workspace_name = ctx.workspace_name + + manifest_dir = "{}.runfiles/{}/{}".format(script.path, workspace_name, ctx.label.package) + if not is_runfiles_enabled(ctx.attr): + runfiles_dir = _create_runfiles_dir(ctx, ctx.attr.script) + build_script_inputs.append(runfiles_dir) + manifest_dir = "{}/{}/{}".format(runfiles_dir.path, workspace_name, ctx.label.package) + streams = struct( stdout = ctx.actions.declare_file(ctx.label.name + ".stdout.log"), stderr = ctx.actions.declare_file(ctx.label.name + ".stderr.log"), @@ -378,7 +423,6 @@ def _cargo_build_script_impl(ctx): args.add(streams.stderr) args.add(ctx.attr.rundir) - build_script_inputs = [] for dep in ctx.attr.link_deps: if rust_common.dep_info in dep and dep[rust_common.dep_info].dep_env: dep_env_file = dep[rust_common.dep_info].dep_env @@ -520,7 +564,12 @@ cargo_build_script = rule( "_experimental_symlink_execroot": attr.label( default = Label("//cargo/settings:experimental_symlink_execroot"), ), - }, + "_runfiles_maker": attr.label( + cfg = "exec", + executable = True, + default = Label("//cargo/private/runfiles_maker"), + ), + } | runfiles_enabled_attr(), fragments = ["cpp"], toolchains = [ str(Label("//rust:toolchain_type")), diff --git a/cargo/private/runfiles_enabled.bzl b/cargo/private/runfiles_enabled.bzl new file mode 100644 index 0000000000..0e5c3c5150 --- /dev/null +++ b/cargo/private/runfiles_enabled.bzl @@ -0,0 +1,74 @@ +"""A small utility module dedicated to detecting whether or not the `--enable_runfiles` and `--windows_enable_symlinks` flag are enabled +""" + +RunfilesEnabledInfo = provider( + doc = "A singleton provider that contains the raw value of a build setting", + fields = { + "value": "The value of the build setting in the current configuration. " + + "This value may come from the command line or an upstream transition, " + + "or else it will be the build setting's default.", + }, +) + +def _runfiles_enabled_setting_impl(ctx): + return RunfilesEnabledInfo(value = ctx.attr.value) + +runfiles_enabled_setting = rule( + implementation = _runfiles_enabled_setting_impl, + doc = "A bool-typed build setting that cannot be set on the command line", + attrs = { + "value": attr.bool( + doc = "A boolean value", + mandatory = True, + ), + }, +) + +_RUNFILES_ENABLED_ATTR_NAME = "_runfiles_enabled" + +def runfiles_enabled_attr(default = Label("//cargo/private:runfiles_enabled")): + return { + _RUNFILES_ENABLED_ATTR_NAME: attr.label( + doc = "A flag representing whether or not runfiles are enabled.", + providers = [RunfilesEnabledInfo], + default = default, + cfg = "exec", + ), + } + +def runfiles_enabled_build_setting(name, **kwargs): + native.config_setting( + name = "{}_enable_runfiles".format(name), + values = {"enable_runfiles": "true"}, + ) + + native.config_setting( + name = "{}_disable_runfiles".format(name), + values = {"enable_runfiles": "false"}, + ) + + runfiles_enabled_setting( + name = name, + value = select({ + # If either of the runfiles are set, use the flag + ":{}_enable_runfiles".format(name): True, + ":{}_disable_runfiles".format(name): False, + # Otherwise fall back to the system default. + "@platforms//os:windows": False, + "//conditions:default": True, + }), + **kwargs + ) + +def is_runfiles_enabled(attr): + """Determine whether or not runfiles are enabled. + + Args: + attr (struct): A rule's struct of attributes (`ctx.attr`) + Returns: + bool: The enable_runfiles value. + """ + + runfiles_enabled = getattr(attr, _RUNFILES_ENABLED_ATTR_NAME, None) + + return runfiles_enabled[RunfilesEnabledInfo].value if runfiles_enabled else True diff --git a/cargo/private/runfiles_maker/BUILD.bazel b/cargo/private/runfiles_maker/BUILD.bazel new file mode 100644 index 0000000000..3b3df766c2 --- /dev/null +++ b/cargo/private/runfiles_maker/BUILD.bazel @@ -0,0 +1,8 @@ +load("//rust:defs.bzl", "rust_binary") + +rust_binary( + name = "runfiles_maker", + srcs = ["runfiles_maker.rs"], + edition = "2021", + visibility = ["//visibility:public"], +) diff --git a/cargo/private/runfiles_maker/runfiles_maker.rs b/cargo/private/runfiles_maker/runfiles_maker.rs new file mode 100644 index 0000000000..439f5741d0 --- /dev/null +++ b/cargo/private/runfiles_maker/runfiles_maker.rs @@ -0,0 +1,71 @@ +//! A tool for building runfiles directories for Bazel environments that don't +//! support runfiles or have runfiles enabled. + +use std::collections::BTreeMap; +use std::path::PathBuf; + +struct Args { + pub output_dir: PathBuf, + pub runfiles: BTreeMap, +} + +impl Args { + fn parse() -> Self { + let args_file = std::env::args().nth(1).expect("No args file was passed."); + + let content = std::fs::read_to_string( + args_file + .strip_prefix("@") + .expect("Param files should start with @"), + ) + .unwrap(); + let mut args = content.lines(); + + let output_dir = PathBuf::from( + args.next() + .unwrap_or_else(|| panic!("Not enough arguments provided.")), + ); + let runfiles = args + .map(|s| { + let s = if s.starts_with("'") && s.ends_with("'") { + s.trim_matches('\'') + } else { + s + }; + let (src, dest) = s + .split_once('=') + .unwrap_or_else(|| panic!("Unexpected runfiles argument: {}", s)); + (PathBuf::from(src), PathBuf::from(dest)) + }) + .collect::>(); + + assert!(!runfiles.is_empty(), "No runfiles found"); + + Args { + output_dir, + runfiles, + } + } +} + +fn main() { + let args = Args::parse(); + + for (src, dest) in args.runfiles.iter() { + let out_dest = args.output_dir.join(dest); + std::fs::create_dir_all( + out_dest + .parent() + .expect("The output location should have a valid parent."), + ) + .expect("Failed to create output directory"); + std::fs::copy(src, &out_dest).unwrap_or_else(|e| { + panic!( + "Failed to copy file {} -> {}\n{:?}", + src.display(), + out_dest.display(), + e + ) + }); + } +} diff --git a/docs/src/index.md b/docs/src/index.md index 1045dca333..53ee5cbd71 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -20,7 +20,7 @@ Note that rules_rust bzlmod support is still a work in progress. Most features s To use `rules_rust` in a project using bzlmod, add the following to your `MODULE.bazel` file: ```python -bazel_dep(name = "rules_rust", version = "0.48.0") +bazel_dep(name = "rules_rust", version = "0.50.1") ``` Don't forget to substitute in your desired release's version number. @@ -36,8 +36,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # https://github.com/bazelbuild/rules_rust/releases http_archive( name = "rules_rust", - integrity = "sha256-Weev1uz2QztBlDA88JX6A1N72SucD1V8lBsaliM0TTg=", - urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.48.0/rules_rust-v0.48.0.tar.gz"], + integrity = "sha256-Log/YgrGjboS4tCqQPBA58VPyFcnCn/IZm+xxKTZrn0=", + urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.50.1/rules_rust-v0.50.1.tar.gz"], ) load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") @@ -57,7 +57,7 @@ To build with a particular version of the Rust compiler, pass that version to [` rust_register_toolchains( edition = "2021", versions = [ - "1.79.0" + "1.79.0", ], ) ``` diff --git a/examples/crate_universe/cargo_aliases/.bazelrc b/examples/crate_universe/cargo_aliases/.bazelrc deleted file mode 100644 index a67f1baf7f..0000000000 --- a/examples/crate_universe/cargo_aliases/.bazelrc +++ /dev/null @@ -1,25 +0,0 @@ -# A config file containing Bazel settings - -# Required on windows -common --enable_platform_specific_config -startup --windows_enable_symlinks -build:windows --enable_runfiles - -# Enable rustfmt -build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect -build:strict --output_groups=+rustfmt_checks - -# Enable clippy -build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:strict --output_groups=+clippy_checks - -# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel -# https://github.com/bazelbuild/rules_rust/issues/2181 -common --noenable_bzlmod - -# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. -build --incompatible_disallow_empty_glob - -# This import should always be last to allow users to override -# settings for local development. -try-import %workspace%/user.bazelrc diff --git a/examples/crate_universe/cargo_remote/.bazelrc b/examples/crate_universe/cargo_remote/.bazelrc deleted file mode 100644 index a67f1baf7f..0000000000 --- a/examples/crate_universe/cargo_remote/.bazelrc +++ /dev/null @@ -1,25 +0,0 @@ -# A config file containing Bazel settings - -# Required on windows -common --enable_platform_specific_config -startup --windows_enable_symlinks -build:windows --enable_runfiles - -# Enable rustfmt -build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect -build:strict --output_groups=+rustfmt_checks - -# Enable clippy -build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:strict --output_groups=+clippy_checks - -# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel -# https://github.com/bazelbuild/rules_rust/issues/2181 -common --noenable_bzlmod - -# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. -build --incompatible_disallow_empty_glob - -# This import should always be last to allow users to override -# settings for local development. -try-import %workspace%/user.bazelrc diff --git a/examples/crate_universe/cargo_workspace/.bazelrc b/examples/crate_universe/cargo_workspace/.bazelrc deleted file mode 100644 index a67f1baf7f..0000000000 --- a/examples/crate_universe/cargo_workspace/.bazelrc +++ /dev/null @@ -1,25 +0,0 @@ -# A config file containing Bazel settings - -# Required on windows -common --enable_platform_specific_config -startup --windows_enable_symlinks -build:windows --enable_runfiles - -# Enable rustfmt -build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect -build:strict --output_groups=+rustfmt_checks - -# Enable clippy -build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:strict --output_groups=+clippy_checks - -# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel -# https://github.com/bazelbuild/rules_rust/issues/2181 -common --noenable_bzlmod - -# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. -build --incompatible_disallow_empty_glob - -# This import should always be last to allow users to override -# settings for local development. -try-import %workspace%/user.bazelrc diff --git a/examples/crate_universe/multi_package/.bazelrc b/examples/crate_universe/multi_package/.bazelrc deleted file mode 100644 index a67f1baf7f..0000000000 --- a/examples/crate_universe/multi_package/.bazelrc +++ /dev/null @@ -1,25 +0,0 @@ -# A config file containing Bazel settings - -# Required on windows -common --enable_platform_specific_config -startup --windows_enable_symlinks -build:windows --enable_runfiles - -# Enable rustfmt -build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect -build:strict --output_groups=+rustfmt_checks - -# Enable clippy -build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:strict --output_groups=+clippy_checks - -# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel -# https://github.com/bazelbuild/rules_rust/issues/2181 -common --noenable_bzlmod - -# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. -build --incompatible_disallow_empty_glob - -# This import should always be last to allow users to override -# settings for local development. -try-import %workspace%/user.bazelrc diff --git a/examples/crate_universe/no_cargo_manifests/.bazelrc b/examples/crate_universe/no_cargo_manifests/.bazelrc deleted file mode 100644 index a67f1baf7f..0000000000 --- a/examples/crate_universe/no_cargo_manifests/.bazelrc +++ /dev/null @@ -1,25 +0,0 @@ -# A config file containing Bazel settings - -# Required on windows -common --enable_platform_specific_config -startup --windows_enable_symlinks -build:windows --enable_runfiles - -# Enable rustfmt -build:strict --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect -build:strict --output_groups=+rustfmt_checks - -# Enable clippy -build:strict --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:strict --output_groups=+clippy_checks - -# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel -# https://github.com/bazelbuild/rules_rust/issues/2181 -common --noenable_bzlmod - -# This isn't currently the defaut in Bazel, but we enable it to test we'll be ready if/when it flips. -build --incompatible_disallow_empty_glob - -# This import should always be last to allow users to override -# settings for local development. -try-import %workspace%/user.bazelrc