@@ -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 {
@@ -386,6 +387,7 @@ impl Config {
386387 // Build up the first cmake command to build the build system.
387388 let executable = env:: var ( "CMAKE" ) . unwrap_or ( "cmake" . to_owned ( ) ) ;
388389 let mut conf_cmd = Command :: new ( & executable) ;
390+ conf_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
389391
390392 if self . verbose_cmake {
391393 conf_cmd. arg ( "-Wdev" ) ;
@@ -698,11 +700,9 @@ impl Config {
698700 conf_cmd. env ( k, v) ;
699701 }
700702
703+ conf_cmd. env ( "CMAKE_PREFIX_PATH" , cmake_prefix_path) ;
701704 if self . always_configure || !build. join ( CMAKE_CACHE_FILE ) . exists ( ) {
702- run (
703- conf_cmd. env ( "CMAKE_PREFIX_PATH" , cmake_prefix_path) ,
704- "cmake" ,
705- ) ;
705+ run_configure ( & build, & mut conf_cmd) ;
706706 } else {
707707 println ! ( "CMake project was already configured. Skipping configuration step." ) ;
708708 }
@@ -749,6 +749,7 @@ impl Config {
749749 // And build!
750750 let target = self . cmake_target . clone ( ) . unwrap_or ( "install" . to_string ( ) ) ;
751751 let mut build_cmd = Command :: new ( & executable) ;
752+ build_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
752753 for & ( ref k, ref v) in c_compiler. env ( ) . iter ( ) . chain ( & self . env ) {
753754 build_cmd. env ( k, v) ;
754755 }
@@ -774,7 +775,7 @@ impl Config {
774775 build_cmd. arg ( flags) ;
775776 }
776777
777- run ( & mut build_cmd, "cmake" ) ;
778+ run_build ( & build , & mut build_cmd, & mut conf_cmd ) ;
778779
779780 println ! ( "cargo:root={}" , dst. display( ) ) ;
780781 return dst;
@@ -855,9 +856,108 @@ impl Config {
855856 }
856857}
857858
859+ fn run_configure ( build_dir : & Path , conf_cmd : & mut Command ) {
860+ let program = "cmake" ;
861+ let need_rerun = match run_and_check_if_need_reconf ( conf_cmd) {
862+ Ok ( x) => x,
863+ Err ( err) => {
864+ handle_cmake_exec_result ( Err ( err) , program) ;
865+ return ;
866+ }
867+ } ;
868+ if need_rerun {
869+ println ! ( "Looks like toolchain was changed" ) ;
870+ //just in case some wrong value was cached
871+ let _ = fs:: remove_file ( & build_dir. join ( CMAKE_CACHE_FILE ) ) ;
872+ run ( conf_cmd, program) ;
873+ }
874+ }
875+ fn run_build ( build_dir : & Path , build_cmd : & mut Command , conf_cmd : & mut Command ) {
876+ let program = "cmake" ;
877+ let need_rerun = match run_and_check_if_need_reconf ( build_cmd) {
878+ Ok ( x) => x,
879+ Err ( err) => {
880+ handle_cmake_exec_result ( Err ( err) , program) ;
881+ return ;
882+ }
883+ } ;
884+ if need_rerun {
885+ println ! ( "Looks like toolchain was changed" ) ;
886+ //just in case some wrong value was cached
887+ let _ = fs:: remove_file ( & build_dir. join ( CMAKE_CACHE_FILE ) ) ;
888+ run ( conf_cmd, program) ;
889+ run ( build_cmd, program) ;
890+ }
891+ }
892+
893+ // Acording to
894+ // https://gitlab.kitware.com/cmake/cmake/-/issues/18959
895+ // CMake does not support usage of the same build directory for different
896+ // compilers. The problem is that we can not make sure that we use the same compiler
897+ // before running of CMake without CMake's logic duplication (for example consider
898+ // usage of CMAKE_TOOLCHAIN_FILE). Fortunately for us, CMake can detect is
899+ // compiler changed by itself. This is done for interactive CMake's configuration,
900+ // like ccmake/cmake-gui. But after compiler change CMake resets all cached variables.
901+ // So
902+ fn run_and_check_if_need_reconf ( cmd : & mut Command ) -> Result < bool , io:: Error > {
903+ println ! ( "running: {:?}" , cmd) ;
904+ let mut child = cmd. spawn ( ) ?;
905+ let mut child_stderr = child. stderr . take ( ) . expect ( "Internal error no stderr" ) ;
906+ let full_stderr = Arc :: new ( Mutex :: new ( Vec :: < u8 > :: with_capacity ( 1024 ) ) ) ;
907+ let full_stderr2 = full_stderr. clone ( ) ;
908+ let stderr_thread = thread:: spawn ( move || {
909+ let mut full_stderr = full_stderr2
910+ . lock ( )
911+ . expect ( "Internal error: Lock of stderr buffer failed" ) ;
912+ log_and_copy_stream ( & mut child_stderr, & mut io:: stderr ( ) , & mut full_stderr)
913+ } ) ;
914+
915+ let mut child_stdout = child. stdout . take ( ) . expect ( "Internal error no stdout" ) ;
916+ let mut full_stdout = Vec :: with_capacity ( 1024 ) ;
917+ log_and_copy_stream ( & mut child_stdout, & mut io:: stdout ( ) , & mut full_stdout) ?;
918+ stderr_thread
919+ . join ( )
920+ . expect ( "Internal stderr thread join failed" ) ?;
921+
922+ static RESET_MSG : & [ u8 ] = b"Configure will be re-run and you may have to reset some variables" ;
923+ let full_stderr = full_stderr
924+ . lock ( )
925+ . expect ( "Internal error stderr lock failed" ) ;
926+ Ok ( contains ( & full_stderr, RESET_MSG ) || contains ( & full_stdout, RESET_MSG ) )
927+ }
928+
858929fn run ( cmd : & mut Command , program : & str ) {
859930 println ! ( "running: {:?}" , cmd) ;
860- let status = match cmd. status ( ) {
931+ handle_cmake_exec_result ( cmd. status ( ) , program) ;
932+ }
933+
934+ fn contains ( haystack : & [ u8 ] , needle : & [ u8 ] ) -> bool {
935+ haystack
936+ . windows ( needle. len ( ) )
937+ . any ( |window| window == needle)
938+ }
939+
940+ fn log_and_copy_stream < R : Read , W : Write > (
941+ reader : & mut R ,
942+ writer : & mut W ,
943+ log : & mut Vec < u8 > ,
944+ ) -> io:: Result < ( ) > {
945+ let mut buf = [ 0 ; 80 ] ;
946+ loop {
947+ let len = match reader. read ( & mut buf) {
948+ Ok ( 0 ) => break ,
949+ Ok ( len) => len,
950+ Err ( ref e) if e. kind ( ) == ErrorKind :: Interrupted => continue ,
951+ Err ( e) => return Err ( e) ,
952+ } ;
953+ log. extend_from_slice ( & buf[ 0 ..len] ) ;
954+ writer. write_all ( & buf[ 0 ..len] ) ?;
955+ }
956+ Ok ( ( ) )
957+ }
958+
959+ fn handle_cmake_exec_result ( r : Result < ExitStatus , io:: Error > , program : & str ) {
960+ let status = match r {
861961 Ok ( status) => status,
862962 Err ( ref e) if e. kind ( ) == ErrorKind :: NotFound => {
863963 fail ( & format ! (
0 commit comments