Squashed 'third_party/autocxx/' content from commit 629e8fa53

git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/gen/cmd/src/depfile.rs b/gen/cmd/src/depfile.rs
new file mode 100644
index 0000000..5bb7a66
--- /dev/null
+++ b/gen/cmd/src/depfile.rs
@@ -0,0 +1,102 @@
+// 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 tempdir::TempDir;
+
+    use super::Depfile;
+
+    #[test]
+    fn test_simple_depfile() {
+        let tmp_dir = TempDir::new("depfile-test").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::new("depfile-test").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");
+    }
+}