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