@@ -737,6 +737,9 @@ fn prepare_project_workdir( |
| 737 | e | 737 | e |
| 738 | ) | 738 | ) |
| 739 | })?; | 739 | })?; |
| | 740 | + if let Some(workspace_root) = project.source.parent() { |
| | 741 | + localize_fgof_git_dependencies(&source_root, workspace_root, &root)?; |
| | 742 | + } |
| 740 | Ok(source_root) | 743 | Ok(source_root) |
| 741 | } | 744 | } |
| 742 | | 745 | |
@@ -773,6 +776,100 @@ fn copy_project_tree(source: &Path, dest: &Path) -> io::Result<()> { |
| 773 | Ok(()) | 776 | Ok(()) |
| 774 | } | 777 | } |
| 775 | | 778 | |
| | 779 | +fn localize_fgof_git_dependencies( |
| | 780 | + project_root: &Path, |
| | 781 | + workspace_root: &Path, |
| | 782 | + sandbox_root: &Path, |
| | 783 | +) -> Result<(), String> { |
| | 784 | + let manifest = project_root.join("fpm.toml"); |
| | 785 | + if !manifest.exists() { |
| | 786 | + return Ok(()); |
| | 787 | + } |
| | 788 | + |
| | 789 | + let original = fs::read_to_string(&manifest) |
| | 790 | + .map_err(|e| format!("cannot read '{}': {}", manifest.display(), e))?; |
| | 791 | + let mut rewritten = Vec::new(); |
| | 792 | + let mut changed = false; |
| | 793 | + let deps_root = sandbox_root.join("deps"); |
| | 794 | + |
| | 795 | + for line in original.lines() { |
| | 796 | + if let Some((dep_name, repo_name)) = parse_fgof_git_dependency(line) { |
| | 797 | + let local_dep = workspace_root.join(&repo_name); |
| | 798 | + if local_dep.exists() { |
| | 799 | + let vendored_dep = deps_root.join(&repo_name); |
| | 800 | + if !vendored_dep.exists() { |
| | 801 | + copy_project_tree(&local_dep, &vendored_dep).map_err(|e| { |
| | 802 | + format!( |
| | 803 | + "cannot vendor dependency '{}' into '{}': {}", |
| | 804 | + local_dep.display(), |
| | 805 | + vendored_dep.display(), |
| | 806 | + e |
| | 807 | + ) |
| | 808 | + })?; |
| | 809 | + localize_fgof_git_dependencies(&vendored_dep, workspace_root, sandbox_root)?; |
| | 810 | + } |
| | 811 | + let relative = relative_path(project_root, &vendored_dep); |
| | 812 | + rewritten.push(format!( |
| | 813 | + "{} = {{ path = \"{}\" }}", |
| | 814 | + dep_name, |
| | 815 | + relative.display() |
| | 816 | + )); |
| | 817 | + changed = true; |
| | 818 | + continue; |
| | 819 | + } |
| | 820 | + } |
| | 821 | + rewritten.push(line.to_string()); |
| | 822 | + } |
| | 823 | + |
| | 824 | + if changed { |
| | 825 | + let mut content = rewritten.join("\n"); |
| | 826 | + if original.ends_with('\n') { |
| | 827 | + content.push('\n'); |
| | 828 | + } |
| | 829 | + fs::write(&manifest, content) |
| | 830 | + .map_err(|e| format!("cannot write '{}': {}", manifest.display(), e))?; |
| | 831 | + } |
| | 832 | + |
| | 833 | + Ok(()) |
| | 834 | +} |
| | 835 | + |
| | 836 | +fn parse_fgof_git_dependency(line: &str) -> Option<(String, String)> { |
| | 837 | + let trimmed = line.trim(); |
| | 838 | + if trimmed.is_empty() || trimmed.starts_with('#') { |
| | 839 | + return None; |
| | 840 | + } |
| | 841 | + let git_prefix = "git = \"https://github.com/FortranGoingOnForty/"; |
| | 842 | + let git_pos = trimmed.find(git_prefix)?; |
| | 843 | + let dep_name = trimmed.split('=').next()?.trim(); |
| | 844 | + let rest = &trimmed[git_pos + git_prefix.len()..]; |
| | 845 | + let repo_name = rest.split(".git").next()?.trim(); |
| | 846 | + if dep_name.is_empty() || repo_name.is_empty() { |
| | 847 | + return None; |
| | 848 | + } |
| | 849 | + Some((dep_name.to_string(), repo_name.to_string())) |
| | 850 | +} |
| | 851 | + |
| | 852 | +fn relative_path(from_dir: &Path, to_path: &Path) -> PathBuf { |
| | 853 | + let from_components: Vec<_> = from_dir.components().collect(); |
| | 854 | + let to_components: Vec<_> = to_path.components().collect(); |
| | 855 | + let mut common = 0usize; |
| | 856 | + while common < from_components.len() |
| | 857 | + && common < to_components.len() |
| | 858 | + && from_components[common] == to_components[common] |
| | 859 | + { |
| | 860 | + common += 1; |
| | 861 | + } |
| | 862 | + |
| | 863 | + let mut relative = PathBuf::new(); |
| | 864 | + for _ in common..from_components.len() { |
| | 865 | + relative.push(".."); |
| | 866 | + } |
| | 867 | + for component in &to_components[common..] { |
| | 868 | + relative.push(component.as_os_str()); |
| | 869 | + } |
| | 870 | + relative |
| | 871 | +} |
| | 872 | + |
| 776 | fn should_skip_copy(name: &std::ffi::OsStr) -> bool { | 873 | fn should_skip_copy(name: &std::ffi::OsStr) -> bool { |
| 777 | matches!( | 874 | matches!( |
| 778 | name.to_str(), | 875 | name.to_str(), |