Auto merge of #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 https://github.com/rust-lang/compiler-team/issues/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.
This commit is contained in:
commit
343ad6f059
32 changed files with 439 additions and 101 deletions
|
@ -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 }
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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],
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue