forked from ocaml/ocaml
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathcmi_format.ml
145 lines (131 loc) · 4.76 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
135
136
137
138
139
140
141
142
143
144
145
(**************************************************************************)
(* *)
(* 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 =
| Rectypes
| Deprecated of string
| Opaque
| Unsafe_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 begin
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
begin
let msg =
if buffer < Config.cmi_magic_number then "an older" else "a newer" in
raise (Error (Wrong_version_interface (filename, msg)))
end else begin
raise(Error(Not_an_interface filename))
end
end;
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
#if true then
(* 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
#end
(* 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
)