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(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(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?"); 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(resource_dir, GetClangResourceDir().GetPath(), 56 "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(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(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(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(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(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(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(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(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(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(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(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(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(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(files), llvm::Triple()); 300 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 301 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 302 } 303