blob: d8d362627f88c6f30de2760bda06c436348b699a [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::{
10 fs::File,
11 io::Write,
12 path::{Path, PathBuf},
13};
14
15/// Type which knows how to write a .d file. All outputs depend on all
16/// dependencies.
17pub(crate) struct Depfile {
18 file: File,
19 outputs: Vec<String>,
20 dependencies: Vec<String>,
21 depfile_dir: PathBuf,
22}
23
24impl Depfile {
25 pub(crate) fn new(depfile: &Path) -> std::io::Result<Self> {
26 let file = File::create(depfile)?;
27 Ok(Self {
28 file,
29 outputs: Vec::new(),
30 dependencies: Vec::new(),
31 depfile_dir: depfile.parent().unwrap().to_path_buf(),
32 })
33 }
34
35 pub(crate) fn add_dependency(&mut self, dependency: &Path) {
36 self.dependencies.push(self.relativize(dependency))
37 }
38
39 pub(crate) fn add_output(&mut self, output: &Path) {
40 self.outputs.push(self.relativize(output))
41 }
42
43 pub(crate) fn write(&mut self) -> std::io::Result<()> {
44 let dependency_list = self.dependencies.join(" \\\n ");
45 for output in &self.outputs {
46 self.file
47 .write_all(format!("{}: {}\n\n", output, dependency_list).as_bytes())?
48 }
49 Ok(())
50 }
51
52 /// Return a string giving a relative path from the depfile.
53 fn relativize(&self, path: &Path) -> String {
54 pathdiff::diff_paths(path, &self.depfile_dir)
55 .expect("Unable to make a relative path from the depfile's directory to the dependency")
56 .to_str()
57 .expect("Unable to represent the file path in a UTF8 encoding")
58 .into()
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use std::{fs::File, io::Read};
65
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070066 use tempfile::tempdir;
Brian Silverman4e662aa2022-05-11 23:10:19 -070067
68 use super::Depfile;
69
70 #[test]
71 fn test_simple_depfile() {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070072 let tmp_dir = tempdir().unwrap();
Brian Silverman4e662aa2022-05-11 23:10:19 -070073 let f = tmp_dir.path().join("depfile.d");
74 let mut df = Depfile::new(&f).unwrap();
75 df.add_output(&tmp_dir.path().join("a/b"));
76 df.add_dependency(&tmp_dir.path().join("c/d"));
77 df.add_dependency(&tmp_dir.path().join("e/f"));
78 df.write().unwrap();
79
80 let mut f = File::open(&f).unwrap();
81 let mut contents = String::new();
82 f.read_to_string(&mut contents).unwrap();
83 assert_eq!(contents, "a/b: c/d \\\n e/f\n\n");
84 }
85
86 #[test]
87 fn test_multiple_outputs() {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070088 let tmp_dir = tempdir().unwrap();
Brian Silverman4e662aa2022-05-11 23:10:19 -070089 let f = tmp_dir.path().join("depfile.d");
90 let mut df = Depfile::new(&f).unwrap();
91 df.add_output(&tmp_dir.path().join("a/b"));
92 df.add_output(&tmp_dir.path().join("z"));
93 df.add_dependency(&tmp_dir.path().join("c/d"));
94 df.add_dependency(&tmp_dir.path().join("e/f"));
95 df.write().unwrap();
96
97 let mut f = File::open(&f).unwrap();
98 let mut contents = String::new();
99 f.read_to_string(&mut contents).unwrap();
100 assert_eq!(contents, "a/b: c/d \\\n e/f\n\nz: c/d \\\n e/f\n\n");
101 }
102}