[clang-tools-extra] [clangd] [C++20] [Modules] Introduce initial support for C++20 Modules (PR #66462)
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 9 02:56:23 PDT 2023
================
@@ -0,0 +1,282 @@
+//===----------------- ModuleFilesInfo.cpp -----------------------*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ModuleFilesInfo.h"
+#include "support/Logger.h"
+
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Serialization/ASTReader.h"
+
+namespace clang {
+namespace clangd {
+
+namespace {
+llvm::SmallString<128> getAbsolutePath(const tooling::CompileCommand &Cmd) {
+ llvm::SmallString<128> AbsolutePath;
+ if (llvm::sys::path::is_absolute(Cmd.Filename)) {
+ AbsolutePath = Cmd.Filename;
+ } else {
+ AbsolutePath = Cmd.Directory;
+ llvm::sys::path::append(AbsolutePath, Cmd.Filename);
+ llvm::sys::path::remove_dots(AbsolutePath, true);
+ }
+ return AbsolutePath;
+}
+} // namespace
+
+ModuleFilesInfo::ModuleFilesInfo(PathRef MainFile,
+ const GlobalCompilationDatabase &CDB) {
+ std::optional<ProjectInfo> PI = CDB.getProjectInfo(MainFile);
+ if (!PI)
+ return;
+
+ llvm::SmallString<128> Result(PI->SourceRoot);
+ llvm::sys::path::append(Result, ".cache");
+ llvm::sys::path::append(Result, "clangd");
+ llvm::sys::path::append(Result, "module_files");
+ llvm::sys::fs::create_directories(Result, /*IgnoreExisting=*/true);
+
+ llvm::sys::path::append(Result, llvm::sys::path::filename(MainFile));
+ llvm::sys::fs::createUniqueDirectory(Result, UniqueModuleFilesPathPrefix);
+
+ log("Initialized module files to {0}", UniqueModuleFilesPathPrefix.str());
+}
+
+ModuleFilesInfo::~ModuleFilesInfo() {
+ DependentModuleNames.clear();
+ Successed = false;
+
+ if (UniqueModuleFilesPathPrefix.empty())
+ return;
+
+ llvm::sys::fs::remove_directories(UniqueModuleFilesPathPrefix);
+ UniqueModuleFilesPathPrefix.clear();
+}
+
+llvm::SmallString<256>
+ModuleFilesInfo::getModuleFilePath(StringRef ModuleName) const {
+ llvm::SmallString<256> ModuleFilePath;
+
+ ModuleFilePath = UniqueModuleFilesPathPrefix;
+ auto [PrimaryModuleName, PartitionName] = ModuleName.split(':');
+ llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
+ if (!PartitionName.empty()) {
+ ModuleFilePath.append("-");
+ ModuleFilePath.append(PartitionName);
+ }
+ ModuleFilePath.append(".pcm");
+
+ return ModuleFilePath;
+}
+
+bool ModuleFilesInfo::IsModuleUnitBuilt(StringRef ModuleName) const {
+ if (!DependentModuleNames.count(ModuleName))
+ return false;
+
+ auto BMIPath = getModuleFilePath(ModuleName);
+ if (llvm::sys::fs::exists(BMIPath))
+ return true;
+
+ Successed = false;
+
+ DependentModuleNames.erase(ModuleName);
+ return false;
+}
+
+void ModuleFilesInfo::ReplaceHeaderSearchOptions(
+ HeaderSearchOptions &Options) const {
+ if (!IsInited())
+ return;
+
+ Options.PrebuiltModulePaths.insert(Options.PrebuiltModulePaths.begin(),
+ UniqueModuleFilesPathPrefix.str().str());
+
+ for (auto Iter = Options.PrebuiltModuleFiles.begin();
+ Iter != Options.PrebuiltModuleFiles.end();) {
+ if (IsModuleUnitBuilt(Iter->first)) {
+ Iter = Options.PrebuiltModuleFiles.erase(Iter);
+ continue;
+ }
+
+ Iter++;
+ }
+}
+
+void ModuleFilesInfo::ReplaceCompileCommands(
+ tooling::CompileCommand &Cmd) const {
+ if (!IsInited())
+ return;
+
+ std::vector<std::string> CommandLine(std::move(Cmd.CommandLine));
+
+ Cmd.CommandLine.emplace_back(CommandLine[0]);
+ Cmd.CommandLine.emplace_back(
+ llvm::Twine("-fprebuilt-module-path=" + UniqueModuleFilesPathPrefix)
+ .str());
+
+ for (std::size_t I = 1; I < CommandLine.size(); I++) {
+ const std::string &Arg = CommandLine[I];
+ const auto &[LHS, RHS] = StringRef(Arg).split("=");
+
+ // Remove original `-fmodule-file=<module-name>=<module-path>` form if it
+ // already built.
+ if (LHS == "-fmodule-file" && RHS.contains("=")) {
+ const auto &[ModuleName, _] = RHS.split("=");
+ if (IsModuleUnitBuilt(ModuleName))
+ continue;
+ }
+
+ Cmd.CommandLine.emplace_back(Arg);
+ }
+}
+
+void ModuleFilesInfo::ReplaceCompileCommands(tooling::CompileCommand &Cmd,
+ StringRef OutputModuleName) const {
+ if (!IsInited())
+ return;
+
+ ReplaceCompileCommands(Cmd);
+
+ Cmd.Output = getModuleFilePath(OutputModuleName).str().str();
+}
+
+bool ModuleFilesInfo::buildModuleFile(PathRef ModuleUnitFileName,
+ ModuleDependencyScanner &Scanner) {
+ if (ModuleUnitFileName.empty())
+ return false;
+
+ for (auto &ModuleName : Scanner.getRequiredModules(ModuleUnitFileName)) {
+ // Return early if there are errors building the module file.
+ if (!IsModuleUnitBuilt(ModuleName) &&
+ !buildModuleFile(Scanner.getSourceForModuleName(ModuleName), Scanner)) {
+ log("Failed to build module {0}", ModuleName);
+ return false;
+ }
+ }
+
+ auto Cmd =
+ Scanner.getCompilationDatabase().getCompileCommand(ModuleUnitFileName);
+ if (!Cmd)
+ return false;
+
+ ReplaceCompileCommands(*Cmd, Scanner.getModuleName(ModuleUnitFileName));
+
+ ParseInputs Inputs;
+ Inputs.TFS = Scanner.getThreadsafeFS();
+ Inputs.CompileCommand = std::move(*Cmd);
+
+ IgnoreDiagnostics IgnoreDiags;
+ auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
+ if (!CI)
+ return false;
+
+ auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
+ auto AbsolutePath = getAbsolutePath(Inputs.CompileCommand);
+ auto Buf = FS->getBufferForFile(AbsolutePath);
+ if (!Buf)
+ return false;
+
+ // Hash the contents of input files and store the hash value to the BMI files.
+ // So that we can check if the files are still valid when we want to reuse the
+ // BMI files.
+ CI->getHeaderSearchOpts().ValidateASTInputFilesContent = true;
+
+ CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
+ auto Clang =
+ prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
+ std::move(*Buf), std::move(FS), IgnoreDiags);
+ if (!Clang)
+ return false;
+
+ GenerateModuleInterfaceAction Action;
+ Clang->ExecuteAction(Action);
+
+ if (Clang->getDiagnostics().hasErrorOccurred())
+ return false;
+
+ DependentModuleNames.insert(Scanner.getModuleName(ModuleUnitFileName));
+
+ return true;
+}
+
+ModuleFilesInfo
+ModuleFilesInfo::buildModuleFilesInfoFor(PathRef File, const ThreadsafeFS *TFS,
+ const GlobalCompilationDatabase &CDB) {
+ ModuleDependencyScanner Scanner(CDB, TFS);
+
+ std::optional<tooling::dependencies::P1689Rule> ScanningResult =
+ Scanner.scan(File);
+ if (!ScanningResult)
+ return {};
+
+ ModuleFilesInfo ModulesInfo(File, CDB);
+
+ Scanner.globalScan(File);
+
+ for (auto &Info : ScanningResult->Requires)
+ // Return early if there is any error.
+ if (!ModulesInfo.buildModuleFile(
+ Scanner.getSourceForModuleName(Info.ModuleName), Scanner)) {
+ log("Failed to build module {0}", Info.ModuleName);
+ return ModulesInfo;
+ }
+
+ ModulesInfo.Successed = true;
+ return ModulesInfo;
+}
+
+bool ModuleFilesInfo::CanReuse(
+ const CompilerInvocation &CI,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const {
+ // Try to avoid expensive check as much as possible.
+ if (!IsInited())
+ return true;
+
+ if (!Successed)
----------------
ChuanqiXu9 wrote:
Done by removing `Successed`.
https://github.com/llvm/llvm-project/pull/66462
More information about the cfe-commits
mailing list