| /// Generating build depfiles from parsed bindings. |
| use std::{collections::BTreeSet, path::PathBuf}; |
| |
| #[derive(Clone, Debug)] |
| pub(crate) struct DepfileSpec { |
| pub output_module: String, |
| pub depfile_path: PathBuf, |
| } |
| |
| impl DepfileSpec { |
| pub fn write(&self, deps: &BTreeSet<Box<str>>) -> std::io::Result<()> { |
| std::fs::write(&self.depfile_path, self.to_string(deps)) |
| } |
| |
| fn to_string(&self, deps: &BTreeSet<Box<str>>) -> String { |
| // Transforms a string by escaping spaces and backslashes. |
| let escape = |s: &str| s.replace('\\', "\\\\").replace(' ', "\\ "); |
| |
| let mut buf = format!("{}:", escape(&self.output_module)); |
| for file in deps { |
| buf = format!("{} {}", buf, escape(file)); |
| } |
| buf |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn escaping_depfile() { |
| let spec = DepfileSpec { |
| output_module: "Mod Name".to_owned(), |
| depfile_path: PathBuf::new(), |
| }; |
| |
| let deps: BTreeSet<_> = vec![ |
| r"/absolute/path".into(), |
| r"C:\win\absolute\path".into(), |
| r"../relative/path".into(), |
| r"..\win\relative\path".into(), |
| r"../path/with spaces/in/it".into(), |
| r"..\win\path\with spaces\in\it".into(), |
| r"path\with/mixed\separators".into(), |
| ] |
| .into_iter() |
| .collect(); |
| assert_eq!( |
| spec.to_string(&deps), |
| "Mod\\ Name: \ |
| ../path/with\\ spaces/in/it \ |
| ../relative/path \ |
| ..\\\\win\\\\path\\\\with\\ spaces\\\\in\\\\it \ |
| ..\\\\win\\\\relative\\\\path \ |
| /absolute/path \ |
| C:\\\\win\\\\absolute\\\\path \ |
| path\\\\with/mixed\\\\separators" |
| ); |
| } |
| } |