From 9b1a1e1d95d1e40bdf57ef9d37ccbac91fc9c280 Mon Sep 17 00:00:00 2001 From: Jing Peng Date: Sun, 26 Feb 2023 15:27:27 -0500 Subject: [PATCH 1/2] Write to stdout if `-` is given as output file If `-o -` or `--emit KIND=-` is provided, output will be written to stdout instead. Binary output (`obj`, `llvm-bc`, `link` and `metadata`) being written this way will result in an error unless stdout is not a tty. Multiple output types going to stdout will trigger an error too, as they will all be mixded together. --- Cargo.lock | 2 + compiler/rustc_codegen_ssa/messages.ftl | 2 + compiler/rustc_codegen_ssa/src/back/link.rs | 24 +++- compiler/rustc_codegen_ssa/src/back/write.rs | 23 +++- compiler/rustc_codegen_ssa/src/errors.rs | 6 + compiler/rustc_driver_impl/src/lib.rs | 13 ++- compiler/rustc_driver_impl/src/pretty.rs | 6 +- compiler/rustc_interface/Cargo.toml | 1 + compiler/rustc_interface/messages.ftl | 1 + compiler/rustc_interface/src/errors.rs | 4 + compiler/rustc_interface/src/interface.rs | 4 +- compiler/rustc_interface/src/passes.rs | 75 ++++++++----- compiler/rustc_interface/src/tests.rs | 20 ++-- compiler/rustc_interface/src/util.rs | 33 +++++- compiler/rustc_metadata/messages.ftl | 9 ++ compiler/rustc_metadata/src/errors.rs | 18 +++ compiler/rustc_metadata/src/fs.rs | 50 +++++++-- compiler/rustc_mir_transform/src/dump_mir.rs | 15 ++- compiler/rustc_session/Cargo.toml | 1 + compiler/rustc_session/src/config.rs | 105 ++++++++++++++++-- compiler/rustc_session/src/output.rs | 43 ++++--- compiler/rustc_session/src/session.rs | 6 +- src/doc/rustc/src/command-line-arguments.md | 6 + .../hotplug_codegen_backend/the_backend.rs | 14 ++- tests/run-make-fulldeps/issue-19371/foo.rs | 4 +- tests/run-make/emit-to-stdout/Makefile | 51 +++++++++ .../run-make/emit-to-stdout/emit-link.stderr | 4 + .../emit-to-stdout/emit-llvm-bc.stderr | 4 + .../emit-to-stdout/emit-metadata.stderr | 4 + .../emit-to-stdout/emit-multiple-types.stderr | 4 + tests/run-make/emit-to-stdout/emit-obj.stderr | 4 + tests/run-make/emit-to-stdout/test.rs | 1 + 32 files changed, 456 insertions(+), 101 deletions(-) create mode 100644 tests/run-make/emit-to-stdout/Makefile create mode 100644 tests/run-make/emit-to-stdout/emit-link.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-llvm-bc.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-metadata.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-multiple-types.stderr create mode 100644 tests/run-make/emit-to-stdout/emit-obj.stderr create mode 100644 tests/run-make/emit-to-stdout/test.rs diff --git a/Cargo.lock b/Cargo.lock index 0369442f11cf6..650553e0d8343 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3702,6 +3702,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "atty", "libloading", "rustc-rayon", "rustc-rayon-core", @@ -4166,6 +4167,7 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ + "atty", "getopts", "libc", "rustc_ast", diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 9aa2b2e2b2ed0..5ecb63986fe1f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -9,6 +9,8 @@ codegen_ssa_archive_build_failure = codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering +codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty + codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8a00c42a0e8bd..311e56cc7d15e 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; -use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; +use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -68,6 +68,7 @@ pub fn link_binary<'a>( ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + let mut tempfiles_for_stdout_output: Vec = Vec::new(); for &crate_type in sess.crate_types().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -97,12 +98,15 @@ pub fn link_binary<'a>( .tempdir() .unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); - let out_filename = out_filename( + let output = out_filename( sess, crate_type, outputs, codegen_results.crate_info.local_crate_name, ); + let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); + let out_filename = + output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str())); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); @@ -152,6 +156,17 @@ pub fn link_binary<'a>( ); } } + + if output.is_stdout() { + if output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { + shorthand: OutputType::Exe.shorthand(), + }); + } else if let Err(e) = copy_to_stdout(&out_filename) { + sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e)); + } + tempfiles_for_stdout_output.push(out_filename); + } } } @@ -189,6 +204,11 @@ pub fn link_binary<'a>( remove_temps_from_module(allocator_module); } + // Remove the temporary files if output goes to stdout + for temp in tempfiles_for_stdout_output { + ensure_removed(sess.diagnostic(), &temp); + } + // If no requested outputs require linking, then the object temporaries should // be kept. if !sess.opts.output_types.should_link() { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c323372bda42d..a1e8725e08edc 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -23,12 +23,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_metadata::fs::copy_to_stdout; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; -use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType}; use rustc_session::config::{Passes, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::source_map::SourceMap; @@ -535,9 +536,16 @@ fn produce_final_output_artifacts( let mut user_wants_objects = false; // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.emit_err(errors::CopyPath::new(from, to, e)); + let copy_gracefully = |from: &Path, to: &OutFileName| match to { + OutFileName::Stdout => { + if let Err(e) = copy_to_stdout(from) { + sess.emit_err(errors::CopyPath::new(from, to.as_path(), e)); + } + } + OutFileName::Real(path) => { + if let Err(e) = fs::copy(from, path) { + sess.emit_err(errors::CopyPath::new(from, path, e)); + } } }; @@ -547,7 +555,12 @@ fn produce_final_output_artifacts( // to copy `foo.0.x` to `foo.x`. let module_name = Some(&compiled_modules.modules[0].name[..]); let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, &crate_output.path(output_type)); + let output = crate_output.path(output_type); + if !output_type.is_text_output() && output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); + } else { + copy_gracefully(&path, &output); + } if !sess.opts.cg.save_temps && !keep_numbered { // The user just wants `foo.x`, not `foo.#module-name#.x`. ensure_removed(sess.diagnostic(), &path); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cf4893b822651..3fed9ea0b41e0 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -82,6 +82,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> { } } +#[derive(Diagnostic)] +#[diag(codegen_ssa_binary_output_to_tty)] +pub struct BinaryOutputToTty { + pub shorthand: &'static str, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_emit_path)] pub struct IgnoringEmitPath { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 40aa69e5a4105..416603a0e1ca2 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -34,7 +34,9 @@ use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; +use rustc_session::config::{ + ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths, +}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -437,9 +439,12 @@ fn run_compiler( } // Extract output directory and file from matches. -fn make_output(matches: &getopts::Matches) -> (Option, Option) { +fn make_output(matches: &getopts::Matches) -> (Option, Option) { let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o)); - let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o)); + let ofile = matches.opt_str("o").map(|o| match o.as_str() { + "-" => OutFileName::Stdout, + path => OutFileName::Real(PathBuf::from(path)), + }); (odir, ofile) } @@ -685,7 +690,7 @@ fn print_crate_info( for &style in &crate_types { let fname = rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.file_name().unwrap().to_string_lossy()); + safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } Cfg => { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index ee64b18d3f60c..24a5f4030b88d 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -9,7 +9,7 @@ use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::map as hir_map; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; +use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::FileName; @@ -359,8 +359,8 @@ fn get_source(sess: &Session) -> (String, FileName) { fn write_or_print(out: &str, sess: &Session) { match &sess.io.output_file { - None => print!("{out}"), - Some(p) => { + None | Some(OutFileName::Stdout) => print!("{out}"), + Some(OutFileName::Real(p)) => { if let Err(e) = std::fs::write(p, out) { sess.emit_fatal(UnprettyDumpFail { path: p.display().to_string(), diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 2c7438ed9db43..7826d42dcb2d7 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +atty = "0.2.13" libloading = "0.7.1" tracing = "0.1" rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index be1a75f020b4d..bd9fad8b04296 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -33,6 +33,7 @@ interface_mixed_proc_macro_crate = interface_multiple_output_types_adaption = due to multiple output types requested, the explicitly specified output file name will be adapted for each output type +interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout interface_out_dir_error = failed to find or create the directory specified by `--out-dir` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 0eedee250269e..a9ab2720d89a5 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -108,3 +108,7 @@ pub struct IgnoringExtraFilename; #[derive(Diagnostic)] #[diag(interface_ignoring_out_dir)] pub struct IgnoringOutDir; + +#[derive(Diagnostic)] +#[diag(interface_multiple_output_types_to_stdout)] +pub struct MultipleOutputTypesToStdout; diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 39d56897999d9..2edc72ba72ea1 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -14,7 +14,7 @@ use rustc_middle::{bug, ty}; use rustc_parse::maybe_new_parser_from_source_str; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; -use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; +use rustc_session::config::{self, ErrorOutputType, Input, OutFileName, OutputFilenames}; use rustc_session::config::{CheckCfg, ExpectedValues}; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; @@ -252,7 +252,7 @@ pub struct Config { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub file_loader: Option>, pub locale_resources: &'static [&'static str], diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 42d8d22809178..83a74742f5b76 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -24,7 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::Resolver; -use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; +use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::{MetadataLoader, Untracked}; use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; @@ -373,19 +373,23 @@ fn generated_output_paths( ) -> Vec { let mut out_filenames = Vec::new(); for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); + let out_filename = outputs.path(*output_type); + let file = out_filename.as_path().to_path_buf(); match *output_type { // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => { for crate_type in sess.crate_types().iter() { let p = filename_for_input(sess, *crate_type, crate_name, outputs); - out_filenames.push(p); + out_filenames.push(p.as_path().to_path_buf()); } } OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => { // Don't add the dep-info output when omitting it from dep-info targets } + OutputType::DepInfo if out_filename.is_stdout() => { + // Don't add the dep-info output when it goes to stdout + } _ => { out_filenames.push(file); } @@ -452,7 +456,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { return; } - let deps_filename = outputs.path(OutputType::DepInfo); + let deps_output = outputs.path(OutputType::DepInfo); + let deps_filename = deps_output.as_path(); let result: io::Result<()> = try { // Build a list of files used to compile the output and @@ -515,33 +520,47 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } - let mut file = BufWriter::new(fs::File::create(&deps_filename)?); - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } + let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{path}:")?; - } + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{path}:")?; + } - // Emit special comments with information about accessed environment variables. - let env_depinfo = sess.parse_sess.env_depinfo.borrow(); - if !env_depinfo.is_empty() { - let mut envs: Vec<_> = env_depinfo - .iter() - .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) - .collect(); - envs.sort_unstable(); - writeln!(file)?; - for (k, v) in envs { - write!(file, "# env-dep:{k}")?; - if let Some(v) = v { - write!(file, "={v}")?; - } + // Emit special comments with information about accessed environment variables. + let env_depinfo = sess.parse_sess.env_depinfo.borrow(); + if !env_depinfo.is_empty() { + let mut envs: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) + .collect(); + envs.sort_unstable(); writeln!(file)?; + for (k, v) in envs { + write!(file, "# env-dep:{k}")?; + if let Some(v) = v { + write!(file, "={v}")?; + } + writeln!(file)?; + } + } + + Ok(()) + }; + + match deps_output { + OutFileName::Stdout => { + let mut file = BufWriter::new(io::stdout()); + write_deps_to_file(&mut file)?; + } + OutFileName::Real(ref path) => { + let mut file = BufWriter::new(fs::File::create(path)?); + write_deps_to_file(&mut file)?; } } }; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 28e719a40e565..77ee2b40e37c1 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -11,7 +11,7 @@ use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ - BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet, + BranchProtection, Externs, OomStrategy, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel, }; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; @@ -167,8 +167,14 @@ fn test_output_types_tracking_hash_different_paths() { let mut v2 = Options::default(); let mut v3 = Options::default(); - v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); - v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); + v1.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("./some/thing"))), + )]); + v2.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("/some/thing"))), + )]); v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); assert_non_crate_hash_different(&v1, &v2); @@ -182,13 +188,13 @@ fn test_output_types_tracking_hash_different_construction_order() { let mut v2 = Options::default(); v1.output_types = OutputTypes::new(&[ - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), ]); v2.output_types = OutputTypes::new(&[ - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), ]); assert_same_hash(&v1, &v2); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index cb19750203e85..87252fefb1e2c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,7 +11,7 @@ use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::CheckCfg; use rustc_session::config::{self, CrateType}; -use rustc_session::config::{ErrorOutputType, OutputFilenames}; +use rustc_session::config::{ErrorOutputType, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::filesearch::sysroot_candidates; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; @@ -500,7 +500,36 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec bool { + if atty::is(atty::Stream::Stdout) { + // If stdout is a tty, check if multiple text output types are + // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar` + let named_text_types = output_types + .iter() + .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout)) + .count(); + let unnamed_text_types = + output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count(); + named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout + } else { + // Otherwise, all the output types should be checked + let named_types = + output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count(); + let unnamed_types = output_types.values().filter(|o| o.is_none()).count(); + named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout + } +} + pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames { + if multiple_output_types_to_stdout( + &sess.opts.output_types, + sess.io.output_file == Some(OutFileName::Stdout), + ) { + sess.emit_fatal(errors::MultipleOutputTypesToStdout); + } match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -544,7 +573,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu OutputFilenames::new( out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), + out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(), ofile, sess.io.temps_dir.clone(), sess.opts.cg.extra_filename.clone(), diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 6d8601b9e2bc5..d6b08d840e38b 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -4,6 +4,9 @@ metadata_as_needed_compatibility = metadata_bad_panic_strategy = the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` +metadata_binary_output_to_tty = + option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + metadata_bundle_needs_static = linking modifier `bundle` is only compatible with `static` linking kind @@ -63,6 +66,9 @@ metadata_fail_seek_file = metadata_fail_write_file = failed to write to the file: {$err} +metadata_failed_copy_to_stdout = + failed to copy {$filename} to stdout: {$err} + metadata_failed_create_encoded_metadata = failed to create encoded metadata from file: {$err} @@ -72,6 +78,9 @@ metadata_failed_create_file = metadata_failed_create_tempdir = couldn't create a temp dir: {$err} +metadata_failed_remove = + failed to remove {$filename}: {$err} + metadata_failed_write_error = failed to write {$filename}: {$err} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index a44c1dd582ed3..e110c68321d46 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -395,6 +395,24 @@ pub struct FailedWriteError { pub err: Error, } +#[derive(Diagnostic)] +#[diag(metadata_failed_copy_to_stdout)] +pub struct FailedCopyToStdout { + pub filename: PathBuf, + pub err: Error, +} + +#[derive(Diagnostic)] +#[diag(metadata_failed_remove)] +pub struct FailedRemove { + pub filename: PathBuf, + pub err: Error, +} + +#[derive(Diagnostic)] +#[diag(metadata_binary_output_to_tty)] +pub struct BinaryOutputToTty; + #[derive(Diagnostic)] #[diag(metadata_missing_native_library)] pub struct MissingNativeLibrary<'a> { diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 08de828fbdba2..5be99c8e4c0d8 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,18 +1,19 @@ use crate::errors::{ - FailedCreateEncodedMetadata, FailedCreateFile, FailedCreateTempdir, FailedWriteError, + BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, + FailedCreateTempdir, FailedRemove, FailedWriteError, }; use crate::{encode_metadata, EncodedMetadata}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; -use rustc_session::config::OutputType; +use rustc_session::config::{OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; use tempfile::Builder as TempFileBuilder; -use std::fs; use std::path::{Path, PathBuf}; +use std::{fs, io}; // FIXME(eddyb) maybe include the crate name in this? pub const METADATA_FILENAME: &str = "lib.rmeta"; @@ -74,26 +75,47 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // this file always exists. let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); let (metadata_filename, metadata_tmpdir) = if need_metadata_file { - if let Err(err) = non_durable_rename(&metadata_filename, &out_filename) { - tcx.sess.emit_fatal(FailedWriteError { filename: out_filename, err }); - } + let filename = match out_filename { + OutFileName::Real(ref path) => { + if let Err(err) = non_durable_rename(&metadata_filename, path) { + tcx.sess.emit_fatal(FailedWriteError { filename: path.to_path_buf(), err }); + } + path.clone() + } + OutFileName::Stdout => { + if out_filename.is_tty() { + tcx.sess.emit_err(BinaryOutputToTty); + } else if let Err(err) = copy_to_stdout(&metadata_filename) { + tcx.sess + .emit_err(FailedCopyToStdout { filename: metadata_filename.clone(), err }); + } + metadata_filename + } + }; if tcx.sess.opts.json_artifact_notifications { tcx.sess .parse_sess .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); + .emit_artifact_notification(&out_filename.as_path(), "metadata"); } - (out_filename, None) + (filename, None) } else { (metadata_filename, Some(metadata_tmpdir)) }; // Load metadata back to memory: codegen may need to include it in object files. - let metadata = - EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { + let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir) + .unwrap_or_else(|err| { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); + // Delete the temporary metadata file if output is stdout + if need_metadata_file && out_filename.is_stdout() { + if let Err(err) = fs::remove_file(&metadata_filename) { + tcx.sess.emit_err(FailedRemove { filename: metadata_filename, err }); + } + } + let need_metadata_module = metadata_kind == MetadataKind::Compressed; (metadata, need_metadata_module) @@ -116,3 +138,11 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { let _ = std::fs::remove_file(dst); std::fs::rename(src, dst) } + +pub fn copy_to_stdout(from: &Path) -> io::Result<()> { + let file = fs::File::open(from)?; + let mut reader = io::BufReader::new(file); + let mut stdout = io::stdout(); + io::copy(&mut reader, &mut stdout)?; + Ok(()) +} diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 746e3d9652db6..13841be494cf0 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -7,7 +7,7 @@ use crate::MirPass; use rustc_middle::mir::write_mir_pretty; use rustc_middle::mir::Body; use rustc_middle::ty::TyCtxt; -use rustc_session::config::OutputType; +use rustc_session::config::{OutFileName, OutputType}; pub struct Marker(pub &'static str); @@ -20,8 +20,15 @@ impl<'tcx> MirPass<'tcx> for Marker { } pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { - let path = tcx.output_filenames(()).path(OutputType::Mir); - let mut f = io::BufWriter::new(File::create(&path)?); - write_mir_pretty(tcx, None, &mut f)?; + match tcx.output_filenames(()).path(OutputType::Mir) { + OutFileName::Stdout => { + let mut f = io::stdout(); + write_mir_pretty(tcx, None, &mut f)?; + } + OutFileName::Real(path) => { + let mut f = io::BufWriter::new(File::create(&path)?); + write_mir_pretty(tcx, None, &mut f)?; + } + } Ok(()) } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 3af83aaaaa8a2..90ad3f90f2c31 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] +atty = "0.2.13" getopts = "0.2" rustc_macros = { path = "../rustc_macros" } tracing = "0.1" diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 6c8c8e484f939..b72a95639e65c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -30,6 +30,7 @@ use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, }; use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::OsStr; use std::fmt; use std::hash::Hash; use std::iter; @@ -333,7 +334,7 @@ impl OutputType { } } - fn shorthand(&self) -> &'static str { + pub fn shorthand(&self) -> &'static str { match *self { OutputType::Bitcode => "llvm-bc", OutputType::Assembly => "asm", @@ -386,6 +387,18 @@ impl OutputType { OutputType::Exe => "", } } + + pub fn is_text_output(&self) -> bool { + match *self { + OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::DepInfo => true, + OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => { + false + } + } + } } /// The type of diagnostics output to generate. @@ -438,14 +451,14 @@ pub enum ResolveDocLinks { /// dependency tracking for command-line arguments. Also only hash keys, since tracking /// should only depend on the output types, not the paths they're written to. #[derive(Clone, Debug, Hash, HashStable_Generic)] -pub struct OutputTypes(BTreeMap>); +pub struct OutputTypes(BTreeMap>); impl OutputTypes { - pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) } - pub fn get(&self, key: &OutputType) -> Option<&Option> { + pub fn get(&self, key: &OutputType) -> Option<&Option> { self.0.get(key) } @@ -453,11 +466,15 @@ impl OutputTypes { self.0.contains_key(key) } - pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { + pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option> { + self.0.iter() + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { self.0.keys() } - pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { self.0.values() } @@ -658,11 +675,71 @@ impl Input { } } +#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq)] +pub enum OutFileName { + Real(PathBuf), + Stdout, +} + +impl OutFileName { + pub fn parent(&self) -> Option<&Path> { + match *self { + OutFileName::Real(ref path) => path.parent(), + OutFileName::Stdout => None, + } + } + + pub fn filestem(&self) -> Option<&OsStr> { + match *self { + OutFileName::Real(ref path) => path.file_stem(), + OutFileName::Stdout => Some(OsStr::new("stdout")), + } + } + + pub fn is_stdout(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => true, + } + } + + pub fn is_tty(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => atty::is(atty::Stream::Stdout), + } + } + + pub fn as_path(&self) -> &Path { + match *self { + OutFileName::Real(ref path) => path.as_ref(), + OutFileName::Stdout => &Path::new("stdout"), + } + } + + /// For a given output filename, return the actual name of the file that + /// can be used to write codegen data of type `flavor`. For real-path + /// output filenames, this would be trivial as we can just use the path. + /// Otherwise for stdout, return a temporary path so that the codegen data + /// may be later copied to stdout. + pub fn file_for_writing( + &self, + outputs: &OutputFilenames, + flavor: OutputType, + codegen_unit_name: Option<&str>, + ) -> PathBuf { + match *self { + OutFileName::Real(ref path) => path.clone(), + OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), + } + } +} + #[derive(Clone, Hash, Debug, HashStable_Generic)] pub struct OutputFilenames { pub out_directory: PathBuf, filestem: String, - pub single_output_file: Option, + pub single_output_file: Option, pub temps_directory: Option, pub outputs: OutputTypes, } @@ -675,7 +752,7 @@ impl OutputFilenames { pub fn new( out_directory: PathBuf, out_filestem: String, - single_output_file: Option, + single_output_file: Option, temps_directory: Option, extra: String, outputs: OutputTypes, @@ -689,12 +766,12 @@ impl OutputFilenames { } } - pub fn path(&self, flavor: OutputType) -> PathBuf { + pub fn path(&self, flavor: OutputType) -> OutFileName { self.outputs .get(&flavor) .and_then(|p| p.to_owned()) .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.output_path(flavor)) + .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) } /// Gets the output path where a compilation artifact of the given type @@ -1821,7 +1898,10 @@ fn parse_output_types( for output_type in list.split(',') { let (shorthand, path) = match output_type.split_once('=') { None => (output_type, None), - Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)), + Some((shorthand, path)) => { + (shorthand, Some(OutFileName::Real(PathBuf::from(path)))) + } }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { early_error( @@ -2892,7 +2972,7 @@ pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, - OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks, + OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; @@ -2990,6 +3070,7 @@ pub(crate) mod dep_tracking { SourceFileHashAlgorithm, TrimmedDefPaths, Option, + OutFileName, OutputType, RealFileName, LocationDetail, diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index fdb9fae44e153..2088744bc5bff 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,5 +1,5 @@ //! Related to out filenames of compilation (e.g. save analysis, binaries). -use crate::config::{CrateType, Input, OutputFilenames, OutputType}; +use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, InvalidCharacterInCrateName, @@ -8,14 +8,14 @@ use crate::Session; use rustc_ast::{self as ast, attr}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use std::path::{Path, PathBuf}; +use std::path::Path; pub fn out_filename( sess: &Session, crate_type: CrateType, outputs: &OutputFilenames, crate_name: Symbol, -) -> PathBuf { +) -> OutFileName { let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); let out_filename = outputs .outputs @@ -24,7 +24,9 @@ pub fn out_filename( .or_else(|| outputs.single_output_file.clone()) .unwrap_or(default_filename); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -112,7 +114,7 @@ pub fn filename_for_metadata( sess: &Session, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { // If the command-line specified the path, use that directly. if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) { return out_filename.clone(); @@ -120,12 +122,13 @@ pub fn filename_for_metadata( let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - let out_filename = outputs - .single_output_file - .clone() - .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{libname}.rmeta"))); + let out_filename = outputs.single_output_file.clone().unwrap_or_else(|| { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rmeta"))) + }); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -135,23 +138,33 @@ pub fn filename_for_input( crate_type: CrateType, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); match crate_type { - CrateType::Rlib => outputs.out_directory.join(&format!("lib{libname}.rlib")), + CrateType::Rlib => { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib"))) + } CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Staticlib => { let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Executable => { let suffix = &sess.target.exe_suffix; let out_filename = outputs.path(OutputType::Exe); - if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } + if let OutFileName::Real(ref path) = out_filename { + if suffix.is_empty() { + out_filename + } else { + OutFileName::Real(path.with_extension(&suffix[1..])) + } + } else { + out_filename + } } } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bbe52dbced071..08e2f19e11b33 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,9 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::Input; -use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; +use crate::config::{ + self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, +}; use crate::errors; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; @@ -135,7 +137,7 @@ pub struct Limits { pub struct CompilerIO { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub temps_dir: Option, } diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3be4382b0a3aa..2c15d1b11100c 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -202,6 +202,12 @@ flag](codegen-options/index.md#extra-filename). The files are written to the current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each emission type may also specify the output filename with the form `KIND=PATH`, which takes precedence over the `-o` flag. +Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout. +Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to +stdout despite it being a tty or not. This will result in an error if any +binary output type is written to stdout that is a tty. +This will also result in an error if multiple output types +would be written to stdout, because they would be all mixed together. [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html [LLVM IR]: https://llvm.org/docs/LangRef.html diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 7db100a08a106..b971037ea6778 100644 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -62,7 +62,7 @@ impl CodegenBackend for TheBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { - use rustc_session::{config::CrateType, output::out_filename}; + use rustc_session::{config::{CrateType, OutFileName}, output::out_filename}; use std::io::Write; let crate_name = codegen_results.crate_info.local_crate_name; for &crate_type in sess.opts.crate_types.iter() { @@ -70,8 +70,16 @@ impl CodegenBackend for TheBackend { sess.fatal(format!("Crate type is {:?}", crate_type)); } let output_name = out_filename(sess, crate_type, &outputs, crate_name); - let mut out_file = ::std::fs::File::create(output_name).unwrap(); - write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + match output_name { + OutFileName::Real(ref path) => { + let mut out_file = ::std::fs::File::create(path).unwrap(); + write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + } + OutFileName::Stdout => { + let mut stdout = std::io::stdout(); + write!(stdout, "This has been \"compiled\" successfully.").unwrap(); + } + } } Ok(()) } diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 53ec79e477bd4..6d08cfd07f89e 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/run-make-fulldeps/issue-19371/foo.rs @@ -6,7 +6,7 @@ extern crate rustc_session; extern crate rustc_span; use rustc_interface::interface; -use rustc_session::config::{Input, Options, OutputType, OutputTypes}; +use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes}; use rustc_span::source_map::FileName; use std::path::PathBuf; @@ -50,7 +50,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { crate_cfg: Default::default(), crate_check_cfg: Default::default(), input, - output_file: Some(output), + output_file: Some(OutFileName::Real(output)), output_dir: None, file_loader: None, locale_resources: &[], diff --git a/tests/run-make/emit-to-stdout/Makefile b/tests/run-make/emit-to-stdout/Makefile new file mode 100644 index 0000000000000..b7455965cffa2 --- /dev/null +++ b/tests/run-make/emit-to-stdout/Makefile @@ -0,0 +1,51 @@ +include ../tools.mk + +SRC=test.rs +OUT=$(TMPDIR)/out + +all: asm llvm-ir dep-info mir llvm-bc obj metadata link multiple-types multiple-types-option-o + +asm: $(OUT) + $(RUSTC) --emit asm=$(OUT)/$@ $(SRC) + $(RUSTC) --emit asm=- $(SRC) | diff - $(OUT)/$@ +llvm-ir: $(OUT) + $(RUSTC) --emit llvm-ir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit llvm-ir=- $(SRC) | diff - $(OUT)/$@ +dep-info: $(OUT) + $(RUSTC) -Z dep-info-omit-d-target=yes --emit dep-info=$(OUT)/$@ $(SRC) + $(RUSTC) --emit dep-info=- $(SRC) | diff - $(OUT)/$@ +mir: $(OUT) + $(RUSTC) --emit mir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit mir=- $(SRC) | diff - $(OUT)/$@ + +llvm-bc: $(OUT) + $(RUSTC) --emit llvm-bc=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-llvm-bc.stderr +obj: $(OUT) + $(RUSTC) --emit obj=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-obj.stderr + +# For metadata output, a temporary directory will be created to hold the temporary +# metadata file. But when output is stdout, the temporary directory will be located +# in the same place as $(SRC), which is mounted as read-only in the tests. Thus as +# a workaround, $(SRC) is copied to the test output directory $(OUT) and we compile +# it there. +metadata: $(OUT) + cp $(SRC) $(OUT) + (cd $(OUT); pwd; ls -d; $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) + diff $(OUT)/$@ emit-metadata.stderr + +link: $(OUT) + $(RUSTC) --emit link=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-link.stderr + +multiple-types: $(OUT) + $(RUSTC) --emit asm=- --emit llvm-ir=- --emit dep-info=- --emit mir=- $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +multiple-types-option-o: $(OUT) + $(RUSTC) -o - --emit asm,llvm-ir,dep-info,mir $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +$(OUT): + mkdir -p $(OUT) diff --git a/tests/run-make/emit-to-stdout/emit-link.stderr b/tests/run-make/emit-to-stdout/emit-link.stderr new file mode 100644 index 0000000000000..a9d856503e629 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-link.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `link` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr new file mode 100644 index 0000000000000..7b53bd16e7a54 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `llvm-bc` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-metadata.stderr b/tests/run-make/emit-to-stdout/emit-metadata.stderr new file mode 100644 index 0000000000000..ee1e52937b987 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-metadata.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-multiple-types.stderr b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr new file mode 100644 index 0000000000000..b8a683cd9ebe6 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr @@ -0,0 +1,4 @@ +error: can't use option `-o` or `--emit` to write multiple output types to stdout + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-obj.stderr b/tests/run-make/emit-to-stdout/emit-obj.stderr new file mode 100644 index 0000000000000..b130353084415 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-obj.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `obj` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/test.rs b/tests/run-make/emit-to-stdout/test.rs new file mode 100644 index 0000000000000..c1bfaa6cab5d9 --- /dev/null +++ b/tests/run-make/emit-to-stdout/test.rs @@ -0,0 +1 @@ +#![crate_type = "rlib"] From ade6c36e5319963e34c5c35a4c0c948951c48f19 Mon Sep 17 00:00:00 2001 From: Jing Peng Date: Tue, 6 Jun 2023 17:54:34 -0400 Subject: [PATCH 2/2] fix - remove useless commands from test Makefile - do not unnecessarily remove metadata temporary files because they'll be managed by MaybeTempDir - remove unused FailedRemove error introduced by this PR --- compiler/rustc_metadata/messages.ftl | 3 --- compiler/rustc_metadata/src/errors.rs | 7 ------- compiler/rustc_metadata/src/fs.rs | 9 +-------- tests/run-make/emit-to-stdout/Makefile | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index d6b08d840e38b..13b3dac85d109 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -78,9 +78,6 @@ metadata_failed_create_file = metadata_failed_create_tempdir = couldn't create a temp dir: {$err} -metadata_failed_remove = - failed to remove {$filename}: {$err} - metadata_failed_write_error = failed to write {$filename}: {$err} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index e110c68321d46..fca06c0f47c22 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -402,13 +402,6 @@ pub struct FailedCopyToStdout { pub err: Error, } -#[derive(Diagnostic)] -#[diag(metadata_failed_remove)] -pub struct FailedRemove { - pub filename: PathBuf, - pub err: Error, -} - #[derive(Diagnostic)] #[diag(metadata_binary_output_to_tty)] pub struct BinaryOutputToTty; diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 5be99c8e4c0d8..238f963ed4630 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,6 +1,6 @@ use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, - FailedCreateTempdir, FailedRemove, FailedWriteError, + FailedCreateTempdir, FailedWriteError, }; use crate::{encode_metadata, EncodedMetadata}; @@ -109,13 +109,6 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); - // Delete the temporary metadata file if output is stdout - if need_metadata_file && out_filename.is_stdout() { - if let Err(err) = fs::remove_file(&metadata_filename) { - tcx.sess.emit_err(FailedRemove { filename: metadata_filename, err }); - } - } - let need_metadata_module = metadata_kind == MetadataKind::Compressed; (metadata, need_metadata_module) diff --git a/tests/run-make/emit-to-stdout/Makefile b/tests/run-make/emit-to-stdout/Makefile index b7455965cffa2..80e9b6a4ded67 100644 --- a/tests/run-make/emit-to-stdout/Makefile +++ b/tests/run-make/emit-to-stdout/Makefile @@ -32,7 +32,7 @@ obj: $(OUT) # it there. metadata: $(OUT) cp $(SRC) $(OUT) - (cd $(OUT); pwd; ls -d; $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) + (cd $(OUT); $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) diff $(OUT)/$@ emit-metadata.stderr link: $(OUT)