diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl index 301e2a8dce..a83aab8cd4 100644 --- a/cargo/private/cargo_build_script.bzl +++ b/cargo/private/cargo_build_script.bzl @@ -627,6 +627,16 @@ def _cargo_build_script_impl(ctx): if out_dir_volatile_basenames: env["RULES_RUST_OUT_DIR_VOLATILE_BASENAMES"] = ":".join(out_dir_volatile_basenames) + emit_warnings_setting = ctx.attr._emit_build_script_warnings[BuildSettingInfo].value + if emit_warnings_setting == "on": + emit_warnings = True + elif emit_warnings_setting == "off": + emit_warnings = False + else: + emit_warnings = ctx.attr.emit_warnings + if not emit_warnings: + env["RULES_RUST_SUPPRESS_BUILD_SCRIPT_WARNINGS"] = "1" + ctx.actions.run( executable = ctx.executable._cargo_build_script_runner, arguments = [args, runfiles_args], @@ -704,6 +714,16 @@ cargo_build_script = rule( providers = [[DepInfo], [CrateGroupInfo]], cfg = "exec", ), + "emit_warnings": attr.bool( + doc = dedent("""\ + Whether to forward `cargo::warning=` lines from the build script to stderr. + + Honored only when `--@rules_rust//cargo/settings:emit_build_script_warnings` + is `auto` (the default). Setting the flag to `on` or `off` overrides + this attribute for every target. + """), + default = True, + ), "link_deps": attr.label_list( doc = dedent("""\ The subset of the Rust (normal) dependencies of the crate that @@ -776,6 +796,9 @@ cargo_build_script = rule( "_debug_std_streams_output_group": attr.label( default = Label("//cargo/settings:debug_std_streams_output_group"), ), + "_emit_build_script_warnings": attr.label( + default = Label("//cargo/settings:emit_build_script_warnings"), + ), "_default_use_default_shell_env": attr.label( default = Label("//cargo/settings:use_default_shell_env"), ), diff --git a/cargo/private/cargo_build_script_runner/bin.rs b/cargo/private/cargo_build_script_runner/bin.rs index 579de21299..065b13ef42 100644 --- a/cargo/private/cargo_build_script_runner/bin.rs +++ b/cargo/private/cargo_build_script_runner/bin.rs @@ -22,7 +22,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use cargo_build_script_runner::cargo_manifest_dir::{remove_symlink, symlink, RunfilesMaker}; -use cargo_build_script_runner::{BuildScriptOutput, CompileAndLinkFlags}; +use cargo_build_script_runner::{BuildScriptOutput, CompileAndLinkFlags, SUPPRESS_WARNINGS_ENV}; fn run_buildrs() -> Result<(), String> { // We use exec_root.join rather than std::fs::canonicalize, to avoid resolving symlinks, as @@ -158,7 +158,12 @@ fn run_buildrs() -> Result<(), String> { ); } - let (buildrs_outputs, process_output) = BuildScriptOutput::outputs_from_command(&mut command) + let emit_warnings = env::var_os(SUPPRESS_WARNINGS_ENV).is_none_or(|v| v != "1"); + + let (buildrs_outputs, process_output) = BuildScriptOutput::outputs_from_command( + &mut command, + emit_warnings, + ) .map_err(|process_output| { format!( "Build script process failed{}\n--stdout:\n{}\n--stderr:\n{}", diff --git a/cargo/private/cargo_build_script_runner/lib.rs b/cargo/private/cargo_build_script_runner/lib.rs index 95659dd24d..a371248941 100644 --- a/cargo/private/cargo_build_script_runner/lib.rs +++ b/cargo/private/cargo_build_script_runner/lib.rs @@ -17,6 +17,8 @@ use std::io::{BufRead, BufReader, Read}; use std::process::{Command, Output}; +pub const SUPPRESS_WARNINGS_ENV: &str = "RULES_RUST_SUPPRESS_BUILD_SCRIPT_WARNINGS"; + pub mod cargo_manifest_dir; #[derive(Debug, PartialEq, Eq)] @@ -46,13 +48,7 @@ pub enum BuildScriptOutput { } impl BuildScriptOutput { - /// Converts a line into a [BuildScriptOutput] enum. - /// - /// Examples - /// ```rust - /// assert_eq!(BuildScriptOutput::new("cargo::rustc-link-lib=lib"), Some(BuildScriptOutput::LinkLib("lib".to_owned()))); - /// ``` - fn new(line: &str) -> Option { + fn new(line: &str, emit_warnings: bool) -> Option { let split = line.splitn(2, '=').collect::>(); if split.len() <= 1 { // Not a cargo directive. @@ -83,7 +79,9 @@ impl BuildScriptOutput { None } "warning" => { - eprint!("Build Script Warning: {}", split[1]); + if emit_warnings { + eprint!("Build Script Warning: {}", split[1]); + } None } "metadata" => { @@ -122,7 +120,10 @@ impl BuildScriptOutput { } /// Converts a [BufReader] into a vector of [BuildScriptOutput] enums. - fn outputs_from_reader(mut reader: BufReader) -> Vec { + fn outputs_from_reader( + mut reader: BufReader, + emit_warnings: bool, + ) -> Vec { let mut result = Vec::::new(); let mut buf = Vec::new(); while reader @@ -132,7 +133,7 @@ impl BuildScriptOutput { { // like cargo, ignore any lines that are not valid utf8 if let Ok(line) = String::from_utf8(buf.clone()) { - if let Some(bso) = BuildScriptOutput::new(&line) { + if let Some(bso) = BuildScriptOutput::new(&line, emit_warnings) { result.push(bso); } } @@ -144,13 +145,14 @@ impl BuildScriptOutput { /// Take a [Command], execute it and converts its input into a vector of [BuildScriptOutput] pub fn outputs_from_command( cmd: &mut Command, + emit_warnings: bool, ) -> Result<(Vec, Output), Output> { let child_output = cmd .output() .unwrap_or_else(|e| panic!("Unable to start command:\n{:#?}\n{:?}", cmd, e)); if child_output.status.success() { let reader = BufReader::new(child_output.stdout.as_slice()); - let output = Self::outputs_from_reader(reader); + let output = Self::outputs_from_reader(reader, emit_warnings); Ok((output, child_output)) } else { Err(child_output) @@ -277,7 +279,7 @@ mod tests { fn from_read_buffer_to_env_and_flags_test_impl(buff: Cursor<&str>) { let reader = BufReader::new(buff); - let result = BuildScriptOutput::outputs_from_reader(reader); + let result = BuildScriptOutput::outputs_from_reader(reader, true); assert_eq!(result.len(), 13); assert_eq!(result[0], BuildScriptOutput::LinkLib("sdfsdf".to_owned())); assert_eq!(result[1], BuildScriptOutput::Env("FOO=BAR".to_owned())); @@ -393,7 +395,7 @@ cargo::rustc-env=valid2=2 ", ); let reader = BufReader::new(buff); - let result = BuildScriptOutput::outputs_from_reader(reader); + let result = BuildScriptOutput::outputs_from_reader(reader, true); assert_eq!(result.len(), 2); assert_eq!( &BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path", ""), @@ -412,7 +414,7 @@ cargo:rustc-env=valid2=2 ", ); let reader = BufReader::new(buff); - let result = BuildScriptOutput::outputs_from_reader(reader); + let result = BuildScriptOutput::outputs_from_reader(reader, true); assert_eq!(result.len(), 2); assert_eq!( &BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path", ""), @@ -420,10 +422,22 @@ cargo:rustc-env=valid2=2 ); } + #[test] + fn warning_lines_never_appear_in_outputs() { + let lines = "cargo::warning=hello\ncargo::rustc-env=A=1\n"; + for emit_warnings in [true, false] { + let result = BuildScriptOutput::outputs_from_reader( + BufReader::new(Cursor::new(lines)), + emit_warnings, + ); + assert_eq!(result, vec![BuildScriptOutput::Env("A=1".to_owned())]); + } + } + #[test] fn metadata_directive_maps_to_dep_env_key_value() { let reader = BufReader::new(Cursor::new("cargo::metadata=version_1_10_0=1\n")); - let result = BuildScriptOutput::outputs_from_reader(reader); + let result = BuildScriptOutput::outputs_from_reader(reader, true); assert_eq!( result, vec![BuildScriptOutput::DepEnv("VERSION_1_10_0=1".to_owned())] @@ -447,7 +461,7 @@ cargo::rustc-env=BAR=/abs/exec_root/elsewhere/file.rs ", ); let reader = BufReader::new(buff); - let result = BuildScriptOutput::outputs_from_reader(reader); + let result = BuildScriptOutput::outputs_from_reader(reader, true); assert_eq!( BuildScriptOutput::outputs_to_env( &result, diff --git a/cargo/private/cargo_build_script_wrapper.bzl b/cargo/private/cargo_build_script_wrapper.bzl index bec53cf465..e556ea89b6 100644 --- a/cargo/private/cargo_build_script_wrapper.bzl +++ b/cargo/private/cargo_build_script_wrapper.bzl @@ -23,6 +23,7 @@ def cargo_build_script( proc_macro_deps = [], build_script_env = {}, build_script_env_files = [], + emit_warnings = True, use_default_shell_env = None, data = [], compile_data = [], @@ -112,6 +113,10 @@ def cargo_build_script( build_script_env (dict, optional): Environment variables for build scripts. build_script_env_files (list of label, optional): Files containing additional environment variables to set when running the build script. + emit_warnings (bool, optional): Whether to forward `cargo::warning=` lines from the build script + to stderr. Honored only when + `@rules_rust//cargo/settings:emit_build_script_warnings` is `auto` (the + default); set the flag to `on` or `off` to override every target. use_default_shell_env (bool, optional): Whether or not to include the default shell environment for the build script action. If unset the global setting `@rules_rust//cargo/settings:use_default_shell_env` will be used to determine this value. data (list, optional): Files needed by the build script. @@ -221,6 +226,7 @@ def cargo_build_script( version = version, build_script_env = build_script_env, build_script_env_files = build_script_env_files, + emit_warnings = emit_warnings, use_default_shell_env = sanitized_use_default_shell_env, links = links, deps = deps, diff --git a/cargo/settings/BUILD.bazel b/cargo/settings/BUILD.bazel index 1eae294cde..7bd646f70d 100644 --- a/cargo/settings/BUILD.bazel +++ b/cargo/settings/BUILD.bazel @@ -3,6 +3,7 @@ load( ":settings.bzl", "cargo_manifest_dir_filename_suffixes_to_retain", "debug_std_streams_output_group", + "emit_build_script_warnings", "experimental_symlink_execroot", "out_dir_volatile_file_basenames", "use_default_shell_env", @@ -30,3 +31,5 @@ experimental_symlink_execroot() use_default_shell_env() out_dir_volatile_file_basenames() + +emit_build_script_warnings() diff --git a/cargo/settings/settings.bzl b/cargo/settings/settings.bzl index a00147c6e6..b6d5484c30 100644 --- a/cargo/settings/settings.bzl +++ b/cargo/settings/settings.bzl @@ -3,7 +3,7 @@ Definitions for all `@rules_rust//cargo` settings """ -load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_list_flag") +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag", "string_list_flag") def experimental_symlink_execroot(): """A flag for which causes `cargo_build_script` to symlink the execroot of the action to \ @@ -43,6 +43,25 @@ def use_default_shell_env(): build_setting_default = True, ) +def emit_build_script_warnings(): + """A flag which controls whether `cargo_build_script` warnings \ + (`cargo::warning=`) are printed to stderr. + + Supported values: + + - `on`: emit warnings for every `cargo_build_script` target, overriding any + per-target `emit_warnings = False`. + - `auto` (default): respect the per-target `emit_warnings` attribute. + `crate_universe`-generated targets set it to `False`, so registry/git + crates stay quiet (matching Cargo); first-party targets emit by default. + - `off`: silence warnings build-wide. + """ + string_flag( + name = "emit_build_script_warnings", + build_setting_default = "auto", + values = ["on", "auto", "off"], + ) + def out_dir_volatile_file_basenames(): """A flag which determines what file basenames are removed from `OUT_DIR` by `cargo_build_script` actions to make the `_bs.out_dir` TreeArtifact deterministic. diff --git a/cargo/tests/cargo_build_script/emit_warnings/BUILD.bazel b/cargo/tests/cargo_build_script/emit_warnings/BUILD.bazel new file mode 100644 index 0000000000..369eb4c483 --- /dev/null +++ b/cargo/tests/cargo_build_script/emit_warnings/BUILD.bazel @@ -0,0 +1,21 @@ +load("//cargo:defs.bzl", "cargo_build_script") +load("//rust:defs.bzl", "rust_library", "rust_test") + +cargo_build_script( + name = "build_quiet", + srcs = ["build.rs"], + edition = "2018", + emit_warnings = False, +) + +rust_library( + name = "quiet_lib", + srcs = ["test_lib.rs"], + edition = "2018", + deps = [":build_quiet"], +) + +rust_test( + name = "quiet_test", + crate = ":quiet_lib", +) diff --git a/cargo/tests/cargo_build_script/emit_warnings/build.rs b/cargo/tests/cargo_build_script/emit_warnings/build.rs new file mode 100644 index 0000000000..93bdba10b5 --- /dev/null +++ b/cargo/tests/cargo_build_script/emit_warnings/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo::warning=this should be suppressed when emit_warnings=False"); + println!("cargo::rustc-env=FROM_BUILD_SCRIPT=ok"); +} diff --git a/cargo/tests/cargo_build_script/emit_warnings/test_lib.rs b/cargo/tests/cargo_build_script/emit_warnings/test_lib.rs new file mode 100644 index 0000000000..9c16667513 --- /dev/null +++ b/cargo/tests/cargo_build_script/emit_warnings/test_lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn build_script_ran() { + assert_eq!(env!("FROM_BUILD_SCRIPT"), "ok"); + } +} diff --git a/crate_universe/src/rendering.rs b/crate_universe/src/rendering.rs index 49360d78ce..500f6e264a 100644 --- a/crate_universe/src/rendering.rs +++ b/crate_universe/src/rendering.rs @@ -610,6 +610,9 @@ impl Renderer { ), platforms, ), + // Match Cargo's default: registry/git crates are quiet unless the + // build fails or the user passes `-vv`. + emit_warnings: false, edition: krate.common_attrs.edition.clone(), linker_script: krate.common_attrs.linker_script.clone(), links: attrs.and_then(|attrs| attrs.links.clone()), @@ -1244,6 +1247,11 @@ mod test { "```\n{}```\n", build_file_content ); + assert!( + build_file_content.contains("emit_warnings = False"), + "```\n{}```\n", + build_file_content + ); // Ensure `cargo_build_script` requirements are met assert!(build_file_content.contains("name = \"_bs\"")); diff --git a/crate_universe/src/utils/starlark.rs b/crate_universe/src/utils/starlark.rs index 14ff5f8c23..7d5401e113 100644 --- a/crate_universe/src/utils/starlark.rs +++ b/crate_universe/src/utils/starlark.rs @@ -111,6 +111,9 @@ pub(crate) struct CargoBuildScript { pub(crate) data: Data, #[serde(skip_serializing_if = "SelectSet::is_empty")] pub(crate) deps: SelectSet