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"
13*499d0b96SSam 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"
17766a08dfSBen Barham #include "llvm/Support/raw_ostream.h"
18766a08dfSBen Barham
19766a08dfSBen Barham #include "gtest/gtest.h"
20766a08dfSBen Barham
21766a08dfSBen Barham using namespace llvm;
22766a08dfSBen Barham using namespace clang;
23766a08dfSBen Barham
24766a08dfSBen Barham namespace {
25766a08dfSBen Barham
26766a08dfSBen Barham class ModuleCacheTest : public ::testing::Test {
SetUp()27766a08dfSBen Barham void SetUp() override {
28766a08dfSBen Barham ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir));
29766a08dfSBen Barham
30766a08dfSBen Barham ModuleCachePath = SmallString<256>(TestDir);
31766a08dfSBen Barham sys::path::append(ModuleCachePath, "mcp");
32766a08dfSBen Barham ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath));
33766a08dfSBen Barham }
34766a08dfSBen Barham
TearDown()35766a08dfSBen Barham void TearDown() override { sys::fs::remove_directories(TestDir); }
36766a08dfSBen Barham
37766a08dfSBen Barham public:
38766a08dfSBen Barham SmallString<256> TestDir;
39766a08dfSBen Barham SmallString<256> ModuleCachePath;
40766a08dfSBen Barham
addFile(StringRef Path,StringRef Contents)41766a08dfSBen Barham void addFile(StringRef Path, StringRef Contents) {
42766a08dfSBen Barham ASSERT_FALSE(sys::path::is_absolute(Path));
43766a08dfSBen Barham
44766a08dfSBen Barham SmallString<256> AbsPath(TestDir);
45766a08dfSBen Barham sys::path::append(AbsPath, Path);
46766a08dfSBen Barham
47766a08dfSBen Barham std::error_code EC;
48766a08dfSBen Barham ASSERT_FALSE(
49766a08dfSBen Barham sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
50766a08dfSBen Barham llvm::raw_fd_ostream OS(AbsPath, EC);
51766a08dfSBen Barham ASSERT_FALSE(EC);
52766a08dfSBen Barham OS << Contents;
53766a08dfSBen Barham }
54766a08dfSBen Barham
addDuplicateFrameworks()55766a08dfSBen Barham void addDuplicateFrameworks() {
56766a08dfSBen Barham addFile("test.m", R"cpp(
57766a08dfSBen Barham @import Top;
58766a08dfSBen Barham )cpp");
59766a08dfSBen Barham
60766a08dfSBen Barham addFile("frameworks/Top.framework/Headers/top.h", R"cpp(
61766a08dfSBen Barham @import M;
62766a08dfSBen Barham )cpp");
63766a08dfSBen Barham addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp(
64766a08dfSBen Barham framework module Top [system] {
65766a08dfSBen Barham header "top.h"
66766a08dfSBen Barham export *
67766a08dfSBen Barham }
68766a08dfSBen Barham )cpp");
69766a08dfSBen Barham
70766a08dfSBen Barham addFile("frameworks/M.framework/Headers/m.h", R"cpp(
71766a08dfSBen Barham void foo();
72766a08dfSBen Barham )cpp");
73766a08dfSBen Barham addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp(
74766a08dfSBen Barham framework module M [system] {
75766a08dfSBen Barham header "m.h"
76766a08dfSBen Barham export *
77766a08dfSBen Barham }
78766a08dfSBen Barham )cpp");
79766a08dfSBen Barham
80766a08dfSBen Barham addFile("frameworks2/M.framework/Headers/m.h", R"cpp(
81766a08dfSBen Barham void foo();
82766a08dfSBen Barham )cpp");
83766a08dfSBen Barham addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp(
84766a08dfSBen Barham framework module M [system] {
85766a08dfSBen Barham header "m.h"
86766a08dfSBen Barham export *
87766a08dfSBen Barham }
88766a08dfSBen Barham )cpp");
89766a08dfSBen Barham }
90766a08dfSBen Barham };
91766a08dfSBen Barham
TEST_F(ModuleCacheTest,CachedModuleNewPath)92766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPath) {
93766a08dfSBen Barham addDuplicateFrameworks();
94766a08dfSBen Barham
95766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path=");
96766a08dfSBen Barham MCPArg.append(ModuleCachePath);
97766a08dfSBen Barham IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
98766a08dfSBen Barham CompilerInstance::createDiagnostics(new DiagnosticOptions());
99*499d0b96SSam McCall CreateInvocationOptions CIOpts;
100*499d0b96SSam McCall CIOpts.Diags = Diags;
101766a08dfSBen Barham
102766a08dfSBen Barham // First run should pass with no errors
103766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
104766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(),
105766a08dfSBen Barham "test.m"};
106766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation =
107*499d0b96SSam McCall createInvocation(Args, CIOpts);
108766a08dfSBen Barham ASSERT_TRUE(Invocation);
109766a08dfSBen Barham CompilerInstance Instance;
110766a08dfSBen Barham Instance.setDiagnostics(Diags.get());
111766a08dfSBen Barham Instance.setInvocation(Invocation);
112766a08dfSBen Barham SyntaxOnlyAction Action;
113766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action));
114766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred());
115766a08dfSBen Barham
116766a08dfSBen Barham // Now add `frameworks2` to the search path. `Top.pcm` will have a reference
117766a08dfSBen Barham // to the `M` from `frameworks`, but a search will find the `M` from
118766a08dfSBen Barham // `frameworks2` - causing a mismatch and it to be considered out of date.
119766a08dfSBen Barham //
120766a08dfSBen Barham // Normally this would be fine - `M` and the modules it depends on would be
121766a08dfSBen Barham // rebuilt. However, since we have a shared module cache and thus an already
122766a08dfSBen Barham // finalized `Top`, recompiling `Top` will cause the existing module to be
123766a08dfSBen Barham // removed from the cache, causing possible crashed if it is ever used.
124766a08dfSBen Barham //
125766a08dfSBen Barham // Make sure that an error occurs instead.
126766a08dfSBen Barham const char *Args2[] = {"clang", "-fmodules", "-Fframeworks2",
127766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory",
128766a08dfSBen Barham TestDir.c_str(), "test.m"};
129766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 =
130*499d0b96SSam McCall createInvocation(Args2, CIOpts);
131766a08dfSBen Barham ASSERT_TRUE(Invocation2);
132766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(),
133766a08dfSBen Barham &Instance.getModuleCache());
134766a08dfSBen Barham Instance2.setDiagnostics(Diags.get());
135766a08dfSBen Barham Instance2.setInvocation(Invocation2);
136766a08dfSBen Barham SyntaxOnlyAction Action2;
137766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2));
138766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred());
139766a08dfSBen Barham }
140766a08dfSBen Barham
TEST_F(ModuleCacheTest,CachedModuleNewPathAllowErrors)141766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) {
142766a08dfSBen Barham addDuplicateFrameworks();
143766a08dfSBen Barham
144766a08dfSBen Barham SmallString<256> MCPArg("-fmodules-cache-path=");
145766a08dfSBen Barham MCPArg.append(ModuleCachePath);
146766a08dfSBen Barham IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
147766a08dfSBen Barham CompilerInstance::createDiagnostics(new DiagnosticOptions());
148*499d0b96SSam McCall CreateInvocationOptions CIOpts;
149*499d0b96SSam McCall CIOpts.Diags = Diags;
150766a08dfSBen Barham
151766a08dfSBen Barham // First run should pass with no errors
152766a08dfSBen Barham const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
153766a08dfSBen Barham MCPArg.c_str(), "-working-directory", TestDir.c_str(),
154766a08dfSBen Barham "test.m"};
155766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation =
156*499d0b96SSam McCall createInvocation(Args, CIOpts);
157766a08dfSBen Barham ASSERT_TRUE(Invocation);
158766a08dfSBen Barham CompilerInstance Instance;
159766a08dfSBen Barham Instance.setDiagnostics(Diags.get());
160766a08dfSBen Barham Instance.setInvocation(Invocation);
161766a08dfSBen Barham SyntaxOnlyAction Action;
162766a08dfSBen Barham ASSERT_TRUE(Instance.ExecuteAction(Action));
163766a08dfSBen Barham ASSERT_FALSE(Diags->hasErrorOccurred());
164766a08dfSBen Barham
165766a08dfSBen Barham // Same as `CachedModuleNewPath` but while allowing errors. This is a hard
166766a08dfSBen Barham // failure where the module wasn't created, so it should still fail.
167766a08dfSBen Barham const char *Args2[] = {
168766a08dfSBen Barham "clang", "-fmodules", "-Fframeworks2",
169766a08dfSBen Barham "-Fframeworks", MCPArg.c_str(), "-working-directory",
170766a08dfSBen Barham TestDir.c_str(), "-Xclang", "-fallow-pcm-with-compiler-errors",
171766a08dfSBen Barham "test.m"};
172766a08dfSBen Barham std::shared_ptr<CompilerInvocation> Invocation2 =
173*499d0b96SSam McCall createInvocation(Args2, CIOpts);
174766a08dfSBen Barham ASSERT_TRUE(Invocation2);
175766a08dfSBen Barham CompilerInstance Instance2(Instance.getPCHContainerOperations(),
176766a08dfSBen Barham &Instance.getModuleCache());
177766a08dfSBen Barham Instance2.setDiagnostics(Diags.get());
178766a08dfSBen Barham Instance2.setInvocation(Invocation2);
179766a08dfSBen Barham SyntaxOnlyAction Action2;
180766a08dfSBen Barham ASSERT_FALSE(Instance2.ExecuteAction(Action2));
181766a08dfSBen Barham ASSERT_TRUE(Diags->hasErrorOccurred());
182766a08dfSBen Barham }
183766a08dfSBen Barham
184766a08dfSBen Barham } // anonymous namespace
185