File indexing completed on 2024-05-19 05:42:35
0001 #!/usr/bin/env python3 0002 0003 import distutils.spawn 0004 import sys 0005 import argparse 0006 import subprocess 0007 import os 0008 import sqlite3 0009 0010 from pathlib import Path 0011 import json 0012 0013 ################################## 0014 # Compare both databases 0015 ################################## 0016 def compare_database_results(): 0017 multi_db_sqlite = sqlite3.connect("multi.db"); 0018 single_db_sqlite = sqlite3.connect("single.db"); 0019 0020 multi_cur = multi_db_sqlite.cursor() 0021 single_cur = single_db_sqlite.cursor() 0022 0023 queries = [ 0024 "SELECT qualified_name FROM class_declaration", 0025 "SELECT qualified_name FROM field_declaration", 0026 "SELECT qualified_name FROM function_declaration", 0027 "SELECT qualified_name FROM method_declaration", 0028 "SELECT qualified_name FROM namespace_declaration", 0029 "SELECT qualified_name FROM source_component", 0030 "SELECT qualified_name FROM source_file", 0031 "SELECT qualified_name FROM source_package", 0032 "SELECT qualified_name FROM source_repository", 0033 "SELECT qualified_name FROM variable_declaration", 0034 ] 0035 0036 has_error = False 0037 for query in queries: 0038 single_names = [] 0039 multi_names = [] 0040 for row in multi_cur.execute(query): 0041 multi_names.append(row[0]) 0042 for row in single_cur.execute(query): 0043 single_names.append(row[0]) 0044 0045 single_names.sort() 0046 multi_names.sort() 0047 if (single_names != multi_names): 0048 single_names_set = set(single_names) 0049 multi_names_set = set(multi_names) 0050 0051 not_in_multi_db = single_names_set.difference(multi_names_set) 0052 not_in_single_db = multi_names_set.difference(single_names_set) 0053 0054 print("\n") 0055 print("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n") 0056 print(f"FAIL: {query}") 0057 print(f"Entities not in the single database:\n {not_in_single_db}") 0058 print(f"Entities not in the joined database:\n {not_in_multi_db}") 0059 has_error = True 0060 else: 0061 print(f"OK: {query} ") 0062 0063 if has_error: 0064 sys.exit(1) 0065 0066 ############################################################################ 0067 # Delete the possible stray files on the disk, to re-generate them. 0068 ############################################################################ 0069 def clean_temp_files(): 0070 if os.path.exists("single.db"): 0071 os.remove("single.db") 0072 if os.path.exists("multi.db"): 0073 os.remove("multi.db") 0074 0075 ############################################################################ 0076 # Generate the single database by re-scanning the entire codebase 0077 # using our multithreaded code 0078 ############################################################################ 0079 def parse_with_single_db(source_path, compile_commands_path, use_system_headers): 0080 cmd_str = f"codevis_create_codebase_db --source-path {source_path} --output single.db --compile-commands-json {compile_commands_path} --use-system-headers {use_system_headers} --replace --silent" 0081 result = subprocess.run(cmd_str, shell=True) 0082 if (result.returncode != 0): 0083 print(f"Trying to save database file {output}") 0084 print("Error running the script, see output") 0085 else: 0086 print("Generating db from multithreaded calls successfully") 0087 0088 ######################## 0089 # Argument Management # 0090 ######################## 0091 if __name__ == "__main__": 0092 parser = argparse.ArgumentParser(description='Launch create_codebase_db, one per compile_commands.json entry') 0093 0094 parser.add_argument('--compile-commands', help='The path of the compile commands file.') 0095 parser.add_argument('--source-path', help='the source path of the project)') 0096 parser.add_argument('--test-single-and-multi', help='does a single and a multipass, comparing the reuslts') 0097 0098 args = parser.parse_args() 0099 print(args.__dict__) 0100 0101 if args.compile_commands is None: 0102 print("--compile-commands is a required argument") 0103 if args.source_path is None: 0104 print("--source-path is a required argument") 0105 0106 if (args.compile_commands is None or args.source_path is None): 0107 sys.exit(1) 0108 0109 ####################### 0110 # Argument Validation 0111 ####################### 0112 compile_commands_path = Path(args.compile_commands) 0113 validate = True 0114 0115 if args.compile_commands.split('/')[-1] != "compile_commands.json": 0116 print("Compile commands json file must be a path ponting to a compile_commands.json") 0117 validate = False 0118 0119 if not compile_commands_path.is_file(): 0120 print("Point the path to an existing compile commands file.") 0121 validate = False 0122 source_path = Path(args.source_path) 0123 if not source_path.is_dir(): 0124 print("Point the source path to a directory containing reall") 0125 validate = False 0126 0127 found = distutils.spawn.find_executable("codevis_create_codebase_db") 0128 if not found: 0129 print("Please set your PATH environment to a place that has codevis_create_codebase_db") 0130 validate = False 0131 0132 if not validate: 0133 sys.exit() 0134 0135 ############################ 0136 # Script Logic Starts Here 0137 ############################ 0138 0139 cmd_str = f"codevis_create_codebase_db --query-system-headers" 0140 result = subprocess.run(cmd_str, shell=True) 0141 use_system_headers = "yes" if result.returncode == 1 else "no" 0142 0143 generated_files = [] 0144 resolved_path = compile_commands_path.parent.resolve() 0145 0146 with open(compile_commands_path) as cc_file: 0147 commands = json.load(cc_file) 0148 print(f"Looping through {len(commands)} compilation commands") 0149 0150 ############################################################################################ 0151 # First we iterate through all the compile commands an generate the db for each object file 0152 ############################################################################################ 0153 total = len(commands) 0154 curr = 1 0155 for command in commands: 0156 output = command["output"] + ".db" 0157 compile_command = command["command"] 0158 dict_str = '"' + str(command) + '"' 0159 dict_str = dict_str.replace("'", "\\\"") 0160 cmd_str = f"codevis_create_codebase_db --source-path {source_path} --output {output} --compile-command {dict_str} --use-system-headers {use_system_headers} --replace --silent" 0161 print(f"{curr} of {total}: Generating {output}") 0162 curr += 1 0163 generated_files.append(resolved_path / output) 0164 result = subprocess.run(cmd_str, shell=True, cwd=resolved_path) 0165 if (result.returncode != 0): 0166 print(f"Trying to save database file {output}") 0167 print("Error running the script, see output") 0168 sys.exit() 0169 0170 ############################################################################ 0171 # Then we iterate thorugh it again to merge them back into a single db file 0172 ############################################################################ 0173 database_str = "" 0174 for gen_file in generated_files: 0175 database_str += "--database " + str(resolved_path / gen_file) + " " 0176 0177 clean_temp_files() 0178 0179 ############################################################################ 0180 # Generate the single database from the multiple db parts 0181 ############################################################################ 0182 cmd_str = f"codevis_merge_databases {database_str} --output multi.db" 0183 subprocess.run(cmd_str, shell=True) 0184 print("Generating single db from merged databases successfully") 0185 0186 if (args.test_single_and_multi): 0187 parse_with_single_db(source_path, compile_commands_path, use_system_headers) 0188 compare_database_results()