Home | History | Annotate | Download | only in clang
      1 #!/usr/bin/env python
      2 #
      3 #=- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python  -*-=#
      4 #
      5 #                     The LLVM Compiler Infrastructure
      6 #
      7 # This file is distributed under the University of Illinois Open Source
      8 # License. See LICENSE.TXT for details.
      9 #
     10 #===------------------------------------------------------------------------===#
     11 
     12 """
     13 Parallel find-all-symbols runner
     14 ================================
     15 
     16 Runs find-all-symbols over all files in a compilation database.
     17 
     18 Example invocations.
     19 - Run find-all-symbols on all files in the current working directory.
     20     run-find-all-symbols.py <source-file>
     21 
     22 Compilation database setup:
     23 http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
     24 """
     25 
     26 import argparse
     27 import json
     28 import multiprocessing
     29 import os
     30 import Queue
     31 import shutil
     32 import subprocess
     33 import sys
     34 import tempfile
     35 import threading
     36 
     37 
     38 def find_compilation_database(path):
     39   """Adjusts the directory until a compilation database is found."""
     40   result = './'
     41   while not os.path.isfile(os.path.join(result, path)):
     42     if os.path.realpath(result) == '/':
     43       print 'Error: could not find compilation database.'
     44       sys.exit(1)
     45     result += '../'
     46   return os.path.realpath(result)
     47 
     48 
     49 def MergeSymbols(directory, args):
     50   """Merge all symbol files (yaml) in a given directaory into a single file."""
     51   invocation = [args.binary, '-merge-dir='+directory, args.saving_path]
     52   subprocess.call(invocation)
     53   print 'Merge is finished. Saving results in ' + args.saving_path
     54 
     55 
     56 def run_find_all_symbols(args, tmpdir, build_path, queue):
     57   """Takes filenames out of queue and runs find-all-symbols on them."""
     58   while True:
     59     name = queue.get()
     60     invocation = [args.binary, name, '-output-dir='+tmpdir, '-p='+build_path]
     61     sys.stdout.write(' '.join(invocation) + '\n')
     62     subprocess.call(invocation)
     63     queue.task_done()
     64 
     65 
     66 def main():
     67   parser = argparse.ArgumentParser(description='Runs find-all-symbols over all'
     68                                    'files in a compilation database.')
     69   parser.add_argument('-binary', metavar='PATH',
     70                       default='./bin/find-all-symbols',
     71                       help='path to find-all-symbols binary')
     72   parser.add_argument('-j', type=int, default=0,
     73                       help='number of instances to be run in parallel.')
     74   parser.add_argument('-p', dest='build_path',
     75                       help='path used to read a compilation database.')
     76   parser.add_argument('-saving-path', default='./find_all_symbols_db.yaml',
     77                       help='result saving path')
     78   args = parser.parse_args()
     79 
     80   db_path = 'compile_commands.json'
     81 
     82   if args.build_path is not None:
     83     build_path = args.build_path
     84   else:
     85     build_path = find_compilation_database(db_path)
     86 
     87   tmpdir = tempfile.mkdtemp()
     88 
     89   # Load the database and extract all files.
     90   database = json.load(open(os.path.join(build_path, db_path)))
     91   files = [entry['file'] for entry in database]
     92 
     93   max_task = args.j
     94   if max_task == 0:
     95     max_task = multiprocessing.cpu_count()
     96 
     97   try:
     98     # Spin up a bunch of tidy-launching threads.
     99     queue = Queue.Queue(max_task)
    100     for _ in range(max_task):
    101       t = threading.Thread(target=run_find_all_symbols,
    102                            args=(args, tmpdir, build_path, queue))
    103       t.daemon = True
    104       t.start()
    105 
    106     # Fill the queue with files.
    107     for name in files:
    108       queue.put(name)
    109 
    110     # Wait for all threads to be done.
    111     queue.join()
    112 
    113     MergeSymbols(tmpdir, args)
    114 
    115 
    116   except KeyboardInterrupt:
    117     # This is a sad hack. Unfortunately subprocess goes
    118     # bonkers with ctrl-c and we start forking merrily.
    119     print '\nCtrl-C detected, goodbye.'
    120     os.kill(0, 9)
    121 
    122 
    123 if __name__ == '__main__':
    124   main()
    125