(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* <O___,, * (see version control and CREDITS file for authors & dates) *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (* * (see LICENSE file for the text of the license) *) (************************************************************************)
(** Rocq files specifies on the command line: - first string is the full filename, with only its extension removed - second string is the absolute version of the previous (via getcwd)
*) type vAccu = (string * string) list
let filename_concat ~separator_hack dir name = if separator_hack then System.(dir // name) else Filename.concat dir name
(* This is used to overcome makefile limitations w.r.t. filenames, (bar/../foo is not the same than ./foo for make) but it is a crude hack and we should remove it, and instead require users to follow
the same naming convention *) let canonize ~separator_hack vAccu f = let f' = filename_concat ~separator_hack
(Loadpath.absolute_dir (Filename.dirname f))
(Filename.basename f) in matchList.filter (fun (_,full) -> f' = full) vAccu with
| (f,_) :: _ -> f
| _ -> f
type what = Library | External let str_of_what = function Library -> "library" | External -> "external file"
let warning_module_notfound = let warn (what, from, f, s) = letopen Pp in
str "in file " ++ str f ++ str ", " ++
str (str_of_what what) ++ spc () ++ str (String.concat "." s) ++ str " is required" ++
pr_opt (fun pth -> str "from root " ++ str (String.concat "." pth)) from ++
str " and has not been found in the loadpath!" in
CWarnings.create ~name:"module-not-found"
~category:CWarnings.CoreCategories.filesystem warn
let warn_if_clash ?(what=Library) exact file dir f1 = letopen Format in function
| f2::fl -> let f = match what with
| Library -> Filename.basename f1 ^ ".v"
| External -> Filename.basename f1 in let what = str_of_what what in let d1 = Filename.dirname f1 in let d2 = Filename.dirname f2 in let dl = List.rev_map Filename.dirname fl in if exact then begin
eprintf "*** Warning: in file %s, \n required %s %s exactly matches several files in path\n (found %s in "
file what (String.concat "." dir) f; List.iter (fun s -> eprintf "%s, " s) dl;
eprintf "%s and %s; used the latter).\n" d2 d1 end else begin
eprintf "*** Warning: in file %s, \n required %s %s matches several files in path\n (found %s in "
file what (String.concat "." dir) f; List.iter (fun s -> eprintf "%s, " s) dl;
eprintf "%s and %s; Require will fail).\n" d2 d1 end
| [] -> ()
let safe_assoc ?(warn_clashes=true) st ?(what=Library) from file k = let search = match what with
| Library -> Loadpath.search_v_known st
| External -> Loadpath.search_other_known st in match search ?from k with
| None -> None
| Some (Loadpath.ExactMatches (f :: l)) -> if warn_clashes then warn_if_clash ~what true file k f l;
Some [f]
| Some (Loadpath.PartialMatchesInSameRoot (root, l)) ->
(matchList.sort String.compare l with [] -> assert false | f :: l as all -> (* If several files match, it will fail at Require;
To be "fair", in rocq dep, we add dependencies on all matching files *) if warn_clashes then warn_if_clash ~what false file k f l;
Some all)
| Some (Loadpath.ExactMatches []) -> assert false
let file_name ~separator_hack s = function
| None -> s
| Some d -> filename_concat ~separator_hack d s
module VData = struct type t = stringlistoption * stringlist let compare = compare end
module VCache = Set.Make(VData)
(** To avoid reading .v files several times for computing dependencies, once for .vo, and once for .vos extensions, the following code performs a single pass and produces a structured list of dependencies, separating dependencies on compiled Rocq files (those loaded by [Require]) from other dependencies, e.g. dependencies
on ".v" files (for [Load]) or ".cmx", ".cmo", etc... (for [Declare]). *)
(* Transform "Declare ML %DECL" to a pair of (meta, cmxs). Something
very similar is in ML top *) let declare_ml_to_file file (decl : string) = let legacy_decl = String.split_on_char ':' decl in match legacy_decl with
| [package] ->
Fl.findlib_deep_resolve ~file ~package
| [cmxs; package] -> (* rocq compile will warn *)
Fl.findlib_deep_resolve ~file ~package
| bad_pkg ->
CErrors.user_err Pp.(str "Failed to resolve plugin: " ++ str decl)
let coq_to_stdlib from strl = let tr_qualid = function
| "Coq" :: l -> "Stdlib" :: l
| l -> l in match from with
| Some from -> Some (tr_qualid from), strl
| None -> None, List.map tr_qualid strl
let with_in_channel ~fname f = let chan = try open_in fname with Sys_error msg -> Error.cannot_open fname msg in
Util.try_finally f chan close_in chan
module State = struct type t = {
loadpath : Loadpath.State.t;
separator_hack : bool;
vAccu : vAccu;
} let loadpath x = x.loadpath end
(* recursive because of Load *) let rec find_dependencies ({State.vAccu; separator_hack; loadpath} as st) basename = (* Visited marks *) let visited_ml = ref CString.Set.empty in let visited_v = ref VCache.empty in let should_visit_v_and_mark from str = ifnot (VCache.mem (from, str) !visited_v) thenbegin
visited_v := VCache.add (from, str) !visited_v; true endelsefalse in (* Output: dependencies found *) let module DepSet = Dep_info.Dep.Setin let dependencies = ref DepSet.empty in let add_dep dep = dependencies := DepSet.add dep !dependencies in let add_dep_other s = add_dep (Dep_info.Dep.Other s) in
(* worker dep *) let () = add_dep_other (Loadpath.get_worker_path loadpath) in
(* Reading file contents *) let f = basename ^ ".v"in
with_in_channel ~fname:f @@ fun chan -> let buf = Lexing.from_channel chan in letopen Lexer in let rec loop () = match coq_action buf with
| exception Fin_fichier ->
DepSet.elements !dependencies
| exception Syntax_error (i,j) ->
Error.cannot_parse f (i,j)
| tok -> match tok with
| Require (from, strl) -> let from, strl = coq_to_stdlib from strl in let decl str = if should_visit_v_and_mark from str thenbegin let files = safe_assoc loadpath from f str in let files = match from, files with
| Some _, _ | None, Some _ -> files
| None, None -> safe_assoc loadpath (Some ["Stdlib"]) f str in match files with
| Some files -> List.iter (fun file_str -> let file_str = canonize ~separator_hack vAccu file_str in
add_dep (Dep_info.Dep.Require file_str)) files
| None -> ifnot (Loadpath.is_in_coqlib loadpath ?from str) then
warning_module_notfound (Library, from, f, str) end in List.iter decl strl;
loop ()
| Declare sl -> (* We resolve "pkg_name" to a .cma file, using the META *) let sl = List.map (declare_ml_to_file f) sl in let decl (meta_file, str) = List.iter add_dep_other meta_file;
str |> List.iter (fun str -> let plugin_file = Filename.chop_extension str in ifnot (CString.Set.mem plugin_file !visited_ml) thenbegin
visited_ml := CString.Set.add plugin_file !visited_ml;
add_dep (Dep_info.Dep.Ml plugin_file) end) in List.iter decl sl;
loop ()
| Load file -> let canon = match file with
| Logical str -> if should_visit_v_and_mark None [str] then safe_assoc loadpath None f [str] else None
| Physical str -> ifString.equal (Filename.basename str) str then if should_visit_v_and_mark None [str] then safe_assoc loadpath None f [str] else None else
Some [canonize ~separator_hack vAccu str] in
(match canon with
| None -> ()
| Some l -> let decl canon =
add_dep_other (Format.sprintf "%s.v" canon); let deps = find_dependencies st canon in List.iter add_dep deps in List.iter decl l);
loop ()
| External(from,str) -> beginmatch safe_assoc loadpath ~what:External (Some from) f [str] with
| Some (file :: _) -> add_dep (Dep_info.Dep.Other (canonize ~separator_hack vAccu file))
| Some [] -> assert false
| None -> warning_module_notfound (External, Some from, f, [str]) end;
loop () in
loop ()
let compute_deps st = let mk_dep (name, _orig_path) = Dep_info.make ~name ~deps:(find_dependencies st name) in
st.vAccu |> CList.rev_map mk_dep
let rec treat_file ~separator_hack vAccu old_dirname old_name = let name = Filename.basename old_name and new_dirname = Filename.dirname old_name in let dirname = match (old_dirname,new_dirname) with
| (d, ".") -> d (* EGJA: We should disable this buggy normalization stuff for
"./foo -> foo" but it breaks dune coq.theory! *)
| (None,d) -> Some d
| (Some d1,d2) -> Some (filename_concat ~separator_hack d1 d2) in let complete_name = file_name ~separator_hack name dirname in let stat_res = try Unix.stat complete_name with Unix.Unix_error(error, _, _) ->
Error.cannot_open complete_name (Unix.error_message error) in match stat_res.Unix.st_kind with
| Unix.S_DIR ->
(if name.[0] = '.'then vAccu else let newdirname = match dirname with
| None -> name
| Some d -> filename_concat ~separator_hack d name in
Array.fold_left (fun vAccu x -> treat_file ~separator_hack vAccu (Some newdirname) x) vAccu
(Sys.readdir complete_name))
| Unix.S_REG ->
(match Loadpath.get_extension name [".v"] with
| base,".v" -> let name = file_name ~separator_hack base dirname in let filename_concat = filename_concat ~separator_hack in let absname = Loadpath.absolute_file_name ~filename_concat base dirname in
(name, absname) :: vAccu
| _ -> vAccu)
| _ -> vAccu
let treat_file_command_line ({State.vAccu; separator_hack} as st) old_name = let vAccu = treat_file ~separator_hack vAccu None old_name in
{ st with State.vAccu }
(* "[sort]" outputs `.v` files required by others *) let sort {State.vAccu; separator_hack; loadpath} = let seen = Hashtbl.create 97 in let rec loop file = let file = canonize ~separator_hack vAccu file in ifnot (Hashtbl.mem seen file) thenbegin
Hashtbl.add seen file (); let cin = open_in (file ^ ".v") in let lb = Lexing.from_channel cin in try whiletruedo match Lexer.coq_action lb with
| Lexer.Require (from, sl) -> List.iter
(fun s -> match safe_assoc loadpath from ~warn_clashes:false file s with
| None -> ()
| Some l -> List.iter loop l)
sl
| _ -> ()
done with Lexer.Fin_fichier ->
close_in cin;
Format.printf "%s.v " file end in List.iter (fun (name, _) -> loop name) vAccu
let add_include st (rc, r, ln) = if rc then
Loadpath.add_r_include st r ln else
Loadpath.add_q_include st r ln
let findlib_init dirs = let env_ocamlpath = try [Sys.getenv "OCAMLPATH"] with Not_found -> [] in let env_ocamlpath = dirs @ env_ocamlpath in let ocamlpathsep = if Sys.unix then":"else";"in let env_ocamlpath = String.concat ocamlpathsep env_ocamlpath in
Findlib.init ~env_ocamlpath ()
let init ~make_separator_hack args = ifnot Coq_config.has_natdynlink then Makefile.set_dyndep "no"; let loadpath = Loadpath.State.make ~worker:args.Args.worker ~boot:args.Args.boot in
Makefile.set_write_vos args.Args.vos;
Makefile.set_noglob args.Args.noglob; (* Add to the findlib search path, common with sysinit/coqinit *) let ml_path = args.Args.ml_path in let rocqenv = Boot.Env.maybe_init ~boot:args.boot ~coqlib:args.coqlib
~warn_ignored_coqlib:CWarnings.warn_ignored_coqlib in let ml_path = match rocqenv with
| Boot -> ml_path
| Env env ->
ml_path @ Boot.Env.Path.[to_string @@ relative (Boot.Env.runtimelib env) ".."] in
findlib_init ml_path; List.iter (add_include loadpath) args.Args.vo_path;
Makefile.set_dyndep args.Args.dyndep;
rocqenv, { State.vAccu = []; loadpath; separator_hack = make_separator_hack }
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.