diff --git a/.gitignore b/.gitignore index a9d37c5..3b874ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ target Cargo.lock +.idea +*.iml diff --git a/gcc-test/build.rs b/gcc-test/build.rs index d3e303c..f81833b 100644 --- a/gcc-test/build.rs +++ b/gcc-test/build.rs @@ -81,4 +81,10 @@ fn main() { .cargo_metadata(false) .file("src/opt_linkage.c") .compile("libOptLinkage.a"); + + let out = gcc::Config::new() + .file("src/expand.c") + .expand(); + let out = String::from_utf8(out).unwrap(); + assert!(out.contains("hello world")); } diff --git a/gcc-test/src/expand.c b/gcc-test/src/expand.c new file mode 100644 index 0000000..2b492a2 --- /dev/null +++ b/gcc-test/src/expand.c @@ -0,0 +1,4 @@ +#define HELLO hello +#define WORLD world + +HELLO WORLD diff --git a/src/lib.rs b/src/lib.rs index 426a4e9..dfee75f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,10 @@ extern crate rayon; use std::env; use std::ffi::{OsString, OsStr}; use std::fs; -use std::io; use std::path::{PathBuf, Path}; use std::process::{Command, Stdio}; -use std::io::{BufReader, BufRead, Write}; +use std::io::{self, BufReader, BufRead, Read, Write}; +use std::thread; #[cfg(windows)] mod registry; @@ -131,6 +131,15 @@ impl ToolFamily { ToolFamily::Clang => "-I", } } + + /// What the flag to request macro-expanded source output looks like + fn expand_flag(&self) -> &'static str { + match *self { + ToolFamily::Msvc => "/E", + ToolFamily::Gnu | + ToolFamily::Clang => "-E", + } + } } /// Compile a library from the given set of input C files. @@ -473,6 +482,29 @@ impl Config { run(&mut cmd, &name); } + /// Run the compiler, returning the macro-expanded version of the input files. + /// + /// This is only relevant for C and C++ files. + pub fn expand(&self) -> Vec { + let compiler = self.get_compiler(); + let mut cmd = compiler.to_command(); + for &(ref a, ref b) in self.env.iter() { + cmd.env(a, b); + } + cmd.arg(compiler.family.expand_flag()); + for file in self.files.iter() { + cmd.arg(file); + } + + let name = compiler.path + .file_name() + .unwrap() + .to_string_lossy() + .into_owned(); + + run(&mut cmd, &name) + } + /// Get the compiler that's in use for this configuration. /// /// This function will return a `Tool` which represents the culmination @@ -1044,23 +1076,27 @@ impl Tool { } } -fn run(cmd: &mut Command, program: &str) { +fn run(cmd: &mut Command, program: &str) -> Vec { println!("running: {:?}", cmd); // Capture the standard error coming from these programs, and write it out // with cargo:warning= prefixes. Note that this is a bit wonky to avoid // requiring the output to be UTF-8, we instead just ship bytes from one // location to another. - let spawn_result = match cmd.stderr(Stdio::piped()).spawn() { + let (spawn_result, stdout) = match cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { Ok(mut child) => { let stderr = BufReader::new(child.stderr.take().unwrap()); - for line in stderr.split(b'\n').filter_map(|l| l.ok()) { - print!("cargo:warning="); - std::io::stdout().write_all(&line).unwrap(); - println!(""); - } - child.wait() + thread::spawn(move || { + for line in stderr.split(b'\n').filter_map(|l| l.ok()) { + print!("cargo:warning="); + std::io::stdout().write_all(&line).unwrap(); + println!(""); + } + }); + let mut stdout = vec![]; + child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap(); + (child.wait(), stdout) } - Err(e) => Err(e), + Err(e) => (Err(e), vec![]), }; let status = match spawn_result { Ok(status) => status, @@ -1083,6 +1119,7 @@ fn run(cmd: &mut Command, program: &str) { if !status.success() { fail(&format!("command did not execute successfully, got: {}", status)); } + stdout } fn fail(s: &str) -> ! {