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