diff --git a/analyzer/codechecker_analyzer/analyzer_context.py b/analyzer/codechecker_analyzer/analyzer_context.py index c55c66dabf..9ff3363011 100644 --- a/analyzer/codechecker_analyzer/analyzer_context.py +++ b/analyzer/codechecker_analyzer/analyzer_context.py @@ -12,6 +12,7 @@ # pylint: disable=no-name-in-module from distutils.spawn import find_executable +from argparse import ArgumentTypeError import os import platform @@ -20,6 +21,7 @@ from pathlib import Path from codechecker_common import logger +from codechecker_analyzer.arg import analyzer_binary from codechecker_common.checker_labels import CheckerLabels from codechecker_common.singleton import Singleton from codechecker_common.util import load_json @@ -90,6 +92,37 @@ def __init__(self): self.__populate_analyzers() self.__populate_replacer() + def __parse_CC_ANALYZER_BIN(self): + env_var_bins = {} + if 'CC_ANALYZER_BIN' in self.__analyzer_env: + had_error = False + for value in self.__analyzer_env['CC_ANALYZER_BIN'].split(';'): + try: + analyzer_name, path = analyzer_binary(value) + except ArgumentTypeError as e: + LOG.error(e) + had_error = True + continue + + if not os.path.isfile(path): + LOG.error(f"'{path}' is not a path to an analyzer binary " + "given to CC_ANALYZER_BIN!") + had_error = True + + if had_error: + continue + + LOG.info(f"Using '{path}' for analyzer '{analyzer_name}'") + env_var_bins[analyzer_name] = path + + if had_error: + LOG.info("The value of CC_ANALYZER_BIN should be in the" + "format of " + "CC_ANALYZER_BIN=':/path/to/bin1;" + ":/path/to/bin2'") + sys.exit(1) + return env_var_bins + def __get_package_config(self): """ Get package configuration. """ pckg_config_file = os.path.join( @@ -166,8 +199,13 @@ def __populate_analyzers(self): if not analyzer_from_path: analyzer_env = self.analyzer_env + env_var_bin = self.__parse_CC_ANALYZER_BIN() + compiler_binaries = self.pckg_layout.get('analyzers') for name, value in compiler_binaries.items(): + if name in env_var_bin: + self.__analyzers[name] = env_var_bin[name] + continue if analyzer_from_path: value = os.path.basename(value) @@ -182,6 +220,7 @@ def __populate_analyzers(self): if not compiler_binary: LOG.debug("'%s' binary can not be found in your PATH!", value) + self.__analyzers[name] = None continue self.__analyzers[name] = os.path.realpath(compiler_binary) diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py index 8882e38c9f..060a89a7f8 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py @@ -189,7 +189,10 @@ def check_supported_analyzers(analyzers): error = analyzer.is_binary_version_incompatible(check_env) if error: failed_analyzers.add((analyzer_name, - f"Incompatible version: {error}")) + f"Incompatible version: {error} " + "Maybe try setting an absolute path to " + "a different analyzer binary via the " + "env variable CC_ANALYZER_BIN?")) available_analyzer = False if not analyzer_bin or \ diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py index 02b32580fa..befcfb7e72 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py @@ -172,6 +172,10 @@ def __add_plugin_load_flags(cls, analyzer_cmd: List[str]): @classmethod def get_binary_version(self, environ, details=False) -> str: + # No need to LOG here, we will emit a warning later anyway. + if not self.analyzer_binary(): + return None + if details: version = [self.analyzer_binary(), '--version'] else: diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py index 5d012750b5..4610823508 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py @@ -231,6 +231,10 @@ def analyzer_binary(cls): @classmethod def get_binary_version(self, environ, details=False) -> str: + # No need to LOG here, we will emit a warning later anyway. + if not self.analyzer_binary(): + return None + version = [self.analyzer_binary(), '--version'] try: output = subprocess.check_output(version, diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py index c8cfbc535f..d4b9e6ad96 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py @@ -85,6 +85,9 @@ def analyzer_binary(cls): @classmethod def get_binary_version(self, environ, details=False) -> str: """ Get analyzer version information. """ + # No need to LOG here, we will emit a warning later anyway. + if not self.analyzer_binary(): + return None version = [self.analyzer_binary(), '--version'] try: output = subprocess.check_output(version, diff --git a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py index 39c3c0ead3..00358e9724 100644 --- a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py @@ -173,6 +173,9 @@ def get_binary_version(self, environ, details=False) -> str: """ Return the analyzer version. """ + # No need to LOG here, we will emit a warning later anyway. + if not self.analyzer_binary(): + return None if details: version = [self.analyzer_binary(), '--version'] else: diff --git a/analyzer/codechecker_analyzer/arg.py b/analyzer/codechecker_analyzer/arg.py index afdc664dcc..6d49cecabf 100644 --- a/analyzer/codechecker_analyzer/arg.py +++ b/analyzer/codechecker_analyzer/arg.py @@ -18,6 +18,8 @@ 'AnalyzerConfig', ["analyzer", "option", "value"]) CheckerConfig = collections.namedtuple( "CheckerConfig", ["analyzer", "checker", "option", "value"]) +AnalyzerBinary = collections.namedtuple( + "AnalyzerBinary", ["analyzer", "path"]) class OrderedCheckersAction(argparse.Action): @@ -133,3 +135,18 @@ def checker_config(arg: str) -> CheckerConfig: return CheckerConfig( m.group("analyzer"), m.group("checker"), m.group("option"), m.group("value")) + + +def analyzer_binary(arg: str) -> AnalyzerBinary: + """ + This function can be used at "type" argument of argparse.add_argument(). + It checks the format of --analyzer_binary flag: : + """ + m = re.match(r"(?P.+):(?P.+)", arg) + + if not m: + raise argparse.ArgumentTypeError( + f"Analyzer binary in wrong format: {arg}, should be " + ":") + + return AnalyzerBinary(m.group("analyzer"), m.group("path")) diff --git a/analyzer/codechecker_analyzer/cmd/analyze.py b/analyzer/codechecker_analyzer/cmd/analyze.py index dd3fe1f8fe..320a8a8426 100644 --- a/analyzer/codechecker_analyzer/cmd/analyze.py +++ b/analyzer/codechecker_analyzer/cmd/analyze.py @@ -44,6 +44,11 @@ epilog_env_var = f""" CC_ANALYZERS_FROM_PATH Set to `yes` or `1` to enforce taking the analyzers from the `PATH` instead of the given binaries. + CC_ANALYZER_BIN Set the absolute paths of an analyzer binaries. + Overrides other means of CodeChecker getting hold of + binary. + Format: CC_ANALYZER_BIN=':/path/to/bin1; + :/path/to/bin2' CC_CLANGSA_PLUGIN_DIR If the CC_ANALYZERS_FROM_PATH environment variable is set you can configure the plugin directory of the Clang Static Analyzer by using this environment diff --git a/docs/analyzer/user_guide.md b/docs/analyzer/user_guide.md index ca48db0c28..8c59b088f0 100644 --- a/docs/analyzer/user_guide.md +++ b/docs/analyzer/user_guide.md @@ -499,6 +499,11 @@ Environment variables for 'CodeChecker analyze' command: CC_ANALYZERS_FROM_PATH Set to `yes` or `1` to enforce taking the analyzers from the `PATH` instead of the given binaries. + CC_ANALYZER_BIN Set the absolute paths of an analyzer binaries. + Overrides other means of CodeChecker getting hold of + binary. + Format: CC_ANALYZER_BIN=':/path/to/bin1; + :/path/to/bin2' CC_CLANGSA_PLUGIN_DIR If the CC_ANALYZERS_FROM_PATH environment variable is set you can configure the plugin directory of the Clang Static Analyzer by using this environment @@ -1031,6 +1036,11 @@ Environment variables CC_ANALYZERS_FROM_PATH Set to `yes` or `1` to enforce taking the analyzers from the `PATH` instead of the given binaries. + CC_ANALYZER_BIN Set the absolute paths of an analyzer binaries. + Overrides other means of CodeChecker getting hold of + binary. + Format: CC_ANALYZER_BIN=':/path/to/bin1; + :/path/to/bin2' CC_CLANGSA_PLUGIN_DIR If the CC_ANALYZERS_FROM_PATH environment variable is set you can configure the plugin directory of the Clang Static Analyzer by using this environment