@@ -50,10 +50,11 @@ use std::collections::HashMap;
5050use  std:: env; 
5151use  std:: ffi:: { OsStr ,  OsString } ; 
5252use  std:: fs:: { self ,  File } ; 
53- use  std:: io:: prelude:: * ; 
54- use  std:: io:: ErrorKind ; 
53+ use  std:: io:: { self ,  prelude:: * ,  ErrorKind } ; 
5554use  std:: path:: { Path ,  PathBuf } ; 
56- use  std:: process:: Command ; 
55+ use  std:: process:: { Command ,  ExitStatus ,  Stdio } ; 
56+ use  std:: sync:: { Arc ,  Mutex } ; 
57+ use  std:: thread:: { self } ; 
5758
5859/// Builder style configuration for a pending CMake build. 
5960pub  struct  Config  { 
@@ -518,6 +519,7 @@ impl Config {
518519            . getenv_target_os ( "CMAKE" ) 
519520            . unwrap_or ( OsString :: from ( "cmake" ) ) ; 
520521        let  mut  conf_cmd = Command :: new ( & executable) ; 
522+         conf_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ; 
521523
522524        if  self . verbose_cmake  { 
523525            conf_cmd. arg ( "-Wdev" ) ; 
@@ -779,11 +781,14 @@ impl Config {
779781            conf_cmd. env ( k,  v) ; 
780782        } 
781783
784+         conf_cmd. env ( "CMAKE_PREFIX_PATH" ,  cmake_prefix_path) ; 
785+         conf_cmd. args ( & self . configure_args ) ; 
782786        if  self . always_configure  || !build. join ( CMAKE_CACHE_FILE ) . exists ( )  { 
783-             conf_cmd. args ( & self . configure_args ) ; 
784-             run ( 
785-                 conf_cmd. env ( "CMAKE_PREFIX_PATH" ,  cmake_prefix_path) , 
786-                 "cmake" , 
787+             run_cmake_action ( 
788+                 & build, 
789+                 CMakeAction :: Configure  { 
790+                     conf_cmd :  & mut  conf_cmd, 
791+                 } , 
787792            ) ; 
788793        }  else  { 
789794            println ! ( "CMake project was already configured. Skipping configuration step." ) ; 
@@ -793,6 +798,7 @@ impl Config {
793798        let  target = self . cmake_target . clone ( ) . unwrap_or ( "install" . to_string ( ) ) ; 
794799        let  mut  build_cmd = Command :: new ( & executable) ; 
795800        build_cmd. current_dir ( & build) ; 
801+         build_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ; 
796802
797803        for  & ( ref  k,  ref  v)  in  c_compiler. env ( ) . iter ( ) . chain ( & self . env )  { 
798804            build_cmd. env ( k,  v) ; 
@@ -836,7 +842,13 @@ impl Config {
836842            build_cmd. arg ( "--" ) . args ( & self . build_args ) ; 
837843        } 
838844
839-         run ( & mut  build_cmd,  "cmake" ) ; 
845+         run_cmake_action ( 
846+             & build, 
847+             CMakeAction :: Build  { 
848+                 build_cmd :  & mut  build_cmd, 
849+                 conf_cmd :  & mut  conf_cmd, 
850+             } , 
851+         ) ; 
840852
841853        println ! ( "cargo:root={}" ,  dst. display( ) ) ; 
842854        return  dst; 
@@ -944,9 +956,118 @@ impl Config {
944956    } 
945957} 
946958
959+ enum  CMakeAction < ' a >  { 
960+     Configure  { 
961+         conf_cmd :  & ' a  mut  Command , 
962+     } , 
963+     Build  { 
964+         conf_cmd :  & ' a  mut  Command , 
965+         build_cmd :  & ' a  mut  Command , 
966+     } , 
967+ } 
968+ 
969+ fn  run_cmake_action ( build_dir :  & Path ,  mut  action :  CMakeAction )  { 
970+     let  program = "cmake" ; 
971+     let  cmd = match  & mut  action { 
972+         CMakeAction :: Configure  {  conf_cmd }  => conf_cmd, 
973+         CMakeAction :: Build  {  build_cmd,  .. }  => build_cmd, 
974+     } ; 
975+     let  need_rerun = match  run_and_check_if_need_reconf ( * cmd,  program)  { 
976+         Ok ( x)  => x, 
977+         Err ( err)  => { 
978+             handle_cmake_exec_result ( Err ( err) ,  program) ; 
979+             return ; 
980+         } 
981+     } ; 
982+     if  need_rerun { 
983+         println ! ( "Looks like toolchain was changed" ) ; 
984+         //just in case some wrong value was cached 
985+         let  _ = fs:: remove_file ( & build_dir. join ( CMAKE_CACHE_FILE ) ) ; 
986+         match  action { 
987+             CMakeAction :: Configure  {  conf_cmd }  => run ( conf_cmd,  program) , 
988+             CMakeAction :: Build  { 
989+                 conf_cmd, 
990+                 build_cmd, 
991+             }  => { 
992+                 run ( conf_cmd,  program) ; 
993+                 run ( build_cmd,  program) ; 
994+             } 
995+         } 
996+     } 
997+ } 
998+ 
999+ // Acording to 
1000+ // https://gitlab.kitware.com/cmake/cmake/-/issues/18959 
1001+ // CMake does not support usage of the same build directory for different 
1002+ // compilers. The problem is that we can not make sure that we use the same compiler 
1003+ // before running of CMake without CMake's logic duplication (for example consider 
1004+ // usage of CMAKE_TOOLCHAIN_FILE). Fortunately for us, CMake can detect is 
1005+ // compiler changed by itself. This is done for interactive CMake's configuration, 
1006+ // like ccmake/cmake-gui. But after compiler change CMake resets all cached variables. 
1007+ fn  run_and_check_if_need_reconf ( cmd :  & mut  Command ,  program :  & str )  -> Result < bool ,  io:: Error >  { 
1008+     println ! ( "running: {:?}" ,  cmd) ; 
1009+     let  mut  child = cmd. spawn ( ) ?; 
1010+     let  mut  child_stderr = child. stderr . take ( ) . expect ( "Internal error no stderr" ) ; 
1011+     let  full_stderr = Arc :: new ( Mutex :: new ( Vec :: < u8 > :: with_capacity ( 1024 ) ) ) ; 
1012+     let  full_stderr2 = full_stderr. clone ( ) ; 
1013+     let  stderr_thread = thread:: spawn ( move  || { 
1014+         let  mut  full_stderr = full_stderr2
1015+             . lock ( ) 
1016+             . expect ( "Internal error: Lock of stderr buffer failed" ) ; 
1017+         log_and_copy_stream ( & mut  child_stderr,  & mut  io:: stderr ( ) ,  & mut  full_stderr) 
1018+     } ) ; 
1019+ 
1020+     let  mut  child_stdout = child. stdout . take ( ) . expect ( "Internal error no stdout" ) ; 
1021+     let  mut  full_stdout = Vec :: with_capacity ( 1024 ) ; 
1022+     log_and_copy_stream ( & mut  child_stdout,  & mut  io:: stdout ( ) ,  & mut  full_stdout) ?; 
1023+     stderr_thread
1024+         . join ( ) 
1025+         . expect ( "Internal stderr thread join failed" ) ?; 
1026+ 
1027+     static  RESET_MSG :  & [ u8 ]  = b"Configure will be re-run and you may have to reset some variables" ; 
1028+     let  full_stderr = full_stderr
1029+         . lock ( ) 
1030+         . expect ( "Internal error stderr lock failed" ) ; 
1031+     if  contains ( & full_stderr,  RESET_MSG )  || contains ( & full_stdout,  RESET_MSG )  { 
1032+         return  Ok ( true ) ; 
1033+     }  else  { 
1034+         handle_cmake_exec_result ( child. wait ( ) ,  program) ; 
1035+         return  Ok ( false ) ; 
1036+     } 
1037+ } 
1038+ 
9471039fn  run ( cmd :  & mut  Command ,  program :  & str )  { 
9481040    println ! ( "running: {:?}" ,  cmd) ; 
949-     let  status = match  cmd. status ( )  { 
1041+     handle_cmake_exec_result ( cmd. status ( ) ,  program) ; 
1042+ } 
1043+ 
1044+ fn  contains ( haystack :  & [ u8 ] ,  needle :  & [ u8 ] )  -> bool  { 
1045+     haystack
1046+         . windows ( needle. len ( ) ) 
1047+         . any ( |window| window == needle) 
1048+ } 
1049+ 
1050+ fn  log_and_copy_stream < R :  Read ,  W :  Write > ( 
1051+     reader :  & mut  R , 
1052+     writer :  & mut  W , 
1053+     log :  & mut  Vec < u8 > , 
1054+ )  -> io:: Result < ( ) >  { 
1055+     let  mut  buf = [ 0 ;  80 ] ; 
1056+     loop  { 
1057+         let  len = match  reader. read ( & mut  buf)  { 
1058+             Ok ( 0 )  => break , 
1059+             Ok ( len)  => len, 
1060+             Err ( ref  e)  if  e. kind ( )  == ErrorKind :: Interrupted  => continue , 
1061+             Err ( e)  => return  Err ( e) , 
1062+         } ; 
1063+         log. extend_from_slice ( & buf[ 0 ..len] ) ; 
1064+         writer. write_all ( & buf[ 0 ..len] ) ?; 
1065+     } 
1066+     Ok ( ( ) ) 
1067+ } 
1068+ 
1069+ fn  handle_cmake_exec_result ( r :  Result < ExitStatus ,  io:: Error > ,  program :  & str )  { 
1070+     let  status = match  r { 
9501071        Ok ( status)  => status, 
9511072        Err ( ref  e)  if  e. kind ( )  == ErrorKind :: NotFound  => { 
9521073            fail ( & format ! ( 
0 commit comments