xref: /llvm-project/clang/unittests/Serialization/ModuleCacheTest.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
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