Skip to content

Commit 1202733

Browse files
panglesdjonludlam
authored andcommitted
Occurrences: add a command to aggregate occurrence tables
Signed-off-by: Paul-Elliot <[email protected]>
1 parent 1ef487d commit 1202733

File tree

4 files changed

+192
-27
lines changed

4 files changed

+192
-27
lines changed

src/odoc/bin/main.ml

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,27 +1111,74 @@ module Targets = struct
11111111
end
11121112

11131113
module Occurrences = struct
1114-
let index directories dst warnings_options =
1115-
let dst = Fpath.v dst in
1116-
Occurrences.count ~dst ~warnings_options directories
1114+
module Count = struct
1115+
let index directories dst warnings_options =
1116+
let dst = Fpath.v dst in
1117+
Occurrences.count ~dst ~warnings_options directories
11171118

1118-
let cmd =
1119-
let dst =
1120-
let doc = "Output file path." in
1121-
Arg.(
1122-
required & opt (some string) None & info ~docs ~docv:"PATH" ~doc [ "o" ])
1123-
in
1124-
Term.(
1125-
const handle_error
1126-
$ (const index $ odoc_file_directories $ dst $ warnings_options))
1119+
let cmd =
1120+
let dst =
1121+
let doc = "Output file path." in
1122+
Arg.(
1123+
required
1124+
& opt (some string) None
1125+
& info ~docs ~docv:"PATH" ~doc [ "o" ])
1126+
in
1127+
Term.(
1128+
const handle_error
1129+
$ (const index $ odoc_file_directories $ dst $ warnings_options))
11271130

1128-
let info ~docs =
1129-
let doc =
1130-
"Generate a hashtable mapping identifiers to number of occurrences, as \
1131-
computed from the implementations of .odocl files found in the given \
1132-
directories."
1133-
in
1134-
Term.info "count-occurrences" ~docs ~doc
1131+
let info ~docs =
1132+
let doc =
1133+
"Generate a hashtable mapping identifiers to number of occurrences, as \
1134+
computed from the implementations of .odocl files found in the given \
1135+
directories."
1136+
in
1137+
Term.info "count-occurrences" ~docs ~doc
1138+
end
1139+
module Aggregate = struct
1140+
let index dst files file_list warnings_options =
1141+
match (files, file_list) with
1142+
| [], [] ->
1143+
Error
1144+
(`Msg
1145+
"At least one of --file-list or a path to a file must be passed \
1146+
to odoc aggregate-occurrences")
1147+
| _ ->
1148+
let dst = Fpath.v dst in
1149+
Occurrences.aggregate ~dst ~warnings_options files file_list
1150+
1151+
let cmd =
1152+
let dst =
1153+
let doc = "Output file path." in
1154+
Arg.(
1155+
required
1156+
& opt (some string) None
1157+
& info ~docs ~docv:"PATH" ~doc [ "o" ])
1158+
in
1159+
let inputs_in_file =
1160+
let doc =
1161+
"Input text file containing a line-separated list of paths to files \
1162+
created with count-occurrences."
1163+
in
1164+
Arg.(
1165+
value & opt_all convert_fpath []
1166+
& info ~doc ~docv:"FILE" [ "file-list" ])
1167+
in
1168+
let inputs =
1169+
let doc = "file created with count-occurrences" in
1170+
Arg.(value & pos_all convert_fpath [] & info ~doc ~docv:"FILE" [])
1171+
in
1172+
Term.(
1173+
const handle_error
1174+
$ (const index $ dst $ inputs $ inputs_in_file $ warnings_options))
1175+
1176+
let info ~docs =
1177+
let doc =
1178+
"Aggregate hashtables created with odoc count-occurrences."
1179+
in
1180+
Term.info "aggregate-occurrences" ~docs ~doc
1181+
end
11351182
end
11361183

11371184
module Odoc_error = struct
@@ -1174,7 +1221,8 @@ let () =
11741221
Printexc.record_backtrace true;
11751222
let subcommands =
11761223
[
1177-
Occurrences.(cmd, info ~docs:section_pipeline);
1224+
Occurrences.Count.(cmd, info ~docs:section_pipeline);
1225+
Occurrences.Aggregate.(cmd, info ~docs:section_pipeline);
11781226
Compile.(cmd, info ~docs:section_pipeline);
11791227
Odoc_link.(cmd, info ~docs:section_pipeline);
11801228
Odoc_html.generate ~docs:section_pipeline;

src/odoc/occurrences.ml

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ let count ~dst ~warnings_options:_ directories =
142142
| Type { documentation = Some (`Resolved p as p'); _ }, _ ->
143143
incr htbl p Odoc_model.Paths.Path.((p' : Type.t :> t))
144144
| _ -> ())
145-
(match unit.source_info with None -> [] | Some i -> i.infos)
145+
(match unit.source_info with
146+
| None -> []
147+
| Some i ->
148+
i.infos)
146149
in
147150
()
148151
in
@@ -151,3 +154,53 @@ let count ~dst ~warnings_options:_ directories =
151154
let oc = open_out_bin (Fs.File.to_string dst) in
152155
Marshal.to_channel oc htbl [];
153156
Ok ()
157+
158+
open Astring
159+
open Or_error
160+
161+
let parse_input_file input =
162+
let is_sep = function '\n' | '\r' -> true | _ -> false in
163+
Fs.File.read input >>= fun content ->
164+
let files =
165+
String.fields ~empty:false ~is_sep content |> List.rev_map Fs.File.of_string
166+
in
167+
Ok files
168+
169+
let parse_input_files input =
170+
List.fold_left
171+
(fun acc file ->
172+
acc >>= fun acc ->
173+
parse_input_file file >>= fun files -> Ok (files :: acc))
174+
(Ok []) input
175+
>>= fun files -> Ok (List.concat files)
176+
177+
let aggregate files file_list ~warnings_options:_ ~dst =
178+
parse_input_files file_list >>= fun new_files ->
179+
let files = files @ new_files in
180+
let from_file file : Occtbl.t =
181+
let ic = open_in_bin (Fs.File.to_string file) in
182+
Marshal.from_channel ic
183+
in
184+
let rec loop n f =
185+
if n > 0 then (
186+
f ();
187+
loop (n - 1) f)
188+
else ()
189+
in
190+
let occtbl =
191+
match files with
192+
| [] -> H.create 0
193+
| file1 :: files ->
194+
let acc = from_file file1 in
195+
List.iter
196+
(fun file ->
197+
Occtbl.iter
198+
(fun id { direct; _ } ->
199+
loop direct (fun () -> Occtbl.add acc id))
200+
(from_file file))
201+
files;
202+
acc
203+
in
204+
let oc = open_out_bin (Fs.File.to_string dst) in
205+
Marshal.to_channel oc occtbl [];
206+
Ok ()

test/occurrences/double_wrapped.t/run.t

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ The module B depends on both B and C, the module C only depends on A.
1313
Passing the count-occurrences flag to odoc compile makes it collect the
1414
occurrences information.
1515

16-
1716
$ odoc compile --count-occurrences -I . main__A.cmt
1817
$ odoc compile --count-occurrences -I . main__C.cmt
1918
$ odoc compile --count-occurrences -I . main__B.cmt
@@ -26,22 +25,45 @@ occurrences information.
2625
$ odoc link -I . main__C.odoc
2726
$ odoc link -I . main__.odoc
2827

28+
$ odoc html-generate -o html main.odocl
29+
$ odoc html-generate -o html main__A.odocl
30+
$ odoc html-generate -o html main__B.odocl
31+
$ odoc html-generate -o html main__C.odocl
32+
$ odoc html-generate -o html main__.odocl
33+
2934
The count occurrences command outputs a marshalled hashtable, whose keys are
3035
odoc identifiers, and whose values are integers corresponding to the number of
31-
uses.
36+
uses. We can later aggregate those hashtables, so we create the full hashtable,
37+
and a hashtable for each compilation unit.
38+
39+
$ mkdir dir1
40+
$ mkdir dir2
41+
$ mkdir dir3
42+
$ mkdir dir4
43+
$ mkdir dir5
3244

45+
$ mv main.odocl dir1
46+
$ mv main__.odocl dir2
47+
$ mv main__A.odocl dir3
48+
$ mv main__B.odocl dir4
49+
$ mv main__C.odocl dir5
3350
$ odoc count-occurrences -I . -o occurrences.txt
51+
$ odoc count-occurrences -I dir1 -o occurrences1.txt
52+
$ odoc count-occurrences -I dir2 -o occurrences2.txt
53+
$ odoc count-occurrences -I dir3 -o occurrences3.txt
54+
$ odoc count-occurrences -I dir4 -o occurrences4.txt
55+
$ odoc count-occurrences -I dir5 -o occurrences5.txt
3456

3557
$ du -h occurrences.txt
3658
4.0K occurrences.txt
3759

3860
The occurrences_print executable, available only for testing, unmarshal the file
3961
and prints the number of occurrences in a readable format.
4062

41-
Uses of A and B are counted correctly, with the path rewritten correctly.
42-
Uses of C are not counted, since the canonical destination (generated by dune) does not exist.
63+
Uses of A are: 2 times in b.ml, 1 time in c.ml, 1 time in main.ml
64+
Uses of B are: 1 time in main.ml
65+
Uses of C are not counted, since the canonical destination (Main.C, generated by dune) does not exist.
4366
Uses of values Y.x and Z.y (in b.ml) are not counted since they come from a "local" module.
44-
Uses of values Main__.C.y and Main__.A.x are not rewritten since we use references instead of paths.
4567

4668
$ occurrences_print occurrences.txt | sort
4769
Main was used directly 0 times and indirectly 11 times
@@ -52,3 +74,45 @@ Uses of values Main__.C.y and Main__.A.x are not rewritten since we use referenc
5274
Main.A.x was used directly 2 times and indirectly 0 times
5375
Main.B was used directly 1 times and indirectly 0 times
5476
string was used directly 1 times and indirectly 0 times
77+
78+
$ occurrences_print occurrences1.txt | sort
79+
Main was used directly 0 times and indirectly 2 times
80+
Main.A was used directly 1 times and indirectly 0 times
81+
Main.B was used directly 1 times and indirectly 0 times
82+
83+
$ occurrences_print occurrences2.txt | sort
84+
85+
$ occurrences_print occurrences3.txt | sort
86+
string was used directly 1 times and indirectly 0 times
87+
88+
$ occurrences_print occurrences4.txt | sort
89+
Main was used directly 0 times and indirectly 7 times
90+
Main.A was used directly 2 times and indirectly 5 times
91+
Main.A.(||>) was used directly 1 times and indirectly 0 times
92+
Main.A.M was used directly 2 times and indirectly 0 times
93+
Main.A.t was used directly 1 times and indirectly 0 times
94+
Main.A.x was used directly 1 times and indirectly 0 times
95+
96+
$ occurrences_print occurrences5.txt | sort
97+
Main was used directly 0 times and indirectly 2 times
98+
Main.A was used directly 1 times and indirectly 1 times
99+
Main.A.x was used directly 1 times and indirectly 0 times
100+
101+
Now we can merge both files
102+
103+
$ cat > files.map << EOF
104+
> occurrences3.txt
105+
> occurrences4.txt
106+
> occurrences5.txt
107+
> EOF
108+
$ odoc aggregate-occurrences occurrences1.txt occurrences2.txt --file-list files.map -o aggregated.txt
109+
110+
$ occurrences_print aggregated.txt | sort
111+
Main was used directly 0 times and indirectly 11 times
112+
Main.A was used directly 4 times and indirectly 6 times
113+
Main.A.(||>) was used directly 1 times and indirectly 0 times
114+
Main.A.M was used directly 2 times and indirectly 0 times
115+
Main.A.t was used directly 1 times and indirectly 0 times
116+
Main.A.x was used directly 2 times and indirectly 0 times
117+
Main.B was used directly 1 times and indirectly 0 times
118+
string was used directly 1 times and indirectly 0 times

test/odoc_print/occurrences_print.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module H = Hashtbl.Make (Odoc_model.Paths.Identifier)
22

33
let run inp =
44
let ic = open_in_bin inp in
5-
let htbl : Odoc_odoc.Occurrences.Occtbl.item Odoc_odoc.Occurrences.H.t =
5+
let htbl : Odoc_odoc.Occurrences.Occtbl.t =
66
Marshal.from_channel ic
77
in
88
Odoc_odoc.Occurrences.Occtbl.iter

0 commit comments

Comments
 (0)