diff options
| author | Jing Peng <pj.hades@gmail.com> | 2023-02-26 15:27:27 -0500 |
|---|---|---|
| committer | Jing Peng <pj.hades@gmail.com> | 2023-06-06 17:53:29 -0400 |
| commit | 9b1a1e1d95d1e40bdf57ef9d37ccbac91fc9c280 (patch) | |
| tree | a9dfaf211a9e470ba26b20416916ab214a2be476 /compiler/rustc_interface | |
| parent | 1221e43bdf413f7c405e9b17ef19d76c88222098 (diff) | |
| download | rust-9b1a1e1d95d1e40bdf57ef9d37ccbac91fc9c280.tar.gz rust-9b1a1e1d95d1e40bdf57ef9d37ccbac91fc9c280.zip | |
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.
Diffstat (limited to 'compiler/rustc_interface')
| -rw-r--r-- | compiler/rustc_interface/Cargo.toml | 1 | ||||
| -rw-r--r-- | compiler/rustc_interface/messages.ftl | 1 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/errors.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/interface.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/passes.rs | 75 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/tests.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/util.rs | 33 |
7 files changed, 99 insertions, 39 deletions
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 2c7438ed9db..7826d42dcb2 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 be1a75f020b..bd9fad8b042 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 0eedee25026..a9ab2720d89 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 39d56897999..2edc72ba72e 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<PathBuf>, - pub output_file: Option<PathBuf>, + pub output_file: Option<OutFileName>, pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>, pub locale_resources: &'static [&'static str], diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 42d8d228091..83a74742f5b 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<PathBuf> { 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 28e719a40e5..77ee2b40e37 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 cb19750203e..87252fefb1e 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<C base } +fn multiple_output_types_to_stdout( + output_types: &OutputTypes, + single_output_file_is_stdout: bool, +) -> 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(), |
