Skip to content

Commit 4e8aca5

Browse files
committed
[fix] Compiler path can be relative in compile_commands.json
When a compiler is given by relative path in compile_commands.json then it should be considered as relative to the "directory" component of the build action. Fixes #4340
1 parent 65c5db5 commit 4e8aca5

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

analyzer/codechecker_analyzer/buildlog/log_parser.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ def get_language(extension):
646646
return mapping.get(extension)
647647

648648

649-
def determine_compiler(gcc_command, is_executable_compiler_fun):
649+
def determine_compiler(gcc_command, cwd, is_executable_compiler_fun):
650650
"""
651651
This function determines the compiler from the given compilation command.
652652
If the first part of the gcc_command is ccache invocation then the rest
@@ -672,12 +672,39 @@ def determine_compiler(gcc_command, is_executable_compiler_fun):
672672
TODO: The second case could be handled if there was a way for querying the
673673
used compiler from ccache. This can be configured for ccache in config
674674
files or environment variables.
675+
676+
In a compilation database the relative paths in "command" section are
677+
relative to the "directory" section. This is supposed to apply to the
678+
compiler itself, too:
679+
680+
{
681+
"directory": "/my/path",
682+
"command": "../compiler/gcc main.c",
683+
"file": "main.c"
684+
}
685+
686+
However, if the compiler name is provided only, then the intuition is that
687+
it should be searched in PATH instead of /my/path/gcc:
688+
689+
{
690+
"directory": "/my/path",
691+
"command": "gcc main.c",
692+
"file": "main.c"
693+
}
694+
695+
If in the future we decide to consider this relative path applied to
696+
"directory" section, then it can be fixed here.
675697
"""
698+
compiler = gcc_command[0]
699+
676700
if gcc_command[0].endswith('ccache'):
677701
if is_executable_compiler_fun(gcc_command[1]):
678-
return gcc_command[1]
702+
compiler = gcc_command[1]
703+
704+
if not Path(compiler).is_absolute() and os.sep in compiler:
705+
compiler = str((Path(cwd) / compiler).resolve())
679706

680-
return gcc_command[0]
707+
return compiler
681708

682709

683710
def __is_not_include_fixed(dirname):
@@ -989,6 +1016,7 @@ def parse_options(compilation_db_entry,
9891016
details['action_type'] = None
9901017
details['compiler'] =\
9911018
determine_compiler(gcc_command,
1019+
details['directory'],
9921020
ImplicitCompilerInfo.is_executable_compiler)
9931021
if '++' in os.path.basename(details['compiler']):
9941022
details['lang'] = 'c++'

analyzer/tests/unit/test_option_parser.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,28 +464,59 @@ def test_compiler_toolchain(self):
464464
def test_ccache_compiler(self):
465465
compiler_action = shlex.split('ccache g++ main.cpp')
466466
res = log_parser.determine_compiler(compiler_action,
467+
"",
467468
self.is_compiler_executable_fun)
468469
self.assertEqual(res, 'g++')
469470

470471
compiler_action = shlex.split('ccache main.cpp')
471472
res = log_parser.determine_compiler(
472473
compiler_action,
474+
"",
473475
self.is_compiler_executable_fun_false)
474476
self.assertEqual(res, 'ccache')
475477

476478
compiler_action = shlex.split('ccache -Ihello main.cpp')
477479
res = log_parser.determine_compiler(
478480
compiler_action,
481+
"",
479482
self.is_compiler_executable_fun_false)
480483
self.assertEqual(res, 'ccache')
481484

482485
compiler_action = shlex.split('/usr/lib/ccache/g++ -Ihello main.cpp')
483486
res = log_parser.determine_compiler(
484487
compiler_action,
488+
"",
485489
self.is_compiler_executable_fun_false)
486490
self.assertEqual(res,
487491
'/usr/lib/ccache/g++')
488492

493+
def test_relative_compiler_path(self):
494+
# When the compiler is provided by absolute path, it should be used
495+
# directly.
496+
compiler_action = shlex.split('/absolute/path/gcc main.c')
497+
res = log_parser.determine_compiler(compiler_action,
498+
'/path/to/project',
499+
self.is_compiler_executable_fun)
500+
self.assertEqual(res, '/absolute/path/gcc')
501+
502+
# When the compiler is provided by relative path, it should be relative
503+
# to the directory section.
504+
compiler_action = shlex.split('../gcc main.c')
505+
res = log_parser.determine_compiler(compiler_action,
506+
'/path/to/project',
507+
self.is_compiler_executable_fun)
508+
self.assertEqual(res, '/path/to/gcc')
509+
510+
# When the compiler is relative, but only the compiler name is provided
511+
# then we searh it in the PATH.
512+
# !!!WARNING!!! Probably this is not the official interpretation of
513+
# the compilation database format.
514+
compiler_action = shlex.split('gcc main.c')
515+
res = log_parser.determine_compiler(compiler_action,
516+
'/path/to/project',
517+
self.is_compiler_executable_fun)
518+
self.assertEqual(res, 'gcc')
519+
489520
@unittest.skipUnless(
490521
'clang' in pathlib.Path(shutil.which('g++')).resolve().name,
491522
"If gcc or g++ is a symlink to clang this test should be "

0 commit comments

Comments
 (0)