@@ -262,19 +262,31 @@ fn link_binary_output(sess: &Session,
262262 check_file_is_writeable ( obj, sess) ;
263263 }
264264
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-
270265 let mut out_filenames = vec ! [ ] ;
271266
272267 if outputs. outputs . contains_key ( & OutputType :: Metadata ) {
273268 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+ }
275282 out_filenames. push ( out_filename) ;
276283 }
277284
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+
278290 if outputs. outputs . should_trans ( ) {
279291 let out_filename = out_filename ( sess, crate_type, outputs, crate_name) ;
280292 match crate_type {
@@ -283,10 +295,10 @@ fn link_binary_output(sess: &Session,
283295 trans,
284296 RlibFlavor :: Normal ,
285297 & out_filename,
286- tmpdir. path ( ) ) . build ( ) ;
298+ & tmpdir) . build ( ) ;
287299 }
288300 config:: CrateTypeStaticlib => {
289- link_staticlib ( sess, trans, & out_filename, tmpdir. path ( ) ) ;
301+ link_staticlib ( sess, trans, & out_filename, & tmpdir) ;
290302 }
291303 _ => {
292304 link_natively ( sess, crate_type, & out_filename, trans, tmpdir. path ( ) ) ;
@@ -321,14 +333,23 @@ fn archive_config<'a>(sess: &'a Session,
321333 }
322334}
323335
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| {
326345 f. write_all ( & trans. metadata . raw_data )
327346 } ) ;
328347
329348 if let Err ( e) = result {
330349 sess. fatal ( & format ! ( "failed to write {}: {}" , out_filename. display( ) , e) ) ;
331350 }
351+
352+ out_filename
332353}
333354
334355enum RlibFlavor {
@@ -346,7 +367,7 @@ fn link_rlib<'a>(sess: &'a Session,
346367 trans : & CrateTranslation ,
347368 flavor : RlibFlavor ,
348369 out_filename : & Path ,
349- tmpdir : & Path ) -> ArchiveBuilder < ' a > {
370+ tmpdir : & TempDir ) -> ArchiveBuilder < ' a > {
350371 info ! ( "preparing rlib to {:?}" , out_filename) ;
351372 let mut ab = ArchiveBuilder :: new ( archive_config ( sess, out_filename, None ) ) ;
352373
@@ -408,12 +429,8 @@ fn link_rlib<'a>(sess: &'a Session,
408429 match flavor {
409430 RlibFlavor :: Normal => {
410431 // 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) ) ;
417434
418435 // For LTO purposes, the bytecode of this library is also inserted
419436 // into the archive.
@@ -457,7 +474,7 @@ fn link_rlib<'a>(sess: &'a Session,
457474fn link_staticlib ( sess : & Session ,
458475 trans : & CrateTranslation ,
459476 out_filename : & Path ,
460- tempdir : & Path ) {
477+ tempdir : & TempDir ) {
461478 let mut ab = link_rlib ( sess,
462479 trans,
463480 RlibFlavor :: StaticlibBase ,
0 commit comments