@@ -262,19 +262,31 @@ fn link_binary_output(sess: &Session,
262
262
check_file_is_writeable ( obj, sess) ;
263
263
}
264
264
265
- let tmpdir = match TempDir :: new ( "rustc" ) {
266
- Ok ( tmpdir) => tmpdir,
267
- Err ( err) => sess. fatal ( & format ! ( "couldn't create a temp dir: {}" , err) ) ,
268
- } ;
269
-
270
265
let mut out_filenames = vec ! [ ] ;
271
266
272
267
if outputs. outputs . contains_key ( & OutputType :: Metadata ) {
273
268
let out_filename = filename_for_metadata ( sess, crate_name, outputs) ;
274
- emit_metadata ( sess, trans, & out_filename) ;
269
+ // To avoid races with another rustc process scanning the output directory,
270
+ // we need to write the file somewhere else and atomically move it to its
271
+ // final destination, with a `fs::rename` call. In order for the rename to
272
+ // always succeed, the temporary file needs to be on the same filesystem,
273
+ // which is why we create it inside the output directory specifically.
274
+ let metadata_tmpdir = match TempDir :: new_in ( out_filename. parent ( ) . unwrap ( ) , "rmeta" ) {
275
+ Ok ( tmpdir) => tmpdir,
276
+ Err ( err) => sess. fatal ( & format ! ( "couldn't create a temp dir: {}" , err) ) ,
277
+ } ;
278
+ let metadata = emit_metadata ( sess, trans, & metadata_tmpdir) ;
279
+ if let Err ( e) = fs:: rename ( metadata, & out_filename) {
280
+ sess. fatal ( & format ! ( "failed to write {}: {}" , out_filename. display( ) , e) ) ;
281
+ }
275
282
out_filenames. push ( out_filename) ;
276
283
}
277
284
285
+ let tmpdir = match TempDir :: new ( "rustc" ) {
286
+ Ok ( tmpdir) => tmpdir,
287
+ Err ( err) => sess. fatal ( & format ! ( "couldn't create a temp dir: {}" , err) ) ,
288
+ } ;
289
+
278
290
if outputs. outputs . should_trans ( ) {
279
291
let out_filename = out_filename ( sess, crate_type, outputs, crate_name) ;
280
292
match crate_type {
@@ -283,10 +295,10 @@ fn link_binary_output(sess: &Session,
283
295
trans,
284
296
RlibFlavor :: Normal ,
285
297
& out_filename,
286
- tmpdir. path ( ) ) . build ( ) ;
298
+ & tmpdir) . build ( ) ;
287
299
}
288
300
config:: CrateTypeStaticlib => {
289
- link_staticlib ( sess, trans, & out_filename, tmpdir. path ( ) ) ;
301
+ link_staticlib ( sess, trans, & out_filename, & tmpdir) ;
290
302
}
291
303
_ => {
292
304
link_natively ( sess, crate_type, & out_filename, trans, tmpdir. path ( ) ) ;
@@ -321,14 +333,23 @@ fn archive_config<'a>(sess: &'a Session,
321
333
}
322
334
}
323
335
324
- fn emit_metadata < ' a > ( sess : & ' a Session , trans : & CrateTranslation , out_filename : & Path ) {
325
- let result = fs:: File :: create ( out_filename) . and_then ( |mut f| {
336
+ /// We use a temp directory here to avoid races between concurrent rustc processes,
337
+ /// such as builds in the same directory using the same filename for metadata while
338
+ /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
339
+ /// directory being searched for `extern crate` (observing an incomplete file).
340
+ /// The returned path is the temporary file containing the complete metadata.
341
+ fn emit_metadata < ' a > ( sess : & ' a Session , trans : & CrateTranslation , tmpdir : & TempDir )
342
+ -> PathBuf {
343
+ let out_filename = tmpdir. path ( ) . join ( METADATA_FILENAME ) ;
344
+ let result = fs:: File :: create ( & out_filename) . and_then ( |mut f| {
326
345
f. write_all ( & trans. metadata . raw_data )
327
346
} ) ;
328
347
329
348
if let Err ( e) = result {
330
349
sess. fatal ( & format ! ( "failed to write {}: {}" , out_filename. display( ) , e) ) ;
331
350
}
351
+
352
+ out_filename
332
353
}
333
354
334
355
enum RlibFlavor {
@@ -346,7 +367,7 @@ fn link_rlib<'a>(sess: &'a Session,
346
367
trans : & CrateTranslation ,
347
368
flavor : RlibFlavor ,
348
369
out_filename : & Path ,
349
- tmpdir : & Path ) -> ArchiveBuilder < ' a > {
370
+ tmpdir : & TempDir ) -> ArchiveBuilder < ' a > {
350
371
info ! ( "preparing rlib to {:?}" , out_filename) ;
351
372
let mut ab = ArchiveBuilder :: new ( archive_config ( sess, out_filename, None ) ) ;
352
373
@@ -408,12 +429,8 @@ fn link_rlib<'a>(sess: &'a Session,
408
429
match flavor {
409
430
RlibFlavor :: Normal => {
410
431
// Instead of putting the metadata in an object file section, rlibs
411
- // contain the metadata in a separate file. We use a temp directory
412
- // here so concurrent builds in the same directory don't try to use
413
- // the same filename for metadata (stomping over one another)
414
- let metadata = tmpdir. join ( METADATA_FILENAME ) ;
415
- emit_metadata ( sess, trans, & metadata) ;
416
- ab. add_file ( & metadata) ;
432
+ // contain the metadata in a separate file.
433
+ ab. add_file ( & emit_metadata ( sess, trans, tmpdir) ) ;
417
434
418
435
// For LTO purposes, the bytecode of this library is also inserted
419
436
// into the archive.
@@ -457,7 +474,7 @@ fn link_rlib<'a>(sess: &'a Session,
457
474
fn link_staticlib ( sess : & Session ,
458
475
trans : & CrateTranslation ,
459
476
out_filename : & Path ,
460
- tempdir : & Path ) {
477
+ tempdir : & TempDir ) {
461
478
let mut ab = link_rlib ( sess,
462
479
trans,
463
480
RlibFlavor :: StaticlibBase ,
0 commit comments