|
13 | 13 | #include "clang/Frontend/FrontendActions.h"
|
14 | 14 | #include "clang/Serialization/ASTReader.h"
|
15 | 15 | #include "clang/Serialization/InMemoryModuleCache.h"
|
| 16 | +#include "llvm/ADT/ScopeExit.h" |
| 17 | +#include <queue> |
16 | 18 |
|
17 | 19 | namespace clang {
|
18 | 20 | namespace clangd {
|
@@ -124,10 +126,58 @@ struct ModuleFile {
|
124 | 126 | llvm::sys::fs::remove(ModuleFilePath);
|
125 | 127 | }
|
126 | 128 |
|
| 129 | + StringRef getModuleName() const { return ModuleName; } |
| 130 | + |
| 131 | + StringRef getModuleFilePath() const { return ModuleFilePath; } |
| 132 | + |
| 133 | +private: |
127 | 134 | std::string ModuleName;
|
128 | 135 | std::string ModuleFilePath;
|
129 | 136 | };
|
130 | 137 |
|
| 138 | +// ReusablePrerequisiteModules - stands for PrerequisiteModules for which all |
| 139 | +// the required modules are built successfully. All the module files |
| 140 | +// are owned by the modules builder. |
| 141 | +class ReusablePrerequisiteModules : public PrerequisiteModules { |
| 142 | +public: |
| 143 | + ReusablePrerequisiteModules() = default; |
| 144 | + |
| 145 | + ReusablePrerequisiteModules(const ReusablePrerequisiteModules &Other) = |
| 146 | + default; |
| 147 | + ReusablePrerequisiteModules & |
| 148 | + operator=(const ReusablePrerequisiteModules &) = default; |
| 149 | + ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) = delete; |
| 150 | + ReusablePrerequisiteModules |
| 151 | + operator=(ReusablePrerequisiteModules &&) = delete; |
| 152 | + |
| 153 | + ~ReusablePrerequisiteModules() override = default; |
| 154 | + |
| 155 | + void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override { |
| 156 | + // Appending all built module files. |
| 157 | + for (const auto &RequiredModule : RequiredModules) |
| 158 | + Options.PrebuiltModuleFiles.insert_or_assign( |
| 159 | + RequiredModule->getModuleName().str(), |
| 160 | + RequiredModule->getModuleFilePath().str()); |
| 161 | + } |
| 162 | + |
| 163 | + bool canReuse(const CompilerInvocation &CI, |
| 164 | + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override; |
| 165 | + |
| 166 | + bool isModuleUnitBuilt(llvm::StringRef ModuleName) const { |
| 167 | + return BuiltModuleNames.contains(ModuleName); |
| 168 | + } |
| 169 | + |
| 170 | + void addModuleFile(std::shared_ptr<const ModuleFile> ModuleFile) { |
| 171 | + BuiltModuleNames.insert(ModuleFile->getModuleName()); |
| 172 | + RequiredModules.emplace_back(std::move(ModuleFile)); |
| 173 | + } |
| 174 | + |
| 175 | +private: |
| 176 | + llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules; |
| 177 | + // A helper class to speedup the query if a module is built. |
| 178 | + llvm::StringSet<> BuiltModuleNames; |
| 179 | +}; |
| 180 | + |
131 | 181 | bool IsModuleFileUpToDate(PathRef ModuleFilePath,
|
132 | 182 | const PrerequisiteModules &RequisiteModules,
|
133 | 183 | llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
|
@@ -192,89 +242,20 @@ bool IsModuleFilesUpToDate(
|
192 | 242 | });
|
193 | 243 | }
|
194 | 244 |
|
195 |
| -// StandalonePrerequisiteModules - stands for PrerequisiteModules for which all |
196 |
| -// the required modules are built successfully. All the module files |
197 |
| -// are owned by the StandalonePrerequisiteModules class. |
198 |
| -// |
199 |
| -// Any of the built module files won't be shared with other instances of the |
200 |
| -// class. So that we can avoid worrying thread safety. |
201 |
| -// |
202 |
| -// We don't need to worry about duplicated module names here since the standard |
203 |
| -// guarantees the module names should be unique to a program. |
204 |
| -class StandalonePrerequisiteModules : public PrerequisiteModules { |
205 |
| -public: |
206 |
| - StandalonePrerequisiteModules() = default; |
207 |
| - |
208 |
| - StandalonePrerequisiteModules(const StandalonePrerequisiteModules &) = delete; |
209 |
| - StandalonePrerequisiteModules |
210 |
| - operator=(const StandalonePrerequisiteModules &) = delete; |
211 |
| - StandalonePrerequisiteModules(StandalonePrerequisiteModules &&) = delete; |
212 |
| - StandalonePrerequisiteModules |
213 |
| - operator=(StandalonePrerequisiteModules &&) = delete; |
214 |
| - |
215 |
| - ~StandalonePrerequisiteModules() override = default; |
216 |
| - |
217 |
| - void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override { |
218 |
| - // Appending all built module files. |
219 |
| - for (auto &RequiredModule : RequiredModules) |
220 |
| - Options.PrebuiltModuleFiles.insert_or_assign( |
221 |
| - RequiredModule.ModuleName, RequiredModule.ModuleFilePath); |
222 |
| - } |
223 |
| - |
224 |
| - bool canReuse(const CompilerInvocation &CI, |
225 |
| - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override; |
226 |
| - |
227 |
| - bool isModuleUnitBuilt(llvm::StringRef ModuleName) const { |
228 |
| - return BuiltModuleNames.contains(ModuleName); |
229 |
| - } |
230 |
| - |
231 |
| - void addModuleFile(llvm::StringRef ModuleName, |
232 |
| - llvm::StringRef ModuleFilePath) { |
233 |
| - RequiredModules.emplace_back(ModuleName, ModuleFilePath); |
234 |
| - BuiltModuleNames.insert(ModuleName); |
235 |
| - } |
236 |
| - |
237 |
| -private: |
238 |
| - llvm::SmallVector<ModuleFile, 8> RequiredModules; |
239 |
| - // A helper class to speedup the query if a module is built. |
240 |
| - llvm::StringSet<> BuiltModuleNames; |
241 |
| -}; |
242 |
| - |
243 |
| -// Build a module file for module with `ModuleName`. The information of built |
244 |
| -// module file are stored in \param BuiltModuleFiles. |
245 |
| -llvm::Error buildModuleFile(llvm::StringRef ModuleName, |
246 |
| - const GlobalCompilationDatabase &CDB, |
247 |
| - const ThreadsafeFS &TFS, ProjectModules &MDB, |
248 |
| - PathRef ModuleFilesPrefix, |
249 |
| - StandalonePrerequisiteModules &BuiltModuleFiles) { |
250 |
| - if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName)) |
251 |
| - return llvm::Error::success(); |
252 |
| - |
253 |
| - PathRef ModuleUnitFileName = MDB.getSourceForModuleName(ModuleName); |
254 |
| - // It is possible that we're meeting third party modules (modules whose |
255 |
| - // source are not in the project. e.g, the std module may be a third-party |
256 |
| - // module for most projects) or something wrong with the implementation of |
257 |
| - // ProjectModules. |
258 |
| - // FIXME: How should we treat third party modules here? If we want to ignore |
259 |
| - // third party modules, we should return true instead of false here. |
260 |
| - // Currently we simply bail out. |
261 |
| - if (ModuleUnitFileName.empty()) |
262 |
| - return llvm::createStringError("Failed to get the primary source"); |
263 |
| - |
| 245 | +/// Build a module file for module with `ModuleName`. The information of built |
| 246 | +/// module file are stored in \param BuiltModuleFiles. |
| 247 | +llvm::Expected<ModuleFile> |
| 248 | +buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName, |
| 249 | + const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS, |
| 250 | + const ReusablePrerequisiteModules &BuiltModuleFiles) { |
264 | 251 | // Try cheap operation earlier to boil-out cheaply if there are problems.
|
265 | 252 | auto Cmd = CDB.getCompileCommand(ModuleUnitFileName);
|
266 | 253 | if (!Cmd)
|
267 | 254 | return llvm::createStringError(
|
268 | 255 | llvm::formatv("No compile command for {0}", ModuleUnitFileName));
|
269 | 256 |
|
270 |
| - for (auto &RequiredModuleName : MDB.getRequiredModules(ModuleUnitFileName)) { |
271 |
| - // Return early if there are errors building the module file. |
272 |
| - if (llvm::Error Err = buildModuleFile(RequiredModuleName, CDB, TFS, MDB, |
273 |
| - ModuleFilesPrefix, BuiltModuleFiles)) |
274 |
| - return llvm::createStringError( |
275 |
| - llvm::formatv("Failed to build dependency {0}: {1}", |
276 |
| - RequiredModuleName, llvm::toString(std::move(Err)))); |
277 |
| - } |
| 257 | + llvm::SmallString<256> ModuleFilesPrefix = |
| 258 | + getUniqueModuleFilesPath(ModuleUnitFileName); |
278 | 259 |
|
279 | 260 | Cmd->Output = getModuleFilePath(ModuleName, ModuleFilesPrefix);
|
280 | 261 |
|
@@ -316,58 +297,186 @@ llvm::Error buildModuleFile(llvm::StringRef ModuleName,
|
316 | 297 | if (Clang->getDiagnostics().hasErrorOccurred())
|
317 | 298 | return llvm::createStringError("Compilation failed");
|
318 | 299 |
|
319 |
| - BuiltModuleFiles.addModuleFile(ModuleName, Inputs.CompileCommand.Output); |
320 |
| - return llvm::Error::success(); |
| 300 | + return ModuleFile{ModuleName, Inputs.CompileCommand.Output}; |
321 | 301 | }
|
| 302 | + |
| 303 | +bool ReusablePrerequisiteModules::canReuse( |
| 304 | + const CompilerInvocation &CI, |
| 305 | + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const { |
| 306 | + if (RequiredModules.empty()) |
| 307 | + return true; |
| 308 | + |
| 309 | + llvm::SmallVector<llvm::StringRef> BMIPaths; |
| 310 | + for (auto &MF : RequiredModules) |
| 311 | + BMIPaths.push_back(MF->getModuleFilePath()); |
| 312 | + return IsModuleFilesUpToDate(BMIPaths, *this, VFS); |
| 313 | +} |
| 314 | + |
| 315 | +class ModuleFileCache { |
| 316 | +public: |
| 317 | + ModuleFileCache(const GlobalCompilationDatabase &CDB) : CDB(CDB) {} |
| 318 | + const GlobalCompilationDatabase &getCDB() const { return CDB; } |
| 319 | + |
| 320 | + std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName); |
| 321 | + |
| 322 | + void add(StringRef ModuleName, std::shared_ptr<const ModuleFile> ModuleFile) { |
| 323 | + std::lock_guard<std::mutex> Lock(ModuleFilesMutex); |
| 324 | + |
| 325 | + ModuleFiles[ModuleName] = ModuleFile; |
| 326 | + } |
| 327 | + |
| 328 | + void remove(StringRef ModuleName); |
| 329 | + |
| 330 | +private: |
| 331 | + const GlobalCompilationDatabase &CDB; |
| 332 | + |
| 333 | + llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles; |
| 334 | + // Mutex to guard accesses to ModuleFiles. |
| 335 | + std::mutex ModuleFilesMutex; |
| 336 | +}; |
| 337 | + |
| 338 | +std::shared_ptr<const ModuleFile> |
| 339 | +ModuleFileCache::getModule(StringRef ModuleName) { |
| 340 | + std::lock_guard<std::mutex> Lock(ModuleFilesMutex); |
| 341 | + |
| 342 | + auto Iter = ModuleFiles.find(ModuleName); |
| 343 | + if (Iter == ModuleFiles.end()) |
| 344 | + return nullptr; |
| 345 | + |
| 346 | + if (auto Res = Iter->second.lock()) |
| 347 | + return Res; |
| 348 | + |
| 349 | + ModuleFiles.erase(Iter); |
| 350 | + return nullptr; |
| 351 | +} |
| 352 | + |
| 353 | +void ModuleFileCache::remove(StringRef ModuleName) { |
| 354 | + std::lock_guard<std::mutex> Lock(ModuleFilesMutex); |
| 355 | + |
| 356 | + ModuleFiles.erase(ModuleName); |
| 357 | +} |
| 358 | + |
| 359 | +/// Collect the directly and indirectly required module names for \param |
| 360 | +/// ModuleName in topological order. The \param ModuleName is guaranteed to |
| 361 | +/// be the last element in \param ModuleNames. |
| 362 | +llvm::SmallVector<StringRef> getAllRequiredModules(ProjectModules &MDB, |
| 363 | + StringRef ModuleName) { |
| 364 | + llvm::SmallVector<llvm::StringRef> ModuleNames; |
| 365 | + llvm::StringSet<> ModuleNamesSet; |
| 366 | + |
| 367 | + auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void { |
| 368 | + ModuleNamesSet.insert(ModuleName); |
| 369 | + |
| 370 | + for (StringRef RequiredModuleName : |
| 371 | + MDB.getRequiredModules(MDB.getSourceForModuleName(ModuleName))) |
| 372 | + if (ModuleNamesSet.insert(RequiredModuleName).second) |
| 373 | + Visitor(RequiredModuleName, Visitor); |
| 374 | + |
| 375 | + ModuleNames.push_back(ModuleName); |
| 376 | + }; |
| 377 | + VisitDeps(ModuleName, VisitDeps); |
| 378 | + |
| 379 | + return ModuleNames; |
| 380 | +} |
| 381 | + |
322 | 382 | } // namespace
|
323 | 383 |
|
| 384 | +class ModulesBuilder::ModulesBuilderImpl { |
| 385 | +public: |
| 386 | + ModulesBuilderImpl(const GlobalCompilationDatabase &CDB) : Cache(CDB) {} |
| 387 | + |
| 388 | + const GlobalCompilationDatabase &getCDB() const { return Cache.getCDB(); } |
| 389 | + |
| 390 | + llvm::Error |
| 391 | + getOrBuildModuleFile(StringRef ModuleName, const ThreadsafeFS &TFS, |
| 392 | + ProjectModules &MDB, |
| 393 | + ReusablePrerequisiteModules &BuiltModuleFiles); |
| 394 | + |
| 395 | +private: |
| 396 | + ModuleFileCache Cache; |
| 397 | +}; |
| 398 | + |
| 399 | +llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile( |
| 400 | + StringRef ModuleName, const ThreadsafeFS &TFS, ProjectModules &MDB, |
| 401 | + ReusablePrerequisiteModules &BuiltModuleFiles) { |
| 402 | + if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName)) |
| 403 | + return llvm::Error::success(); |
| 404 | + |
| 405 | + PathRef ModuleUnitFileName = MDB.getSourceForModuleName(ModuleName); |
| 406 | + /// It is possible that we're meeting third party modules (modules whose |
| 407 | + /// source are not in the project. e.g, the std module may be a third-party |
| 408 | + /// module for most project) or something wrong with the implementation of |
| 409 | + /// ProjectModules. |
| 410 | + /// FIXME: How should we treat third party modules here? If we want to ignore |
| 411 | + /// third party modules, we should return true instead of false here. |
| 412 | + /// Currently we simply bail out. |
| 413 | + if (ModuleUnitFileName.empty()) |
| 414 | + return llvm::createStringError( |
| 415 | + llvm::formatv("Don't get the module unit for module {0}", ModuleName)); |
| 416 | + |
| 417 | + // Get Required modules in topological order. |
| 418 | + auto ReqModuleNames = getAllRequiredModules(MDB, ModuleName); |
| 419 | + for (llvm::StringRef ReqModuleName : ReqModuleNames) { |
| 420 | + if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName)) |
| 421 | + continue; |
| 422 | + |
| 423 | + if (auto Cached = Cache.getModule(ReqModuleName)) { |
| 424 | + if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles, |
| 425 | + TFS.view(std::nullopt))) { |
| 426 | + log("Reusing module {0} from {1}", ModuleName, |
| 427 | + Cached->getModuleFilePath()); |
| 428 | + BuiltModuleFiles.addModuleFile(std::move(Cached)); |
| 429 | + continue; |
| 430 | + } |
| 431 | + Cache.remove(ReqModuleName); |
| 432 | + } |
| 433 | + |
| 434 | + llvm::Expected<ModuleFile> MF = buildModuleFile( |
| 435 | + ModuleName, ModuleUnitFileName, getCDB(), TFS, BuiltModuleFiles); |
| 436 | + if (llvm::Error Err = MF.takeError()) |
| 437 | + return Err; |
| 438 | + |
| 439 | + log("Built module {0} to {1}", ModuleName, MF->getModuleFilePath()); |
| 440 | + auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF)); |
| 441 | + Cache.add(ModuleName, BuiltModuleFile); |
| 442 | + BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile)); |
| 443 | + } |
| 444 | + |
| 445 | + return llvm::Error::success(); |
| 446 | +} |
| 447 | + |
324 | 448 | std::unique_ptr<PrerequisiteModules>
|
325 | 449 | ModulesBuilder::buildPrerequisiteModulesFor(PathRef File,
|
326 |
| - const ThreadsafeFS &TFS) const { |
327 |
| - std::unique_ptr<ProjectModules> MDB = CDB.getProjectModules(File); |
| 450 | + const ThreadsafeFS &TFS) { |
| 451 | + std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(File); |
328 | 452 | if (!MDB) {
|
329 | 453 | elog("Failed to get Project Modules information for {0}", File);
|
330 | 454 | return std::make_unique<FailedPrerequisiteModules>();
|
331 | 455 | }
|
332 | 456 |
|
333 | 457 | std::vector<std::string> RequiredModuleNames = MDB->getRequiredModules(File);
|
334 | 458 | if (RequiredModuleNames.empty())
|
335 |
| - return std::make_unique<StandalonePrerequisiteModules>(); |
336 |
| - |
337 |
| - llvm::SmallString<256> ModuleFilesPrefix = getUniqueModuleFilesPath(File); |
338 |
| - |
339 |
| - log("Trying to build required modules for {0} in {1}", File, |
340 |
| - ModuleFilesPrefix); |
341 |
| - |
342 |
| - auto RequiredModules = std::make_unique<StandalonePrerequisiteModules>(); |
| 459 | + return std::make_unique<ReusablePrerequisiteModules>(); |
343 | 460 |
|
| 461 | + auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>(); |
344 | 462 | for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
|
345 | 463 | // Return early if there is any error.
|
346 |
| - if (llvm::Error Err = |
347 |
| - buildModuleFile(RequiredModuleName, CDB, TFS, *MDB.get(), |
348 |
| - ModuleFilesPrefix, *RequiredModules.get())) { |
| 464 | + if (llvm::Error Err = Impl->getOrBuildModuleFile( |
| 465 | + RequiredModuleName, TFS, *MDB.get(), *RequiredModules.get())) { |
349 | 466 | elog("Failed to build module {0}; due to {1}", RequiredModuleName,
|
350 | 467 | toString(std::move(Err)));
|
351 | 468 | return std::make_unique<FailedPrerequisiteModules>();
|
352 | 469 | }
|
353 | 470 | }
|
354 | 471 |
|
355 |
| - log("Built required modules for {0} in {1}", File, ModuleFilesPrefix); |
356 |
| - |
357 | 472 | return std::move(RequiredModules);
|
358 | 473 | }
|
359 | 474 |
|
360 |
| -bool StandalonePrerequisiteModules::canReuse( |
361 |
| - const CompilerInvocation &CI, |
362 |
| - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const { |
363 |
| - if (RequiredModules.empty()) |
364 |
| - return true; |
365 |
| - |
366 |
| - SmallVector<StringRef> BMIPaths; |
367 |
| - for (auto &MF : RequiredModules) |
368 |
| - BMIPaths.push_back(MF.ModuleFilePath); |
369 |
| - return IsModuleFilesUpToDate(BMIPaths, *this, VFS); |
| 475 | +ModulesBuilder::ModulesBuilder(const GlobalCompilationDatabase &CDB) { |
| 476 | + Impl = std::make_unique<ModulesBuilderImpl>(CDB); |
370 | 477 | }
|
371 | 478 |
|
| 479 | +ModulesBuilder::~ModulesBuilder() {} |
| 480 | + |
372 | 481 | } // namespace clangd
|
373 | 482 | } // namespace clang
|
0 commit comments