@@ -24,19 +24,52 @@ 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| {
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