1#!/usr/bin/env python 2# Copyright 2016 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 6import json 7import os 8import re 9import shlex 10import sys 11import subprocess 12 13 14_RSP_RE = re.compile(r' (@(.+?\.rsp)) ') 15_debugging = False 16 17 18def _ProcessEntry(entry): 19 """Transforms one entry in the compile database to be clang-tool friendly.""" 20 split_command = shlex.split(entry['command'], posix=(sys.platform != 'win32')) 21 22 # Drop gomacc.exe from the front, if present. 23 if split_command[0].endswith('gomacc.exe'): 24 split_command = split_command[1:] 25 # Insert --driver-mode=cl as the first argument. 26 split_command = split_command[:1] + ['--driver-mode=cl'] + split_command[1:] 27 entry['command'] = subprocess.list2cmdline(split_command) 28 29 # Expand the contents of the response file, if any. 30 # http://llvm.org/bugs/show_bug.cgi?id=21634 31 try: 32 match = _RSP_RE.search(entry['command']) 33 if match: 34 rsp_path = os.path.join(entry['directory'], match.group(2)) 35 rsp_contents = file(rsp_path).read() 36 entry['command'] = ''.join([ 37 entry['command'][:match.start(1)], 38 rsp_contents, 39 entry['command'][match.end(1):]]) 40 except IOError: 41 if _debugging: 42 print 'Couldn\'t read response file for %s' % entry['file'] 43 44 return entry 45 46 47def ProcessCompileDatabaseIfNeeded(compile_db): 48 """Make the compile db generated by ninja on Windows more clang-tool friendly. 49 50 Args: 51 compile_db: The compile database parsed as a Python dictionary. 52 53 Returns: 54 A postprocessed compile db that clang tooling can use. 55 """ 56 # TODO(dcheng): Ideally this would check target_os... but not sure there's an 57 # easy way to do that, and (for now) cross-compiles don't work without custom 58 # patches anyway. 59 if sys.platform != 'win32': 60 return compile_db 61 62 if _debugging > 0: 63 print 'Read in %d entries from the compile db' % len(compile_db) 64 compile_db = [_ProcessEntry(e) for e in compile_db] 65 original_length = len(compile_db) 66 67 # Filter out NaCl stuff. The clang tooling chokes on them. 68 # TODO(dcheng): This doesn't appear to do anything anymore, remove? 69 compile_db = [e for e in compile_db if '_nacl.cc.pdb' not in e['command'] 70 and '_nacl_win64.cc.pdb' not in e['command']] 71 if _debugging > 0: 72 print 'Filtered out %d entries...' % (original_length - len(compile_db)) 73 74 # TODO(dcheng): Also filter out multiple commands for the same file. Not sure 75 # how that happens, but apparently it's an issue on Windows. 76 return compile_db 77 78 79def GetNinjaPath(): 80 ninja_executable = 'ninja.exe' if sys.platform == 'win32' else 'ninja' 81 return os.path.join( 82 os.path.dirname(os.path.realpath(__file__)), 83 '..', '..', '..', '..', 'third_party', 'depot_tools', ninja_executable) 84 85 86# FIXME: This really should be a build target, rather than generated at runtime. 87def GenerateWithNinja(path, targets=[]): 88 """Generates a compile database using ninja. 89 90 Args: 91 path: The build directory to generate a compile database for. 92 targets: Additional targets to pass to ninja. 93 94 Returns: 95 List of the contents of the compile database. 96 """ 97 # TODO(dcheng): Ensure that clang is enabled somehow. 98 99 # First, generate the compile database. 100 json_compile_db = subprocess.check_output( 101 [GetNinjaPath(), '-C', path] + targets + 102 ['-t', 'compdb', 'cc', 'cxx', 'objc', 'objcxx']) 103 return json.loads(json_compile_db) 104 105 106def Read(path): 107 """Reads a compile database into memory. 108 109 Args: 110 path: Directory that contains the compile database. 111 """ 112 with open(os.path.join(path, 'compile_commands.json'), 'rb') as db: 113 return json.load(db) 114