[clang][deps] Handle precompiled headers' AST files
authorJan Svoboda <jan_svoboda@apple.com>
Mon, 14 Jun 2021 09:15:03 +0000 (11:15 +0200)
committerJan Svoboda <jan_svoboda@apple.com>
Mon, 14 Jun 2021 09:28:39 +0000 (11:28 +0200)
The `PreprocessOnlyAction` doesn't support loading the AST file of a precompiled header. This is problematic for dependency scanning, since the `#include` manufactured for the PCH is treated as textual. This means the PCH contents get scanned with each TU, which is redundant. Moreover, dependencies of the PCH end up being considered dependency of the TU.

To handle AST file of PCH properly, this patch creates new `FrontendAction` that behaves the same way `PreprocessorOnlyAction` does, but treats the manufactured PCH `#include` as a normal compilation would (by not claiming it only uses a preprocessor and creating the default AST consumer).

The AST file is now reported as a file dependency of the TU.

Depends on D103519.

Reviewed By: Bigcheese

Differential Revision: https://reviews.llvm.org/D103524

clang/include/clang/Frontend/FrontendActions.h
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
clang/test/ClangScanDeps/modules-pch.c

index 25ca959..ff8d441 100644 (file)
@@ -34,6 +34,17 @@ public:
   bool usesPreprocessorOnly() const override { return false; }
 };
 
+/// Preprocessor-based frontend action that also loads PCH files.
+class ReadPCHAndPreprocessAction : public FrontendAction {
+  void ExecuteAction() override;
+
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override;
+
+public:
+  bool usesPreprocessorOnly() const override { return false; }
+};
+
 class DumpCompilerOptionsAction : public FrontendAction {
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef InFile) override {
index b3f6cfc..6df57cb 100644 (file)
@@ -62,6 +62,27 @@ InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
 void InitOnlyAction::ExecuteAction() {
 }
 
+// Basically PreprocessOnlyAction::ExecuteAction.
+void ReadPCHAndPreprocessAction::ExecuteAction() {
+  Preprocessor &PP = getCompilerInstance().getPreprocessor();
+
+  // Ignore unknown pragmas.
+  PP.IgnorePragmas();
+
+  Token Tok;
+  // Start parsing the specified input file.
+  PP.EnterMainSourceFile();
+  do {
+    PP.Lex(Tok);
+  } while (Tok.isNot(tok::eof));
+}
+
+std::unique_ptr<ASTConsumer>
+ReadPCHAndPreprocessAction::CreateASTConsumer(CompilerInstance &CI,
+                                              StringRef InFile) {
+  return std::make_unique<ASTConsumer>();
+}
+
 //===----------------------------------------------------------------------===//
 // AST Consumer Actions
 //===----------------------------------------------------------------------===//
index ecaeeed..0df515a 100644 (file)
@@ -73,6 +73,8 @@ public:
     if (!Compiler.hasDiagnostics())
       return false;
 
+    Compiler.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true;
+
     // Use the dependency scanning optimized file system if we can.
     if (DepFS) {
       const CompilerInvocation &CI = Compiler.getInvocation();
@@ -133,7 +135,7 @@ public:
     // the impact of strict context hashing.
     Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
 
-    auto Action = std::make_unique<PreprocessOnlyAction>();
+    auto Action = std::make_unique<ReadPCHAndPreprocessAction>();
     const bool Result = Compiler.ExecuteAction(*Action);
     if (!DepFS)
       FileMgr->clearStatCache();
index dfd8538..3271075 100644 (file)
@@ -156,6 +156,9 @@ void ModuleDepCollectorPP::EndOfMainFile() {
   MDC.MainFile = std::string(
       Instance.getSourceManager().getFileEntryForID(MainFileID)->getName());
 
+  if (!Instance.getPreprocessorOpts().ImplicitPCHInclude.empty())
+    MDC.FileDeps.push_back(Instance.getPreprocessorOpts().ImplicitPCHInclude);
+
   for (const Module *M : DirectModularDeps)
     handleTopLevelModule(M);
 
index ddb6949..020818f 100644 (file)
@@ -9,5 +9,52 @@
 // Scan dependencies of the TU:
 //
 // RUN: sed "s|DIR|%/t|g" %S/Inputs/modules-pch/cdb_tu.json > %t/cdb.json
+// RUN: echo -%t > %t/result_tu.json
+// FIXME: Make this work with '-mode preprocess-minimized-sources'.
 // RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \
-// RUN:   -generate-modules-path-args -module-files-dir %t/build
+// RUN:   -generate-modules-path-args -module-files-dir %t/build -mode preprocess >> %t/result_tu.json
+// RUN: cat %t/result_tu.json | sed 's:\\\\\?:/:g' | FileCheck %s -check-prefix=CHECK-TU
+//
+// CHECK-TU:      -[[PREFIX:.*]]
+// CHECK-TU-NEXT: {
+// CHECK-TU-NEXT:   "modules": [
+// CHECK-TU-NEXT:     {
+// CHECK-TU-NEXT:       "clang-module-deps": [],
+// CHECK-TU-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-TU-NEXT:       "command-line": [
+// CHECK-TU-NEXT:         "-cc1",
+// CHECK-TU:              "-emit-module",
+// CHECK-TU:              "-fmodule-name=ModTU",
+// CHECK-TU:              "-fno-implicit-modules",
+// CHECK-TU:            ],
+// CHECK-TU-NEXT:       "context-hash": "[[HASH_MOD_TU:.*]]",
+// CHECK-TU-NEXT:       "file-deps": [
+// CHECK-TU-NEXT:         "[[PREFIX]]/mod_tu.h",
+// CHECK-TU-NEXT:         "[[PREFIX]]/module.modulemap"
+// CHECK-TU-NEXT:       ],
+// CHECK-TU-NEXT:       "name": "ModTU"
+// CHECK-TU-NEXT:     }
+// CHECK-TU-NEXT:   ],
+// CHECK-TU-NEXT:   "translation-units": [
+// CHECK-TU-NEXT:     {
+// CHECK-TU-NEXT:       "clang-context-hash": "[[HASH_TU:.*]]",
+// CHECK-TU-NEXT:       "clang-module-deps": [
+// CHECK-TU-NEXT:         {
+// CHECK-TU-NEXT:           "context-hash": "[[HASH_MOD_TU]]",
+// CHECK-TU-NEXT:           "module-name": "ModTU"
+// CHECK-TU-NEXT:         }
+// CHECK-TU-NEXT:       ],
+// CHECK-TU-NEXT:       "command-line": [
+// CHECK-TU-NEXT:         "-fno-implicit-modules",
+// CHECK-TU-NEXT:         "-fno-implicit-module-maps",
+// CHECK-TU-NEXT:         "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_TU]]/ModTU-{{.*}}.pcm",
+// CHECK-TU-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-TU-NEXT:       ],
+// CHECK-TU-NEXT:       "file-deps": [
+// CHECK-TU-NEXT:         "[[PREFIX]]/tu.c",
+// CHECK-TU-NEXT:         "[[PREFIX]]/pch.h.gch"
+// CHECK-TU-NEXT:       ],
+// CHECK-TU-NEXT:       "input-file": "[[PREFIX]]/tu.c"
+// CHECK-TU-NEXT:     }
+// CHECK-TU-NEXT:   ]
+// CHECK-TU-NEXT: }