Skip to content

Commit 343ad6f

Browse files
committed
Auto merge of rust-lang#111626 - pjhades:output, r=b-naber
Write to stdout if `-` is given as output file With this PR, if `-o -` or `--emit KIND=-` is provided, output will be written to stdout instead. Binary output (those of type `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. This implements rust-lang/compiler-team#431 The idea behind the changes is to introduce an `OutFileName` enum that represents the output - be it a real path or stdout - and to use this enum along the code paths that handle different output types.
2 parents dcc9028 + ade6c36 commit 343ad6f

File tree

32 files changed

+439
-101
lines changed

32 files changed

+439
-101
lines changed

Diff for: Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,7 @@ dependencies = [
35953595
name = "rustc_interface"
35963596
version = "0.0.0"
35973597
dependencies = [
3598+
"atty",
35983599
"libloading",
35993600
"rustc-rayon",
36003601
"rustc-rayon-core",
@@ -4059,6 +4060,7 @@ dependencies = [
40594060
name = "rustc_session"
40604061
version = "0.0.0"
40614062
dependencies = [
4063+
"atty",
40624064
"getopts",
40634065
"libc",
40644066
"rustc_ast",

Diff for: compiler/rustc_codegen_ssa/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ codegen_ssa_archive_build_failure =
99
1010
codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering
1111
12+
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
13+
1214
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.
1315
1416
codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}

Diff for: compiler/rustc_codegen_ssa/src/back/link.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, Handler};
88
use rustc_fs_util::fix_windows_verbatim_for_gcc;
99
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
1010
use rustc_metadata::find_native_static_library;
11-
use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
11+
use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
1212
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
1313
use rustc_middle::middle::dependency_format::Linkage;
1414
use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -68,6 +68,7 @@ pub fn link_binary<'a>(
6868
) -> Result<(), ErrorGuaranteed> {
6969
let _timer = sess.timer("link_binary");
7070
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
71+
let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
7172
for &crate_type in sess.crate_types().iter() {
7273
// Ignore executable crates if we have -Z no-codegen, as they will error.
7374
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
@@ -97,12 +98,15 @@ pub fn link_binary<'a>(
9798
.tempdir()
9899
.unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error }));
99100
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
100-
let out_filename = out_filename(
101+
let output = out_filename(
101102
sess,
102103
crate_type,
103104
outputs,
104105
codegen_results.crate_info.local_crate_name,
105106
);
107+
let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
108+
let out_filename =
109+
output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str()));
106110
match crate_type {
107111
CrateType::Rlib => {
108112
let _timer = sess.timer("link_rlib");
@@ -152,6 +156,17 @@ pub fn link_binary<'a>(
152156
);
153157
}
154158
}
159+
160+
if output.is_stdout() {
161+
if output.is_tty() {
162+
sess.emit_err(errors::BinaryOutputToTty {
163+
shorthand: OutputType::Exe.shorthand(),
164+
});
165+
} else if let Err(e) = copy_to_stdout(&out_filename) {
166+
sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e));
167+
}
168+
tempfiles_for_stdout_output.push(out_filename);
169+
}
155170
}
156171
}
157172

@@ -189,6 +204,11 @@ pub fn link_binary<'a>(
189204
remove_temps_from_module(allocator_module);
190205
}
191206

207+
// Remove the temporary files if output goes to stdout
208+
for temp in tempfiles_for_stdout_output {
209+
ensure_removed(sess.diagnostic(), &temp);
210+
}
211+
192212
// If no requested outputs require linking, then the object temporaries should
193213
// be kept.
194214
if !sess.opts.output_types.should_link() {

Diff for: compiler/rustc_codegen_ssa/src/back/write.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
2323
use rustc_incremental::{
2424
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
2525
};
26+
use rustc_metadata::fs::copy_to_stdout;
2627
use rustc_metadata::EncodedMetadata;
2728
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2829
use rustc_middle::middle::exported_symbols::SymbolExportInfo;
2930
use rustc_middle::ty::TyCtxt;
3031
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
31-
use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
32+
use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType};
3233
use rustc_session::config::{Passes, SwitchWithOptPath};
3334
use rustc_session::Session;
3435
use rustc_span::source_map::SourceMap;
@@ -535,9 +536,16 @@ fn produce_final_output_artifacts(
535536
let mut user_wants_objects = false;
536537

537538
// Produce final compile outputs.
538-
let copy_gracefully = |from: &Path, to: &Path| {
539-
if let Err(e) = fs::copy(from, to) {
540-
sess.emit_err(errors::CopyPath::new(from, to, e));
539+
let copy_gracefully = |from: &Path, to: &OutFileName| match to {
540+
OutFileName::Stdout => {
541+
if let Err(e) = copy_to_stdout(from) {
542+
sess.emit_err(errors::CopyPath::new(from, to.as_path(), e));
543+
}
544+
}
545+
OutFileName::Real(path) => {
546+
if let Err(e) = fs::copy(from, path) {
547+
sess.emit_err(errors::CopyPath::new(from, path, e));
548+
}
541549
}
542550
};
543551

@@ -547,7 +555,12 @@ fn produce_final_output_artifacts(
547555
// to copy `foo.0.x` to `foo.x`.
548556
let module_name = Some(&compiled_modules.modules[0].name[..]);
549557
let path = crate_output.temp_path(output_type, module_name);
550-
copy_gracefully(&path, &crate_output.path(output_type));
558+
let output = crate_output.path(output_type);
559+
if !output_type.is_text_output() && output.is_tty() {
560+
sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() });
561+
} else {
562+
copy_gracefully(&path, &output);
563+
}
551564
if !sess.opts.cg.save_temps && !keep_numbered {
552565
// The user just wants `foo.x`, not `foo.#module-name#.x`.
553566
ensure_removed(sess.diagnostic(), &path);

Diff for: compiler/rustc_codegen_ssa/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> {
8282
}
8383
}
8484

85+
#[derive(Diagnostic)]
86+
#[diag(codegen_ssa_binary_output_to_tty)]
87+
pub struct BinaryOutputToTty {
88+
pub shorthand: &'static str,
89+
}
90+
8591
#[derive(Diagnostic)]
8692
#[diag(codegen_ssa_ignoring_emit_path)]
8793
pub struct IgnoringEmitPath {

Diff for: compiler/rustc_driver_impl/src/lib.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ use rustc_interface::{interface, Queries};
3434
use rustc_lint::LintStore;
3535
use rustc_metadata::locator;
3636
use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
37-
use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
37+
use rustc_session::config::{
38+
ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths,
39+
};
3840
use rustc_session::cstore::MetadataLoader;
3941
use rustc_session::getopts::{self, Matches};
4042
use rustc_session::lint::{Lint, LintId};
@@ -454,9 +456,12 @@ fn run_compiler(
454456
}
455457

456458
// Extract output directory and file from matches.
457-
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
459+
fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
458460
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
459-
let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
461+
let ofile = matches.opt_str("o").map(|o| match o.as_str() {
462+
"-" => OutFileName::Stdout,
463+
path => OutFileName::Real(PathBuf::from(path)),
464+
});
460465
(odir, ofile)
461466
}
462467

@@ -702,7 +707,7 @@ fn print_crate_info(
702707
for &style in &crate_types {
703708
let fname =
704709
rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
705-
safe_println!("{}", fname.file_name().unwrap().to_string_lossy());
710+
safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
706711
}
707712
}
708713
Cfg => {

Diff for: compiler/rustc_driver_impl/src/pretty.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_hir_pretty as pprust_hir;
99
use rustc_middle::hir::map as hir_map;
1010
use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
1111
use rustc_middle::ty::{self, TyCtxt};
12-
use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
12+
use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
1313
use rustc_session::Session;
1414
use rustc_span::symbol::Ident;
1515
use rustc_span::FileName;
@@ -359,8 +359,8 @@ fn get_source(sess: &Session) -> (String, FileName) {
359359

360360
fn write_or_print(out: &str, sess: &Session) {
361361
match &sess.io.output_file {
362-
None => print!("{out}"),
363-
Some(p) => {
362+
None | Some(OutFileName::Stdout) => print!("{out}"),
363+
Some(OutFileName::Real(p)) => {
364364
if let Err(e) = std::fs::write(p, out) {
365365
sess.emit_fatal(UnprettyDumpFail {
366366
path: p.display().to_string(),

Diff for: compiler/rustc_interface/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[lib]
77

88
[dependencies]
9+
atty = "0.2.13"
910
libloading = "0.7.1"
1011
tracing = "0.1"
1112
rustc-rayon-core = { version = "0.5.0", optional = true }

Diff for: compiler/rustc_interface/messages.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ interface_mixed_proc_macro_crate =
3333
interface_multiple_output_types_adaption =
3434
due to multiple output types requested, the explicitly specified output file name will be adapted for each output type
3535
36+
interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout
3637
interface_out_dir_error =
3738
failed to find or create the directory specified by `--out-dir`
3839

Diff for: compiler/rustc_interface/src/errors.rs

+4
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,7 @@ pub struct IgnoringExtraFilename;
108108
#[derive(Diagnostic)]
109109
#[diag(interface_ignoring_out_dir)]
110110
pub struct IgnoringOutDir;
111+
112+
#[derive(Diagnostic)]
113+
#[diag(interface_multiple_output_types_to_stdout)]
114+
pub struct MultipleOutputTypesToStdout;

Diff for: compiler/rustc_interface/src/interface.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::{bug, ty};
1414
use rustc_parse::maybe_new_parser_from_source_str;
1515
use rustc_query_impl::QueryCtxt;
1616
use rustc_query_system::query::print_query_stack;
17-
use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
17+
use rustc_session::config::{self, ErrorOutputType, Input, OutFileName, OutputFilenames};
1818
use rustc_session::config::{CheckCfg, ExpectedValues};
1919
use rustc_session::lint;
2020
use rustc_session::parse::{CrateConfig, ParseSess};
@@ -252,7 +252,7 @@ pub struct Config {
252252

253253
pub input: Input,
254254
pub output_dir: Option<PathBuf>,
255-
pub output_file: Option<PathBuf>,
255+
pub output_file: Option<OutFileName>,
256256
pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
257257
pub locale_resources: &'static [&'static str],
258258

Diff for: compiler/rustc_interface/src/passes.rs

+47-28
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a
2424
use rustc_passes::{self, hir_stats, layout_test};
2525
use rustc_plugin_impl as plugin;
2626
use rustc_resolve::Resolver;
27-
use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
27+
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
2828
use rustc_session::cstore::{MetadataLoader, Untracked};
2929
use rustc_session::output::filename_for_input;
3030
use rustc_session::search_paths::PathKind;
@@ -373,19 +373,23 @@ fn generated_output_paths(
373373
) -> Vec<PathBuf> {
374374
let mut out_filenames = Vec::new();
375375
for output_type in sess.opts.output_types.keys() {
376-
let file = outputs.path(*output_type);
376+
let out_filename = outputs.path(*output_type);
377+
let file = out_filename.as_path().to_path_buf();
377378
match *output_type {
378379
// If the filename has been overridden using `-o`, it will not be modified
379380
// by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
380381
OutputType::Exe if !exact_name => {
381382
for crate_type in sess.crate_types().iter() {
382383
let p = filename_for_input(sess, *crate_type, crate_name, outputs);
383-
out_filenames.push(p);
384+
out_filenames.push(p.as_path().to_path_buf());
384385
}
385386
}
386387
OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => {
387388
// Don't add the dep-info output when omitting it from dep-info targets
388389
}
390+
OutputType::DepInfo if out_filename.is_stdout() => {
391+
// Don't add the dep-info output when it goes to stdout
392+
}
389393
_ => {
390394
out_filenames.push(file);
391395
}
@@ -452,7 +456,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
452456
if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
453457
return;
454458
}
455-
let deps_filename = outputs.path(OutputType::DepInfo);
459+
let deps_output = outputs.path(OutputType::DepInfo);
460+
let deps_filename = deps_output.as_path();
456461

457462
let result: io::Result<()> = try {
458463
// 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
515520
}
516521
}
517522

518-
let mut file = BufWriter::new(fs::File::create(&deps_filename)?);
519-
for path in out_filenames {
520-
writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
521-
}
523+
let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
524+
for path in out_filenames {
525+
writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
526+
}
522527

523-
// Emit a fake target for each input file to the compilation. This
524-
// prevents `make` from spitting out an error if a file is later
525-
// deleted. For more info see #28735
526-
for path in files {
527-
writeln!(file, "{path}:")?;
528-
}
528+
// Emit a fake target for each input file to the compilation. This
529+
// prevents `make` from spitting out an error if a file is later
530+
// deleted. For more info see #28735
531+
for path in files {
532+
writeln!(file, "{path}:")?;
533+
}
529534

530-
// Emit special comments with information about accessed environment variables.
531-
let env_depinfo = sess.parse_sess.env_depinfo.borrow();
532-
if !env_depinfo.is_empty() {
533-
let mut envs: Vec<_> = env_depinfo
534-
.iter()
535-
.map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
536-
.collect();
537-
envs.sort_unstable();
538-
writeln!(file)?;
539-
for (k, v) in envs {
540-
write!(file, "# env-dep:{k}")?;
541-
if let Some(v) = v {
542-
write!(file, "={v}")?;
543-
}
535+
// Emit special comments with information about accessed environment variables.
536+
let env_depinfo = sess.parse_sess.env_depinfo.borrow();
537+
if !env_depinfo.is_empty() {
538+
let mut envs: Vec<_> = env_depinfo
539+
.iter()
540+
.map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
541+
.collect();
542+
envs.sort_unstable();
544543
writeln!(file)?;
544+
for (k, v) in envs {
545+
write!(file, "# env-dep:{k}")?;
546+
if let Some(v) = v {
547+
write!(file, "={v}")?;
548+
}
549+
writeln!(file)?;
550+
}
551+
}
552+
553+
Ok(())
554+
};
555+
556+
match deps_output {
557+
OutFileName::Stdout => {
558+
let mut file = BufWriter::new(io::stdout());
559+
write_deps_to_file(&mut file)?;
560+
}
561+
OutFileName::Real(ref path) => {
562+
let mut file = BufWriter::new(fs::File::create(path)?);
563+
write_deps_to_file(&mut file)?;
545564
}
546565
}
547566
};

0 commit comments

Comments
 (0)