From 83ad54e45275802a0439b2a1fa174993821f9ede Mon Sep 17 00:00:00 2001 From: Allan Blanchard <allan.blanchard@cea.fr> Date: Mon, 25 Jul 2022 11:19:45 +0200 Subject: [PATCH] [dune] add configurator --- Makefile | 29 +----- configurator.ml | 268 ++++++++++++++++++++++++++++++++++++++++++++++++ dune | 9 ++ 3 files changed, 280 insertions(+), 26 deletions(-) create mode 100644 configurator.ml diff --git a/Makefile b/Makefile index 85eb3110662..cffce70db2e 100644 --- a/Makefile +++ b/Makefile @@ -49,36 +49,16 @@ FRAMAC_HDRCK_SRC:=tools/hdrck # Frama-C Version # ################### -VERSION:=$(shell $(CAT) VERSION) -VERSION_CODENAME:=$(shell $(CAT) VERSION_CODENAME) - .PHONY: all -all: config.sed +all: dune build $(DUNE_BUILD_OPTS) @install -MAJOR_VERSION=$(shell $(SED) -E 's/^([0-9]+)\..*/\1/' VERSION) -MINOR_VERSION=$(shell $(SED) -E 's/^[0-9]+\.([0-9]+).*/\1/' VERSION) -VERSION_CODENAME=$(shell $(CAT) VERSION_CODENAME) - -# File used by dune to build src/kernel_internals/runtime/fc_config.ml -config.sed: VERSION share/Makefile.config share/Makefile.common Makefile configure.ac - @echo "# generated file" > $@ - @echo "s|@VERSION@|$(VERSION)|" >> $@ - @echo "s|@VERSION_CODENAME@|$(VERSION_CODENAME)|" >> $@ - @echo "s|@MAJOR_VERSION@|$(MAJOR_VERSION)|g" >> $@ - @echo "s|@MINOR_VERSION@|$(MINOR_VERSION)|g" >> $@ - @echo "s|@FRAMAC_DEFAULT_CPP@|$(FRAMAC_DEFAULT_CPP)|" >> $@ - @echo "s|@FRAMAC_DEFAULT_CPP_ARGS@|$(FRAMAC_DEFAULT_CPP_ARGS)|" >> $@ - @echo "s|@FRAMAC_GNU_CPP@|$(FRAMAC_GNU_CPP)|" >> $@ - @echo "s|@DEFAULT_CPP_KEEP_COMMENTS@|$(DEFAULT_CPP_KEEP_COMMENTS)|" >> $@ - @echo "s|@DEFAULT_CPP_SUPPORTED_ARCH_OPTS@|$(DEFAULT_CPP_SUPPORTED_ARCH_OPTS)|" >> $@ - clean:: purge-tests # to be done before a "dune" command dune clean dune clean --root $(FRAMAC_PTESTS_SRC) dune clean --root $(FRAMAC_HDRCK_SRC) - rm -rf _build .merlin config.sed autom4te.cache + rm -rf _build .merlin autom4te.cache ######################################################################## # Makefile.config is rebuilt whenever configure.ac is modified # @@ -153,12 +133,9 @@ FRAMAC_WTESTS:=$(FRAMAC_PTESTS_SRC)/wtests.exe # Frama-C also have ptest directories in plugins, so we do not use default PTEST_ALL_DIRS:=tests $(wildcard src/plugins/*/tests) -# Test aliasing definition allowing ./configure --disable-<plugin> +# Test aliasing definition allowing ./configure --disable-<plugin> PTEST_ALIASES:=@tests/ptests @src/plugins/ptests -# Ptests needs config.sed so that dune can build Frama-C (if it is not built) -PTEST_DEPS:=config.sed - # WP tests need WP cache PTEST_USE_WP_CACHE:=yes diff --git a/configurator.ml b/configurator.ml new file mode 100644 index 00000000000..69511aec6c7 --- /dev/null +++ b/configurator.ml @@ -0,0 +1,268 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2022 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +module C = Configurator.V1 + +module Temp = struct (* Almost copied from configurator *) + let (^/) a b = a ^ "/" ^ b + + let rec rm_rf dir = + Array.iter (fun fn -> + let fn = dir ^/ fn in + if Sys.is_directory fn then rm_rf fn else Unix.unlink fn) + (Sys.readdir dir); + Unix.rmdir dir + + let prng = lazy (Random.State.make_self_init ()) + + let gen_name ~dir ~prefix ~suffix = + let rnd = Random.State.bits (Lazy.force prng) land 0xFFFFFF in + dir ^/ Printf.sprintf "%s%06x%s" prefix rnd suffix + + let create_dir () = + let dir = + gen_name ~dir:(Filename.get_temp_dir_name ()) ~prefix:"" ~suffix:"" in + Unix.mkdir dir 0o700 ; + at_exit (fun () -> rm_rf dir) ; + dir + + let create ?(dir=create_dir ()) ?(prefix="") ?(suffix="") mk = + let rec try_name counter = + let name = gen_name ~dir ~prefix ~suffix in + match mk name with + | () -> name + | exception Unix.Unix_error _ when counter < 1000 -> try_name (counter + 1) + in + try_name 0 +end + +module C_compiler = struct (* This could be put in Dune? *) + type t = + { compiler: string + ; is_gnu: bool + } + + let find_compiler configurator = + let cc_env = try Sys.getenv "CC" with Not_found -> "" in + if cc_env <> "" then cc_env + 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 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 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 ]) + + + let preprocess_flag = "-E" + + let is_gnu configurator compiler = + let code = {|#ifndef __GNUC__ + this is not a gnuc compiler +#endif +|} + in + (call configurator compiler ["-c"] code).exit_code = 0 + + 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 _compile configurator t options = + call configurator t.compiler ("-c" :: options) +end + +(* Frama-C specific part *) + +module Cpp = struct + module GnuLike = struct + let code = {| +#define foo 0 +/* foo */ +int main(){} +|} + + let check configurator compiler = + let options = ["-dD" ; "-nostdinc"] in + (C_compiler.preprocess configurator compiler 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 + 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 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 + then Some arch else None + + let supported_archs configurator compiler archs = + let check = check configurator compiler in + List.map (fun s -> "-m" ^ s) @@ List.filter_map check archs + end + + type t = + { compiler : C_compiler.t + ; default_args : string list + ; is_gnu_like : bool + ; keep_comments : bool + ; supported_archs_opts : string list + } + + 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 supported_archs_opts = + Archs.supported_archs configurator compiler [ "16" ; "32" ; "64" ] in + { compiler; default_args; is_gnu_like; keep_comments; supported_archs_opts } + + let pp_flags fmt = + let pp_sep fmt () = Format.fprintf fmt " " in + Format.pp_print_list ~pp_sep Format.pp_print_string fmt + + let pp_default_cpp fmt cpp = + Format.fprintf fmt "%s %a" + cpp.compiler.compiler + pp_flags (C_compiler.preprocess_flag :: cpp.default_args) + + let pp_archs fmt cpp = + let pp_arch fmt arch = Format.fprintf fmt "\"%s\"" arch in + let pp_sep fmt () = Format.fprintf fmt "; " in + Format.fprintf fmt + "%a" (Format.pp_print_list ~pp_sep pp_arch) cpp.supported_archs_opts + + let pp_sed fmt cpp = + Format.fprintf fmt + "s|@FRAMAC_DEFAULT_CPP@|%a|\n" pp_default_cpp cpp ; + Format.fprintf fmt + "s|@FRAMAC_DEFAULT_CPP_ARGS@|%a|\n" pp_flags cpp.default_args ; + Format.fprintf fmt + "s|@FRAMAC_GNU_CPP@|%b|\n" cpp.is_gnu_like ; + Format.fprintf fmt + "s|@DEFAULT_CPP_KEEP_COMMENTS@|%b|\n" cpp.keep_comments ; + Format.fprintf fmt + "s|@DEFAULT_CPP_SUPPORTED_ARCH_OPTS@|%a|" pp_archs cpp +end + +module Fc_version = struct + type t = + { major: string + ; minor: string + ; ext: string + ; name: string + } + + let get configurator = + let out_VERSION = + let out = C.Process.run configurator "cat" ["VERSION"] in + if out.exit_code <> 0 then C.die "Can't read VERSION." ; + out.stdout + in + let re_version = + Str.regexp {|\([1-9][0-9]\)\.\([0-9]\)\(.*\)|} + in + let major, minor, ext = + if Str.string_match re_version out_VERSION 0 then + Str.matched_group 1 out_VERSION, + Str.matched_group 2 out_VERSION, + try Str.matched_group 3 out_VERSION with Not_found -> "" + else + C.die "Can't read VERSION." + in + let name = + let out = C.Process.run configurator "cat" ["VERSION_CODENAME"] in + if out.exit_code <> 0 then + C.die "Can't read VERSION_CODENAME." ; + String.sub out.stdout 0 (String.length out.stdout - 1) + in + { major; minor; ext; name } + + let pp_sed fmt version = + Format.fprintf fmt + "s|@VERSION@|%s.%s%s|\n" version.major version.minor version.ext ; + Format.fprintf fmt + "s|@VERSION_CODENAME@|%s|\n" version.name ; + Format.fprintf fmt + "s|@MAJOR_VERSION@|%s|\n" version.major ; + Format.fprintf fmt + "s|@MINOR_VERSION@|%s|" version.minor +end + +let python_available configurator = + let result = C.Process.run configurator "python3" ["--version"] in + if result.exit_code <> 0 then false + else + let out = result.stdout in + let re_version = + Str.regexp {|.* \([0-9]\)\.\([0-9]+\).[0-9]+|} + in + try + let maj, med = + if Str.string_match re_version out 0 then + int_of_string @@ Str.matched_group 1 out, + int_of_string @@ Str.matched_group 2 out + else raise Not_found + in + if maj <> 3 || med < 7 then raise Not_found ; + true + with Not_found | Failure _ -> false + +let configure configurator = + let version = Fc_version.get configurator in + let cpp = Cpp.get configurator in + let config_sed = open_out "config.sed" in + let fmt = Format.formatter_of_out_channel config_sed in + Format.fprintf fmt "%a\n%a\n" Fc_version.pp_sed version Cpp.pp_sed cpp ; + close_out config_sed ; + let python = open_out "python-3.7-available" in + Printf.fprintf python "%b" (python_available configurator) ; + close_out python + +let () = + C.main ~name:"frama_c_config" configure diff --git a/dune b/dune index 0f46950b014..88f9131e447 100644 --- a/dune +++ b/dune @@ -22,3 +22,12 @@ (alias (name default) (deps (alias install))) (dirs src tools tests headers share bin) + +(executable + (name configurator) + (libraries dune-configurator str)) + +(rule + (deps VERSION VERSION_CODENAME) + (targets config.sed python-3.7-available) + (action (run ./configurator.exe))) -- GitLab