@@ -49,10 +49,11 @@ extern crate cc;
4949use  std:: env; 
5050use  std:: ffi:: { OsStr ,  OsString } ; 
5151use  std:: fs:: { self ,  File } ; 
52- use  std:: io:: prelude:: * ; 
53- use  std:: io:: ErrorKind ; 
52+ use  std:: io:: { self ,  prelude:: * ,  ErrorKind } ; 
5453use  std:: path:: { Path ,  PathBuf } ; 
55- use  std:: process:: Command ; 
54+ use  std:: process:: { Command ,  ExitStatus ,  Stdio } ; 
55+ use  std:: sync:: { Arc ,  Mutex } ; 
56+ use  std:: thread:: { self } ; 
5657
5758/// Builder style configuration for a pending CMake build. 
5859pub  struct  Config  { 
@@ -482,6 +483,7 @@ impl Config {
482483        // Build up the first cmake command to build the build system. 
483484        let  executable = env:: var ( "CMAKE" ) . unwrap_or ( "cmake" . to_owned ( ) ) ; 
484485        let  mut  conf_cmd = Command :: new ( & executable) ; 
486+         conf_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ; 
485487
486488        if  self . verbose_cmake  { 
487489            conf_cmd. arg ( "-Wdev" ) ; 
@@ -738,11 +740,14 @@ impl Config {
738740            conf_cmd. env ( k,  v) ; 
739741        } 
740742
743+         conf_cmd. env ( "CMAKE_PREFIX_PATH" ,  cmake_prefix_path) ; 
744+         conf_cmd. args ( & self . configure_args ) ; 
741745        if  self . always_configure  || !build. join ( CMAKE_CACHE_FILE ) . exists ( )  { 
742-             conf_cmd. args ( & self . configure_args ) ; 
743-             run ( 
744-                 conf_cmd. env ( "CMAKE_PREFIX_PATH" ,  cmake_prefix_path) , 
745-                 "cmake" , 
746+             run_cmake_action ( 
747+                 & build, 
748+                 CMakeAction :: Configure  { 
749+                     conf_cmd :  & mut  conf_cmd, 
750+                 } , 
746751            ) ; 
747752        }  else  { 
748753            println ! ( "CMake project was already configured. Skipping configuration step." ) ; 
@@ -790,6 +795,7 @@ impl Config {
790795        // And build! 
791796        let  target = self . cmake_target . clone ( ) . unwrap_or ( "install" . to_string ( ) ) ; 
792797        let  mut  build_cmd = Command :: new ( & executable) ; 
798+         build_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ; 
793799        for  & ( ref  k,  ref  v)  in  c_compiler. env ( ) . iter ( ) . chain ( & self . env )  { 
794800            build_cmd. env ( k,  v) ; 
795801        } 
@@ -815,7 +821,13 @@ impl Config {
815821            build_cmd. arg ( flags) ; 
816822        } 
817823
818-         run ( & mut  build_cmd,  "cmake" ) ; 
824+         run_cmake_action ( 
825+             & build, 
826+             CMakeAction :: Build  { 
827+                 build_cmd :  & mut  build_cmd, 
828+                 conf_cmd :  & mut  conf_cmd, 
829+             } , 
830+         ) ; 
819831
820832        println ! ( "cargo:root={}" ,  dst. display( ) ) ; 
821833        return  dst; 
@@ -896,9 +908,113 @@ impl Config {
896908    } 
897909} 
898910
911+ enum  CMakeAction < ' a >  { 
912+     Configure  { 
913+         conf_cmd :  & ' a  mut  Command , 
914+     } , 
915+     Build  { 
916+         conf_cmd :  & ' a  mut  Command , 
917+         build_cmd :  & ' a  mut  Command , 
918+     } , 
919+ } 
920+ 
921+ fn  run_cmake_action ( build_dir :  & Path ,  mut  action :  CMakeAction )  { 
922+     let  program = "cmake" ; 
923+     let  cmd = match  & mut  action { 
924+         CMakeAction :: Configure  {  conf_cmd }  => conf_cmd, 
925+         CMakeAction :: Build  {  build_cmd,  .. }  => build_cmd, 
926+     } ; 
927+     let  need_rerun = match  run_and_check_if_need_reconf ( * cmd)  { 
928+         Ok ( x)  => x, 
929+         Err ( err)  => { 
930+             handle_cmake_exec_result ( Err ( err) ,  program) ; 
931+             return ; 
932+         } 
933+     } ; 
934+     if  need_rerun { 
935+         println ! ( "Looks like toolchain was changed" ) ; 
936+         //just in case some wrong value was cached 
937+         let  _ = fs:: remove_file ( & build_dir. join ( CMAKE_CACHE_FILE ) ) ; 
938+         match  action { 
939+             CMakeAction :: Configure  {  conf_cmd }  => run ( conf_cmd,  program) , 
940+             CMakeAction :: Build  { 
941+                 conf_cmd, 
942+                 build_cmd, 
943+             }  => { 
944+                 run ( conf_cmd,  program) ; 
945+                 run ( build_cmd,  program) ; 
946+             } 
947+         } 
948+     } 
949+ } 
950+ 
951+ // Acording to 
952+ // https://gitlab.kitware.com/cmake/cmake/-/issues/18959 
953+ // CMake does not support usage of the same build directory for different 
954+ // compilers. The problem is that we can not make sure that we use the same compiler 
955+ // before running of CMake without CMake's logic duplication (for example consider 
956+ // usage of CMAKE_TOOLCHAIN_FILE). Fortunately for us, CMake can detect is 
957+ // compiler changed by itself. This is done for interactive CMake's configuration, 
958+ // like ccmake/cmake-gui. But after compiler change CMake resets all cached variables. 
959+ fn  run_and_check_if_need_reconf ( cmd :  & mut  Command )  -> Result < bool ,  io:: Error >  { 
960+     println ! ( "running: {:?}" ,  cmd) ; 
961+     let  mut  child = cmd. spawn ( ) ?; 
962+     let  mut  child_stderr = child. stderr . take ( ) . expect ( "Internal error no stderr" ) ; 
963+     let  full_stderr = Arc :: new ( Mutex :: new ( Vec :: < u8 > :: with_capacity ( 1024 ) ) ) ; 
964+     let  full_stderr2 = full_stderr. clone ( ) ; 
965+     let  stderr_thread = thread:: spawn ( move  || { 
966+         let  mut  full_stderr = full_stderr2
967+             . lock ( ) 
968+             . expect ( "Internal error: Lock of stderr buffer failed" ) ; 
969+         log_and_copy_stream ( & mut  child_stderr,  & mut  io:: stderr ( ) ,  & mut  full_stderr) 
970+     } ) ; 
971+ 
972+     let  mut  child_stdout = child. stdout . take ( ) . expect ( "Internal error no stdout" ) ; 
973+     let  mut  full_stdout = Vec :: with_capacity ( 1024 ) ; 
974+     log_and_copy_stream ( & mut  child_stdout,  & mut  io:: stdout ( ) ,  & mut  full_stdout) ?; 
975+     stderr_thread
976+         . join ( ) 
977+         . expect ( "Internal stderr thread join failed" ) ?; 
978+ 
979+     static  RESET_MSG :  & [ u8 ]  = b"Configure will be re-run and you may have to reset some variables" ; 
980+     let  full_stderr = full_stderr
981+         . lock ( ) 
982+         . expect ( "Internal error stderr lock failed" ) ; 
983+     Ok ( contains ( & full_stderr,  RESET_MSG )  || contains ( & full_stdout,  RESET_MSG ) ) 
984+ } 
985+ 
899986fn  run ( cmd :  & mut  Command ,  program :  & str )  { 
900987    println ! ( "running: {:?}" ,  cmd) ; 
901-     let  status = match  cmd. status ( )  { 
988+     handle_cmake_exec_result ( cmd. status ( ) ,  program) ; 
989+ } 
990+ 
991+ fn  contains ( haystack :  & [ u8 ] ,  needle :  & [ u8 ] )  -> bool  { 
992+     haystack
993+         . windows ( needle. len ( ) ) 
994+         . any ( |window| window == needle) 
995+ } 
996+ 
997+ fn  log_and_copy_stream < R :  Read ,  W :  Write > ( 
998+     reader :  & mut  R , 
999+     writer :  & mut  W , 
1000+     log :  & mut  Vec < u8 > , 
1001+ )  -> io:: Result < ( ) >  { 
1002+     let  mut  buf = [ 0 ;  80 ] ; 
1003+     loop  { 
1004+         let  len = match  reader. read ( & mut  buf)  { 
1005+             Ok ( 0 )  => break , 
1006+             Ok ( len)  => len, 
1007+             Err ( ref  e)  if  e. kind ( )  == ErrorKind :: Interrupted  => continue , 
1008+             Err ( e)  => return  Err ( e) , 
1009+         } ; 
1010+         log. extend_from_slice ( & buf[ 0 ..len] ) ; 
1011+         writer. write_all ( & buf[ 0 ..len] ) ?; 
1012+     } 
1013+     Ok ( ( ) ) 
1014+ } 
1015+ 
1016+ fn  handle_cmake_exec_result ( r :  Result < ExitStatus ,  io:: Error > ,  program :  & str )  { 
1017+     let  status = match  r { 
9021018        Ok ( status)  => status, 
9031019        Err ( ref  e)  if  e. kind ( )  == ErrorKind :: NotFound  => { 
9041020            fail ( & format ! ( 
0 commit comments