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