Skip to content

Commit 9a723ea

Browse files
committed
chore: use hardlinks when copying honggfuzz source code
1 parent f9ca63e commit 9a723ea

File tree

1 file changed

+54
-13
lines changed

1 file changed

+54
-13
lines changed

build.rs

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,52 @@ const GNU_MAKE: &str = "make";
2424
))]
2525
const GNU_MAKE: &str = "gmake";
2626

27-
/// Recursively copy a directory tree from `src` to `dst`.
28-
fn copy_dir_all(src: &Path, dst: &Path) {
27+
/// Directories to skip when mirroring the source tree (they are not needed
28+
/// for the build and would waste a lot of space).
29+
const SKIP_DIRS: &[&str] = &["examples"];
30+
31+
/// Check whether hardlinks work between `src` and `dst` directories by
32+
/// trying to hardlink an existing file from `src` into `dst`.
33+
fn can_hardlink(src: &Path, dst: &Path) -> bool {
34+
// Pick any existing file in src to use as a probe.
35+
let probe_src = match fs::read_dir(src).ok().and_then(|mut d| {
36+
d.find(|e| {
37+
e.as_ref()
38+
.is_ok_and(|e| e.file_type().is_ok_and(|t| t.is_file()))
39+
})
40+
}) {
41+
Some(Ok(entry)) => entry.path(),
42+
_ => return false,
43+
};
44+
let probe_dst = dst.join(".hardlink_probe");
45+
let ok = fs::hard_link(&probe_src, &probe_dst).is_ok();
46+
let _ = fs::remove_file(&probe_dst);
47+
ok
48+
}
49+
50+
/// Recursively mirror a directory tree from `src` to `dst`.
51+
///
52+
/// When `use_hardlinks` is true, regular files are hard-linked instead of
53+
/// copied, saving disk space and I/O.
54+
/// Directories listed in `SKIP_DIRS` are skipped entirely.
55+
fn mirror_dir_all(src: &Path, dst: &Path, use_hardlinks: bool) {
2956
fs::create_dir_all(dst).unwrap();
3057
for entry in fs::read_dir(src).unwrap() {
3158
let entry = entry.unwrap();
59+
let name = entry.file_name();
3260
let ty = entry.file_type().unwrap();
33-
let dst_path = dst.join(entry.file_name());
61+
let dst_path = dst.join(&name);
3462
if ty.is_dir() {
35-
copy_dir_all(&entry.path(), &dst_path);
63+
if SKIP_DIRS.iter().any(|s| *s == name) {
64+
continue;
65+
}
66+
mirror_dir_all(&entry.path(), &dst_path, use_hardlinks);
3667
} else if ty.is_symlink() {
3768
let target = fs::read_link(entry.path()).unwrap();
3869
#[cfg(unix)]
3970
std::os::unix::fs::symlink(&target, &dst_path).unwrap();
71+
} else if use_hardlinks {
72+
fs::hard_link(entry.path(), &dst_path).unwrap();
4073
} else {
4174
fs::copy(entry.path(), &dst_path).unwrap();
4275
}
@@ -66,14 +99,14 @@ fn main() {
6699
let honggfuzz_target = Path::new(&env::var("CRATE_ROOT").unwrap()) // from honggfuzz
67100
.join(honggfuzz_target); // resolve the original honggfuzz_target relative to CRATE_ROOT
68101

69-
// Copy honggfuzz source tree into OUT_DIR so we can build in a writable
70-
// directory. This is required for Nix builds where the source directory
71-
// is read-only.
102+
// Mirror honggfuzz source tree into OUT_DIR so we can build in a
103+
// writable directory. This is required for Nix builds where the source
104+
// directory is read-only. Use hardlinks when possible (same filesystem)
105+
// to save disk space, falling back to copies otherwise.
72106
let build_dir = out_dir.join("honggfuzz");
73-
if build_dir.exists() {
74-
fs::remove_dir_all(&build_dir).unwrap();
75-
}
76-
copy_dir_all(Path::new("honggfuzz"), &build_dir);
107+
fs::create_dir_all(&build_dir).unwrap();
108+
let use_hardlinks = can_hardlink(Path::new("honggfuzz"), &build_dir);
109+
mirror_dir_all(Path::new("honggfuzz"), &build_dir, use_hardlinks);
77110

78111
let build_dir_str = build_dir.to_str().unwrap();
79112

@@ -84,13 +117,21 @@ fn main() {
84117
.unwrap_or_else(|_e| panic!("failed to run \"{} -C {} honggfuzz libhfuzz/libhfuzz.a libhfcommon/libhfcommon.a\"", GNU_MAKE, build_dir_str));
85118
assert!(status.success());
86119

87-
fs::copy(build_dir.join("libhfuzz/libhfuzz.a"), out_dir.join("libhfuzz.a")).unwrap();
120+
fs::copy(
121+
build_dir.join("libhfuzz/libhfuzz.a"),
122+
out_dir.join("libhfuzz.a"),
123+
)
124+
.unwrap();
88125
fs::copy(
89126
build_dir.join("libhfcommon/libhfcommon.a"),
90127
out_dir.join("libhfcommon.a"),
91128
)
92129
.unwrap();
93-
fs::copy(build_dir.join("honggfuzz"), honggfuzz_target.join("honggfuzz")).unwrap();
130+
fs::copy(
131+
build_dir.join("honggfuzz"),
132+
honggfuzz_target.join("honggfuzz"),
133+
)
134+
.unwrap();
94135

95136
// tell cargo how to link final executable to hfuzz static library
96137
println!("cargo:rustc-link-lib=static={}", "hfuzz");

0 commit comments

Comments
 (0)