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()