Skip to content

Commit e4743e5

Browse files
committed
[clang][DependencyScanner] Include the working directory in the context hash
The working directory is included in the PCM, but is not currently part of the context hash. This causes problems because different builds of a PCM with exactly the same command line can end up with different binary content for a PCM. If a build system tracks tasks by both working directory and command line, it may build a given PCM multiple times, causing a "module file out of date" error when loading the PCM due to different sizes.
1 parent e7f5d60 commit e4743e5

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-2
lines changed

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
316316

317317
static std::string getModuleContextHash(const ModuleDeps &MD,
318318
const CowCompilerInvocation &CI,
319-
bool EagerLoadModules) {
319+
bool EagerLoadModules,
320+
llvm::vfs::FileSystem &VFS) {
320321
llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::endianness::native>
321322
HashBuilder;
322323
SmallString<32> Scratch;
@@ -325,6 +326,9 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
325326
// will be readable.
326327
HashBuilder.add(getClangFullRepositoryVersion());
327328
HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR);
329+
llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory();
330+
if (CWD)
331+
HashBuilder.add(*CWD);
328332

329333
// Hash the BuildInvocation without any input files.
330334
SmallString<0> ArgVec;
@@ -356,7 +360,8 @@ static std::string getModuleContextHash(const ModuleDeps &MD,
356360

357361
void ModuleDepCollector::associateWithContextHash(
358362
const CowCompilerInvocation &CI, ModuleDeps &Deps) {
359-
Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules);
363+
Deps.ID.ContextHash = getModuleContextHash(
364+
Deps, CI, EagerLoadModules, ScanInstance.getVirtualFileSystem());
360365
bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second;
361366
(void)Inserted;
362367
assert(Inserted && "duplicate module mapping");
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands.json.in > %t/build/compile-commands.json
4+
// RUN: clang-scan-deps -compilation-database %t/build/compile-commands.json \
5+
// RUN: -j 1 -format experimental-full --optimize-args=all > %t/deps.db
6+
// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t
7+
8+
// Check that there are two separate modules hashes. One for each working dir.
9+
10+
// CHECK: {
11+
// CHECK-NEXT: "modules": [
12+
// CHECK-NEXT: {
13+
// CHECK-NEXT: "clang-module-deps":
14+
// CHECK-NEXT: "clang-modulemap-file":
15+
// CHECK-NEXT: "command-line": [
16+
// CHECK: ],
17+
// CHECK-NEXT: "context-hash": "{{.*}}",
18+
// CHECK-NEXT: "file-deps": [
19+
// CHECK: ],
20+
// CHECK-NEXT: "name": "A"
21+
// CHECK-NEXT: },
22+
// CHECK-NEXT: {
23+
// CHECK-NEXT: "clang-module-deps":
24+
// CHECK-NEXT: "clang-modulemap-file":
25+
// CHECK-NEXT: "command-line": [
26+
// CHECK: ],
27+
// CHECK-NEXT: "context-hash": "{{.*}}",
28+
// CHECK-NEXT: "file-deps": [
29+
// CHECK: ],
30+
// CHECK-NEXT: "name": "A"
31+
// CHECK-NEXT: },
32+
// CHECK-NEXT: {
33+
// CHECK-NEXT: "clang-module-deps":
34+
// CHECK: ],
35+
// CHECK-NEXT: "clang-modulemap-file":
36+
// CHECK-NEXT: "command-line": [
37+
// CHECK: ],
38+
// CHECK-NEXT: "context-hash": "{{.*}}",
39+
// CHECK-NEXT: "file-deps": [
40+
// CHECK: ],
41+
// CHECK-NEXT: "name": "B"
42+
// CHECK-NEXT: },
43+
// CHECK-NEXT: {
44+
// CHECK-NEXT: "clang-module-deps":
45+
// CHECK: ],
46+
// CHECK-NEXT: "clang-modulemap-file":
47+
// CHECK-NEXT: "command-line": [
48+
// CHECK: ],
49+
// CHECK-NEXT: "context-hash": "{{.*}}",
50+
// CHECK-NEXT: "file-deps": [
51+
// CHECK: ],
52+
// CHECK-NEXT: "name": "B"
53+
// CHECK-NEXT: }
54+
// CHECK-NEXT: ],
55+
// CHECK-NEXT: "translation-units": [
56+
// CHECK: ]
57+
// CHECK: }
58+
59+
//--- build/compile-commands.json.in
60+
61+
[
62+
{
63+
"directory": "DIR/build",
64+
"command": "clang -c DIR/A.m -IDIR/modules/A -IDIR/modules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
65+
"file": "DIR/A.m"
66+
},
67+
{
68+
"directory": "DIR",
69+
"command": "clang -c DIR/B.m -IDIR/modules/A -IDIR/modules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps",
70+
"file": "DIR/B.m"
71+
}
72+
]
73+
74+
75+
//--- modules/A/module.modulemap
76+
77+
module A {
78+
umbrella header "A.h"
79+
}
80+
81+
//--- modules/A/A.h
82+
83+
typedef int A_t;
84+
85+
//--- modules/B/module.modulemap
86+
87+
module B {
88+
umbrella header "B.h"
89+
}
90+
91+
//--- modules/B/B.h
92+
93+
#include <A.h>
94+
95+
typedef int B_t;
96+
97+
//--- A.m
98+
99+
#include <B.h>
100+
101+
A_t a = 0;
102+
103+
//--- B.m
104+
105+
#include <B.h>
106+
107+
B_t b = 0;

0 commit comments

Comments
 (0)