From b6064a64e0475f4a01292eaca87a568bb50faf70 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 8 Mar 2017 09:34:48 -0800 Subject: [PATCH 1/3] Refactor msvc slightly w/ `while let` Helps avoid some extra indentation --- src/compiler/msvc.rs | 86 +++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index f3f2dcdd6..dffda4612 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -155,54 +155,50 @@ pub fn parse_arguments(arguments: &[String]) -> CompilerArguments { - match arg.as_ref() { - "-c" => compilation = true, - v if v.starts_with("-Fo") => { - output_arg = Some(String::from(&v[3..])); - } - // Arguments that take a value. - "-FI" => { - common_args.push(arg.clone()); - if let Some(arg_val) = it.next() { - common_args.push(arg_val.clone()); - } - } - v @ _ if v.starts_with("-deps") => { - depfile = Some(v[5..].to_owned()); - } - // Arguments we can't handle. - "-showIncludes" => return CompilerArguments::CannotCache("-showIncludes"), - a if a.starts_with('@') => return CompilerArguments::CannotCache("@file"), - // Arguments we can't handle because they output more files. - // TODO: support more multi-file outputs. - "-FA" | "-Fa" | "-Fe" | "-Fm" | "-Fp" | "-FR" | "-Fx" => return CompilerArguments::CannotCache("multi-file output"), - "-Zi" => { - debug_info = true; - common_args.push(arg.clone()); - } - v if v.starts_with("-Fd") => { - pdb = Some(String::from(&v[3..])); - common_args.push(arg.clone()); - } - // Other options. - v if v.starts_with('-') && v.len() > 1 => { - common_args.push(arg.clone()); - } - // Anything else is an input file. - v => { - if input_arg.is_some() { - // Can't cache compilations with multiple inputs. - return CompilerArguments::CannotCache("multiple input files"); - } - input_arg = Some(v); - } + while let Some(arg) = it.next() { + match arg.as_ref() { + "-c" => compilation = true, + v if v.starts_with("-Fo") => { + output_arg = Some(String::from(&v[3..])); + } + // Arguments that take a value. + "-FI" => { + common_args.push(arg.clone()); + if let Some(arg_val) = it.next() { + common_args.push(arg_val.clone()); + } + } + v @ _ if v.starts_with("-deps") => { + depfile = Some(v[5..].to_owned()); + } + // Arguments we can't handle. + "-showIncludes" => return CompilerArguments::CannotCache("-showIncludes"), + a if a.starts_with('@') => return CompilerArguments::CannotCache("@file"), + // Arguments we can't handle because they output more files. + // TODO: support more multi-file outputs. + "-FA" | "-Fa" | "-Fe" | "-Fm" | "-Fp" | "-FR" | "-Fx" => return CompilerArguments::CannotCache("multi-file output"), + "-Zi" => { + debug_info = true; + common_args.push(arg.clone()); + } + v if v.starts_with("-Fd") => { + pdb = Some(String::from(&v[3..])); + common_args.push(arg.clone()); + } + // Other options. + v if v.starts_with('-') && v.len() > 1 => { + common_args.push(arg.clone()); + } + // Anything else is an input file. + v => { + if input_arg.is_some() { + // Can't cache compilations with multiple inputs. + return CompilerArguments::CannotCache("multiple input files"); } + input_arg = Some(v); } - None => break, } + input_arg = Some(v); } // We only support compilation. if !compilation { From 7b4c6878ef23e5ddc5ba26365b82fb4d46bd0edf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 8 Mar 2017 09:35:04 -0800 Subject: [PATCH 2/3] Support `/foo` and `-foo` on MSVC Just do a simple translation from `/foo` to `-foo` --- src/compiler/msvc.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index dffda4612..dea4ae238 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -154,9 +154,15 @@ pub fn parse_arguments(arguments: &[String]) -> CompilerArguments compilation = true, v if v.starts_with("-Fo") => { output_arg = Some(String::from(&v[3..])); @@ -195,7 +201,7 @@ pub fn parse_arguments(arguments: &[String]) -> CompilerArguments CompilerArguments { - match Path::new(i).extension().and_then(|e| e.to_str()) { + match Path::new(&i).extension().and_then(|e| e.to_str()) { Some(e) => (i.to_owned(), e.to_owned()), _ => { trace!("Bad or missing source extension: {:?}", i); From 8660519666d7a27b6ae69bd4211dbc240dc26e3c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 8 Mar 2017 09:56:04 -0800 Subject: [PATCH 3/3] Support -showIncludes on MSVC Track the `-showIncludes` flag being passed down to the compilation itself, and then also be sure to thread the entire output of the preprocessor down to compilations instead of just stdout. Once that was done support was implemented by simply prepending the preprocessor stderr to the compiler stderr if the `-showIncludes` flag was passed. --- src/compiler/c.rs | 12 +- src/compiler/clang.rs | 15 +- src/compiler/gcc.rs | 323 ++++++++++++++++++++++++++---------------- src/compiler/msvc.rs | 244 +++++++++++++++++++++++-------- src/test/utils.rs | 10 ++ 5 files changed, 409 insertions(+), 195 deletions(-) diff --git a/src/compiler/c.rs b/src/compiler/c.rs index b97b8023c..b2556856d 100644 --- a/src/compiler/c.rs +++ b/src/compiler/c.rs @@ -64,6 +64,8 @@ pub struct ParsedArguments { pub preprocessor_args: Vec, /// Commandline arguments for the preprocessor or the compiler. pub common_args: Vec, + /// Whether or not the `-showIncludes` argument is passed on MSVC + pub msvc_show_includes: bool, } impl ParsedArguments { @@ -77,7 +79,7 @@ struct CCompilation { parsed_args: ParsedArguments, executable: String, /// The output from running the preprocessor. - preprocessor_output: Vec, + preprocessor_result: process::Output, compiler: I, } @@ -114,7 +116,7 @@ pub trait CCompilerImpl: Clone + fmt::Debug + Send + 'static { fn compile(&self, creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -217,7 +219,7 @@ impl CompilerHasher for CCompilerHasher compilation: Box::new(CCompilation { parsed_args: parsed_args, executable: executable, - preprocessor_output: preprocessor_result.stdout, + preprocessor_result: preprocessor_result, compiler: compiler, }), }) @@ -244,8 +246,8 @@ impl Compilation for CCompilation -> SFuture<(Cacheable, process::Output)> { let me = *self; - let CCompilation { parsed_args, executable, preprocessor_output, compiler } = me; - compiler.compile(creator, &executable, preprocessor_output, &parsed_args, cwd, env_vars, + let CCompilation { parsed_args, executable, preprocessor_result, compiler } = me; + compiler.compile(creator, &executable, preprocessor_result, &parsed_args, cwd, env_vars, pool) } diff --git a/src/compiler/clang.rs b/src/compiler/clang.rs index 217105a7d..27031db75 100644 --- a/src/compiler/clang.rs +++ b/src/compiler/clang.rs @@ -68,7 +68,7 @@ impl CCompilerImpl for Clang { fn compile(&self, creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -76,7 +76,7 @@ impl CCompilerImpl for Clang { -> SFuture<(Cacheable, process::Output)> where T: CommandCreatorSync { - compile(creator, executable, preprocessor_output, parsed_args, cwd, env_vars, pool) + compile(creator, executable, preprocessor_result, parsed_args, cwd, env_vars, pool) } } @@ -95,7 +95,7 @@ pub fn argument_takes_value(arg: &str) -> bool { fn compile(creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -111,7 +111,7 @@ fn compile(creator: &T, Some(name) => name, None => return future::err("missing input filename".into()).boxed(), }; - write_temp_file(pool, filename.as_ref(), preprocessor_output) + write_temp_file(pool, filename.as_ref(), preprocessor_result.stdout) }; let input = parsed_args.input.clone(); let out_file = match parsed_args.outputs.get("obj") { @@ -193,6 +193,7 @@ mod test { } } + #[test] fn test_parse_arguments_simple() { let a = parses!("-c", "foo.c", "-o", "foo.o"); @@ -236,13 +237,14 @@ mod test { outputs: vec![("obj", "foo.o".to_owned())].into_iter().collect::>(), preprocessor_args: vec!(), common_args: vec!(), + msvc_show_includes: false, }; let compiler = f.bins[0].to_str().unwrap(); // Compiler invocation. next_command(&creator, Ok(MockChild::new(exit_status(0), "", ""))); let (cacheable, _) = compile(&creator, &compiler, - vec!(), + empty_output(), &parsed_args, f.tempdir.path().to_str().unwrap(), &[], @@ -264,6 +266,7 @@ mod test { outputs: vec![("obj", "foo.o".to_owned())].into_iter().collect::>(), preprocessor_args: vec!(), common_args: stringvec!("-c", "-o", "foo.o", "-Werror=blah", "foo.c"), + msvc_show_includes: false, }; let compiler = f.bins[0].to_str().unwrap(); // First compiler invocation fails. @@ -272,7 +275,7 @@ mod test { next_command(&creator, Ok(MockChild::new(exit_status(0), "", ""))); let (cacheable, output) = compile(&creator, &compiler, - vec!(), + empty_output(), &parsed_args, f.tempdir.path().to_str().unwrap(), &[], diff --git a/src/compiler/gcc.rs b/src/compiler/gcc.rs index ba760681a..f7280f133 100644 --- a/src/compiler/gcc.rs +++ b/src/compiler/gcc.rs @@ -62,7 +62,7 @@ impl CCompilerImpl for GCC { fn compile(&self, creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -70,7 +70,7 @@ impl CCompilerImpl for GCC { -> SFuture<(Cacheable, process::Output)> where T: CommandCreatorSync { - compile(creator, executable, preprocessor_output, parsed_args, cwd, env_vars, pool) + compile(creator, executable, preprocessor_result, parsed_args, cwd, env_vars, pool) } } @@ -230,6 +230,7 @@ fn _parse_arguments(arguments: &[String], outputs: outputs, preprocessor_args: preprocessor_args, common_args: common_args, + msvc_show_includes: false, }) } @@ -259,7 +260,7 @@ pub fn preprocess(creator: &T, fn compile(creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -291,7 +292,7 @@ fn compile(creator: &T, .env_clear() .envs(env_vars.iter().map(|&(ref k, ref v)| (k, v))) .current_dir(cwd); - Box::new(run_input_output(cmd, Some(preprocessor_output)).map(|output| { + Box::new(run_input_output(cmd, Some(preprocessor_result.stdout)).map(|output| { (Cacheable::Yes, output) })) } @@ -374,138 +375,210 @@ mod test { #[test] fn test_parse_arguments_simple() { - match _parse_arguments(&stringvec!["-c", "foo.c", "-o", "foo.o"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert!(common_args.is_empty()); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-o", "foo.o"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert!(common_args.is_empty()); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_split_dwarf() { - match _parse_arguments(&stringvec!["-gsplit-dwarf", "-c", "foo.cpp", "-o", "foo.o"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.cpp", input); - assert_eq!("cpp", extension); - assert_map_contains!(outputs, ("obj", "foo.o"), ("dwo", "foo.dwo")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(2, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(stringvec!["-gsplit-dwarf"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-gsplit-dwarf", "-c", "foo.cpp", "-o", "foo.o"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.cpp", input); + assert_eq!("cpp", extension); + assert_map_contains!(outputs, ("obj", "foo.o"), ("dwo", "foo.dwo")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(2, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(stringvec!["-gsplit-dwarf"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_extra() { - match _parse_arguments(&stringvec!["-c", "foo.cc", "-fabc", "-o", "foo.o", "-mxyz"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.cc", input); - assert_eq!("cc", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(stringvec!["-fabc", "-mxyz"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.cc", "-fabc", "-o", "foo.o", "-mxyz"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.cc", input); + assert_eq!("cc", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(stringvec!["-fabc", "-mxyz"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_values() { - match _parse_arguments(&stringvec!["-c", "foo.cxx", "-fabc", "-I", "include", "-o", "foo.o", "-include", "file"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.cxx", input); - assert_eq!("cxx", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(stringvec!["-fabc", "-I", "include", "-include", "file"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.cxx", "-fabc", "-I", "include", "-o", "foo.o", "-include", "file"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.cxx", input); + assert_eq!("cxx", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(stringvec!["-fabc", "-I", "include", "-include", "file"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_preprocessor_args() { - match _parse_arguments(&stringvec!["-c", "foo.c", "-fabc", "-MF", "file", "-o", "foo.o", "-MQ", "abc"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert_eq!(stringvec!["-MF", "file", "-MQ", "abc"], preprocessor_args); - assert_eq!(stringvec!["-fabc"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-fabc", "-MF", "file", "-o", "foo.o", "-MQ", "abc"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert_eq!(stringvec!["-MF", "file", "-MQ", "abc"], preprocessor_args); + assert_eq!(stringvec!["-fabc"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_explicit_dep_target() { - match _parse_arguments(&stringvec!["-c", "foo.c", "-MT", "depfile", "-fabc", "-MF", "file", "-o", "foo.o"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert_eq!(stringvec!["-MF", "file"], preprocessor_args); - assert_eq!(stringvec!["-fabc"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-MT", "depfile", "-fabc", "-MF", "file", "-o", "foo.o"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert_eq!(stringvec!["-MF", "file"], preprocessor_args); + assert_eq!(stringvec!["-fabc"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_explicit_dep_target_needed() { - match _parse_arguments(&stringvec!["-c", "foo.c", "-MT", "depfile", "-fabc", "-MF", "file", "-o", "foo.o", "-MD"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert_eq!(stringvec!["-MF", "file", "-MD", "-MT", "depfile"], preprocessor_args); - assert_eq!(stringvec!["-fabc"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-MT", "depfile", "-fabc", "-MF", "file", "-o", "foo.o", "-MD"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert_eq!(stringvec!["-MF", "file", "-MD", "-MT", "depfile"], preprocessor_args); + assert_eq!(stringvec!["-fabc"], common_args); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_dep_target_needed() { - match _parse_arguments(&stringvec!["-c", "foo.c", "-fabc", "-MF", "file", "-o", "foo.o", "-MD"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert_eq!(stringvec!["-MF", "file", "-MD", "-MT", "foo.o"], preprocessor_args); - assert_eq!(stringvec!["-fabc"], common_args); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-fabc", "-MF", "file", "-o", "foo.o", "-MD"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert_eq!(stringvec!["-MF", "file", "-MD", "-MT", "foo.o"], preprocessor_args); + assert_eq!(stringvec!["-fabc"], common_args); + assert!(!msvc_show_includes); } #[test] @@ -553,18 +626,26 @@ mod test { -c foo.c -o foo.o\ ").unwrap(); let arg = format!("@{}", td.path().join("foo").display()); - match _parse_arguments(&[arg]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.o")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert!(common_args.is_empty()); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match _parse_arguments(&[arg]) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.o")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert!(common_args.is_empty()); + assert!(!msvc_show_includes); } } diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index dea4ae238..4c6dbc63a 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -33,6 +33,7 @@ use std::io::{ self, Write, }; +use std::mem; use std::path::Path; use std::process::{self,Stdio}; use util::run_input_output; @@ -72,7 +73,7 @@ impl CCompilerImpl for MSVC { fn compile(&self, creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -80,7 +81,7 @@ impl CCompilerImpl for MSVC { -> SFuture<(Cacheable, process::Output)> where T: CommandCreatorSync { - compile(creator, executable, preprocessor_output, parsed_args, cwd, env_vars, pool) + compile(creator, executable, preprocessor_result, parsed_args, cwd, env_vars, pool) } } @@ -152,8 +153,8 @@ pub fn parse_arguments(arguments: &[String]) -> CompilerArguments CompilerArguments { depfile = Some(v[5..].to_owned()); } + "-showIncludes" => show_includes = true, // Arguments we can't handle. - "-showIncludes" => return CompilerArguments::CannotCache("-showIncludes"), a if a.starts_with('@') => return CompilerArguments::CannotCache("@file"), // Arguments we can't handle because they output more files. // TODO: support more multi-file outputs. @@ -204,7 +205,6 @@ pub fn parse_arguments(arguments: &[String]) -> CompilerArguments CompilerArguments(creator: &T, fn compile(creator: &T, executable: &str, - preprocessor_output: Vec, + preprocessor_result: process::Output, parsed_args: &ParsedArguments, cwd: &str, env_vars: &[(OsString, OsString)], @@ -400,7 +401,7 @@ fn compile(creator: &T, Some(name) => name, None => return f_err("missing input filename"), }; - write_temp_file(pool, filename.as_ref(), preprocessor_output) + write_temp_file(pool, filename.as_ref(), preprocessor_result.stdout) }; let mut cmd = creator.clone().new_command_sync(executable); @@ -432,7 +433,7 @@ fn compile(creator: &T, .env_clear() .envs(env_vars.iter().map(|&(ref k, ref v)| (k, v))) .current_dir(cwd); - Box::new(output.or_else(move |err| -> SFuture<_> { + let ret = output.or_else(move |err| -> SFuture<_> { match err { // If compiling from the preprocessed source failed, try // again from the original source. @@ -444,6 +445,25 @@ fn compile(creator: &T, } e @ _ => f_err(e), } + }); + + // If the `-showIncludes` command line option was originally passed we need + // to be sure to ship the output from the preprocessor as the actual + // result of this compilation. + // + // Note, though, that when we ran the preprocessor we passed `-E` which + // means that the "show includes" business when to stderr. Normally, though, + // the compiler emits `-showIncludes` output to stdout. To handle that we + // take the stderr of the preprocessor and prepend it to the stdout of the + // compilation. + let mut extra_stdout = Vec::new(); + if parsed_args.msvc_show_includes { + extra_stdout = preprocessor_result.stderr; + } + Box::new(ret.map(|(cacheable, mut output)| { + let prev = mem::replace(&mut output.stdout, extra_stdout); + output.stdout.extend(prev); + (cacheable, output) })) } @@ -478,70 +498,132 @@ mod test { #[test] fn test_parse_arguments_simple() { - match parse_arguments(&stringvec!["-c", "foo.c", "-Fofoo.obj"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.obj")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert!(common_args.is_empty()); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-Fofoo.obj"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.obj")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert!(common_args.is_empty()); + assert!(!msvc_show_includes); + } + + #[test] + fn parse_argument_slashes() { + let args = stringvec!["-c", "foo.c", "/Fofoo.obj"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.obj")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert!(common_args.is_empty()); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_extra() { - match parse_arguments(&stringvec!["-c", "foo.c", "-foo", "-Fofoo.obj", "-bar"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.obj")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(common_args, &["-foo", "-bar"]); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-foo", "-Fofoo.obj", "-bar"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.obj")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(common_args, &["-foo", "-bar"]); + assert!(!msvc_show_includes); } #[test] fn test_parse_arguments_values() { - match parse_arguments(&stringvec!["-c", "foo.c", "-FI", "file", "-Fofoo.obj"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.obj")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(1, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(common_args, &["-FI", "file"]); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-FI", "file", "-Fofoo.obj", "/showIncludes"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.obj")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(1, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(common_args, &["-FI", "file"]); + assert!(msvc_show_includes); } #[test] fn test_parse_arguments_pdb() { - match parse_arguments(&stringvec!["-c", "foo.c", "-Zi", "-Fdfoo.pdb", "-Fofoo.obj"]) { - CompilerArguments::Ok(ParsedArguments { input, extension, depfile: _depfile, outputs, preprocessor_args, common_args }) => { - assert!(true, "Parsed ok"); - assert_eq!("foo.c", input); - assert_eq!("c", extension); - assert_map_contains!(outputs, ("obj", "foo.obj"), ("pdb", "foo.pdb")); - //TODO: fix assert_map_contains to assert no extra keys! - assert_eq!(2, outputs.len()); - assert!(preprocessor_args.is_empty()); - assert_eq!(common_args, &["-Zi", "-Fdfoo.pdb"]); - } - o @ _ => assert!(false, format!("Got unexpected parse result: {:?}", o)), - } + let args = stringvec!["-c", "foo.c", "-Zi", "-Fdfoo.pdb", "-Fofoo.obj"]; + let ParsedArguments { + input, + extension, + depfile: _, + outputs, + preprocessor_args, + msvc_show_includes, + common_args, + } = match parse_arguments(&args) { + CompilerArguments::Ok(args) => args, + o @ _ => panic!("Got unexpected parse result: {:?}", o), + }; + assert!(true, "Parsed ok"); + assert_eq!("foo.c", input); + assert_eq!("c", extension); + assert_map_contains!(outputs, ("obj", "foo.obj"), ("pdb", "foo.pdb")); + //TODO: fix assert_map_contains to assert no extra keys! + assert_eq!(2, outputs.len()); + assert!(preprocessor_args.is_empty()); + assert_eq!(common_args, &["-Zi", "-Fdfoo.pdb"]); + assert!(!msvc_show_includes); } #[test] @@ -598,6 +680,7 @@ mod test { outputs: vec![("obj", "foo.obj".to_owned())].into_iter().collect::>(), preprocessor_args: vec!(), common_args: vec!(), + msvc_show_includes: false, }; let compiler = f.bins[0].to_str().unwrap(); // Compiler invocation. @@ -605,7 +688,7 @@ mod test { next_command(&creator, Ok(MockChild::new(exit_status(1), "", ""))); let (cacheable, _) = compile(&creator, &compiler, - vec!(), + empty_output(), &parsed_args, f.tempdir.path().to_str().unwrap(), &[], @@ -629,6 +712,7 @@ mod test { ("pdb", pdb.to_str().unwrap().to_owned())].into_iter().collect::>(), preprocessor_args: vec!(), common_args: vec!(), + msvc_show_includes: false, }; let compiler = f.bins[0].to_str().unwrap(); // Compiler invocation. @@ -636,7 +720,7 @@ mod test { next_command(&creator, Ok(MockChild::new(exit_status(1), "", ""))); let (cacheable, _) = compile(&creator, &compiler, - vec!(), + empty_output(), &parsed_args, f.tempdir.path().to_str().unwrap(), &[], @@ -658,6 +742,7 @@ mod test { outputs: vec![("obj", "foo.obj".to_owned())].into_iter().collect::>(), preprocessor_args: vec!(), common_args: vec!(), + msvc_show_includes: false, }; let compiler = f.bins[0].to_str().unwrap(); // First compiler invocation fails. @@ -666,7 +751,7 @@ mod test { next_command(&creator, Ok(MockChild::new(exit_status(0), "", ""))); let (cacheable, _) = compile(&creator, &compiler, - vec!(), + empty_output(), &parsed_args, f.tempdir.path().to_str().unwrap(), &[], @@ -675,4 +760,37 @@ mod test { // Ensure that we ran all processes. assert_eq!(0, creator.lock().unwrap().children.len()); } + + #[test] + fn preprocess_output_appended() { + let creator = new_creator(); + let pool = CpuPool::new(1); + let f = TestFixture::new(); + let parsed_args = ParsedArguments { + input: "foo.c".to_owned(), + extension: "c".to_owned(), + depfile: None, + outputs: vec![("obj", "foo.obj".to_owned())].into_iter().collect::>(), + preprocessor_args: vec!(), + common_args: vec!(), + msvc_show_includes: true, + }; + let compiler = f.bins[0].to_str().unwrap(); + // Compiler invocation. + next_command(&creator, Ok(MockChild::new(exit_status(0), "stdout1", "stderr1"))); + next_command(&creator, Ok(MockChild::new(exit_status(1), "", ""))); + let mut output = empty_output(); + output.stdout.extend(b"stdout2"); + output.stderr.extend(b"stderr2"); + let (_, output) = compile(&creator, + &compiler, + output, + &parsed_args, + f.tempdir.path().to_str().unwrap(), + &[], + &pool).wait().unwrap(); + assert_eq!(0, creator.lock().unwrap().children.len()); + assert_eq!(output.stdout, b"stderr2stdout1"); + assert_eq!(output.stderr, b"stderr1"); + } } diff --git a/src/test/utils.rs b/src/test/utils.rs index 4a08312a0..484bb0f36 100644 --- a/src/test/utils.rs +++ b/src/test/utils.rs @@ -21,6 +21,8 @@ use std::ffi::OsString; use std::fs::{self,File}; use std::io; use std::path::{Path,PathBuf}; +use std::process; + use std::sync::{Arc,Mutex}; use tempdir::TempDir; use tokio_core::reactor::Core; @@ -175,6 +177,14 @@ impl TestFixture { } +pub fn empty_output() -> process::Output { + process::Output { + stdout: Vec::new(), + stderr: Vec::new(), + status: exit_status(0), + } +} + #[test] fn test_map_contains_ok() { let mut m = HashMap::new();