Auto merge of #28768 - alexcrichton:dep-info++, r=brson

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.
This commit is contained in:
bors 2015-10-02 16:33:26 +00:00
commit e650491f20
11 changed files with 194 additions and 145 deletions

View File

@ -53,7 +53,9 @@ Comma separated list of types of crates for the compiler to emit.
Specify the name of the crate being built. Specify the name of the crate being built.
.TP .TP
\fB\-\-emit\fR [asm|llvm\-bc|llvm\-ir|obj|link|dep\-info] \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 .TP
\fB\-\-print\fR [crate\-name|file\-names|sysroot] \fB\-\-print\fR [crate\-name|file\-names|sysroot]
Comma separated list of compiler information to print on stdout. Comma separated list of compiler information to print on stdout.
@ -66,7 +68,8 @@ Equivalent to \fI\-C\ opt\-level=2\fR.
.TP .TP
\fB\-o\fR \fIFILENAME\fR \fB\-o\fR \fIFILENAME\fR
Write output to \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 .TP
\fB\-\-out\-dir\fR \fIDIR\fR \fB\-\-out\-dir\fR \fIDIR\fR
Write output to compiler\[hy]chosen filename in \fIDIR\fR. Write output to compiler\[hy]chosen filename in \fIDIR\fR.

View File

@ -15,7 +15,6 @@ pub use self::EntryFnType::*;
pub use self::CrateType::*; pub use self::CrateType::*;
pub use self::Passes::*; pub use self::Passes::*;
pub use self::OptLevel::*; pub use self::OptLevel::*;
pub use self::OutputType::*;
pub use self::DebugInfoLevel::*; pub use self::DebugInfoLevel::*;
use session::{early_error, early_warn, Session}; use session::{early_error, early_warn, Session};
@ -62,14 +61,14 @@ pub enum DebugInfoLevel {
FullDebugInfo, FullDebugInfo,
} }
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)] #[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum OutputType { pub enum OutputType {
OutputTypeBitcode, Bitcode,
OutputTypeAssembly, Assembly,
OutputTypeLlvmAssembly, LlvmAssembly,
OutputTypeObject, Object,
OutputTypeExe, Exe,
OutputTypeDepInfo, DepInfo,
} }
#[derive(Clone)] #[derive(Clone)]
@ -85,7 +84,7 @@ pub struct Options {
pub lint_opts: Vec<(String, lint::Level)>, pub lint_opts: Vec<(String, lint::Level)>,
pub lint_cap: Option<lint::Level>, pub lint_cap: Option<lint::Level>,
pub describe_lints: bool, 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 // This was mutable for rustpkg, which updates search paths based on the
// parsed code. It remains mutable in case its replacements wants to use // parsed code. It remains mutable in case its replacements wants to use
// this. // this.
@ -105,8 +104,6 @@ pub struct Options {
pub always_build_mir: bool, pub always_build_mir: bool,
pub no_analysis: bool, pub no_analysis: bool,
pub debugging_opts: DebuggingOptions, 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 prints: Vec<PrintRequest>,
pub cg: CodegenOptions, pub cg: CodegenOptions,
pub color: ColorConfig, pub color: ColorConfig,
@ -151,26 +148,25 @@ pub struct OutputFilenames {
pub out_filestem: String, pub out_filestem: String,
pub single_output_file: Option<PathBuf>, pub single_output_file: Option<PathBuf>,
pub extra: String, pub extra: String,
pub outputs: HashMap<OutputType, Option<PathBuf>>,
} }
impl OutputFilenames { impl OutputFilenames {
pub fn path(&self, flavor: OutputType) -> PathBuf { pub fn path(&self, flavor: OutputType) -> PathBuf {
match self.single_output_file { self.outputs.get(&flavor).and_then(|p| p.to_owned())
Some(ref path) => return path.clone(), .or_else(|| self.single_output_file.clone())
None => {} .unwrap_or_else(|| self.temp_path(flavor))
}
self.temp_path(flavor)
} }
pub fn temp_path(&self, flavor: OutputType) -> PathBuf { pub fn temp_path(&self, flavor: OutputType) -> PathBuf {
let base = self.out_directory.join(&self.filestem()); let base = self.out_directory.join(&self.filestem());
match flavor { match flavor {
OutputTypeBitcode => base.with_extension("bc"), OutputType::Bitcode => base.with_extension("bc"),
OutputTypeAssembly => base.with_extension("s"), OutputType::Assembly => base.with_extension("s"),
OutputTypeLlvmAssembly => base.with_extension("ll"), OutputType::LlvmAssembly => base.with_extension("ll"),
OutputTypeObject => base.with_extension("o"), OutputType::Object => base.with_extension("o"),
OutputTypeDepInfo => base.with_extension("d"), OutputType::DepInfo => base.with_extension("d"),
OutputTypeExe => base, OutputType::Exe => base,
} }
} }
@ -206,7 +202,7 @@ pub fn basic_options() -> Options {
lint_opts: Vec::new(), lint_opts: Vec::new(),
lint_cap: None, lint_cap: None,
describe_lints: false, describe_lints: false,
output_types: Vec::new(), output_types: HashMap::new(),
search_paths: SearchPaths::new(), search_paths: SearchPaths::new(),
maybe_sysroot: None, maybe_sysroot: None,
target_triple: host_triple().to_string(), target_triple: host_triple().to_string(),
@ -218,7 +214,6 @@ pub fn basic_options() -> Options {
always_build_mir: false, always_build_mir: false,
no_analysis: false, no_analysis: false,
debugging_opts: basic_debugging_options(), debugging_opts: basic_debugging_options(),
write_dependency_info: (false, None),
prints: Vec::new(), prints: Vec::new(),
cg: basic_codegen_options(), cg: basic_codegen_options(),
color: Auto, color: Auto,
@ -907,31 +902,30 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
unsafe { llvm::LLVMSetDebug(1); } unsafe { llvm::LLVMSetDebug(1); }
} }
let mut output_types = Vec::new(); let mut output_types = HashMap::new();
if !debugging_opts.parse_only && !no_trans { if !debugging_opts.parse_only && !no_trans {
let unparsed_output_types = matches.opt_strs("emit"); for list in matches.opt_strs("emit") {
for unparsed_output_type in &unparsed_output_types { for output_type in list.split(',') {
for part in unparsed_output_type.split(',') { let mut parts = output_type.splitn(2, '=');
let output_type = match part { let output_type = match parts.next().unwrap() {
"asm" => OutputTypeAssembly, "asm" => OutputType::Assembly,
"llvm-ir" => OutputTypeLlvmAssembly, "llvm-ir" => OutputType::LlvmAssembly,
"llvm-bc" => OutputTypeBitcode, "llvm-bc" => OutputType::Bitcode,
"obj" => OutputTypeObject, "obj" => OutputType::Object,
"link" => OutputTypeExe, "link" => OutputType::Exe,
"dep-info" => OutputTypeDepInfo, "dep-info" => OutputType::DepInfo,
_ => { part => {
early_error(color, &format!("unknown emission type: `{}`", early_error(color, &format!("unknown emission type: `{}`",
part)) 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() { if output_types.is_empty() {
output_types.push(OutputTypeExe); output_types.insert(OutputType::Exe, None);
} }
let cg = build_codegen_options(matches, color); let cg = build_codegen_options(matches, color);
@ -1004,7 +998,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let cfg = parse_cfgspecs(matches.opt_strs("cfg")); let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
let test = matches.opt_present("test"); 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| { let prints = matches.opt_strs("print").into_iter().map(|s| {
match &*s { match &*s {
@ -1059,7 +1052,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
always_build_mir: always_build_mir, always_build_mir: always_build_mir,
no_analysis: no_analysis, no_analysis: no_analysis,
debugging_opts: debugging_opts, debugging_opts: debugging_opts,
write_dependency_info: write_dependency_info,
prints: prints, prints: prints,
cg: cg, cg: cg,
color: color, color: color,

View File

@ -12,7 +12,7 @@ use rustc::front;
use rustc::front::map as hir_map; use rustc::front::map as hir_map;
use rustc_mir as mir; use rustc_mir as mir;
use rustc::session::Session; 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::session::search_paths::PathKind;
use rustc::lint; use rustc::lint;
use rustc::metadata; use rustc::metadata;
@ -36,6 +36,7 @@ use super::Compilation;
use serialize::json; use serialize::json;
use std::collections::HashMap;
use std::env; use std::env;
use std::ffi::{OsString, OsStr}; use std::ffi::{OsString, OsStr};
use std::fs; use std::fs;
@ -117,7 +118,7 @@ pub fn compile_input(sess: Session,
let arenas = ty::CtxtArenas::new(); let arenas = ty::CtxtArenas::new();
let ast_map = make_map(&sess, &mut hir_forest); 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, controller_entry_point!(after_write_deps,
sess, sess,
@ -807,16 +808,16 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
trans: &trans::CrateTranslation, trans: &trans::CrateTranslation,
outputs: &OutputFilenames) { outputs: &OutputFilenames) {
if sess.opts.cg.no_integrated_as { 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", || 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); write::run_assembler(sess, outputs);
// Remove assembly source, unless --save-temps was specified // Remove assembly source, unless --save-temps was specified
if !sess.opts.cg.save_temps { 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 { } else {
time(sess.time_passes(), "LLVM passes", || time(sess.time_passes(), "LLVM passes", ||
@ -847,16 +848,12 @@ fn escape_dep_filename(filename: &str) -> String {
filename.replace(" ", "\\ ") filename.replace(" ", "\\ ")
} }
fn write_out_deps(sess: &Session, fn write_out_deps(sess: &Session, outputs: &OutputFilenames, id: &str) {
input: &Input,
outputs: &OutputFilenames,
id: &str) {
let mut out_filenames = Vec::new(); 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); let file = outputs.path(*output_type);
match *output_type { match *output_type {
config::OutputTypeExe => { OutputType::Exe => {
for output in sess.crate_types.borrow().iter() { for output in sess.crate_types.borrow().iter() {
let p = link::filename_for_input(sess, *output, id, let p = link::filename_for_input(sess, *output, id,
outputs); outputs);
@ -867,23 +864,11 @@ fn write_out_deps(sess: &Session,
} }
} }
// Write out dependency rules to the dep-info file if requested with // Write out dependency rules to the dep-info file if requested
// --dep-info if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
let deps_filename = match sess.opts.write_dependency_info { return
// Use filename from --dep-file argument if given }
(true, Some(ref filename)) => filename.clone(), let deps_filename = outputs.path(OutputType::DepInfo);
// 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,
};
let result = (|| -> io::Result<()> { let result = (|| -> io::Result<()> {
// Build a list of files used to compile the output and // Build a list of files used to compile the output and
@ -896,9 +881,16 @@ fn write_out_deps(sess: &Session,
.collect(); .collect();
let mut file = try!(fs::File::create(&deps_filename)); let mut file = try!(fs::File::create(&deps_filename));
for path in &out_filenames { for path in &out_filenames {
try!(write!(&mut file, try!(write!(file,
"{}: {}\n\n", path.display(), files.join(" "))); "{}: {}\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(()) Ok(())
})(); })();
@ -1012,11 +1004,15 @@ pub fn build_output_filenames(input: &Input,
out_filestem: stem, out_filestem: stem,
single_output_file: None, single_output_file: None,
extra: sess.opts.cg.extra_filename.clone(), extra: sess.opts.cg.extra_filename.clone(),
outputs: sess.opts.output_types.clone(),
} }
} }
Some(ref out_file) => { 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 \ sess.warn("ignoring specified output filename because multiple \
outputs were requested"); outputs were requested");
None None
@ -1035,6 +1031,7 @@ pub fn build_output_filenames(input: &Input,
.to_str().unwrap().to_string(), .to_str().unwrap().to_string(),
single_output_file: ofile, single_output_file: ofile,
extra: sess.opts.cg.extra_filename.clone(), extra: sess.opts.cg.extra_filename.clone(),
outputs: sess.opts.output_types.clone(),
} }
} }
} }

View File

@ -63,7 +63,7 @@ use rustc_resolve as resolve;
use rustc_trans::back::link; use rustc_trans::back::link;
use rustc_trans::save; use rustc_trans::save;
use rustc::session::{config, Session, build_session}; 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::Lint;
use rustc::lint; use rustc::lint;
use rustc::metadata; use rustc::metadata;
@ -382,7 +382,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
control.after_analysis.stop = Compilation::Stop; 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; control.after_llvm.stop = Compilation::Stop;
} }

View File

@ -16,7 +16,7 @@ use super::msvc;
use super::svh::Svh; use super::svh::Svh;
use session::config; use session::config;
use session::config::NoDebugInfo; 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::search_paths::PathKind;
use session::Session; use session::Session;
use metadata::common::LinkMeta; use metadata::common::LinkMeta;
@ -491,7 +491,7 @@ pub fn filename_for_input(sess: &Session,
} }
config::CrateTypeExecutable => { config::CrateTypeExecutable => {
let suffix = &sess.target.target.options.exe_suffix; 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() { if suffix.is_empty() {
out_filename.to_path_buf() out_filename.to_path_buf()
} else { } else {
@ -532,10 +532,12 @@ fn link_binary_output(sess: &Session,
outputs: &OutputFilenames, outputs: &OutputFilenames,
crate_name: &str) -> PathBuf { crate_name: &str) -> PathBuf {
let objects = object_filenames(sess, outputs); let objects = object_filenames(sess, outputs);
let out_filename = match outputs.single_output_file { let default_filename = filename_for_input(sess, crate_type, crate_name,
Some(ref file) => file.clone(), outputs);
None => 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 // Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
// check this already -- however, the Linux linker will happily overwrite a // check this already -- however, the Linux linker will happily overwrite a
@ -576,7 +578,7 @@ fn link_binary_output(sess: &Session,
fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec<PathBuf> { fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec<PathBuf> {
(0..sess.opts.cg.codegen_units).map(|i| { (0..sess.opts.cg.codegen_units).map(|i| {
let ext = format!("{}.o", i); let ext = format!("{}.o", i);
outputs.temp_path(OutputTypeObject).with_extension(&ext) outputs.temp_path(OutputType::Object).with_extension(&ext)
}).collect() }).collect()
} }
@ -723,7 +725,7 @@ fn link_rlib<'a>(sess: &'a Session,
// See the bottom of back::write::run_passes for an explanation // See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .0.bc files around. // of when we do and don't keep .0.bc files around.
let user_wants_numbered_bitcode = 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; sess.opts.cg.codegen_units > 1;
if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode { if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode {
remove(sess, &bc_filename); remove(sess, &bc_filename);

View File

@ -12,7 +12,7 @@ use back::lto;
use back::link::{get_linker, remove}; use back::link::{get_linker, remove};
use session::config::{OutputFilenames, Passes, SomePasses, AllPasses}; use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
use session::Session; use session::Session;
use session::config; use session::config::{self, OutputType};
use llvm; use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
use llvm::SMDiagnosticRef; use llvm::SMDiagnosticRef;
@ -23,9 +23,10 @@ use syntax::codemap;
use syntax::diagnostic; use syntax::diagnostic;
use syntax::diagnostic::{Emitter, Handler, Level}; use syntax::diagnostic::{Emitter, Handler, Level};
use std::collections::HashMap;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::fs; use std::fs;
use std::path::Path; use std::path::{Path, PathBuf};
use std::ptr; use std::ptr;
use std::str; use std::str;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -33,15 +34,6 @@ use std::sync::mpsc::channel;
use std::thread; use std::thread;
use libc::{self, c_uint, c_int, c_void}; use libc::{self, c_uint, c_int, c_void};
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
pub enum OutputType {
OutputTypeBitcode,
OutputTypeAssembly,
OutputTypeLlvmAssembly,
OutputTypeObject,
OutputTypeExe,
}
pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! { pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
unsafe { unsafe {
let cstr = llvm::LLVMRustGetLastError(); let cstr = llvm::LLVMRustGetLastError();
@ -571,7 +563,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
pub fn run_passes(sess: &Session, pub fn run_passes(sess: &Session,
trans: &CrateTranslation, trans: &CrateTranslation,
output_types: &[config::OutputType], output_types: &HashMap<OutputType, Option<PathBuf>>,
crate_output: &OutputFilenames) { crate_output: &OutputFilenames) {
// It's possible that we have `codegen_units > 1` but only one item in // It's possible that we have `codegen_units > 1` but only one item in
// `trans.modules`. We could theoretically proceed and do LTO in that // `trans.modules`. We could theoretically proceed and do LTO in that
@ -611,32 +603,32 @@ pub fn run_passes(sess: &Session,
// archive in order to allow LTO against it. // archive in order to allow LTO against it.
let needs_crate_bitcode = let needs_crate_bitcode =
sess.crate_types.borrow().contains(&config::CrateTypeRlib) && sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
sess.opts.output_types.contains(&config::OutputTypeExe); sess.opts.output_types.contains_key(&OutputType::Exe);
let needs_crate_object = let needs_crate_object =
sess.opts.output_types.contains(&config::OutputTypeExe); sess.opts.output_types.contains_key(&OutputType::Exe);
if needs_crate_bitcode { if needs_crate_bitcode {
modules_config.emit_bc = true; modules_config.emit_bc = true;
} }
for output_type in output_types { for output_type in output_types.keys() {
match *output_type { match *output_type {
config::OutputTypeBitcode => { modules_config.emit_bc = true; }, OutputType::Bitcode => { modules_config.emit_bc = true; },
config::OutputTypeLlvmAssembly => { modules_config.emit_ir = true; }, OutputType::LlvmAssembly => { modules_config.emit_ir = true; },
config::OutputTypeAssembly => { OutputType::Assembly => {
modules_config.emit_asm = true; modules_config.emit_asm = true;
// If we're not using the LLVM assembler, this function // If we're not using the LLVM assembler, this function
// could be invoked specially with output_type_assembly, so // could be invoked specially with output_type_assembly, so
// in this case we still want the metadata object file. // in this case we still want the metadata object file.
if !sess.opts.output_types.contains(&config::OutputTypeAssembly) { if !sess.opts.output_types.contains_key(&OutputType::Assembly) {
metadata_config.emit_obj = true; metadata_config.emit_obj = true;
} }
}, },
config::OutputTypeObject => { modules_config.emit_obj = true; }, OutputType::Object => { modules_config.emit_obj = true; },
config::OutputTypeExe => { OutputType::Exe => {
modules_config.emit_obj = true; modules_config.emit_obj = true;
metadata_config.emit_obj = true; metadata_config.emit_obj = true;
}, },
config::OutputTypeDepInfo => {} OutputType::DepInfo => {}
} }
} }
@ -686,8 +678,9 @@ pub fn run_passes(sess: &Session,
} }
}; };
let copy_if_one_unit = |ext: &str, output_type: config::OutputType, keep_numbered: bool| { let copy_if_one_unit = |ext: &str,
// Three cases: output_type: OutputType,
keep_numbered: bool| {
if sess.opts.cg.codegen_units == 1 { if sess.opts.cg.codegen_units == 1 {
// 1) Only one codegen unit. In this case it's no difficulty // 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`. // to copy `foo.0.x` to `foo.x`.
@ -697,17 +690,20 @@ pub fn run_passes(sess: &Session,
// The user just wants `foo.x`, not `foo.0.x`. // The user just wants `foo.x`, not `foo.0.x`.
remove(sess, &crate_output.with_extension(ext)); remove(sess, &crate_output.with_extension(ext));
} }
} else if crate_output.outputs.contains_key(&output_type) {
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(&format!("ignoring emit path because multiple .{} files \
were produced", ext));
} else if crate_output.single_output_file.is_some() {
// 3) Multiple codegen units, with `-o some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(&format!("ignoring -o because multiple .{} files \
were produced", ext));
} else { } else {
if crate_output.single_output_file.is_some() { // 4) Multiple codegen units, but no explicit name. We
// 2) Multiple codegen units, with `-o some_name`. We have // just leave the `foo.0.x` files in place.
// no good solution for this case, so warn the user. // (We don't have to do any work in this case.)
sess.warn(&format!("ignoring -o because multiple .{} files were produced",
ext));
} else {
// 3) Multiple codegen units, but no `-o some_name`. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
} }
}; };
@ -716,27 +712,27 @@ pub fn run_passes(sess: &Session,
// to get rid of it. // to get rid of it.
let mut user_wants_bitcode = false; let mut user_wants_bitcode = false;
let mut user_wants_objects = false; let mut user_wants_objects = false;
for output_type in output_types { for output_type in output_types.keys() {
match *output_type { match *output_type {
config::OutputTypeBitcode => { OutputType::Bitcode => {
user_wants_bitcode = true; user_wants_bitcode = true;
// Copy to .bc, but always keep the .0.bc. There is a later // Copy to .bc, but always keep the .0.bc. There is a later
// check to figure out if we should delete .0.bc files, or keep // check to figure out if we should delete .0.bc files, or keep
// them for making an rlib. // them for making an rlib.
copy_if_one_unit("0.bc", config::OutputTypeBitcode, true); copy_if_one_unit("0.bc", OutputType::Bitcode, true);
} }
config::OutputTypeLlvmAssembly => { OutputType::LlvmAssembly => {
copy_if_one_unit("0.ll", config::OutputTypeLlvmAssembly, false); copy_if_one_unit("0.ll", OutputType::LlvmAssembly, false);
} }
config::OutputTypeAssembly => { OutputType::Assembly => {
copy_if_one_unit("0.s", config::OutputTypeAssembly, false); copy_if_one_unit("0.s", OutputType::Assembly, false);
} }
config::OutputTypeObject => { OutputType::Object => {
user_wants_objects = true; user_wants_objects = true;
copy_if_one_unit("0.o", config::OutputTypeObject, true); copy_if_one_unit("0.o", OutputType::Object, true);
} }
config::OutputTypeExe | OutputType::Exe |
config::OutputTypeDepInfo => {} OutputType::DepInfo => {}
} }
} }
let user_wants_bitcode = user_wants_bitcode; let user_wants_bitcode = user_wants_bitcode;
@ -913,8 +909,8 @@ fn run_work_multithreaded(sess: &Session,
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
let (pname, mut cmd) = get_linker(sess); let (pname, mut cmd) = get_linker(sess);
cmd.arg("-c").arg("-o").arg(&outputs.path(config::OutputTypeObject)) cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object))
.arg(&outputs.temp_path(config::OutputTypeAssembly)); .arg(&outputs.temp_path(OutputType::Assembly));
debug!("{:?}", cmd); debug!("{:?}", cmd);
match cmd.output() { match cmd.output() {

View File

@ -24,7 +24,7 @@ use testing;
use rustc_lint; use rustc_lint;
use rustc::front::map as hir_map; use rustc::front::map as hir_map;
use rustc::session::{self, config}; use rustc::session::{self, config};
use rustc::session::config::get_unstable_features_setting; use rustc::session::config::{get_unstable_features_setting, OutputType};
use rustc::session::search_paths::{SearchPaths, PathKind}; use rustc::session::search_paths::{SearchPaths, PathKind};
use rustc_front::lowering::lower_crate; use rustc_front::lowering::lower_crate;
use rustc_back::tempdir::TempDir; use rustc_back::tempdir::TempDir;
@ -167,13 +167,15 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
// never wrap the test in `fn main() { ... }` // never wrap the test in `fn main() { ... }`
let test = maketest(test, Some(cratename), as_test_harness, opts); let test = maketest(test, Some(cratename), as_test_harness, opts);
let input = config::Input::Str(test.to_string()); let input = config::Input::Str(test.to_string());
let mut outputs = HashMap::new();
outputs.insert(OutputType::Exe, None);
let sessopts = config::Options { let sessopts = config::Options {
maybe_sysroot: Some(env::current_exe().unwrap().parent().unwrap() maybe_sysroot: Some(env::current_exe().unwrap().parent().unwrap()
.parent().unwrap().to_path_buf()), .parent().unwrap().to_path_buf()),
search_paths: libs, search_paths: libs,
crate_types: vec!(config::CrateTypeExecutable), crate_types: vec!(config::CrateTypeExecutable),
output_types: vec!(config::OutputTypeExe), output_types: outputs,
externs: externs, externs: externs,
cg: config::CodegenOptions { cg: config::CodegenOptions {
prefer_dynamic: true, prefer_dynamic: true,

View File

@ -7,9 +7,10 @@
ifneq ($(shell uname),FreeBSD) ifneq ($(shell uname),FreeBSD)
ifndef IS_WINDOWS ifndef IS_WINDOWS
all: all:
$(RUSTC) --emit dep-info,link --crate-type=lib lib.rs cp *.rs $(TMPDIR)
$(RUSTC) --emit dep-info,link --crate-type=lib $(TMPDIR)/lib.rs
sleep 2 sleep 2
touch foo.rs touch $(TMPDIR)/foo.rs
-rm -f $(TMPDIR)/done -rm -f $(TMPDIR)/done
$(MAKE) -drf Makefile.foo $(MAKE) -drf Makefile.foo
sleep 2 sleep 2
@ -17,6 +18,11 @@ all:
pwd pwd
$(MAKE) -drf Makefile.foo $(MAKE) -drf Makefile.foo
rm $(TMPDIR)/done && exit 1 || exit 0 rm $(TMPDIR)/done && exit 1 || exit 0
# When a source file is deleted `make` should still work
rm $(TMPDIR)/bar.rs
cp $(TMPDIR)/lib2.rs $(TMPDIR)/lib.rs
$(MAKE) -drf Makefile.foo
else else
all: all:

View File

@ -0,0 +1,13 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_name = "foo"]
pub mod foo;

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(rustc_private, path, convert)] #![feature(rustc_private)]
extern crate rustc; extern crate rustc;
extern crate rustc_driver; extern crate rustc_driver;
@ -16,7 +16,7 @@ extern crate rustc_lint;
extern crate syntax; extern crate syntax;
use rustc::session::{build_session, Session}; use rustc::session::{build_session, Session};
use rustc::session::config::{basic_options, build_configuration, Input, OutputTypeExe}; use rustc::session::config::{basic_options, build_configuration, Input, OutputType};
use rustc_driver::driver::{compile_input, CompileController}; use rustc_driver::driver::{compile_input, CompileController};
use syntax::diagnostics::registry::Registry; use syntax::diagnostics::registry::Registry;
@ -46,7 +46,7 @@ fn main() {
fn basic_sess(sysroot: PathBuf) -> Session { fn basic_sess(sysroot: PathBuf) -> Session {
let mut opts = basic_options(); let mut opts = basic_options();
opts.output_types = vec![OutputTypeExe]; opts.output_types.insert(OutputType::Exe, None);
opts.maybe_sysroot = Some(sysroot); opts.maybe_sysroot = Some(sysroot);
let descriptions = Registry::new(&rustc::DIAGNOSTICS); let descriptions = Registry::new(&rustc::DIAGNOSTICS);

View File

@ -23,45 +23,83 @@ all:
rm -f $(TMPDIR)/bar.pdb rm -f $(TMPDIR)/bar.pdb
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=asm -o $(TMPDIR)/foo $(RUSTC) foo.rs --emit asm -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit asm=$(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=llvm-bc -o $(TMPDIR)/foo $(RUSTC) foo.rs --emit llvm-bc -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit llvm-bc=$(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=llvm-ir -o $(TMPDIR)/foo $(RUSTC) foo.rs --emit llvm-ir -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit llvm-ir=$(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=obj -o $(TMPDIR)/foo $(RUSTC) foo.rs --emit obj -o $(TMPDIR)/foo
rm $(TMPDIR)/foo
$(RUSTC) foo.rs --emit obj=$(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=link -o $(TMPDIR)/$(call BIN,foo) $(RUSTC) foo.rs --emit link -o $(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo)
$(RUSTC) foo.rs --emit link=$(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo) rm $(TMPDIR)/$(call BIN,foo)
rm -f $(TMPDIR)/foo.pdb rm -f $(TMPDIR)/foo.pdb
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo $(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
$(RUSTC) foo.rs --crate-type=rlib --emit link=$(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=dylib -o $(TMPDIR)/$(call BIN,foo) $(RUSTC) foo.rs --crate-type=dylib -o $(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo) rm $(TMPDIR)/$(call BIN,foo)
$(RUSTC) foo.rs --crate-type=dylib --emit link=$(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo)
rm -f $(TMPDIR)/foo.{exp,lib,pdb} rm -f $(TMPDIR)/foo.{exp,lib,pdb}
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo $(RUSTC) foo.rs --crate-type=staticlib -o $(TMPDIR)/foo
rm $(TMPDIR)/foo rm $(TMPDIR)/foo
$(RUSTC) foo.rs --crate-type=staticlib --emit link=$(TMPDIR)/foo
rm $(TMPDIR)/foo
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --crate-type=bin -o $(TMPDIR)/$(call BIN,foo) $(RUSTC) foo.rs --crate-type=bin -o $(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo) rm $(TMPDIR)/$(call BIN,foo)
$(RUSTC) foo.rs --crate-type=bin --emit link=$(TMPDIR)/$(call BIN,foo)
rm $(TMPDIR)/$(call BIN,foo)
rm -f $(TMPDIR)/foo.pdb rm -f $(TMPDIR)/foo.pdb
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit llvm-ir=$(TMPDIR)/ir \
--emit link \
--crate-type=rlib
rm $(TMPDIR)/ir
rm $(TMPDIR)/libbar.rlib
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit asm=$(TMPDIR)/asm \
--emit llvm-ir=$(TMPDIR)/ir \
--emit llvm-bc=$(TMPDIR)/bc \
--emit obj=$(TMPDIR)/obj \
--emit link=$(TMPDIR)/link \
--crate-type=staticlib
rm $(TMPDIR)/asm
rm $(TMPDIR)/ir
rm $(TMPDIR)/bc
rm $(TMPDIR)/obj
rm $(TMPDIR)/link
[ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ]
$(RUSTC) foo.rs --emit=asm,llvm-ir,llvm-bc,obj,link --crate-type=staticlib $(RUSTC) foo.rs --emit=asm,llvm-ir,llvm-bc,obj,link --crate-type=staticlib
rm $(TMPDIR)/bar.ll rm $(TMPDIR)/bar.ll
rm $(TMPDIR)/bar.s rm $(TMPDIR)/bar.s