Skip to content

Commit

Permalink
Add new compile_intermediates function.
Browse files Browse the repository at this point in the history
This new function can be used to just compile the files to a bunch of .o
files.
  • Loading branch information
roblabla committed Jan 2, 2024
1 parent 802399f commit 1b35617
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 42 deletions.
118 changes: 76 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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<PathBuf> {
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<Vec<PathBuf>, 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;
Expand Down Expand Up @@ -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<Item = &'a Path>>(files: I, dst: &Path) -> Result<Vec<Object>, 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,
Expand Down
20 changes: 20 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}

0 comments on commit 1b35617

Please sign in to comment.