diff --git a/src/lib.rs b/src/lib.rs index 699facfea..fa9638cb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1131,48 +1131,7 @@ impl Build { }; let dst = self.get_out_dir()?; - let mut objects = Vec::new(); - for file in self.files.iter() { - let obj = if file.has_root() || file.components().any(|x| x == Component::ParentDir) { - // If `file` is an absolute path or might not be usable directly as a suffix due to - // using "..", use the `basename` prefixed with the `dirname`'s hash to ensure name - // uniqueness. - let basename = file - .file_name() - .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "file_name() failure"))? - .to_string_lossy(); - let dirname = file - .parent() - .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "parent() failure"))? - .to_string_lossy(); - let mut hasher = hash_map::DefaultHasher::new(); - hasher.write(dirname.to_string().as_bytes()); - dst.join(format!("{:016x}-{}", hasher.finish(), basename)) - .with_extension("o") - } else { - dst.join(file).with_extension("o") - }; - let obj = if !obj.starts_with(&dst) { - dst.join(obj.file_name().ok_or_else(|| { - Error::new(ErrorKind::IOError, "Getting object file details failed.") - })?) - } else { - obj - }; - - match obj.parent() { - Some(s) => fs::create_dir_all(s)?, - None => { - return Err(Error::new( - ErrorKind::IOError, - "Getting object file details failed.", - )); - } - }; - - objects.push(Object::new(file.to_path_buf(), obj)); - } - + let objects = objects_from_files(self.files.iter().map(|v| &**v), &dst)?; let print = PrintThread::new()?; self.compile_objects(&objects, &print)?; @@ -1316,6 +1275,33 @@ impl Build { } } + + /// Run the compiler, generating intermediate files, but without linking + /// them into an archive file. + /// + /// This will return a list of compiled object files, in the same order + /// as they were passed in as `file`/`files` methods. + pub fn compile_intermediates(&self) -> Vec { + match self.try_compile_intermediates() { + Ok(v) => v, + Err(e) => fail(&e.message), + } + } + + /// Run the compiler, generating intermediate files, but without linking + /// them into an archive file. + /// + /// This will return a result instead of panicing; see `compile_intermediates()` for the complete description. + pub fn try_compile_intermediates(&self) -> Result, Error> { + let dst = self.get_out_dir()?; + let objects = objects_from_files(self.files.iter().map(|v| &**v), &dst)?; + let print = PrintThread::new()?; + + self.compile_objects(&objects, &print)?; + + Ok(objects.into_iter().map(|v| v.dst).collect()) + } + #[cfg(feature = "parallel")] fn compile_objects(&self, objs: &[Object], print: &PrintThread) -> Result<(), Error> { use std::cell::Cell; @@ -3830,6 +3816,54 @@ fn wait_on_child(cmd: &Command, program: &str, child: &mut Child) -> Result<(), } } +/// Find the destination object path for each file in the input source files, +/// and store them in the output Object. +fn objects_from_files<'a, I: IntoIterator>(files: I, dst: &Path) -> Result, Error> { + let mut objects = Vec::new(); + for file in files.into_iter() { + let obj = if file.has_root() || file.components().any(|x| x == Component::ParentDir) { + // If `file` is an absolute path or might not be usable directly as a suffix due to + // using "..", use the `basename` prefixed with the `dirname`'s hash to ensure name + // uniqueness. + let basename = file + .file_name() + .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "file_name() failure"))? + .to_string_lossy(); + let dirname = file + .parent() + .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "parent() failure"))? + .to_string_lossy(); + let mut hasher = hash_map::DefaultHasher::new(); + hasher.write(dirname.to_string().as_bytes()); + dst.join(format!("{:016x}-{}", hasher.finish(), basename)) + .with_extension("o") + } else { + dst.join(file).with_extension("o") + }; + let obj = if !obj.starts_with(&dst) { + dst.join(obj.file_name().ok_or_else(|| { + Error::new(ErrorKind::IOError, "Getting object file details failed.") + })?) + } else { + obj + }; + + match obj.parent() { + Some(s) => fs::create_dir_all(s)?, + None => { + return Err(Error::new( + ErrorKind::IOError, + "Getting object file details failed.", + )); + } + }; + + objects.push(Object::new(file.to_path_buf(), obj)); + } + + Ok(objects) +} + #[cfg(feature = "parallel")] fn try_wait_on_child( cmd: &Command, diff --git a/tests/test.rs b/tests/test.rs index 36cdd8410..f2c937e8e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -576,3 +576,23 @@ fn clang_apple_tvsimulator() { test.cmd(0).must_have("-mappletvsimulator-version-min=9.0"); } } + +#[test] +fn compile_intermediates() { + let test = Test::gnu(); + let intermediates = test.gcc() + .file("foo.c") + .file("x86_64.asm") + .file("x86_64.S") + .asm_flag("--abc") + .compile_intermediates(); + test.cmd(0).must_not_have("--abc"); + test.cmd(1).must_have("--abc"); + test.cmd(2).must_have("--abc"); + + assert_eq!(intermediates.len(), 3); + + assert!(intermediates[0].display().to_string().contains("foo")); + assert!(intermediates[1].display().to_string().contains("x86_64")); + assert!(intermediates[2].display().to_string().contains("x86_64")); +}