Skip to content

Commit 2baef1c

Browse files
committed
Fix compilation database
Overrules the compilation database to change the compiler to to the exact llvm/clair compiler used to compile clair-c2py. This enables clair to find all systems path properly on complex machines (e.g. cluster) Two use cases: - when calling clair-c2py with -- option, on complex machines - when developping with e.g. gcc: the default compilation database would use gcc, we need to change it to clang for the command line
1 parent 653d9d5 commit 2baef1c

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

src/clu/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ target_link_libraries(clu PRIVATE fmt::fmt clang_llvm)
1212
target_include_directories(clu PRIVATE ${PROJECT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
1313
target_compile_definitions(clu PRIVATE CLANG_RESOURCE_DIR="${CLANG_RESOURCE_DIR}"
1414
SDKROOT="${SDKROOT}"
15+
CMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}"
1516
GIT_HASH="${PROJECT_GIT_HASH}")

src/clu/cmd_line_arg.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ namespace fs = std::filesystem;
1010
using namespace std::string_literals;
1111

1212
static const std::string resource_dir = CLANG_RESOURCE_DIR;
13+
static const std::string cxx_compiler = CMAKE_CXX_COMPILER;
1314

1415
namespace clu {
16+
std::string get_clang_compiler_path() { return cxx_compiler; }
17+
1518
clang::tooling::CommandLineArguments get_clang_additional_args_from_env_variables() {
1619

1720
#ifdef __APPLE__

src/clu/cmd_line_arg.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ namespace clu {
1414
// include e.g. on stdlib will fail
1515
clang::tooling::CommandLineArguments get_clang_additional_args_from_env_variables();
1616

17+
// Get the path to the clang++ compiler used to build clair
18+
std::string get_clang_compiler_path();
19+
1720
std::string get_git_hash();
1821
} // namespace clu
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
#include "clang/Tooling/CompilationDatabase.h"
3+
#include "clang/Tooling/Tooling.h"
4+
#include <vector>
5+
#include <string>
6+
7+
namespace clu {
8+
9+
/**
10+
* @brief Custom implementation of CompilationDatabase.
11+
*
12+
* This class provides a simple in-memory compilation database that stores
13+
* a vector of compile commands. It is useful when you need to:
14+
* - Modify compile commands from an existing database
15+
* - Create a compilation database programmatically
16+
* - Work with a fixed set of compile commands
17+
*/
18+
class custom_compilation_database : public clang::tooling::CompilationDatabase {
19+
std::vector<clang::tooling::CompileCommand> Commands;
20+
21+
public:
22+
/**
23+
* @brief Construct a custom compilation database from a vector of compile commands.
24+
* @param Cmds The compile commands to store in the database.
25+
*/
26+
custom_compilation_database(std::vector<clang::tooling::CompileCommand> Cmds) : Commands(std::move(Cmds)) {}
27+
28+
/**
29+
* @brief Get compile commands for a specific file.
30+
* @param FilePath The path to the file.
31+
* @return Vector of compile commands matching the given file path.
32+
*/
33+
std::vector<clang::tooling::CompileCommand> getCompileCommands(llvm::StringRef FilePath) const override;
34+
35+
/**
36+
* @brief Get all files in the compilation database.
37+
* @return Vector of file paths.
38+
*/
39+
std::vector<std::string> getAllFiles() const override;
40+
41+
/**
42+
* @brief Get all compile commands.
43+
* @return Vector of all compile commands stored in the database.
44+
*/
45+
std::vector<clang::tooling::CompileCommand> getAllCompileCommands() const override { return Commands; }
46+
};
47+
48+
} // namespace clu

src/tools/clair-c2py/main.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "clang/Tooling/CommonOptionsParser.h"
66
#include "clang/Basic/Version.h"
77

8+
#include "clu/custom_compilation_database.hpp"
89
#include "clu/cmd_line_arg.hpp"
910
#include "utility/macros.hpp"
1011
#include "utility/logger.hpp"
@@ -81,9 +82,60 @@ int main(int argc, const char **argv) try {
8182
config = read_configuration(config_filename);
8283
config._depfile_name = opt_depfile;
8384

84-
// ------- main tool
85+
// --- Correct the compilation database
86+
// Enforce that the compiler for source0 is the clang compiler used in compiling clair itself.
87+
// This a ABSOLUTELY necessary to ensure clair sees the system paths, has the right -resource-dir.
88+
// It is useful in at least 2 cases:
89+
// - call with -- options: the default "compiler" would be "clang-tool", and if the system path are not standard (i.e. in most cluster machine)
90+
// it would fail.
91+
// - when developing with another compiler, e.g. gcc.
92+
93+
auto &current_compdb = opt_parser->getCompilations();
94+
auto all_compile_cmds = current_compdb.getAllCompileCommands();
95+
// WARNING: Database behavior depends on the type of the compilation database:
96+
// - JSONCompilationDatabase (from compile_commands.json): getAllCompileCommands() returns all entries
97+
// - FixedCompilationDatabase (from -- arguments) : getAllCompileCommands() returns empty always
98+
// it can generate commands for any file on-demand via getCompileCommands(filename), so there's no finite "all" to return.
99+
// This seems to be a long-standing LLVM design choice [Cf Claude 4.5].
100+
// If future LLVM versions change this behavior, adjust the logic below accordingly.
101+
bool using_fixed_db = all_compile_cmds.empty();
102+
if (using_fixed_db) {
103+
llvm::errs() << "Calling with --. Getting compile commands for source file.\n";
104+
all_compile_cmds = current_compdb.getCompileCommands(source0);
105+
if (all_compile_cmds.empty())
106+
throw std::runtime_error("Internal Error: no compilation commands found or for " + source0 + "\n and none could be generated.");
107+
}
108+
109+
// Now fix the compiler for the compile command corresponding to source0.
110+
// Use canonical for comparison (to handle symlinks), but absolute for storage (ClangTool doesn't follow symlinks).
111+
auto canonical_source0 = fs::canonical(source0).string();
112+
for (auto &cmd : all_compile_cmds) {
113+
if (fs::canonical(cmd.Filename).string() == canonical_source0) {
114+
if (cmd.CommandLine.empty()) throw std::runtime_error("CompileCommand has empty CommandLine for " + source0);
115+
auto expected_compiler = clu::get_clang_compiler_path();
116+
if (cmd.CommandLine[0] != expected_compiler) {
117+
if (opt_verbose)
118+
llvm::errs() << "Warning: clair-c2py. When analyzing source file " << source0 << ", replacing compiler \n"
119+
<< cmd.CommandLine[0] << "\n with \n"
120+
<< expected_compiler << "\n";
121+
cmd.CommandLine[0] = expected_compiler;
122+
}
123+
// For FixedCompilationDatabase: update Filename to canonical path.
124+
// For JSONCompilationDatabase: do nothing
125+
if (using_fixed_db) {
126+
cmd.Filename = canonical_source0;
127+
source0 = canonical_source0;
128+
}
129+
break; // we found and fixed the command for source0. we are done.
130+
}
131+
}
132+
// Finally we construct a custom compilation database with the fixed commands
133+
auto custom_db = clu::custom_compilation_database(all_compile_cmds);
85134

86-
clang::tooling::ClangTool main_tool(opt_parser->getCompilations(), opt_parser->getSourcePathList());
135+
// ------- main tool
136+
// For FixedCompilationDatabase, pass canonical path to ClangTool to match what's in the database
137+
//auto source_for_tool = using_fixed_db ? canonical_source0 : source0;
138+
clang::tooling::ClangTool main_tool(custom_db, {source0}); // we use the fixed compilation database
87139

88140
// Additional Command line arguments to be given to the compiler, after all other options
89141
// from e.g. CXXFLAGS and co, and the -resource-dir.

0 commit comments

Comments
 (0)