2 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
4 * This file is part of Coccinelle.
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.
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.
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/>.
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
25 module CP
= Classic_patch
27 (* ./split_patch ../demos/janitorings/patch-kzalloc-vnew3.patch /tmp/xx "0 -> NULL" ../bodymail.doc
28 ./split_patch /tmp/badzero.patch /tmp/xx ../mailbody.doc ../kernel_dirs.meta
30 update: see http://lwn.net/Articles/284469/
31 for a script using git annotate to find automatically to who send
32 a patch (by looking at authors of lines close concerning the patch I guess
35 (*****************************************************************************)
37 (*****************************************************************************)
39 let just_patch = ref false
40 let verbose = ref true
43 if !verbose then pr2 s
45 (*****************************************************************************)
47 (*****************************************************************************)
49 let print_header_patch patch
=
50 patch
+> List.iter
(function CP.File
(s
, header
, body
) -> pr s
)
53 (*****************************************************************************)
54 (* Grouping strategies *)
55 (*****************************************************************************)
57 let group_patch_depth_2 patch
=
58 let patch_with_dir = patch
+> List.map
(function (CP.File
(s
,header
,body
)) ->
59 Common.split
"/" (Common.dirname s
),
60 (CP.File
(s
, header
, body
))
63 let rec aux_patch xs
=
66 | (dir_elems
,x
)::xs
->
68 if List.length dir_elems
>= 2 then
69 let base = Common.take
2 dir_elems
in
71 List.length dir_elems'
>= 2 && Common.take
2 dir_elems'
= base),
74 (fun dir_elems'
-> dir_elems'
= dir_elems
),
78 let (yes
, no
) = xs
+> Common.partition_either
(fun (dir_elems'
, x
) ->
81 else Right
(dir_elems'
, x
)
83 (Common.join
"/" base, ["NOEMAIL"], (x
::yes
))::aux_patch no
85 aux_patch patch_with_dir
89 let group_patch_subsystem_info patch subinfo
=
90 let patch_with_dir = patch
+> List.map
(function (CP.File
(s
,header
,body
)) ->
91 (Common.dirname s
), (CP.File
(s
, header
, body
))
94 let index = Maintainers.mk_inverted_index_subsystem subinfo
in
95 let hash = Maintainers.subsystem_to_hash subinfo
in
97 let rec aux_patch xs
=
100 | ((dir
,patchitem
)::xs
) as xs'
->
102 try Hashtbl.find
index dir
103 with Not_found
-> failwith
("cant find leader for : " ^ dir
)
105 let (emailsleader
, subdirs
) =
106 try Hashtbl.find
hash leader
107 with Not_found
-> failwith
("cant find subdirs of : " ^
leader)
110 (match emailsleader
with
111 | ["NOEMAIL"] | [] | [""] -> pr2 ("no leader maintainer for: "^
leader);
115 let emails = ref emailsleader
in
116 let allsubdirs = (leader, emailsleader
)::subdirs
in
118 let (yes
, no
) = xs'
+> Common.partition_either
(fun (dir'
,patchitem'
)->
120 let emailsdir'
= List.assoc dir'
allsubdirs in
121 emails := !emails $
+$
emailsdir'
;
123 ) with Not_found
-> Right
(dir'
, patchitem'
)
125 if List.mem dir' (leader::subdirs)
130 (leader, !emails, yes
)::aux_patch no
132 aux_patch patch_with_dir
135 (*****************************************************************************)
137 (*****************************************************************************)
138 let i_to_s_padded i total
=
140 | i
when i
< 10 && total
>= 10 -> "0" ^ i_to_s i
141 | i
when i
< 100 -> i_to_s i
144 let split_patch file prefix bodymail subinfofile
=
145 let patch = CP.parse_patch file
in
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 *)
151 let total = List.length
minipatches in
152 let minipatches_indexed = Common.index_list_1
minipatches in
154 let (subject
, bodymail_rest
) =
155 match Common.cat bodymail
with
157 if x
=~
"Subject: \\(.*\\)"
159 let subject = matched1 x
in
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
)
168 Common.command2_y_or_no
("rm -f " ^ prefix ^
"*");
171 minipatches_indexed +> List.iter
(fun ((dir
,emails, minipatch
), i
) ->
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
);
177 CP.unparse_patch minipatch
patchfile;
181 | ["NOEMAIL"] | [] | [""] ->
182 pr2 "no maintainer"; []
184 ) @ ["akpm@linux-foundation.org"]
189 then command2
(sprintf
"cat %s > %s" patchfile tmpfile)
191 Common.with_open_outfile
tmpfile (fun (pr_no_nl
, chan
) ->
192 let pr s
= pr_no_nl
(s ^
"\n") in
193 pr "To: kernel-janitors@vger.kernel.org";
194 pr (sprintf
"Subject: [PATCH %s/%d] %s, for %s"
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--";
202 bodymail_rest
+> List.iter
pr;
204 pr "Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>";
205 emails +> List.iter
(fun s
->
213 command2
(sprintf
"diffstat -p1 %s >> %s" patchfile tmpfile);
214 command2
(sprintf
"echo >> %s" tmpfile);
215 command2
(sprintf
"cat %s >> %s" patchfile tmpfile);
221 (*****************************************************************************)
223 (*****************************************************************************)
225 let test_patch file
=
226 let patch = CP.parse_patch file
in
227 let groups = group_patch_depth_2 patch in
228 groups +> List.iter
(fun (dir
, email
, minipatch
) ->
229 print_header_patch minipatch
;
234 (*****************************************************************************)
235 (* Main entry point *)
236 (*****************************************************************************)
242 "-just_patch", Arg.Set
just_patch, "";
243 "-no_verbose", Arg.Clear
verbose, "";
246 "Usage: " ^ basename
Sys.argv
.(0) ^
247 " <patch> <prefix> <bodymailfile> <maintainerfile> [options]" ^
"\n"
251 Arg.parse
(Arg.align
options) (fun x
-> args := x
::!args) usage_msg;
252 args := List.rev
!args;
255 | [patch] -> test_patch patch
256 | [patch;prefix
;bodymail
;subinfofile
] ->
257 split_patch patch prefix bodymail subinfofile
;
259 command2
("rm -f /tmp/split_check*");
260 let checkfile = "/tmp/split_check.all" in
261 let checkprefix = "/tmp/split_check-xx" in
262 save_excursion
verbose (fun () ->
263 save_excursion
just_patch (fun () ->
266 split_patch patch checkprefix bodymail subinfofile
;
268 command2
("cat /tmp/split_check*.mail > " ^
checkfile);
270 let diff = Common.cmd_to_list
(sprintf
"diff %s %s " patch checkfile)
272 let samesize = Common.filesize
patch = Common.filesize
checkfile in
273 if (List.length
diff <> 0)
276 then pr2 "diff but at least same size"
277 else pr2 "PB: diff and not same size"
279 | _
-> Arg.usage
(Arg.align
options) usage_msg;
283 (*****************************************************************************)