diff --git a/configurator.ml b/configurator.ml index 82db7c709f0569284c40af9b90851a595a451383..4bf225310fb71852fd541c89f62742c9065f17fc 100644 --- a/configurator.ml +++ b/configurator.ml @@ -55,52 +55,69 @@ module Temp = struct (* Almost copied from configurator *) try_name 0 end -module C_compiler = struct (* This could be put in Dune? *) +module C_preprocessor = struct (* This could be put in Dune? *) type t = - { compiler: string + { preprocessor: string + ; pp_opt: string option ; is_gnu: bool } - let find_compiler configurator = - let cc_env = try Sys.getenv "CC" with Not_found -> "" in - if cc_env <> "" then cc_env + let stdout_contains out str = + let re = Str.regexp_string str in + try ignore (Str.search_forward re out 0); true + with Not_found -> false + + let find_preprocessor configurator = + let cc_env = try Sys.getenv "CPP" with Not_found -> "" in + if cc_env <> "" then (cc_env, None) (* assume default CPP needs no args *) else - let finder compiler = C.which configurator compiler |> Option.is_some in - try List.find finder [ "gcc"; "cc"; "cl.exe" ] - with Not_found -> C.die "Could not find a C compiler" + let finder (command, _pp_opt) = + C.which configurator command |> Option.is_some + in + (* Note: We could add 'cl.exe' to the list, but since it requires + '/<opt>' and not '-<opt>' for its options, it will fail in every + check anyway. So the user may manually specify it if they want it, + but having it here brings no benefit. + Note: 'cpp' is NOT the POSIX way to call the preprocessor, and it + behaves VERY badly on macOS (as if using '-traditional', see + https://stackoverflow.com/questions/9508159). + Therefore, we try `gcc -E` and `cc -E`, but not 'cpp'. + *) + try List.find finder [("gcc", Some "-E"); ("cc", Some "-E")] + with Not_found -> C.die "Could not find a C preprocessor" let write_file name code = let out = open_out name in Printf.fprintf out "%s" code ; close_out out - let call configurator compiler options code = + let call configurator preprocessor options code = let dir = Temp.create_dir () in let file = Temp.create ~dir ~suffix:".c" (fun name -> write_file name code) in - C.Process.run configurator ~dir compiler (options @ [ file ]) - + C.Process.run configurator ~dir preprocessor (options @ [ file ]) - let preprocess_flag = "-E" - - let is_gnu configurator compiler = - let code = {|#ifndef __GNUC__ - this is not a gnuc compiler + let is_gnu configurator preprocessor = + let code = {|#ifdef _FC_UNDEFINED_SYMBOL +#error This should not remain after preprocessing #endif +int kept_after_preprocessing = 42; |} in - (call configurator compiler ["-c"] code).exit_code = 0 + (* GNU preprocessors are always compatible with '-E'. + For 'cpp', the '-E' flag is unnecessary, but still works. *) + let result = call configurator preprocessor ["-E"] code in + result.exit_code = 0 && + stdout_contains result.stdout "kept_after_preprocessing" && + not (stdout_contains result.stdout "should not remain") let get configurator = - let compiler = find_compiler configurator in - let is_gnu = is_gnu configurator compiler in - { compiler ; is_gnu } - - let preprocess configurator t options = - call configurator t.compiler (preprocess_flag :: options) + let preprocessor, pp_opt = find_preprocessor configurator in + let is_gnu = is_gnu configurator preprocessor in + { preprocessor ; pp_opt; is_gnu } - let _compile configurator t options = - call configurator t.compiler ("-c" :: options) + let preprocess configurator t options code = + call configurator t.preprocessor (Option.to_list t.pp_opt @ options) code end (* Frama-C specific part *) @@ -113,40 +130,39 @@ module Cpp = struct int main(){} |} - let check configurator compiler = + let check configurator preprocessor = let options = ["-dD" ; "-nostdinc"] in - (C_compiler.preprocess configurator compiler options code).exit_code = 0 + (C_preprocessor.preprocess configurator preprocessor options code).exit_code = 0 end module KeepComments = struct let code = {|/* Check whether comments are kept in output */|} - let check configurator compiler options = - let result = C_compiler.preprocess configurator compiler options code in - result.exit_code = 0 && - let re = Str.regexp_string "kept" in - try ignore (Str.search_forward re result.stdout 0); true - with Not_found -> false + let keep_comments_option = "-C" + + let check configurator preprocessor = + let result = C_preprocessor.preprocess configurator preprocessor [keep_comments_option] code in + result.exit_code = 0 && C_preprocessor.stdout_contains result.stdout "kept" end module Archs = struct let opt_m_code value = Format.asprintf {|/* Check if preprocessor supports option -m%s */|} value - let check configurator compiler arch = + let check configurator preprocessor arch = let code = opt_m_code arch in let options = [ Format.asprintf "-m%s" arch ] in - if (C_compiler.preprocess configurator compiler options code).exit_code = 0 + if (C_preprocessor.preprocess configurator preprocessor options code).exit_code = 0 then Some arch else None - let supported_archs configurator compiler archs = - let check = check configurator compiler in + let supported_archs configurator preprocessor archs = + let check = check configurator preprocessor in List.map (fun s -> "-m" ^ s) @@ List.filter_map check archs end type t = - { compiler : C_compiler.t + { preprocessor : C_preprocessor.t ; default_args : string list ; is_gnu_like : bool ; keep_comments : bool @@ -154,13 +170,13 @@ int main(){} } let get configurator = - let compiler = C_compiler.get configurator in - let default_args = [ "-C" ; "-I." ] in - let is_gnu_like = GnuLike.check configurator compiler in - let keep_comments = KeepComments.check configurator compiler [ "-C" ] in + let preprocessor = C_preprocessor.get configurator in + let default_args = Option.to_list preprocessor.pp_opt @ [ "-C" ; "-I." ] in + let is_gnu_like = GnuLike.check configurator preprocessor in + let keep_comments = KeepComments.check configurator preprocessor in let supported_archs_opts = - Archs.supported_archs configurator compiler [ "16" ; "32" ; "64" ] in - { compiler; default_args; is_gnu_like; keep_comments; supported_archs_opts } + Archs.supported_archs configurator preprocessor [ "16" ; "32" ; "64" ] in + { preprocessor; default_args; is_gnu_like; keep_comments; supported_archs_opts } let pp_flags fmt = let pp_sep fmt () = Format.fprintf fmt " " in @@ -168,8 +184,8 @@ int main(){} let pp_default_cpp fmt cpp = Format.fprintf fmt "%s %a" - cpp.compiler.compiler - pp_flags (C_compiler.preprocess_flag :: cpp.default_args) + cpp.preprocessor.preprocessor + pp_flags cpp.default_args let pp_archs fmt cpp = let pp_arch fmt arch = Format.fprintf fmt "\"%s\"" arch in diff --git a/src/kernel_internals/runtime/fc_config.ml.in b/src/kernel_internals/runtime/fc_config.ml.in index 6ac18e7a8e3d0617c4d11126c594cf2eead2973c..790125b2c8241464fb6008b05bff228637308511 100644 --- a/src/kernel_internals/runtime/fc_config.ml.in +++ b/src/kernel_internals/runtime/fc_config.ml.in @@ -63,7 +63,13 @@ let preprocessor = env_or_default (fun x -> x) default_cpp let using_default_cpp = env_or_default (fun _ -> false) true let preprocessor_is_gnu_like = - env_or_default (fun _ -> false) @FRAMAC_GNU_CPP@ + env_or_default + (fun _ -> + (* be more lenient when trying to determine if the preprocessor + is gnu-like: in Cygwin, for instance, CC is "<prefix>-gcc" but + CPP is "<prefix>-cpp", so this extra test allows proper detection. *) + let env = Sys.getenv "CC" ^ default_cpp_args in + env=default_cpp) @FRAMAC_GNU_CPP@ let preprocessor_supported_arch_options = [@DEFAULT_CPP_SUPPORTED_ARCH_OPTS@]