1 | //===-- CppModuleConfigurationTest.cpp ------------------------------------===// |
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 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Plugins/ExpressionParser/Clang/CppModuleConfiguration.h" |
10 | #include "Plugins/ExpressionParser/Clang/ClangHost.h" |
11 | #include "TestingSupport/SubsystemRAII.h" |
12 | #include "lldb/Host/FileSystem.h" |
13 | #include "lldb/Host/HostInfo.h" |
14 | |
15 | #include "gmock/gmock.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | using namespace lldb_private; |
19 | |
20 | namespace { |
21 | struct CppModuleConfigurationTest : public testing::Test { |
22 | llvm::MemoryBufferRef m_empty_buffer; |
23 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> m_fs; |
24 | |
25 | CppModuleConfigurationTest() |
26 | : m_empty_buffer("" , "<empty buffer>" ), |
27 | m_fs(new llvm::vfs::InMemoryFileSystem()) {} |
28 | |
29 | void SetUp() override { |
30 | FileSystem::Initialize(t&: m_fs); |
31 | HostInfo::Initialize(); |
32 | } |
33 | |
34 | void TearDown() override { |
35 | HostInfo::Terminate(); |
36 | FileSystem::Terminate(); |
37 | } |
38 | |
39 | /// Utility function turning a list of paths into a FileSpecList. |
40 | FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) { |
41 | FileSpecList result; |
42 | for (const std::string &path : paths) { |
43 | result.Append(file: FileSpec(path, FileSpec::Style::posix)); |
44 | if (!m_fs->addFileNoOwn(Path: path, ModificationTime: static_cast<time_t>(0), Buffer: m_empty_buffer)) |
45 | llvm_unreachable("Invalid test configuration?" ); |
46 | } |
47 | return result; |
48 | } |
49 | }; |
50 | } // namespace |
51 | |
52 | /// Returns the Clang resource include directory. |
53 | static std::string ResourceInc() { |
54 | llvm::SmallString<256> resource_dir; |
55 | llvm::sys::path::append(path&: resource_dir, a: GetClangResourceDir().GetPath(), |
56 | b: "include" ); |
57 | return std::string(resource_dir); |
58 | } |
59 | |
60 | TEST_F(CppModuleConfigurationTest, Linux) { |
61 | // Test the average Linux configuration. |
62 | |
63 | std::string usr = "/usr/include" ; |
64 | std::string libcpp = "/usr/include/c++/v1" ; |
65 | std::vector<std::string> files = {// C library |
66 | usr + "/stdio.h" , |
67 | // C++ library |
68 | libcpp + "/vector" , |
69 | libcpp + "/module.modulemap" }; |
70 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
71 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
72 | EXPECT_THAT(config.GetIncludeDirs(), |
73 | testing::ElementsAre(libcpp, ResourceInc(), usr)); |
74 | } |
75 | |
76 | TEST_F(CppModuleConfigurationTest, LinuxTargetSpecificInclude) { |
77 | // Test the average Linux configuration. |
78 | |
79 | std::string usr = "/usr/include" ; |
80 | std::string usr_target = "/usr/include/x86_64-linux-gnu" ; |
81 | std::string libcpp = "/usr/include/c++/v1" ; |
82 | std::string libcpp_target = "/usr/include/x86_64-unknown-linux-gnu/c++/v1" ; |
83 | std::vector<std::string> files = { |
84 | // C library |
85 | usr + "/stdio.h" , usr_target + "/sys/cdefs.h" , |
86 | // C++ library |
87 | libcpp + "/vector" , libcpp + "/module.modulemap" }; |
88 | CppModuleConfiguration config(makeFiles(paths: files), |
89 | llvm::Triple("x86_64-unknown-linux-gnu" )); |
90 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
91 | EXPECT_THAT(config.GetIncludeDirs(), |
92 | testing::ElementsAre(libcpp, ResourceInc(), usr, usr_target, |
93 | libcpp_target)); |
94 | } |
95 | |
96 | TEST_F(CppModuleConfigurationTest, Sysroot) { |
97 | // Test that having a sysroot for the whole system works fine. |
98 | |
99 | std::string libcpp = "/home/user/sysroot/usr/include/c++/v1" ; |
100 | std::string usr = "/home/user/sysroot/usr/include" ; |
101 | std::vector<std::string> files = {// C library |
102 | usr + "/stdio.h" , |
103 | // C++ library |
104 | libcpp + "/vector" , |
105 | libcpp + "/module.modulemap" }; |
106 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
107 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
108 | EXPECT_THAT(config.GetIncludeDirs(), |
109 | testing::ElementsAre(libcpp, ResourceInc(), usr)); |
110 | } |
111 | |
112 | TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) { |
113 | // Test that a locally build libc++ is detected. |
114 | |
115 | std::string usr = "/usr/include" ; |
116 | std::string libcpp = "/home/user/llvm-build/include/c++/v1" ; |
117 | std::vector<std::string> files = {// C library |
118 | usr + "/stdio.h" , |
119 | // C++ library |
120 | libcpp + "/vector" , |
121 | libcpp + "/module.modulemap" }; |
122 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
123 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
124 | EXPECT_THAT(config.GetIncludeDirs(), |
125 | testing::ElementsAre(libcpp, ResourceInc(), usr)); |
126 | } |
127 | |
128 | TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) { |
129 | // Test that having an unrelated library in /usr/include doesn't break. |
130 | |
131 | std::string usr = "/usr/include" ; |
132 | std::string libcpp = "/home/user/llvm-build/include/c++/v1" ; |
133 | std::vector<std::string> files = {// C library |
134 | usr + "/stdio.h" , |
135 | // unrelated library |
136 | usr + "/boost/vector" , |
137 | // C++ library |
138 | libcpp + "/vector" , |
139 | libcpp + "/module.modulemap" }; |
140 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
141 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
142 | EXPECT_THAT(config.GetIncludeDirs(), |
143 | testing::ElementsAre(libcpp, ResourceInc(), usr)); |
144 | } |
145 | |
146 | TEST_F(CppModuleConfigurationTest, UnrelatedLibraryWithTargetSpecificInclude) { |
147 | // Test that having an unrelated library in /usr/include doesn't break. |
148 | |
149 | std::string usr = "/usr/include" ; |
150 | std::string libcpp = "/home/user/llvm-build/include/c++/v1" ; |
151 | std::string libcpp_target = |
152 | "/home/user/llvm-build/include/x86_64-unknown-linux-gnu/c++/v1" ; |
153 | std::vector<std::string> files = {// C library |
154 | usr + "/stdio.h" , |
155 | // unrelated library |
156 | usr + "/boost/vector" , |
157 | // C++ library |
158 | libcpp + "/vector" , |
159 | libcpp + "/module.modulemap" }; |
160 | CppModuleConfiguration config(makeFiles(paths: files), |
161 | llvm::Triple("x86_64-unknown-linux-gnu" )); |
162 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
163 | EXPECT_THAT(config.GetIncludeDirs(), |
164 | testing::ElementsAre(libcpp, ResourceInc(), usr, libcpp_target)); |
165 | } |
166 | |
167 | TEST_F(CppModuleConfigurationTest, Xcode) { |
168 | // Test detection of libc++ coming from Xcode with generic platform names. |
169 | |
170 | std::string p = "/Applications/Xcode.app/Contents/Developer/" ; |
171 | std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1" ; |
172 | std::string usr = |
173 | p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include" ; |
174 | std::vector<std::string> files = { |
175 | // C library |
176 | usr + "/stdio.h" , |
177 | // C++ library |
178 | libcpp + "/vector" , |
179 | libcpp + "/module.modulemap" , |
180 | }; |
181 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
182 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
183 | EXPECT_THAT(config.GetIncludeDirs(), |
184 | testing::ElementsAre(libcpp, ResourceInc(), usr)); |
185 | } |
186 | |
187 | TEST_F(CppModuleConfigurationTest, LibCppV2) { |
188 | // Test that a "v2" of libc++ is still correctly detected. |
189 | |
190 | std::string libcpp = "/usr/include/c++/v2" ; |
191 | std::vector<std::string> files = {// C library |
192 | "/usr/include/stdio.h" , |
193 | // C++ library |
194 | libcpp + "/vector" , |
195 | libcpp + "/module.modulemap" }; |
196 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
197 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
198 | EXPECT_THAT(config.GetIncludeDirs(), |
199 | testing::ElementsAre("/usr/include/c++/v2" , ResourceInc(), |
200 | "/usr/include" )); |
201 | } |
202 | |
203 | TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) { |
204 | // Test that having some unknown file in the libc++ path doesn't break |
205 | // anything. |
206 | |
207 | std::string libcpp = "/usr/include/c++/v1" ; |
208 | std::vector<std::string> files = {// C library |
209 | "/usr/include/stdio.h" , |
210 | // C++ library |
211 | libcpp + "/non_existing_file" , |
212 | libcpp + "/module.modulemap" , |
213 | libcpp + "/vector" }; |
214 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
215 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std" )); |
216 | EXPECT_THAT(config.GetIncludeDirs(), |
217 | testing::ElementsAre("/usr/include/c++/v1" , ResourceInc(), |
218 | "/usr/include" )); |
219 | } |
220 | |
221 | TEST_F(CppModuleConfigurationTest, MissingUsrInclude) { |
222 | // Test that we don't load 'std' if we can't find the C standard library. |
223 | |
224 | std::string libcpp = "/usr/include/c++/v1" ; |
225 | std::vector<std::string> files = {// C++ library |
226 | libcpp + "/vector" , |
227 | libcpp + "/module.modulemap" }; |
228 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
229 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); |
230 | EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); |
231 | } |
232 | |
233 | TEST_F(CppModuleConfigurationTest, MissingLibCpp) { |
234 | // Test that we don't load 'std' if we don't have a libc++. |
235 | |
236 | std::string usr = "/usr/include" ; |
237 | std::vector<std::string> files = { |
238 | // C library |
239 | usr + "/stdio.h" , |
240 | }; |
241 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
242 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); |
243 | EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); |
244 | } |
245 | |
246 | TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) { |
247 | // Test that we don't do anything bad when we encounter libstdc++ paths. |
248 | |
249 | std::string usr = "/usr/include" ; |
250 | std::vector<std::string> files = { |
251 | // C library |
252 | usr + "/stdio.h" , |
253 | // C++ library |
254 | usr + "/c++/8.0.1/vector" , |
255 | }; |
256 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
257 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); |
258 | EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); |
259 | } |
260 | |
261 | TEST_F(CppModuleConfigurationTest, AmbiguousCLib) { |
262 | // Test that we don't do anything when we are not sure where the |
263 | // right C standard library is. |
264 | |
265 | std::string usr1 = "/usr/include" ; |
266 | std::string usr2 = "/usr/include/other/path" ; |
267 | std::string libcpp = usr1 + "c++/v1" ; |
268 | std::vector<std::string> files = { |
269 | // First C library |
270 | usr1 + "/stdio.h" , |
271 | // Second C library |
272 | usr2 + "/stdio.h" , |
273 | // C++ library |
274 | libcpp + "/vector" , |
275 | libcpp + "/module.modulemap" , |
276 | }; |
277 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
278 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); |
279 | EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); |
280 | } |
281 | |
282 | TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) { |
283 | // Test that we don't do anything when we are not sure where the |
284 | // right libc++ is. |
285 | |
286 | std::string usr = "/usr/include" ; |
287 | std::string libcpp1 = usr + "c++/v1" ; |
288 | std::string libcpp2 = usr + "c++/v2" ; |
289 | std::vector<std::string> files = { |
290 | // C library |
291 | usr + "/stdio.h" , |
292 | // First C++ library |
293 | libcpp1 + "/vector" , |
294 | libcpp1 + "/module.modulemap" , |
295 | // Second C++ library |
296 | libcpp2 + "/vector" , |
297 | libcpp2 + "/module.modulemap" , |
298 | }; |
299 | CppModuleConfiguration config(makeFiles(paths: files), llvm::Triple()); |
300 | EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); |
301 | EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); |
302 | } |
303 | |