// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use itertools::Itertools; use super::*; use crate::{action::BuildAction, input::BuildInput}; pub struct SyncSubmodule { pub path: &'static str, } impl BuildAction for SyncSubmodule { fn command(&self) -> &str { "git submodule update --init $path" } fn files(&mut self, build: &mut impl build::FilesHandle) { if let Some(head) = locate_git_head() { build.add_inputs("", head); } else { println!("Warning, .git/HEAD not found; submodules may be stale"); } build.add_variable("path", self.path); build.add_output_stamp(format!("git/{}", self.path)); } fn on_first_instance(&self, build: &mut Build) -> Result<()> { build.pool("git", 1); Ok(()) } fn concurrency_pool(&self) -> Option<&'static str> { Some("git") } } /// We check the mtime of .git/HEAD to detect when we should sync submodules. /// If this repo is a submodule of another project, .git/HEAD will not exist, /// and we fall back on .git/modules/*/HEAD in a parent folder instead. fn locate_git_head() -> Option { let standard_path = Utf8Path::new(".git/HEAD"); if standard_path.exists() { return Some(inputs![standard_path.to_string()]); } let mut folder = Utf8Path::new(".").canonicalize_utf8().unwrap(); loop { let path = folder.join(".git").join("modules"); if path.exists() { let heads = path .read_dir_utf8() .unwrap() .filter_map(|p| { let head = p.unwrap().path().join("HEAD"); if head.exists() { Some(head.to_string()) } else { None } }) .collect_vec(); return Some(inputs![heads]); } if let Some(parent) = folder.parent() { folder = parent.to_owned(); } else { return None; } } }