Remove CompileCommandsEntry and reduce clangDriver invocations

This commit is contained in:
Fangrui Song 2018-08-17 22:20:51 -07:00
parent 06aa252335
commit 2f5fcadc39

View File

@ -32,22 +32,6 @@ using namespace llvm;
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
struct CompileCommandsEntry {
std::string directory;
std::string file;
std::string command;
std::vector<std::string> args;
std::string ResolveIfRelative(std::string path) const {
if (sys::path::is_absolute(path))
return path;
SmallString<256> Ret;
sys::path::append(Ret, directory, path);
return NormalizePath(Ret.str());
}
};
MAKE_REFLECT_STRUCT(CompileCommandsEntry, directory, file, command, args);
namespace { namespace {
enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand }; enum class ProjectMode { CompileCommandsJson, DotCcls, ExternalCommand };
@ -67,16 +51,27 @@ enum OptionClass {
Separate, Separate,
}; };
Project::Entry std::string ResolveIfRelative(const std::string &directory,
GetCompilationEntryFromCompileCommandEntry(ProjectConfig *config, const std::string &path) {
const CompileCommandsEntry &entry) { if (sys::path::is_absolute(path))
Project::Entry result; return path;
result.filename = entry.file; SmallString<256> Ret;
const std::string base_name = sys::path::filename(entry.file); sys::path::append(Ret, directory, path);
return NormalizePath(Ret.str());
}
struct ProjectProcessor {
ProjectConfig *config;
std::unordered_set<size_t> command_set;
ProjectProcessor(ProjectConfig *config) : config(config) {}
void Process(Project::Entry &entry) {
const std::string base_name = sys::path::filename(entry.filename);
// Expand %c %cpp %clang // Expand %c %cpp %clang
std::vector<std::string> args; std::vector<std::string> args;
const LanguageId lang = SourceFileLanguage(entry.file); args.reserve(entry.args.size() + config->extra_flags.size() + 3);
const LanguageId lang = SourceFileLanguage(entry.filename);
for (const std::string &arg : entry.args) { for (const std::string &arg : entry.args) {
if (arg.compare(0, 3, "%c ") == 0) { if (arg.compare(0, 3, "%c ") == 0) {
if (lang == LanguageId::C) if (lang == LanguageId::C)
@ -91,10 +86,42 @@ GetCompilationEntryFromCompileCommandEntry(ProjectConfig *config,
} }
} }
if (args.empty()) if (args.empty())
return result; return;
args.insert(args.end(), config->extra_flags.begin(), args.insert(args.end(), config->extra_flags.begin(),
config->extra_flags.end()); config->extra_flags.end());
size_t hash = std::hash<std::string>{}(entry.directory);
for (auto &arg : args) {
if (arg[0] != '-' && EndsWith(arg, base_name)) {
const LanguageId lang = SourceFileLanguage(arg);
if (lang != LanguageId::Unknown) {
hash_combine(hash, size_t(lang));
continue;
}
}
hash_combine(hash, std::hash<std::string>{}(arg));
}
for (size_t i = 1; i < args.size(); i++)
// This is most likely the file path we will be passing to clang. The
// path needs to be absolute, otherwise clang_codeCompleteAt is extremely
// slow. See
// https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678.
if (args[i][0] != '-' && EndsWith(args[i], base_name)) {
args[i] = ResolveIfRelative(entry.directory, args[i]);
continue;
}
args.push_back("-resource-dir=" + g_config->clang.resourceDir);
args.push_back("-working-directory=" + entry.directory);
// There could be a clang version mismatch between what the project uses and
// what ccls uses. Make sure we do not emit warnings for mismatched options.
args.push_back("-Wno-unknown-warning-option");
if (!command_set.insert(hash).second) {
entry.args = std::move(args);
return;
}
// a weird C++ deduction guide heap-use-after-free causes libclang to crash. // a weird C++ deduction guide heap-use-after-free causes libclang to crash.
IgnoringDiagConsumer DiagC; IgnoringDiagConsumer DiagC;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions()); IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
@ -113,13 +140,15 @@ GetCompilationEntryFromCompileCommandEntry(ProjectConfig *config,
Driver.setCheckInputsExist(false); Driver.setCheckInputsExist(false);
std::vector<const char *> cargs; std::vector<const char *> cargs;
cargs.reserve(args.size() + 1);
for (auto &arg : args) for (auto &arg : args)
cargs.push_back(arg.c_str()); cargs.push_back(arg.c_str());
cargs.push_back("-fsyntax-only"); cargs.push_back("-fsyntax-only");
std::unique_ptr<driver::Compilation> C(Driver.BuildCompilation(cargs)); std::unique_ptr<driver::Compilation> C(Driver.BuildCompilation(cargs));
const driver::JobList &Jobs = C->getJobs(); const driver::JobList &Jobs = C->getJobs();
if (Jobs.size() != 1) if (Jobs.size() != 1)
return result; return;
const driver::ArgStringList &CCArgs = Jobs.begin()->getArguments(); const driver::ArgStringList &CCArgs = Jobs.begin()->getArguments();
auto CI = std::make_unique<CompilerInvocation>(); auto CI = std::make_unique<CompilerInvocation>();
@ -130,7 +159,7 @@ GetCompilationEntryFromCompileCommandEntry(ProjectConfig *config,
HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts(); HeaderSearchOptions &HeaderOpts = CI->getHeaderSearchOpts();
for (auto &E : HeaderOpts.UserEntries) { for (auto &E : HeaderOpts.UserEntries) {
std::string path = entry.ResolveIfRelative(E.Path); std::string path = ResolveIfRelative(entry.directory, E.Path);
switch (E.Group) { switch (E.Group) {
default: default:
config->angle_dirs.insert(path); config->angle_dirs.insert(path);
@ -144,31 +173,9 @@ GetCompilationEntryFromCompileCommandEntry(ProjectConfig *config,
break; break;
} }
} }
entry.args = std::move(args);
for (size_t i = 1; i < args.size(); i++)
// This is most likely the file path we will be passing to clang. The
// path needs to be absolute, otherwise clang_codeCompleteAt is extremely
// slow. See
// https://github.com/cquery-project/cquery/commit/af63df09d57d765ce12d40007bf56302a0446678.
if (args[i][0] != '-' && EndsWith(args[i], base_name)) {
args[i] = entry.ResolveIfRelative(args[i]);
continue;
} }
};
// if (!sys::fs::exists(HeaderOpts.ResourceDir) &&
// HeaderOpts.UseBuiltinIncludes)
args.push_back("-resource-dir=" + g_config->clang.resourceDir);
if (CI->getFileSystemOpts().WorkingDir.empty())
args.push_back("-working-directory=" + entry.directory);
// There could be a clang version mismatch between what the project uses and
// what ccls uses. Make sure we do not emit warnings for mismatched options.
args.push_back("-Wno-unknown-warning-option");
result.directory = entry.directory;
result.args = std::move(args);
return result;
}
std::vector<std::string> std::vector<std::string>
ReadCompilerArgumentsFromFile(const std::string &path) { ReadCompilerArgumentsFromFile(const std::string &path) {
@ -226,23 +233,25 @@ std::vector<Project::Entry> LoadFromDirectoryListing(ProjectConfig *config) {
return folder_args[project_dir]; return folder_args[project_dir];
}; };
ProjectProcessor proc(config);
for (const std::string &file : files) { for (const std::string &file : files) {
CompileCommandsEntry e; Project::Entry e;
e.directory = config->project_dir; e.directory = config->project_dir;
e.file = file; e.filename = file;
e.args = GetCompilerArgumentForFile(file); e.args = GetCompilerArgumentForFile(file);
if (e.args.empty()) if (e.args.empty())
e.args.push_back("%clang"); // Add a Dummy. e.args.push_back("%clang"); // Add a Dummy.
e.args.push_back(e.file); e.args.push_back(e.filename);
result.push_back(GetCompilationEntryFromCompileCommandEntry(config, e)); proc.Process(e);
result.push_back(e);
} }
return result; return result;
} }
std::vector<Project::Entry> std::vector<Project::Entry>
LoadCompilationEntriesFromDirectory(ProjectConfig *project, LoadEntriesFromDirectory(ProjectConfig *project,
const std::string &opt_compilation_db_dir) { const std::string &opt_compdb_dir) {
// If there is a .ccls file always load using directory listing. // If there is a .ccls file always load using directory listing.
SmallString<256> Path; SmallString<256> Path;
sys::path::append(Path, project->project_dir, ".ccls"); sys::path::append(Path, project->project_dir, ".ccls");
@ -255,8 +264,8 @@ LoadCompilationEntriesFromDirectory(ProjectConfig *project,
if (g_config->compilationDatabaseCommand.empty()) { if (g_config->compilationDatabaseCommand.empty()) {
project->mode = ProjectMode::CompileCommandsJson; project->mode = ProjectMode::CompileCommandsJson;
// Try to load compile_commands.json, but fallback to a project listing. // Try to load compile_commands.json, but fallback to a project listing.
comp_db_dir = opt_compilation_db_dir.empty() ? project->project_dir comp_db_dir =
: opt_compilation_db_dir; opt_compdb_dir.empty() ? project->project_dir : opt_compdb_dir;
sys::path::append(Path, comp_db_dir, "compile_commands.json"); sys::path::append(Path, comp_db_dir, "compile_commands.json");
} else { } else {
project->mode = ProjectMode::ExternalCommand; project->mode = ProjectMode::ExternalCommand;
@ -302,14 +311,15 @@ LoadCompilationEntriesFromDirectory(ProjectConfig *project,
StringSet<> Seen; StringSet<> Seen;
std::vector<Project::Entry> result; std::vector<Project::Entry> result;
ProjectProcessor proc(project);
for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) {
CompileCommandsEntry entry; Project::Entry entry;
entry.directory = std::move(Cmd.Directory); entry.directory = std::move(Cmd.Directory);
entry.file = entry.ResolveIfRelative(Cmd.Filename); entry.filename = ResolveIfRelative(entry.directory, Cmd.Filename);
entry.args = std::move(Cmd.CommandLine); entry.args = std::move(Cmd.CommandLine);
auto entry1 = GetCompilationEntryFromCompileCommandEntry(project, entry); proc.Process(entry);
if (Seen.insert(entry1.filename).second) if (Seen.insert(entry.filename).second)
result.push_back(entry1); result.push_back(entry);
} }
return result; return result;
} }
@ -335,12 +345,11 @@ bool Project::loaded = false;
void Project::Load(const std::string &root_directory) { void Project::Load(const std::string &root_directory) {
Project::loaded = false; Project::loaded = false;
// Load data.
ProjectConfig project; ProjectConfig project;
project.extra_flags = g_config->clang.extraArgs; project.extra_flags = g_config->clang.extraArgs;
project.project_dir = root_directory; project.project_dir = root_directory;
entries = LoadCompilationEntriesFromDirectory( entries = LoadEntriesFromDirectory(&project,
&project, g_config->compilationDatabaseDirectory); g_config->compilationDatabaseDirectory);
// Cleanup / postprocess include directories. // Cleanup / postprocess include directories.
quote_include_directories.assign(project.quote_dirs.begin(), quote_include_directories.assign(project.quote_dirs.begin(),
@ -355,14 +364,6 @@ void Project::Load(const std::string &root_directory) {
EnsureEndsInSlash(path); EnsureEndsInSlash(path);
LOG_S(INFO) << "angle_include_dir: " << path; LOG_S(INFO) << "angle_include_dir: " << path;
} }
// Setup project entries.
std::lock_guard<std::mutex> lock(mutex_);
absolute_path_to_entry_index_.reserve(entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
entries[i].id = i;
absolute_path_to_entry_index_[entries[i].filename] = i;
}
} }
void Project::SetFlagsForFile(const std::vector<std::string> &flags, void Project::SetFlagsForFile(const std::vector<std::string> &flags,