Skip to content

Commit 286f675

Browse files
committed
[clang][modules] Invalidate module cache when SDKSettings.json changes (llvm#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 f327405 commit 286f675

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
@@ -1771,6 +1771,29 @@ struct InputFileEntry {
17711771
uint32_t ContentHash[2];
17721772

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

17761799
} // namespace
@@ -1843,25 +1866,41 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr,
18431866
Entry.IsTopLevel = getAffectingIncludeLoc(SourceMgr, File).isInvalid();
18441867
Entry.IsModuleMap = isModuleMap(File.getFileCharacteristic());
18451868

1846-
uint64_t ContentHash = 0;
1847-
if (PP->getHeaderSearchInfo()
1848-
.getHeaderSearchOpts()
1849-
.ValidateASTInputFilesContent) {
1850-
auto MemBuff = Cache->getBufferIfLoaded();
1851-
if (MemBuff)
1852-
ContentHash = xxh3_64bits(MemBuff->getBuffer());
1853-
else
1854-
PP->Diag(SourceLocation(), diag::err_module_unable_to_hash_content)
1855-
<< Entry.File.getName();
1856-
}
1857-
Entry.ContentHash[0] = uint32_t(ContentHash);
1858-
Entry.ContentHash[1] = uint32_t(ContentHash >> 32);
1869+
Entry.trySetContentHash(*PP, [&] { return Cache->getBufferIfLoaded(); });
1870+
18591871
if (Entry.IsSystemFile)
18601872
SystemFiles.push_back(Entry);
18611873
else
18621874
UserFiles.push_back(Entry);
18631875
}
18641876

1877+
// FIXME: Make providing input files not in the SourceManager more flexible.
1878+
// The SDKSettings.json file is necessary for correct evaluation of
1879+
// availability annotations.
1880+
StringRef Sysroot = PP->getHeaderSearchInfo().getHeaderSearchOpts().Sysroot;
1881+
if (!Sysroot.empty()) {
1882+
SmallString<128> SDKSettingsJSON = Sysroot;
1883+
llvm::sys::path::append(SDKSettingsJSON, "SDKSettings.json");
1884+
FileManager &FM = PP->getFileManager();
1885+
if (auto FE = FM.getOptionalFileRef(SDKSettingsJSON)) {
1886+
InputFileEntry Entry(*FE);
1887+
Entry.IsSystemFile = true;
1888+
Entry.IsTransient = false;
1889+
Entry.BufferOverridden = false;
1890+
Entry.IsTopLevel = true;
1891+
Entry.IsModuleMap = false;
1892+
std::unique_ptr<MemoryBuffer> MB;
1893+
Entry.trySetContentHash(*PP, [&]() -> std::optional<MemoryBufferRef> {
1894+
if (auto MBOrErr = FM.getBufferForFile(Entry.File)) {
1895+
MB = std::move(*MBOrErr);
1896+
return MB->getMemBufferRef();
1897+
}
1898+
return std::nullopt;
1899+
});
1900+
SystemFiles.push_back(Entry);
1901+
}
1902+
}
1903+
18651904
// User files go at the front, system files at the back.
18661905
auto SortedFiles = llvm::concat<InputFileEntry>(std::move(UserFiles),
18671906
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
@@ -597,7 +597,10 @@ template <typename Container>
597597
static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
598598
return [&JOS, Strings = std::forward<Container>(Strings)] {
599599
for (StringRef Str : Strings)
600-
JOS.value(Str);
600+
// Not reporting SDKSettings.json so that test checks can remain (mostly)
601+
// platform-agnostic.
602+
if (!Str.ends_with("SDKSettings.json"))
603+
JOS.value(Str);
601604
};
602605
}
603606

@@ -743,7 +746,12 @@ class FullDeps {
743746
toJSONStrings(JOS, MD.getBuildArguments()));
744747
JOS.attribute("context-hash", StringRef(MD.ID.ContextHash));
745748
JOS.attributeArray("file-deps", [&] {
746-
MD.forEachFileDep([&](StringRef FileDep) { JOS.value(FileDep); });
749+
MD.forEachFileDep([&](StringRef FileDep) {
750+
// Not reporting SDKSettings.json so that test checks can remain
751+
// (mostly) platform-agnostic.
752+
if (!FileDep.ends_with("SDKSettings.json"))
753+
JOS.value(FileDep);
754+
});
747755
});
748756
JOS.attributeArray("link-libraries",
749757
toJSONSorted(JOS, MD.LinkLibraries));

0 commit comments

Comments
 (0)