2018-08-15 00:03:32 +08:00
|
|
|
//===--- GlobalCompilationDatabase.cpp ---------------------------*- C++-*-===//
|
2017-05-16 17:38:59 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-05-16 17:38:59 +08:00
|
|
|
//
|
2018-08-15 00:03:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2017-05-16 17:38:59 +08:00
|
|
|
|
|
|
|
#include "GlobalCompilationDatabase.h"
|
2017-10-02 23:13:20 +08:00
|
|
|
#include "Logger.h"
|
2019-01-22 17:10:20 +08:00
|
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
|
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
2017-05-16 17:38:59 +08:00
|
|
|
#include "clang/Tooling/CompilationDatabase.h"
|
2019-01-22 17:10:20 +08:00
|
|
|
#include "llvm/ADT/Optional.h"
|
2017-05-16 17:38:59 +08:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
|
2017-07-06 16:44:54 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
2019-01-22 17:10:20 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
void adjustArguments(tooling::CompileCommand &Cmd,
|
|
|
|
llvm::StringRef ResourceDir) {
|
[clangd] Adjust compile commands to be applicable for tooling
Summary:
As can be seen in https://github.com/llvm-mirror/clang/blob/master/lib/Tooling/Tooling.cpp#L385
clang tool invocations adjust commands normally like this. In clangd we have
different code paths for invoking a frontend action(preamble builds, ast builds,
background index, clangd-indexer) they all work on the same GlobalCompilationDatabase
abstraction, but later on are subject to different modifications.
This patch makes sure all of the clangd actions make use of the same compile
commands before invocation.
Enables background-index to work on chromium codebase(since they had dependency
file output in their compile commands).
Reviewers: gribozavr, hokein
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D59086
llvm-svn: 355669
2019-03-08 16:38:25 +08:00
|
|
|
tooling::ArgumentsAdjuster ArgsAdjuster = tooling::combineAdjusters(
|
|
|
|
// clangd should not write files to disk, including dependency files
|
|
|
|
// requested on the command line.
|
|
|
|
tooling::getClangStripDependencyFileAdjuster(),
|
|
|
|
// Strip plugin related command line arguments. Clangd does
|
|
|
|
// not support plugins currently. Therefore it breaks if
|
|
|
|
// compiler tries to load plugins.
|
|
|
|
tooling::combineAdjusters(tooling::getStripPluginsAdjuster(),
|
|
|
|
tooling::getClangSyntaxOnlyAdjuster()));
|
|
|
|
|
|
|
|
Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename);
|
2019-01-22 17:10:20 +08:00
|
|
|
// Inject the resource dir.
|
|
|
|
// FIXME: Don't overwrite it if it's already there.
|
|
|
|
if (!ResourceDir.empty())
|
|
|
|
Cmd.CommandLine.push_back(("-resource-dir=" + ResourceDir).str());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getStandardResourceDir() {
|
|
|
|
static int Dummy; // Just an address in this process.
|
|
|
|
return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
2017-07-06 16:44:54 +08:00
|
|
|
|
2019-01-07 20:35:02 +08:00
|
|
|
static std::string getFallbackClangPath() {
|
|
|
|
static int Dummy;
|
|
|
|
std::string ClangdExecutable =
|
|
|
|
llvm::sys::fs::getMainExecutable("clangd", (void *)&Dummy);
|
|
|
|
SmallString<128> ClangPath;
|
|
|
|
ClangPath = llvm::sys::path::parent_path(ClangdExecutable);
|
|
|
|
llvm::sys::path::append(ClangPath, "clang");
|
|
|
|
return ClangPath.str();
|
|
|
|
}
|
|
|
|
|
2017-12-04 18:08:45 +08:00
|
|
|
tooling::CompileCommand
|
|
|
|
GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
|
2019-01-07 20:35:02 +08:00
|
|
|
std::vector<std::string> Argv = {getFallbackClangPath()};
|
2019-06-18 19:54:17 +08:00
|
|
|
// Clang treats .h files as C by default and files without extension as linker
|
|
|
|
// input, resulting in unhelpful diagnostics.
|
2018-04-20 19:35:17 +08:00
|
|
|
// Parsing as Objective C++ is friendly to more cases.
|
2019-06-18 19:54:17 +08:00
|
|
|
auto FileExtension = llvm::sys::path::extension(File);
|
|
|
|
if (FileExtension.empty() || FileExtension == ".h")
|
2018-04-20 19:35:17 +08:00
|
|
|
Argv.push_back("-xobjective-c++-header");
|
|
|
|
Argv.push_back(File);
|
2019-04-15 20:32:28 +08:00
|
|
|
tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
|
|
|
|
llvm::sys::path::filename(File), std::move(Argv),
|
|
|
|
/*Output=*/"");
|
|
|
|
Cmd.Heuristic = "clangd fallback";
|
|
|
|
return Cmd;
|
2017-07-06 16:44:54 +08:00
|
|
|
}
|
2017-05-16 17:38:59 +08:00
|
|
|
|
2017-09-20 15:24:15 +08:00
|
|
|
DirectoryBasedGlobalCompilationDatabase::
|
2019-01-07 23:45:19 +08:00
|
|
|
DirectoryBasedGlobalCompilationDatabase(
|
|
|
|
llvm::Optional<Path> CompileCommandsDir)
|
2017-12-13 20:51:22 +08:00
|
|
|
: CompileCommandsDir(std::move(CompileCommandsDir)) {}
|
2017-09-20 15:24:15 +08:00
|
|
|
|
2018-04-20 19:35:17 +08:00
|
|
|
DirectoryBasedGlobalCompilationDatabase::
|
|
|
|
~DirectoryBasedGlobalCompilationDatabase() = default;
|
|
|
|
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::Optional<tooling::CompileCommand>
|
2019-07-11 02:16:35 +08:00
|
|
|
DirectoryBasedGlobalCompilationDatabase::getCompileCommand(
|
|
|
|
PathRef File, ProjectInfo *Project) const {
|
|
|
|
if (auto CDB = getCDBForFile(File, Project)) {
|
|
|
|
auto Candidates = CDB->getCompileCommands(File);
|
|
|
|
if (!Candidates.empty()) {
|
|
|
|
return std::move(Candidates.front());
|
|
|
|
}
|
|
|
|
} else {
|
[clangd] Upgrade logging facilities with levels and formatv.
Summary:
log() is split into four functions:
- elog()/log()/vlog() have different severity levels, allowing filtering
- dlog() is a lazy macro which uses LLVM_DEBUG - it logs to the logger, but
conditionally based on -debug-only flag and is omitted in release builds
All logging functions use formatv-style format strings now, e.g:
log("Could not resolve URI {0}: {1}", URI, Result.takeError());
Existing log sites have been split between elog/log/vlog by best guess.
This includes a workaround for passing Error to formatv that can be
simplified when D49170 or similar lands.
Subscribers: ilya-biryukov, javed.absar, ioeric, MaskRay, jkorous, cfe-commits
Differential Revision: https://reviews.llvm.org/D49008
llvm-svn: 336785
2018-07-11 18:35:11 +08:00
|
|
|
log("Failed to find compilation database for {0}", File);
|
2017-07-06 16:44:54 +08:00
|
|
|
}
|
2018-10-20 23:30:37 +08:00
|
|
|
return None;
|
2017-12-04 18:08:45 +08:00
|
|
|
}
|
2017-07-06 16:44:54 +08:00
|
|
|
|
2019-07-11 02:16:35 +08:00
|
|
|
std::pair<tooling::CompilationDatabase *, /*Cached*/ bool>
|
2017-12-22 17:47:34 +08:00
|
|
|
DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const {
|
|
|
|
// FIXME(ibiryukov): Invalidate cached compilation databases on changes
|
|
|
|
auto CachedIt = CompilationDatabases.find(Dir);
|
2017-10-02 23:13:20 +08:00
|
|
|
if (CachedIt != CompilationDatabases.end())
|
2019-07-11 02:16:35 +08:00
|
|
|
return {CachedIt->second.get(), true};
|
2017-10-02 23:13:20 +08:00
|
|
|
std::string Error = "";
|
2019-07-11 02:16:35 +08:00
|
|
|
auto CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
|
|
|
|
auto Result = CDB.get();
|
|
|
|
CompilationDatabases.insert(std::make_pair(Dir, std::move(CDB)));
|
2018-11-20 18:56:03 +08:00
|
|
|
return {Result, false};
|
2017-10-02 23:13:20 +08:00
|
|
|
}
|
2017-09-20 15:24:15 +08:00
|
|
|
|
2019-07-11 02:16:35 +08:00
|
|
|
tooling::CompilationDatabase *
|
|
|
|
DirectoryBasedGlobalCompilationDatabase::getCDBForFile(
|
|
|
|
PathRef File, ProjectInfo *Project) const {
|
|
|
|
namespace path = llvm::sys::path;
|
|
|
|
assert((path::is_absolute(File, path::Style::posix) ||
|
|
|
|
path::is_absolute(File, path::Style::windows)) &&
|
2017-12-22 17:47:34 +08:00
|
|
|
"path must be absolute");
|
2017-10-02 23:13:20 +08:00
|
|
|
|
2019-07-11 02:16:35 +08:00
|
|
|
tooling::CompilationDatabase *CDB = nullptr;
|
|
|
|
bool Cached = false;
|
|
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
2018-11-20 18:56:03 +08:00
|
|
|
if (CompileCommandsDir) {
|
2019-07-11 02:16:35 +08:00
|
|
|
std::tie(CDB, Cached) = getCDBInDirLocked(*CompileCommandsDir);
|
|
|
|
if (Project && CDB)
|
|
|
|
Project->SourceRoot = *CompileCommandsDir;
|
|
|
|
} else {
|
|
|
|
for (auto Path = path::parent_path(File); !CDB && !Path.empty();
|
|
|
|
Path = path::parent_path(Path)) {
|
|
|
|
std::tie(CDB, Cached) = getCDBInDirLocked(Path);
|
|
|
|
if (Project && CDB)
|
|
|
|
Project->SourceRoot = Path;
|
2018-11-20 18:56:03 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-11 02:16:35 +08:00
|
|
|
// FIXME: getAllFiles() may return relative paths, we need absolute paths.
|
|
|
|
// Hopefully the fix is to change JSONCompilationDatabase and the interface.
|
|
|
|
if (CDB && !Cached)
|
|
|
|
OnCommandChanged.broadcast(CDB->getAllFiles());
|
|
|
|
return CDB;
|
2018-11-20 18:56:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
|
2019-01-22 17:10:20 +08:00
|
|
|
std::vector<std::string> FallbackFlags,
|
|
|
|
llvm::Optional<std::string> ResourceDir)
|
|
|
|
: Base(Base), ResourceDir(ResourceDir ? std::move(*ResourceDir)
|
|
|
|
: getStandardResourceDir()),
|
|
|
|
FallbackFlags(std::move(FallbackFlags)) {
|
2018-11-20 18:56:03 +08:00
|
|
|
if (Base)
|
|
|
|
BaseChanged = Base->watch([this](const std::vector<std::string> Changes) {
|
|
|
|
OnCommandChanged.broadcast(Changes);
|
|
|
|
});
|
2017-05-16 17:38:59 +08:00
|
|
|
}
|
2017-07-06 16:44:54 +08:00
|
|
|
|
2019-01-07 23:45:19 +08:00
|
|
|
llvm::Optional<tooling::CompileCommand>
|
2019-07-11 02:16:35 +08:00
|
|
|
OverlayCDB::getCompileCommand(PathRef File, ProjectInfo *Project) const {
|
2019-01-22 17:10:20 +08:00
|
|
|
llvm::Optional<tooling::CompileCommand> Cmd;
|
2018-11-02 21:09:36 +08:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
auto It = Commands.find(File);
|
2019-07-11 02:16:35 +08:00
|
|
|
if (It != Commands.end()) {
|
|
|
|
if (Project)
|
|
|
|
Project->SourceRoot = "";
|
2019-01-22 17:10:20 +08:00
|
|
|
Cmd = It->second;
|
2019-07-11 02:16:35 +08:00
|
|
|
}
|
2018-11-02 21:09:36 +08:00
|
|
|
}
|
2019-01-22 17:10:20 +08:00
|
|
|
if (!Cmd && Base)
|
2019-07-11 02:16:35 +08:00
|
|
|
Cmd = Base->getCompileCommand(File, Project);
|
2019-01-22 17:10:20 +08:00
|
|
|
if (!Cmd)
|
|
|
|
return llvm::None;
|
|
|
|
adjustArguments(*Cmd, ResourceDir);
|
|
|
|
return Cmd;
|
2018-11-02 21:09:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const {
|
|
|
|
auto Cmd = Base ? Base->getFallbackCommand(File)
|
|
|
|
: GlobalCompilationDatabase::getFallbackCommand(File);
|
2018-08-02 01:39:29 +08:00
|
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
2018-11-02 21:09:36 +08:00
|
|
|
Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(),
|
|
|
|
FallbackFlags.end());
|
2019-06-04 21:38:36 +08:00
|
|
|
adjustArguments(Cmd, ResourceDir);
|
2018-11-02 21:09:36 +08:00
|
|
|
return Cmd;
|
2018-08-02 01:39:29 +08:00
|
|
|
}
|
|
|
|
|
2018-11-02 21:09:36 +08:00
|
|
|
void OverlayCDB::setCompileCommand(
|
|
|
|
PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) {
|
2018-11-20 18:56:03 +08:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> Lock(Mutex);
|
|
|
|
if (Cmd)
|
|
|
|
Commands[File] = std::move(*Cmd);
|
|
|
|
else
|
|
|
|
Commands.erase(File);
|
|
|
|
}
|
|
|
|
OnCommandChanged.broadcast({File});
|
2018-08-02 01:39:29 +08:00
|
|
|
}
|
|
|
|
|
2017-07-06 16:44:54 +08:00
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|