summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ModulesBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/ModulesBuilder.cpp')
-rw-r--r--clang-tools-extra/clangd/ModulesBuilder.cpp140
1 files changed, 118 insertions, 22 deletions
diff --git a/clang-tools-extra/clangd/ModulesBuilder.cpp b/clang-tools-extra/clangd/ModulesBuilder.cpp
index 56d00c22674e..0177a1751bd6 100644
--- a/clang-tools-extra/clangd/ModulesBuilder.cpp
+++ b/clang-tools-extra/clangd/ModulesBuilder.cpp
@@ -14,6 +14,8 @@
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/ModuleCache.h"
#include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/CommandLine.h"
+
#include <queue>
namespace clang {
@@ -21,6 +23,12 @@ namespace clangd {
namespace {
+llvm::cl::opt<bool> DebugModulesBuilder(
+ "debug-modules-builder",
+ llvm::cl::desc("Don't remove clangd's built module files for debugging. "
+ "Remember to remove them later after debugging."),
+ llvm::cl::init(false));
+
// Create a path to store module files. Generally it should be:
//
// {TEMP_DIRS}/clangd/module_files/{hashed-file-name}-%%-%%-%%-%%-%%-%%/.
@@ -95,10 +103,13 @@ public:
}
};
-struct ModuleFile {
+/// Represents a reference to a module file (*.pcm).
+class ModuleFile {
+protected:
ModuleFile(StringRef ModuleName, PathRef ModuleFilePath)
: ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
+public:
ModuleFile() = delete;
ModuleFile(const ModuleFile &) = delete;
@@ -120,21 +131,58 @@ struct ModuleFile {
new (this) ModuleFile(std::move(Other));
return *this;
}
-
- ~ModuleFile() {
- if (!ModuleFilePath.empty())
- llvm::sys::fs::remove(ModuleFilePath);
- }
+ virtual ~ModuleFile() = default;
StringRef getModuleName() const { return ModuleName; }
StringRef getModuleFilePath() const { return ModuleFilePath; }
-private:
+protected:
std::string ModuleName;
std::string ModuleFilePath;
};
+/// Represents a prebuilt module file which is not owned by us.
+class PrebuiltModuleFile : public ModuleFile {
+private:
+ // private class to make sure the class can only be constructed by member
+ // functions.
+ struct CtorTag {};
+
+public:
+ PrebuiltModuleFile(StringRef ModuleName, PathRef ModuleFilePath, CtorTag)
+ : ModuleFile(ModuleName, ModuleFilePath) {}
+
+ static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
+ PathRef ModuleFilePath) {
+ return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
+ CtorTag{});
+ }
+};
+
+/// Represents a module file built by us. We're responsible to remove it.
+class BuiltModuleFile : public ModuleFile {
+private:
+ // private class to make sure the class can only be constructed by member
+ // functions.
+ struct CtorTag {};
+
+public:
+ BuiltModuleFile(StringRef ModuleName, PathRef ModuleFilePath, CtorTag)
+ : ModuleFile(ModuleName, ModuleFilePath) {}
+
+ static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
+ PathRef ModuleFilePath) {
+ return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
+ CtorTag{});
+ }
+
+ virtual ~BuiltModuleFile() {
+ if (!ModuleFilePath.empty() && !DebugModulesBuilder)
+ llvm::sys::fs::remove(ModuleFilePath);
+ }
+};
+
// ReusablePrerequisiteModules - stands for PrerequisiteModules for which all
// the required modules are built successfully. All the module files
// are owned by the modules builder.
@@ -163,9 +211,9 @@ public:
std::string getAsString() const {
std::string Result;
llvm::raw_string_ostream OS(Result);
- for (const auto &ModuleFile : RequiredModules) {
- OS << "-fmodule-file=" << ModuleFile->getModuleName() << "="
- << ModuleFile->getModuleFilePath() << " ";
+ for (const auto &MF : RequiredModules) {
+ OS << "-fmodule-file=" << MF->getModuleName() << "="
+ << MF->getModuleFilePath() << " ";
}
return Result;
}
@@ -177,9 +225,9 @@ public:
return BuiltModuleNames.contains(ModuleName);
}
- void addModuleFile(std::shared_ptr<const ModuleFile> ModuleFile) {
- BuiltModuleNames.insert(ModuleFile->getModuleName());
- RequiredModules.emplace_back(std::move(ModuleFile));
+ void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
+ BuiltModuleNames.insert(MF->getModuleName());
+ RequiredModules.emplace_back(std::move(MF));
}
private:
@@ -257,7 +305,7 @@ bool IsModuleFilesUpToDate(
/// Build a module file for module with `ModuleName`. The information of built
/// module file are stored in \param BuiltModuleFiles.
-llvm::Expected<ModuleFile>
+llvm::Expected<std::shared_ptr<BuiltModuleFile>>
buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS,
const ReusablePrerequisiteModules &BuiltModuleFiles) {
@@ -315,7 +363,7 @@ buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
Cmds += Arg;
}
- clangd::vlog("Failed to compile {0} with command: {1}.", ModuleUnitFileName,
+ clangd::vlog("Failed to compile {0} with command: {1}", ModuleUnitFileName,
Cmds);
std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
@@ -325,11 +373,14 @@ buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
return llvm::createStringError(
llvm::formatv("Failed to compile {0}. Use '--log=verbose' to view "
- "detailed failure reasons.",
+ "detailed failure reasons. It is helpful to use "
+ "'--debug-modules-builder' flag to keep the clangd's "
+ "built module files to reproduce the failure for "
+ "debugging. Remember to remove them after debugging.",
ModuleUnitFileName));
}
- return ModuleFile{ModuleName, Inputs.CompileCommand.Output};
+ return BuiltModuleFile::make(ModuleName, Inputs.CompileCommand.Output);
}
bool ReusablePrerequisiteModules::canReuse(
@@ -498,10 +549,51 @@ public:
ReusablePrerequisiteModules &BuiltModuleFiles);
private:
+ /// Try to get prebuilt module files from the compilation database.
+ void getPrebuiltModuleFile(StringRef ModuleName, PathRef ModuleUnitFileName,
+ const ThreadsafeFS &TFS,
+ ReusablePrerequisiteModules &BuiltModuleFiles);
+
ModuleFileCache Cache;
ModuleNameToSourceCache ProjectModulesCache;
};
+void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
+ StringRef ModuleName, PathRef ModuleUnitFileName, const ThreadsafeFS &TFS,
+ ReusablePrerequisiteModules &BuiltModuleFiles) {
+ auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
+ if (!Cmd)
+ return;
+
+ ParseInputs Inputs;
+ Inputs.TFS = &TFS;
+ Inputs.CompileCommand = std::move(*Cmd);
+
+ IgnoreDiagnostics IgnoreDiags;
+ auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
+ if (!CI)
+ return;
+
+ // We don't need to check if the module files are in ModuleCache or adding
+ // them to the module cache. As even if the module files are in the module
+ // cache, we still need to validate them. And it looks not helpful to add them
+ // to the module cache, since we may always try to get the prebuilt module
+ // files before building the module files by ourselves.
+ for (auto &[ModuleName, ModuleFilePath] :
+ CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
+ if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
+ continue;
+
+ if (IsModuleFileUpToDate(ModuleFilePath, BuiltModuleFiles,
+ TFS.view(std::nullopt))) {
+ log("Reusing prebuilt module file {0} of module {1} for {2}",
+ ModuleFilePath, ModuleName, ModuleUnitFileName);
+ BuiltModuleFiles.addModuleFile(
+ PrebuiltModuleFile::make(ModuleName, ModuleFilePath));
+ }
+ }
+}
+
llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName, const ThreadsafeFS &TFS,
CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
@@ -521,6 +613,11 @@ llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
return llvm::createStringError(
llvm::formatv("Don't get the module unit for module {0}", ModuleName));
+ /// Try to get prebuilt module files from the compilation database first. This
+ /// helps to avoid building the module files that are already built by the
+ /// compiler.
+ getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
+
// Get Required modules in topological order.
auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
for (llvm::StringRef ReqModuleName : ReqModuleNames) {
@@ -540,15 +637,14 @@ llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
std::string ReqFileName =
MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
- llvm::Expected<ModuleFile> MF = buildModuleFile(
+ llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
ReqModuleName, ReqFileName, getCDB(), TFS, BuiltModuleFiles);
if (llvm::Error Err = MF.takeError())
return Err;
- log("Built module {0} to {1}", ReqModuleName, MF->getModuleFilePath());
- auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF));
- Cache.add(ReqModuleName, BuiltModuleFile);
- BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile));
+ log("Built module {0} to {1}", ReqModuleName, (*MF)->getModuleFilePath());
+ Cache.add(ReqModuleName, *MF);
+ BuiltModuleFiles.addModuleFile(std::move(*MF));
}
return llvm::Error::success();