blob: d8d362627f88c6f30de2760bda06c436348b699a [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};
/// Type which knows how to write a .d file. All outputs depend on all
/// dependencies.
pub(crate) struct Depfile {
file: File,
outputs: Vec<String>,
dependencies: Vec<String>,
depfile_dir: PathBuf,
}
impl Depfile {
pub(crate) fn new(depfile: &Path) -> std::io::Result<Self> {
let file = File::create(depfile)?;
Ok(Self {
file,
outputs: Vec::new(),
dependencies: Vec::new(),
depfile_dir: depfile.parent().unwrap().to_path_buf(),
})
}
pub(crate) fn add_dependency(&mut self, dependency: &Path) {
self.dependencies.push(self.relativize(dependency))
}
pub(crate) fn add_output(&mut self, output: &Path) {
self.outputs.push(self.relativize(output))
}
pub(crate) fn write(&mut self) -> std::io::Result<()> {
let dependency_list = self.dependencies.join(" \\\n ");
for output in &self.outputs {
self.file
.write_all(format!("{}: {}\n\n", output, dependency_list).as_bytes())?
}
Ok(())
}
/// Return a string giving a relative path from the depfile.
fn relativize(&self, path: &Path) -> String {
pathdiff::diff_paths(path, &self.depfile_dir)
.expect("Unable to make a relative path from the depfile's directory to the dependency")
.to_str()
.expect("Unable to represent the file path in a UTF8 encoding")
.into()
}
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::Read};
use tempfile::tempdir;
use super::Depfile;
#[test]
fn test_simple_depfile() {
let tmp_dir = tempdir().unwrap();
let f = tmp_dir.path().join("depfile.d");
let mut df = Depfile::new(&f).unwrap();
df.add_output(&tmp_dir.path().join("a/b"));
df.add_dependency(&tmp_dir.path().join("c/d"));
df.add_dependency(&tmp_dir.path().join("e/f"));
df.write().unwrap();
let mut f = File::open(&f).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert_eq!(contents, "a/b: c/d \\\n e/f\n\n");
}
#[test]
fn test_multiple_outputs() {
let tmp_dir = tempdir().unwrap();
let f = tmp_dir.path().join("depfile.d");
let mut df = Depfile::new(&f).unwrap();
df.add_output(&tmp_dir.path().join("a/b"));
df.add_output(&tmp_dir.path().join("z"));
df.add_dependency(&tmp_dir.path().join("c/d"));
df.add_dependency(&tmp_dir.path().join("e/f"));
df.write().unwrap();
let mut f = File::open(&f).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
assert_eq!(contents, "a/b: c/d \\\n e/f\n\nz: c/d \\\n e/f\n\n");
}
}