def check_assert_usage(source_files, lines_of_code): """ Check how many assertions are used in the code. :param source_files: The list of files to count assertions in. :param lines_of_code: The total lines of code. :return: The assertion score. """ print(strings.RUN_ASSERTION_CHECK_HEADER) assert_count = 0 for file in source_files: f = open(file, 'r', encoding='latin-1') file_lines = f.readlines() for line in file_lines: if assertion_used_in_code_line(line): assert_count += 1 f.close() assertion_rate = assert_count / lines_of_code detailled_result_string = strings.RESULT_ASSERTION_RATE_DETAILED.format(count=assert_count, loc=lines_of_code, rate=assertion_rate, percentage=100*assertion_rate) print(strings.RESULT_ASSERTION_RATE.format(assertion_rate, assert_count, lines_of_code)) util.write_into_file_string(strings.RESULTS_FILENAME_ASSERTION_CHECK, detailled_result_string) score = scoring.calculate_assertion_score(assertion_rate) scoring.print_score(score, 'Assertion') return score
def run_kwstyle(source_files, lines_of_code): """ Runs KWStyle. :param source_files: The list of source files to analyze. :param lines_of_code: The lines of pure code count. :return: The KWStyle score. """ print(strings.RUN_KWSTYLE_HEADER) softwipe_directory = os.path.dirname(os.path.realpath(__file__)) kwstyle_xml = os.path.join(softwipe_directory, 'KWStyle.xml') kwstyle_call = [TOOLS.KWSTYLE.exe_name, '-v', '-xml', kwstyle_xml] output = '' # KWStyle only works properly when specifying just one single input file. Thus, iterate and call KWStyle again # for each source file, each time appending to the result output. for source_file in source_files: cur_kwstyle_call = kwstyle_call[::] cur_kwstyle_call.append(source_file) try: output += subprocess.check_output(cur_kwstyle_call, universal_newlines=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: # Same as with the lizard call. KWStyle exits with status 1 by output += e.output # default. So catch that, ignore the exception, and keep the output of the command warning_count = get_kwstyle_warning_count_from_kwstyle_output(output) warning_rate = warning_count / lines_of_code print(strings.RESULT_KWSTYLE_WARNING_RATE.format(warning_rate, warning_count, lines_of_code)) util.write_into_file_string(strings.RESULTS_FILENAME_KWSTYLE, output) score = scoring.calculate_kwstyle_score(warning_rate) scoring.print_score(score, 'KWStyle') return score
def run_clang_tidy(source_files, lines_of_code, cpp): """ Runs clang-tidy. :param source_files: The list of source files to analyze. :param lines_of_code: The lines of pure code count. :param cpp: Whether C++ is used or not. True if C++, false if C. :return: The clang-tidy score. """ print(strings.RUN_CLANG_TIDY_HEADER) clang_tidy_call = [TOOLS.CLANG_TIDY.exe_name] clang_tidy_call.extend(source_files) # Create checks list clang_tidy_checks = strings.CLANG_TIDY_CHECKS_CPP if cpp else strings.CLANG_TIDY_CHECKS_C clang_tidy_call.append('-checks=' + clang_tidy_checks) try: output = subprocess.check_output(clang_tidy_call, universal_newlines=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: output = e.output # clang-tidy can exit with exit code 1 if there is no compilation database, which might be the case when # compiling with just clang. Thus, ignore the exception here. warning_lines = get_clang_tidy_warning_lines_from_clang_tidy_output(output) weighted_warning_count = get_weighted_clang_tidy_warning_count_from_clang_tidy_warning_lines(warning_lines) warning_rate = weighted_warning_count / lines_of_code print(strings.RESULT_WEIGHTED_CLANG_TIDY_WARNING_RATE.format(warning_rate, weighted_warning_count, lines_of_code)) beautified_warning_lines = beautify_clang_tidy_warning_lines(warning_lines) util.write_into_file_list(strings.RESULTS_FILENAME_CLANG_TIDY, beautified_warning_lines) score = scoring.calculate_clang_tidy_score(warning_rate) scoring.print_score(score, 'Clang-tidy') return score
def end_game(): #print "\nDone! Analysing game performance..." print "\n" perc = scoring.get_result(lines, result_lines) * 100.0 #print_result(perc) scoring.print_score(user_name, perc) km.print_funsies() time.sleep(25) cancel_game() return
def compile_and_execute_program_with_sanitizers(args, lines_of_code, program_dir_abs, cpp, excluded_paths, no_exec=False): """ Automatically compile and execute the program :param args: The "args" Namespace as returned from parse_arguments(). :param lines_of_code: The lines of pure code count. :param program_dir_abs: The absolute path to the root directory of the target program. :param cpp: Whether C++ is used or not. True if C++, False if C. :param excluded_paths: A tupel containing the paths to be excluded. :param no_exec: If True, skip execution of the program. :return The compiler + sanitizer score. """ compiler_flags = strings.COMPILER_WARNING_FLAGS if no_exec else strings.COMPILE_FLAGS if args.compileroptionsfile: options = open(args.compileroptionsfile[0], 'r').read().rstrip() compiler_flags += " " + options weighted_sum_of_compiler_warnings = compile_program( args, lines_of_code, cpp, compiler_flags, excluded_paths) if not no_exec: execute_file = args.executefile[0] if args.executefile else None weighted_sum_of_sanitizer_warnings = execute_program( program_dir_abs, execute_file, args.cmake, lines_of_code) else: weighted_sum_of_sanitizer_warnings = 0 print(strings.WARNING_PROGRAM_EXECUTION_SKIPPED) weighted_warning_rate = ( weighted_sum_of_compiler_warnings + weighted_sum_of_sanitizer_warnings) / lines_of_code score = scoring.calculate_compiler_and_sanitizer_score( weighted_warning_rate) scoring.print_score(score, 'Compiler + Sanitizer') return score
def main(): add_kwstyle_to_path_variable() # Allow the user to auto-install the dependencies by just running "./softwipe.py" without any arguments if len(sys.argv) == 1: automatic_tool_installation.check_if_all_required_tools_are_installed() args = parse_arguments() # Normal check for the dependencies if len(sys.argv) != 1: automatic_tool_installation.check_if_all_required_tools_are_installed() add_user_paths_to_path_variable(args) if not args.allow_running_as_root: warn_if_user_is_root() cpp = True if args.cpp else False program_dir_abs = os.path.abspath(args.programdir) exclude = args.exclude[0] if args.exclude else None excluded_paths = util.get_excluded_paths(program_dir_abs, exclude) source_files = util.find_all_source_files(program_dir_abs, excluded_paths) lines_of_code = util.count_lines_of_code(source_files) compiler_and_sanitizer_score = compile_and_execute_program_with_sanitizers( args, lines_of_code, program_dir_abs, cpp, excluded_paths, args.no_execution) assertion_score, cppcheck_score, clang_tidy_score, cyclomatic_complexity_score, warning_score, \ unique_score, kwstyle_score = static_analysis(source_files, lines_of_code, cpp) all_scores = [ compiler_and_sanitizer_score, assertion_score, cppcheck_score, clang_tidy_score, cyclomatic_complexity_score, warning_score, unique_score, kwstyle_score ] overall_score = scoring.average_score(all_scores) scoring.print_score(overall_score, 'Overall program')
def run_cppcheck(source_files, lines_of_code, cpp): """ Runs cppcheck. :param source_files: The list of source files to analyze. :param lines_of_code: The lines of pure code count. :param cpp: Whether we're using C++ or not. True if C++ is used, False if C is used. :return: The Cppcheck score. """ print(strings.RUN_CPPCHECK_HEADER) language = 'c++' if cpp else 'c' cppcheck_call = [TOOLS.CPPCHECK.exe_name, '--enable=all', '--force', '--language=' + language] cppcheck_call.extend(source_files) output = subprocess.check_output(cppcheck_call, universal_newlines=True, stderr=subprocess.STDOUT) warning_lines = get_cppcheck_warning_lines_from_cppcheck_output(output) cppcheck_output = output_classes.CppcheckOutput(warning_lines) weighted_cppcheck_rate = cppcheck_output.print_information(lines_of_code) util.write_into_file_list(strings.RESULTS_FILENAME_CPPCHECK, warning_lines) score = scoring.calculate_cppcheck_score(weighted_cppcheck_rate) scoring.print_score(score, 'Cppcheck') return score
def print_information_and_return_scores(self): print('Average cyclomatic complexity:', self.average_cyclomatic_complexity) cyclomatic_complexity_score = scoring.calculate_cyclomatic_complexity_score( self.average_cyclomatic_complexity) scoring.print_score(cyclomatic_complexity_score, 'Cyclomatic complexity') warning_rate = self.warning_count / self.function_count print( 'Lizard warning rate (~= rate of functions that are too complex):', strings.RATE_COUNT_TOTAL.format(warning_rate, self.warning_count, self.function_count)) warning_score = scoring.calculate_lizard_warning_score(warning_rate) scoring.print_score(warning_score, 'Lizard warning') print('Unique code rate:', self.unique_rate) unique_score = scoring.calculate_unique_score(self.unique_rate) scoring.print_score(unique_score, 'Unique (code duplication)') return cyclomatic_complexity_score, warning_score, unique_score
def main(): """ Main function: Runs compilation, static analysis and prints results. """ add_kwstyle_to_path_variable( ) # TODO: hopefully get a conda package for this sometime add_lizard_to_path_variable() # Allow the user to auto-install the dependencies by just running "./softwipe.py" without any arguments # Should not be needed if conda is used. TODO: maybe remove this if len(sys.argv) == 1: automatic_tool_installation.check_if_all_required_tools_are_installed() args = parse_arguments() for argument in sys.argv: print(argument, end=" ") print() # Normal check for the dependencies if len(sys.argv) != 1: automatic_tool_installation.check_if_all_required_tools_are_installed() # if args.use_infer: # add_infer_to_path_variable() add_user_paths_to_path_variable(args) if not args.allow_running_as_root: warn_if_user_is_root() use_cpp = args.cpp use_cmake = args.cmake use_make = args.make program_dir_abs = os.path.abspath(args.programdir) exclude = args.exclude[0] if args.exclude else None exclude_file = args.X[0] if args.X else None excluded_paths = util.get_excluded_paths(program_dir_abs, exclude, exclude_file) custom_asserts = args.custom_assert[0].split( ',') if args.custom_assert else None source_files = util.find_all_source_files(program_dir_abs, excluded_paths) lines_of_code = util.count_lines_of_code(source_files) analysis_tools = [ ] # TODO: maybe add valgrind at some point if we get its error counts normalized somehow all_scores = [] data = { "program_dir_abs": program_dir_abs, "args": args, "excluded_paths": excluded_paths, "use_cpp": use_cpp, "use_cmake": use_cmake, "use_make": use_make, "custom_asserts": custom_asserts, "source_files": source_files, "lines_of_code": lines_of_code, "executefile": args.executefile } if not args.exclude_compilation: compiler_and_sanitizer_score = compile_and_execute_program_with_sanitizers( args, lines_of_code, program_dir_abs, use_cpp, excluded_paths, args.no_execution) all_scores.append(compiler_and_sanitizer_score) if args.use_infer: # TODO: maybe completely remove Infer since it requires a lot of disk space add_infer_to_path_variable() analysis_tools.append(InferTool) if not args.exclude_assertions: analysis_tools.append(AssertionTool) if not args.exclude_clang_tidy: analysis_tools.append(ClangTidyTool) if not args.exclude_cppcheck: analysis_tools.append(CppcheckTool) if not args.exclude_lizard: analysis_tools.append(LizardTool) if not args.exclude_kwstyle: analysis_tools.append(KWStyleTool) analysis_tools.append(TestCountTool) for tool in analysis_tools: scores, log, success = tool.run(data) if success: print(log) all_scores.extend(scores) else: print("excluded {} from analysis\n".format(tool.name())) overall_score = scoring.average_score(all_scores) scoring.print_score(overall_score, 'Overall program absolute') if args.add_badge: add_badge_to_file(args.add_badge[0], overall_score) print("Added badge to file {}".format(args.add_badge[0]))