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 argparse
7import json
8import os
9import re
10import shlex
11import sys
12
13script_dir = os.path.dirname(os.path.realpath(__file__))
14tool_dir = os.path.abspath(os.path.join(script_dir, '../pylib'))
15sys.path.insert(0, tool_dir)
16
17from clang import compile_db
18
19_PROBABLY_CLANG_RE = re.compile(r'clang(?:\+\+)?$')
20
21
22def ParseArgs():
23  parser = argparse.ArgumentParser(
24      description='Utility to build one Chromium file for debugging clang')
25  parser.add_argument('-p', default='.', help='path to the compile database')
26  parser.add_argument('--generate-compdb',
27                      help='regenerate the compile database')
28  parser.add_argument('--prefix',
29                      help='optional prefix to prepend, e.g. --prefix=lldb')
30  parser.add_argument(
31      '--compiler',
32      help='compiler to override the compiler specied in the compile db')
33  parser.add_argument('--suffix',
34                      help='optional suffix to append, e.g.' +
35                      ' --suffix="-Xclang -ast-dump -fsyntax-only"')
36  parser.add_argument('target_file', help='file to build')
37  return parser.parse_args()
38
39
40def BuildIt(record, prefix, compiler, suffix):
41  """Builds the file in the provided compile DB record.
42
43  Args:
44    prefix: Optional prefix to prepend to the build command.
45    compiler: Optional compiler to override the compiler specified the record.
46    suffix: Optional suffix to append to the build command.
47  """
48  raw_args = shlex.split(record['command'])
49  # The compile command might have some goop in front of it, e.g. if the build
50  # is using goma, so shift arguments off the front until raw_args[0] looks like
51  # a clang invocation.
52  while raw_args:
53    if _PROBABLY_CLANG_RE.search(raw_args[0]):
54      break
55    raw_args = raw_args[1:]
56  if not raw_args:
57    print 'error: command %s does not appear to invoke clang!' % record[
58        'command']
59    return 2
60  args = []
61  if prefix:
62    args.extend(shlex.split(prefix))
63  if compiler:
64    raw_args[0] = compiler
65  args.extend(raw_args)
66  if suffix:
67    args.extend(shlex.split(suffix))
68  print 'Running %s' % ' '.join(args)
69  os.execv(args[0], args)
70
71
72def main():
73  args = ParseArgs()
74  os.chdir(args.p)
75  if args.generate_compdb:
76    compile_db.GenerateWithNinja('.')
77  db = compile_db.Read('.')
78  for record in db:
79    if os.path.normpath(os.path.join(args.p, record[
80        'file'])) == args.target_file:
81      return BuildIt(record, args.prefix, args.compiler, args.suffix)
82  print 'error: could not find %s in compile DB!' % args.target_file
83  return 1
84
85
86if __name__ == '__main__':
87  sys.exit(main())
88