101
101
use crate :: core:: compiler:: CompileTarget ;
102
102
use crate :: core:: Workspace ;
103
103
use crate :: util:: paths;
104
- use crate :: util:: { CargoResult , FileLock } ;
104
+ use crate :: util:: { CargoResult , CargoResultExt , FileLock } ;
105
105
use std:: path:: { Path , PathBuf } ;
106
106
107
107
/// Contains the paths of all target output locations.
@@ -125,6 +125,8 @@ pub struct Layout {
125
125
examples : PathBuf ,
126
126
/// The directory for rustdoc output: `$root/doc`
127
127
doc : PathBuf ,
128
+ /// Root in system's temporary directory
129
+ temp_root : Option < PathBuf > ,
128
130
/// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
129
131
/// struct is `drop`ped.
130
132
_lock : FileLock ,
@@ -170,6 +172,7 @@ impl Layout {
170
172
fingerprint : dest. join ( ".fingerprint" ) ,
171
173
examples : dest. join ( "examples" ) ,
172
174
doc : root. join ( "doc" ) ,
175
+ temp_root : Self :: temp_root_path ( & root) ,
173
176
root,
174
177
dest,
175
178
_lock : lock,
@@ -178,15 +181,87 @@ impl Layout {
178
181
179
182
/// Makes sure all directories stored in the Layout exist on the filesystem.
180
183
pub fn prepare ( & mut self ) -> CargoResult < ( ) > {
181
- paths:: create_dir_all ( & self . deps ) ?;
182
- paths:: create_dir_all ( & self . incremental ) ?;
183
- paths:: create_dir_all ( & self . fingerprint ) ?;
184
+ let temp_root = self . temp_root . as_deref ( ) ;
185
+ if let Some ( temp_root) = temp_root {
186
+ paths:: create_dir_all ( temp_root) ?;
187
+ }
188
+ Self :: create_dir_or_symlink_to_temp ( temp_root, & self . deps ) ?;
189
+ Self :: create_dir_or_symlink_to_temp ( temp_root, & self . incremental ) ?;
190
+ Self :: create_dir_or_symlink_to_temp ( temp_root, & self . fingerprint ) ?;
191
+ Self :: create_dir_or_symlink_to_temp ( temp_root, & self . build ) ?;
184
192
paths:: create_dir_all ( & self . examples ) ?;
185
- paths:: create_dir_all ( & self . build ) ?;
186
193
187
194
Ok ( ( ) )
188
195
}
189
196
197
+ /// Create a path for a subdirectory in system's temporary directory
198
+ /// that is specifically for the root path.
199
+ #[ cfg( unix) ]
200
+ fn temp_root_path ( target_root : & Path ) -> Option < PathBuf > {
201
+ let temp_root = paths:: persistent_temp_path ( ) ?;
202
+
203
+ // Each target dir gets its own temp subdir based on a hash of the path
204
+ // with some printable chars for friendlier paths
205
+ let root_bytes = paths:: path2bytes ( target_root) . ok ( ) ?;
206
+ let mut dir_name = String :: with_capacity ( 64 + 17 ) ;
207
+ for ch in root_bytes[ root_bytes. len ( ) . saturating_sub ( 64 ) ..]
208
+ . iter ( )
209
+ . skip ( 1 ) // initial slash
210
+ . copied ( )
211
+ {
212
+ dir_name. push ( if ch. is_ascii_alphanumeric ( ) {
213
+ ch as char
214
+ } else {
215
+ '-'
216
+ } ) ;
217
+ }
218
+ dir_name. push ( '-' ) ;
219
+ dir_name. push_str ( & crate :: util:: short_hash ( & root_bytes) ) ;
220
+
221
+ Some ( temp_root. join ( dir_name) )
222
+ }
223
+
224
+ /// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
225
+ /// so symlinking to temp won't be used.
226
+ #[ cfg( not( unix) ) ]
227
+ fn temp_root_path ( _root : & Path ) -> Option < PathBuf > {
228
+ None
229
+ }
230
+
231
+ /// Symlink `path` to inside of `temp_root`, or create the `path` as dir as a fallback
232
+ #[ cfg( unix) ]
233
+ fn create_dir_or_symlink_to_temp ( temp_root : Option < & Path > , path : & Path ) -> CargoResult < ( ) > {
234
+ // Don't change existing target subdirectories (this also verifies that the symlink is valid)
235
+ if path. exists ( ) {
236
+ return Ok ( ( ) ) ;
237
+ }
238
+ // Clean up broken symlinks (OK to ignore failures, subsequent operations will report a failure)
239
+ let _ = std:: fs:: remove_file ( path) ;
240
+
241
+ if let Some ( temp_root) = temp_root {
242
+ let file_name = path. file_name ( ) . expect ( "/ isn't allowed" ) ;
243
+ let temp_dest = temp_root. join ( file_name) ;
244
+ paths:: create_dir_all ( & temp_dest) ?;
245
+ std:: os:: unix:: fs:: symlink ( & temp_dest, path) . chain_err ( || {
246
+ format ! (
247
+ "failed to symlink `{}` to `{}`" ,
248
+ path. display( ) ,
249
+ temp_dest. display( )
250
+ )
251
+ } ) ?;
252
+ Ok ( ( ) )
253
+ } else {
254
+ paths:: create_dir_all ( path)
255
+ }
256
+ }
257
+
258
+ /// On non-Unix it's not safe to assume that symlinks are reliable and efficient,
259
+ /// so symlinking to temp won't be used.
260
+ #[ cfg( not( unix) ) ]
261
+ fn create_dir_or_symlink_to_temp ( _temp_root : & Path , path : & Path ) -> CargoResult < ( ) > {
262
+ paths:: create_dir_all ( path)
263
+ }
264
+
190
265
/// Fetch the destination path for final artifacts (`/…/target/debug`).
191
266
pub fn dest ( & self ) -> & Path {
192
267
& self . dest
0 commit comments