[M120 Migration] Implement ewk_view_is_video_playing api
[platform/framework/web/chromium-efl.git] / build / fix_gn_headers.py
1 #!/usr/bin/env python3
2 # Copyright 2017 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Fix header files missing in GN.
7
8 This script takes the missing header files from check_gn_headers.py, and
9 try to fix them by adding them to the GN files.
10 Manual cleaning up is likely required afterwards.
11 """
12
13
14 import argparse
15 import os
16 import re
17 import subprocess
18 import sys
19
20
21 def GitGrep(pattern):
22   p = subprocess.Popen(
23       ['git', 'grep', '-En', pattern, '--', '*.gn', '*.gni'],
24       stdout=subprocess.PIPE)
25   out, _ = p.communicate()
26   return out, p.returncode
27
28
29 def ValidMatches(basename, cc, grep_lines):
30   """Filter out 'git grep' matches with header files already."""
31   matches = []
32   for line in grep_lines:
33     gnfile, linenr, contents = line.split(':')
34     linenr = int(linenr)
35     new = re.sub(cc, basename, contents)
36     lines = open(gnfile).read().splitlines()
37     assert contents in lines[linenr - 1]
38     # Skip if it's already there. It could be before or after the match.
39     if lines[linenr] == new:
40       continue
41     if lines[linenr - 2] == new:
42       continue
43     print('    ', gnfile, linenr, new)
44     matches.append((gnfile, linenr, new))
45   return matches
46
47
48 def AddHeadersNextToCC(headers, skip_ambiguous=True):
49   """Add header files next to the corresponding .cc files in GN files.
50
51   When skip_ambiguous is True, skip if multiple .cc files are found.
52   Returns unhandled headers.
53
54   Manual cleaning up is likely required, especially if not skip_ambiguous.
55   """
56   edits = {}
57   unhandled = []
58   for filename in headers:
59     filename = filename.strip()
60     if not (filename.endswith('.h') or filename.endswith('.hh')):
61       continue
62     basename = os.path.basename(filename)
63     print(filename)
64     cc = r'\b' + os.path.splitext(basename)[0] + r'\.(cc|cpp|mm)\b'
65     out, returncode = GitGrep('(/|")' + cc + '"')
66     if returncode != 0 or not out:
67       unhandled.append(filename)
68       continue
69
70     matches = ValidMatches(basename, cc, out.splitlines())
71
72     if len(matches) == 0:
73       continue
74     if len(matches) > 1:
75       print('\n[WARNING] Ambiguous matching for', filename)
76       for i in enumerate(matches, 1):
77         print('%d: %s' % (i[0], i[1]))
78       print()
79       if skip_ambiguous:
80         continue
81
82       picked = raw_input('Pick the matches ("2,3" for multiple): ')
83       try:
84         matches = [matches[int(i) - 1] for i in picked.split(',')]
85       except (ValueError, IndexError):
86         continue
87
88     for match in matches:
89       gnfile, linenr, new = match
90       print('  ', gnfile, linenr, new)
91       edits.setdefault(gnfile, {})[linenr] = new
92
93   for gnfile in edits:
94     lines = open(gnfile).read().splitlines()
95     for l in sorted(edits[gnfile].keys(), reverse=True):
96       lines.insert(l, edits[gnfile][l])
97     open(gnfile, 'w').write('\n'.join(lines) + '\n')
98
99   return unhandled
100
101
102 def AddHeadersToSources(headers, skip_ambiguous=True):
103   """Add header files to the sources list in the first GN file.
104
105   The target GN file is the first one up the parent directories.
106   This usually does the wrong thing for _test files if the test and the main
107   target are in the same .gn file.
108   When skip_ambiguous is True, skip if multiple sources arrays are found.
109
110   "git cl format" afterwards is required. Manually cleaning up duplicated items
111   is likely required.
112   """
113   for filename in headers:
114     filename = filename.strip()
115     print(filename)
116     dirname = os.path.dirname(filename)
117     while not os.path.exists(os.path.join(dirname, 'BUILD.gn')):
118       dirname = os.path.dirname(dirname)
119     rel = filename[len(dirname) + 1:]
120     gnfile = os.path.join(dirname, 'BUILD.gn')
121
122     lines = open(gnfile).read().splitlines()
123     matched = [i for i, l in enumerate(lines) if ' sources = [' in l]
124     if skip_ambiguous and len(matched) > 1:
125       print('[WARNING] Multiple sources in', gnfile)
126       continue
127
128     if len(matched) < 1:
129       continue
130     print('  ', gnfile, rel)
131     index = matched[0]
132     lines.insert(index + 1, '"%s",' % rel)
133     open(gnfile, 'w').write('\n'.join(lines) + '\n')
134
135
136 def RemoveHeader(headers, skip_ambiguous=True):
137   """Remove non-existing headers in GN files.
138
139   When skip_ambiguous is True, skip if multiple matches are found.
140   """
141   edits = {}
142   unhandled = []
143   for filename in headers:
144     filename = filename.strip()
145     if not (filename.endswith('.h') or filename.endswith('.hh')):
146       continue
147     basename = os.path.basename(filename)
148     print(filename)
149     out, returncode = GitGrep('(/|")' + basename + '"')
150     if returncode != 0 or not out:
151       unhandled.append(filename)
152       print('  Not found')
153       continue
154
155     grep_lines = out.splitlines()
156     matches = []
157     for line in grep_lines:
158       gnfile, linenr, contents = line.split(':')
159       print('    ', gnfile, linenr, contents)
160       linenr = int(linenr)
161       lines = open(gnfile).read().splitlines()
162       assert contents in lines[linenr - 1]
163       matches.append((gnfile, linenr, contents))
164
165     if len(matches) == 0:
166       continue
167     if len(matches) > 1:
168       print('\n[WARNING] Ambiguous matching for', filename)
169       for i in enumerate(matches, 1):
170         print('%d: %s' % (i[0], i[1]))
171       print()
172       if skip_ambiguous:
173         continue
174
175       picked = raw_input('Pick the matches ("2,3" for multiple): ')
176       try:
177         matches = [matches[int(i) - 1] for i in picked.split(',')]
178       except (ValueError, IndexError):
179         continue
180
181     for match in matches:
182       gnfile, linenr, contents = match
183       print('  ', gnfile, linenr, contents)
184       edits.setdefault(gnfile, set()).add(linenr)
185
186   for gnfile in edits:
187     lines = open(gnfile).read().splitlines()
188     for l in sorted(edits[gnfile], reverse=True):
189       lines.pop(l - 1)
190     open(gnfile, 'w').write('\n'.join(lines) + '\n')
191
192   return unhandled
193
194
195 def main():
196   parser = argparse.ArgumentParser()
197   parser.add_argument('input_file', help="missing or non-existing headers, "
198                       "output of check_gn_headers.py")
199   parser.add_argument('--prefix',
200                       help="only handle path name with this prefix")
201   parser.add_argument('--remove', action='store_true',
202                       help="treat input_file as non-existing headers")
203
204   args, _extras = parser.parse_known_args()
205
206   headers = open(args.input_file).readlines()
207
208   if args.prefix:
209     headers = [i for i in headers if i.startswith(args.prefix)]
210
211   if args.remove:
212     RemoveHeader(headers, False)
213   else:
214     unhandled = AddHeadersNextToCC(headers)
215     AddHeadersToSources(unhandled)
216
217
218 if __name__ == '__main__':
219   sys.exit(main())