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