Skip to content

Commit

Permalink
Merge branch 'branch-2-backup' of https://github.com/feyruzb/codechecker
Browse files Browse the repository at this point in the history
 into branch-2-backup
  • Loading branch information
feyruzb committed Sep 24, 2024
2 parents ebd1aa3 + 9595370 commit 5256b0e
Show file tree
Hide file tree
Showing 20 changed files with 171 additions and 137 deletions.
5 changes: 3 additions & 2 deletions analyzer/codechecker_analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def perform_analysis(args, skip_handlers, rs_handler: ReviewStatusHandler,
enabled_checkers[analyzer].append(check)

version = analyzer_types.supported_analyzers[analyzer] \
.get_binary_version(context.get_analyzer_env(analyzer))
.get_binary_version()
metadata_info['analyzer_statistics']['version'] = version

metadata_tool['analyzers'][analyzer] = metadata_info
Expand Down Expand Up @@ -355,7 +355,8 @@ def perform_analysis(args, skip_handlers, rs_handler: ReviewStatusHandler,
end_time = time.time()
LOG.info("Analysis length: %s sec.", end_time - start_time)

analyzer_types.print_unsupported_analyzers(errored)
if args.analyzers:
analyzer_types.print_unsupported_analyzers(errored)

metadata_tool['timestamps'] = {'begin': start_time,
'end': end_time}
Expand Down
50 changes: 34 additions & 16 deletions analyzer/codechecker_analyzer/analyzer_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from codechecker_common.checker_labels import CheckerLabels
from codechecker_common.singleton import Singleton
from codechecker_common.util import load_json
from pathlib import Path

from . import env

Expand Down Expand Up @@ -62,8 +63,13 @@ def __init__(self):
self.__package_build_date = None
self.__package_git_hash = None
self.__analyzers = {}
self.__analyzer_envs = {}

# CodeChecker's current runtime environment
self.__cc_env = None
# cc_env extended with packaged LD_LIBRARY_PATH for packaged binaries
self.__package_env = None
# Original caller environment of CodeChecker for external binaries
self.__original_env = None

self.logger_lib_dir_path = os.path.join(
self._data_files_dir_path, 'ld_logger', 'lib')
Expand Down Expand Up @@ -155,6 +161,9 @@ def __init_env(self):
self.env_vars['cc_logger_compiles'])
self.ld_preload = os.environ.get(self.env_vars['ld_preload'])
self.ld_lib_path = self.env_vars['env_ld_lib_path']
self.__original_env = env.get_original_env()
self.__package_env = env.extend(self.path_env_extra,
self.ld_lib_path_extra)

def __set_version(self):
"""
Expand Down Expand Up @@ -186,6 +195,29 @@ def __set_version(self):
logger.DEBUG_ANALYZER):
self.__package_git_tag = package_git_dirtytag

def get_env_for_bin(self, binary):
"""
binary must be a binary with full path
Returns the correct environment for binaries called by CodeChecker.
For binaries packaged with CodeChecker the LD_LIBRARY path is extended.
For non-packaged binaries, the original calling environment
is returned.
"""
bin_path = Path(binary).resolve()
if not bin_path.exists():
LOG.error("Binary %s not found", binary)
return None

codechecker_dir = Path(self._data_files_dir_path)

if str(bin_path).startswith(str(codechecker_dir)):
LOG.debug("Package env is returned for %s", bin_path)
return self.__package_env
else:
LOG.debug("Original env is returned for %s", bin_path)
return self.__original_env

def __populate_analyzers(self):
""" Set analyzer binaries for each registered analyzers. """
cc_env = None
Expand All @@ -198,9 +230,8 @@ def __populate_analyzers(self):
compiler_binaries = self.pckg_layout.get('analyzers')
for name, value in compiler_binaries.items():
if name in env_var_bin:
# For non-packaged analyzers the original env is set.
# env_var_bin has priority over package config and PATH
self.__analyzers[name] = env_var_bin[name]
self.__analyzer_envs[name] = env.get_original_env()
continue

if analyzer_from_path:
Expand All @@ -210,10 +241,6 @@ def __populate_analyzers(self):
# Check if it is a package relative path.
self.__analyzers[name] = os.path.join(
self._data_files_dir_path, value)
# For packaged analyzers the ld_library path
# must be extended with the packed libs.
self.__analyzer_envs[name] =\
env.extend(self.path_env_extra, self.ld_lib_path_extra)
else:
env_path = cc_env['PATH'] if cc_env else None
compiler_binary = which(cmd=value, path=env_path)
Expand All @@ -224,17 +251,12 @@ def __populate_analyzers(self):
continue

self.__analyzers[name] = os.path.realpath(compiler_binary)
# For non-packaged analyzers the original env is set.
self.__analyzer_envs[name] = env.get_original_env()

# If the compiler binary is a simlink to ccache, use the
# original compiler binary.
if self.__analyzers[name].endswith("/ccache"):
self.__analyzers[name] = compiler_binary

def get_analyzer_env(self, analyzer_name):
return self.__analyzer_envs.get(analyzer_name)

def __populate_replacer(self):
""" Set clang-apply-replacements tool. """
replacer_binary = self.pckg_layout.get('clang-apply-replacements')
Expand All @@ -243,12 +265,8 @@ def __populate_replacer(self):
# Check if it is a package relative path.
self.__replacer = os.path.join(self._data_files_dir_path,
replacer_binary)
self.__analyzer_envs['clang-apply-replacements'] =\
env.extend(self.path_env_extra, self.ld_lib_path_extra)
else:
self.__replacer = which(replacer_binary)
self.__analyzer_envs['clang-apply-replacements'] =\
env.get_original_env()

@property
def version(self):
Expand Down
23 changes: 9 additions & 14 deletions analyzer/codechecker_analyzer/analyzers/analyzer_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def resolve_missing_binary(cls, configured_binary, environ):

@classmethod
@abstractmethod
def get_binary_version(cls, environ, details=False) -> str:
def get_binary_version(cls, details=False) -> str:
"""
Return the version number of the binary that CodeChecker found, even
if its incompatible. If details is true, additional version information
Expand All @@ -68,7 +68,7 @@ def get_binary_version(cls, environ, details=False) -> str:
raise NotImplementedError("Subclasses should implement this!")

@classmethod
def is_binary_version_incompatible(cls, environ) -> Optional[str]:
def is_binary_version_incompatible(cls) -> Optional[str]:
"""
CodeChecker can only execute certain versions of analyzers.
Returns a error object (an optional string). If the return value is
Expand Down Expand Up @@ -102,7 +102,7 @@ def construct_result_handler(self, buildaction, report_output,
"""
raise NotImplementedError("Subclasses should implement this!")

def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None):
def analyze(self, analyzer_cmd, res_handler, proc_callback=None):
"""
Run the analyzer.
"""
Expand All @@ -111,17 +111,12 @@ def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None):
LOG.debug_analyzer('\n%s',
' '.join([shlex.quote(x) for x in analyzer_cmd]))

if env is None:
env = analyzer_context.get_context()\
.get_analyzer_env(self.ANALYZER_NAME)

res_handler.analyzer_cmd = analyzer_cmd
try:
ret_code, stdout, stderr \
= SourceAnalyzer.run_proc(analyzer_cmd,
res_handler.buildaction.directory,
proc_callback,
env)
proc_callback)
res_handler.analyzer_returncode = ret_code
res_handler.analyzer_stdout = stdout
res_handler.analyzer_stderr = stderr
Expand All @@ -145,7 +140,7 @@ def post_analyze(self, result_handler):
"""

@staticmethod
def run_proc(command, cwd=None, proc_callback=None, env=None):
def run_proc(command, cwd=None, proc_callback=None):
"""
Just run the given command and return the return code
and the stdout and stderr outputs of the process.
Expand All @@ -161,11 +156,11 @@ def signal_handler(signum, _):

signal.signal(signal.SIGINT, signal_handler)

if env is None:
env = analyzer_context.get_context().cc_env
env = analyzer_context.get_context().get_env_for_bin(command[0])

LOG.debug_analyzer('\nENV:\n')
LOG.debug_analyzer(env)
LOG.debug('\nexecuting:%s\n', command)
LOG.debug('\nENV:\n')
LOG.debug(env)

proc = subprocess.Popen(
command,
Expand Down
14 changes: 7 additions & 7 deletions analyzer/codechecker_analyzer/analyzers/analyzer_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ def is_ignore_conflict_supported():
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=context
.get_analyzer_env(
os.path.basename(context.replacer_binary)),
.get_env_for_bin(
context.replacer_binary),
encoding="utf-8", errors="ignore")
out, _ = proc.communicate()
return '--ignore-insert-conflict' in out
Expand All @@ -115,7 +115,7 @@ def is_ignore_conflict_supported():
def print_unsupported_analyzers(errored):
""" Print error messages which occured during analyzer detection. """
for analyzer_binary, reason in errored:
LOG.warning("Analyzer '%s' is enabled but CodeChecker is failed to "
LOG.warning("Analyzer '%s' is enabled but CodeChecker failed to "
"execute analysis with it: '%s'. Please check your "
"'PATH' environment variable and the "
"'config/package_layout.json' file!",
Expand Down Expand Up @@ -159,7 +159,6 @@ def check_supported_analyzers(analyzers):
"""

context = analyzer_context.get_context()
check_env = context.cc_env

analyzer_binaries = context.analyzer_binaries

Expand All @@ -182,7 +181,8 @@ def check_supported_analyzers(analyzers):
elif not os.path.isabs(analyzer_bin):
# If the analyzer is not in an absolute path, try to find it...
found_bin = supported_analyzers[analyzer_name].\
resolve_missing_binary(analyzer_bin, check_env)
resolve_missing_binary(analyzer_bin,
context.get_env_for_bin(analyzer_bin))

# found_bin is an absolute path, an executable in one of the
# PATH folders.
Expand All @@ -201,7 +201,7 @@ def check_supported_analyzers(analyzers):
# Check version compatibility of the analyzer binary.
if analyzer_bin:
analyzer = supported_analyzers[analyzer_name]
error = analyzer.is_binary_version_incompatible(check_env)
error = analyzer.is_binary_version_incompatible()
if error:
failed_analyzers.add((analyzer_name,
f"Incompatible version: {error} "
Expand All @@ -211,7 +211,7 @@ def check_supported_analyzers(analyzers):
available_analyzer = False

if not analyzer_bin or \
not host_check.check_analyzer(analyzer_bin, check_env):
not host_check.check_analyzer(analyzer_bin):
# Analyzers unavailable under absolute paths are deliberately a
# configuration problem.
failed_analyzers.add((analyzer_name,
Expand Down
14 changes: 9 additions & 5 deletions analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def parse_clang_help_page(
command,
stderr=subprocess.STDOUT,
env=analyzer_context.get_context()
.get_analyzer_env(ClangSA.ANALYZER_NAME),
.get_env_for_bin(command[0]),
universal_newlines=True,
encoding="utf-8",
errors="ignore")
Expand Down Expand Up @@ -172,8 +172,11 @@ def __add_plugin_load_flags(cls, analyzer_cmd: List[str]):
analyzer_cmd.extend(["-load", plugin])

@classmethod
def get_binary_version(cls, environ, details=False) -> str:
def get_binary_version(cls, details=False) -> str:
# No need to LOG here, we will emit a warning later anyway.

environ = analyzer_context.get_context().get_env_for_bin(
cls.analyzer_binary())
if not cls.analyzer_binary():
return None

Expand Down Expand Up @@ -209,7 +212,7 @@ def ctu_capability(cls):
cls.__ctu_autodetection = CTUAutodetection(
cls.analyzer_binary(),
analyzer_context.get_context()
.get_analyzer_env(ClangSA.ANALYZER_NAME))
.get_env_for_bin(cls.analyzer_binary()))

return cls.__ctu_autodetection

Expand Down Expand Up @@ -587,7 +590,7 @@ def resolve_missing_binary(cls, configured_binary, environ):
return clang

@classmethod
def is_binary_version_incompatible(cls, environ):
def is_binary_version_incompatible(cls):
"""
We support pretty much every ClangSA version.
"""
Expand All @@ -609,7 +612,8 @@ def construct_result_handler(self, buildaction, report_output,
def construct_config_handler(cls, args):

context = analyzer_context.get_context()
environ = context.get_analyzer_env(ClangSA.ANALYZER_NAME)
environ = context.get_env_for_bin(
cls.analyzer_binary())

handler = config_handler.ClangSAConfigHandler(environ)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import subprocess

from codechecker_common.logger import get_logger
from codechecker_analyzer import analyzer_context
from codechecker_analyzer import host_check
from codechecker_analyzer.analyzers.clangsa import version

Expand Down Expand Up @@ -75,7 +76,7 @@ def ctu_mapping(clang_version_info):
return None, None


def invoke_binary_checked(binary_path, args=None, environ=None):
def invoke_binary_checked(binary_path, args=None):
"""
Invoke the binary with the specified args, and return the output if the
command finished running with zero exit code. Return False otherwise.
Expand All @@ -91,6 +92,7 @@ def invoke_binary_checked(binary_path, args=None, environ=None):
args = args or []
invocation = [binary_path]
invocation.extend(args)
environ = analyzer_context.get_context().get_env_for_bin(binary_path)
try:
output = subprocess.check_output(
invocation,
Expand Down Expand Up @@ -123,7 +125,7 @@ def __init__(self, analyzer_binary, environ):
return

analyzer_version = invoke_binary_checked(
self.__analyzer_binary, ['--version'], self.environ)
self.__analyzer_binary, ['--version'])

if analyzer_version is False:
LOG.debug('Failed to invoke command to get Clang version!')
Expand Down Expand Up @@ -222,7 +224,7 @@ def is_ctu_capable(self):
if not tool_path:
return False

return invoke_binary_checked(tool_path, ['-version'], self.environ) \
return invoke_binary_checked(tool_path, ['-version']) \
is not False

@property
Expand All @@ -233,8 +235,7 @@ def is_on_demand_ctu_available(self):
"""

analyzer_options = invoke_binary_checked(
self.__analyzer_binary, ['-cc1', '-analyzer-config-help'],
self.environ)
self.__analyzer_binary, ['-cc1', '-analyzer-config-help'])

if analyzer_options is False:
return False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,10 @@ def generate_ast(triple_arch, action, source, config):
os.makedirs(ast_dir)
except OSError:
pass

cmdstr = ' '.join(cmd)
LOG.debug_analyzer("Generating AST using '%s'", cmdstr)
ret_code, _, err = \
analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory)
analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory, None)

if ret_code != 0:
LOG.error("Error generating AST.\n\ncommand:\n\n%s\n\nstderr:\n\n%s",
Expand Down
2 changes: 1 addition & 1 deletion analyzer/codechecker_analyzer/analyzers/clangsa/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def get(clang_binary):
"""
compiler_version = subprocess.check_output(
[clang_binary, '--version'],
env=analyzer_context.get_context().get_analyzer_env("clangsa"),
env=analyzer_context.get_context().get_env_for_bin(clang_binary),
encoding="utf-8",
errors="ignore")
version_parser = ClangVersionInfoParser(clang_binary)
Expand Down
Loading

0 comments on commit 5256b0e

Please sign in to comment.