Skip to content

Commit 989a40c

Browse files
authored
[clang][modules] Invalidate module cache when SDKSettings.json changes (#139751)
This PR adds the `%sdk/SDKSettings.json` file to the PCM input file table, so that the PCM gets invalidated when the file changes. This is necessary for availability checks to work correctly.
1 parent 8a05c20 commit 989a40c

File tree

3 files changed

+115
-15
lines changed

3 files changed

+115
-15
lines changed

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,29 @@ struct InputFileEntry {
17741774
uint32_t ContentHash[2];
17751775

17761776
InputFileEntry(FileEntryRef File) : File(File) {}
1777+
1778+
void trySetContentHash(
1779+
Preprocessor &PP,
1780+
llvm::function_ref<std::optional<llvm::MemoryBufferRef>()> GetMemBuff) {
1781+
ContentHash[0] = 0;
1782+
ContentHash[1] = 0;
1783+
1784+
if (!PP.getHeaderSearchInfo()
1785+
.getHeaderSearchOpts()
1786+
.ValidateASTInputFilesContent)
1787+
return;
1788+
1789+
auto MemBuff = GetMemBuff();
1790+
if (!MemBuff) {
1791+
PP.Diag(SourceLocation(), diag::err_module_unable_to_hash_content)
1792+
<< File.getName();
1793+
return;
1794+
}
1795+
1796+
uint64_t Hash = xxh3_64bits(MemBuff->getBuffer());
1797+
ContentHash[0] = uint32_t(Hash);
1798+
ContentHash[1] = uint32_t(Hash >> 32);
1799+
}
17771800
};
17781801

17791802
} // namespace
@@ -1848,25 +1871,41 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr) {
18481871
!IsSLocFileEntryAffecting[IncludeFileID.ID];
18491872
Entry.IsModuleMap = isModuleMap(File.getFileCharacteristic());
18501873

1851-
uint64_t ContentHash = 0;
1852-
if (PP->getHeaderSearchInfo()
1853-
.getHeaderSearchOpts()
1854-
.ValidateASTInputFilesContent) {
1855-
auto MemBuff = Cache->getBufferIfLoaded();
1856-
if (MemBuff)
1857-
ContentHash = xxh3_64bits(MemBuff->getBuffer());
1858-
else
1859-
PP->Diag(SourceLocation(), diag::err_module_unable_to_hash_content)
1860-
<< Entry.File.getName();
1861-
}
1862-
Entry.ContentHash[0] = uint32_t(ContentHash);
1863-
Entry.ContentHash[1] = uint32_t(ContentHash >> 32);
1874+
Entry.trySetContentHash(*PP, [&] { return Cache->getBufferIfLoaded(); });
1875+
18641876
if (Entry.IsSystemFile)
18651877
SystemFiles.push_back(Entry);
18661878
else
18671879
UserFiles.push_back(Entry);
18681880
}
18691881

1882+
// FIXME: Make providing input files not in the SourceManager more flexible.
1883+
// The SDKSettings.json file is necessary for correct evaluation of
1884+
// availability annotations.
1885+
StringRef Sysroot = PP->getHeaderSearchInfo().getHeaderSearchOpts().Sysroot;
1886+
if (!Sysroot.empty()) {
1887+
SmallString<128> SDKSettingsJSON = Sysroot;
1888+
llvm::sys::path::append(SDKSettingsJSON, "SDKSettings.json");
1889+
FileManager &FM = PP->getFileManager();
1890+
if (auto FE = FM.getOptionalFileRef(SDKSettingsJSON)) {
1891+
InputFileEntry Entry(*FE);
1892+
Entry.IsSystemFile = true;
1893+
Entry.IsTransient = false;
1894+
Entry.BufferOverridden = false;
1895+
Entry.IsTopLevel = true;
1896+
Entry.IsModuleMap = false;
1897+
std::unique_ptr<MemoryBuffer> MB;
1898+
Entry.trySetContentHash(*PP, [&]() -> std::optional<MemoryBufferRef> {
1899+
if (auto MBOrErr = FM.getBufferForFile(Entry.File)) {
1900+
MB = std::move(*MBOrErr);
1901+
return MB->getMemBufferRef();
1902+
}
1903+
return std::nullopt;
1904+
});
1905+
SystemFiles.push_back(Entry);
1906+
}
1907+
}
1908+
18701909
// User files go at the front, system files at the back.
18711910
auto SortedFiles = llvm::concat<InputFileEntry>(std::move(UserFiles),
18721911
std::move(SystemFiles));
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This test checks that the module cache gets invalidated when the SDKSettings.json file changes.
2+
3+
// RUN: rm -rf %t
4+
// RUN: split-file %s %t
5+
6+
//--- AppleTVOS15.0.sdk/SDKSettings-old.json
7+
{
8+
"DisplayName": "tvOS 15.0",
9+
"Version": "15.0",
10+
"CanonicalName": "appletvos15.0",
11+
"MaximumDeploymentTarget": "15.0.99",
12+
"PropertyConditionFallbackNames": []
13+
}
14+
//--- AppleTVOS15.0.sdk/SDKSettings-new.json
15+
{
16+
"DisplayName": "tvOS 15.0",
17+
"Version": "15.0",
18+
"CanonicalName": "appletvos15.0",
19+
"MaximumDeploymentTarget": "15.0.99",
20+
"PropertyConditionFallbackNames": [],
21+
"VersionMap": {
22+
"iOS_tvOS": {
23+
"13.2": "13.1"
24+
},
25+
"tvOS_iOS": {
26+
"13.1": "13.2"
27+
}
28+
}
29+
}
30+
//--- module.modulemap
31+
module M { header "M.h" }
32+
//--- M.h
33+
void foo(void) __attribute__((availability(iOS, obsoleted = 13.2)));
34+
void test() { foo(); }
35+
36+
//--- tu.m
37+
#include "M.h"
38+
39+
// Compiling for tvOS 13.1 without "VersionMap" should succeed, since by default iOS 13.2 gets mapped to tvOS 13.2,
40+
// and \c foo is therefore **not** deprecated.
41+
// RUN: cp %t/AppleTVOS15.0.sdk/SDKSettings-old.json %t/AppleTVOS15.0.sdk/SDKSettings.json
42+
// RUN: %clang -target x86_64-apple-tvos13.1 -isysroot %t/AppleTVOS15.0.sdk \
43+
// RUN: -fsyntax-only %t/tu.m -o %t/tu.o -fmodules -Xclang -fdisable-module-hash -fmodules-cache-path=%t/cache
44+
45+
// Compiling for tvOS 13.1 with "VersionMap" saying it maps to iOS 13.2 should fail, since \c foo is now deprecated.
46+
// RUN: sleep 1
47+
// RUN: cp %t/AppleTVOS15.0.sdk/SDKSettings-new.json %t/AppleTVOS15.0.sdk/SDKSettings.json
48+
// RUN: not %clang -target x86_64-apple-tvos13.1 -isysroot %t/AppleTVOS15.0.sdk \
49+
// RUN: -fsyntax-only %t/tu.m -o %t/tu.o -fmodules -Xclang -fdisable-module-hash -fmodules-cache-path=%t/cache 2>&1 \
50+
// RUN: | FileCheck %s
51+
// CHECK: M.h:2:15: error: 'foo' is unavailable: obsoleted in tvOS 13.1
52+
// CHECK: M.h:1:6: note: 'foo' has been explicitly marked unavailable here
53+
// CHECK: tu.m:1:10: fatal error: could not build module 'M'

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,10 @@ template <typename Container>
346346
static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
347347
return [&JOS, Strings = std::forward<Container>(Strings)] {
348348
for (StringRef Str : Strings)
349-
JOS.value(Str);
349+
// Not reporting SDKSettings.json so that test checks can remain (mostly)
350+
// platform-agnostic.
351+
if (!Str.ends_with("SDKSettings.json"))
352+
JOS.value(Str);
350353
};
351354
}
352355

@@ -498,7 +501,12 @@ class FullDeps {
498501
toJSONStrings(JOS, MD.getBuildArguments()));
499502
JOS.attribute("context-hash", StringRef(MD.ID.ContextHash));
500503
JOS.attributeArray("file-deps", [&] {
501-
MD.forEachFileDep([&](StringRef FileDep) { JOS.value(FileDep); });
504+
MD.forEachFileDep([&](StringRef FileDep) {
505+
// Not reporting SDKSettings.json so that test checks can remain
506+
// (mostly) platform-agnostic.
507+
if (!FileDep.ends_with("SDKSettings.json"))
508+
JOS.value(FileDep);
509+
});
502510
});
503511
JOS.attributeArray("link-libraries",
504512
toJSONSorted(JOS, MD.LinkLibraries));

0 commit comments

Comments
 (0)