Index: include/lldb/Host/macosx/HostInfoMacOSX.h =================================================================== --- include/lldb/Host/macosx/HostInfoMacOSX.h +++ include/lldb/Host/macosx/HostInfoMacOSX.h @@ -38,6 +38,8 @@ static bool ComputeHeaderDirectory(FileSpec &file_spec); static bool ComputePythonDirectory(FileSpec &file_spec); static bool ComputeClangDirectory(FileSpec &file_spec); + static bool ComputeClangDirectory(FileSpec &lldb_shlib_spec, + FileSpec &file_spec, bool verify); static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); static bool ComputeUserPluginsDirectory(FileSpec &file_spec); }; Index: source/Host/macosx/HostInfoMacOSX.mm =================================================================== --- source/Host/macosx/HostInfoMacOSX.mm +++ source/Host/macosx/HostInfoMacOSX.mm @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" // C++ Includes @@ -226,21 +227,67 @@ #endif } +static bool VerifyClangPath(const llvm::Twine &clang_path) { + if (llvm::sys::fs::is_directory(clang_path)) + return true; + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + if (log) + log->Printf("HostInfoMacOSX::ComputeClangDirectory(): " + "failed to stat clang resource directory at \"%s\"", + clang_path.str().c_str()); + return false; +} + bool HostInfoMacOSX::ComputeClangDirectory(FileSpec &file_spec) { FileSpec lldb_file_spec; if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) return false; + return ComputeClangDirectory(lldb_file_spec, file_spec, true); +} - std::string raw_path = lldb_file_spec.GetPath(); +bool HostInfoMacOSX::ComputeClangDirectory(FileSpec &lldb_shlib_spec, + FileSpec &file_spec, bool verify) { + std::string raw_path = lldb_shlib_spec.GetPath(); - size_t framework_pos = raw_path.find("LLDB.framework"); - if (framework_pos == std::string::npos) + auto rev_it = llvm::sys::path::rbegin(raw_path); + auto r_end = llvm::sys::path::rend(raw_path); + + // Check for a Posix-style build of LLDB. + if (rev_it == r_end || *rev_it != "LLDB.framework") return HostInfoPosix::ComputeClangDirectory(file_spec); - - framework_pos += strlen("LLDB.framework"); - raw_path.resize(framework_pos); + + // Inside Xcode and in Xcode toolchains LLDB is always in lockstep + // with the Swift compiler, so it can reuse its Clang resource + // directory. This allows LLDB and the Swift compiler to share the + // same Clang module cache. + llvm::SmallString<256> clang_path; + const char *swift_clang_resource_dir = "usr/lib/swift/clang"; + ++rev_it; + if (rev_it != r_end && *rev_it == "SharedFrameworks") { + // This is the top-level LLDB in the Xcode.app bundle. + raw_path.resize(rev_it - r_end); + llvm::sys::path::append(clang_path, raw_path, + "Developer/Toolchains/XcodeDefault.xctoolchain", + swift_clang_resource_dir); + if (!verify || VerifyClangPath(clang_path)) { + file_spec.SetFile(clang_path.c_str(), true); + return true; + } + } else if (rev_it != r_end && *rev_it == "PrivateFrameworks" && + ++rev_it != r_end && ++rev_it != r_end) { + // This is LLDB inside an Xcode toolchain. + raw_path.resize(rev_it - r_end); + llvm::sys::path::append(clang_path, raw_path, swift_clang_resource_dir); + if (!verify || VerifyClangPath(clang_path)) { + file_spec.SetFile(clang_path.c_str(), true); + return true; + } + } + + // Fall back to the Clang resource directory inside the framework. + raw_path.resize(rev_it - r_end); raw_path.append("/Resources/Clang"); - + file_spec.SetFile(raw_path.c_str(), true); return true; } Index: unittests/Host/CMakeLists.txt =================================================================== --- unittests/Host/CMakeLists.txt +++ unittests/Host/CMakeLists.txt @@ -21,4 +21,5 @@ LINK_LIBS lldbCore lldbHost + lldbUtilityHelpers ) Index: unittests/Host/HostInfoTest.cpp =================================================================== --- unittests/Host/HostInfoTest.cpp +++ unittests/Host/HostInfoTest.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" #include "lldb/lldb-defines.h" +#include "TestingSupport/TestUtilities.h" #include "gtest/gtest.h" using namespace lldb_private; @@ -43,3 +45,41 @@ EXPECT_EQ(HostInfo::GetAugmentedArchSpec(LLDB_ARCH_DEFAULT).GetTriple(), HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple()); } + + +struct HostInfoMacOSXTest : public HostInfoMacOSX { + static std::string ComputeClangDir(std::string lldb_shlib_path, + bool verify = false) { + FileSpec clang_dir; + FileSpec lldb_shlib_spec(lldb_shlib_path, false); + ComputeClangDirectory(lldb_shlib_spec, clang_dir, verify); + return clang_dir.GetPath(); + } +}; + + +TEST_F(HostInfoTest, MacOSX) { + // This returns whatever the POSIX fallback returns. + std::string posix = "/usr/lib/liblldb.dylib"; + EXPECT_FALSE(HostInfoMacOSXTest::ComputeClangDir(posix).empty()); + + std::string xcode = + "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework"; + std::string xcode_clang = + "/Applications/Xcode.app/Contents/Developer/Toolchains/" + "XcodeDefault.xctoolchain/usr/lib/swift/clang"; + EXPECT_EQ(HostInfoMacOSXTest::ComputeClangDir(xcode), xcode_clang); + + std::string toolchain = + "/Applications/Xcode.app/Contents/Developer/Toolchains/" + "Swift-4.1-development-snapshot.xctoolchain/System/Library/" + "PrivateFrameworks/LLDB.framework"; + std::string toolchain_clang = + "/Applications/Xcode.app/Contents/Developer/Toolchains/" + "Swift-4.1-development-snapshot.xctoolchain/usr/lib/swift/clang"; + EXPECT_EQ(HostInfoMacOSXTest::ComputeClangDir(toolchain), toolchain_clang); + + // Test that a bogus path is detected. + EXPECT_NE(HostInfoMacOSXTest::ComputeClangDir(GetInputFilePath(xcode), true), + HostInfoMacOSXTest::ComputeClangDir(GetInputFilePath(xcode))); +}