1766a08dfSBen Barham //===- unittests/Serialization/ModuleCacheTest.cpp - CI tests -------------===// 2766a08dfSBen Barham // 3766a08dfSBen Barham // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4766a08dfSBen Barham // See https://llvm.org/LICENSE.txt for license information. 5766a08dfSBen Barham // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6766a08dfSBen Barham // 7766a08dfSBen Barham //===----------------------------------------------------------------------===// 8766a08dfSBen Barham 9766a08dfSBen Barham #include "clang/Basic/FileManager.h" 10766a08dfSBen Barham #include "clang/Frontend/CompilerInstance.h" 11766a08dfSBen Barham #include "clang/Frontend/CompilerInvocation.h" 12766a08dfSBen Barham #include "clang/Frontend/FrontendActions.h" 13499d0b96SSam McCall #include "clang/Frontend/Utils.h" 14766a08dfSBen Barham #include "clang/Lex/HeaderSearch.h" 15766a08dfSBen Barham #include "llvm/ADT/SmallString.h" 16766a08dfSBen Barham #include "llvm/Support/FileSystem.h" 17476e7c49SKadir Cetinkaya #include "llvm/Support/VirtualFileSystem.h" 18766a08dfSBen Barham #include "llvm/Support/raw_ostream.h" 19766a08dfSBen Barham 20766a08dfSBen Barham #include "gtest/gtest.h" 21766a08dfSBen Barham 22766a08dfSBen Barham using namespace llvm; 23766a08dfSBen Barham using namespace clang; 24766a08dfSBen Barham 25766a08dfSBen Barham namespace { 26766a08dfSBen Barham 27766a08dfSBen Barham class ModuleCacheTest : public ::testing::Test { 28766a08dfSBen Barham void SetUp() override { 29766a08dfSBen Barham ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir)); 30766a08dfSBen Barham 31766a08dfSBen Barham ModuleCachePath = SmallString<256>(TestDir); 32766a08dfSBen Barham sys::path::append(ModuleCachePath, "mcp"); 33766a08dfSBen Barham ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath)); 34766a08dfSBen Barham } 35766a08dfSBen Barham 36766a08dfSBen Barham void TearDown() override { sys::fs::remove_directories(TestDir); } 37766a08dfSBen Barham 38766a08dfSBen Barham public: 39766a08dfSBen Barham SmallString<256> TestDir; 40766a08dfSBen Barham SmallString<256> ModuleCachePath; 41766a08dfSBen Barham 42766a08dfSBen Barham void addFile(StringRef Path, StringRef Contents) { 43766a08dfSBen Barham ASSERT_FALSE(sys::path::is_absolute(Path)); 44766a08dfSBen Barham 45766a08dfSBen Barham SmallString<256> AbsPath(TestDir); 46766a08dfSBen Barham sys::path::append(AbsPath, Path); 47766a08dfSBen Barham 48766a08dfSBen Barham std::error_code EC; 49766a08dfSBen Barham ASSERT_FALSE( 50766a08dfSBen Barham sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); 51766a08dfSBen Barham llvm::raw_fd_ostream OS(AbsPath, EC); 52766a08dfSBen Barham ASSERT_FALSE(EC); 53766a08dfSBen Barham OS << Contents; 54766a08dfSBen Barham } 55766a08dfSBen Barham 56766a08dfSBen Barham void addDuplicateFrameworks() { 57766a08dfSBen Barham addFile("test.m", R"cpp( 58766a08dfSBen Barham @import Top; 59766a08dfSBen Barham )cpp"); 60766a08dfSBen Barham 61766a08dfSBen Barham addFile("frameworks/Top.framework/Headers/top.h", R"cpp( 62766a08dfSBen Barham @import M; 63766a08dfSBen Barham )cpp"); 64766a08dfSBen Barham addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp( 65766a08dfSBen Barham framework module Top [system] { 66766a08dfSBen Barham header "top.h" 67766a08dfSBen Barham export * 68766a08dfSBen Barham } 69766a08dfSBen Barham )cpp"); 70766a08dfSBen Barham 71766a08dfSBen Barham addFile("frameworks/M.framework/Headers/m.h", R"cpp( 72766a08dfSBen Barham void foo(); 73766a08dfSBen Barham )cpp"); 74766a08dfSBen Barham addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp( 75766a08dfSBen Barham framework module M [system] { 76766a08dfSBen Barham header "m.h" 77766a08dfSBen Barham export * 78766a08dfSBen Barham } 79766a08dfSBen Barham )cpp"); 80766a08dfSBen Barham 81766a08dfSBen Barham addFile("frameworks2/M.framework/Headers/m.h", R"cpp( 82766a08dfSBen Barham void foo(); 83766a08dfSBen Barham )cpp"); 84766a08dfSBen Barham addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp( 85766a08dfSBen Barham framework module M [system] { 86766a08dfSBen Barham header "m.h" 87766a08dfSBen Barham export * 88766a08dfSBen Barham } 89766a08dfSBen Barham )cpp"); 90766a08dfSBen Barham } 91b2ebd8b8SKrasimir Georgiev 92b2ebd8b8SKrasimir Georgiev std::unique_ptr<CompilerInvocation> 93b2ebd8b8SKrasimir Georgiev createInvocationAndEnableFree(ArrayRef<const char *> Args, 94b2ebd8b8SKrasimir Georgiev CreateInvocationOptions Opts) { 95b2ebd8b8SKrasimir Georgiev std::unique_ptr<CompilerInvocation> Invocation = 96b2ebd8b8SKrasimir Georgiev createInvocation(Args, Opts); 97b2ebd8b8SKrasimir Georgiev if (Invocation) 98b2ebd8b8SKrasimir Georgiev Invocation->getFrontendOpts().DisableFree = false; 99b2ebd8b8SKrasimir Georgiev 100b2ebd8b8SKrasimir Georgiev return Invocation; 101b2ebd8b8SKrasimir Georgiev } 102766a08dfSBen Barham }; 103766a08dfSBen Barham 104766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPath) { 105766a08dfSBen Barham addDuplicateFrameworks(); 106766a08dfSBen Barham 107766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path="); 108766a08dfSBen Barham MCPArg.append(ModuleCachePath); 109a1153cd6SSylvestre Ledru CreateInvocationOptions CIOpts; 110a1153cd6SSylvestre Ledru CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); 111*df9a14d7SKadir Cetinkaya IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 112*df9a14d7SKadir Cetinkaya CompilerInstance::createDiagnostics(*CIOpts.VFS, new DiagnosticOptions()); 113*df9a14d7SKadir Cetinkaya CIOpts.Diags = Diags; 114766a08dfSBen Barham 115766a08dfSBen Barham // First run should pass with no errors 116766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks", 117766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(), 118766a08dfSBen Barham "test.m"}; 119766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation = 120b2ebd8b8SKrasimir Georgiev createInvocationAndEnableFree(Args, CIOpts); 121766a08dfSBen Barham ASSERT_TRUE(Invocation); 122766a08dfSBen Barham CompilerInstance Instance; 123766a08dfSBen Barham Instance.setDiagnostics(Diags.get()); 124766a08dfSBen Barham Instance.setInvocation(Invocation); 125766a08dfSBen Barham SyntaxOnlyAction Action; 126766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action)); 127766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred()); 128766a08dfSBen Barham 129766a08dfSBen Barham // Now add `frameworks2` to the search path. `Top.pcm` will have a reference 130766a08dfSBen Barham // to the `M` from `frameworks`, but a search will find the `M` from 131766a08dfSBen Barham // `frameworks2` - causing a mismatch and it to be considered out of date. 132766a08dfSBen Barham // 133766a08dfSBen Barham // Normally this would be fine - `M` and the modules it depends on would be 134766a08dfSBen Barham // rebuilt. However, since we have a shared module cache and thus an already 135766a08dfSBen Barham // finalized `Top`, recompiling `Top` will cause the existing module to be 136766a08dfSBen Barham // removed from the cache, causing possible crashed if it is ever used. 137766a08dfSBen Barham // 138766a08dfSBen Barham // Make sure that an error occurs instead. 139766a08dfSBen Barham const char *Args2[] = {"clang", "-fmodules", "-Fframeworks2", 140766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory", 141766a08dfSBen Barham TestDir.c_str(), "test.m"}; 142766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 = 143b2ebd8b8SKrasimir Georgiev createInvocationAndEnableFree(Args2, CIOpts); 144766a08dfSBen Barham ASSERT_TRUE(Invocation2); 145766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(), 146766a08dfSBen Barham &Instance.getModuleCache()); 147766a08dfSBen Barham Instance2.setDiagnostics(Diags.get()); 148766a08dfSBen Barham Instance2.setInvocation(Invocation2); 149766a08dfSBen Barham SyntaxOnlyAction Action2; 150766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2)); 151766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred()); 152766a08dfSBen Barham } 153766a08dfSBen Barham 154766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) { 155766a08dfSBen Barham addDuplicateFrameworks(); 156766a08dfSBen Barham 157766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path="); 158766a08dfSBen Barham MCPArg.append(ModuleCachePath); 159a1153cd6SSylvestre Ledru CreateInvocationOptions CIOpts; 160a1153cd6SSylvestre Ledru CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); 161*df9a14d7SKadir Cetinkaya IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 162*df9a14d7SKadir Cetinkaya CompilerInstance::createDiagnostics(*CIOpts.VFS, new DiagnosticOptions()); 163*df9a14d7SKadir Cetinkaya CIOpts.Diags = Diags; 164766a08dfSBen Barham 165766a08dfSBen Barham // First run should pass with no errors 166766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks", 167766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(), 168766a08dfSBen Barham "test.m"}; 169766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation = 170b2ebd8b8SKrasimir Georgiev createInvocationAndEnableFree(Args, CIOpts); 171766a08dfSBen Barham ASSERT_TRUE(Invocation); 172766a08dfSBen Barham CompilerInstance Instance; 173766a08dfSBen Barham Instance.setDiagnostics(Diags.get()); 174766a08dfSBen Barham Instance.setInvocation(Invocation); 175766a08dfSBen Barham SyntaxOnlyAction Action; 176766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action)); 177766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred()); 178766a08dfSBen Barham 179766a08dfSBen Barham // Same as `CachedModuleNewPath` but while allowing errors. This is a hard 180766a08dfSBen Barham // failure where the module wasn't created, so it should still fail. 181766a08dfSBen Barham const char *Args2[] = { 182766a08dfSBen Barham "clang", "-fmodules", "-Fframeworks2", 183766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory", 184766a08dfSBen Barham TestDir.c_str(), "-Xclang", "-fallow-pcm-with-compiler-errors", 185766a08dfSBen Barham "test.m"}; 186766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 = 187b2ebd8b8SKrasimir Georgiev createInvocationAndEnableFree(Args2, CIOpts); 188766a08dfSBen Barham ASSERT_TRUE(Invocation2); 189766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(), 190766a08dfSBen Barham &Instance.getModuleCache()); 191766a08dfSBen Barham Instance2.setDiagnostics(Diags.get()); 192766a08dfSBen Barham Instance2.setInvocation(Invocation2); 193766a08dfSBen Barham SyntaxOnlyAction Action2; 194766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2)); 195766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred()); 196766a08dfSBen Barham } 197766a08dfSBen Barham 198766a08dfSBen Barham } // anonymous namespace 199