1 //===-- CppModuleConfigurationTest.cpp ------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
18 using namespace lldb_private
;
21 struct CppModuleConfigurationTest
: public testing::Test
{
22 llvm::MemoryBufferRef m_empty_buffer
;
23 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> m_fs
;
25 CppModuleConfigurationTest()
26 : m_empty_buffer("", "<empty buffer>"),
27 m_fs(new llvm::vfs::InMemoryFileSystem()) {}
29 void SetUp() override
{
30 FileSystem::Initialize(m_fs
);
31 HostInfo::Initialize();
34 void TearDown() override
{
35 HostInfo::Terminate();
36 FileSystem::Terminate();
39 /// Utility function turning a list of paths into a FileSpecList.
40 FileSpecList
makeFiles(llvm::ArrayRef
<std::string
> paths
) {
42 for (const std::string
&path
: paths
) {
43 result
.Append(FileSpec(path
, FileSpec::Style::posix
));
44 if (!m_fs
->addFileNoOwn(path
, static_cast<time_t>(0), m_empty_buffer
))
45 llvm_unreachable("Invalid test configuration?");
52 /// Returns the Clang resource include directory.
53 static std::string
ResourceInc() {
54 llvm::SmallString
<256> resource_dir
;
55 llvm::sys::path::append(resource_dir
, GetClangResourceDir().GetPath(),
57 return std::string(resource_dir
);
60 TEST_F(CppModuleConfigurationTest
, Linux
) {
61 // Test the average Linux configuration.
63 std::string usr
= "/usr/include";
64 std::string libcpp
= "/usr/include/c++/v1";
65 std::vector
<std::string
> files
= {// C library
69 libcpp
+ "/module.modulemap"};
70 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
71 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
72 EXPECT_THAT(config
.GetIncludeDirs(),
73 testing::ElementsAre(libcpp
, ResourceInc(), usr
));
76 TEST_F(CppModuleConfigurationTest
, LinuxTargetSpecificInclude
) {
77 // Test the average Linux configuration.
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
= {
85 usr
+ "/stdio.h", usr_target
+ "/sys/cdefs.h",
87 libcpp
+ "/vector", libcpp
+ "/module.modulemap"};
88 CppModuleConfiguration
config(makeFiles(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
,
96 TEST_F(CppModuleConfigurationTest
, Sysroot
) {
97 // Test that having a sysroot for the whole system works fine.
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
105 libcpp
+ "/module.modulemap"};
106 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
107 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
108 EXPECT_THAT(config
.GetIncludeDirs(),
109 testing::ElementsAre(libcpp
, ResourceInc(), usr
));
112 TEST_F(CppModuleConfigurationTest
, LinuxLocalLibCpp
) {
113 // Test that a locally build libc++ is detected.
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
121 libcpp
+ "/module.modulemap"};
122 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
123 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
124 EXPECT_THAT(config
.GetIncludeDirs(),
125 testing::ElementsAre(libcpp
, ResourceInc(), usr
));
128 TEST_F(CppModuleConfigurationTest
, UnrelatedLibrary
) {
129 // Test that having an unrelated library in /usr/include doesn't break.
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
136 usr
+ "/boost/vector",
139 libcpp
+ "/module.modulemap"};
140 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
141 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
142 EXPECT_THAT(config
.GetIncludeDirs(),
143 testing::ElementsAre(libcpp
, ResourceInc(), usr
));
146 TEST_F(CppModuleConfigurationTest
, UnrelatedLibraryWithTargetSpecificInclude
) {
147 // Test that having an unrelated library in /usr/include doesn't break.
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
156 usr
+ "/boost/vector",
159 libcpp
+ "/module.modulemap"};
160 CppModuleConfiguration
config(makeFiles(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
));
167 TEST_F(CppModuleConfigurationTest
, Xcode
) {
168 // Test detection of libc++ coming from Xcode with generic platform names.
170 std::string p
= "/Applications/Xcode.app/Contents/Developer/";
171 std::string libcpp
= p
+ "Toolchains/B.xctoolchain/usr/include/c++/v1";
173 p
+ "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
174 std::vector
<std::string
> files
= {
179 libcpp
+ "/module.modulemap",
181 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
182 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
183 EXPECT_THAT(config
.GetIncludeDirs(),
184 testing::ElementsAre(libcpp
, ResourceInc(), usr
));
187 TEST_F(CppModuleConfigurationTest
, LibCppV2
) {
188 // Test that a "v2" of libc++ is still correctly detected.
190 std::string libcpp
= "/usr/include/c++/v2";
191 std::vector
<std::string
> files
= {// C library
192 "/usr/include/stdio.h",
195 libcpp
+ "/module.modulemap"};
196 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
197 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
198 EXPECT_THAT(config
.GetIncludeDirs(),
199 testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
203 TEST_F(CppModuleConfigurationTest
, UnknownLibCppFile
) {
204 // Test that having some unknown file in the libc++ path doesn't break
207 std::string libcpp
= "/usr/include/c++/v1";
208 std::vector
<std::string
> files
= {// C library
209 "/usr/include/stdio.h",
211 libcpp
+ "/non_existing_file",
212 libcpp
+ "/module.modulemap",
214 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
215 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre("std"));
216 EXPECT_THAT(config
.GetIncludeDirs(),
217 testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
221 TEST_F(CppModuleConfigurationTest
, MissingUsrInclude
) {
222 // Test that we don't load 'std' if we can't find the C standard library.
224 std::string libcpp
= "/usr/include/c++/v1";
225 std::vector
<std::string
> files
= {// C++ library
227 libcpp
+ "/module.modulemap"};
228 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
229 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre());
230 EXPECT_THAT(config
.GetIncludeDirs(), testing::ElementsAre());
233 TEST_F(CppModuleConfigurationTest
, MissingLibCpp
) {
234 // Test that we don't load 'std' if we don't have a libc++.
236 std::string usr
= "/usr/include";
237 std::vector
<std::string
> files
= {
241 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
242 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre());
243 EXPECT_THAT(config
.GetIncludeDirs(), testing::ElementsAre());
246 TEST_F(CppModuleConfigurationTest
, IgnoreLibStdCpp
) {
247 // Test that we don't do anything bad when we encounter libstdc++ paths.
249 std::string usr
= "/usr/include";
250 std::vector
<std::string
> files
= {
254 usr
+ "/c++/8.0.1/vector",
256 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
257 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre());
258 EXPECT_THAT(config
.GetIncludeDirs(), testing::ElementsAre());
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.
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
= {
275 libcpp
+ "/module.modulemap",
277 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
278 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre());
279 EXPECT_THAT(config
.GetIncludeDirs(), testing::ElementsAre());
282 TEST_F(CppModuleConfigurationTest
, AmbiguousLibCpp
) {
283 // Test that we don't do anything when we are not sure where the
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
= {
294 libcpp1
+ "/module.modulemap",
295 // Second C++ library
297 libcpp2
+ "/module.modulemap",
299 CppModuleConfiguration
config(makeFiles(files
), llvm::Triple());
300 EXPECT_THAT(config
.GetImportedModules(), testing::ElementsAre());
301 EXPECT_THAT(config
.GetIncludeDirs(), testing::ElementsAre());