Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions analyzer/codechecker_analyzer/buildlog/log_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1184,30 +1184,40 @@ def extend_compilation_database_entries(compilation_database):
cmd = []
source_files = []
source_dir = entry['directory']
response_files = set()

options = shlex.split(entry['command'])
for opt in options:
if opt.startswith('@'):
response_file = os.path.join(source_dir, opt[1:])
if not os.path.exists(response_file):
source_file = os.path.join(source_dir, opt)

if os.path.exists(response_file):
opts, sources = process_response_file(response_file)
cmd.extend([shlex.quote(x) for x in opts])
source_files.extend(sources)
response_files.add(os.path.abspath(response_file))
elif os.path.exists(source_file):
cmd.append(shlex.quote(opt))
else:
LOG.warning("Response file '%s' does not exists.",
response_file)
continue

opts, sources = process_response_file(response_file)
cmd.extend([shlex.quote(x) for x in opts])
source_files.extend(sources)
else:
cmd.append(shlex.quote(opt))

entry['command'] = ' '.join(cmd)

if entry['file'].startswith('@'):
for source_file in source_files:
new_entry = dict(entry)
new_entry['file'] = source_file
yield new_entry
continue
response_file = os.path.abspath(os.path.join(
source_dir, entry['file'][1:]))

if response_file in response_files:
for source_file in source_files:
new_entry = dict(entry)
new_entry['file'] = source_file
yield new_entry
continue

yield entry

Expand Down
31 changes: 31 additions & 0 deletions analyzer/tests/unit/test_log_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,37 @@ def test_source_file_contains_at_sign(self):
build_action = build_actions[0]
self.assertEqual(build_action.source, src_file_path)

def test_source_file_path_starts_with_at_sign(self):
"""
Test source file where the path starts with '@'.

Such paths should not be handled as response files if the path itself
exists as a normal source file.
"""
src_dir = os.path.join(self.tmp_dir, "@folder")
os.mkdir(src_dir)
src_file_path = os.path.join(src_dir, "main.cpp")

with open(src_file_path, "w",
encoding="utf-8", errors="ignore") as src_file:
src_file.write("int main() { return 0; }")

with open(self.compile_command_file_path, "w",
encoding="utf-8", errors="ignore") as build_json:
build_json.write(json.dumps([{
"directory": self.tmp_dir,
"command": "g++ @folder/main.cpp",
"file": "@folder/main.cpp"
}]))

build_actions, _ = log_parser.parse_unique_log(load_json(
self.compile_command_file_path))

self.assertEqual(len(build_actions), 1)

build_action = build_actions[0]
self.assertEqual(build_action.source, src_file_path)

def test_symlink(self):
"""
Test if each source file is analyzed exclusively once,
Expand Down
Loading