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""" 7Clang tools on Windows are still a bit busted. The tooling can't handle 8backslashes in paths, doesn't understand how to read .rsp files, etc. In 9addition, ninja generates compile commands prefixed with the ninja msvc helper, 10which also confuses clang. This script generates a compile DB that should mostly 11work until clang tooling can be improved upstream. 12""" 13 14import argparse 15import os 16import re 17import json 18import shlex 19import subprocess 20import sys 21 22 23_NINJA_MSVC_WRAPPER = re.compile('ninja -t msvc -e .+? -- ') 24_RSP_RE = re.compile(r' (@(.+?\.rsp)) ') 25 26 27def _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 63def 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 91if __name__ == '__main__': 92 sys.exit(main(sys.argv[1:])) 93