@@ -24,19 +24,49 @@ const GNU_MAKE: &str = "make";
2424) ) ]
2525const 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| e. as_ref ( ) . is_ok_and ( |e| e. file_type ( ) . is_ok_and ( |t| t. is_file ( ) ) ) )
37+ } ) {
38+ Some ( Ok ( entry) ) => entry. path ( ) ,
39+ _ => return false ,
40+ } ;
41+ let probe_dst = dst. join ( ".hardlink_probe" ) ;
42+ let ok = fs:: hard_link ( & probe_src, & probe_dst) . is_ok ( ) ;
43+ let _ = fs:: remove_file ( & probe_dst) ;
44+ ok
45+ }
46+
47+ /// Recursively mirror a directory tree from `src` to `dst`.
48+ ///
49+ /// When `use_hardlinks` is true, regular files are hard-linked instead of
50+ /// copied, saving disk space and I/O.
51+ /// Directories listed in `SKIP_DIRS` are skipped entirely.
52+ fn mirror_dir_all ( src : & Path , dst : & Path , use_hardlinks : bool ) {
2953 fs:: create_dir_all ( dst) . unwrap ( ) ;
3054 for entry in fs:: read_dir ( src) . unwrap ( ) {
3155 let entry = entry. unwrap ( ) ;
56+ let name = entry. file_name ( ) ;
3257 let ty = entry. file_type ( ) . unwrap ( ) ;
33- let dst_path = dst. join ( entry . file_name ( ) ) ;
58+ let dst_path = dst. join ( & name ) ;
3459 if ty. is_dir ( ) {
35- copy_dir_all ( & entry. path ( ) , & dst_path) ;
60+ if SKIP_DIRS . iter ( ) . any ( |s| * s == name) {
61+ continue ;
62+ }
63+ mirror_dir_all ( & entry. path ( ) , & dst_path, use_hardlinks) ;
3664 } else if ty. is_symlink ( ) {
3765 let target = fs:: read_link ( entry. path ( ) ) . unwrap ( ) ;
3866 #[ cfg( unix) ]
3967 std:: os:: unix:: fs:: symlink ( & target, & dst_path) . unwrap ( ) ;
68+ } else if use_hardlinks {
69+ fs:: hard_link ( entry. path ( ) , & dst_path) . unwrap ( ) ;
4070 } else {
4171 fs:: copy ( entry. path ( ) , & dst_path) . unwrap ( ) ;
4272 }
@@ -66,14 +96,17 @@ fn main() {
6696 let honggfuzz_target = Path :: new ( & env:: var ( "CRATE_ROOT" ) . unwrap ( ) ) // from honggfuzz
6797 . join ( honggfuzz_target) ; // resolve the original honggfuzz_target relative to CRATE_ROOT
6898
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.
99+ // Mirror honggfuzz source tree into OUT_DIR so we can build in a
100+ // writable directory. This is required for Nix builds where the source
101+ // directory is read-only. Use hardlinks when possible (same filesystem)
102+ // to save disk space, falling back to copies otherwise.
72103 let build_dir = out_dir. join ( "honggfuzz" ) ;
73104 if build_dir. exists ( ) {
74105 fs:: remove_dir_all ( & build_dir) . unwrap ( ) ;
75106 }
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
0 commit comments