(*
* Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
* This file is part of Coccinelle.
*
* Coccinelle is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, according to version 2 of the License.
*
* Coccinelle is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Coccinelle. If not, see .
*
* The authors reserve the right to distribute this or future versions of
* Coccinelle under other licenses.
*)
open Common
module CP = Classic_patch
(* ./split_patch ../demos/janitorings/patch-kzalloc-vnew3.patch /tmp/xx "0 -> NULL" ../bodymail.doc
./split_patch /tmp/badzero.patch /tmp/xx ../mailbody.doc ../kernel_dirs.meta
update: see http://lwn.net/Articles/284469/
for a script using git annotate to find automatically to who send
a patch (by looking at authors of lines close concerning the patch I guess
*)
(*****************************************************************************)
(* Flags *)
(*****************************************************************************)
let just_patch = ref false
let verbose = ref true
let pr2 s =
if !verbose then pr2 s
(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)
let print_header_patch patch =
patch +> List.iter (function CP.File (s, header, body) -> pr s)
(*****************************************************************************)
(* Grouping strategies *)
(*****************************************************************************)
let group_patch_depth_2 patch =
let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
Common.split "/" (Common.dirname s),
(CP.File (s, header, body))
)
in
let rec aux_patch xs =
match xs with
| [] -> []
| (dir_elems,x)::xs ->
let cond, base =
if List.length dir_elems >= 2 then
let base = Common.take 2 dir_elems in
(fun dir_elems' ->
List.length dir_elems' >= 2 && Common.take 2 dir_elems' = base),
base
else
(fun dir_elems' -> dir_elems' = dir_elems),
dir_elems
in
let (yes, no) = xs +> Common.partition_either (fun (dir_elems', x) ->
if cond dir_elems'
then Left x
else Right (dir_elems', x)
) in
(Common.join "/" base, ["NOEMAIL"], (x::yes))::aux_patch no
in
aux_patch patch_with_dir
let group_patch_subsystem_info patch subinfo =
let patch_with_dir = patch +> List.map (function (CP.File (s,header,body)) ->
(Common.dirname s), (CP.File (s, header, body))
)
in
let index = Maintainers.mk_inverted_index_subsystem subinfo in
let hash = Maintainers.subsystem_to_hash subinfo in
let rec aux_patch xs =
match xs with
| [] -> []
| ((dir,patchitem)::xs) as xs' ->
let leader =
try Hashtbl.find index dir
with Not_found -> failwith ("cant find leader for : " ^ dir)
in
let (emailsleader, subdirs) =
try Hashtbl.find hash leader
with Not_found -> failwith ("cant find subdirs of : " ^ leader)
in
(match emailsleader with
| ["NOEMAIL"] | [] | [""] -> pr2 ("no leader maintainer for: "^leader);
| _ -> ()
);
let emails = ref emailsleader in
let allsubdirs = (leader, emailsleader)::subdirs in
let (yes, no) = xs' +> Common.partition_either (fun (dir',patchitem')->
try (
let emailsdir' = List.assoc dir' allsubdirs in
emails := !emails $+$ emailsdir';
Left patchitem'
) with Not_found -> Right (dir', patchitem')
(*
if List.mem dir' (leader::subdirs)
then Left x
else Right (dir', x)
*)
) in
(leader, !emails, yes)::aux_patch no
in
aux_patch patch_with_dir
(*****************************************************************************)
(* Split patch *)
(*****************************************************************************)
let i_to_s_padded i total =
match i with
| i when i < 10 && total >= 10 -> "0" ^ i_to_s i
| i when i < 100 -> i_to_s i
| i -> i_to_s i
let split_patch file prefix bodymail subinfofile =
let patch = CP.parse_patch file in
let subsystem_info = Maintainers.parse_subsystem_info subinfofile in
let minipatches = group_patch_subsystem_info patch subsystem_info in
(* let minipatches = group_patch_depth_2 patch in *)
let total = List.length minipatches in
let minipatches_indexed = Common.index_list_1 minipatches in
let (subject, bodymail_rest) =
match Common.cat bodymail with
| x::y::xs ->
if x =~ "Subject: \\(.*\\)"
then
let subject = matched1 x in
if y =~ "[-]+$"
then
subject, xs
else failwith ("wrong format for mailbody in:" ^ bodymail)
else failwith ("wrong format for mailbody in:" ^ bodymail)
| _ -> failwith ("wrong format for mailbody in:" ^ bodymail)
in
Common.command2_y_or_no ("rm -f " ^ prefix ^ "*");
minipatches_indexed +> List.iter (fun ((dir,emails, minipatch), i) ->
let numpatch = i_to_s_padded i total in
let tmpfile = prefix ^ numpatch ^ ".mail" in
let patchfile = "/tmp/x.patch" in
pr2 ("generating :" ^ tmpfile ^ " for " ^ dir);
CP.unparse_patch minipatch patchfile;
let emails =
(match emails with
| ["NOEMAIL"] | [] | [""] ->
pr2 "no maintainer"; []
| xs -> xs
) @ ["akpm@linux-foundation.org"]
in
if !just_patch
then command2(sprintf "cat %s > %s" patchfile tmpfile)
else begin
Common.with_open_outfile tmpfile (fun (pr_no_nl, chan) ->
let pr s = pr_no_nl (s ^ "\n") in
pr "To: kernel-janitors@vger.kernel.org";
pr (sprintf "Subject: [PATCH %s/%d] %s, for %s"
numpatch total subject dir);
pr ("Cc: " ^ (Common.join ", " (emails @ ["linux-kernel@vger.kernel.org"])));
pr "BCC: padator@wanadoo.fr";
pr "From: Yoann Padioleau ";
pr "--text follows this line--";
pr "";
bodymail_rest +> List.iter pr;
pr "";
pr "Signed-off-by: Yoann Padioleau ";
emails +> List.iter (fun s ->
pr ("Cc: " ^ s)
);
pr "---";
pr "";
);
command2(sprintf "diffstat -p1 %s >> %s" patchfile tmpfile);
command2(sprintf "echo >> %s" tmpfile);
command2(sprintf "cat %s >> %s" patchfile tmpfile);
end
)
(*****************************************************************************)
(* Test *)
(*****************************************************************************)
let test_patch file =
let patch = CP.parse_patch file in
let groups = group_patch_depth_2 patch in
groups +> List.iter (fun (dir, email, minipatch) ->
print_header_patch minipatch;
pr ""
)
(*****************************************************************************)
(* Main entry point *)
(*****************************************************************************)
let main () =
begin
let args = ref [] in
let options = [
"-just_patch", Arg.Set just_patch, "";
"-no_verbose", Arg.Clear verbose, "";
] in
let usage_msg =
"Usage: " ^ basename Sys.argv.(0) ^
" [options]" ^ "\n"
^ "Options are:"
in
Arg.parse (Arg.align options) (fun x -> args := x::!args) usage_msg;
args := List.rev !args;
(match (!args) with
| [patch] -> test_patch patch
| [patch;prefix;bodymail;subinfofile] ->
split_patch patch prefix bodymail subinfofile;
command2("rm -f /tmp/split_check*");
let checkfile = "/tmp/split_check.all" in
let checkprefix = "/tmp/split_check-xx" in
save_excursion verbose (fun () ->
save_excursion just_patch (fun () ->
just_patch := true;
verbose := false;
split_patch patch checkprefix bodymail subinfofile;
));
command2("cat /tmp/split_check*.mail > " ^ checkfile);
let diff = Common.cmd_to_list (sprintf "diff %s %s " patch checkfile)
in
let samesize = Common.filesize patch = Common.filesize checkfile in
if (List.length diff <> 0)
then
if samesize
then pr2 "diff but at least same size"
else pr2 "PB: diff and not same size"
| _ -> Arg.usage (Arg.align options) usage_msg;
)
end
(*****************************************************************************)
let _ =
main ()