Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 # Copyright 2014 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """
      7 Clang tools on Windows are still a bit busted. The tooling can't handle
      8 backslashes in paths, doesn't understand how to read .rsp files, etc. In
      9 addition, ninja generates compile commands prefixed with the ninja msvc helper,
     10 which also confuses clang. This script generates a compile DB that should mostly
     11 work until clang tooling can be improved upstream.
     12 """
     13 
     14 import argparse
     15 import os
     16 import re
     17 import json
     18 import shlex
     19 import subprocess
     20 import sys
     21 
     22 
     23 _NINJA_MSVC_WRAPPER = re.compile('ninja -t msvc -e .+? -- ')
     24 _RSP_RE = re.compile(r' (@(.+?\.rsp)) ')
     25 
     26 
     27 def _ProcessEntry(e):
     28   # Strip off the ninja -t msvc wrapper.
     29   e['command'] = _NINJA_MSVC_WRAPPER.sub('', e['command'])
     30 
     31   # Prepend --driver-mode=cl to the command's arguments.
     32   # Escape backslashes so shlex doesn't try to interpret them.
     33   escaped_command = e['command'].replace('\\', '\\\\')
     34   split_command = shlex.split(escaped_command)
     35   e['command'] = ' '.join(
     36       split_command[:1] + ['--driver-mode=cl'] + split_command[1:])
     37 
     38   # Expand the contents of the response file, if any.
     39   # http://llvm.org/bugs/show_bug.cgi?id=21634
     40   try:
     41     match = _RSP_RE.search(e['command'])
     42     rsp_path = os.path.join(e['directory'], match.group(2))
     43     rsp_contents = file(rsp_path).read()
     44     e['command'] = ''.join([
     45         e['command'][:match.start(1)],
     46         rsp_contents,
     47         e['command'][match.end(1):]])
     48   except IOError:
     49     pass
     50 
     51   # TODO(dcheng): This should be implemented in Clang tooling.
     52   # http://llvm.org/bugs/show_bug.cgi?id=19687
     53   # Finally, use slashes instead of backslashes to avoid bad escaping by the
     54   # tooling. This should really only matter for command, but we do it for all
     55   # keys for consistency.
     56   e['directory'] = e['directory'].replace('\\', '/')
     57   e['command'] = e['command'].replace('\\', '/')
     58   e['file'] = e['file'].replace('\\', '/')
     59 
     60   return e
     61 
     62 
     63 def main(argv):
     64   # Parse argument
     65   parser = argparse.ArgumentParser()
     66   parser.add_argument(
     67       'build_path',
     68       nargs='?',
     69       help='Path to build directory',
     70       default='out/Debug')
     71   args = parser.parse_args()
     72   # First, generate the compile database.
     73   print 'Generating compile DB with ninja...'
     74   compile_db_as_json = subprocess.check_output(shlex.split(
     75       'ninja -C %s -t compdb cc cxx objc objcxx' % args.build_path))
     76 
     77   compile_db = json.loads(compile_db_as_json)
     78   print 'Read in %d entries from the compile db' % len(compile_db)
     79   compile_db = [_ProcessEntry(e) for e in compile_db]
     80   original_length = len(compile_db)
     81 
     82   # Filter out NaCl stuff. The clang tooling chokes on them.
     83   compile_db = [e for e in compile_db if '_nacl.cc.pdb' not in e['command']
     84       and '_nacl_win64.cc.pdb' not in e['command']]
     85   print 'Filtered out %d entries...' % (original_length - len(compile_db))
     86   f = file('%s/compile_commands.json' % args.build_path, 'w')
     87   f.write(json.dumps(compile_db, indent=2))
     88   print 'Done!'
     89 
     90 
     91 if __name__ == '__main__':
     92   sys.exit(main(sys.argv[1:]))
     93