Release coccinelle-0.1.2
[bpt/coccinelle.git] / tools / split_patch.ml
CommitLineData
485bce71 1open Common
34e49164
C
2
3module CP = Classic_patch
4
5(* ./split_patch ../demos/janitorings/patch-kzalloc-vnew3.patch /tmp/xx "0 -> NULL" ../bodymail.doc
6./split_patch /tmp/badzero.patch /tmp/xx ../mailbody.doc ../kernel_dirs.meta
7
8update: see http://lwn.net/Articles/284469/
9for a script using git annotate to find automatically to who send
10a patch (by looking at authors of lines close concerning the patch I guess
11*)
12
13(*****************************************************************************)
14(* Flags *)
15(*****************************************************************************)
16
17let just_patch = ref false
18let verbose = ref true
19
20let pr2 s =
21 if !verbose then pr2 s
22
23(*****************************************************************************)
24(* Helpers *)
25(*****************************************************************************)
26
27let print_header_patch patch =
28 patch +> List.iter (function CP.File (s, header, body) -> pr s)
29
30
31(*****************************************************************************)
32(* Grouping strategies *)
33(*****************************************************************************)
34
35let group_patch_depth_2 patch =
36 let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
37 Common.split "/" (Common.dirname s),
38 (CP.File (s, header, body))
39 )
40 in
41 let rec aux_patch xs =
42 match xs with
43 | [] -> []
44 | (dir_elems,x)::xs ->
45 let cond, base =
46 if List.length dir_elems >= 2 then
47 let base = Common.take 2 dir_elems in
48 (fun dir_elems' ->
49 List.length dir_elems' >= 2 && Common.take 2 dir_elems' = base),
50 base
51 else
52 (fun dir_elems' -> dir_elems' = dir_elems),
53 dir_elems
54 in
55
56 let (yes, no) = xs +> Common.partition_either (fun (dir_elems', x) ->
57 if cond dir_elems'
58 then Left x
59 else Right (dir_elems', x)
60 ) in
61 (Common.join "/" base, ["NOEMAIL"], (x::yes))::aux_patch no
62 in
63 aux_patch patch_with_dir
64
65
66
67let group_patch_subsystem_info patch subinfo =
68 let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
69 (Common.dirname s), (CP.File (s, header, body))
70 )
71 in
72 let index = Maintainers.mk_inverted_index_subsystem subinfo in
73 let hash = Maintainers.subsystem_to_hash subinfo in
74
75 let rec aux_patch xs =
76 match xs with
77 | [] -> []
78 | ((dir,patchitem)::xs) as xs' ->
79 let leader =
80 try Hashtbl.find index dir
81 with Not_found -> failwith ("cant find leader for : " ^ dir)
82 in
83 let (emailsleader, subdirs) =
84 try Hashtbl.find hash leader
85 with Not_found -> failwith ("cant find subdirs of : " ^ leader)
86 in
87
88 (match emailsleader with
89 | ["NOEMAIL"] | [] | [""] -> pr2 ("no leader maintainer for: "^leader);
90 | _ -> ()
91 );
92
93 let emails = ref emailsleader in
94 let allsubdirs = (leader, emailsleader)::subdirs in
95
96 let (yes, no) = xs' +> Common.partition_either (fun (dir',patchitem')->
97 try (
98 let emailsdir' = List.assoc dir' allsubdirs in
99 emails := !emails $+$ emailsdir';
100 Left patchitem'
101 ) with Not_found -> Right (dir', patchitem')
102 (*
103 if List.mem dir' (leader::subdirs)
104 then Left x
105 else Right (dir', x)
106 *)
107 ) in
108 (leader, !emails, yes)::aux_patch no
109 in
110 aux_patch patch_with_dir
111
112
113(*****************************************************************************)
114(* Split patch *)
115(*****************************************************************************)
116let i_to_s_padded i total =
117 match i with
118 | i when i < 10 && total >= 10 -> "0" ^ i_to_s i
119 | i when i < 100 -> i_to_s i
120 | i -> i_to_s i
121
122let split_patch file prefix bodymail subinfofile =
123 let patch = CP.parse_patch file in
124
125 let subsystem_info = Maintainers.parse_subsystem_info subinfofile in
126 let minipatches = group_patch_subsystem_info patch subsystem_info in
127 (* let minipatches = group_patch_depth_2 patch in *)
128
129 let total = List.length minipatches in
130 let minipatches_indexed = Common.index_list_1 minipatches in
131
132 let (subject, bodymail_rest) =
133 match Common.cat bodymail with
134 | x::y::xs ->
135 if x =~ "Subject: \\(.*\\)"
136 then
137 let subject = matched1 x in
138 if y =~ "[-]+$"
139 then
140 subject, xs
141 else failwith ("wrong format for mailbody in:" ^ bodymail)
142 else failwith ("wrong format for mailbody in:" ^ bodymail)
143 | _ -> failwith ("wrong format for mailbody in:" ^ bodymail)
144 in
145
146 Common.command2_y_or_no ("rm -f " ^ prefix ^ "*");
147
148
149 minipatches_indexed +> List.iter (fun ((dir,emails, minipatch), i) ->
150 let numpatch = i_to_s_padded i total in
151 let tmpfile = prefix ^ numpatch ^ ".mail" in
152 let patchfile = "/tmp/x.patch" in
153 pr2 ("generating :" ^ tmpfile ^ " for " ^ dir);
154
155 CP.unparse_patch minipatch patchfile;
156
157 let emails =
158 (match emails with
159 | ["NOEMAIL"] | [] | [""] ->
160 pr2 "no maintainer"; []
161 | xs -> xs
162 ) @ ["akpm@linux-foundation.org"]
163 in
164
165
166 if !just_patch
167 then command2(sprintf "cat %s > %s" patchfile tmpfile)
168 else begin
169 Common.with_open_outfile tmpfile (fun (pr_no_nl, chan) ->
170 let pr s = pr_no_nl (s ^ "\n") in
171 pr "To: kernel-janitors@vger.kernel.org";
172 pr (sprintf "Subject: [PATCH %s/%d] %s, for %s"
173 numpatch total subject dir);
174 pr ("Cc: " ^ (Common.join ", " (emails @ ["linux-kernel@vger.kernel.org"])));
175 pr "BCC: padator@wanadoo.fr";
176 pr "From: Yoann Padioleau <padator@wanadoo.fr>";
177 pr "--text follows this line--";
178
179 pr "";
180 bodymail_rest +> List.iter pr;
181 pr "";
182 pr "Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>";
183 emails +> List.iter (fun s ->
184 pr ("Cc: " ^ s)
185 );
186 pr "---";
187
188 pr "";
189 );
190
191 command2(sprintf "diffstat -p1 %s >> %s" patchfile tmpfile);
192 command2(sprintf "echo >> %s" tmpfile);
193 command2(sprintf "cat %s >> %s" patchfile tmpfile);
194 end
195 )
196
197
198
199(*****************************************************************************)
200(* Test *)
201(*****************************************************************************)
202
203let test_patch file =
204 let patch = CP.parse_patch file in
205 let groups = group_patch_depth_2 patch in
206 groups +> List.iter (fun (dir, email, minipatch) ->
207 print_header_patch minipatch;
208 pr ""
209 )
210
211
212(*****************************************************************************)
213(* Main entry point *)
214(*****************************************************************************)
215
216let main () =
217 begin
218 let args = ref [] in
219 let options = [
220 "-just_patch", Arg.Set just_patch, "";
221 "-no_verbose", Arg.Clear verbose, "";
222 ] in
223 let usage_msg =
224 "Usage: " ^ basename Sys.argv.(0) ^
225 " <patch> <prefix> <bodymailfile> <maintainerfile> [options]" ^ "\n"
226 ^ "Options are:"
227 in
228
229 Arg.parse (Arg.align options) (fun x -> args := x::!args) usage_msg;
230 args := List.rev !args;
231
232 (match (!args) with
233 | [patch] -> test_patch patch
234 | [patch;prefix;bodymail;subinfofile] ->
235 split_patch patch prefix bodymail subinfofile;
236
237 command2("rm -f /tmp/split_check*");
238 let checkfile = "/tmp/split_check.all" in
239 let checkprefix = "/tmp/split_check-xx" in
240 save_excursion verbose (fun () ->
241 save_excursion just_patch (fun () ->
242 just_patch := true;
243 verbose := false;
244 split_patch patch checkprefix bodymail subinfofile;
245 ));
246 command2("cat /tmp/split_check*.mail > " ^ checkfile);
247
248 let diff = Common.cmd_to_list (sprintf "diff %s %s " patch checkfile)
249 in
250 let samesize = Common.filesize patch = Common.filesize checkfile in
251 if (List.length diff <> 0)
252 then
253 if samesize
254 then pr2 "diff but at least same size"
255 else pr2 "PB: diff and not same size"
256
257 | _ -> Arg.usage (Arg.align options) usage_msg;
258 )
259 end
260
261(*****************************************************************************)
262let _ =
263 main ()
264