@@ -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 {
@@ -482,6 +483,7 @@ impl Config {
482
483
// Build up the first cmake command to build the build system.
483
484
let executable = env:: var ( "CMAKE" ) . unwrap_or ( "cmake" . to_owned ( ) ) ;
484
485
let mut conf_cmd = Command :: new ( & executable) ;
486
+ conf_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
485
487
486
488
if self . verbose_cmake {
487
489
conf_cmd. arg ( "-Wdev" ) ;
@@ -738,11 +740,14 @@ impl Config {
738
740
conf_cmd. env ( k, v) ;
739
741
}
740
742
743
+ conf_cmd. env ( "CMAKE_PREFIX_PATH" , cmake_prefix_path) ;
744
+ conf_cmd. args ( & self . configure_args ) ;
741
745
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
+ } ,
746
751
) ;
747
752
} else {
748
753
println ! ( "CMake project was already configured. Skipping configuration step." ) ;
@@ -790,6 +795,7 @@ impl Config {
790
795
// And build!
791
796
let target = self . cmake_target . clone ( ) . unwrap_or ( "install" . to_string ( ) ) ;
792
797
let mut build_cmd = Command :: new ( & executable) ;
798
+ build_cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
793
799
for & ( ref k, ref v) in c_compiler. env ( ) . iter ( ) . chain ( & self . env ) {
794
800
build_cmd. env ( k, v) ;
795
801
}
@@ -815,7 +821,13 @@ impl Config {
815
821
build_cmd. arg ( flags) ;
816
822
}
817
823
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
+ ) ;
819
831
820
832
println ! ( "cargo:root={}" , dst. display( ) ) ;
821
833
return dst;
@@ -896,9 +908,113 @@ impl Config {
896
908
}
897
909
}
898
910
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
+
899
986
fn run ( cmd : & mut Command , program : & str ) {
900
987
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 {
902
1018
Ok ( status) => status,
903
1019
Err ( ref e) if e. kind ( ) == ErrorKind :: NotFound => {
904
1020
fail ( & format ! (
0 commit comments