|
| 1 | +use std::collections::HashSet; |
1 | 2 | use std::fs; |
2 | 3 | use std::path::Path; |
3 | 4 |
|
4 | 5 | use anyhow::{Context, Result}; |
5 | 6 |
|
6 | | -pub(crate) fn copy_glob_with_predicate( |
7 | | - src_dir: &Path, |
| 7 | +pub(crate) fn union_glob_with_predicate( |
| 8 | + src_dirs: impl Iterator<Item: AsRef<Path>>, |
8 | 9 | dst_dir: &Path, |
9 | 10 | glob: &str, |
10 | 11 | predicate: impl Fn(&Path) -> bool, |
11 | 12 | ) -> Result<()> { |
12 | | - let files = glob::glob(&format!("{}/{}", src_dir.display(), glob)) |
13 | | - .context("Failed to read include source directory")?; |
14 | | - for file in files { |
15 | | - let src = file.context("Failed to read include source file")?; |
16 | | - let dst = src.strip_prefix(src_dir).unwrap(); |
17 | | - if !predicate(dst) { |
18 | | - continue; |
| 13 | + let mut copied = HashSet::new(); |
| 14 | + |
| 15 | + for src_dir in src_dirs { |
| 16 | + let src_dir = src_dir.as_ref(); |
| 17 | + let files = glob::glob(&format!("{}/{}", src_dir.display(), glob)) |
| 18 | + .context("Failed to read source directory")?; |
| 19 | + for file in files { |
| 20 | + let src = file.context("Failed to read source file")?; |
| 21 | + if !src.is_file() { |
| 22 | + // contents will also show up |
| 23 | + continue; |
| 24 | + } |
| 25 | + let suffix = src.strip_prefix(src_dir)?; |
| 26 | + if !predicate(suffix) { |
| 27 | + continue; |
| 28 | + } |
| 29 | + let dst = dst_dir.join(suffix); |
| 30 | + |
| 31 | + let parent_dir = dst.parent().ok_or(anyhow::anyhow!( |
| 32 | + "Could not get parent directory of destination file" |
| 33 | + ))?; |
| 34 | + fs::create_dir_all(parent_dir).context("Failed to create subdirectory")?; |
| 35 | + fs::copy(&src, &dst).context("Failed to copy file")?; |
| 36 | + copied.insert(dst); |
19 | 37 | } |
20 | | - let dst = dst_dir.join(dst); |
| 38 | + } |
21 | 39 |
|
22 | | - fs::create_dir_all(dst.parent().unwrap()) |
23 | | - .context("Failed to create include subdirectory")?; |
24 | | - fs::copy(&src, &dst).context("Failed to copy include file")?; |
| 40 | + // This doesn't do a good job of cleaning up empty directories, |
| 41 | + // but that shouldn't cause any problems right now, since this is |
| 42 | + // only used for header files/libraries/binaries. |
| 43 | + let dst_files = glob::glob(&format!("{}/**/*", dst_dir.display())) |
| 44 | + .context("Failed to read destination directory")?; |
| 45 | + for dst_file in dst_files { |
| 46 | + let dst = dst_file.context("Failed to read destination file")?; |
| 47 | + if !copied.contains(&dst) { |
| 48 | + if dst.is_file() { |
| 49 | + fs::remove_file(dst).context("Failed to remove stale destination file")?; |
| 50 | + } |
| 51 | + } |
25 | 52 | } |
26 | 53 | Ok(()) |
27 | 54 | } |
28 | 55 |
|
29 | | -pub(crate) fn copy_glob(src_dir: &Path, dst_dir: &Path, glob: &str) -> Result<()> { |
30 | | - copy_glob_with_predicate(src_dir, dst_dir, glob, |_| true) |
| 56 | +pub(crate) fn union_glob( |
| 57 | + src_dir: impl Iterator<Item: AsRef<Path>>, |
| 58 | + dst_dir: &Path, |
| 59 | + glob: &str, |
| 60 | +) -> Result<()> { |
| 61 | + union_glob_with_predicate(src_dir, dst_dir, glob, |_| true) |
31 | 62 | } |
0 commit comments