Source file opamPackage.ml
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
open OpamStd.Op
let log fmt = OpamConsole.log "PACKAGE" fmt
let slog = OpamConsole.slog
module Version = struct
type version = string
type t = version
let to_string x = x
let of_string x =
if String.length x = 0 then failwith "Package version can't be empty";
String.iter (function
| 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' | '_' | '+' | '.' | '~' -> ()
| c ->
failwith
(Printf.sprintf "Invalid character '%c' in package version %S" c x))
x;
x
let default = "dev"
let compare = OpamVersionCompare.compare
let equal v1 v2 =
compare v1 v2 = 0
let to_json x =
`String (to_string x)
let of_json = function
| `String x -> (try Some (of_string x) with _ -> None)
| _ -> None
module O = struct
type t = version
let to_string = to_string
let compare = compare
let to_json = to_json
let of_json = of_json
end
module Set = OpamStd.Set.Make(O)
module Map = OpamStd.Map.Make(O)
end
module Name = struct
type t = string
let to_string x = x
let of_string x =
match
OpamStd.String.fold_left (fun acc c ->
if acc = Some false then acc else match c with
| 'a'..'z' | 'A'..'Z' -> Some true
| '0'..'9' | '-' | '_' | '+' -> acc
| _ -> Some false)
None x
with
| Some false ->
failwith
(Printf.sprintf "Invalid character in package name %S" x)
| None ->
failwith
(Printf.sprintf "Package name %S should contain at least one letter" x)
| Some true ->
x
let compare = OpamStd.String.compare_case
let equal n1 n2 =
compare n1 n2 = 0
let to_json x = `String x
let of_json = function
| `String s -> (try Some (of_string s) with _ -> None)
| _ -> None
module O = struct
type t = string
let to_string = to_string
let compare = compare
let to_json = to_json
let of_json = of_json
end
module Set = OpamStd.Set.Make(O)
module Map = OpamStd.Map.Make(O)
end
type t = {
name : Name.t;
version: Version.t;
}
let create name version = { name; version }
let name_to_string t = Name.to_string t.name
let version_to_string t = Version.to_string t.version
let name t = t.name
let version t = t.version
let sep = '.'
let of_string_opt s =
if OpamStd.String.contains_char s ' ' ||
OpamStd.String.contains_char s '\n' then
None
else match OpamStd.String.cut_at s sep with
| None -> None
| Some (n, v) ->
try Some { name = Name.of_string n; version = Version.of_string v }
with Failure _ -> None
let of_string s = match of_string_opt s with
| Some x -> x
| None -> failwith "OpamPackage.of_string"
let to_string t =
match Version.to_string t.version with
| "" -> Name.to_string t.name
| _ -> Printf.sprintf "%s%c%s" (Name.to_string t.name) sep (Version.to_string t.version)
let compare nv1 nv2 =
match Name.compare nv1.name nv2.name with
| 0 -> Version.compare nv1.version nv2.version
| i -> i
let hash nv = Hashtbl.hash nv
let equal nv1 nv2 =
compare nv1 nv2 = 0
let to_json nv =
`O [ ("name", Name.to_json (name nv));
("version", Version.to_json (version nv));
]
let of_json = function
| `O dict ->
(try
let open OpamStd.Option.Op in
Name.of_json (OpamStd.List.assoc String.equal "name" dict)
>>= fun name ->
Version.of_json (OpamStd.List.assoc String.equal "version" dict)
>>= fun version -> Some {name; version}
with Not_found -> None)
| _ -> None
module O = struct
type tmp = t
type t = tmp
let compare p1 p2 =
let r = Name.compare p1.name p2.name in
if r = 0 then Version.compare p1.version p2.version else r
let hash = hash
let equal = equal
let to_string = to_string
let to_json = to_json
let of_json = of_json
end
module Set = OpamStd.Set.Make (O)
module Map = OpamStd.Map.Make (O)
let to_map nv =
Set.fold (fun nv map ->
let name = name nv in
let version = version nv in
try Name.Map.add name
(Version.Set.add version (Name.Map.find name map)) map
with Not_found -> Name.Map.add name (Version.Set.singleton version) map
) nv Name.Map.empty
let of_map nvm =
Name.Map.fold (fun n -> Version.Set.fold (fun v -> Set.add (create n v)))
nvm Set.empty
let keys map =
Map.fold (fun nv _ set -> Set.add nv set) map Set.empty
let of_dirname f =
f
|> OpamFilename.basename_dir
|> OpamFilename.Base.to_string
|> of_string_opt
let of_filename f =
if OpamFilename.basename f = OpamFilename.Base.of_string "opam" then
of_dirname (OpamFilename.dirname f)
else if OpamFilename.check_suffix f ".opam" then
of_string_opt OpamFilename.(Base.to_string (basename (chop_extension f)))
else
None
let of_archive f =
let base = OpamFilename.basename f in
match OpamStd.String.cut_at (OpamFilename.Base.to_string base) '+' with
| None -> None
| Some (s,_) -> of_string_opt s
let list dir =
log "list %a" (slog OpamFilename.Dir.to_string) dir;
if OpamFilename.exists_dir dir then (
let files = OpamFilename.rec_files dir in
List.fold_left (fun set f ->
match of_filename f with
| None -> set
| Some p ->
if not (Set.mem p set) then Set.add p set
else
let suffix = Filename.concat (to_string p) "opam" in
let files = List.filter (OpamFilename.ends_with suffix) files in
OpamConsole.error_and_exit `File_error
"Multiple definition of package %s in %s:\n%s"
(to_string p) (OpamFilename.Dir.to_string dir)
(OpamStd.Format.itemize ~bullet:"" OpamFilename.to_string files);
) Set.empty files
) else
Set.empty
let prefixes repodir =
log "prefixes %a" (slog OpamFilename.Dir.to_string) repodir;
if OpamFilename.exists_dir repodir then (
let files = OpamFilename.rec_files repodir in
List.fold_left (fun map f ->
match of_filename f with
| None -> map
| Some p ->
let pkgdir = OpamFilename.dirname_dir (OpamFilename.dirname f) in
let prefix =
match OpamFilename.remove_prefix_dir repodir pkgdir with
| "" -> None
| p -> Some p
in
Map.add p prefix map
) Map.empty files
) else
Map.empty
let versions_of_packages nvset =
Set.fold
(fun nv vset -> Version.Set.add (version nv) vset)
nvset
Version.Set.empty
let has_name nvset n =
Set.exists (fun nv -> name nv = n) nvset
let names_of_packages nvset =
Set.fold
(fun nv vset -> Name.Set.add (name nv) vset)
nvset
Name.Set.empty
let package_of_name_aux empty split filter nv n =
if n = "" then empty else
let inf = {name = String.sub n 0 (String.length n - 1); version= ""} in
let sup = {name = n^"\000"; version = ""} in
let _, _, nv = split inf nv in
let nv, _, _ = split sup nv in
filter nv
let packages_of_name nv n =
package_of_name_aux Set.empty Set.split
(Set.filter (fun nv -> nv.name = n))
nv n
let packages_of_name_map nv n =
package_of_name_aux Map.empty Map.split
(Map.filter (fun nv _ -> nv.name = n))
nv n
let package_of_name nvset n =
Set.choose (packages_of_name nvset n)
let package_of_name_opt nvset n =
try Some (package_of_name nvset n) with Not_found -> None
let packages_of_names nvset nameset =
Name.Set.fold
(fun name acc ->
Set.union acc (packages_of_name nvset name))
nameset Set.empty
let versions_of_name packages n =
versions_of_packages
(packages_of_name packages n)
let filter_name_out packages name =
Set.diff packages (packages_of_name packages name)
let max_version set name =
let versions = versions_of_name set name in
let version = Version.Set.max_elt versions in
create name version
module Graph = (OpamParallel.MakeGraph (O) : OpamParallel.GRAPH with type V.t = t)