Author: Jan Svoboda
Date: 2022-03-16T12:17:53+01:00
New Revision: d73daa9135463ad4d4a08a9f0a75e109f921ad54

URL: 
https://github.com/llvm/llvm-project/commit/d73daa9135463ad4d4a08a9f0a75e109f921ad54
DIFF: 
https://github.com/llvm/llvm-project/commit/d73daa9135463ad4d4a08a9f0a75e109f921ad54.diff

LOG: [clang][deps] Don't prune search paths used by dependencies

When pruning header search paths (to reduce the number of modules we need to 
build explicitly), we can't prune the search paths used in (transitive) 
dependencies of a module. Otherwise, we could end up with either of the 
following dependency graphs:

```
X:<hash1> -> Y:<hash2>
X:<hash1> -> Y:<hash3>
```

depending on the search paths of the translation unit we discovered `X` and `Y` 
from.

This patch fixes that.

Depends on D121295.

Reviewed By: dexonsmith

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

Added: 
    clang/test/ClangScanDeps/header-search-pruning-transitive.c

Modified: 
    clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp 
b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 4d70b0c80aad2..d3a84bf8a8e47 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -23,9 +23,21 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions 
&Opts,
   // Only preserve search paths that were used during the dependency scan.
   std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries;
   Opts.UserEntries.clear();
-  for (unsigned I = 0; I < Entries.size(); ++I)
-    if (MF.SearchPathUsage[I])
-      Opts.UserEntries.push_back(Entries[I]);
+
+  llvm::BitVector SearchPathUsage(Entries.size());
+  llvm::DenseSet<const serialization::ModuleFile *> Visited;
+  std::function<void(const serialization::ModuleFile *)> VisitMF =
+      [&](const serialization::ModuleFile *MF) {
+        SearchPathUsage |= MF->SearchPathUsage;
+        Visited.insert(MF);
+        for (const serialization::ModuleFile *Import : MF->Imports)
+          if (!Visited.contains(Import))
+            VisitMF(Import);
+      };
+  VisitMF(&MF);
+
+  for (auto Idx : SearchPathUsage.set_bits())
+    Opts.UserEntries.push_back(Entries[Idx]);
 }
 
 CompilerInvocation 
ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(

diff  --git a/clang/test/ClangScanDeps/header-search-pruning-transitive.c 
b/clang/test/ClangScanDeps/header-search-pruning-transitive.c
new file mode 100644
index 0000000000000..434f7e5d9fb22
--- /dev/null
+++ b/clang/test/ClangScanDeps/header-search-pruning-transitive.c
@@ -0,0 +1,169 @@
+// This test checks that pruning of header search paths produces consistent 
dependency graphs.
+//
+// When pruning header search paths for a module, we can't remove any paths 
its dependencies use.
+// Otherwise, we could get either of the following dependency graphs depending 
on the search path
+// configuration of the particular TU that first discovered the module:
+//   X:<hash1> -> Y:<hash2>
+//   X:<hash1> -> Y:<hash3>
+// We can't have the same version of module X depend on multiple 
diff erent versions of Y based on
+// the TU configuration.
+//
+// Keeping all header search paths (transitive) dependencies use will ensure 
we get consistent
+// dependency graphs:
+//   X:<hash1> -> Y:<hash2>
+//   X:<hash4> -> Y:<hash3>
+
+// RUN: rm -rf %t && mkdir %t
+// RUN: split-file %s %t
+
+//--- a/a.h
+//--- b/b.h
+//--- begin/begin.h
+//--- end/end.h
+//--- Y.h
+#include "begin.h"
+#if __has_include("a.h")
+#include "a.h"
+#endif
+#include "end.h"
+
+//--- X.h
+#include "Y.h"
+
+//--- module.modulemap
+module Y { header "Y.h" }
+module X { header "X.h" }
+
+//--- test.c
+#include "X.h"
+
+//--- cdb_with_a.json.template
+[{
+  "file": "DIR/test.c",
+  "directory": "DIR",
+  "command": "clang -c test.c -o DIR/test.o -fmodules -fimplicit-modules 
-fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -Ibegin -Ia -Ib 
-Iend"
+}]
+
+//--- cdb_without_a.json.template
+[{
+  "file": "DIR/test.c",
+  "directory": "DIR",
+  "command": "clang -c test.c -o DIR/test.o -fmodules -fimplicit-modules 
-fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -Ibegin     -Ib 
-Iend"
+}]
+
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb_with_a.json.template    > 
%t/cdb_with_a.json
+// RUN: sed -e "s|DIR|%/t|g" %t/cdb_without_a.json.template > 
%t/cdb_without_a.json
+
+// RUN: echo -%t > %t/results.json
+// RUN: clang-scan-deps -compilation-database %t/cdb_with_a.json    -format 
experimental-full -optimize-args >> %t/results.json
+// RUN: clang-scan-deps -compilation-database %t/cdb_without_a.json -format 
experimental-full -optimize-args >> %t/results.json
+// RUN: cat %t/results.json | sed 's/\\/\//g' | FileCheck %s
+
+// CHECK:      -[[PREFIX:.*]]
+// CHECK-NEXT: {
+// CHECK-NEXT:   "modules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "context-hash": "[[HASH_Y_WITH_A:.*]]",
+// CHECK-NEXT:           "module-name": "Y"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// CHECK-NEXT:       "context-hash": "[[HASH_X:.*]]",
+// CHECK-NEXT:       "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/./X.h",
+// CHECK-NEXT:         "[[PREFIX]]/./module.modulemap"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "name": "X"
+// CHECK-NEXT:     },
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-module-deps": [],
+// CHECK-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// CHECK-NEXT:       "context-hash": "[[HASH_Y_WITH_A]]",
+// CHECK-NEXT:       "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/./Y.h",
+// CHECK-NEXT:         "[[PREFIX]]/./a/a.h",
+// CHECK-NEXT:         "[[PREFIX]]/./begin/begin.h",
+// CHECK-NEXT:         "[[PREFIX]]/./end/end.h",
+// CHECK-NEXT:         "[[PREFIX]]/./module.modulemap"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "name": "Y"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "translation-units": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "context-hash": "[[HASH_X]]",
+// CHECK-NEXT:           "module-name": "X"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// CHECK-NEXT:       "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/test.c"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "input-file": "[[PREFIX]]/test.c"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ]
+// CHECK-NEXT: }
+// CHECK-NEXT: {
+// CHECK-NEXT:   "modules": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "context-hash": "[[HASH_Y_WITHOUT_A:.*]]",
+// CHECK-NEXT:           "module-name": "Y"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// Here is the actual check that this module X (which imports 
diff erent version of Y)
+// also has a 
diff erent context hash from the first version of module X.
+// CHECK-NOT:        "context-hash": "[[HASH_X]]",
+// CHECK:            "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/./X.h",
+// CHECK-NEXT:         "[[PREFIX]]/./module.modulemap"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "name": "X"
+// CHECK-NEXT:     },
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-module-deps": [],
+// CHECK-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// CHECK-NEXT:       "context-hash": "[[HASH_Y_WITHOUT_A]]",
+// CHECK-NEXT:       "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/./Y.h",
+// CHECK-NEXT:         "[[PREFIX]]/./begin/begin.h",
+// CHECK-NEXT:         "[[PREFIX]]/./end/end.h",
+// CHECK-NEXT:         "[[PREFIX]]/./module.modulemap"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "name": "Y"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "translation-units": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "clang-context-hash": "{{.*}}",
+// CHECK-NEXT:       "clang-module-deps": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "context-hash": "{{.*}}",
+// CHECK-NEXT:           "module-name": "X"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "command-line": [
+// CHECK:            ],
+// CHECK-NEXT:       "file-deps": [
+// CHECK-NEXT:         "[[PREFIX]]/test.c"
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "input-file": "[[PREFIX]]/test.c"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ]
+// CHECK-NEXT: }


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to