Author: Jan Svoboda Date: 2025-05-13T14:00:31-07:00 New Revision: 989a40cba889630a916a4f815a8cabca1e14242a
URL: https://github.com/llvm/llvm-project/commit/989a40cba889630a916a4f815a8cabca1e14242a DIFF: https://github.com/llvm/llvm-project/commit/989a40cba889630a916a4f815a8cabca1e14242a.diff LOG: [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. Added: clang/test/Modules/sdk-settings-json-dep.m Modified: clang/lib/Serialization/ASTWriter.cpp clang/tools/clang-scan-deps/ClangScanDeps.cpp Removed: ################################################################################ diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 7de510c85bfed..1b3d3c22aa9f5 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1774,6 +1774,29 @@ struct InputFileEntry { uint32_t ContentHash[2]; InputFileEntry(FileEntryRef File) : File(File) {} + + void trySetContentHash( + Preprocessor &PP, + llvm::function_ref<std::optional<llvm::MemoryBufferRef>()> GetMemBuff) { + ContentHash[0] = 0; + ContentHash[1] = 0; + + if (!PP.getHeaderSearchInfo() + .getHeaderSearchOpts() + .ValidateASTInputFilesContent) + return; + + auto MemBuff = GetMemBuff(); + if (!MemBuff) { + PP.Diag(SourceLocation(), diag::err_module_unable_to_hash_content) + << File.getName(); + return; + } + + uint64_t Hash = xxh3_64bits(MemBuff->getBuffer()); + ContentHash[0] = uint32_t(Hash); + ContentHash[1] = uint32_t(Hash >> 32); + } }; } // namespace @@ -1848,25 +1871,41 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr) { !IsSLocFileEntryAffecting[IncludeFileID.ID]; Entry.IsModuleMap = isModuleMap(File.getFileCharacteristic()); - uint64_t ContentHash = 0; - if (PP->getHeaderSearchInfo() - .getHeaderSearchOpts() - .ValidateASTInputFilesContent) { - auto MemBuff = Cache->getBufferIfLoaded(); - if (MemBuff) - ContentHash = xxh3_64bits(MemBuff->getBuffer()); - else - PP->Diag(SourceLocation(), diag::err_module_unable_to_hash_content) - << Entry.File.getName(); - } - Entry.ContentHash[0] = uint32_t(ContentHash); - Entry.ContentHash[1] = uint32_t(ContentHash >> 32); + Entry.trySetContentHash(*PP, [&] { return Cache->getBufferIfLoaded(); }); + if (Entry.IsSystemFile) SystemFiles.push_back(Entry); else UserFiles.push_back(Entry); } + // FIXME: Make providing input files not in the SourceManager more flexible. + // The SDKSettings.json file is necessary for correct evaluation of + // availability annotations. + StringRef Sysroot = PP->getHeaderSearchInfo().getHeaderSearchOpts().Sysroot; + if (!Sysroot.empty()) { + SmallString<128> SDKSettingsJSON = Sysroot; + llvm::sys::path::append(SDKSettingsJSON, "SDKSettings.json"); + FileManager &FM = PP->getFileManager(); + if (auto FE = FM.getOptionalFileRef(SDKSettingsJSON)) { + InputFileEntry Entry(*FE); + Entry.IsSystemFile = true; + Entry.IsTransient = false; + Entry.BufferOverridden = false; + Entry.IsTopLevel = true; + Entry.IsModuleMap = false; + std::unique_ptr<MemoryBuffer> MB; + Entry.trySetContentHash(*PP, [&]() -> std::optional<MemoryBufferRef> { + if (auto MBOrErr = FM.getBufferForFile(Entry.File)) { + MB = std::move(*MBOrErr); + return MB->getMemBufferRef(); + } + return std::nullopt; + }); + SystemFiles.push_back(Entry); + } + } + // User files go at the front, system files at the back. auto SortedFiles = llvm::concat<InputFileEntry>(std::move(UserFiles), std::move(SystemFiles)); diff --git a/clang/test/Modules/sdk-settings-json-dep.m b/clang/test/Modules/sdk-settings-json-dep.m new file mode 100644 index 0000000000000..196f4219bd989 --- /dev/null +++ b/clang/test/Modules/sdk-settings-json-dep.m @@ -0,0 +1,53 @@ +// This test checks that the module cache gets invalidated when the SDKSettings.json file changes. + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- AppleTVOS15.0.sdk/SDKSettings-old.json +{ + "DisplayName": "tvOS 15.0", + "Version": "15.0", + "CanonicalName": "appletvos15.0", + "MaximumDeploymentTarget": "15.0.99", + "PropertyConditionFallbackNames": [] +} +//--- AppleTVOS15.0.sdk/SDKSettings-new.json +{ + "DisplayName": "tvOS 15.0", + "Version": "15.0", + "CanonicalName": "appletvos15.0", + "MaximumDeploymentTarget": "15.0.99", + "PropertyConditionFallbackNames": [], + "VersionMap": { + "iOS_tvOS": { + "13.2": "13.1" + }, + "tvOS_iOS": { + "13.1": "13.2" + } + } +} +//--- module.modulemap +module M { header "M.h" } +//--- M.h +void foo(void) __attribute__((availability(iOS, obsoleted = 13.2))); +void test() { foo(); } + +//--- tu.m +#include "M.h" + +// Compiling for tvOS 13.1 without "VersionMap" should succeed, since by default iOS 13.2 gets mapped to tvOS 13.2, +// and \c foo is therefore **not** deprecated. +// RUN: cp %t/AppleTVOS15.0.sdk/SDKSettings-old.json %t/AppleTVOS15.0.sdk/SDKSettings.json +// RUN: %clang -target x86_64-apple-tvos13.1 -isysroot %t/AppleTVOS15.0.sdk \ +// RUN: -fsyntax-only %t/tu.m -o %t/tu.o -fmodules -Xclang -fdisable-module-hash -fmodules-cache-path=%t/cache + +// Compiling for tvOS 13.1 with "VersionMap" saying it maps to iOS 13.2 should fail, since \c foo is now deprecated. +// RUN: sleep 1 +// RUN: cp %t/AppleTVOS15.0.sdk/SDKSettings-new.json %t/AppleTVOS15.0.sdk/SDKSettings.json +// RUN: not %clang -target x86_64-apple-tvos13.1 -isysroot %t/AppleTVOS15.0.sdk \ +// RUN: -fsyntax-only %t/tu.m -o %t/tu.o -fmodules -Xclang -fdisable-module-hash -fmodules-cache-path=%t/cache 2>&1 \ +// RUN: | FileCheck %s +// CHECK: M.h:2:15: error: 'foo' is unavailable: obsoleted in tvOS 13.1 +// CHECK: M.h:1:6: note: 'foo' has been explicitly marked unavailable here +// CHECK: tu.m:1:10: fatal error: could not build module 'M' diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index dae2b9a9fe683..3b42267f4d5f4 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -346,7 +346,10 @@ template <typename Container> static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) { return [&JOS, Strings = std::forward<Container>(Strings)] { for (StringRef Str : Strings) - JOS.value(Str); + // Not reporting SDKSettings.json so that test checks can remain (mostly) + // platform-agnostic. + if (!Str.ends_with("SDKSettings.json")) + JOS.value(Str); }; } @@ -498,7 +501,12 @@ class FullDeps { toJSONStrings(JOS, MD.getBuildArguments())); JOS.attribute("context-hash", StringRef(MD.ID.ContextHash)); JOS.attributeArray("file-deps", [&] { - MD.forEachFileDep([&](StringRef FileDep) { JOS.value(FileDep); }); + MD.forEachFileDep([&](StringRef FileDep) { + // Not reporting SDKSettings.json so that test checks can remain + // (mostly) platform-agnostic. + if (!FileDep.ends_with("SDKSettings.json")) + JOS.value(FileDep); + }); }); JOS.attributeArray("link-libraries", toJSONSorted(JOS, MD.LinkLibraries)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits