From dcc47a515b56a8a91a350389e994587dcbf423e2 Mon Sep 17 00:00:00 2001 From: bruntib Date: Tue, 3 Jan 2023 12:45:11 +0100 Subject: [PATCH] [refactor] Analyzer context as singleton `analyzer_context.Context` is a singleton class. Anywhere it is constructed, the same object is acquired. So there is no need to pass it as parameter through long function call chains, because this object can be constructed where it's needed. It has no cost, because the same instance is used once it was created. --- .../codechecker_analyzer/analysis_manager.py | 45 ++----- analyzer/codechecker_analyzer/analyzer.py | 60 ++------- .../codechecker_analyzer/analyzer_context.py | 20 ++- .../analyzers/analyzer_base.py | 18 ++- .../analyzers/analyzer_types.py | 77 +++++------ .../analyzers/clangsa/analyzer.py | 42 +++--- .../analyzers/clangsa/config_handler.py | 2 - .../analyzers/clangsa/ctu_autodetection.py | 2 +- .../analyzers/clangsa/ctu_manager.py | 12 +- .../analyzers/clangsa/ctu_triple_arch.py | 3 +- .../analyzers/clangsa/statistics.py | 2 +- .../analyzers/clangsa/version.py | 6 +- .../analyzers/clangtidy/analyzer.py | 31 ++--- .../analyzers/config_handler.py | 5 +- .../analyzers/cppcheck/analyzer.py | 38 +++--- .../analyzers/cppcheck/config_handler.py | 2 - .../analyzers/result_handler_base.py | 3 +- .../buildlog/build_manager.py | 4 +- .../buildlog/log_parser.py | 7 +- analyzer/codechecker_analyzer/cmd/analyze.py | 22 ++-- .../codechecker_analyzer/cmd/analyzers.py | 15 +-- analyzer/codechecker_analyzer/cmd/check.py | 12 +- analyzer/codechecker_analyzer/cmd/checkers.py | 64 ++------- analyzer/codechecker_analyzer/cmd/fixit.py | 3 +- analyzer/codechecker_analyzer/cmd/log.py | 3 - analyzer/codechecker_analyzer/env.py | 4 +- analyzer/codechecker_analyzer/host_check.py | 25 ++-- analyzer/codechecker_analyzer/makefile.py | 16 +-- .../pre_analysis_manager.py | 27 ++-- analyzer/tests/functional/__init__.py | 1 + .../ctu_failure/test_ctu_failure.py | 2 +- analyzer/tests/libtest/codechecker.py | 8 +- analyzer/tests/unit/__init__.py | 2 + analyzer/tests/unit/test_checker_handling.py | 123 +++++++----------- 34 files changed, 261 insertions(+), 445 deletions(-) diff --git a/analyzer/codechecker_analyzer/analysis_manager.py b/analyzer/codechecker_analyzer/analysis_manager.py index a12f538d53..41edb34d2e 100644 --- a/analyzer/codechecker_analyzer/analysis_manager.py +++ b/analyzer/codechecker_analyzer/analysis_manager.py @@ -12,7 +12,6 @@ import glob import multiprocessing import os -import pickle import shlex import shutil import signal @@ -25,7 +24,6 @@ import psutil -from codechecker_analyzer import env from codechecker_common.logger import get_logger from codechecker_statistics_collector.collectors.special_return_value import \ @@ -60,8 +58,7 @@ def print_analyzer_statistic_summary(metadata_analyzers, status, msg=None): LOG.info(" %s: %s", analyzer_type, res) -def worker_result_handler(results, metadata_tool, output_path, - analyzer_binaries): +def worker_result_handler(results, metadata_tool, output_path): """ Print the analysis summary. """ skipped_num = 0 reanalyzed_num = 0 @@ -169,7 +166,7 @@ def is_ctu_active(source_analyzer): source_analyzer.is_ctu_enabled() -def prepare_check(action, analyzer_config, output_dir, checker_labels, +def prepare_check(action, analyzer_config, output_dir, skip_handlers, statistics_data, disable_ctu=False): """ Construct the source analyzer and result handler. """ # Create a source analyzer. @@ -209,7 +206,6 @@ def prepare_check(action, analyzer_config, output_dir, checker_labels, # which only returns metadata, but can't process the results. rh = source_analyzer.construct_result_handler(action, output_dir, - checker_labels, skip_handlers) # NOTICE! @@ -488,10 +484,10 @@ def check(check_data): skiplist handler is None if no skip file was configured. """ - actions_map, action, context, analyzer_config, \ + actions_map, action, analyzer_config, \ output_dir, skip_handlers, quiet_output_on_stdout, \ capture_analysis_output, generate_reproducer, analysis_timeout, \ - analyzer_environment, ctu_reanalyze_on_failure, \ + ctu_reanalyze_on_failure, \ output_dirs, statistics_data = check_data failed_dir = output_dirs["failed"] @@ -509,7 +505,7 @@ def check(check_data): raise Exception("Analyzer configuration is missing.") source_analyzer, rh = prepare_check(action, analyzer_config, - output_dir, context.checker_labels, + output_dir, skip_handlers, statistics_data) reanalyzed = os.path.exists(rh.analyzer_result_file) @@ -545,20 +541,8 @@ def __create_timeout(analyzer_process): result_file_exists = os.path.exists(rh.analyzer_result_file) - # FIXME: cppcheck (and other analyzers) - # need the original env when invoked - # we should handle this in a generic way. - if "cppcheck" in os.path.basename(analyzer_cmd[0]): - original_env_file = os.environ.get( - 'CODECHECKER_ORIGINAL_BUILD_ENV') - if original_env_file: - with open(original_env_file, 'rb') as env_file: - analyzer_environment = \ - pickle.load(env_file, encoding='utf-8') - # Fills up the result handler with the analyzer information. - source_analyzer.analyze(analyzer_cmd, rh, analyzer_environment, - __create_timeout) + source_analyzer.analyze(analyzer_cmd, rh, __create_timeout) # If execution reaches this line, the analyzer process has quit. if timeout_cleanup[0](): @@ -652,7 +636,7 @@ def handle_analysis_result(success, zip_file=zip_file): # Try to reanalyze with CTU disabled. source_analyzer, rh = \ prepare_check(action, analyzer_config, - output_dir, context.checker_labels, + output_dir, skip_handlers, statistics_data, True) reanalyzed = os.path.exists(rh.analyzer_result_file) @@ -662,9 +646,7 @@ def handle_analysis_result(success, zip_file=zip_file): # Fills up the result handler with # the analyzer information. - source_analyzer.analyze(analyzer_cmd, - rh, - analyzer_environment) + source_analyzer.analyze(analyzer_cmd, rh) return_codes = rh.analyzer_returncode if rh.analyzer_returncode == 0: @@ -733,7 +715,7 @@ def skip_cpp(compile_actions, skip_handlers): return analyze, skip -def start_workers(actions_map, actions, context, analyzer_config_map, +def start_workers(actions_map, actions, analyzer_config_map, jobs, output_path, skip_handlers, metadata_tool, quiet_analyze, capture_analysis_output, generate_reproducer, timeout, ctu_reanalyze_on_failure, statistics_data, manager, @@ -791,13 +773,8 @@ def signal_handler(signum, frame): 'reproducer': reproducer_dir, 'ctu_connections': ctu_connections_dir} - # Construct analyzer env. - analyzer_environment = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - analyzed_actions = [(actions_map, build_action, - context, analyzer_config_map.get(build_action.analyzer_type), output_path, skip_handlers, @@ -805,7 +782,6 @@ def signal_handler(signum, frame): capture_analysis_output, generate_reproducer, timeout, - analyzer_environment, ctu_reanalyze_on_failure, output_dirs, statistics_data) @@ -829,8 +805,7 @@ def signal_handler(signum, frame): analyzed_actions, 1, callback=lambda results: worker_result_handler( - results, metadata_tool, output_path, - context.analyzer_binaries) + results, metadata_tool, output_path) ).get(timeout) pool.close() diff --git a/analyzer/codechecker_analyzer/analyzer.py b/analyzer/codechecker_analyzer/analyzer.py index eeac095295..3d065c1ad6 100644 --- a/analyzer/codechecker_analyzer/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzer.py @@ -13,10 +13,8 @@ from collections import defaultdict from multiprocessing.managers import SyncManager import os -import shlex import shutil import signal -import subprocess import time from codechecker_common.logger import get_logger @@ -26,7 +24,8 @@ from codechecker_statistics_collector.collectors.return_value import \ ReturnValueCollector -from . import analysis_manager, pre_analysis_manager, env, checkers +from . import analyzer_context, analysis_manager, pre_analysis_manager, \ + checkers from .analyzers import analyzer_types from .analyzers.config_handler import CheckerState from .analyzers.clangsa.analyzer import ClangSA @@ -69,41 +68,6 @@ def create_actions_map(actions, manager): return result -def __get_analyzer_version(context, analyzer_config_map): - """ - Get the path and the version of the analyzer binaries. - """ - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - - # Get the analyzer binaries from the config_map which - # contains only the checked and available analyzers. - versions = {} - for _, analyzer_cfg in analyzer_config_map.items(): - analyzer_bin = analyzer_cfg.analyzer_binary - version = [analyzer_bin, ' --version'] - try: - output = subprocess.check_output( - shlex.split( - ' '.join(version)), - env=check_env, - universal_newlines=True, - encoding="utf-8", - errors="ignore") - versions[analyzer_bin] = output - except subprocess.CalledProcessError as oerr: - LOG.warning("Failed to get analyzer version: %s", - ' '.join(version)) - LOG.warning(oerr.output) - LOG.warning(oerr.stderr) - except OSError as oerr: - LOG.warning("Failed to get analyzer version: %s", - ' '.join(version)) - LOG.warning(oerr.strerror) - - return versions - - def __mgr_init(): """ This function is set for the SyncManager object which handles shared data @@ -159,7 +123,7 @@ def __get_ctu_data(config_map, ctu_dir): 'ctu_temp_fnmap_folder': 'tmpExternalFnMaps'} -def perform_analysis(args, skip_handlers, context, actions, metadata_tool, +def perform_analysis(args, skip_handlers, actions, metadata_tool, compile_cmd_count): """ Perform static analysis via the given (or if not, all) analyzers, @@ -167,6 +131,8 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, Additionally, insert statistical information into the metadata dict. """ + context = analyzer_context.get_context() + ctu_reanalyze_on_failure = 'ctu_reanalyze_on_failure' in args and \ args.ctu_reanalyze_on_failure if ctu_reanalyze_on_failure: @@ -176,8 +142,7 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, analyzers = args.analyzers if 'analyzers' in args \ else analyzer_types.supported_analyzers - analyzers, errored = analyzer_types.check_supported_analyzers( - analyzers, context) + analyzers, errored = analyzer_types.check_supported_analyzers(analyzers) analyzer_types.check_available_analyzers(analyzers, errored) ctu_collect = False @@ -199,7 +164,7 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, return actions = prepare_actions(actions, analyzers) - config_map = analyzer_types.build_config_handlers(args, context, analyzers) + config_map = analyzer_types.build_config_handlers(args, analyzers) available_checkers = set() # Add profile names to the checkers list so we will not warn @@ -241,9 +206,6 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, config_map[ClangSA.ANALYZER_NAME].set_checker_enabled( ReturnValueCollector.checker_collect, False) - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - enabled_checkers = defaultdict(list) # Save some metadata information. @@ -264,7 +226,8 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, if state == CheckerState.enabled: enabled_checkers[analyzer].append(check) - version = config_map[analyzer].get_version(check_env) + # TODO: cppcheck may require a different environment than clang. + version = config_map[analyzer].get_version(context.analyzer_env) metadata_info['analyzer_statistics']['version'] = version metadata_tool['analyzers'][analyzer] = metadata_info @@ -280,7 +243,7 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, ctu_data = __get_ctu_data(config_map, ctu_dir) makefile_creator = MakeFileCreator(analyzers, args.output_path, - config_map, context, skip_handlers, + config_map, skip_handlers, ctu_collect, statistics_data, ctu_data) makefile_creator.create(actions) @@ -326,7 +289,6 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, if clangsa_config is not None: pre_analysis_manager.run_pre_analysis(pre_analyze, - context, clangsa_config, args.jobs, pre_anal_skip_handlers, @@ -346,7 +308,7 @@ def perform_analysis(args, skip_handlers, context, actions, metadata_tool, if ctu_analyze or statistics_data or (not ctu_analyze and not ctu_collect): LOG.info("Starting static analysis ...") - analysis_manager.start_workers(actions_map, actions, context, + analysis_manager.start_workers(actions_map, actions, config_map, args.jobs, args.output_path, skip_handlers, diff --git a/analyzer/codechecker_analyzer/analyzer_context.py b/analyzer/codechecker_analyzer/analyzer_context.py index b40ecc839a..14a50ccbd2 100644 --- a/analyzer/codechecker_analyzer/analyzer_context.py +++ b/analyzer/codechecker_analyzer/analyzer_context.py @@ -28,7 +28,14 @@ # ----------------------------------------------------------------------------- class Context(metaclass=Singleton): - """ Generic package specific context. """ + """ + Generic package specific context. + + This class is to query all information that might be important about the + running environment around CodeChecker analysis. This is a singleton + object, so it is cheap to construct and can be used as a read-only + dictionary of the data on its interface. + """ def __init__(self): """ Initialize analyzer context. """ @@ -54,6 +61,7 @@ def __init__(self): self.__package_build_date = None self.__package_git_hash = None self.__analyzers = {} + self.__analyzer_env = None self.logger_lib_dir_path = os.path.join( self._data_files_dir_path, 'ld_logger', 'lib') @@ -146,8 +154,7 @@ def __populate_analyzers(self): analyzer_env = None analyzer_from_path = env.is_analyzer_from_path() if not analyzer_from_path: - analyzer_env = env.extend(self.path_env_extra, - self.ld_lib_path_extra) + analyzer_env = self.analyzer_env compiler_binaries = self.pckg_layout.get('analyzers') for name, value in compiler_binaries.items(): @@ -257,6 +264,13 @@ def ld_lib_path_extra(self): ld_paths.append(os.path.join(self._data_files_dir_path, path)) return ld_paths + @property + def analyzer_env(self): + if not self.__analyzer_env: + self.__analyzer_env = \ + env.extend(self.path_env_extra, self.ld_lib_path_extra) + return self.__analyzer_env + @property def analyzer_binaries(self): return self.__analyzers diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py index ac5f153621..dc24d5878d 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py @@ -17,6 +17,7 @@ import sys import shlex +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger LOG = get_logger('analyzer') @@ -63,7 +64,7 @@ def version_compatible(cls, configured_binary, environ): raise NotImplementedError("Subclasses should implement this!") @classmethod - def construct_config_handler(cls, args, context): + def construct_config_handler(cls, args): """ Should return a subclass of AnalyzerConfigHandler.""" raise NotImplementedError("Subclasses should implement this!") @@ -78,7 +79,7 @@ def get_analyzer_mentioned_files(self, output): @abstractmethod def construct_result_handler(self, buildaction, report_output, - checker_labels, skiplist_handler): + skiplist_handler): """ This method constructs the class that is responsible to handle the results of the analysis. The result should be a subclass of @@ -86,7 +87,7 @@ def construct_result_handler(self, buildaction, report_output, """ raise NotImplementedError("Subclasses should implement this!") - def analyze(self, analyzer_cmd, res_handler, env=None, proc_callback=None): + def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None): """ Run the analyzer. """ @@ -99,9 +100,9 @@ def analyze(self, analyzer_cmd, res_handler, env=None, proc_callback=None): try: ret_code, stdout, stderr \ = SourceAnalyzer.run_proc(analyzer_cmd, - env, res_handler.buildaction.directory, - proc_callback) + proc_callback, + env) res_handler.analyzer_returncode = ret_code res_handler.analyzer_stdout = stdout res_handler.analyzer_stderr = stderr @@ -113,7 +114,7 @@ def analyze(self, analyzer_cmd, res_handler, env=None, proc_callback=None): return res_handler @classmethod - def get_analyzer_checkers(cls, cfg_handler, environ): + def get_analyzer_checkers(cls, cfg_handler): """ Return the checkers available in the analyzer. """ @@ -126,7 +127,7 @@ def post_analyze(self, result_handler): pass @staticmethod - def run_proc(command, env=None, cwd=None, proc_callback=None): + def run_proc(command, cwd=None, proc_callback=None, env=None): """ Just run the given command and return the return code and the stdout and stderr outputs of the process. @@ -142,6 +143,9 @@ def signal_handler(signum, frame): signal.signal(signal.SIGINT, signal_handler) + if env is None: + env = analyzer_context.get_context().analyzer_env + proc = subprocess.Popen( command, bufsize=-1, diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py index 2f0caa9685..bc0e9dae78 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py @@ -16,7 +16,7 @@ import subprocess import sys -from codechecker_analyzer import env +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger from .. import host_check @@ -32,45 +32,38 @@ Cppcheck.ANALYZER_NAME: Cppcheck} -def is_ctu_capable(context): +def is_ctu_capable(): """ Detects if the current clang is CTU compatible. """ - enabled_analyzers, _ = \ - check_supported_analyzers([ClangSA.ANALYZER_NAME], context) + enabled_analyzers, _ = check_supported_analyzers([ClangSA.ANALYZER_NAME]) if not enabled_analyzers: return False - clangsa_cfg = ClangSA.construct_config_handler([], context) + clangsa_cfg = ClangSA.construct_config_handler([]) return clangsa_cfg.ctu_capability.is_ctu_capable -def is_ctu_on_demand_available(context): +def is_ctu_on_demand_available(): """ Detects if the current clang is capable of on-demand AST loading. """ - enabled_analyzers, _ = \ - check_supported_analyzers([ClangSA.ANALYZER_NAME], context) + enabled_analyzers, _ = check_supported_analyzers([ClangSA.ANALYZER_NAME]) if not enabled_analyzers: return False - clangsa_cfg = ClangSA.construct_config_handler([], context) + clangsa_cfg = ClangSA.construct_config_handler([]) return clangsa_cfg.ctu_capability.is_on_demand_ctu_available -def is_statistics_capable(context): +def is_statistics_capable(): """ Detects if the current clang is Statistics compatible. """ # Resolve potentially missing binaries. - enabled_analyzers, _ = \ - check_supported_analyzers([ClangSA.ANALYZER_NAME], context) + enabled_analyzers, _ = check_supported_analyzers([ClangSA.ANALYZER_NAME]) if not enabled_analyzers: return False - clangsa_cfg = ClangSA.construct_config_handler([], context) + clangsa_cfg = ClangSA.construct_config_handler([]) - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - - checkers = ClangSA.get_analyzer_checkers( - clangsa_cfg, check_env, True, True) + checkers = ClangSA.get_analyzer_checkers(clangsa_cfg, True, True) stat_checkers_pattern = re.compile(r'.+statisticscollector.+') @@ -81,55 +74,46 @@ def is_statistics_capable(context): return False -def is_z3_capable(context): +def is_z3_capable(): """ Detects if the current clang is Z3 compatible. """ - enabled_analyzers, _ = \ - check_supported_analyzers([ClangSA.ANALYZER_NAME], context) + enabled_analyzers, _ = check_supported_analyzers([ClangSA.ANALYZER_NAME]) if not enabled_analyzers: return False - analyzer_binary = context.analyzer_binaries.get(ClangSA.ANALYZER_NAME) - - analyzer_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) + analyzer_binary = analyzer_context.get_context() \ + .analyzer_binaries.get(ClangSA.ANALYZER_NAME) return host_check.has_analyzer_option(analyzer_binary, ['-Xclang', - '-analyzer-constraints=z3'], - analyzer_env) + '-analyzer-constraints=z3']) -def is_z3_refutation_capable(context): +def is_z3_refutation_capable(): """ Detects if the current clang is Z3 refutation compatible. """ # This function basically checks whether the corresponding analyzer config # option exists i.e. it is visible on analyzer config option help page. # However, it doesn't mean that Clang itself is compiled with Z3. - if not is_z3_capable(context): + if not is_z3_capable(): return False - check_supported_analyzers([ClangSA.ANALYZER_NAME], context) - analyzer_binary = context.analyzer_binaries.get(ClangSA.ANALYZER_NAME) - - analyzer_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) + check_supported_analyzers([ClangSA.ANALYZER_NAME]) + analyzer_binary = analyzer_context.get_context() \ + .analyzer_binaries.get(ClangSA.ANALYZER_NAME) return host_check.has_analyzer_config_option(analyzer_binary, - 'crosscheck-with-z3', - analyzer_env) + 'crosscheck-with-z3') -def is_ignore_conflict_supported(context): +def is_ignore_conflict_supported(): """ Detects if clang-apply-replacements supports --ignore-insert-conflict flag. """ - analyzer_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - + context = analyzer_context.get_context() proc = subprocess.Popen([context.replacer_binary, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=analyzer_env, + env=context.analyzer_env, encoding="utf-8", errors="ignore") out, _ = proc.communicate() return '--ignore-insert-conflict' in out @@ -156,7 +140,7 @@ def check_available_analyzers(analyzers, errored): sys.exit(1) -def check_supported_analyzers(analyzers, context): +def check_supported_analyzers(analyzers): """ Checks the given analyzers in the current context for their executability and support in CodeChecker. @@ -168,8 +152,8 @@ def check_supported_analyzers(analyzers, context): and failed is a list of (analyzer, reason) tuple. """ - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) + context = analyzer_context.get_context() + check_env = context.analyzer_env analyzer_binaries = context.analyzer_binaries @@ -249,7 +233,7 @@ def construct_analyzer(buildaction, return None -def build_config_handlers(args, context, enabled_analyzers): +def build_config_handlers(args, enabled_analyzers): """ Handle config from command line or from config file if no command line config is given. @@ -261,8 +245,7 @@ def build_config_handlers(args, context, enabled_analyzers): analyzer_config_map = {} for ea in enabled_analyzers: - config_handler = supported_analyzers[ea].\ - construct_config_handler(args, context) + config_handler = supported_analyzers[ea].construct_config_handler(args) analyzer_config_map[ea] = config_handler return analyzer_config_map diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py index bd779f5f93..7b087960d6 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py @@ -15,11 +15,11 @@ import shlex import subprocess -from typing import Dict, List +from typing import List from codechecker_common.logger import get_logger -from codechecker_analyzer import env +from codechecker_analyzer import analyzer_context, env from .. import analyzer_base from ..config_handler import CheckerState @@ -37,8 +37,7 @@ def parse_clang_help_page( command: List[str], - start_label: str, - environ: Dict[str, str] + start_label: str ) -> List[str]: """ Parse the clang help page starting from a specific label. @@ -48,7 +47,7 @@ def parse_clang_help_page( help_page = subprocess.check_output( command, stderr=subprocess.STDOUT, - env=environ, + env=analyzer_context.get_context().analyzer_env, universal_newlines=True, encoding="utf-8", errors="ignore") @@ -145,7 +144,6 @@ def add_checker_config(self, checker_cfg): def get_analyzer_checkers( cls, cfg_handler: config_handler.ClangSAConfigHandler, - environ: Dict[str, str], alpha: bool = True, debug: bool = False ) -> List[str]: @@ -154,30 +152,28 @@ def get_analyzer_checkers( cfg_handler, alpha=alpha, debug=debug) - return parse_clang_help_page(checker_list_args, 'CHECKERS:', environ) + return parse_clang_help_page(checker_list_args, 'CHECKERS:') @classmethod def get_checker_config( cls, - cfg_handler: config_handler.ClangSAConfigHandler, - environ: Dict[str, str] + cfg_handler: config_handler.ClangSAConfigHandler ) -> List[str]: """Return the list of checker config options.""" checker_config_args = clang_options.get_checker_config_cmd( cfg_handler, alpha=True) - return parse_clang_help_page(checker_config_args, 'OPTIONS:', environ) + return parse_clang_help_page(checker_config_args, 'OPTIONS:') @classmethod def get_analyzer_config( cls, - cfg_handler: config_handler.ClangSAConfigHandler, - environ: Dict[str, str] + cfg_handler: config_handler.ClangSAConfigHandler ) -> List[str]: """Return the list of analyzer config options.""" analyzer_config_args = clang_options.get_analyzer_config_cmd( cfg_handler) - return parse_clang_help_page(analyzer_config_args, 'OPTIONS:', environ) + return parse_clang_help_page(analyzer_config_args, 'OPTIONS:') def construct_analyzer_cmd(self, result_handler): """ @@ -316,11 +312,9 @@ def get_ctu_dir(self): Returns the path of the ctu directory (containing the triple). """ config = self.config_handler - environ = env.extend(config.path_env_extra, - config.ld_lib_path_extra) triple_arch = ctu_triple_arch.get_triple_arch(self.buildaction, self.source_file, - config, environ) + config) ctu_dir = os.path.join(config.ctu_dir, triple_arch) return ctu_dir @@ -420,29 +414,28 @@ def version_compatible(cls, configured_binary, environ): return True def construct_result_handler(self, buildaction, report_output, - checker_labels, skiplist_handler): + skiplist_handler): """ See base class for docs. """ res_handler = ClangSAResultHandler(buildaction, report_output, self.config_handler.report_hash) - res_handler.checker_labels = checker_labels res_handler.skiplist_handler = skiplist_handler return res_handler @classmethod - def construct_config_handler(cls, args, context): + def construct_config_handler(cls, args): - environ = env.extend(context.path_env_extra, - context.ld_lib_path_extra) + context = analyzer_context.get_context() + environ = context.analyzer_env handler = config_handler.ClangSAConfigHandler(environ) handler.analyzer_plugins_dir = context.checker_plugin handler.analyzer_binary = context.analyzer_binaries.get( cls.ANALYZER_NAME) - handler.version_info = version.get(handler.analyzer_binary, environ) + handler.version_info = version.get(handler.analyzer_binary) handler.report_hash = args.report_hash \ if 'report_hash' in args else None @@ -459,8 +452,6 @@ def construct_config_handler(cls, args, context): 'ctu_ast_mode' in args and \ args.ctu_ast_mode == 'parse-on-demand' handler.log_file = args.logfile - handler.path_env_extra = context.path_env_extra - handler.ld_lib_path_extra = context.ld_lib_path_extra try: with open(args.clangsa_args_cfg_file, 'r', encoding='utf8', @@ -477,7 +468,7 @@ def construct_config_handler(cls, args, context): # No clangsa arguments file was given in the command line. LOG.debug_analyzer(aerr) - checkers = ClangSA.get_analyzer_checkers(handler, environ) + checkers = ClangSA.get_analyzer_checkers(handler) try: cmdline_checkers = args.ordered_checkers @@ -487,7 +478,6 @@ def construct_config_handler(cls, args, context): cmdline_checkers = [] handler.initialize_checkers( - context, checkers, cmdline_checkers, 'enable_all' in args and args.enable_all) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/config_handler.py b/analyzer/codechecker_analyzer/analyzers/clangsa/config_handler.py index daf841c4da..e1ca5256b0 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/config_handler.py @@ -29,8 +29,6 @@ def __init__(self, environ): self.ctu_dir = '' self.ctu_on_demand = False self.log_file = '' - self.path_env_extra = '' - self.ld_lib_path_extra = '' self.enable_z3 = False self.enable_z3_refutation = False self.environ = environ diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py index d5af0a02ec..3ac45c39aa 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py @@ -137,7 +137,7 @@ def display_progress(self): 'display-ctu-progress=true'] ok = host_check.has_analyzer_config_option( - self.__analyzer_binary, "display-ctu-progress", self.environ) + self.__analyzer_binary, "display-ctu-progress") if not ok: return None return ctu_display_progress_args diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py index f99f7f99e1..52254b6cd8 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py @@ -78,7 +78,7 @@ def generate_ast_cmd(action, config, triple_arch, source): return cmd, ast_dir -def generate_invocation_list(triple_arch, action, source, config, env): +def generate_invocation_list(triple_arch, action, source, config): """ Generates the invocation for the source file of the current compilation command. Used during on-demand analysis. The invocation list is a mapping from absolute paths of the source files to the parts of the @@ -112,7 +112,7 @@ def generate_invocation_list(triple_arch, action, source, config, env): invocation_file.write(invocation_line) -def generate_ast(triple_arch, action, source, config, env): +def generate_ast(triple_arch, action, source, config): """ Generates ASTs for the current compilation command. Used during ast-dump based analysis. """ @@ -127,7 +127,7 @@ def generate_ast(triple_arch, action, source, config, env): cmdstr = ' '.join(cmd) LOG.debug_analyzer("Generating AST using '%s'", cmdstr) ret_code, _, err = \ - analyzer_base.SourceAnalyzer.run_proc(cmd, env, action.directory) + analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory) if ret_code != 0: LOG.error("Error generating AST.\n\ncommand:\n\n%s\n\nstderr:\n\n%s", @@ -187,7 +187,7 @@ def get_extdef_mapping_cmd(action, config, source, func_map_cmd): return cmd -def map_functions(triple_arch, action, source, config, env, +def map_functions(triple_arch, action, source, config, func_map_cmd, temp_fnmap_folder): """ Generate function map file for the current source. @@ -201,9 +201,7 @@ def map_functions(triple_arch, action, source, config, env, cmdstr = ' '.join(cmd) LOG.debug_analyzer("Generating function map using '%s'", cmdstr) ret_code, stdout, err \ - = analyzer_base.SourceAnalyzer.run_proc(cmd, - env, - action.directory) + = analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory) if ret_code != 0: LOG.error("Error generating function map." "\n\ncommand:\n\n%s\n\nstderr:\n\n%s", cmdstr, err) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py index 3b7228d40d..65a348effc 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py @@ -62,14 +62,13 @@ def _find_arch_in_command(output): pass -def get_triple_arch(action, source, config, env): +def get_triple_arch(action, source, config): """Returns the architecture part of the target triple for the given compilation command. """ cmd = get_compile_command(action, config, source) cmd.insert(1, '-###') _, stdout, stderr = analyzer_base.SourceAnalyzer.run_proc(cmd, - env, action.directory) # The -### flag in a Clang invocation emits the commands of substeps in a diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py b/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py index 150d2bd1e8..a19feffca5 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py @@ -48,7 +48,7 @@ def build_stat_coll_cmd(action, config, source): # Enable the statistics collector checkers only. collector_checkers = [] - checks = ClangSA.get_analyzer_checkers(config, config.environ, True, True) + checks = ClangSA.get_analyzer_checkers(config, True, True) for checker_name, _ in checks: if SpecialReturnValueCollector.checker_collect in checker_name: collector_checkers.append(checker_name) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/version.py b/analyzer/codechecker_analyzer/analyzers/clangsa/version.py index 99cddeb28d..3749766b93 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/version.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/version.py @@ -12,6 +12,8 @@ import shutil import subprocess +from codechecker_analyzer import analyzer_context + class ClangVersionInfo: """ClangVersionInfo holds the version information of the used Clang.""" @@ -67,7 +69,7 @@ def parse(self, version_string): version_match.group('vendor')) -def get(clang_binary, env=None): +def get(clang_binary): """Get and parse the version information from given clang binary Should return False for getting the version @@ -75,7 +77,7 @@ def get(clang_binary, env=None): """ compiler_version = subprocess.check_output( [clang_binary, '--version'], - env=env, + env=analyzer_context.get_context().analyzer_env, encoding="utf-8", errors="ignore") version_parser = ClangVersionInfoParser(clang_binary) diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py index 20f1664f21..3dfede131e 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py @@ -18,7 +18,7 @@ from codechecker_common.logger import get_logger -from codechecker_analyzer import env +from codechecker_analyzer import analyzer_context, env from .. import analyzer_base from ..config_handler import CheckerState, get_compiler_warning_name @@ -81,14 +81,14 @@ def add_checker_config(self, checker_cfg): LOG.error("Not implemented yet") @classmethod - def get_analyzer_checkers(cls, cfg_handler, environ): + def get_analyzer_checkers(cls, cfg_handler): """ Return the list of the all of the supported checkers. """ try: result = subprocess.check_output( [cfg_handler.analyzer_binary, "-list-checks", "-checks=*"], - env=environ, + env=analyzer_context.get_context().analyzer_env, universal_newlines=True, encoding="utf-8", errors="ignore") @@ -97,14 +97,14 @@ def get_analyzer_checkers(cls, cfg_handler, environ): return [] @classmethod - def get_checker_config(cls, cfg_handler, environ): + def get_checker_config(cls, cfg_handler): """ Return the checker configuration of the all of the supported checkers. """ try: result = subprocess.check_output( [cfg_handler.analyzer_binary, "-dump-config", "-checks=*"], - env=environ, + env=analyzer_context.get_context().analyzer_env, universal_newlines=True, encoding="utf-8", errors="ignore") @@ -113,14 +113,14 @@ def get_checker_config(cls, cfg_handler, environ): return [] @classmethod - def get_analyzer_config(cls, cfg_handler, environ): + def get_analyzer_config(cls, cfg_handler): """ Return the analyzer configuration with all checkers enabled. """ try: result = subprocess.check_output( [cfg_handler.analyzer_binary, "-dump-config", "-checks=*"], - env=environ, + env=analyzer_context.get_context().analyzer_env, universal_newlines=True, encoding="utf-8", errors="ignore") @@ -327,7 +327,7 @@ def version_compatible(cls, configured_binary, environ): return True def construct_result_handler(self, buildaction, report_output, - checker_labels, skiplist_handler): + skiplist_handler): """ See base class for docs. """ @@ -335,12 +335,12 @@ def construct_result_handler(self, buildaction, report_output, res_handler = result_handler.ClangTidyResultHandler( buildaction, report_output, report_hash) - res_handler.checker_labels = checker_labels res_handler.skiplist_handler = skiplist_handler return res_handler @classmethod - def construct_config_handler(cls, args, context): + def construct_config_handler(cls, args): + context = analyzer_context.get_context() handler = config_handler.ClangTidyConfigHandler() handler.analyzer_binary = context.analyzer_binaries.get( cls.ANALYZER_NAME) @@ -350,11 +350,6 @@ def construct_config_handler(cls, args, context): # FIXME We cannot get the resource dir from the clang-tidy binary, # therefore we get a sibling clang binary which of clang-tidy. # TODO Support "clang-tidy -print-resource-dir" . - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - # Overwrite PATH to contain only the parent of the clang binary. - if os.path.isabs(handler.analyzer_binary): - check_env['PATH'] = os.path.dirname(handler.analyzer_binary) try: with open(args.tidy_args_cfg_file, 'r', encoding='utf-8', errors='ignore') as tidy_cfg: @@ -435,10 +430,7 @@ def construct_config_handler(cls, args, context): analyzer_config.get('take-config-from-directory') != 'true': handler.checker_config = json.dumps(analyzer_config) - check_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - - checkers = ClangTidy.get_analyzer_checkers(handler, check_env) + checkers = ClangTidy.get_analyzer_checkers(handler) try: cmdline_checkers = args.ordered_checkers @@ -449,7 +441,6 @@ def construct_config_handler(cls, args, context): cmdline_checkers = [] handler.initialize_checkers( - context, checkers, cmdline_checkers, 'enable_all' in args and args.enable_all) diff --git a/analyzer/codechecker_analyzer/analyzers/config_handler.py b/analyzer/codechecker_analyzer/analyzers/config_handler.py index 71c46b33eb..b3aab36d29 100644 --- a/analyzer/codechecker_analyzer/analyzers/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/config_handler.py @@ -16,6 +16,7 @@ import platform import subprocess +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger LOG = get_logger('system') @@ -137,7 +138,6 @@ def __gen_name_variations(self): return reserved_names def initialize_checkers(self, - analyzer_context, checkers, cmdline_enable=[], enable_all=False): @@ -155,7 +155,6 @@ def initialize_checkers(self, - Without prefix it means a profile name, a guideline name or a checker group/name in this priority order. - analyzer_context -- Context object. checkers -- [(checker name, description), ...] Checkers to add with their description. cmdline_enable -- [(argument, enabled), ...] Arguments of @@ -164,7 +163,7 @@ def initialize_checkers(self, enable_all -- Boolean value whether "--enable-all" is given. """ - checker_labels = analyzer_context.checker_labels + checker_labels = analyzer_context.get_context().checker_labels # Add all checkers marked as default. This means the analyzer should # manage whether it is enabled or disabled. diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py index 004c62986c..3d5f378bc7 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py @@ -11,6 +11,7 @@ from distutils.version import StrictVersion from pathlib import Path import os +import pickle import re import shutil import subprocess @@ -18,7 +19,8 @@ from codechecker_common.logger import get_logger -from codechecker_analyzer.env import extend, get_binary_in_path +from codechecker_analyzer import analyzer_context +from codechecker_analyzer.env import get_binary_in_path from .. import analyzer_base @@ -206,17 +208,14 @@ def construct_analyzer_cmd(self, result_handler): return [] @classmethod - def get_analyzer_checkers( - cls, - cfg_handler: CppcheckConfigHandler, - env): + def get_analyzer_checkers(cls, cfg_handler: CppcheckConfigHandler): """ Return the list of the supported checkers. """ command = [cfg_handler.analyzer_binary, "--errorlist"] try: - result = subprocess.check_output(command, env=env) + result = subprocess.check_output(command) return parse_checkers(result) except (subprocess.CalledProcessError) as e: LOG.error(e.stderr) @@ -225,7 +224,7 @@ def get_analyzer_checkers( return [] @classmethod - def get_analyzer_config(cls, cfg_handler, environ): + def get_analyzer_config(cls, cfg_handler): """ Config options for cppcheck. """ @@ -236,12 +235,23 @@ def get_analyzer_config(cls, cfg_handler, environ): ] @classmethod - def get_checker_config(cls, cfg_handler, environ): + def get_checker_config(cls, cfg_handler): """ TODO add config options for cppcheck checkers. """ return [] + def analyze(self, analyzer_cmd, res_handler, proc_callback=None): + env = None + + original_env_file = os.environ.get( + 'CODECHECKER_ORIGINAL_BUILD_ENV') + if original_env_file: + with open(original_env_file, 'rb') as env_file: + env = pickle.load(env_file, encoding='utf-8') + + return super().analyze(analyzer_cmd, res_handler, proc_callback, env) + def post_analyze(self, result_handler): """ Post process the reuslts after the analysis. @@ -327,20 +337,20 @@ def version_compatible(cls, configured_binary, environ): return False def construct_result_handler(self, buildaction, report_output, - checker_labels, skiplist_handler): + skiplist_handler): """ See base class for docs. """ res_handler = CppcheckResultHandler(buildaction, report_output, self.config_handler.report_hash) - res_handler.checker_labels = checker_labels res_handler.skiplist_handler = skiplist_handler return res_handler @classmethod - def construct_config_handler(cls, args, context): + def construct_config_handler(cls, args): + context = analyzer_context.get_context() handler = CppcheckConfigHandler() handler.analyzer_binary = context.analyzer_binaries.get( cls.ANALYZER_NAME) @@ -360,14 +370,13 @@ def construct_config_handler(cls, args, context): handler.analyzer_config = analyzer_config - check_env = extend(context.path_env_extra, - context.ld_lib_path_extra) + check_env = context.analyzer_env # Overwrite PATH to contain only the parent of the cppcheck binary. if os.path.isabs(handler.analyzer_binary): check_env['PATH'] = os.path.dirname(handler.analyzer_binary) - checkers = cls.get_analyzer_checkers(handler, check_env) + checkers = cls.get_analyzer_checkers(handler) # Cppcheck can and will report with checks that have a different # name than marked in the --errorlist xml. To be able to suppress @@ -389,7 +398,6 @@ def construct_config_handler(cls, args, context): cmdline_checkers = [] handler.initialize_checkers( - context, checkers, cmdline_checkers, 'enable_all' in args and args.enable_all) diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/config_handler.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/config_handler.py index 8508ea2fb0..05682a0757 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/config_handler.py @@ -17,7 +17,6 @@ class CppcheckConfigHandler(config_handler.AnalyzerConfigHandler): Configuration handler for Cppcheck analyzer. """ def initialize_checkers(self, - analyzer_context, checkers, cmdline_enable=None, enable_all=False): @@ -28,7 +27,6 @@ def initialize_checkers(self, --enable=all will not run with all the possible checkers """ super().initialize_checkers( - analyzer_context, checkers, cmdline_enable, enable_all) diff --git a/analyzer/codechecker_analyzer/analyzers/result_handler_base.py b/analyzer/codechecker_analyzer/analyzers/result_handler_base.py index 2f0997504d..bb4425c78c 100644 --- a/analyzer/codechecker_analyzer/analyzers/result_handler_base.py +++ b/analyzer/codechecker_analyzer/analyzers/result_handler_base.py @@ -16,6 +16,7 @@ from abc import ABCMeta from typing import Optional +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger from codechecker_common.skiplist_handler import SkipListHandlers @@ -55,7 +56,7 @@ def __init__(self, action, workspace, report_hash_type=None): self.analyzer_cmd = [] self.analyzer_stdout = '' self.analyzer_stderr = '' - self.checker_labels = None + self.checker_labels = analyzer_context.get_context().checker_labels self.skiplist_handler = None self.analyzed_source_file = None self.analyzer_returncode = 1 diff --git a/analyzer/codechecker_analyzer/buildlog/build_manager.py b/analyzer/codechecker_analyzer/buildlog/build_manager.py index 3e7d52fb7c..7066c161df 100644 --- a/analyzer/codechecker_analyzer/buildlog/build_manager.py +++ b/analyzer/codechecker_analyzer/buildlog/build_manager.py @@ -53,7 +53,7 @@ def execute_buildcmd(command, silent=False, env=None, cwd=None): return proc.returncode -def perform_build_command(logfile, command, context, keep_link, silent=False, +def perform_build_command(logfile, command, keep_link, silent=False, verbose=None): """ Build the project and create a log file. @@ -83,7 +83,7 @@ def perform_build_command(logfile, command, context, keep_link, silent=False, # Same as linux's touch. open(logfile, 'a', encoding="utf-8", errors="ignore").close() - log_env = env.get_log_env(logfile, context, original_env) + log_env = env.get_log_env(logfile, original_env) if 'CC_LOGGER_GCC_LIKE' not in log_env: log_env['CC_LOGGER_GCC_LIKE'] = 'gcc:g++:clang:clang++:cc:c++' if keep_link or ('CC_LOGGER_KEEP_LINK' in log_env and diff --git a/analyzer/codechecker_analyzer/buildlog/log_parser.py b/analyzer/codechecker_analyzer/buildlog/log_parser.py index f0e05043da..b52c991286 100644 --- a/analyzer/codechecker_analyzer/buildlog/log_parser.py +++ b/analyzer/codechecker_analyzer/buildlog/log_parser.py @@ -902,7 +902,6 @@ def parse_options(compilation_db_entry, keep_gcc_include_fixed=False, keep_gcc_intrin=False, get_clangsa_version_func=None, - env=None, analyzer_clang_version=None): """ This function parses a GCC compilation action and returns a BuildAction @@ -928,7 +927,6 @@ def parse_options(compilation_db_entry, It requires the compiler binary and an env. get_clangsa_version_func(compiler_binary, env) Should return false for a non clang compiler. - env -- Is the environment where a subprocess call should be executed. analyzer_clang_version -- version information about the clang which is used to execute the analysis """ @@ -1003,7 +1001,7 @@ def parse_options(compilation_db_entry, # did not find in the cache yet try: compiler_version_info = \ - get_clangsa_version_func(details['compiler'], env) + get_clangsa_version_func(details['compiler']) except (subprocess.CalledProcessError, OSError) as cerr: LOG.error('Failed to get and parse version of: %s', details['compiler']) @@ -1199,7 +1197,6 @@ def parse_unique_log(compilation_database, analysis_skip_handlers=None, pre_analysis_skip_handlers=None, ctu_or_stats_enabled=False, - env=None, analyzer_clang_version=None): """ This function reads up the compilation_database @@ -1249,7 +1246,6 @@ def parse_unique_log(compilation_database, skipped during pre analysis ctu_or_stats_enabled -- ctu or statistics based analysis was enabled influences the behavior which files are skipped. - env -- Is the environment where a subprocess call should be executed. analyzer_clang_version -- version information about the clang which is used to execute the analysis """ @@ -1290,7 +1286,6 @@ def parse_unique_log(compilation_database, keep_gcc_include_fixed, keep_gcc_intrin, clangsa.version.get, - env, analyzer_clang_version) if not action.lang: diff --git a/analyzer/codechecker_analyzer/cmd/analyze.py b/analyzer/codechecker_analyzer/cmd/analyze.py index f1c3ec2100..e08e38c35b 100644 --- a/analyzer/codechecker_analyzer/cmd/analyze.py +++ b/analyzer/codechecker_analyzer/cmd/analyze.py @@ -24,7 +24,7 @@ from tu_collector import tu_collector -from codechecker_analyzer import analyzer, analyzer_context, env +from codechecker_analyzer import analyzer, analyzer_context from codechecker_analyzer.analyzers import analyzer_types, clangsa from codechecker_analyzer.arg import \ OrderedCheckersAction, OrderedConfigAction @@ -447,8 +447,7 @@ def add_arguments_to_parser(parser): "the analysis is considered as a failed " "one.") - context = analyzer_context.get_context() - clang_has_z3 = analyzer_types.is_z3_capable(context) + clang_has_z3 = analyzer_types.is_z3_capable() if clang_has_z3: analyzer_opts.add_argument('--z3', @@ -464,7 +463,7 @@ def add_arguments_to_parser(parser): "backend is a highly experimental " "and likely unstable feature.") - clang_has_z3_refutation = analyzer_types.is_z3_refutation_capable(context) + clang_has_z3_refutation = analyzer_types.is_z3_refutation_capable() if clang_has_z3_refutation: analyzer_opts.add_argument('--z3-refutation', @@ -482,7 +481,7 @@ def add_arguments_to_parser(parser): "that much of a slowdown compared to " "using only the Z3 solver.") - if analyzer_types.is_ctu_capable(context): + if analyzer_types.is_ctu_capable(): ctu_opts = parser.add_argument_group( "cross translation unit analysis arguments", """ @@ -537,7 +536,7 @@ def add_arguments_to_parser(parser): "Cross-TU enabled.") # Only check for AST loading modes if CTU is available. - if analyzer_types.is_ctu_on_demand_available(context): + if analyzer_types.is_ctu_on_demand_available(): ctu_opts.add_argument('--ctu-ast-mode', action='store', dest='ctu_ast_mode', @@ -558,7 +557,7 @@ def add_arguments_to_parser(parser): "phase of the analysis. (default: " "parse-on-demand)") - if analyzer_types.is_statistics_capable(context): + if analyzer_types.is_statistics_capable(): stat_opts = parser.add_argument_group( "Statistics analysis feature arguments", """ @@ -964,8 +963,6 @@ def main(args): ctu_or_stats_enabled = True context = analyzer_context.get_context() - analyzer_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) # Number of all the compilation commands in the parsed log files, # logged by the logger. @@ -1001,8 +998,7 @@ def main(args): analyzer_clang_version = None if analyzer_clang_binary: - analyzer_clang_version = clangsa.version.get(analyzer_clang_binary, - analyzer_env) + analyzer_clang_version = clangsa.version.get(analyzer_clang_binary) actions, skipped_cmp_cmd_count = log_parser.parse_unique_log( compile_commands, @@ -1014,7 +1010,6 @@ def main(args): skip_handlers, pre_analysis_skip_handlers, ctu_or_stats_enabled, - analyzer_env, analyzer_clang_version) if not actions: @@ -1081,8 +1076,7 @@ def main(args): LOG.debug_analyzer("Compile commands forwarded for analysis: %d", compile_cmd_count.analyze) - analyzer.perform_analysis(args, skip_handlers, context, actions, - metadata_tool, + analyzer.perform_analysis(args, skip_handlers, actions, metadata_tool, compile_cmd_count) __update_skip_file(args) diff --git a/analyzer/codechecker_analyzer/cmd/analyzers.py b/analyzer/codechecker_analyzer/cmd/analyzers.py index 2fac4b7a96..2275537ad6 100644 --- a/analyzer/codechecker_analyzer/cmd/analyzers.py +++ b/analyzer/codechecker_analyzer/cmd/analyzers.py @@ -18,7 +18,6 @@ from codechecker_report_converter import twodim from codechecker_analyzer import analyzer_context -from codechecker_analyzer import env from codechecker_analyzer.analyzers import analyzer_types from codechecker_common import logger @@ -52,10 +51,8 @@ def add_arguments_to_parser(parser): Add the subcommand's arguments to the given argparse.ArgumentParser. """ - context = analyzer_context.get_context() working_analyzers, _ = analyzer_types.check_supported_analyzers( - analyzer_types.supported_analyzers, - context) + analyzer_types.supported_analyzers) parser.add_argument('--all', dest="all", @@ -123,8 +120,7 @@ def main(args): context = analyzer_context.get_context() working_analyzers, errored = \ analyzer_types.check_supported_analyzers( - analyzer_types.supported_analyzers, - context) + analyzer_types.supported_analyzers) if args.dump_config: binary = context.analyzer_binaries.get(args.dump_config) @@ -152,10 +148,8 @@ def main(args): return - analyzer_environment = env.extend(context.path_env_extra, - context.ld_lib_path_extra) analyzer_config_map = analyzer_types.build_config_handlers( - args, context, working_analyzers) + args, working_analyzers) def uglify(text): """ @@ -177,8 +171,7 @@ def uglify(text): config_handler = analyzer_config_map.get(analyzer) analyzer_class = analyzer_types.supported_analyzers[analyzer] - configs = analyzer_class.get_analyzer_config(config_handler, - analyzer_environment) + configs = analyzer_class.get_analyzer_config(config_handler) if not configs: LOG.error("Failed to get analyzer configuration options for '%s' " "analyzer! Please try to upgrade your analyzer version " diff --git a/analyzer/codechecker_analyzer/cmd/check.py b/analyzer/codechecker_analyzer/cmd/check.py index 43055c65c9..f057ed011b 100644 --- a/analyzer/codechecker_analyzer/cmd/check.py +++ b/analyzer/codechecker_analyzer/cmd/check.py @@ -18,7 +18,6 @@ import sys import tempfile -from codechecker_analyzer import analyzer_context from codechecker_analyzer.analyzers import analyzer_types from codechecker_analyzer.arg import \ OrderedCheckersAction, OrderedConfigAction @@ -394,8 +393,7 @@ def add_arguments_to_parser(parser): "the analysis is considered as a failed " "one.") - context = analyzer_context.get_context() - clang_has_z3 = analyzer_types.is_z3_capable(context) + clang_has_z3 = analyzer_types.is_z3_capable() if clang_has_z3: analyzer_opts.add_argument('--z3', @@ -411,7 +409,7 @@ def add_arguments_to_parser(parser): "backend is a highly experimental " "and likely unstable feature.") - clang_has_z3_refutation = analyzer_types.is_z3_refutation_capable(context) + clang_has_z3_refutation = analyzer_types.is_z3_refutation_capable() if clang_has_z3_refutation: analyzer_opts.add_argument('--z3-refutation', @@ -429,7 +427,7 @@ def add_arguments_to_parser(parser): "that much of a slowdown compared to " "using only the Z3 solver.") - if analyzer_types.is_ctu_capable(context): + if analyzer_types.is_ctu_capable(): ctu_opts = parser.add_argument_group( "cross translation unit analysis arguments", """ @@ -485,7 +483,7 @@ def add_arguments_to_parser(parser): "Cross-TU enabled.") # Only check for AST loading modes if CTU is available. - if analyzer_types.is_ctu_on_demand_available(context): + if analyzer_types.is_ctu_on_demand_available(): ctu_opts.add_argument('--ctu-ast-mode', action='store', dest='ctu_ast_mode', @@ -506,7 +504,7 @@ def add_arguments_to_parser(parser): "phase of the analysis. (default: " "parse-on-demand)") - if analyzer_types.is_statistics_capable(context): + if analyzer_types.is_statistics_capable(): stat_opts = parser.add_argument_group( "Statistics analysis feature arguments", """ diff --git a/analyzer/codechecker_analyzer/cmd/checkers.py b/analyzer/codechecker_analyzer/cmd/checkers.py index e942c84add..6e730aa4ef 100644 --- a/analyzer/codechecker_analyzer/cmd/checkers.py +++ b/analyzer/codechecker_analyzer/cmd/checkers.py @@ -28,7 +28,6 @@ from codechecker_common import arg, logger from codechecker_common.output import USER_FORMATS from codechecker_common.checker_labels import CheckerLabels -from codechecker_analyzer import env from codechecker_analyzer.analyzers.config_handler import CheckerState LOG = logger.get_logger('system') @@ -59,7 +58,7 @@ def get_diagtool_bin(): clang_bin) -def get_warnings(env=None): +def get_warnings(): """ Returns list of warning flags by using diagtool. """ @@ -71,7 +70,7 @@ def get_warnings(env=None): try: result = subprocess.check_output( [diagtool_bin, 'tree'], - env=env, + env=analyzer_context.get_context().analyzer_env, universal_newlines=True, encoding="utf-8", errors="ignore") @@ -270,26 +269,6 @@ def __uglify(text: str) -> str: return text.lower().replace(' ', '_') -def guideline_rules_for_checker( - checker: str, - context: analyzer_context.Context -) -> Dict[str, list]: - """ - Returns the guideline and rules covered by the given checker. This - function returns a dict which maps guideline names to the covered - rules: { "sei-cert": ["arr30-c", ...], ... }. - """ - result = defaultdict(list) - labels = context.checker_labels.labels_of_checker(checker) - guidelines = context.checker_labels.get_description('guideline') - - for label in labels: - if label[0] in guidelines: - result[label[0]].append(label[1]) - - return result - - def __guideline_to_label( args: argparse.Namespace, cl: CheckerLabels @@ -323,18 +302,11 @@ def __get_detailed_checker_info( supported checkers. Checker information is described with tuples of this information: (status, checker name, analyzer name, description, labels). """ - context = analyzer_context.get_context() - working_analyzers, _ = analyzer_types.check_supported_analyzers( - analyzer_types.supported_analyzers, - context) + analyzer_types.supported_analyzers) analyzer_config_map = analyzer_types.build_config_handlers( - args, context, working_analyzers) - - analyzer_environment = env.extend( - context.path_env_extra, - context.ld_lib_path_extra) + args, working_analyzers) checker_info = defaultdict(list) @@ -342,8 +314,7 @@ def __get_detailed_checker_info( config_handler = analyzer_config_map.get(analyzer) analyzer_class = analyzer_types.supported_analyzers[analyzer] - checkers = analyzer_class.get_analyzer_checkers( - config_handler, analyzer_environment) + checkers = analyzer_class.get_analyzer_checkers(config_handler) profile_checkers = [] if 'profile' in args: @@ -366,25 +337,16 @@ def __get_detailed_checker_info( if 'guideline' in args: profile_checkers.append((__guideline_to_label(args, cl), True)) - config_handler.initialize_checkers( - context, checkers, profile_checkers) + config_handler.initialize_checkers(checkers, profile_checkers) for checker, (state, description) in config_handler.checks().items(): - # severity = cl.severity(checker) - # guideline = guideline_rules_for_checker(checker, context) - # checker_info[analyzer].append( - # (state, checker, analyzer, severity, guideline, description)) checker_info[analyzer].append( (state, checker, analyzer, description, sorted(cl.labels_of_checker(checker, analyzer)))) if 'show_warnings' in args: - for warning in get_warnings(analyzer_environment): + for warning in get_warnings(): warning = 'clang-diagnostic-' + warning - # guideline = guideline_rules_for_checker(warning, context) - # checker_info[ClangTidy.ANALYZER_NAME].append( - # (CheckerState.default, warning, ClangTidy.ANALYZER_NAME, - # 'MEDIUM', guideline, '')) checker_info[ClangTidy.ANALYZER_NAME].append( (CheckerState.default, warning, ClangTidy.ANALYZER_NAME, '', sorted(cl.labels_of_checker( @@ -651,17 +613,12 @@ def __print_checker_config(args: argparse.Namespace): if args.output_format == 'custom': args.output_format = 'rows' - context = analyzer_context.get_context() working_analyzers, errored = analyzer_types.check_supported_analyzers( - args.analyzers, - context) + args.analyzers) analyzer_types.check_available_analyzers(working_analyzers, errored) - analyzer_environment = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - analyzer_config_map = analyzer_types.build_config_handlers( - args, context, working_analyzers) + args, working_analyzers) if 'details' in args: header = ['Option', 'Description'] @@ -677,8 +634,7 @@ def __print_checker_config(args: argparse.Namespace): config_handler = analyzer_config_map.get(analyzer) analyzer_class = analyzer_types.supported_analyzers[analyzer] - configs = analyzer_class.get_checker_config(config_handler, - analyzer_environment) + configs = analyzer_class.get_checker_config(config_handler) if not configs: # Checker configurations are not supported by cppcheck if analyzer != "cppcheck": diff --git a/analyzer/codechecker_analyzer/cmd/fixit.py b/analyzer/codechecker_analyzer/cmd/fixit.py index cec0f6b8e8..9064bdef1b 100644 --- a/analyzer/codechecker_analyzer/cmd/fixit.py +++ b/analyzer/codechecker_analyzer/cmd/fixit.py @@ -292,8 +292,7 @@ def apply_process(out_dir): modified_files = set() ignore_flag = ["--ignore-insert-conflict"] if \ - analyzer_types.is_ignore_conflict_supported( - analyzer_context.get_context()) else [] + analyzer_types.is_ignore_conflict_supported() else [] for i in inputs: fixit_dir = os.path.join(i, 'fixit') diff --git a/analyzer/codechecker_analyzer/cmd/log.py b/analyzer/codechecker_analyzer/cmd/log.py index 9ee0c83050..8cca7168af 100644 --- a/analyzer/codechecker_analyzer/cmd/log.py +++ b/analyzer/codechecker_analyzer/cmd/log.py @@ -17,7 +17,6 @@ import argparse import os -from codechecker_analyzer import analyzer_context from codechecker_analyzer.buildlog import build_manager from codechecker_analyzer.buildlog.host_check import check_intercept from codechecker_analyzer.buildlog.host_check import check_ldlogger @@ -174,12 +173,10 @@ def main(args): encoding="utf-8", errors="ignore") as logfile: logfile.write("[\n]") - context = analyzer_context.get_context() verbose = args.verbose if 'verbose' in args else None build_manager.perform_build_command(args.logfile, args.command, - context, 'keep_link' in args, silent='quiet' in args, verbose=verbose) diff --git a/analyzer/codechecker_analyzer/env.py b/analyzer/codechecker_analyzer/env.py index 890f70e900..b6c8000d03 100644 --- a/analyzer/codechecker_analyzer/env.py +++ b/analyzer/codechecker_analyzer/env.py @@ -11,17 +11,19 @@ import os import re +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger LOG = get_logger('system') -def get_log_env(logfile, context, original_env): +def get_log_env(logfile, original_env): """ Environment for logging. With the ld logger. Keep the original environment unmodified as possible. Only environment variables required for logging are changed. """ + context = analyzer_context.get_context() new_env = original_env new_env[context.env_var_cc_logger_bin] = context.path_logger_bin diff --git a/analyzer/codechecker_analyzer/host_check.py b/analyzer/codechecker_analyzer/host_check.py index 5b7fe5f7dd..dcf80d1f60 100644 --- a/analyzer/codechecker_analyzer/host_check.py +++ b/analyzer/codechecker_analyzer/host_check.py @@ -15,6 +15,7 @@ import subprocess import tempfile +from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger LOG = get_logger('analyzer') @@ -47,17 +48,19 @@ def check_analyzer(compiler_bin, env): return False -def has_analyzer_config_option(clang_bin, config_option_name, env=None): +def has_analyzer_config_option(clang_bin, config_option_name): """Check if an analyzer config option is available.""" cmd = [clang_bin, "-cc1", "-analyzer-config-help"] LOG.debug('run: "%s"', ' '.join(cmd)) try: - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, encoding="utf-8", errors="ignore") + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=analyzer_context.get_context().analyzer_env, + encoding="utf-8", errors="ignore") out, err = proc.communicate() LOG.debug("stdout:\n%s", out) LOG.debug("stderr:\n%s", err) @@ -72,7 +75,7 @@ def has_analyzer_config_option(clang_bin, config_option_name, env=None): raise -def has_analyzer_option(clang_bin, feature, env=None): +def has_analyzer_option(clang_bin, feature): """Test if the analyzer has a specific option. Testing a feature is done by compiling a dummy file.""" @@ -85,10 +88,12 @@ def has_analyzer_option(clang_bin, feature, env=None): LOG.debug('run: "%s"', ' '.join(cmd)) try: - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, encoding="utf-8", errors="ignore") + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=analyzer_context.get_context().analyzer_env, + encoding="utf-8", errors="ignore") out, err = proc.communicate() LOG.debug("stdout:\n%s", out) LOG.debug("stderr:\n%s", err) diff --git a/analyzer/codechecker_analyzer/makefile.py b/analyzer/codechecker_analyzer/makefile.py index ab41a591ed..34d9fc2746 100644 --- a/analyzer/codechecker_analyzer/makefile.py +++ b/analyzer/codechecker_analyzer/makefile.py @@ -17,7 +17,7 @@ from codechecker_statistics_collector.collectors.special_return_value import \ SpecialReturnValueCollector -from . import analysis_manager, env +from . import analysis_manager, analyzer_context from .analyzers.clangsa.analyzer import ClangSA from .analyzers.clangsa.ctu_manager import generate_ast_cmd, \ get_extdef_mapping_cmd @@ -32,12 +32,11 @@ class MakeFileCreator: """ Creates a Makefile from analyzer actions. """ - def __init__(self, analyzers, output_path, config_map, context, + def __init__(self, analyzers, output_path, config_map, skip_handlers, pre_analysis, statistics_data, ctu_data): self.__analyzers = analyzers self.__output_path = output_path self.__config_map = config_map - self.__context = context self.__skip_handlers = skip_handlers self.__pre_analysis = pre_analysis self.__log_info = "[`date +'%Y-%m-%d %H:%M:%S'`] -" @@ -58,9 +57,6 @@ def __init__(self, analyzers, output_path, config_map, context, self.__makefile = os.path.join(output_path, 'Makefile') - self.__analyzer_env = env.extend(context.path_env_extra, - context.ld_lib_path_extra) - self.__config = None self.__func_map_cmd = None if ClangSA.ANALYZER_NAME in config_map: @@ -89,7 +85,8 @@ def __write_header(self, mfile): """ mfile.write("#\n# Autogenerated by CodeChecker v{0}.\n#\n" "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT " - "YOU ARE DOING.\n#\n\n".format(self.__context.version)) + "YOU ARE DOING.\n#\n\n".format( + analyzer_context.get_context().version)) def __write_env_exports(self, mfile): """ Exports environment variables. """ @@ -128,8 +125,7 @@ def __get_ctu_pre_analysis_cmds(self, action): cmds = [] # Get architecture part of the target triple. - triple_arch = get_triple_arch(action, action.source, - self.__config, self.__analyzer_env) + triple_arch = get_triple_arch(action, action.source, self.__config) # Get command to generate PCH file. cmd, ast_dir = generate_ast_cmd(action, self.__config, @@ -234,7 +230,7 @@ def __write_analysis_targets(self, mfile, action, post_pre_all_target): """ Creates normal analysis targets. """ source_analyzer, rh = analysis_manager.prepare_check( action, self.__config_map.get(action.analyzer_type), - self.__output_path, self.__context.checker_labels, + self.__output_path, self.__skip_handlers, self.__statistics_data) if self.__statistics_data and post_pre_all_target: diff --git a/analyzer/codechecker_analyzer/pre_analysis_manager.py b/analyzer/codechecker_analyzer/pre_analysis_manager.py index f2c8481861..475722d3f8 100644 --- a/analyzer/codechecker_analyzer/pre_analysis_manager.py +++ b/analyzer/codechecker_analyzer/pre_analysis_manager.py @@ -18,7 +18,6 @@ import traceback import uuid -from codechecker_analyzer import env from codechecker_common.logger import get_logger from codechecker_statistics_collector import post_process_stats @@ -31,8 +30,7 @@ LOG = get_logger('analyzer') -def collect_statistics(action, source, clangsa_config, - environ, statistics_data): +def collect_statistics(action, source, clangsa_config, statistics_data): """ Run the statistics collection command and save the stdout and stderr to a file. @@ -48,7 +46,7 @@ def collect_statistics(action, source, clangsa_config, LOG.debug_analyzer(' '.join(map(shlex.quote, cmd))) ret_code, analyzer_out, analyzer_err = \ - analyzer_base.SourceAnalyzer.run_proc(cmd, env=environ) + analyzer_base.SourceAnalyzer.run_proc(cmd) LOG.debug(analyzer_out) LOG.debug(analyzer_err) @@ -85,11 +83,7 @@ def init_worker(checked_num, action_num): def pre_analyze(params): - action, context, clangsa_config, skip_handlers, \ - ctu_data, statistics_data = params - - analyzer_environment = env.extend(context.path_env_extra, - context.ld_lib_path_extra) + action, clangsa_config, skip_handlers, ctu_data, statistics_data = params progress_checked_num.value += 1 @@ -112,27 +106,24 @@ def pre_analyze(params): triple_arch = \ ctu_triple_arch.get_triple_arch(action, action.source, - clangsa_config, - analyzer_environment) + clangsa_config) # TODO: reorganize the various ctu modes parameters # Dump-based analysis requires serialized ASTs. if clangsa_config.ctu_on_demand: ctu_manager.generate_invocation_list(triple_arch, action, action.source, - clangsa_config, - analyzer_environment) + clangsa_config) else: ctu_manager.generate_ast(triple_arch, action, action.source, - clangsa_config, analyzer_environment) + clangsa_config) # On-demand analysis does not require AST-dumps. # We map the function names to corresponding sources of ASTs. # In case of On-demand analysis this source is the original source # code. In case of AST-dump based analysis these sources are the # generated AST-dumps. ctu_manager.map_functions(triple_arch, action, action.source, - clangsa_config, analyzer_environment, - ctu_func_map_cmd, + clangsa_config, ctu_func_map_cmd, ctu_temp_fnmap_folder) except Exception as ex: @@ -146,7 +137,6 @@ def pre_analyze(params): collect_statistics(action, action.source, clangsa_config, - analyzer_environment, statistics_data) except Exception as ex: @@ -155,7 +145,7 @@ def pre_analyze(params): raise -def run_pre_analysis(actions, context, clangsa_config, +def run_pre_analysis(actions, clangsa_config, jobs, skip_handlers, ctu_data, statistics_data, manager): """ Run multiple pre analysis jobs before the actual analysis. @@ -196,7 +186,6 @@ def signal_handler(signum, frame): try: collect_actions = [(build_action, - context, clangsa_config, skip_handlers, ctu_data, diff --git a/analyzer/tests/functional/__init__.py b/analyzer/tests/functional/__init__.py index ba8fe8b555..b6f4626afa 100644 --- a/analyzer/tests/functional/__init__.py +++ b/analyzer/tests/functional/__init__.py @@ -18,5 +18,6 @@ REPO_ROOT = os.path.abspath(os.environ['REPO_ROOT']) PKG_ROOT = os.path.join(REPO_ROOT, 'build', 'CodeChecker') +os.environ["CC_DATA_FILES_DIR"] = PKG_ROOT sys.path.append(os.path.join(REPO_ROOT)) sys.path.append(os.path.join(PKG_ROOT, 'lib', 'python3')) diff --git a/analyzer/tests/functional/ctu_failure/test_ctu_failure.py b/analyzer/tests/functional/ctu_failure/test_ctu_failure.py index 721134a2f2..542ca2785d 100644 --- a/analyzer/tests/functional/ctu_failure/test_ctu_failure.py +++ b/analyzer/tests/functional/ctu_failure/test_ctu_failure.py @@ -66,7 +66,7 @@ def setUp(self): setattr(self, DISPLAY_PROGRESS_ATTR, is_ctu_display_progress_capable( - self.__getClangSaPath(), self.env)) + self.__getClangSaPath())) print("Has display-ctu-progress=true? " + str(getattr(self, DISPLAY_PROGRESS_ATTR))) diff --git a/analyzer/tests/libtest/codechecker.py b/analyzer/tests/libtest/codechecker.py index 288d90e0dd..50a885bf56 100644 --- a/analyzer/tests/libtest/codechecker.py +++ b/analyzer/tests/libtest/codechecker.py @@ -15,7 +15,6 @@ import subprocess from distutils import util -from typing import Dict from codechecker_analyzer import host_check @@ -161,15 +160,12 @@ def is_ctu_on_demand_capable(output: str) -> bool: return check_force_ctu_capable('--ctu-ast-mode' in output) -def is_ctu_display_progress_capable( - clangsa_path: str, - env: Dict -) -> bool: +def is_ctu_display_progress_capable(clangsa_path: str) -> bool: """ Returns True if the used clang is CTU display progress capable or if it's force enabled by environment variable. """ ctu_display_progress_capable = host_check.has_analyzer_config_option( - clangsa_path, 'display-ctu-progress', env) + clangsa_path, 'display-ctu-progress') return check_force_ctu_capable(ctu_display_progress_capable) diff --git a/analyzer/tests/unit/__init__.py b/analyzer/tests/unit/__init__.py index e9961e3287..8e35e46afb 100644 --- a/analyzer/tests/unit/__init__.py +++ b/analyzer/tests/unit/__init__.py @@ -16,6 +16,8 @@ REPO_ROOT = os.path.abspath(os.environ['REPO_ROOT']) PKG_ROOT = os.path.join(REPO_ROOT, 'build', 'CodeChecker') +os.environ["CC_DATA_FILES_DIR"] = PKG_ROOT + sys.path.append(REPO_ROOT) sys.path.append(os.path.join( REPO_ROOT, 'analyzer', 'tools', 'statistics_collector')) diff --git a/analyzer/tests/unit/test_checker_handling.py b/analyzer/tests/unit/test_checker_handling.py index 085f234e8d..9a0dcd9d2d 100644 --- a/analyzer/tests/unit/test_checker_handling.py +++ b/analyzer/tests/unit/test_checker_handling.py @@ -18,45 +18,36 @@ from codechecker_analyzer.analyzers.clangtidy.analyzer import ClangTidy from codechecker_analyzer.analyzers.config_handler import CheckerState +from codechecker_analyzer import analyzer_context from codechecker_analyzer.buildlog import log_parser -class MockContextSA: - class CheckerLabels: - def checkers_by_labels(self, labels): - if labels[0] == 'profile:default': - return ['core', 'deadcode', 'security.FloatLoopCounter'] - elif labels[0] == 'profile:security': - return ['alpha.security'] - elif labels[0] == 'guideline:sei-cert': - return ['alpha.core.CastSize', 'alpha.core.CastToStruct'] - elif labels[0] == 'severity:LOW': - return ['security.insecureAPI.bcmp', 'alpha.llvm.Conventions'] - - def get_description(self, label): - if label == 'profile': - return ['default', 'sensitive', 'security', 'portability', - 'extreme'] - - def occurring_values(self, label): - if label == 'guideline': - return ['sei-cert'] - elif label == 'sei-cert': - return ['rule1', 'rule2'] - - path_env_extra = None - ld_lib_path_extra = None - checker_plugin = None - analyzer_binaries = {'clangsa': 'clang'} - checker_labels = CheckerLabels() - available_profiles = ['profile1'] - package_root = './' +class MockCheckerLabels: + def checkers_by_labels(self, labels): + if labels[0] == 'profile:default': + return ['core', 'deadcode', 'security.FloatLoopCounter'] + elif labels[0] == 'profile:security': + return ['alpha.security'] + elif labels[0] == 'guideline:sei-cert': + return ['alpha.core.CastSize', 'alpha.core.CastToStruct'] + elif labels[0] == 'severity:LOW': + return ['security.insecureAPI.bcmp', 'alpha.llvm.Conventions'] + + def get_description(self, label): + if label == 'profile': + return ['default', 'sensitive', 'security', 'portability', + 'extreme'] + + def occurring_values(self, label): + if label == 'guideline': + return ['sei-cert'] + elif label == 'sei-cert': + return ['rule1', 'rule2'] def create_analyzer_sa(): args = [] - context = MockContextSA() - cfg_handler = ClangSA.construct_config_handler(args, context) + cfg_handler = ClangSA.construct_config_handler(args) action = { 'file': 'main.cpp', @@ -77,7 +68,6 @@ def create_result_handler(analyzer): rh = analyzer.construct_result_handler( build_action, build_action.directory, - None, None) rh.analyzed_source_file = build_action.source @@ -94,6 +84,9 @@ class CheckerHandlingClangSATest(unittest.TestCase): @classmethod def setUpClass(cls): + context = analyzer_context.get_context() + context._checker_labels = MockCheckerLabels() + analyzer = create_analyzer_sa() result_handler = create_result_handler(analyzer) cls.cmd = analyzer.construct_analyzer_cmd(result_handler) @@ -132,7 +125,6 @@ def f(checks, checkers): return f args = [] - context = MockContextSA() # "security" profile, but alpha -> not in default. security_profile_alpha = [ @@ -161,8 +153,8 @@ def f(checks, checkers): # "default" profile checkers are enabled explicitly. Others are in # "default" state. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers) + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), default_profile)) self.assertTrue(all_with_status(CheckerState.default) @@ -170,24 +162,24 @@ def f(checks, checkers): # "--enable-all" leaves alpha checkers in "default" state. Others # become enabled. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, enable_all=True) + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, enable_all=True) self.assertTrue(all_with_status(CheckerState.default) (cfg_handler.checks(), security_profile_alpha)) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), default_profile)) # Enable alpha checkers explicitly. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, [('alpha', True)]) + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('alpha', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), security_profile_alpha)) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), default_profile)) # Enable "security" profile checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('profile:security', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), security_profile_alpha)) @@ -195,8 +187,8 @@ def f(checks, checkers): (cfg_handler.checks(), default_profile)) # Enable "security" profile checkers without "profile:" prefix. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('security', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), security_profile_alpha)) @@ -204,64 +196,43 @@ def f(checks, checkers): (cfg_handler.checks(), default_profile)) # Enable "sei-cert" guideline checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('guideline:sei-cert', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), cert_guideline)) # Enable "sei-cert" guideline checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('sei-cert', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), cert_guideline)) # Disable "sei-cert" guideline checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('guideline:sei-cert', False)]) self.assertTrue(all_with_status(CheckerState.disabled) (cfg_handler.checks(), cert_guideline)) # Disable "sei-cert" guideline checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('sei-cert', False)]) self.assertTrue(all_with_status(CheckerState.disabled) (cfg_handler.checks(), cert_guideline)) # Enable "LOW" severity checkers. - cfg_handler = ClangSA.construct_config_handler(args, context) - cfg_handler.initialize_checkers(context, checkers, + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, [('severity:LOW', True)]) self.assertTrue(all_with_status(CheckerState.enabled) (cfg_handler.checks(), low_severity)) -class MockContextTidy: - class CheckerLabels: - def checkers_by_labels(self, labels): - return [] - - def get_description(self, checker): - return [] - - def occurring_values(self, checker): - return [] - - path_env_extra = None - ld_lib_path_extra = None - checker_plugin = None - analyzer_binaries = {'clang-tidy': 'clang-tidy'} - checker_labels = CheckerLabels() - available_profiles = ['profile1'] - package_root = './' - - def create_analyzer_tidy(args=[]): - context = MockContextTidy() - cfg_handler = ClangTidy.construct_config_handler(args, context) + cfg_handler = ClangTidy.construct_config_handler(args) action = { 'file': 'main.cpp',