Source file commandPreBuild.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
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
open Ezcmd.TYPES
open EzFile.OP
open EzConfig.OP
open OpamParserTypes
let cmd_name = "pre-build"
let cache_file ~cache ~md5 =
cache // "md5" // String.sub md5 0 2 // md5
let check_cache_file ~cache ~md5 =
let file = cache_file ~cache ~md5 in
if Sys.file_exists file then Some file else None
let find_archive_in_cache ~repo ~md5 =
match check_cache_file ~cache:Globals.opam_cache_dir ~md5 with
| Some file -> Some file
| None ->
match check_cache_file ~cache:Globals.opambin_cache_dir ~md5 with
| Some file -> Some file
| None ->
ignore repo;
None
let check_cached_binary_archive ~version ~repo ~package =
Misc.global_log "found binary package in repo %s" repo;
let package_dir = repo // "packages" // package // version in
let src = ref None in
let md5 = ref None in
let opam = OpamParser.file ( package_dir // "opam" ) in
List.iter (function
| Section ( _ , { section_kind = "url" ; section_items ; _ } ) ->
List.iter (function
Variable ( _, "src", String ( _ , v )) -> src := Some v
| Variable ( _, "checksum",
List ( _, [ String ( _, v ) ] )) ->
assert ( EzString.starts_with v ~prefix:"md5=" );
let len = String.length v in
md5 := Some ( String.sub v 4 (len-4) )
| _ -> ()
) section_items
| _ -> ()
) opam.file_contents ;
let binary_archive =
match !md5 with
| None ->
Misc.global_log "url.checksum.md5 not found";
None
| Some md5 ->
match find_archive_in_cache ~repo ~md5 with
| Some binary_archive ->
Some binary_archive
| None ->
match !src with
| None ->
Printf.eprintf "error: url.src not found\n%!";
exit 2
| Some url ->
let temp_dir = Globals.opambin_switch_temp_dir () in
let output = temp_dir // md5 in
match Misc.wget ~url ~output with
| None ->
Printf.eprintf "Error: could not download archive at %S\n%!" url;
exit 2
| Some binary_archive ->
let digest = Digest.file binary_archive in
assert ( Digest.to_hex digest = md5 );
let cache_dir =
Globals.opam_cache_dir //
"md5" // String.sub md5 0 2 in
let cached_file = cache_dir // md5 in
EzFile.make_dir ~p:true cache_dir;
Sys.rename binary_archive cached_file ;
Some cached_file
in
EzFile.make_dir Globals.marker_cached ;
let install_file = package ^ ".install" in
if Sys.file_exists install_file then
Sys.remove install_file ;
Unix.chdir Globals.marker_cached ;
let package_files = package_dir // "files" in
let s = EzFile.read_file
( package_files // Globals.package_version ) in
EzFile.write_file Globals.package_version s ;
begin
match EzFile.read_file
( package_files // Globals.package_config ) with
| exception _ -> ()
| s ->
EzFile.write_file Globals.package_config s
end;
begin
match binary_archive with
| None -> ()
| Some binary_archive ->
Misc.call [| "tar" ; "zxf" ; binary_archive |] ;
end;
true
let chop_prefix s ~prefix =
if EzString.starts_with s ~prefix then
let prefix_len = String.length prefix in
let len = String.length s in
Some ( String.sub s prefix_len (len - prefix_len) )
else
None
let chop_suffix s ~suffix =
if EzString.ends_with s ~suffix then
let suffix_len = String.length suffix in
let len = String.length s in
Some ( String.sub s 0 (len - suffix_len) )
else
None
let has_equal_suffix v =
let len = String.length v in
assert ( len > 0 );
v.[len-1] = '='
let maybe_apply_patch ~name ~version =
let keep_version = version in
let patches_dir =
let patches_url = !!Config.patches_url in
match chop_prefix patches_url ~prefix:"file://" with
| Some s -> s
| None -> Globals.opambin_patches_dir
in
if not ( Sys.file_exists patches_dir ) then
Printf.kprintf failwith
{|
Error: patches dir '%s' does not exist.\n
Maybe you didn't use 'opam-bin install patches' ?\n%!|}
patches_dir ;
let rec iter_package package =
let package_dir = patches_dir // "patches" // package in
if Sys.file_exists package_dir then
let files = Sys.readdir package_dir in
let versions = ref [] in
let alias = ref None in
Array.iter (fun file ->
match chop_suffix file ~suffix:".alias" with
| Some package -> alias := Some package
| None ->
match chop_suffix file ~suffix:".patch" with
| Some version -> versions := version :: !versions
| None -> ()
) files;
match !alias with
| Some package -> iter_package package
| None ->
let versions = Array.of_list !versions in
Array.sort VersionCompare.compare versions ;
let rec iter version versions current =
match versions with
| [] -> current
| v :: versions ->
if has_equal_suffix v then
if v = version ^ "=" then
Some v
else
iter version versions current
else
if VersionCompare.compare version v >= 0 then
iter version versions (Some v)
else current
in
match iter version (Array.to_list versions) None with
| None ->
Printf.eprintf
"Package %S is not relocatable, but no patch found for version %S.\n%!"
name version;
Printf.eprintf
"You may have to disable opam-bin to install that version.\n%!";
false
| Some version ->
let patch = package_dir // version ^ ".patch" in
Misc.global_log "Using patch %s for %s.%s"
patch name keep_version ;
Misc.call [| "cp" ; "-f";
patch ; Globals.marker_patch |];
Misc.call [| "patch" ; "-p1"; "-i"; patch |] ;
if Sys.file_exists "reloc-patch.sh" then
Misc.call [| "sh"; "./reloc_patch.sh" |];
true
else
true
in
iter_package name
let cached_binary_archive ~name ~version ~depends =
if not ( maybe_apply_patch ~name ~version ) then
`NotRelocatable
else
let ( source_md5, _depends, _dependset, missing_versions, _opam_file ) =
CommandPostInstall.compute_hash
~name ~version ~depends () in
if missing_versions <> [] then
`MissingVersions missing_versions
else
let version_prefix = Printf.sprintf "%s.%s+bin+%s+"
name version source_md5 in
if Misc.iter_repos ~cont:(fun x -> x)
( Misc.all_repos () )
(fun ~repo ~package ~version ->
if EzString.starts_with version ~prefix:version_prefix then begin
check_cached_binary_archive ~package ~repo ~version
end else
false
) then
`BinaryArchiveFound
else begin
Misc.global_log "Could not find cached binary package %s"
version_prefix ;
`NoBinaryArchiveFound source_md5
end
let error_on_compile =
match Sys.getenv "OPAM_BIN_FORCE" with
| exception _ -> false
| _ -> true
let error_on_non_reloc =
match Sys.getenv "OPAM_BIN_RELOC" with
| exception _ -> false
| _ -> true
let action args =
Misc.global_log "CMD: %s"
( String.concat "\n " ( cmd_name :: args) ) ;
match args with
| name :: version :: depends :: [] ->
let marker_skip = Globals.marker_skip in
if not !!Config.enabled
|| Misc.not_this_switch () then begin
Misc.global_log "opam-bin is disabled";
EzFile.write_file marker_skip
"opam-bin is disabled";
end else
let marker_source = Globals.marker_source in
let marker_opam = Globals.marker_opam in
let marker_patch = Globals.marker_patch in
if Sys.file_exists marker_source then begin
Misc.global_log "removing marker_source";
Sys.remove marker_source ;
end;
if Sys.file_exists marker_opam then begin
Misc.global_log "removing marker_opam";
Sys.remove marker_opam ;
end;
if Sys.file_exists marker_patch then begin
Misc.global_log "removing marker_patch";
Sys.remove marker_patch ;
end;
if Sys.file_exists Globals.marker_cached then begin
Misc.global_log "%s should not already exist!"
Globals.marker_cached;
exit 2
end else
if Sys.file_exists Globals.package_version then begin
Misc.global_log "already a binary package";
EzFile.write_file marker_source "already-a-binary-package";
end else begin
Misc.global_log "checking for cached archive";
match cached_binary_archive ~name ~version ~depends with
| `BinaryArchiveFound ->
Misc.global_log "found a binary archive in cache";
| `NoBinaryArchiveFound source_md5 ->
Misc.global_log "no binary archive found.";
if error_on_compile then begin
Printf.eprintf
"Error: opam-bin is configured to prevent compilation.\n%!";
exit 2
end;
EzFile.write_file marker_source source_md5
| `MissingVersions missing_versions ->
EzFile.write_file marker_skip
( Printf.sprintf "Missing binary deps: %s"
( String.concat " " missing_versions ) )
| `NotRelocatable ->
if error_on_non_reloc then begin
Printf.eprintf
"Error: opam-bin is configured to force relocation.\n%!";
exit 2
end;
EzFile.write_file marker_skip
"Missing relocation patch for unrelocatable package"
end
| _ ->
Misc.global_log "unexpected arg.";
Printf.eprintf
"Unexpected args: usage is '%s %s name version depends cmd...'\n%!" Globals.command cmd_name ;
exit 2
let cmd =
let args = ref [] in
Arg.{
cmd_name ;
cmd_action = (fun () -> action !args) ;
cmd_args = [
[], Anons (fun list -> args := list),
Ezcmd.info "args"
];
cmd_man = [];
cmd_doc = "(opam hook) Backup the sources before building the package";
}