From 8c963c07a8e56320d33681050f94b467cfcc6251 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Sep 2015 10:08:37 -0700 Subject: [PATCH 1/2] rustc: Support output filenames for each emit type Currently the compiler supports the ability to emit multiple output types as part of one compilation (e.g. asm, LLVM IR, bytecode, link, dep-info, etc). It does not, however, support the ability to customize the output filename for each of these output types. The `-o` flag is ignored if multiple emit types are specified (and the compiler emits a warning about this). Normally this doesn't matter too much, but in the case of `dep-info` it can lead to a number of problems (e.g. see #28716). By allowing customization of the output filename for each emit type we're able to solve the problems in that issue. This commit adds support for the `--emit` option to the compiler to look like: rustc foo.rs --emit dep-info=.deps/foo.d,link This indicates that the `dep-info` output type will be placed at `.deps/foo.d` and the `link` output type will otherwise be determined via the `--out-dir` and `-o` flags. Closes #28716 --- man/rustc.1 | 7 +- src/librustc/session/config.rs | 76 +++++++--------- src/librustc_driver/driver.rs | 52 +++++------ src/librustc_driver/lib.rs | 4 +- src/librustc_trans/back/link.rs | 18 ++-- src/librustc_trans/back/write.rs | 90 +++++++++---------- src/librustdoc/test.rs | 6 +- src/test/run-make/issue-19371/foo.rs | 6 +- .../output-type-permutations/Makefile | 48 ++++++++-- 9 files changed, 165 insertions(+), 142 deletions(-) diff --git a/man/rustc.1 b/man/rustc.1 index 3ac79ecae64..56e3fe0515e 100644 --- a/man/rustc.1 +++ b/man/rustc.1 @@ -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. @@ -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. diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index dcc4ca137ea..0739420ea4d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -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}; @@ -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)] @@ -85,7 +84,7 @@ pub struct Options { pub lint_opts: Vec<(String, lint::Level)>, pub lint_cap: Option, pub describe_lints: bool, - pub output_types: Vec, + pub output_types: HashMap>, // 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. @@ -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), pub prints: Vec, pub cg: CodegenOptions, pub color: ColorConfig, @@ -151,26 +148,25 @@ pub struct OutputFilenames { pub out_filestem: String, pub single_output_file: Option, pub extra: String, + pub outputs: HashMap>, } 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, } } @@ -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(), @@ -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, @@ -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); @@ -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 { @@ -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, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ce04a5befd9..e2835ae8f0d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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; @@ -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; @@ -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, @@ -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", || @@ -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); @@ -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 @@ -1012,11 +997,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 @@ -1035,6 +1024,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(), } } } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 7d9c74fe487..d5644d49e1e 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -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; @@ -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; } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 4e493e6779c..1590d20b09e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -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; @@ -486,7 +486,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 { @@ -527,10 +527,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 @@ -571,7 +573,7 @@ fn link_binary_output(sess: &Session, fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec { (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() } @@ -718,7 +720,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); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 94e2891f40f..1fbbf82ba38 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -12,7 +12,7 @@ use back::lto; use back::link::{get_linker, remove}; use session::config::{OutputFilenames, Passes, SomePasses, AllPasses}; use session::Session; -use session::config; +use session::config::{self, OutputType}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; use llvm::SMDiagnosticRef; @@ -23,9 +23,10 @@ use syntax::codemap; use syntax::diagnostic; use syntax::diagnostic::{Emitter, Handler, Level}; +use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::ptr; use std::str; use std::sync::{Arc, Mutex}; @@ -33,15 +34,6 @@ use std::sync::mpsc::channel; use std::thread; 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) -> ! { unsafe { let cstr = llvm::LLVMRustGetLastError(); @@ -571,7 +563,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, pub fn run_passes(sess: &Session, trans: &CrateTranslation, - output_types: &[config::OutputType], + output_types: &HashMap>, crate_output: &OutputFilenames) { // 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 @@ -611,32 +603,32 @@ pub fn run_passes(sess: &Session, // archive in order to allow LTO against it. let needs_crate_bitcode = 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 = - sess.opts.output_types.contains(&config::OutputTypeExe); + sess.opts.output_types.contains_key(&OutputType::Exe); if needs_crate_bitcode { modules_config.emit_bc = true; } - for output_type in output_types { + for output_type in output_types.keys() { match *output_type { - config::OutputTypeBitcode => { modules_config.emit_bc = true; }, - config::OutputTypeLlvmAssembly => { modules_config.emit_ir = true; }, - config::OutputTypeAssembly => { + OutputType::Bitcode => { modules_config.emit_bc = true; }, + OutputType::LlvmAssembly => { modules_config.emit_ir = true; }, + OutputType::Assembly => { modules_config.emit_asm = true; // If we're not using the LLVM assembler, this function // could be invoked specially with output_type_assembly, so // 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; } }, - config::OutputTypeObject => { modules_config.emit_obj = true; }, - config::OutputTypeExe => { + OutputType::Object => { modules_config.emit_obj = true; }, + OutputType::Exe => { modules_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| { - // Three cases: + let copy_if_one_unit = |ext: &str, + output_type: OutputType, + keep_numbered: bool| { if sess.opts.cg.codegen_units == 1 { // 1) Only one codegen unit. In this case it's no difficulty // 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`. 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 { - if crate_output.single_output_file.is_some() { - // 2) 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 { - // 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.) - } + // 4) Multiple codegen units, but no explicit 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. let mut user_wants_bitcode = false; let mut user_wants_objects = false; - for output_type in output_types { + for output_type in output_types.keys() { match *output_type { - config::OutputTypeBitcode => { + OutputType::Bitcode => { user_wants_bitcode = true; // 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 // 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 => { - copy_if_one_unit("0.ll", config::OutputTypeLlvmAssembly, false); + OutputType::LlvmAssembly => { + copy_if_one_unit("0.ll", OutputType::LlvmAssembly, false); } - config::OutputTypeAssembly => { - copy_if_one_unit("0.s", config::OutputTypeAssembly, false); + OutputType::Assembly => { + copy_if_one_unit("0.s", OutputType::Assembly, false); } - config::OutputTypeObject => { + OutputType::Object => { user_wants_objects = true; - copy_if_one_unit("0.o", config::OutputTypeObject, true); + copy_if_one_unit("0.o", OutputType::Object, true); } - config::OutputTypeExe | - config::OutputTypeDepInfo => {} + OutputType::Exe | + OutputType::DepInfo => {} } } 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) { let (pname, mut cmd) = get_linker(sess); - cmd.arg("-c").arg("-o").arg(&outputs.path(config::OutputTypeObject)) - .arg(&outputs.temp_path(config::OutputTypeAssembly)); + cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object)) + .arg(&outputs.temp_path(OutputType::Assembly)); debug!("{:?}", cmd); match cmd.output() { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 2f47353fee7..15ec26e5afe 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, Mutex}; use testing; use rustc_lint; 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_front::lowering::lower_crate; use rustc_back::tempdir::TempDir; @@ -163,13 +163,15 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths, // never wrap the test in `fn main() { ... }` let test = maketest(test, Some(cratename), as_test_harness, opts); let input = config::Input::Str(test.to_string()); + let mut outputs = HashMap::new(); + outputs.insert(OutputType::Exe, None); let sessopts = config::Options { maybe_sysroot: Some(env::current_exe().unwrap().parent().unwrap() .parent().unwrap().to_path_buf()), search_paths: libs, crate_types: vec!(config::CrateTypeExecutable), - output_types: vec!(config::OutputTypeExe), + output_types: outputs, externs: externs, cg: config::CodegenOptions { prefer_dynamic: true, diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs index 8745cbecf91..bd8c735df31 100644 --- a/src/test/run-make/issue-19371/foo.rs +++ b/src/test/run-make/issue-19371/foo.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_private, path, convert)] +#![feature(rustc_private)] extern crate rustc; extern crate rustc_driver; @@ -16,7 +16,7 @@ extern crate rustc_lint; extern crate syntax; 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 syntax::diagnostics::registry::Registry; @@ -46,7 +46,7 @@ fn main() { fn basic_sess(sysroot: PathBuf) -> Session { let mut opts = basic_options(); - opts.output_types = vec![OutputTypeExe]; + opts.output_types.insert(OutputType::Exe, None); opts.maybe_sysroot = Some(sysroot); let descriptions = Registry::new(&rustc::DIAGNOSTICS); diff --git a/src/test/run-make/output-type-permutations/Makefile b/src/test/run-make/output-type-permutations/Makefile index a785e916cf7..b4b2e827e94 100644 --- a/src/test/run-make/output-type-permutations/Makefile +++ b/src/test/run-make/output-type-permutations/Makefile @@ -23,45 +23,83 @@ all: rm -f $(TMPDIR)/bar.pdb [ "$$(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 [ "$$(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 [ "$$(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 [ "$$(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 [ "$$(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 -f $(TMPDIR)/foo.pdb [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] $(RUSTC) foo.rs --crate-type=rlib -o $(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" ] $(RUSTC) foo.rs --crate-type=dylib -o $(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} [ "$$(ls -1 $(TMPDIR) | wc -l)" -eq "0" ] $(RUSTC) foo.rs --crate-type=staticlib -o $(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" ] $(RUSTC) foo.rs --crate-type=bin -o $(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 [ "$$(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 rm $(TMPDIR)/bar.ll rm $(TMPDIR)/bar.s From 17419622712af44311aa7a8730e4535dff3554f5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Sep 2015 10:17:07 -0700 Subject: [PATCH 2/2] rustc: Emit phony targets for inputs in dep-info This helps protect against files being deleted to ensure that `make` won't emit errors. Closes #28735 --- src/librustc_driver/driver.rs | 9 ++++++++- src/test/run-make/dep-info/Makefile | 10 ++++++++-- src/test/run-make/dep-info/lib2.rs | 13 +++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/test/run-make/dep-info/lib2.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e2835ae8f0d..0ec3424b82d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -881,9 +881,16 @@ fn write_out_deps(sess: &Session, outputs: &OutputFilenames, id: &str) { .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(()) })(); diff --git a/src/test/run-make/dep-info/Makefile b/src/test/run-make/dep-info/Makefile index a1828cd1f5d..9b79d1af521 100644 --- a/src/test/run-make/dep-info/Makefile +++ b/src/test/run-make/dep-info/Makefile @@ -7,9 +7,10 @@ ifneq ($(shell uname),FreeBSD) ifndef IS_WINDOWS 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 - touch foo.rs + touch $(TMPDIR)/foo.rs -rm -f $(TMPDIR)/done $(MAKE) -drf Makefile.foo sleep 2 @@ -17,6 +18,11 @@ all: pwd $(MAKE) -drf Makefile.foo 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 all: diff --git a/src/test/run-make/dep-info/lib2.rs b/src/test/run-make/dep-info/lib2.rs new file mode 100644 index 00000000000..1b70fb4eb4b --- /dev/null +++ b/src/test/run-make/dep-info/lib2.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +pub mod foo;