1. Make resource dir unique and global.

2. Use our custom CDB loader.
This commit is contained in:
ykiko
2024-12-10 16:15:50 +08:00
parent f9bff085b2
commit c082458fb1
16 changed files with 332 additions and 194 deletions

View File

@@ -5,45 +5,114 @@
namespace clice {
void CommandManager::update(llvm::StringRef dir) {
std::string error;
auto CDB = clang::tooling::CompilationDatabase::loadFromDirectory(dir, error);
if(!CDB) {
log::fatal("Failed to load compilation database from {0}, because {1}", dir, error);
llvm::SmallString<128> path;
path::append(path, dir, "compile_commands.json");
auto buffer = llvm::MemoryBuffer::getFile(path);
if(!buffer) {
log::warn("Failed to read compile_commands.json from {}, because {}",
dir,
buffer.getError().message());
return;
}
CDBs.try_emplace(dir, std::move(CDB));
log::info("Successfully loaded compilation database from {0}.", dir);
}
auto json = json::parse(buffer.get()->getBuffer());
if(!json) {
log::warn("Failed to parse json file at {}, because {}", path, json.takeError());
return;
}
std::vector<std::string> CommandManager::lookup(llvm::StringRef file) {
std::vector<std::string> result;
for(const auto& [dir, CDB]: CDBs) {
auto commands = CDB->getCompileCommands(file);
if(commands.empty()) {
if(!json->getAsArray()) {
log::warn("Invalid JSON format at {}, Compilation Database requires Array, but get {}",
path,
refl::enum_name(json->kind()));
return;
}
auto& CDB = CDBs[dir];
/// Clear old commands.
/// FIXME: it would be better to have cache in some way.
CDB.clear();
for(auto& value: *json->getAsArray()) {
auto element = value.getAsObject();
if(!element) {
log::warn("Invalid JSON format at {}, Compilation Database requires Object, but get {}",
path,
refl::enum_name(value.kind()));
continue;
}
auto& args = commands[0].CommandLine;
for(auto iter = args.begin(); iter != args.end(); ++iter) {
if(iter->starts_with("-o")) {
iter++;
continue;
} else if(iter->starts_with("-c")) {
continue;
} else if(*iter == "--") {
continue;
/// Source file path.
llvm::SmallString<128> path;
auto file = element->getString("file");
if(!file) {
log::warn("the element in {} does not have a file field",
path,
refl::enum_name(value.kind()));
continue;
}
if(path::is_relative(file.value())) {
auto working = element->getString("directory");
if(!working) {
log::warn(
"Invalid JSON format at {}, {} is relative path, but directory is not provided",
path,
file.value());
}
result.push_back(std::move(*iter));
path::append(path, working.value(), file.value());
} else {
path::append(path, file.value());
}
break;
/// Command to compile the source file.
llvm::SmallString<1024> rawCommand;
if(auto command = element->getString("command")) {
rawCommand = command.value();
} else if(auto arguments = element->getArray("arguments")) {
/// FIXME:
std::terminate();
} else {
log::warn(
"Invalid JSON format, the element in {} does not have a command or arguments field",
path,
refl::enum_name(value.kind()));
continue;
}
CDB[path].emplace_back(rawCommand.str());
}
result.push_back("-resource-dir=");
result.back() += config::frontend().resource_dictionary;
log::info("Compilation Database at {} is up-to-date", dir);
}
return result;
llvm::StringRef CommandManager::lookupFirst(llvm::StringRef file) {
for(auto& [dir, cdb]: CDBs) {
auto iter = cdb.find(file);
if(iter != cdb.end()) {
return iter->second.front();
}
}
return {};
}
llvm::ArrayRef<std::string> CommandManager::lookup(llvm::StringRef file) {
for(auto& [dir, cdb]: CDBs) {
/// FIXME: currently we directly return the first match.
/// it is better to provide a callback to handle all matches.
auto iter = cdb.find(file);
if(iter != cdb.end()) {
return iter->second;
}
}
return {};
}
} // namespace clice

View File

@@ -6,16 +6,17 @@ namespace clice {
async::promise<void> Scheduler::updatePCH(llvm::StringRef filepath,
llvm::StringRef content,
llvm::ArrayRef<const char*> args) {
llvm::StringRef command) {
auto [iter, success] = pchs.try_emplace(filepath);
if(success || iter->second.needUpdate(content)) {
Tracer tracer;
CompliationParams params;
params.srcPath = filepath;
params.content = content;
params.args = args;
params.srcPath = filepath;
params.outPath = filepath;
params.command = command;
path::replace_path_prefix(params.outPath,
config::workplace(),
config::frontend().cache_directory);
@@ -77,32 +78,20 @@ async::promise<void> Scheduler::buildAST(llvm::StringRef filepath, llvm::StringR
initCmd = true;
}
/// FIXME: lookup from CDB file and adjust and remove unnecessary arguments.1
auto cmd = cmdMgr.lookup(filepath);
llvm::SmallVector<const char*> args;
for(auto& arg: cmd) {
args.push_back(arg.c_str());
}
/// through arguments to judge is it a module.
bool isModule = false;
co_await (isModule ? updatePCM() : updatePCH(filepath, content, args));
Tracer tracer;
llvm::SmallString<128> command;
llvm::raw_svector_ostream os(command);
for(auto arg: args) {
os << arg << " ";
}
log::info("Start building AST for {0}, command: [{1}]", filepath, command.str());
CompliationParams params;
params.srcPath = path;
params.content = content;
params.args = args;
params.command = cmdMgr.lookupFirst(filepath);
params.addPCH(pchs.at(filepath));
/// through arguments to judge is it a module.
bool isModule = false;
co_await (isModule ? updatePCM() : updatePCH(filepath, content, params.command));
Tracer tracer;
log::info("Start building AST for {0}, command: [{1}]", filepath, params.command.str());
auto task = [&] {
/// FIXME: We cannot use reference capture the `pch` here, beacuse the reference may be
/// Invalid Because other changed the `pchs` map. We also cannot to retrieve the `pch` from
@@ -153,22 +142,15 @@ async::promise<proto::CompletionResult> Scheduler::codeComplete(llvm::StringRef
llvm::SmallString<128> path = filepath;
/// FIXME: lookup from CDB file and adjust and remove unnecessary arguments.1
llvm::SmallVector<const char*> args = {
"clang++",
"-std=c++20",
path.c_str(),
"-resource-dir",
"/home/ykiko/C++/clice2/build/lib/clang/20",
};
CompliationParams params;
params.srcPath = path;
params.args = args;
params.content = iter->second.content;
params.srcPath = path;
params.command = cmdMgr.lookupFirst(filepath);
/// through arguments to judge is it a module.
bool isModule = false;
co_await (isModule ? updatePCM() : updatePCH(params.srcPath, params.content, args));
co_await (isModule ? updatePCM() : updatePCH(params.srcPath, params.content, params.command));
params.addPCH(pchs.at(filepath));
Tracer tracer;

View File

@@ -22,6 +22,12 @@ int Server::run(int argc, const char** argv) {
log::info("Successfully loaded configuration file from {0}.", cl::config.getValue());
}
/// Get the resource directory.
if(auto error = fs::init_resource_dir(argv[0])) {
log::fatal("Failed to get resource directory, because {0}", error);
return 1;
}
auto dispatch = [this](json::Value value) -> async::promise<void> {
assert(value.kind() == json::Value::Object);
auto object = value.getAsObject();