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