Skip to content

Commit

Permalink
Merge pull request #3587 from csordasmarton/handle_relative_path_in_c…
Browse files Browse the repository at this point in the history
…ompilation_db

[analyzer] Handle relative file paths in compilation database
  • Loading branch information
csordasmarton authored Mar 1, 2022
2 parents 3b57c1a + dd017cd commit f2beb60
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ def postprocess_result(self, skip_handler: Optional[SkipListHandler]):
"""
if os.path.exists(self.analyzer_result_file):
reports = report_file.get_reports(
self.analyzer_result_file, self.checker_labels)
self.analyzer_result_file, self.checker_labels,
source_dir_path=self.source_dir_path)
reports = [r for r in reports if not r.skip(skip_handler)]

hash_type = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ def buildaction(self):
"""
return self.__buildaction

@property
def source_dir_path(self):
""" Get directory path of the compiled source file. """
return os.path.normpath(os.path.join(
os.getcwd(), self.__buildaction.directory))

@property
def workspace(self):
"""
Expand Down
62 changes: 62 additions & 0 deletions analyzer/tests/functional/analyze/test_analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from libtest import env

from codechecker_report_converter.report import report_file
from codechecker_analyzer.analyzers.clangsa import version


Expand Down Expand Up @@ -1092,3 +1093,64 @@ def test_invalid_compilation_database(self):
process.communicate()

self.assertEqual(process.returncode, 1)

def test_compilation_db_relative_file_path(self):
"""
Test relative path in compilation database.
If the file/directory paths in the compilation database are relative
ClangSA analyzer will generate plist files where the file paths are
also relative to the current directory where the analyzer was executed.
After the plist files are created, report converter will try to
post-process these files and creates absolute paths from the relative
paths. This test will check whether these files paths are exist.
"""
test_dir = os.path.join(self.test_workspace, "test_rel_file_path")
os.makedirs(test_dir)

source_file_name = "success.c"
shutil.copy(os.path.join(self.test_dir, source_file_name), test_dir)

cc_files_dir_path = os.path.join(test_dir, "codechecker_files")
os.makedirs(cc_files_dir_path, exist_ok=True)

build_json = os.path.join(cc_files_dir_path, "build.json")
report_dir = os.path.join(cc_files_dir_path, "reports")

# Create a compilation database.
build_log = [{
"directory": ".",
"command": f"cc -c {source_file_name} -o /dev/null",
"file": source_file_name}]

with open(build_json, 'w',
encoding="utf-8", errors="ignore") as outfile:
json.dump(build_log, outfile)

# Analyze the project
analyze_cmd = [
self._codechecker_cmd, "analyze",
build_json,
"--report-hash", "context-free-v2",
"-o", report_dir,
"--clean"]

process = subprocess.Popen(
analyze_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=test_dir,
encoding="utf-8",
errors="ignore")
process.communicate()

errcode = process.returncode
self.assertEqual(errcode, 0)

# Test that file paths in plist files are exist.
plist_files = glob.glob(os.path.join(report_dir, '*.plist'))
for plist_file in plist_files:
reports = report_file.get_reports(plist_file)
for r in reports:
for file in r.files:
self.assertTrue(os.path.exists(file.original_path))
4 changes: 4 additions & 0 deletions docs/analyzer/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,10 @@ Example:
CodeChecker analyze ../codechecker_myProject_build.log -o my_plists
```

**Note**: If your compilation database log file contains relative paths you
have to make sure that you run the analysis command from the same directory
as the logger was run (i.e. that paths are relative to).

`CodeChecker analyze` supports a myriad of fine-tuning arguments, explained
below:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ def parse(fp: BinaryIO):

def get_file_index_map(
plist: Any,
analyzer_result_dir_path: str,
source_dir_path: str,
file_cache: Dict[str, File]
) -> Dict[int, File]:
""" Get file index map from the given plist object. """
file_index_map: Dict[int, File] = {}

for i, orig_file_path in enumerate(plist.get('files', [])):
file_path = os.path.normpath(os.path.join(
analyzer_result_dir_path, orig_file_path))
source_dir_path, orig_file_path))
file_index_map[i] = get_or_create_file(file_path, file_cache)

return file_index_map
Expand All @@ -183,11 +183,15 @@ def get_file_index_map(
class Parser(BaseParser):
def get_reports(
self,
analyzer_result_file_path: str
analyzer_result_file_path: str,
source_dir_path: Optional[str] = None
) -> List[Report]:
""" Get reports from the given analyzer result file. """
reports: List[Report] = []

if not source_dir_path:
source_dir_path = os.path.dirname(analyzer_result_file_path)

try:
with open(analyzer_result_file_path, 'rb') as fp:
plist = parse(fp)
Expand All @@ -196,10 +200,9 @@ def get_reports(
return reports

metadata = plist.get('metadata')
analyzer_result_dir_path = os.path.dirname(
analyzer_result_file_path)

files = get_file_index_map(
plist, analyzer_result_dir_path, self._file_cache)
plist, source_dir_path, self._file_cache)

for diag in plist.get('diagnostics', []):
report = self.__create_report(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ def get_parser(
def get_reports(
analyzer_result_file_path: str,
checker_labels: Optional[CheckerLabels] = None,
file_cache: Optional[Dict[str, File]] = None
file_cache: Optional[Dict[str, File]] = None,
source_dir_path: Optional[str] = None
) -> List[Report]:
""" Get reports from the given report file. """
parser = get_parser(analyzer_result_file_path, checker_labels, file_cache)

if parser:
return parser.get_reports(analyzer_result_file_path)
return parser.get_reports(analyzer_result_file_path, source_dir_path)

return []

Expand Down

0 comments on commit f2beb60

Please sign in to comment.