Improve compilation database handling (#142)

Co-authored-by: star9029 <hengxings783@gmail.com>
This commit is contained in:
ykiko
2025-06-23 16:49:35 +08:00
committed by GitHub
parent a3075f86c0
commit d4c943d803
15 changed files with 229 additions and 154 deletions

View File

@@ -2,77 +2,12 @@
#include "Compiler/Command.h"
#include "Compiler/Compilation.h"
#include "Support/FileSystem.h"
#include "llvm/Support/CommandLine.h"
namespace clice {
std::expected<void, std::string> mangle_command(llvm::StringRef command,
llvm::SmallVectorImpl<const char*>& out,
llvm::SmallVectorImpl<char>& buffer) {
llvm::SmallString<128> current;
llvm::SmallVector<uint32_t> indices;
bool inSingleQuote = false;
bool inDoubleQuote = false;
for(size_t i = 0; i < command.size(); ++i) {
char c = command[i];
if(c == ' ' && !inSingleQuote && !inDoubleQuote) {
if(!current.empty()) {
indices.push_back(buffer.size());
buffer.append(current);
buffer.push_back('\0');
current.clear();
}
} else if(c == '\'' && !inDoubleQuote) {
inSingleQuote = !inSingleQuote;
} else if(c == '"' && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote;
} else {
current.push_back(c);
}
}
if(!current.empty()) {
indices.push_back(buffer.size());
buffer.append(current);
buffer.push_back('\0');
}
/// Add resource directory.
indices.push_back(buffer.size());
current = std::format("-resource-dir={}", fs::resource_dir);
buffer.append(current);
buffer.push_back('\0');
/// FIXME: use better way to remove args.
for(size_t i = 0; i < indices.size(); ++i) {
llvm::StringRef arg(buffer.data() + indices[i]);
/// Skip `-c` and `-o` arguments.
if(arg == "-c") {
continue;
}
if(arg.starts_with("-o")) {
if(arg == "-o") {
++i;
}
continue;
}
if(arg.starts_with("@CMakeFiles")) {
continue;
}
/// TODO: remove PCH.
out.push_back(arg.data());
}
return {};
}
/// Update the compile commands with the given file.
void CompilationDatabase::updateCommands(llvm::StringRef filename) {
void CompilationDatabase::update_commands(this Self& self, llvm::StringRef filename) {
auto path = path::real_path(filename);
filename = path;
@@ -140,12 +75,12 @@ void CompilationDatabase::updateCommands(llvm::StringRef filename) {
continue;
}
commands[path] = *command;
self.add_command(path, *command);
}
log::info("Successfully loaded compile commands from {0}, total {1} commands",
filename,
commands.size());
self.commands.size());
/// Scan all files to build module map.
// CompilationParams params;
@@ -159,29 +94,16 @@ void CompilationDatabase::updateCommands(llvm::StringRef filename) {
// }
//}
log::info("Successfully built module map, total {0} modules", moduleMap.size());
}
void CompilationDatabase::updateCommand(llvm::StringRef file, llvm::StringRef command) {
commands[path::real_path(file)] = command;
log::info("Successfully built module map, total {0} modules", self.moduleMap.size());
}
/// Update the module map with the given file and module name.
void CompilationDatabase::updateModule(llvm::StringRef file, llvm::StringRef name) {
void CompilationDatabase::update_module(llvm::StringRef file, llvm::StringRef name) {
moduleMap[path::real_path(file)] = file;
}
/// Lookup the compile commands of the given file.
llvm::StringRef CompilationDatabase::getCommand(llvm::StringRef file) {
auto iter = commands.find(file);
if(iter == commands.end()) {
return "";
}
return iter->second;
}
/// Lookup the module interface unit file path of the given module name.
llvm::StringRef CompilationDatabase::getModuleFile(llvm::StringRef name) {
llvm::StringRef CompilationDatabase::get_module_file(llvm::StringRef name) {
auto iter = moduleMap.find(name);
if(iter == moduleMap.end()) {
return "";
@@ -189,4 +111,81 @@ llvm::StringRef CompilationDatabase::getModuleFile(llvm::StringRef name) {
return iter->second;
}
llvm::StringRef CompilationDatabase::save_string(this Self& self, llvm::StringRef string) {
auto it = self.unique.find(string);
/// FIXME: arg may be empty?
/// If we already store the argument, reuse it.
if(it != self.unique.end()) {
return *it;
}
/// Allocate new argument.
const auto size = string.size();
auto ptr = self.memory_pool.Allocate<char>(size + 1);
std::memcpy(ptr, string.data(), size);
ptr[size] = '\0';
/// Insert new argument.
auto result = llvm::StringRef(ptr, size);
self.unique.insert(result);
return result;
}
std::vector<const char*> CompilationDatabase::save_args(this Self& self,
llvm::ArrayRef<const char*> args) {
std::vector<const char*> result;
result.reserve(args.size());
for(auto i = 0; i < args.size(); i++) {
result.emplace_back(self.save_string(args[i]).data());
}
return result;
}
void CompilationDatabase::add_command(this Self& self,
llvm::StringRef path,
llvm::StringRef command,
Style style) {
llvm::SmallVector<const char*> args;
/// temporary allocator to meet the argument requirements of tokenize.
llvm::BumpPtrAllocator allocator;
llvm::StringSaver saver(allocator);
/// FIXME: we may want to check the first argument of command to
/// make sure its mode.
if(style == Style::GNU) {
llvm::cl::TokenizeGNUCommandLine(command, saver, args);
} else if(style == Style::MSVC) {
llvm::cl::TokenizeWindowsCommandLineFull(command, saver, args);
} else {
std::abort();
}
auto path_ = self.save_string(path);
auto new_args = self.save_args(args);
auto it = self.commands.find(path_.data());
if(it == self.commands.end()) {
self.commands.try_emplace(path_.data(),
std::make_unique<std::vector<const char*>>(std::move(new_args)));
} else {
*it->second = std::move(new_args);
}
}
llvm::ArrayRef<const char*> CompilationDatabase::get_command(this Self& self,
llvm::StringRef path) {
auto path_ = self.save_string(path);
auto it = self.commands.find(path_.data());
if(it != self.commands.end()) {
return *it->second;
} else {
return {};
}
}
} // namespace clice

View File

@@ -42,18 +42,13 @@ auto create_invocation(CompilationParams& params,
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>& diagnostic_engine)
-> std::expected<std::unique_ptr<clang::CompilerInvocation>, std::string> {
/// Split orgin command into c-style command arguments for creating invocation.
llvm::SmallString<1024> buffer;
llvm::SmallVector<const char*, 32> args;
TRY_OR_RETURN(mangle_command(params.command, args, buffer));
/// Create clang invocation.
clang::CreateInvocationOptions options = {
.Diags = diagnostic_engine,
.VFS = params.vfs,
};
auto invocation = clang::createInvocation(args, options);
auto invocation = clang::createInvocation(params.arguments, options);
if(!invocation) {
return report_diagnostics("fail to create compiler invocation", *diagnostics);
}
@@ -221,7 +216,7 @@ std::expected<CompilationUnit, std::string> compile(CompilationParams& params, P
out.path = params.outPath.str();
/// out.preamble = params.content.substr(0, *params.bound);
out.command = params.command.str();
/// out.command = params.arguments.str();
/// FIXME: out.deps = info->deps();
return clang_compile<clang::GeneratePCHAction>(params, [&](clang::CompilerInstance& instance) {

View File

@@ -47,7 +47,7 @@ async::Task<> Indexer::index(CompilationUnit& unit) {
async::Task<> Indexer::index(llvm::StringRef file) {
CompilationParams params;
params.command = database.getCommand(file);
params.arguments = database.get_command(file);
auto AST = co_await async::submit([&] { return compile(params); });

View File

@@ -54,7 +54,7 @@ async::Task<json::Value> Scheduler::completion(std::string path, std::uint32_t o
/// Set compilation params ... .
CompilationParams params;
params.command = database.getCommand(path);
params.arguments = database.get_command(path);
params.add_remapped_file(path, openFile->content);
params.pch = {PCH->path, PCH->preamble.size()};
params.completion = {path, offset};
@@ -74,8 +74,9 @@ async::Task<bool> Scheduler::isPCHOutdated(llvm::StringRef path, llvm::StringRef
}
/// Check command and preamble matchs.
auto command = database.getCommand(path);
if(openFile->PCH->command != command || openFile->PCH->preamble != preamble) {
auto command = database.get_command(path);
/// FIXME: check command. openFile->PCH->command != command
if(openFile->PCH->preamble != preamble) {
co_return true;
}
@@ -104,7 +105,7 @@ async::Task<> Scheduler::buildPCH(std::string path, std::string content) {
std::uint32_t bound,
std::string content) -> async::Task<> {
CompilationParams params;
params.command = scheduler.database.getCommand(path);
params.arguments = scheduler.database.get_command(path);
params.outPath = path::join(config::index.dir, path::filename(path) + ".pch");
params.add_remapped_file(path, content, bound);
@@ -161,7 +162,7 @@ async::Task<> Scheduler::buildAST(std::string path, std::string content) {
}
CompilationParams params;
params.command = database.getCommand(path);
params.arguments = database.get_command(path);
params.add_remapped_file(path, content);
params.pch = {PCH->path, PCH->preamble.size()};

View File

@@ -118,7 +118,7 @@ async::Task<json::Value> Server::onInitialize(json::Value value) {
config::init(converter.workspace());
for(auto& dir: config::server.compile_commands_dirs) {
database.updateCommands(dir + "/compile_commands.json");
database.update_commands(dir + "/compile_commands.json");
}
co_return result;