From 80c4b67b2c82d8a8dbacd3e2ec16841ec1a63a00 Mon Sep 17 00:00:00 2001 From: Allan Blanchard <allan.blanchard@cea.fr> Date: Mon, 1 Jul 2024 09:58:30 +0200 Subject: [PATCH] [kernel] add user directories --- configurator.ml | 25 ++++- src/kernel_internals/runtime/macos_dirs.ml | 17 ++++ .../runtime/system_config.ml.in | 4 + .../runtime/system_config.mli | 12 +++ src/kernel_internals/runtime/unix_dirs.ml | 20 ++++ src/kernel_internals/runtime/win_dirs.ml | 13 +++ .../plugin_entry_points/kernel.ml | 32 ++++++- .../plugin_entry_points/kernel.mli | 10 ++ .../plugin_entry_points/plugin.ml | 93 +++++++++++++------ .../plugin_entry_points/plugin.mli | 34 ++++++- src/plugins/gui/gtk_helper.ml | 2 +- src/plugins/gui/gui_parameters.ml | 2 + src/plugins/gui/gui_parameters.mli | 2 + 13 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 src/kernel_internals/runtime/macos_dirs.ml create mode 100644 src/kernel_internals/runtime/unix_dirs.ml create mode 100644 src/kernel_internals/runtime/win_dirs.ml diff --git a/configurator.ml b/configurator.ml index 5e4c11ec982..87bb3e182bb 100644 --- a/configurator.ml +++ b/configurator.ml @@ -234,6 +234,27 @@ module Fc_version = struct "s|@MINOR_VERSION@|%s|" version.minor end +module OS = struct + type t = Windows | MacOS | OtherUnix + + let get configurator = + match C.ocaml_config_var_exn configurator "os_type" with + | "Win32" -> Windows + | _ -> + match C.ocaml_config_var_exn configurator "system" with + | "macosx" -> MacOS + | _ -> OtherUnix + + let pp_sed fmt t = + let module_dirs = match t with + | Windows -> "Win_dirs" + | MacOS -> "Macos_dirs" + | OtherUnix -> "Unix_dirs" + in + Format.fprintf fmt + "s|@FRAMAC_TARGET_SYSTEM_CONFIG_INCLUDE@|%s|" module_dirs +end + let python_available configurator = let result = C.Process.run configurator "python3" ["--version"] in if result.exit_code <> 0 then false @@ -256,9 +277,11 @@ let python_available configurator = let configure configurator = let version = Fc_version.get configurator in let cpp = Cpp.get configurator in + let os = OS.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 ; + Format.fprintf fmt "%a\n%a\n%a\n" + Fc_version.pp_sed version Cpp.pp_sed cpp OS.pp_sed os ; close_out config_sed ; let python = open_out "python-3.7-available" in Printf.fprintf python "%b" (python_available configurator) ; diff --git a/src/kernel_internals/runtime/macos_dirs.ml b/src/kernel_internals/runtime/macos_dirs.ml new file mode 100644 index 00000000000..042dc2beb92 --- /dev/null +++ b/src/kernel_internals/runtime/macos_dirs.ml @@ -0,0 +1,17 @@ +let home () = + match Sys.getenv "HOME" with + | "" -> raise Not_found + | s -> Filepath.Normalized.of_string s + +let env_or_default env default = + let open Filepath.Normalized in + match Sys.getenv_opt env with + | Some s when s <> "" -> concat (of_string s) "frama-c" + | _ -> concats (home ()) default + +let cache () = + env_or_default "XDG_CACHE_HOME" [ "Library" ; "Caches" ; "frama-c"] +let config () = + env_or_default "XDG_CONFIG_HOME" [ "Application Support" ; "frama-c" ; "config" ] +let state () = + env_or_default "XDG_STATE_HOME" [ "Application Support" ; "frama-c" ; "state" ] diff --git a/src/kernel_internals/runtime/system_config.ml.in b/src/kernel_internals/runtime/system_config.ml.in index fbf4944d659..028fd90f61d 100644 --- a/src/kernel_internals/runtime/system_config.ml.in +++ b/src/kernel_internals/runtime/system_config.ml.in @@ -91,3 +91,7 @@ module Preprocessor = struct let supported_arch_options = [@DEFAULT_CPP_SUPPORTED_ARCH_OPTS@] end + +module User_dirs = struct + include @FRAMAC_TARGET_SYSTEM_CONFIG_INCLUDE@ +end diff --git a/src/kernel_internals/runtime/system_config.mli b/src/kernel_internals/runtime/system_config.mli index ccceae39226..b0eb095867a 100644 --- a/src/kernel_internals/runtime/system_config.mli +++ b/src/kernel_internals/runtime/system_config.mli @@ -112,6 +112,18 @@ module Preprocessor : sig *) end +(** Default user directories *) +module User_dirs : sig + val cache: unit -> Filepath.Normalized.t + (** Where Frama-C should read/write cached files. *) + + val config: unit -> Filepath.Normalized.t + (** Where Frama-C should read/write config files. *) + + val state: unit -> Filepath.Normalized.t + (** Where Frama-C should read/write state files *) +end + val is_gui: bool (** Is the Frama-C GUI running? *) diff --git a/src/kernel_internals/runtime/unix_dirs.ml b/src/kernel_internals/runtime/unix_dirs.ml new file mode 100644 index 00000000000..3ac63d6ef4f --- /dev/null +++ b/src/kernel_internals/runtime/unix_dirs.ml @@ -0,0 +1,20 @@ +let home () = + match Sys.getenv "HOME" with + | "" -> raise Not_found + | s -> Filepath.Normalized.of_string s + +let env_or_default env default = + let open Filepath.Normalized in + let location = + match Sys.getenv_opt env with + | Some env when env <> "" -> of_string env + | _ -> concats (home ()) default + in + concat location "frama-c" + +let cache () = + env_or_default "XDG_CACHE_HOME" [ ".cache" ] +let config () = + env_or_default "XDG_CONFIG_HOME" [ ".config" ] +let state () = + env_or_default "XDG_STATE_HOME" [ ".local" ; "state" ] diff --git a/src/kernel_internals/runtime/win_dirs.ml b/src/kernel_internals/runtime/win_dirs.ml new file mode 100644 index 00000000000..a8bf5c510e5 --- /dev/null +++ b/src/kernel_internals/runtime/win_dirs.ml @@ -0,0 +1,13 @@ +let env_or_default env default sub = + let open Filepath.Normalized in + match Sys.getenv_opt env, sub with + | Some s, _ (* ignored *) when s <> "" -> concat (of_string s) "frama-c" + | _, Some sub -> concats (of_string default) [ "frama-c" ; sub ] + | _, None -> concat (of_string default) "frama-c" + +let cache () = + env_or_default "XDG_CACHE_HOME" (Sys.getenv "TEMP") None +let config () = + env_or_default "XDG_CONFIG_HOME" (Sys.getenv "LOCALAPPDATA") (Some "config") +let state () = + env_or_default "XDG_STATE_HOME" (Sys.getenv "LOCALAPPDATA") (Some "state") diff --git a/src/kernel_services/plugin_entry_points/kernel.ml b/src/kernel_services/plugin_entry_points/kernel.ml index 11f898a7554..50962dcf303 100644 --- a/src/kernel_services/plugin_entry_points/kernel.ml +++ b/src/kernel_services/plugin_entry_points/kernel.ml @@ -973,6 +973,21 @@ module Session_dir = let () = Plugin.session_is_set_ref := Session_dir.is_set let () = Plugin.session_ref := Session_dir.get +let () = Parameter_customize.set_cmdline_stage Cmdline.Extending +let () = Parameter_customize.set_group saveload +let () = Parameter_customize.do_not_projectify () +module Cache_dir = + P.Filepath + (struct + let option_name = "-cache" + let arg_name = "path" + let existence = Filepath.Indifferent + let file_kind = "directory" + let help = "directory in which cache files are stored" + end) +let () = Plugin.cache_is_set_ref := Cache_dir.is_set +let () = Plugin.cache_ref := Cache_dir.get + let () = Parameter_customize.set_cmdline_stage Cmdline.Extending let () = Parameter_customize.set_group saveload let () = Parameter_customize.do_not_projectify () @@ -983,11 +998,26 @@ module Config_dir = let arg_name = "path" let existence = Filepath.Indifferent let file_kind = "directory" - let help = "directory in which configuration files are searched" + let help = "directory in which configuration files are stored" end) let () = Plugin.config_is_set_ref := Config_dir.is_set let () = Plugin.config_ref := Config_dir.get +let () = Parameter_customize.set_cmdline_stage Cmdline.Extending +let () = Parameter_customize.set_group saveload +let () = Parameter_customize.do_not_projectify () +module State_dir = + P.Filepath + (struct + let option_name = "-state" + let arg_name = "path" + let existence = Filepath.Indifferent + let file_kind = "directory" + let help = "directory in which state files are stored" + end) +let () = Plugin.state_is_set_ref := State_dir.is_set +let () = Plugin.state_ref := State_dir.get + (* ************************************************************************* *) (** {2 Parsing} *) (* ************************************************************************* *) diff --git a/src/kernel_services/plugin_entry_points/kernel.mli b/src/kernel_services/plugin_entry_points/kernel.mli index 13e35743a12..861c22d2901 100644 --- a/src/kernel_services/plugin_entry_points/kernel.mli +++ b/src/kernel_services/plugin_entry_points/kernel.mli @@ -418,12 +418,22 @@ module Session_dir: Parameter_sig.Filepath @before 23.0-Vanadium parameter type was string instead of Filepath. *) +module Cache_dir: Parameter_sig.Filepath +(** Directory in which cache files are searched. + @since Frama-C+dev +*) + module Config_dir: Parameter_sig.Filepath (** Directory in which config files are searched. @since Neon-20140301 @before 23.0-Vanadium parameter type was string instead of Filepath. *) +module State_dir: Parameter_sig.Filepath +(** Directory in which state files are searched. + @since Frama-C+dev +*) + (* this stop special comment does not work as expected (and as explained in the OCamldoc manual, Section 15.2.2. It just skips all the rest of the file instead of skipping until the next stop comment... diff --git a/src/kernel_services/plugin_entry_points/plugin.ml b/src/kernel_services/plugin_entry_points/plugin.ml index 7d0e44b8cbd..7731ea1766c 100644 --- a/src/kernel_services/plugin_entry_points/plugin.ml +++ b/src/kernel_services/plugin_entry_points/plugin.ml @@ -27,8 +27,12 @@ let empty_string = "" let positive_debug_ref = ref 0 let session_is_set_ref = Extlib.mk_fun "session_is_set_ref" let session_ref = Extlib.mk_fun "session_ref" +let cache_is_set_ref = Extlib.mk_fun "cache_is_set_ref" +let cache_ref = Extlib.mk_fun "cache_ref" let config_is_set_ref = Extlib.mk_fun "config_is_set_ref" let config_ref = Extlib.mk_fun "config_ref" +let state_is_set_ref = Extlib.mk_fun "state_is_set_ref" +let state_ref = Extlib.mk_fun "state_ref" (* ************************************************************************* *) (** {2 Signatures} *) @@ -40,7 +44,9 @@ module type S_no_log = sig module Debug: Parameter_sig.Int module Share: Parameter_sig.Specific_dir module Session: Parameter_sig.Specific_dir - module Config: Parameter_sig.Specific_dir + module Cache_dir () : Parameter_sig.Specific_dir + module Config_dir () : Parameter_sig.Specific_dir + module State_dir () : Parameter_sig.Specific_dir val help: Cmdline.Group.t val messages: Cmdline.Group.t val add_plugin_output_aliases: @@ -82,8 +88,12 @@ let is_share_visible () = share_visible_ref := true let session_visible_ref = ref false let is_session_visible () = session_visible_ref := true +let cache_visible_ref = ref false +let is_cache_visible () = cache_visible_ref := true let config_visible_ref = ref false let is_config_visible () = config_visible_ref := true +let state_visible_ref = ref false +let is_state_visible () = state_visible_ref := true let plugin_subpath_ref = ref None let plugin_subpath s = plugin_subpath_ref := Some s @@ -94,7 +104,9 @@ let reset_plugin () = kernel := false; share_visible_ref := false; session_visible_ref := false; + cache_visible_ref := false; config_visible_ref := false; + state_visible_ref := false; plugin_subpath_ref := None; default_msg_keys_ref := []; ;; @@ -466,36 +478,61 @@ struct let visible_ref = !session_visible_ref end) - module Config = - Make_specific_dir + module type User_dir_input = sig + val name: string + val getter: unit -> Fc_Filepath.Normalized.t + val visible_ref: bool ref + val kernel_get: unit -> Fc_Filepath.Normalized.t + val kernel_is_set: unit -> bool + end + + module User_dir (I: User_dir_input) = struct + include Make_specific_dir + (struct + let option_name = I.name + let arg_name = "dir" + let help = Format.asprintf + "set the plug-in %s directory to <dir> \ + (may be used on systems with no default user directory)" + I.name + end) + (struct + let dirs () = [ + let var = "FRAMAC_" ^ (Stdlib.String.uppercase_ascii I.name) in + if I.kernel_is_set () then I.kernel_get () + else match Sys.getenv_opt var with + | Some p when p <> "" -> Fc_Filepath.Normalized.of_string p + | _ -> I.getter () + ] + let visible_ref = !I.visible_ref + end) + end + + module Cache_dir () = User_dir (struct - let option_name = "config" - let arg_name = "dir" - let help = "set the plug-in config directory to <dir> \ - (may be used on systems with no default user directory)" + let name = "cache" + let getter = System_config.User_dirs.cache + let visible_ref = cache_visible_ref + let kernel_get () = !cache_ref () + let kernel_is_set () = !cache_is_set_ref () end) + + module Config_dir () = User_dir (struct - let dirs () = [ - let to_path = Fc_Filepath.Normalized.of_string in - let d, vis = - if !config_is_set_ref () then !config_ref (), false - else - try to_path (Sys.getenv "FRAMAC_CONFIG"), false - with Not_found -> - try to_path (Sys.getenv "USERPROFILE"), false (* Win32 *) - with Not_found -> - (* Unix like *) - try to_path (Sys.getenv "XDG_CONFIG_HOME"), true - with Not_found -> - try - Fc_Filepath.Normalized.concat - (to_path (Sys.getenv "HOME")) ".config", true - with Not_found -> to_path ".", false - in - Fc_Filepath.Normalized.concat - d (if vis then "frama-c" else ".frama-c") - ] - let visible_ref = !config_visible_ref + let name = "config" + let getter = System_config.User_dirs.config + let visible_ref = config_visible_ref + let kernel_get () = !config_ref () + let kernel_is_set () = !config_is_set_ref () + end) + + module State_dir () = User_dir + (struct + let name = "state" + let getter = System_config.User_dirs.state + let visible_ref = state_visible_ref + let kernel_get () = !state_ref () + let kernel_is_set () = !state_is_set_ref () end) let help = add_group "Getting Information" diff --git a/src/kernel_services/plugin_entry_points/plugin.mli b/src/kernel_services/plugin_entry_points/plugin.mli index 887bdc5befc..58f69a3f554 100644 --- a/src/kernel_services/plugin_entry_points/plugin.mli +++ b/src/kernel_services/plugin_entry_points/plugin.mli @@ -51,9 +51,21 @@ module type S_no_log = sig @since Neon-20140301 *) module Session: Parameter_sig.Specific_dir + (** Handle the specific `cache' directory of the plug-in. + @since Frama-C+dev + *) + module Cache_dir (): Parameter_sig.Specific_dir + (** Handle the specific `config' directory of the plug-in. - @since Neon-20140301 *) - module Config: Parameter_sig.Specific_dir + @since Neon-20140301 + @before Frama-C+dev this was not a functor + *) + module Config_dir (): Parameter_sig.Specific_dir + + (** Handle the specific `state' directory of the plug-in. + @since Frama-C+dev + *) + module State_dir (): Parameter_sig.Specific_dir val help: Cmdline.Group.t (** The group containing option -*-help. @@ -131,8 +143,18 @@ val is_session_visible: unit -> unit To be called just before applying {!Register} to create plug-in services. @since Neon-20140301 *) +val is_cache_visible: unit -> unit +(** Make visible to the end-user the -<plug-in>-cache-dir option. + To be called just before applying {!Register} to create plug-in services. + @since Neon-20140301 *) + val is_config_visible: unit -> unit -(** Make visible to the end-user the -<plug-in>-config option. +(** Make visible to the end-user the -<plug-in>-config-dir option. + To be called just before applying {!Register} to create plug-in services. + @since Neon-20140301 *) + +val is_state_visible: unit -> unit +(** Make visible to the end-user the -<plug-in>-state-dir option. To be called just before applying {!Register} to create plug-in services. @since Neon-20140301 *) @@ -178,9 +200,15 @@ val positive_debug_ref: int ref val session_is_set_ref: (unit -> bool) ref val session_ref: (unit -> Filepath.Normalized.t) ref +val cache_is_set_ref: (unit -> bool) ref +val cache_ref: (unit -> Filepath.Normalized.t) ref + val config_is_set_ref: (unit -> bool) ref val config_ref: (unit -> Filepath.Normalized.t) ref +val state_is_set_ref: (unit -> bool) ref +val state_ref: (unit -> Filepath.Normalized.t) ref + (**/**) (* diff --git a/src/plugins/gui/gtk_helper.ml b/src/plugins/gui/gtk_helper.ml index 9879790fff9..8a622821768 100644 --- a/src/plugins/gui/gtk_helper.ml +++ b/src/plugins/gui/gtk_helper.ml @@ -48,7 +48,7 @@ let framac_logo, framac_icon = module Configuration = struct include Cilconfig let configuration_file () = - Gui_parameters.Config.get_file ~mode:`Create_path "frama-c-gui.config" + Gui_parameters.Config_dir.get_file ~mode:`Create_path "frama-c-gui.config" let load () = loadConfiguration (configuration_file ()) let save () = saveConfiguration (configuration_file ()) let reset () = Extlib.safe_remove (configuration_file () :> string); diff --git a/src/plugins/gui/gui_parameters.ml b/src/plugins/gui/gui_parameters.ml index 70882c2143a..4284fb0a1a6 100644 --- a/src/plugins/gui/gui_parameters.ml +++ b/src/plugins/gui/gui_parameters.ml @@ -28,6 +28,8 @@ include Plugin.Register let help = "Graphical User Interface" end) +module Config_dir = Config_dir () + let () = Parameter_customize.do_not_projectify () module Project_name = Empty_string diff --git a/src/plugins/gui/gui_parameters.mli b/src/plugins/gui/gui_parameters.mli index ecc785aeda0..2c665e2dc1b 100644 --- a/src/plugins/gui/gui_parameters.mli +++ b/src/plugins/gui/gui_parameters.mli @@ -24,6 +24,8 @@ include Plugin.S +module Config_dir : Parameter_sig.Specific_dir + module Project_name: Parameter_sig.String (** Option -gui-project. *) -- GitLab