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 62 TEST_F(CppModuleConfigurationTest, Linux) { 63 // Test the average Linux configuration. 64 65 std::string usr = "/usr/include"; 66 std::string libcpp = "/usr/include/c++/v1"; 67 std::vector<std::string> files = {// C library 68 usr + "/stdio.h", 69 // C++ library 70 libcpp + "/vector", 71 libcpp + "/module.modulemap"}; 72 CppModuleConfiguration config(makeFiles(files)); 73 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 74 EXPECT_THAT(config.GetIncludeDirs(), 75 testing::ElementsAre(libcpp, ResourceInc(), usr)); 76 } 77 78 TEST_F(CppModuleConfigurationTest, Sysroot) { 79 // Test that having a sysroot for the whole system works fine. 80 81 std::string libcpp = "/home/user/sysroot/usr/include/c++/v1"; 82 std::string usr = "/home/user/sysroot/usr/include"; 83 std::vector<std::string> files = {// C library 84 usr + "/stdio.h", 85 // C++ library 86 libcpp + "/vector", 87 libcpp + "/module.modulemap"}; 88 CppModuleConfiguration config(makeFiles(files)); 89 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 90 EXPECT_THAT(config.GetIncludeDirs(), 91 testing::ElementsAre(libcpp, ResourceInc(), usr)); 92 } 93 94 TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) { 95 // Test that a locally build libc++ is detected. 96 97 std::string usr = "/usr/include"; 98 std::string libcpp = "/home/user/llvm-build/include/c++/v1"; 99 std::vector<std::string> files = {// C library 100 usr + "/stdio.h", 101 // C++ library 102 libcpp + "/vector", 103 libcpp + "/module.modulemap"}; 104 CppModuleConfiguration config(makeFiles(files)); 105 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 106 EXPECT_THAT(config.GetIncludeDirs(), 107 testing::ElementsAre(libcpp, ResourceInc(), usr)); 108 } 109 110 TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) { 111 // Test that having an unrelated library in /usr/include doesn't break. 112 113 std::string usr = "/usr/include"; 114 std::string libcpp = "/home/user/llvm-build/include/c++/v1"; 115 std::vector<std::string> files = {// C library 116 usr + "/stdio.h", 117 // unrelated library 118 usr + "/boost/vector", 119 // C++ library 120 libcpp + "/vector", 121 libcpp + "/module.modulemap"}; 122 CppModuleConfiguration config(makeFiles(files)); 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, Xcode) { 129 // Test detection of libc++ coming from Xcode with generic platform names. 130 131 std::string p = "/Applications/Xcode.app/Contents/Developer/"; 132 std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1"; 133 std::string usr = 134 p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include"; 135 std::vector<std::string> files = { 136 // C library 137 usr + "/stdio.h", 138 // C++ library 139 libcpp + "/vector", 140 libcpp + "/module.modulemap", 141 }; 142 CppModuleConfiguration config(makeFiles(files)); 143 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 144 EXPECT_THAT(config.GetIncludeDirs(), 145 testing::ElementsAre(libcpp, ResourceInc(), usr)); 146 } 147 148 TEST_F(CppModuleConfigurationTest, LibCppV2) { 149 // Test that a "v2" of libc++ is still correctly detected. 150 151 std::string libcpp = "/usr/include/c++/v2"; 152 std::vector<std::string> files = {// C library 153 "/usr/include/stdio.h", 154 // C++ library 155 libcpp + "/vector", 156 libcpp + "/module.modulemap"}; 157 CppModuleConfiguration config(makeFiles(files)); 158 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 159 EXPECT_THAT(config.GetIncludeDirs(), 160 testing::ElementsAre("/usr/include/c++/v2", ResourceInc(), 161 "/usr/include")); 162 } 163 164 TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) { 165 // Test that having some unknown file in the libc++ path doesn't break 166 // anything. 167 168 std::string libcpp = "/usr/include/c++/v1"; 169 std::vector<std::string> files = {// C library 170 "/usr/include/stdio.h", 171 // C++ library 172 libcpp + "/non_existing_file", 173 libcpp + "/module.modulemap", 174 libcpp + "/vector"}; 175 CppModuleConfiguration config(makeFiles(files)); 176 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 177 EXPECT_THAT(config.GetIncludeDirs(), 178 testing::ElementsAre("/usr/include/c++/v1", ResourceInc(), 179 "/usr/include")); 180 } 181 182 TEST_F(CppModuleConfigurationTest, MissingUsrInclude) { 183 // Test that we don't load 'std' if we can't find the C standard library. 184 185 std::string libcpp = "/usr/include/c++/v1"; 186 std::vector<std::string> files = {// C++ library 187 libcpp + "/vector", 188 libcpp + "/module.modulemap"}; 189 CppModuleConfiguration config(makeFiles(files)); 190 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 191 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 192 } 193 194 TEST_F(CppModuleConfigurationTest, MissingLibCpp) { 195 // Test that we don't load 'std' if we don't have a libc++. 196 197 std::string usr = "/usr/include"; 198 std::vector<std::string> files = { 199 // C library 200 usr + "/stdio.h", 201 }; 202 CppModuleConfiguration config(makeFiles(files)); 203 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 204 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 205 } 206 207 TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) { 208 // Test that we don't do anything bad when we encounter libstdc++ paths. 209 210 std::string usr = "/usr/include"; 211 std::vector<std::string> files = { 212 // C library 213 usr + "/stdio.h", 214 // C++ library 215 usr + "/c++/8.0.1/vector", 216 }; 217 CppModuleConfiguration config(makeFiles(files)); 218 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 219 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 220 } 221 222 TEST_F(CppModuleConfigurationTest, AmbiguousCLib) { 223 // Test that we don't do anything when we are not sure where the 224 // right C standard library is. 225 226 std::string usr1 = "/usr/include"; 227 std::string usr2 = "/usr/include/other/path"; 228 std::string libcpp = usr1 + "c++/v1"; 229 std::vector<std::string> files = { 230 // First C library 231 usr1 + "/stdio.h", 232 // Second C library 233 usr2 + "/stdio.h", 234 // C++ library 235 libcpp + "/vector", 236 libcpp + "/module.modulemap", 237 }; 238 CppModuleConfiguration config(makeFiles(files)); 239 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 240 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 241 } 242 243 TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) { 244 // Test that we don't do anything when we are not sure where the 245 // right libc++ is. 246 247 std::string usr = "/usr/include"; 248 std::string libcpp1 = usr + "c++/v1"; 249 std::string libcpp2 = usr + "c++/v2"; 250 std::vector<std::string> files = { 251 // C library 252 usr + "/stdio.h", 253 // First C++ library 254 libcpp1 + "/vector", 255 libcpp1 + "/module.modulemap", 256 // Second C++ library 257 libcpp2 + "/vector", 258 libcpp2 + "/module.modulemap", 259 }; 260 CppModuleConfiguration config(makeFiles(files)); 261 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 262 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 263 } 264