Release coccinelle-0.2.3rc1
[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
5636bb2c
C
23(*
24 * Copyright 2005-2010, Ecole des Mines de Nantes, University of Copenhagen
25 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
26 * This file is part of Coccinelle.
27 *
28 * Coccinelle is free software: you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation, according to version 2 of the License.
31 *
32 * Coccinelle is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
39 *
40 * The authors reserve the right to distribute this or future versions of
41 * Coccinelle under other licenses.
42 *)
43
44
c3e37e97
C
45(* split patch per file *)
46
5636bb2c
C
47let git_tree = "/var/linuxes/linux-next"
48
49let maintainer_command file =
50 Printf.sprintf
51 "cd %s; scripts/get_maintainer.pl --separator , --nomultiline --nogit -f %s"
52 git_tree file
53
54let subsystem_command file =
55 Printf.sprintf
56 "cd %s; scripts/get_maintainer.pl --nogit --subsystem -f %s | grep -v @"
57 git_tree file
58
59let checkpatch_command file =
60 Printf.sprintf "cd %s; scripts/checkpatch.pl %s" git_tree file
61
62let default_string = "THE REST" (* split by file *)
63
64let extra_cc = Some "kernel-janitors@vger.kernel.org" (* comma separated *)
65
66let prefix_before = Some "/var/linuxes/linux-next"
67let prefix_after = Some "/var/julia/linuxcopy"
68
69(* ------------------------------------------------------------------------ *)
70
71let spaces = Str.regexp "[ \t]"
72
73let fix_before_after l prefix = function
74 Some old_prefix ->
75 (match Str.split spaces l with
76 ("diff"|"+++"|"---")::_ ->
77 (match Str.split (Str.regexp old_prefix) l with
78 [a;b] ->
79 (match Str.split_delim (Str.regexp ("[ \t]"^prefix)) a with
80 [_;""] -> a^b (* prefix is alwaredy there *)
81 | _ -> a^prefix^b)
82 | _ -> l)
83 | _ -> l)
84 | _ -> l
85
86let fix_date l =
87 match Str.split spaces l with
88 (("+++"|"---") as a)::path::rest -> Printf.sprintf "%s %s" a path
89 | _ -> l
90
91(* ------------------------------------------------------------------------ *)
92
c3e37e97
C
93let is_diff = Str.regexp "diff "
94let split_patch i =
95 let patches = ref [] in
96 let cur = ref [] in
5636bb2c
C
97 let get_size l =
98 match Str.split_delim (Str.regexp ",") l with
99 [_;size] -> int_of_string size
100 | _ -> failwith ("bad size: "^l) in
101 let rec read_diff_or_atat _ =
c3e37e97 102 let l = input_line i in
5636bb2c
C
103 let l = fix_date(fix_before_after l "a" prefix_before) in
104 let l = fix_date(fix_before_after l "b" prefix_after) in
105 match Str.split spaces l with
106 "diff"::_ ->
107 (if List.length !cur > 0
108 then patches := List.rev !cur :: !patches);
109 cur := [l];
110 read_diff()
111 | "@@"::min::pl::"@@"::rest ->
112 let msize = get_size min in
113 let psize = get_size pl in
114 cur := l :: !cur;
115 read_hunk msize psize
116 | "\\"::_ -> cur := l :: !cur; read_diff_or_atat()
117 | _ ->
118 failwith
119 "expected diff or @@ (diffstat information should not be present)"
120 and read_diff _ =
121 let l = input_line i in
122 let l = fix_date(fix_before_after l "a" prefix_before) in
123 let l = fix_date(fix_before_after l "b" prefix_after) in
124 cur := l :: !cur;
125 match Str.split spaces l with
126 "+++"::_ -> read_diff_or_atat()
127 | _ -> read_diff()
128 and read_hunk msize psize =
129 if msize = 0 && psize = 0
130 then read_diff_or_atat()
131 else
132 let l = input_line i in
133 cur := l :: !cur;
134 match Str.split spaces l with
135 "-"::_ -> read_hunk (msize - 1) psize
136 | "+"::_ -> read_hunk msize (psize - 1)
137 | _ -> read_hunk (msize - 1) (psize - 1) in
138 try read_diff_or_atat()
139 with End_of_file -> List.rev ((List.rev !cur)::!patches)
c3e37e97
C
140
141(* ------------------------------------------------------------------------ *)
142
5636bb2c
C
143(* can get_maintainers take a file as an argument, or only a patch? *)
144let resolve_maintainers patches =
c3e37e97
C
145 let maintainer_table = Hashtbl.create (List.length patches) in
146 List.iter
147 (function
148 diff_line::rest ->
149 (match Str.split (Str.regexp " a/") diff_line with
150 [before;after] ->
5636bb2c 151 (match Str.split spaces after with
c3e37e97 152 file::_ ->
5636bb2c
C
153 let maintainers =
154 List.hd (Common.cmd_to_list (maintainer_command file)) in
155 let maintainers =
156 match extra_cc with
157 None -> maintainers
158 | Some extra_cc -> maintainers ^ "," ^ extra_cc in
159 let subsystems =
160 Common.cmd_to_list (subsystem_command file) in
161 let info = (subsystems,maintainers) in
162 let cell =
163 try Hashtbl.find maintainer_table info
164 with Not_found ->
165 let cell = ref [] in
166 Hashtbl.add maintainer_table info cell;
167 cell in
168 cell := (file,(diff_line :: rest)) :: !cell
c3e37e97
C
169 | _ -> failwith "filename not found")
170 | _ ->
171 failwith (Printf.sprintf "prefix a/ not found in %s" diff_line))
172 | _ -> failwith "bad diff line")
173 patches;
174 maintainer_table
175
176(* ------------------------------------------------------------------------ *)
177
5636bb2c
C
178let common_prefix l1 l2 =
179 let rec loop = function
180 ([],_) | (_,[]) -> []
181 | (x::xs,y::ys) when x = y -> x :: (loop (xs,ys))
182 | _ -> [] in
183 match loop (l1,l2) with
184 [] ->
185 failwith
186 (Printf.sprintf "found nothing in common for %s and %s"
187 (String.concat "/" l1) (String.concat "/" l2))
188 | res -> res
189
190let merge_files the_rest files =
191 let butlast l = if the_rest then l else List.rev(List.tl(List.rev l)) in
192 match List.map (function s -> Str.split (Str.regexp "/") s) files with
193 first::rest ->
194 let rec loop res = function
195 [] -> String.concat "/" res
196 | x::rest -> loop (common_prefix res x) rest in
197 loop (butlast first) rest
198 | _ -> failwith "not possible"
199
200(* ------------------------------------------------------------------------ *)
201
c3e37e97
C
202let print_all o l =
203 List.iter (function x -> Printf.fprintf o "%s\n" x) l
204
205let make_output_files template maintainer_table patch =
206 let ctr = ref 0 in
5636bb2c
C
207 let elements =
208 Hashtbl.fold
209 (function (services,maintainers) ->
210 function diffs ->
211 function rest ->
212 if services=[default_string]
213 then
214 (* if no maintainer, then one file per diff *)
215 (List.map
216 (function (file,diff) ->
217 ctr := !ctr + 1;
218 (!ctr,true,maintainers,[file],[diff]))
219 (List.rev !diffs)) @
220 rest
221 else
222 begin
223 ctr := !ctr + 1;
224 let (files,diffs) = List.split (List.rev !diffs) in
225 (!ctr,false,maintainers,files,diffs)::rest
226 end)
227 maintainer_table [] in
228 let number = List.length elements in
229 List.iter
230 (function (ctr,the_rest,maintainers,files,diffs) ->
231 let output_file = Printf.sprintf "%s%d" patch ctr in
232 let o = open_out output_file in
233 Printf.fprintf o "To: %s\n\n" maintainers;
234 Printf.fprintf o "Subject: [PATCH %d/%d] %s: "
235 ctr number (merge_files the_rest files);
236 print_all o template;
237 let (nm,o1) = Filename.open_temp_file "patch" "patch" in
238 List.iter (print_all o1) (List.rev diffs);
239 close_out o1;
240 let diffstat =
241 Common.cmd_to_list
242 (Printf.sprintf "diffstat -p1 < %s ; /bin/rm %s" nm nm) in
243 List.iter (print_all o) [diffstat];
244 Printf.fprintf o "\n";
245 List.iter (print_all o) diffs;
246 close_out o;
247 let (info,stat) =
248 Common.cmd_to_list_and_status
249 (checkpatch_command ((Sys.getcwd())^"/"^output_file)) in
250 if not(stat = Unix.WEXITED 0)
251 then (print_all stderr info; Printf.fprintf stderr "\n"))
252 (List.rev elements);
253 let later = Printf.sprintf "%s%d" patch (number + 1) in
254 if Sys.file_exists later
255 then Printf.fprintf stderr "Warning: %s and other files may be left over from a previous run\n" later
256
c3e37e97
C
257(* ------------------------------------------------------------------------ *)
258
c3e37e97
C
259let file = ref ""
260
5636bb2c 261let options = []
c3e37e97
C
262
263let usage = ""
264
265let anonymous x = file := x
34e49164 266
34e49164 267let _ =
c3e37e97
C
268 Arg.parse (Arg.align options) (fun x -> file := x) usage;
269 let i = open_in !file in
270 let patches = split_patch i in
5636bb2c
C
271 close_in i;
272 let maintainer_table = resolve_maintainers patches in
c3e37e97
C
273 let template = Common.cmd_to_list (Printf.sprintf "cat %s.tmp" !file) in
274 make_output_files template maintainer_table !file