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