1*766a08dfSBen Barham //===- unittests/Serialization/ModuleCacheTest.cpp - CI tests -------------===// 2*766a08dfSBen Barham // 3*766a08dfSBen Barham // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*766a08dfSBen Barham // See https://llvm.org/LICENSE.txt for license information. 5*766a08dfSBen Barham // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*766a08dfSBen Barham // 7*766a08dfSBen Barham //===----------------------------------------------------------------------===// 8*766a08dfSBen Barham 9*766a08dfSBen Barham #include "clang/Basic/FileManager.h" 10*766a08dfSBen Barham #include "clang/Frontend/CompilerInstance.h" 11*766a08dfSBen Barham #include "clang/Frontend/CompilerInvocation.h" 12*766a08dfSBen Barham #include "clang/Frontend/FrontendActions.h" 13*766a08dfSBen Barham #include "clang/Lex/HeaderSearch.h" 14*766a08dfSBen Barham #include "llvm/ADT/SmallString.h" 15*766a08dfSBen Barham #include "llvm/Support/FileSystem.h" 16*766a08dfSBen Barham #include "llvm/Support/raw_ostream.h" 17*766a08dfSBen Barham 18*766a08dfSBen Barham #include "gtest/gtest.h" 19*766a08dfSBen Barham 20*766a08dfSBen Barham using namespace llvm; 21*766a08dfSBen Barham using namespace clang; 22*766a08dfSBen Barham 23*766a08dfSBen Barham namespace { 24*766a08dfSBen Barham 25*766a08dfSBen Barham class ModuleCacheTest : public ::testing::Test { 26*766a08dfSBen Barham void SetUp() override { 27*766a08dfSBen Barham ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir)); 28*766a08dfSBen Barham 29*766a08dfSBen Barham ModuleCachePath = SmallString<256>(TestDir); 30*766a08dfSBen Barham sys::path::append(ModuleCachePath, "mcp"); 31*766a08dfSBen Barham ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath)); 32*766a08dfSBen Barham } 33*766a08dfSBen Barham 34*766a08dfSBen Barham void TearDown() override { sys::fs::remove_directories(TestDir); } 35*766a08dfSBen Barham 36*766a08dfSBen Barham public: 37*766a08dfSBen Barham SmallString<256> TestDir; 38*766a08dfSBen Barham SmallString<256> ModuleCachePath; 39*766a08dfSBen Barham 40*766a08dfSBen Barham void addFile(StringRef Path, StringRef Contents) { 41*766a08dfSBen Barham ASSERT_FALSE(sys::path::is_absolute(Path)); 42*766a08dfSBen Barham 43*766a08dfSBen Barham SmallString<256> AbsPath(TestDir); 44*766a08dfSBen Barham sys::path::append(AbsPath, Path); 45*766a08dfSBen Barham 46*766a08dfSBen Barham std::error_code EC; 47*766a08dfSBen Barham ASSERT_FALSE( 48*766a08dfSBen Barham sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); 49*766a08dfSBen Barham llvm::raw_fd_ostream OS(AbsPath, EC); 50*766a08dfSBen Barham ASSERT_FALSE(EC); 51*766a08dfSBen Barham OS << Contents; 52*766a08dfSBen Barham } 53*766a08dfSBen Barham 54*766a08dfSBen Barham void addDuplicateFrameworks() { 55*766a08dfSBen Barham addFile("test.m", R"cpp( 56*766a08dfSBen Barham @import Top; 57*766a08dfSBen Barham )cpp"); 58*766a08dfSBen Barham 59*766a08dfSBen Barham addFile("frameworks/Top.framework/Headers/top.h", R"cpp( 60*766a08dfSBen Barham @import M; 61*766a08dfSBen Barham )cpp"); 62*766a08dfSBen Barham addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp( 63*766a08dfSBen Barham framework module Top [system] { 64*766a08dfSBen Barham header "top.h" 65*766a08dfSBen Barham export * 66*766a08dfSBen Barham } 67*766a08dfSBen Barham )cpp"); 68*766a08dfSBen Barham 69*766a08dfSBen Barham addFile("frameworks/M.framework/Headers/m.h", R"cpp( 70*766a08dfSBen Barham void foo(); 71*766a08dfSBen Barham )cpp"); 72*766a08dfSBen Barham addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp( 73*766a08dfSBen Barham framework module M [system] { 74*766a08dfSBen Barham header "m.h" 75*766a08dfSBen Barham export * 76*766a08dfSBen Barham } 77*766a08dfSBen Barham )cpp"); 78*766a08dfSBen Barham 79*766a08dfSBen Barham addFile("frameworks2/M.framework/Headers/m.h", R"cpp( 80*766a08dfSBen Barham void foo(); 81*766a08dfSBen Barham )cpp"); 82*766a08dfSBen Barham addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp( 83*766a08dfSBen Barham framework module M [system] { 84*766a08dfSBen Barham header "m.h" 85*766a08dfSBen Barham export * 86*766a08dfSBen Barham } 87*766a08dfSBen Barham )cpp"); 88*766a08dfSBen Barham } 89*766a08dfSBen Barham }; 90*766a08dfSBen Barham 91*766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPath) { 92*766a08dfSBen Barham addDuplicateFrameworks(); 93*766a08dfSBen Barham 94*766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path="); 95*766a08dfSBen Barham MCPArg.append(ModuleCachePath); 96*766a08dfSBen Barham IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 97*766a08dfSBen Barham CompilerInstance::createDiagnostics(new DiagnosticOptions()); 98*766a08dfSBen Barham 99*766a08dfSBen Barham // First run should pass with no errors 100*766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks", 101*766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(), 102*766a08dfSBen Barham "test.m"}; 103*766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation = 104*766a08dfSBen Barham createInvocationFromCommandLine(Args, Diags); 105*766a08dfSBen Barham ASSERT_TRUE(Invocation); 106*766a08dfSBen Barham CompilerInstance Instance; 107*766a08dfSBen Barham Instance.setDiagnostics(Diags.get()); 108*766a08dfSBen Barham Instance.setInvocation(Invocation); 109*766a08dfSBen Barham SyntaxOnlyAction Action; 110*766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action)); 111*766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred()); 112*766a08dfSBen Barham 113*766a08dfSBen Barham // Now add `frameworks2` to the search path. `Top.pcm` will have a reference 114*766a08dfSBen Barham // to the `M` from `frameworks`, but a search will find the `M` from 115*766a08dfSBen Barham // `frameworks2` - causing a mismatch and it to be considered out of date. 116*766a08dfSBen Barham // 117*766a08dfSBen Barham // Normally this would be fine - `M` and the modules it depends on would be 118*766a08dfSBen Barham // rebuilt. However, since we have a shared module cache and thus an already 119*766a08dfSBen Barham // finalized `Top`, recompiling `Top` will cause the existing module to be 120*766a08dfSBen Barham // removed from the cache, causing possible crashed if it is ever used. 121*766a08dfSBen Barham // 122*766a08dfSBen Barham // Make sure that an error occurs instead. 123*766a08dfSBen Barham const char *Args2[] = {"clang", "-fmodules", "-Fframeworks2", 124*766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory", 125*766a08dfSBen Barham TestDir.c_str(), "test.m"}; 126*766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 = 127*766a08dfSBen Barham createInvocationFromCommandLine(Args2, Diags); 128*766a08dfSBen Barham ASSERT_TRUE(Invocation2); 129*766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(), 130*766a08dfSBen Barham &Instance.getModuleCache()); 131*766a08dfSBen Barham Instance2.setDiagnostics(Diags.get()); 132*766a08dfSBen Barham Instance2.setInvocation(Invocation2); 133*766a08dfSBen Barham SyntaxOnlyAction Action2; 134*766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2)); 135*766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred()); 136*766a08dfSBen Barham } 137*766a08dfSBen Barham 138*766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) { 139*766a08dfSBen Barham addDuplicateFrameworks(); 140*766a08dfSBen Barham 141*766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path="); 142*766a08dfSBen Barham MCPArg.append(ModuleCachePath); 143*766a08dfSBen Barham IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 144*766a08dfSBen Barham CompilerInstance::createDiagnostics(new DiagnosticOptions()); 145*766a08dfSBen Barham 146*766a08dfSBen Barham // First run should pass with no errors 147*766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks", 148*766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(), 149*766a08dfSBen Barham "test.m"}; 150*766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation = 151*766a08dfSBen Barham createInvocationFromCommandLine(Args, Diags); 152*766a08dfSBen Barham ASSERT_TRUE(Invocation); 153*766a08dfSBen Barham CompilerInstance Instance; 154*766a08dfSBen Barham Instance.setDiagnostics(Diags.get()); 155*766a08dfSBen Barham Instance.setInvocation(Invocation); 156*766a08dfSBen Barham SyntaxOnlyAction Action; 157*766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action)); 158*766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred()); 159*766a08dfSBen Barham 160*766a08dfSBen Barham // Same as `CachedModuleNewPath` but while allowing errors. This is a hard 161*766a08dfSBen Barham // failure where the module wasn't created, so it should still fail. 162*766a08dfSBen Barham const char *Args2[] = { 163*766a08dfSBen Barham "clang", "-fmodules", "-Fframeworks2", 164*766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory", 165*766a08dfSBen Barham TestDir.c_str(), "-Xclang", "-fallow-pcm-with-compiler-errors", 166*766a08dfSBen Barham "test.m"}; 167*766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 = 168*766a08dfSBen Barham createInvocationFromCommandLine(Args2, Diags); 169*766a08dfSBen Barham ASSERT_TRUE(Invocation2); 170*766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(), 171*766a08dfSBen Barham &Instance.getModuleCache()); 172*766a08dfSBen Barham Instance2.setDiagnostics(Diags.get()); 173*766a08dfSBen Barham Instance2.setInvocation(Invocation2); 174*766a08dfSBen Barham SyntaxOnlyAction Action2; 175*766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2)); 176*766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred()); 177*766a08dfSBen Barham } 178*766a08dfSBen Barham 179*766a08dfSBen Barham } // anonymous namespace 180