diff --git a/src/lib.rs b/src/lib.rs index 7a82cfa..4454381 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,7 +204,19 @@ fn wait_for_other_builds( build_dir: &Path ) { thread::sleep( Duration::from_secs(5) ); waiting = false; for entry in WalkDir::new( build_dir ) { - let entry = entry.unwrap(); + // Sibling crates' build scripts can create and delete tempfiles + // inside this directory while we're walking. WalkDir surfaces + // those as Err(NotFound) at iteration time. Don't panic — the + // directory is still in flux, so just keep waiting another + // round. A NotFound here means we definitely shouldn't declare + // the build "stable" yet. + let entry = match entry { + Ok( e ) => e, + Err( _ ) => { + waiting = true; + continue; + } + }; let path = entry.path(); if generated.insert( path.to_owned() ) { waiting = true; @@ -231,15 +243,17 @@ fn locate_manifest_paths() -> HashMap> { let path = entry.path(); if path.is_dir() { let inwelling_file_path = path.join("out").join( MANIFEST_DIR_INWELLING ); - if inwelling_file_path.exists() { - let contents = fs::read_to_string( &inwelling_file_path ) - .expect( &format!( "to read {:?} to get one manifest path", inwelling_file_path )); + // The .exists() check and the subsequent read are not + // atomic — sibling builds can clean up the file between + // them. Use match-on-read instead of expect so a TOCTOU + // race becomes a graceful skip instead of a panic. + if let Ok( contents ) = fs::read_to_string( &inwelling_file_path ) { let mut lines = contents.lines(); - let manifest_dir = lines.next() - .expect( &format!( "{:?} should contain the line of manifest dir.", inwelling_file_path )); - path_bufs - .entry( PathBuf::from( manifest_dir ).join( "Cargo.toml" )) - .or_insert_with( || lines.map( ToOwned::to_owned ).collect() ); + if let Some( manifest_dir ) = lines.next() { + path_bufs + .entry( PathBuf::from( manifest_dir ).join( "Cargo.toml" )) + .or_insert_with( || lines.map( ToOwned::to_owned ).collect() ); + } }}}}} path_bufs }