--- /dev/null
+"""
+Check that missing module source files are correctly handled by LLDB.
+"""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import os
+import shutil
+
+
+class TestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ # We only emulate a fake libc++ in this test and don't use the real libc++,
+ # but we still add the libc++ category so that this test is only run in
+ # test configurations where libc++ is actually supposed to be tested.
+ @add_test_categories(["libc++"])
+ @skipIf(compiler=no_match("clang"))
+ def test(self):
+ # The path to our temporary target root that contains the temporary
+ # module sources.
+ target_sysroot = self.getBuildArtifact("root")
+
+ # Copy the sources to the root.
+ shutil.copytree(self.getSourcePath("root"), target_sysroot)
+ # Build the binary with the copied sources.
+ self.build()
+ # Delete the copied sources so that they are now unavailable.
+ shutil.rmtree(target_sysroot)
+
+ # Set the sysroot where our dummy libc++ used to exist. Just to make
+ # sure we don't find some existing headers on the system that could
+ # XPASS this test.
+ self.runCmd("platform select --sysroot '" + target_sysroot + "' host")
+
+ lldbutil.run_to_source_breakpoint(self,
+ "// Set break point at this line.",
+ lldb.SBFileSpec("main.cpp"))
+
+ # Import the std C++ module and run an expression.
+ # As we deleted the sources, LLDB should refuse the load the module
+ # and just print the normal error we get from the expression.
+ self.runCmd("settings set target.import-std-module true")
+ self.expect("expr v.unknown_identifier", error=True,
+ substrs=["no member named 'unknown_identifier'"])
+ # Check that there is no confusing error about failing to build the
+ # module.
+ self.expect("expr v.unknown_identifier", error=True, matching=False,
+ substrs=["could not build module 'std'"])
+
+ # Test the fallback mode. It should also just print the normal
+ # error but not mention a failed module build.
+ self.runCmd("settings set target.import-std-module fallback")
+
+ self.expect("expr v.unknown_identifier", error=True,
+ substrs=["no member named 'unknown_identifier'"])
+ self.expect("expr v.unknown_identifier", error=True, matching=False,
+ substrs=["could not build module 'std'"])
#include "TestingSupport/SubsystemRAII.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
struct CppModuleConfigurationTest : public testing::Test {
- SubsystemRAII<FileSystem, HostInfo> subsystems;
+ llvm::MemoryBufferRef m_empty_buffer;
+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> m_fs;
+
+ CppModuleConfigurationTest()
+ : m_empty_buffer("", "<empty buffer>"),
+ m_fs(new llvm::vfs::InMemoryFileSystem()) {}
+
+ void SetUp() override {
+ FileSystem::Initialize(m_fs);
+ HostInfo::Initialize();
+ }
+
+ void TearDown() override {
+ HostInfo::Terminate();
+ FileSystem::Terminate();
+ }
+
+ /// Utility function turning a list of paths into a FileSpecList.
+ FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
+ FileSpecList result;
+ for (const std::string &path : paths) {
+ result.Append(FileSpec(path, FileSpec::Style::posix));
+ if (!m_fs->addFileNoOwn(path, static_cast<time_t>(0), m_empty_buffer))
+ llvm_unreachable("Invalid test configuration?");
+ }
+ return result;
+ }
};
} // namespace
return std::string(resource_dir);
}
-/// Utility function turningn a list of paths into a FileSpecList.
-static FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
- FileSpecList result;
- for (const std::string &path : paths)
- result.Append(FileSpec(path, FileSpec::Style::posix));
- return result;
-}
TEST_F(CppModuleConfigurationTest, Linux) {
// Test the average Linux configuration.
- std::string libcpp = "/usr/include/c++/v1";
+
std::string usr = "/usr/include";
- CppModuleConfiguration config(
- makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+ std::string libcpp = "/usr/include/c++/v1";
+ std::vector<std::string> files = {// C library
+ usr + "/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre(libcpp, ResourceInc(), usr));
TEST_F(CppModuleConfigurationTest, Sysroot) {
// Test that having a sysroot for the whole system works fine.
+
std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";
std::string usr = "/home/user/sysroot/usr/include";
- CppModuleConfiguration config(
- makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+ std::vector<std::string> files = {// C library
+ usr + "/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre(libcpp, ResourceInc(), usr));
TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
// Test that a locally build libc++ is detected.
- std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+
std::string usr = "/usr/include";
- CppModuleConfiguration config(
- makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
+ std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+ std::vector<std::string> files = {// C library
+ usr + "/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre(libcpp, ResourceInc(), usr));
TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
// Test that having an unrelated library in /usr/include doesn't break.
- std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+
std::string usr = "/usr/include";
- CppModuleConfiguration config(makeFiles(
- {usr + "/bits/types.h", libcpp + "/vector", usr + "/boost/vector"}));
+ std::string libcpp = "/home/user/llvm-build/include/c++/v1";
+ std::vector<std::string> files = {// C library
+ usr + "/stdio.h",
+ // unrelated library
+ usr + "/boost/vector",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre(libcpp, ResourceInc(), usr));
TEST_F(CppModuleConfigurationTest, Xcode) {
// Test detection of libc++ coming from Xcode with generic platform names.
+
std::string p = "/Applications/Xcode.app/Contents/Developer/";
std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";
std::string usr =
p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
- CppModuleConfiguration config(
- makeFiles({libcpp + "/unordered_map", usr + "/stdio.h"}));
+ std::vector<std::string> files = {
+ // C library
+ usr + "/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap",
+ };
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre(libcpp, ResourceInc(), usr));
TEST_F(CppModuleConfigurationTest, LibCppV2) {
// Test that a "v2" of libc++ is still correctly detected.
- CppModuleConfiguration config(
- makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v2/vector"}));
+
+ std::string libcpp = "/usr/include/c++/v2";
+ std::vector<std::string> files = {// C library
+ "/usr/include/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
// Test that having some unknown file in the libc++ path doesn't break
// anything.
- CppModuleConfiguration config(makeFiles(
- {"/usr/include/bits/types.h", "/usr/include/c++/v1/non_existing_file"}));
+
+ std::string libcpp = "/usr/include/c++/v1";
+ std::vector<std::string> files = {// C library
+ "/usr/include/stdio.h",
+ // C++ library
+ libcpp + "/non_existing_file",
+ libcpp + "/module.modulemap",
+ libcpp + "/vector"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
EXPECT_THAT(config.GetIncludeDirs(),
testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
// Test that we don't load 'std' if we can't find the C standard library.
- CppModuleConfiguration config(makeFiles({"/usr/include/c++/v1/vector"}));
+
+ std::string libcpp = "/usr/include/c++/v1";
+ std::vector<std::string> files = {// C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap"};
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
}
TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
// Test that we don't load 'std' if we don't have a libc++.
- CppModuleConfiguration config(makeFiles({"/usr/include/bits/types.h"}));
+
+ std::string usr = "/usr/include";
+ std::vector<std::string> files = {
+ // C library
+ usr + "/stdio.h",
+ };
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
}
TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
// Test that we don't do anything bad when we encounter libstdc++ paths.
- CppModuleConfiguration config(makeFiles(
- {"/usr/include/bits/types.h", "/usr/include/c++/8.0.1/vector"}));
+
+ std::string usr = "/usr/include";
+ std::vector<std::string> files = {
+ // C library
+ usr + "/stdio.h",
+ // C++ library
+ usr + "/c++/8.0.1/vector",
+ };
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
}
TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
// Test that we don't do anything when we are not sure where the
// right C standard library is.
- CppModuleConfiguration config(
- makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
- "/sysroot/usr/include/bits/types.h"}));
+
+ std::string usr1 = "/usr/include";
+ std::string usr2 = "/usr/include/other/path";
+ std::string libcpp = usr1 + "c++/v1";
+ std::vector<std::string> files = {
+ // First C library
+ usr1 + "/stdio.h",
+ // Second C library
+ usr2 + "/stdio.h",
+ // C++ library
+ libcpp + "/vector",
+ libcpp + "/module.modulemap",
+ };
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
}
TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
// Test that we don't do anything when we are not sure where the
// right libc++ is.
- CppModuleConfiguration config(
- makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
- "/usr/include/c++/v2/vector"}));
+
+ std::string usr = "/usr/include";
+ std::string libcpp1 = usr + "c++/v1";
+ std::string libcpp2 = usr + "c++/v2";
+ std::vector<std::string> files = {
+ // C library
+ usr + "/stdio.h",
+ // First C++ library
+ libcpp1 + "/vector",
+ libcpp1 + "/module.modulemap",
+ // Second C++ library
+ libcpp2 + "/vector",
+ libcpp2 + "/module.modulemap",
+ };
+ CppModuleConfiguration config(makeFiles(files));
EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
}