1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
open Util
module DP = Names.DirPath
(** Load paths. Mapping from physical to logical paths. *)
type t = {
path_physical : CUnix.physical_path;
path_logical : DP.t;
path_implicit : bool;
path_root : (CUnix.physical_path * DP.t);
}
let load_paths = Summary.ref ([] : t list) ~stage:Summary.Stage.Synterp ~name:"LOADPATHS"
let logical p = p.path_logical
let physical p = p.path_physical
let pp p =
let dir = DP.print p.path_logical in
let path = Pp.str (CUnix.escaped_string_of_physical_path p.path_physical) in
Pp.(hov 2 (dir ++ spc () ++ path))
let get_load_paths () = !load_paths
let anomaly_too_many_paths path =
CErrors.anomaly Pp.(str "Several logical paths are associated with" ++ spc () ++ str path ++ str ".")
let find_load_path phys_dir =
let phys_dir = CUnix.canonical_path_name phys_dir in
let filter p = String.equal p.path_physical phys_dir in
let paths = List.filter filter !load_paths in
match paths with
| [] -> raise Not_found
| [p] -> p
| _ -> anomaly_too_many_paths phys_dir
let find_with_logical_path dirpath =
List.filter (fun p -> Names.DirPath.equal p.path_logical dirpath) !load_paths
let warn_file_found_multiple_times =
CWarnings.create ~name:"ambiguous-extra-dep" ~category:CWarnings.CoreCategories.filesystem
(fun (file,from,other,) ->
Pp.(str "File " ++ str file ++ str " found twice in " ++
Names.DirPath.print from ++ str":" ++ spc () ++ str other ++ str " (selected)," ++
spc() ++ str extra ++ str ".") )
let rec first_path_containing ?loc from file acc = function
| [] ->
begin match acc with
| Some x -> x
| None ->
CErrors.user_err Pp.(str "File " ++ str file ++ str " not found in " ++
Names.DirPath.print from ++ str".")
end
| x :: xs ->
let abspath = x ^ "/" ^ file in
if Sys.file_exists abspath then begin
match acc with
| None -> first_path_containing ?loc from file (Some abspath) xs
| Some other ->
warn_file_found_multiple_times ?loc (file,from,other,abspath);
first_path_containing ?loc from file acc xs
end else
first_path_containing ?loc from file acc xs
let ?loc ~from ~file () =
match find_with_logical_path from with
| _ :: _ as paths ->
let paths = List.map physical paths in
first_path_containing ?loc from file None paths
| [] -> CErrors.user_err
Pp.(str "No LoadPath found for " ++ Names.DirPath.print from ++ str".")
let remove_load_path dir =
let filter p = not (String.equal p.path_physical dir) in
load_paths := List.filter filter !load_paths
let warn_overriding_logical_loadpath =
CWarnings.create ~name:"overriding-logical-loadpath" ~category:CWarnings.CoreCategories.filesystem
(fun (phys_path, old_path, rocq_path) ->
Pp.(seq [str phys_path; strbrk " was previously bound to "
; DP.print old_path; strbrk "; it is remapped to "
; DP.print rocq_path]))
let add_load_path root phys_path rocq_path ~implicit =
let phys_path = CUnix.canonical_path_name phys_path in
let filter p = String.equal p.path_physical phys_path in
let binding = {
path_logical = rocq_path;
path_physical = phys_path;
path_implicit = implicit;
path_root = root;
} in
match List.filter filter !load_paths with
| [] ->
load_paths := binding :: !load_paths
| [{ path_logical = old_path; path_implicit = old_implicit }] ->
let replace =
if DP.equal rocq_path old_path then
implicit <> old_implicit
else
let () =
if not (DP.equal old_path Libnames.default_root_prefix) then
warn_overriding_logical_loadpath (phys_path, old_path, rocq_path)
in
true in
if replace then
begin
remove_load_path phys_path;
load_paths := binding :: !load_paths;
end
| _ -> anomaly_too_many_paths phys_path
let filter_path f =
let rec aux = function
| [] -> []
| p :: l ->
if f p.path_logical then (p.path_physical, p.path_logical) :: aux l
else aux l
in
aux !load_paths
let eq_root (phys,log_path) (phys',log_path') =
String.equal phys phys' && Names.DirPath.equal log_path log_path'
let add_path root file = function
| [] -> [root,[file]]
| (root',l) :: l' as l'' -> if eq_root root root' then (root', file::l) :: l' else (root,[file]) :: l''
let expand_path ?root dir =
let exact_path = match root with None -> dir | Some root -> Libnames.append_dirpath root dir in
let rec aux = function
| [] -> [], []
| { path_physical = ph; path_logical = lg; path_implicit = implicit; path_root } :: l ->
let full, others = aux l in
if DP.equal exact_path lg then
(ph, lg) :: full, others
else
let success =
match root with
| None -> implicit && Libnames.is_dirpath_suffix_of dir lg
| Some root ->
Libnames.(is_dirpath_prefix_of root lg &&
is_dirpath_suffix_of dir (drop_dirpath_prefix root lg))
in
if success then
full, add_path path_root (ph, lg) others
else
full, others in
let full, others = aux !load_paths in
full, List.map snd others
let locate_file fname =
let paths = List.map physical !load_paths in
let _,longfname =
System.find_file_in_path ~warn:(not !Flags.quiet) paths fname in
longfname
module Error = struct
type t = LibUnmappedDir | LibNotFound
let unmapped_dir qid =
let prefix, _ = Libnames.repr_qualid qid in
CErrors.user_err
Pp.(seq [ str "Cannot load "; Libnames.pr_qualid qid; str ":"; spc ()
; str "no physical path bound to"; spc ()
; Names.DirPath.print prefix; fnl ()
])
let lib_not_found dir =
let vos = !Flags.load_vos_libraries in
let vos_msg = if vos then [Pp.str " (while searching for a .vos file)"] else [] in
CErrors.user_err
Pp.(seq ([ str "Cannot find library "; Names.DirPath.print dir; str" in loadpath"]@vos_msg))
let raise dp = function
| LibUnmappedDir ->
unmapped_dir (Libnames.qualid_of_dirpath dp)
| LibNotFound ->
lib_not_found dp
end
let select_vo_file ~find base =
let find ext =
try
let name = Names.Id.to_string base ^ ext in
let lpath, file = find name in
Ok (lpath, file)
with Not_found -> Error Error.LibNotFound in
if !Flags.load_vos_libraries
then begin
match find ".vos" with
| Ok (_, vos as resvos) when (Unix.stat vos).Unix.st_size > 0 -> Ok resvos
| _ -> find ".vo"
end
else find ".vo"
let find_first loadpath base =
match System.all_in_path loadpath base with
| [] -> raise Not_found
| f :: _ -> f
let find_unique fullqid loadpath base =
match System.all_in_path loadpath base with
| [] -> raise Not_found
| [f] -> f
| _::_ as l ->
CErrors.user_err Pp.(str "Required library " ++ Libnames.pr_qualid fullqid ++
strbrk " matches several files in path (found " ++ pr_enum str (List.map snd l) ++ str ").")
let locate_absolute_library dir : (CUnix.physical_path, Error.t) Result.t =
let pref, base = Libnames.split_dirpath dir in
let loadpath = filter_path (fun dir -> DP.equal dir pref) in
match loadpath with
| [] -> Error LibUnmappedDir
| _ ->
match select_vo_file ~find:(find_first loadpath) base with
| Ok (_, file) -> Ok file
| Error fail -> Error fail
let locate_qualified_library ?root qid :
(DP.t * CUnix.physical_path, Error.t) Result.t =
let dir, base = Libnames.repr_qualid qid in
match expand_path ?root dir with
| [], [] -> Error LibUnmappedDir
| full_matches, others ->
let result =
match select_vo_file ~find:(find_first full_matches) base with
| Ok _ as x -> x
| Error _ ->
let rec aux = function
| [] -> Error Error.LibUnmappedDir
| block :: rest ->
match select_vo_file ~find:(find_unique qid block) base with
| Ok _ as x -> x
| Error _ -> aux rest
in
aux others
in
match result with
| Ok (dir,file) ->
let library = Libnames.add_dirpath_suffix dir base in
Ok (library, file)
| Error _ as e -> e
let warn_deprecated_missing_stdlib =
CWarnings.create ~name:"deprecated-missing-stdlib"
~category:Deprecation.Version.v9_0
(fun qid ->
Pp.(str "Loading Stdlib without prefix is deprecated." ++ spc ()
++ str "Use \"From Stdlib Require " ++ Libnames.pr_qualid qid
++ str "\"" ++ spc() ++ str "or the deprecated \"From Coq Require "
++ Libnames.pr_qualid qid ++ str "\"" ++ spc ()
++ str "for compatibility with older Coq versions."))
let locate_qualified_library ?root qid =
let root_stdlib =
Names.(Libnames.add_dirpath_suffix DirPath.empty (Id.of_string "Stdlib")) in
match root, locate_qualified_library ?root qid with
| Some _, r | None, (Ok _ as r) -> r
| None, (Error _ as e) ->
match locate_qualified_library ~root:root_stdlib qid with
| Error _ -> e
| Ok _ as o -> warn_deprecated_missing_stdlib ?loc:qid.loc qid; o
(** { 5 Extending the load path } *)
type vo_path =
{ unix_path : string
(** Filesystem path containing vo/ml files *)
; coq_path : DP.t
(** Coq prefix for the path *)
; implicit : bool
(** [implicit = true] avoids having to qualify with [coq_path]
true for -R, false for -Q in command line *)
; recursive : bool
(** [recursive] will determine whether we explore sub-directories *)
}
let warn_cannot_open_path =
CWarnings.create ~name:"cannot-open-path" ~category:CWarnings.CoreCategories.filesystem
(fun unix_path -> Pp.(str "Cannot open " ++ str unix_path))
let warn_cannot_use_directory =
CWarnings.create ~name:"cannot-use-directory" ~category:CWarnings.CoreCategories.filesystem
(fun d ->
Pp.(str "Directory " ++ str d ++
strbrk " cannot be used as a Rocq identifier (skipped)"))
let convert_string d =
try Names.Id.of_string d
with
| CErrors.UserError _ ->
let d = Unicode.escaped_if_non_utf8 d in
warn_cannot_use_directory d;
raise_notrace Exit
let add_vo_path lp =
let unix_path = lp.unix_path in
let implicit = lp.implicit in
let recursive = lp.recursive in
if System.exists_dir unix_path then
let dirs = if recursive then System.all_subdirs ~unix_path else [] in
let dirs = List.sort (fun a b -> String.compare (fst a) (fst b)) dirs in
let prefix = DP.repr lp.coq_path in
let convert_dirs (lp, cp) =
try
let path = List.rev_map convert_string cp @ prefix in
Some (lp, DP.make path)
with Exit -> None
in
let dirs = List.map_filter convert_dirs dirs in
let root = (unix_path,lp.coq_path) in
let add (path, dir) = add_load_path root path ~implicit dir in
let dirs = List.rev dirs in
let () = List.iter add dirs in
add_load_path root unix_path ~implicit lp.coq_path
else
warn_cannot_open_path unix_path