Skip to content

Commit

Permalink
Auto merge of #28768 - alexcrichton:dep-info++, r=brson
Browse files Browse the repository at this point in the history
This PR closes out #28716 and #28735 by making two changes to the compiler:

1. The `--emit` flag to the compiler now supports the ability to specify the output file name of a partuclar emit type. For example `--emit dep-info=bar.d,asm=foo.s,link` is now accepted.
2. The dep-info emission now emits a dummy target for all input file names to protect against deleted files.
  • Loading branch information
bors committed Oct 2, 2015
2 parents 17a2cb4 + 1741962 commit e650491
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 145 deletions.
7 changes: 5 additions & 2 deletions man/rustc.1
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ Comma separated list of types of crates for the compiler to emit.
Specify the name of the crate being built.
.TP
\fB\-\-emit\fR [asm|llvm\-bc|llvm\-ir|obj|link|dep\-info]
Configure the output that \fBrustc\fR will produce.
Configure the output that \fBrustc\fR will produce. Each option may also be of
the form KIND=PATH to specify the explicit output location for that particular
emission kind.
.TP
\fB\-\-print\fR [crate\-name|file\-names|sysroot]
Comma separated list of compiler information to print on stdout.
Expand All @@ -66,7 +68,8 @@ Equivalent to \fI\-C\ opt\-level=2\fR.
.TP
\fB\-o\fR \fIFILENAME\fR
Write output to \fIFILENAME\fR.
Ignored if multiple \fI\-\-emit\fR outputs are specified.
Ignored if multiple \fI\-\-emit\fR outputs are specified which don't have an
explicit path otherwise.
.TP
\fB\-\-out\-dir\fR \fIDIR\fR
Write output to compiler\[hy]chosen filename in \fIDIR\fR.
Expand Down
76 changes: 34 additions & 42 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pub use self::EntryFnType::*;
pub use self::CrateType::*;
pub use self::Passes::*;
pub use self::OptLevel::*;
pub use self::OutputType::*;
pub use self::DebugInfoLevel::*;

use session::{early_error, early_warn, Session};
Expand Down Expand Up @@ -62,14 +61,14 @@ pub enum DebugInfoLevel {
FullDebugInfo,
}

#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum OutputType {
OutputTypeBitcode,
OutputTypeAssembly,
OutputTypeLlvmAssembly,
OutputTypeObject,
OutputTypeExe,
OutputTypeDepInfo,
Bitcode,
Assembly,
LlvmAssembly,
Object,
Exe,
DepInfo,
}

#[derive(Clone)]
Expand All @@ -85,7 +84,7 @@ pub struct Options {
pub lint_opts: Vec<(String, lint::Level)>,
pub lint_cap: Option<lint::Level>,
pub describe_lints: bool,
pub output_types: Vec<OutputType>,
pub output_types: HashMap<OutputType, Option<PathBuf>>,
// This was mutable for rustpkg, which updates search paths based on the
// parsed code. It remains mutable in case its replacements wants to use
// this.
Expand All @@ -105,8 +104,6 @@ pub struct Options {
pub always_build_mir: bool,
pub no_analysis: bool,
pub debugging_opts: DebuggingOptions,
/// Whether to write dependency files. It's (enabled, optional filename).
pub write_dependency_info: (bool, Option<PathBuf>),
pub prints: Vec<PrintRequest>,
pub cg: CodegenOptions,
pub color: ColorConfig,
Expand Down Expand Up @@ -151,26 +148,25 @@ pub struct OutputFilenames {
pub out_filestem: String,
pub single_output_file: Option<PathBuf>,
pub extra: String,
pub outputs: HashMap<OutputType, Option<PathBuf>>,
}

impl OutputFilenames {
pub fn path(&self, flavor: OutputType) -> PathBuf {
match self.single_output_file {
Some(ref path) => return path.clone(),
None => {}
}
self.temp_path(flavor)
self.outputs.get(&flavor).and_then(|p| p.to_owned())
.or_else(|| self.single_output_file.clone())
.unwrap_or_else(|| self.temp_path(flavor))
}

pub fn temp_path(&self, flavor: OutputType) -> PathBuf {
let base = self.out_directory.join(&self.filestem());
match flavor {
OutputTypeBitcode => base.with_extension("bc"),
OutputTypeAssembly => base.with_extension("s"),
OutputTypeLlvmAssembly => base.with_extension("ll"),
OutputTypeObject => base.with_extension("o"),
OutputTypeDepInfo => base.with_extension("d"),
OutputTypeExe => base,
OutputType::Bitcode => base.with_extension("bc"),
OutputType::Assembly => base.with_extension("s"),
OutputType::LlvmAssembly => base.with_extension("ll"),
OutputType::Object => base.with_extension("o"),
OutputType::DepInfo => base.with_extension("d"),
OutputType::Exe => base,
}
}

Expand Down Expand Up @@ -206,7 +202,7 @@ pub fn basic_options() -> Options {
lint_opts: Vec::new(),
lint_cap: None,
describe_lints: false,
output_types: Vec::new(),
output_types: HashMap::new(),
search_paths: SearchPaths::new(),
maybe_sysroot: None,
target_triple: host_triple().to_string(),
Expand All @@ -218,7 +214,6 @@ pub fn basic_options() -> Options {
always_build_mir: false,
no_analysis: false,
debugging_opts: basic_debugging_options(),
write_dependency_info: (false, None),
prints: Vec::new(),
cg: basic_codegen_options(),
color: Auto,
Expand Down Expand Up @@ -907,31 +902,30 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
unsafe { llvm::LLVMSetDebug(1); }
}

let mut output_types = Vec::new();
let mut output_types = HashMap::new();
if !debugging_opts.parse_only && !no_trans {
let unparsed_output_types = matches.opt_strs("emit");
for unparsed_output_type in &unparsed_output_types {
for part in unparsed_output_type.split(',') {
let output_type = match part {
"asm" => OutputTypeAssembly,
"llvm-ir" => OutputTypeLlvmAssembly,
"llvm-bc" => OutputTypeBitcode,
"obj" => OutputTypeObject,
"link" => OutputTypeExe,
"dep-info" => OutputTypeDepInfo,
_ => {
for list in matches.opt_strs("emit") {
for output_type in list.split(',') {
let mut parts = output_type.splitn(2, '=');
let output_type = match parts.next().unwrap() {
"asm" => OutputType::Assembly,
"llvm-ir" => OutputType::LlvmAssembly,
"llvm-bc" => OutputType::Bitcode,
"obj" => OutputType::Object,
"link" => OutputType::Exe,
"dep-info" => OutputType::DepInfo,
part => {
early_error(color, &format!("unknown emission type: `{}`",
part))
}
};
output_types.push(output_type)
let path = parts.next().map(PathBuf::from);
output_types.insert(output_type, path);
}
}
};
output_types.sort();
output_types.dedup();
if output_types.is_empty() {
output_types.push(OutputTypeExe);
output_types.insert(OutputType::Exe, None);
}

let cg = build_codegen_options(matches, color);
Expand Down Expand Up @@ -1004,7 +998,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {

let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
let test = matches.opt_present("test");
let write_dependency_info = (output_types.contains(&OutputTypeDepInfo), None);

let prints = matches.opt_strs("print").into_iter().map(|s| {
match &*s {
Expand Down Expand Up @@ -1059,7 +1052,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
always_build_mir: always_build_mir,
no_analysis: no_analysis,
debugging_opts: debugging_opts,
write_dependency_info: write_dependency_info,
prints: prints,
cg: cg,
color: color,
Expand Down
61 changes: 29 additions & 32 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc::front;
use rustc::front::map as hir_map;
use rustc_mir as mir;
use rustc::session::Session;
use rustc::session::config::{self, Input, OutputFilenames};
use rustc::session::config::{self, Input, OutputFilenames, OutputType};
use rustc::session::search_paths::PathKind;
use rustc::lint;
use rustc::metadata;
Expand All @@ -36,6 +36,7 @@ use super::Compilation;

use serialize::json;

use std::collections::HashMap;
use std::env;
use std::ffi::{OsString, OsStr};
use std::fs;
Expand Down Expand Up @@ -117,7 +118,7 @@ pub fn compile_input(sess: Session,
let arenas = ty::CtxtArenas::new();
let ast_map = make_map(&sess, &mut hir_forest);

write_out_deps(&sess, input, &outputs, &id[..]);
write_out_deps(&sess, &outputs, &id);

controller_entry_point!(after_write_deps,
sess,
Expand Down Expand Up @@ -807,16 +808,16 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
trans: &trans::CrateTranslation,
outputs: &OutputFilenames) {
if sess.opts.cg.no_integrated_as {
let output_type = config::OutputTypeAssembly;

let mut map = HashMap::new();
map.insert(OutputType::Assembly, None);
time(sess.time_passes(), "LLVM passes", ||
write::run_passes(sess, trans, &[output_type], outputs));
write::run_passes(sess, trans, &map, outputs));

write::run_assembler(sess, outputs);

// Remove assembly source, unless --save-temps was specified
if !sess.opts.cg.save_temps {
fs::remove_file(&outputs.temp_path(config::OutputTypeAssembly)).unwrap();
fs::remove_file(&outputs.temp_path(OutputType::Assembly)).unwrap();
}
} else {
time(sess.time_passes(), "LLVM passes", ||
Expand Down Expand Up @@ -847,16 +848,12 @@ fn escape_dep_filename(filename: &str) -> String {
filename.replace(" ", "\\ ")
}

fn write_out_deps(sess: &Session,
input: &Input,
outputs: &OutputFilenames,
id: &str) {

fn write_out_deps(sess: &Session, outputs: &OutputFilenames, id: &str) {
let mut out_filenames = Vec::new();
for output_type in &sess.opts.output_types {
for output_type in sess.opts.output_types.keys() {
let file = outputs.path(*output_type);
match *output_type {
config::OutputTypeExe => {
OutputType::Exe => {
for output in sess.crate_types.borrow().iter() {
let p = link::filename_for_input(sess, *output, id,
outputs);
Expand All @@ -867,23 +864,11 @@ fn write_out_deps(sess: &Session,
}
}

// Write out dependency rules to the dep-info file if requested with
// --dep-info
let deps_filename = match sess.opts.write_dependency_info {
// Use filename from --dep-file argument if given
(true, Some(ref filename)) => filename.clone(),
// Use default filename: crate source filename with extension replaced
// by ".d"
(true, None) => match *input {
Input::File(..) => outputs.with_extension("d"),
Input::Str(..) => {
sess.warn("can not write --dep-info without a filename \
when compiling stdin.");
return
},
},
_ => return,
};
// Write out dependency rules to the dep-info file if requested
if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
return
}
let deps_filename = outputs.path(OutputType::DepInfo);

let result = (|| -> io::Result<()> {
// Build a list of files used to compile the output and
Expand All @@ -896,9 +881,16 @@ fn write_out_deps(sess: &Session,
.collect();
let mut file = try!(fs::File::create(&deps_filename));
for path in &out_filenames {
try!(write!(&mut file,
try!(write!(file,
"{}: {}\n\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 {
try!(writeln!(file, "{}:", path));
}
Ok(())
})();

Expand Down Expand Up @@ -1012,11 +1004,15 @@ pub fn build_output_filenames(input: &Input,
out_filestem: stem,
single_output_file: None,
extra: sess.opts.cg.extra_filename.clone(),
outputs: sess.opts.output_types.clone(),
}
}

Some(ref out_file) => {
let ofile = if sess.opts.output_types.len() > 1 {
let unnamed_output_types = sess.opts.output_types.values()
.filter(|a| a.is_none())
.count();
let ofile = if unnamed_output_types > 1 {
sess.warn("ignoring specified output filename because multiple \
outputs were requested");
None
Expand All @@ -1035,6 +1031,7 @@ pub fn build_output_filenames(input: &Input,
.to_str().unwrap().to_string(),
single_output_file: ofile,
extra: sess.opts.cg.extra_filename.clone(),
outputs: sess.opts.output_types.clone(),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_driver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use rustc_resolve as resolve;
use rustc_trans::back::link;
use rustc_trans::save;
use rustc::session::{config, Session, build_session};
use rustc::session::config::{Input, PrintRequest};
use rustc::session::config::{Input, PrintRequest, OutputType};
use rustc::lint::Lint;
use rustc::lint;
use rustc::metadata;
Expand Down Expand Up @@ -382,7 +382,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
control.after_analysis.stop = Compilation::Stop;
}

if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) {
if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe) {
control.after_llvm.stop = Compilation::Stop;
}

Expand Down
18 changes: 10 additions & 8 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::msvc;
use super::svh::Svh;
use session::config;
use session::config::NoDebugInfo;
use session::config::{OutputFilenames, Input, OutputTypeBitcode, OutputTypeExe, OutputTypeObject};
use session::config::{OutputFilenames, Input, OutputType};
use session::search_paths::PathKind;
use session::Session;
use metadata::common::LinkMeta;
Expand Down Expand Up @@ -491,7 +491,7 @@ pub fn filename_for_input(sess: &Session,
}
config::CrateTypeExecutable => {
let suffix = &sess.target.target.options.exe_suffix;
let out_filename = outputs.path(OutputTypeExe);
let out_filename = outputs.path(OutputType::Exe);
if suffix.is_empty() {
out_filename.to_path_buf()
} else {
Expand Down Expand Up @@ -532,10 +532,12 @@ fn link_binary_output(sess: &Session,
outputs: &OutputFilenames,
crate_name: &str) -> PathBuf {
let objects = object_filenames(sess, outputs);
let out_filename = match outputs.single_output_file {
Some(ref file) => file.clone(),
None => filename_for_input(sess, crate_type, crate_name, outputs),
};
let default_filename = filename_for_input(sess, crate_type, crate_name,
outputs);
let out_filename = outputs.outputs.get(&OutputType::Exe)
.and_then(|s| s.to_owned())
.or_else(|| outputs.single_output_file.clone())
.unwrap_or(default_filename);

// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
// check this already -- however, the Linux linker will happily overwrite a
Expand Down Expand Up @@ -576,7 +578,7 @@ fn link_binary_output(sess: &Session,
fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec<PathBuf> {
(0..sess.opts.cg.codegen_units).map(|i| {
let ext = format!("{}.o", i);
outputs.temp_path(OutputTypeObject).with_extension(&ext)
outputs.temp_path(OutputType::Object).with_extension(&ext)
}).collect()
}

Expand Down Expand Up @@ -723,7 +725,7 @@ fn link_rlib<'a>(sess: &'a Session,
// See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .0.bc files around.
let user_wants_numbered_bitcode =
sess.opts.output_types.contains(&OutputTypeBitcode) &&
sess.opts.output_types.contains_key(&OutputType::Bitcode) &&
sess.opts.cg.codegen_units > 1;
if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode {
remove(sess, &bc_filename);
Expand Down
Loading

0 comments on commit e650491

Please sign in to comment.