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