forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcmi_format.ml
134 lines (122 loc) · 4.59 KB
/
cmi_format.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
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Fabrice Le Fessant, INRIA Saclay *)
(* *)
(* Copyright 2012 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
type pers_flags = Deprecated of string
type error =
| Not_an_interface of string
| Wrong_version_interface of string * string
| Corrupted_interface of string
exception Error of error
type cmi_infos = {
cmi_name: string;
cmi_sign: Types.signature_item list;
cmi_crcs: (string * Digest.t option) list;
cmi_flags: pers_flags list;
}
let input_cmi ic =
let name, sign = input_value ic in
let crcs = input_value ic in
let flags = input_value ic in
{cmi_name = name; cmi_sign = sign; cmi_crcs = crcs; cmi_flags = flags}
let read_cmi filename =
let ic = open_in_bin filename in
try
let buffer =
really_input_string ic (String.length Config.cmi_magic_number)
in
if buffer <> Config.cmi_magic_number then (
close_in ic;
let pre_len = String.length Config.cmi_magic_number - 3 in
if
String.sub buffer 0 pre_len
= String.sub Config.cmi_magic_number 0 pre_len
then
let msg =
if buffer < Config.cmi_magic_number then "an older" else "a newer"
in
raise (Error (Wrong_version_interface (filename, msg)))
else raise (Error (Not_an_interface filename)));
let cmi = input_cmi ic in
close_in ic;
cmi
with
| End_of_file | Failure _ ->
close_in ic;
raise (Error (Corrupted_interface filename))
| Error e ->
close_in ic;
raise (Error e)
let output_cmi filename oc cmi =
(* beware: the provided signature must have been substituted for saving *)
output_string oc Config.cmi_magic_number;
output_value oc (cmi.cmi_name, cmi.cmi_sign);
flush oc;
let crc = Digest.file filename in
let crcs = (cmi.cmi_name, Some crc) :: cmi.cmi_crcs in
output_value oc crcs;
output_value oc cmi.cmi_flags;
crc
(* This function is also called by [save_cmt] as cmi_format is subset of
cmt_format, so dont close the channel yet
*)
let create_cmi ?check_exists filename (cmi : cmi_infos) =
(* beware: the provided signature must have been substituted for saving *)
let content =
Config.cmi_magic_number ^ Marshal.to_string (cmi.cmi_name, cmi.cmi_sign) []
(* checkout [output_value] in {!Pervasives} module *)
in
let crc = Digest.string content in
let cmi_infos =
if check_exists <> None && Sys.file_exists filename then
Some (read_cmi filename)
else None
in
match cmi_infos with
| Some
{
cmi_name = _;
cmi_sign = _;
cmi_crcs = (old_name, Some old_crc) :: rest;
cmi_flags;
}
(* TODO: design the cmi format so that we don't need read the whole cmi *)
when cmi.cmi_name = old_name && crc = old_crc && cmi.cmi_crcs = rest
&& cmi_flags = cmi.cmi_flags ->
crc
| _ ->
let crcs = (cmi.cmi_name, Some crc) :: cmi.cmi_crcs in
let oc = open_out_bin filename in
output_string oc content;
output_value oc crcs;
output_value oc cmi.cmi_flags;
close_out oc;
crc
(* Error report *)
open Format
let report_error ppf = function
| Not_an_interface filename ->
fprintf ppf "%a@ is not a compiled interface" Location.print_filename
filename
| Wrong_version_interface (filename, older_newer) ->
fprintf ppf
"%a@ is not a compiled interface for this version of OCaml.@.It seems to \
be for %s version of OCaml."
Location.print_filename filename older_newer
| Corrupted_interface filename ->
fprintf ppf "Corrupted compiled interface@ %a" Location.print_filename
filename
let () =
Location.register_error_of_exn (function
| Error err -> Some (Location.error_of_printer_file report_error err)
| _ -> None)