Commit | Line | Data |
---|---|---|
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 |
47 | let git_tree = "/var/linuxes/linux-next" |
48 | ||
49 | let maintainer_command file = | |
50 | Printf.sprintf | |
51 | "cd %s; scripts/get_maintainer.pl --separator , --nomultiline --nogit -f %s" | |
52 | git_tree file | |
53 | ||
54 | let subsystem_command file = | |
55 | Printf.sprintf | |
56 | "cd %s; scripts/get_maintainer.pl --nogit --subsystem -f %s | grep -v @" | |
57 | git_tree file | |
58 | ||
59 | let checkpatch_command file = | |
60 | Printf.sprintf "cd %s; scripts/checkpatch.pl %s" git_tree file | |
61 | ||
62 | let default_string = "THE REST" (* split by file *) | |
63 | ||
64 | let extra_cc = Some "kernel-janitors@vger.kernel.org" (* comma separated *) | |
65 | ||
66 | let prefix_before = Some "/var/linuxes/linux-next" | |
67 | let prefix_after = Some "/var/julia/linuxcopy" | |
68 | ||
69 | (* ------------------------------------------------------------------------ *) | |
70 | ||
71 | let spaces = Str.regexp "[ \t]" | |
72 | ||
73 | let 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 | ||
86 | let 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 |
93 | let is_diff = Str.regexp "diff " |
94 | let 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? *) |
144 | let 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 |
178 | let 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 | ||
190 | let 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 |
202 | let print_all o l = |
203 | List.iter (function x -> Printf.fprintf o "%s\n" x) l | |
204 | ||
205 | let 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 |
259 | let file = ref "" |
260 | ||
5636bb2c | 261 | let options = [] |
c3e37e97 C |
262 | |
263 | let usage = "" | |
264 | ||
265 | let anonymous x = file := x | |
34e49164 | 266 | |
34e49164 | 267 | let _ = |
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 |