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
18using namespace lldb_private;
19
20namespace {
21struct 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.
53static 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
60TEST_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
76TEST_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
96TEST_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
112TEST_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
128TEST_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
146TEST_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
167TEST_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
187TEST_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
203TEST_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
221TEST_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
233TEST_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
246TEST_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
261TEST_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
282TEST_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

source code of lldb/unittests/Expression/CppModuleConfigurationTest.cpp