@@ -49,10 +49,11 @@ extern crate cc;
49
49
use std:: env;
50
50
use std:: ffi:: { OsStr , OsString } ;
51
51
use std:: fs:: { self , File } ;
52
- use std:: io:: prelude:: * ;
53
- use std:: io:: ErrorKind ;
52
+ use std:: io:: { self , prelude:: * , ErrorKind } ;
54
53
use 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 } ;
56
57
57
58
/// Builder style configuration for a pending CMake build.
58
59
pub struct Config {
@@ -386,6 +387,7 @@ impl Config {
386
387
// Build up the first cmake command to build the build system.
387
388
let executable = env:: var ( "CMAKE" ) . unwrap_or ( "cmake" . to_owned ( ) ) ;
388
389
let mut conf_cmd = Command :: new ( & executable) ;
390
+ conf_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
389
391
390
392
if self . verbose_cmake {
391
393
conf_cmd. arg ( "-Wdev" ) ;
@@ -698,11 +700,9 @@ impl Config {
698
700
conf_cmd. env ( k, v) ;
699
701
}
700
702
703
+ conf_cmd. env ( "CMAKE_PREFIX_PATH" , cmake_prefix_path) ;
701
704
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) ;
706
706
} else {
707
707
println ! ( "CMake project was already configured. Skipping configuration step." ) ;
708
708
}
@@ -749,6 +749,7 @@ impl Config {
749
749
// And build!
750
750
let target = self . cmake_target . clone ( ) . unwrap_or ( "install" . to_string ( ) ) ;
751
751
let mut build_cmd = Command :: new ( & executable) ;
752
+ build_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
752
753
for & ( ref k, ref v) in c_compiler. env ( ) . iter ( ) . chain ( & self . env ) {
753
754
build_cmd. env ( k, v) ;
754
755
}
@@ -774,7 +775,7 @@ impl Config {
774
775
build_cmd. arg ( flags) ;
775
776
}
776
777
777
- run ( & mut build_cmd, "cmake" ) ;
778
+ run_build ( & build , & mut build_cmd, & mut conf_cmd ) ;
778
779
779
780
println ! ( "cargo:root={}" , dst. display( ) ) ;
780
781
return dst;
@@ -855,9 +856,108 @@ impl Config {
855
856
}
856
857
}
857
858
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
+
858
929
fn run ( cmd : & mut Command , program : & str ) {
859
930
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 {
861
961
Ok ( status) => status,
862
962
Err ( ref e) if e. kind ( ) == ErrorKind :: NotFound => {
863
963
fail ( & format ! (
0 commit comments