tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

fix_gn_headers.py (6632B)


      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())