@@ -93,7 +93,7 @@ fn run_buildrs() -> Result<(), String> {
93
93
command
94
94
. current_dir ( & working_directory)
95
95
. envs ( target_env_vars)
96
- . env ( "OUT_DIR" , out_dir_abs)
96
+ . env ( "OUT_DIR" , & out_dir_abs)
97
97
. env ( "CARGO_MANIFEST_DIR" , manifest_dir)
98
98
. env ( "RUSTC" , rustc)
99
99
. env ( "RUST_BACKTRACE" , "full" ) ;
@@ -228,9 +228,10 @@ fn run_buildrs() -> Result<(), String> {
228
228
229
229
// Delete any runfiles that do not need to be propagated to down stream dependents.
230
230
if let Some ( cargo_manifest_maker) = cargo_manifest_maker {
231
- cargo_manifest_maker. drain_runfiles_dir ( ) . unwrap ( ) ;
231
+ cargo_manifest_maker
232
+ . drain_runfiles_dir ( & out_dir_abs)
233
+ . unwrap ( ) ;
232
234
}
233
-
234
235
Ok ( ( ) )
235
236
}
236
237
@@ -568,18 +569,19 @@ impl RunfilesMaker {
568
569
}
569
570
570
571
/// Delete runfiles from the runfiles directory that do not match user defined suffixes
571
- fn drain_runfiles_dir ( & self ) -> Result < ( ) , String > {
572
+ fn drain_runfiles_dir ( & self , out_dir : & Path ) -> Result < ( ) , String > {
572
573
if cfg ! ( target_family = "windows" ) {
573
574
// If symlinks are supported then symlinks will have been used.
574
575
let supports_symlinks = system_supports_symlinks ( & self . output_dir ) ?;
575
576
if supports_symlinks {
576
- self . drain_runfiles_dir_unix ( )
577
+ self . drain_runfiles_dir_unix ( ) ? ;
577
578
} else {
578
- self . drain_runfiles_dir_windows ( )
579
+ self . drain_runfiles_dir_windows ( ) ? ;
579
580
}
580
581
} else {
581
- self . drain_runfiles_dir_unix ( )
582
+ self . drain_runfiles_dir_unix ( ) ? ;
582
583
}
584
+ replace_symlinks_in_out_dir ( out_dir)
583
585
}
584
586
}
585
587
@@ -720,6 +722,56 @@ fn parse_rustc_cfg_output(stdout: &str) -> BTreeMap<String, String> {
720
722
. collect ( )
721
723
}
722
724
725
+ /// Iterates over the given directory recursively and resolves any symlinks
726
+ ///
727
+ /// Symlinks shouldn't present in `out_dir` as those amy contain paths to sandboxes which doesn't exists anymore.
728
+ /// Therefore, bazel will fail because of dangling symlinks.
729
+ fn replace_symlinks_in_out_dir ( out_dir : & Path ) -> Result < ( ) , String > {
730
+ if out_dir. is_dir ( ) {
731
+ let out_dir_paths = std:: fs:: read_dir ( out_dir) . map_err ( |e| {
732
+ format ! (
733
+ "Failed to read directory `{}` with {:?}" ,
734
+ out_dir. display( ) ,
735
+ e
736
+ )
737
+ } ) ?;
738
+ for entry in out_dir_paths {
739
+ let entry =
740
+ entry. map_err ( |e| format ! ( "Failed to read directory entry with {:?}" , e, ) ) ?;
741
+ let path = entry. path ( ) ;
742
+
743
+ if path. is_symlink ( ) {
744
+ let target_path = std:: fs:: read_link ( & path) . map_err ( |e| {
745
+ format ! ( "Failed to read symlink `{}` with {:?}" , path. display( ) , e, )
746
+ } ) ?;
747
+ // we don't want to replace relative symlinks
748
+ if target_path. is_relative ( ) {
749
+ continue ;
750
+ }
751
+ std:: fs:: remove_file ( & path)
752
+ . map_err ( |e| format ! ( "Failed remove file `{}` with {:?}" , path. display( ) , e) ) ?;
753
+ std:: fs:: copy ( & target_path, & path) . map_err ( |e| {
754
+ format ! (
755
+ "Failed to copy `{} -> {}` with {:?}" ,
756
+ target_path. display( ) ,
757
+ path. display( ) ,
758
+ e
759
+ )
760
+ } ) ?;
761
+ } else if path. is_dir ( ) {
762
+ replace_symlinks_in_out_dir ( & path) . map_err ( |e| {
763
+ format ! (
764
+ "Failed to normalize nested directory `{}` with {}" ,
765
+ path. display( ) ,
766
+ e,
767
+ )
768
+ } ) ?;
769
+ }
770
+ }
771
+ }
772
+ Ok ( ( ) )
773
+ }
774
+
723
775
fn main ( ) {
724
776
std:: process:: exit ( match run_buildrs ( ) {
725
777
Ok ( _) => 0 ,
@@ -735,6 +787,9 @@ fn main() {
735
787
mod test {
736
788
use super :: * ;
737
789
790
+ use std:: fs;
791
+ use std:: io:: Write ;
792
+
738
793
#[ test]
739
794
fn rustc_cfg_parsing ( ) {
740
795
let macos_output = r#"\
@@ -775,4 +830,67 @@ windows
775
830
assert_eq ! ( tree[ "CARGO_CFG_WINDOWS" ] , "" ) ;
776
831
assert_eq ! ( tree[ "CARGO_CFG_TARGET_FAMILY" ] , "windows" ) ;
777
832
}
833
+
834
+ fn prepare_output_dir_with_symlinks ( ) -> PathBuf {
835
+ let test_tmp = PathBuf :: from ( std:: env:: var ( "TEST_TMPDIR" ) . unwrap ( ) ) ;
836
+ let out_dir = test_tmp. join ( "out_dir" ) ;
837
+ fs:: create_dir ( & out_dir) . unwrap ( ) ;
838
+ let nested_dir = out_dir. join ( "nested" ) ;
839
+ fs:: create_dir ( & nested_dir) . unwrap ( ) ;
840
+
841
+ let temp_dir_file = test_tmp. join ( "outside.txt" ) ;
842
+ let mut file = fs:: File :: create ( & temp_dir_file) . unwrap ( ) ;
843
+ file. write_all ( b"outside world" ) . unwrap ( ) ;
844
+ // symlink abs path outside of the out_dir
845
+ symlink ( & temp_dir_file, & out_dir. join ( "outside.txt" ) ) . unwrap ( ) ;
846
+
847
+ let inside_dir_file = out_dir. join ( "inside.txt" ) ;
848
+ let mut file = fs:: File :: create ( inside_dir_file) . unwrap ( ) ;
849
+ file. write_all ( b"inside world" ) . unwrap ( ) ;
850
+ // symlink relative next to the file in the out_dir
851
+ symlink (
852
+ & PathBuf :: from ( "inside.txt" ) ,
853
+ & out_dir. join ( "inside_link.txt" ) ,
854
+ )
855
+ . unwrap ( ) ;
856
+ // symlink relative within a subdir in the out_dir
857
+ symlink (
858
+ & PathBuf :: from ( ".." ) . join ( "inside.txt" ) ,
859
+ & out_dir. join ( "nested" ) . join ( "inside_link.txt" ) ,
860
+ )
861
+ . unwrap ( ) ;
862
+
863
+ out_dir
864
+ }
865
+
866
+ #[ cfg( any( target_family = "windows" , target_family = "unix" ) ) ]
867
+ #[ test]
868
+ fn replace_symlinks_in_out_dir ( ) {
869
+ let out_dir = prepare_output_dir_with_symlinks ( ) ;
870
+ super :: replace_symlinks_in_out_dir ( & out_dir) . unwrap ( ) ;
871
+
872
+ // this should be replaced because it is an absolute symlink
873
+ let file_path = out_dir. join ( "outside.txt" ) ;
874
+ assert ! ( !file_path. is_symlink( ) ) ;
875
+ let contents = fs:: read_to_string ( file_path) . unwrap ( ) ;
876
+ assert_eq ! ( contents, "outside world" ) ;
877
+
878
+ // this is the file created inside the out_dir
879
+ let file_path = out_dir. join ( "inside.txt" ) ;
880
+ assert ! ( !file_path. is_symlink( ) ) ;
881
+ let contents = fs:: read_to_string ( file_path) . unwrap ( ) ;
882
+ assert_eq ! ( contents, "inside world" ) ;
883
+
884
+ // this is the symlink in the out_dir
885
+ let file_path = out_dir. join ( "inside_link.txt" ) ;
886
+ assert ! ( file_path. is_symlink( ) ) ;
887
+ let contents = fs:: read_to_string ( file_path) . unwrap ( ) ;
888
+ assert_eq ! ( contents, "inside world" ) ;
889
+
890
+ // this is the symlink in the out_dir under another directory which refers to ../inside.txt
891
+ let file_path = out_dir. join ( "nested" ) . join ( "inside_link.txt" ) ;
892
+ assert ! ( file_path. is_symlink( ) ) ;
893
+ let contents = fs:: read_to_string ( file_path) . unwrap ( ) ;
894
+ assert_eq ! ( contents, "inside world" ) ;
895
+ }
778
896
}
0 commit comments