From 5bfb078cd0e7becfa37e6b847bd08d4bdc1a80fc Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 8 Sep 2025 09:24:52 +0200 Subject: [PATCH 1/8] Add code_checker_script.py to replace the inline code_checker bash wrapper --- src/BUILD | 5 +- src/code_checker_script.py | 113 +++++++++++++++++++++++++++++++++++++ src/per_file.bzl | 86 +++++++++------------------- 3 files changed, 143 insertions(+), 61 deletions(-) create mode 100644 src/code_checker_script.py diff --git a/src/BUILD b/src/BUILD index 8a2e5cdb..3d4f8c9e 100644 --- a/src/BUILD +++ b/src/BUILD @@ -7,7 +7,10 @@ py_binary( # Build & Test script template exports_files( - ["codechecker_script.py"], + [ + "codechecker_script.py", + "code_checker_script.py", + ], ) # The following are flags and default values for clang_tidy_aspect diff --git a/src/code_checker_script.py b/src/code_checker_script.py new file mode 100644 index 00000000..bfd5c234 --- /dev/null +++ b/src/code_checker_script.py @@ -0,0 +1,113 @@ +#!{PythonPath} + +# Copyright 2023 Ericsson AB +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import re +import shutil +import subprocess +import sys + +DATA_DIR: str = "{data_dir}" +ANALYZER_PLIST_PATHS: list[tuple[str, str]] = {analyzer_output_list} +LOG_FILE: str = "{log_file}" +COMPILE_COMMANDS_JSON: str = "{compile_commands_json}" +COMPILE_COMMANDS_ABSOLUTE: str = f"{COMPILE_COMMANDS_JSON}.abs" +CODECHECKER_ARGS: str = "{codechecker_args}" + + +def log(msg: str) -> None: + with open(LOG_FILE, "a") as log_file: + log_file.write(msg) + + +with open(COMPILE_COMMANDS_JSON, "r") as original_file, open( + COMPILE_COMMANDS_ABSOLUTE, "w" +) as new_file: + content = original_file.read() + # Replace '"directory":"."' with the absolute path + # of the current working directory + new_content = content.replace( + '"directory":".', f'"directory":"{os.getcwd()}' + ) + new_file.write(new_content) + +with open(LOG_FILE, "w") as log_file: + log_file.write( + f"CodeChecker command: CodeChecker analyze {CODECHECKER_ARGS} \ +{COMPILE_COMMANDS_ABSOLUTE}\n" + ) + log_file.write( + "===-----------------------------------------------------===\n" + ) + log_file.write( + " CodeChecker error log \n" + ) + log_file.write( + "===-----------------------------------------------------===\n" + ) + +result = subprocess.run( + ["echo", "$PATH"], + shell=True, + env=os.environ, + capture_output=True, + text=True, +) +log(result.stdout) + +codechecker_cmd: list[str] = ( + ["CodeChecker", "analyze"] + + CODECHECKER_ARGS.split() + + [COMPILE_COMMANDS_ABSOLUTE] +) + +try: + with open(LOG_FILE, "a") as log_file: + proc = subprocess.run( + codechecker_cmd, + env=os.environ, + stdout=log_file, + stderr=log_file, + check=True, + ) + ret_code = 0 +except subprocess.CalledProcessError as e: + ret_code = e.returncode + with open(LOG_FILE, "a") as log_file: + log_file.write(e.output.decode() if e.output else "") + +# Log and exit on error +if ret_code == 1 or ret_code >= 128: + print("===-----------------------------------------------------===") + print(f"[ERROR]: CodeChecker returned with {ret_code}!") + with open(LOG_FILE, "r") as log_file: + print(log_file.read()) + sys.exit(1) + +# NOTE: the following we do to get rid of md5 hash in plist file names +# Copy the plist files to the specified destinations +for file in os.listdir(DATA_DIR): + for analyzer_info in ANALYZER_PLIST_PATHS: + if re.search( + rf"_{analyzer_info[0]}_.*\.plist$", file + ) and os.path.isfile(os.path.join(DATA_DIR, file)): + shutil.copy(os.path.join(DATA_DIR, file), analyzer_info[1]) + +# I have conserved this comment from the original bash script +# The sed commands are commented out, so we won't implement them +# # sed -i -e "s|.*execroot/bazel_codechecker/||g" $CLANG_TIDY_PLIST +# # sed -i -e "s|.*execroot/bazel_codechecker/||g" $CLANGSA_PLIST diff --git a/src/per_file.bzl b/src/per_file.bzl index cfb76621..f94413c0 100644 --- a/src/per_file.bzl +++ b/src/per_file.bzl @@ -4,43 +4,6 @@ load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") -CODE_CHECKER_WRAPPER_SCRIPT = """#!/usr/bin/env bash -#set -x -DATA_DIR=$1 -shift -CLANG_TIDY_PLIST=$1 -shift -CLANGSA_PLIST=$1 -shift -LOG_FILE=$1 -shift -COMPILE_COMMANDS_JSON=$1 -shift -COMPILE_COMMANDS_ABS=$COMPILE_COMMANDS_JSON.abs -sed 's|"directory":"."|"directory":"'$(pwd)'"|g' $COMPILE_COMMANDS_JSON > $COMPILE_COMMANDS_ABS -echo "CodeChecker command: $@" $COMPILE_COMMANDS_ABS > $LOG_FILE -echo "===-----------------------------------------------------===" >> $LOG_FILE -echo " CodeChecker error log " >> $LOG_FILE -echo "===-----------------------------------------------------===" >> $LOG_FILE -eval "$@" $COMPILE_COMMANDS_ABS >> $LOG_FILE 2>&1 -# ls -la $DATA_DIR -# NOTE: the following we do to get rid of md5 hash in plist file names -ret_code=$? -echo "===-----------------------------------------------------===" >> $LOG_FILE -if [ $ret_code -eq 1 ] || [ $ret_code -ge 128 ]; then - echo "===-----------------------------------------------------===" - echo "[ERROR]: CodeChecker returned with $ret_code!" - cat $LOG_FILE - exit 1 -fi -cp $DATA_DIR/*_clang-tidy_*.plist $CLANG_TIDY_PLIST -cp $DATA_DIR/*_clangsa_*.plist $CLANGSA_PLIST - -# sed -i -e "s|.*execroot/bazel_codechecker/||g" $CLANG_TIDY_PLIST -# sed -i -e "s|.*execroot/bazel_codechecker/||g" $CLANGSA_PLIST - -""" - def _run_code_checker( ctx, src, @@ -71,35 +34,31 @@ def _run_code_checker( outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] - # Create CodeChecker wrapper script - wrapper = ctx.actions.declare_file(ctx.attr.name + "/code_checker.sh") - ctx.actions.write( - output = wrapper, + codechecker_options = "" + for item in options: + codechecker_options += item + " " + codechecker_options += "--output=" + data_dir + " " + codechecker_options += "--file=*/" + src.path + + ctx.actions.expand_template( + template = ctx.file._code_checker_script_template, + output = ctx.outputs.code_checker_script, is_executable = True, - content = CODE_CHECKER_WRAPPER_SCRIPT, + substitutions = { + "{PythonPath}": ctx.attr._python_runtime[PyRuntimeInfo].interpreter_path, + "{data_dir}": data_dir, + "{log_file}": codechecker_log.path, + "{compile_commands_json}": compile_commands_json.path, + "{codechecker_args}": codechecker_options, + "{analyzer_output_list}": str(analyzer_output_paths), + }, ) - # Prepare arguments - args = ctx.actions.args() - - # NOTE: we pass: data dir, PList and log file names as first 4 arguments - args.add(data_dir) - args.add(clang_tidy_plist.path) - args.add(clangsa_plist.path) - args.add(codechecker_log.path) - args.add(compile_commands_json.path) - args.add("CodeChecker") - args.add("analyze") - args.add_all(options) - args.add("--output=" + data_dir) - args.add("--file=*/" + src.path) - # Action to run CodeChecker for a file ctx.actions.run( inputs = inputs, outputs = outputs, - executable = wrapper, - arguments = [args], + executable = ctx.outputs.code_checker_script, mnemonic = "CodeChecker", use_default_shell_env = True, progress_message = "CodeChecker analyze {}".format(src.short_path), @@ -125,7 +84,6 @@ def check_valid_file_type(src): return False def _rule_sources(ctx): - srcs = [] if hasattr(ctx.rule.attr, "srcs"): for src in ctx.rule.attr.srcs: @@ -384,9 +342,17 @@ per_file_test = rule( ], doc = "List of compilable targets which should be checked.", ), + "_code_checker_script_template": attr.label( + default = ":code_checker_script.py", + allow_single_file = True, + ), + "_python_runtime": attr.label( + default = "@default_python_tools//:py3_runtime", + ), }, outputs = { "test_script": "%{name}/test_script.sh", + "code_checker_script": "%{name}/code_checker_script.py", }, test = True, ) From 7de4e318d0e79a0a9fe09be6cd1a66df7a2a39cc Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 8 Sep 2025 15:50:56 +0200 Subject: [PATCH 2/8] Expand template only once, pass variables as argument instead --- src/code_checker_script.py | 11 +++++++--- src/per_file.bzl | 43 +++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/code_checker_script.py b/src/code_checker_script.py index bfd5c234..02543e88 100644 --- a/src/code_checker_script.py +++ b/src/code_checker_script.py @@ -21,9 +21,12 @@ import subprocess import sys -DATA_DIR: str = "{data_dir}" -ANALYZER_PLIST_PATHS: list[tuple[str, str]] = {analyzer_output_list} -LOG_FILE: str = "{log_file}" +DATA_DIR: str = sys.argv[1] +FILE_PATH: str = sys.argv[2] +ANALYZER_PLIST_PATHS: list[list[str, str]] = [ + item.split(",") for item in sys.argv[4].split(";") +] +LOG_FILE: str = sys.argv[3] COMPILE_COMMANDS_JSON: str = "{compile_commands_json}" COMPILE_COMMANDS_ABSOLUTE: str = f"{COMPILE_COMMANDS_JSON}.abs" CODECHECKER_ARGS: str = "{codechecker_args}" @@ -72,6 +75,8 @@ def log(msg: str) -> None: codechecker_cmd: list[str] = ( ["CodeChecker", "analyze"] + CODECHECKER_ARGS.split() + + ["--output=" + DATA_DIR] + + ["--file=*/" + FILE_PATH] + [COMPILE_COMMANDS_ABSOLUTE] ) diff --git a/src/per_file.bzl b/src/per_file.bzl index f94413c0..6841da36 100644 --- a/src/per_file.bzl +++ b/src/per_file.bzl @@ -34,31 +34,20 @@ def _run_code_checker( outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] - codechecker_options = "" - for item in options: - codechecker_options += item + " " - codechecker_options += "--output=" + data_dir + " " - codechecker_options += "--file=*/" + src.path - - ctx.actions.expand_template( - template = ctx.file._code_checker_script_template, - output = ctx.outputs.code_checker_script, - is_executable = True, - substitutions = { - "{PythonPath}": ctx.attr._python_runtime[PyRuntimeInfo].interpreter_path, - "{data_dir}": data_dir, - "{log_file}": codechecker_log.path, - "{compile_commands_json}": compile_commands_json.path, - "{codechecker_args}": codechecker_options, - "{analyzer_output_list}": str(analyzer_output_paths), - }, - ) + analyzer_output_paths = "clangsa," + clangsa_plist.path + \ + ";clang-tidy," + clang_tidy_plist.path # Action to run CodeChecker for a file ctx.actions.run( inputs = inputs, outputs = outputs, executable = ctx.outputs.code_checker_script, + arguments = [ + data_dir, + src.path, + codechecker_log.path, + analyzer_output_paths + ], mnemonic = "CodeChecker", use_default_shell_env = True, progress_message = "CodeChecker analyze {}".format(src.short_path), @@ -272,11 +261,27 @@ def _collect_all_sources_and_headers(ctx): sources_and_headers = all_files + headers.to_list() return sources_and_headers +def _create_wrapper_script(ctx, options, compile_commands_json): + options_str = "" + for item in options: + options_str += item + " " + ctx.actions.expand_template( + template = ctx.file._code_checker_script_template, + output = ctx.outputs.code_checker_script, + is_executable = True, + substitutions = { + "{PythonPath}": ctx.attr._python_runtime[PyRuntimeInfo].interpreter_path, + "{compile_commands_json}": compile_commands_json.path, + "{codechecker_args}": options_str, + }, + ) + def _per_file_impl(ctx): compile_commands_json = _compile_commands_impl(ctx) sources_and_headers = _collect_all_sources_and_headers(ctx) options = ctx.attr.default_options + ctx.attr.options all_files = [compile_commands_json] + _create_wrapper_script(ctx, options, compile_commands_json) for target in ctx.attr.targets: if not CcInfo in target: continue From 4e9e24df7f88be05508ed2bc00fa8065070e60f3 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 8 Sep 2025 16:12:57 +0200 Subject: [PATCH 3/8] Beautify --- src/code_checker_script.py | 147 ++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 61 deletions(-) diff --git a/src/code_checker_script.py b/src/code_checker_script.py index 02543e88..4fb7a795 100644 --- a/src/code_checker_script.py +++ b/src/code_checker_script.py @@ -33,84 +33,109 @@ def log(msg: str) -> None: + """ + Append message to the log file + """ with open(LOG_FILE, "a") as log_file: log_file.write(msg) -with open(COMPILE_COMMANDS_JSON, "r") as original_file, open( - COMPILE_COMMANDS_ABSOLUTE, "w" -) as new_file: - content = original_file.read() - # Replace '"directory":"."' with the absolute path - # of the current working directory - new_content = content.replace( - '"directory":".', f'"directory":"{os.getcwd()}' - ) - new_file.write(new_content) +def _create_compile_commands_json_with_absolute_paths(): + """ + Modifies the paths in compile_commands.json to contain the absolute path + of the files. + """ + with open(COMPILE_COMMANDS_JSON, "r") as original_file, open( + COMPILE_COMMANDS_ABSOLUTE, "w" + ) as new_file: + content = original_file.read() + # Replace '"directory":"."' with the absolute path + # of the current working directory + new_content = content.replace( + '"directory":".', f'"directory":"{os.getcwd()}' + ) + new_file.write(new_content) -with open(LOG_FILE, "w") as log_file: - log_file.write( + +def _run_codechecker() -> None: + """ + Runs CodeChecker analyze + """ + log( f"CodeChecker command: CodeChecker analyze {CODECHECKER_ARGS} \ -{COMPILE_COMMANDS_ABSOLUTE}\n" - ) - log_file.write( - "===-----------------------------------------------------===\n" +{COMPILE_COMMANDS_ABSOLUTE} --output={DATA_DIR} --file=*/{FILE_PATH}\n" ) - log_file.write( - " CodeChecker error log \n" + log("===-----------------------------------------------------===\n") + log(" CodeChecker error log \n") + log("===-----------------------------------------------------===\n") + + result = subprocess.run( + ["echo", "$PATH"], + shell=True, + env=os.environ, + capture_output=True, + text=True, ) - log_file.write( - "===-----------------------------------------------------===\n" + log(result.stdout) + + codechecker_cmd: list[str] = ( + ["CodeChecker", "analyze"] + + CODECHECKER_ARGS.split() + + ["--output=" + DATA_DIR] + + ["--file=*/" + FILE_PATH] + + [COMPILE_COMMANDS_ABSOLUTE] ) -result = subprocess.run( - ["echo", "$PATH"], - shell=True, - env=os.environ, - capture_output=True, - text=True, -) -log(result.stdout) - -codechecker_cmd: list[str] = ( - ["CodeChecker", "analyze"] - + CODECHECKER_ARGS.split() - + ["--output=" + DATA_DIR] - + ["--file=*/" + FILE_PATH] - + [COMPILE_COMMANDS_ABSOLUTE] -) - -try: - with open(LOG_FILE, "a") as log_file: - proc = subprocess.run( - codechecker_cmd, - env=os.environ, - stdout=log_file, - stderr=log_file, - check=True, - ) - ret_code = 0 -except subprocess.CalledProcessError as e: - ret_code = e.returncode - with open(LOG_FILE, "a") as log_file: - log_file.write(e.output.decode() if e.output else "") + try: + with open(LOG_FILE, "a") as log_file: + subprocess.run( + codechecker_cmd, + env=os.environ, + stdout=log_file, + stderr=log_file, + check=True, + ) + except subprocess.CalledProcessError as e: + log(e.output.decode() if e.output else "") + if e.returncode == 1 or e.returncode >= 128: + _display_error(e.returncode) + -# Log and exit on error -if ret_code == 1 or ret_code >= 128: +def _display_error(ret_code: int) -> None: + """ + Display the log file, and exit with 1 + """ + # Log and exit on error print("===-----------------------------------------------------===") print(f"[ERROR]: CodeChecker returned with {ret_code}!") with open(LOG_FILE, "r") as log_file: print(log_file.read()) sys.exit(1) -# NOTE: the following we do to get rid of md5 hash in plist file names -# Copy the plist files to the specified destinations -for file in os.listdir(DATA_DIR): - for analyzer_info in ANALYZER_PLIST_PATHS: - if re.search( - rf"_{analyzer_info[0]}_.*\.plist$", file - ) and os.path.isfile(os.path.join(DATA_DIR, file)): - shutil.copy(os.path.join(DATA_DIR, file), analyzer_info[1]) + +def _move_plist_files(): + """ + Move the plist files from the temporary directory to their final destination + """ + # NOTE: the following we do to get rid of md5 hash in plist file names + # Copy the plist files to the specified destinations + for file in os.listdir(DATA_DIR): + for analyzer_info in ANALYZER_PLIST_PATHS: + if re.search( + rf"_{analyzer_info[0]}_.*\.plist$", file + ) and os.path.isfile(os.path.join(DATA_DIR, file)): + shutil.copy(os.path.join(DATA_DIR, file), analyzer_info[1]) + + +def main(): + _create_compile_commands_json_with_absolute_paths() + _run_codechecker() + _move_plist_files() + + +if __name__ == "__main__": + main() + # I have conserved this comment from the original bash script # The sed commands are commented out, so we won't implement them From 05f62ee9ec7fd7ad803d4f78895c1438c5fded12 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Wed, 10 Sep 2025 08:37:33 +0200 Subject: [PATCH 4/8] Fix incorrect type --- src/code_checker_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code_checker_script.py b/src/code_checker_script.py index 4fb7a795..83fa4238 100644 --- a/src/code_checker_script.py +++ b/src/code_checker_script.py @@ -23,7 +23,7 @@ DATA_DIR: str = sys.argv[1] FILE_PATH: str = sys.argv[2] -ANALYZER_PLIST_PATHS: list[list[str, str]] = [ +ANALYZER_PLIST_PATHS: list[list[str]] = [ item.split(",") for item in sys.argv[4].split(";") ] LOG_FILE: str = sys.argv[3] From 0847cd4409865be8feec2b82d854677b5556089b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 22 Sep 2025 09:57:11 +0200 Subject: [PATCH 5/8] Rename code_checker_script to per_file_script --- src/BUILD | 2 +- src/per_file.bzl | 12 ++++++------ src/{code_checker_script.py => per_file_script.py} | 0 3 files changed, 7 insertions(+), 7 deletions(-) rename src/{code_checker_script.py => per_file_script.py} (100%) diff --git a/src/BUILD b/src/BUILD index 3d4f8c9e..36c98a37 100644 --- a/src/BUILD +++ b/src/BUILD @@ -9,7 +9,7 @@ py_binary( exports_files( [ "codechecker_script.py", - "code_checker_script.py", + "per_file_script.py", ], ) diff --git a/src/per_file.bzl b/src/per_file.bzl index 6841da36..931dc449 100644 --- a/src/per_file.bzl +++ b/src/per_file.bzl @@ -41,7 +41,7 @@ def _run_code_checker( ctx.actions.run( inputs = inputs, outputs = outputs, - executable = ctx.outputs.code_checker_script, + executable = ctx.outputs.per_file_script, arguments = [ data_dir, src.path, @@ -266,8 +266,8 @@ def _create_wrapper_script(ctx, options, compile_commands_json): for item in options: options_str += item + " " ctx.actions.expand_template( - template = ctx.file._code_checker_script_template, - output = ctx.outputs.code_checker_script, + template = ctx.file._per_file_script_template, + output = ctx.outputs.per_file_script, is_executable = True, substitutions = { "{PythonPath}": ctx.attr._python_runtime[PyRuntimeInfo].interpreter_path, @@ -347,8 +347,8 @@ per_file_test = rule( ], doc = "List of compilable targets which should be checked.", ), - "_code_checker_script_template": attr.label( - default = ":code_checker_script.py", + "_per_file_script_template": attr.label( + default = ":per_file_script.py", allow_single_file = True, ), "_python_runtime": attr.label( @@ -357,7 +357,7 @@ per_file_test = rule( }, outputs = { "test_script": "%{name}/test_script.sh", - "code_checker_script": "%{name}/code_checker_script.py", + "per_file_script": "%{name}/per_file_script.py", }, test = True, ) diff --git a/src/code_checker_script.py b/src/per_file_script.py similarity index 100% rename from src/code_checker_script.py rename to src/per_file_script.py From bf07dfd644d6ae4823ac573cf4057e6c611ac435 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 6 Oct 2025 14:48:55 +0200 Subject: [PATCH 6/8] Change to move --- src/per_file_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/per_file_script.py b/src/per_file_script.py index 83fa4238..4163b4de 100644 --- a/src/per_file_script.py +++ b/src/per_file_script.py @@ -124,7 +124,7 @@ def _move_plist_files(): if re.search( rf"_{analyzer_info[0]}_.*\.plist$", file ) and os.path.isfile(os.path.join(DATA_DIR, file)): - shutil.copy(os.path.join(DATA_DIR, file), analyzer_info[1]) + shutil.move(os.path.join(DATA_DIR, file), analyzer_info[1]) def main(): From 6afcac899527db62dcbd06452ab4c80c546a34aa Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 13:46:06 +0200 Subject: [PATCH 7/8] make comments nicer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kristóf Umann --- src/per_file_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/per_file_script.py b/src/per_file_script.py index 4163b4de..1bd9f01e 100644 --- a/src/per_file_script.py +++ b/src/per_file_script.py @@ -49,7 +49,7 @@ def _create_compile_commands_json_with_absolute_paths(): COMPILE_COMMANDS_ABSOLUTE, "w" ) as new_file: content = original_file.read() - # Replace '"directory":"."' with the absolute path + # Replace "directory":"." with the absolute path # of the current working directory new_content = content.replace( '"directory":".', f'"directory":"{os.getcwd()}' From f988dd480c4f3817dcccd3bc45802af24f595cdb Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 13 Oct 2025 14:10:31 +0200 Subject: [PATCH 8/8] Move argument parsing to a separate function --- src/per_file_script.py | 54 ++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/per_file_script.py b/src/per_file_script.py index 1bd9f01e..46bd13b4 100644 --- a/src/per_file_script.py +++ b/src/per_file_script.py @@ -20,23 +20,44 @@ import shutil import subprocess import sys - -DATA_DIR: str = sys.argv[1] -FILE_PATH: str = sys.argv[2] -ANALYZER_PLIST_PATHS: list[list[str]] = [ - item.split(",") for item in sys.argv[4].split(";") -] -LOG_FILE: str = sys.argv[3] +from typing import Optional + +# The output directory for CodeChecker +DATA_DIR: Optional[str] = None +# The file to be analyzed +FILE_PATH: Optional[str] = None +# List of pairs of analyzers and their plist files +ANALYZER_PLIST_PATHS: Optional[list[list[str]]] = None +LOG_FILE: Optional[str] = None COMPILE_COMMANDS_JSON: str = "{compile_commands_json}" COMPILE_COMMANDS_ABSOLUTE: str = f"{COMPILE_COMMANDS_JSON}.abs" CODECHECKER_ARGS: str = "{codechecker_args}" +def parse_args(): + """ + Parse arguments that may change from file-to-file. + """ + if len(sys.argv) != 5: + print("Wrong amount of arguments") + sys.exit(1) + + global DATA_DIR + global FILE_PATH + global LOG_FILE + global ANALYZER_PLIST_PATHS + + DATA_DIR = sys.argv[1] + FILE_PATH = sys.argv[2] + LOG_FILE = sys.argv[3] + ANALYZER_PLIST_PATHS = [item.split(",") for item in sys.argv[4].split(";")] + + def log(msg: str) -> None: """ Append message to the log file """ - with open(LOG_FILE, "a") as log_file: + with open(LOG_FILE, "a") as log_file: # type: ignore log_file.write(msg) @@ -81,13 +102,13 @@ def _run_codechecker() -> None: codechecker_cmd: list[str] = ( ["CodeChecker", "analyze"] + CODECHECKER_ARGS.split() - + ["--output=" + DATA_DIR] - + ["--file=*/" + FILE_PATH] + + ["--output=" + DATA_DIR] # type: ignore + + ["--file=*/" + FILE_PATH] # type: ignore + [COMPILE_COMMANDS_ABSOLUTE] ) try: - with open(LOG_FILE, "a") as log_file: + with open(LOG_FILE, "a") as log_file: # type: ignore subprocess.run( codechecker_cmd, env=os.environ, @@ -108,7 +129,7 @@ def _display_error(ret_code: int) -> None: # Log and exit on error print("===-----------------------------------------------------===") print(f"[ERROR]: CodeChecker returned with {ret_code}!") - with open(LOG_FILE, "r") as log_file: + with open(LOG_FILE, "r") as log_file: # type: ignore print(log_file.read()) sys.exit(1) @@ -120,14 +141,17 @@ def _move_plist_files(): # NOTE: the following we do to get rid of md5 hash in plist file names # Copy the plist files to the specified destinations for file in os.listdir(DATA_DIR): - for analyzer_info in ANALYZER_PLIST_PATHS: + for analyzer_info in ANALYZER_PLIST_PATHS: # type: ignore if re.search( rf"_{analyzer_info[0]}_.*\.plist$", file - ) and os.path.isfile(os.path.join(DATA_DIR, file)): - shutil.move(os.path.join(DATA_DIR, file), analyzer_info[1]) + ) and os.path.isfile( + os.path.join(DATA_DIR, file) + ): # type: ignore + shutil.move(os.path.join(DATA_DIR, file), analyzer_info[1]) # type: ignore def main(): + parse_args() _create_compile_commands_json_with_absolute_paths() _run_codechecker() _move_plist_files()