172 lines · cpp
1//===-- ClangHost.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ClangHost.h"10 11#include "clang/Basic/Version.h"12#include "clang/Config/config.h"13#include "clang/Options/OptionUtils.h"14 15#include "llvm/ADT/StringRef.h"16#include "llvm/ADT/Twine.h"17#include "llvm/Support/FileSystem.h"18#include "llvm/Support/Threading.h"19 20#include "lldb/Host/Config.h"21#include "lldb/Host/FileSystem.h"22#include "lldb/Host/HostInfo.h"23#include "lldb/Utility/FileSpec.h"24#include "lldb/Utility/LLDBLog.h"25#include "lldb/Utility/Log.h"26 27#include <string>28 29using namespace lldb_private;30 31static bool VerifyClangPath(const llvm::Twine &clang_path) {32 if (FileSystem::Instance().IsDirectory(clang_path))33 return true;34 Log *log = GetLog(LLDBLog::Host);35 LLDB_LOGF(log,36 "VerifyClangPath(): "37 "failed to stat clang resource directory at \"%s\"",38 clang_path.str().c_str());39 return false;40}41 42///43/// This will compute the clang resource directory assuming that clang was44/// installed with the same prefix as lldb.45///46/// If verify is true, the first candidate resource directory will be returned.47/// This mode is only used for testing.48///49static bool DefaultComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,50 FileSpec &file_spec,51 bool verify) {52 Log *log = GetLog(LLDBLog::Host);53 std::string raw_path = lldb_shlib_spec.GetPath();54 llvm::StringRef parent_dir = llvm::sys::path::parent_path(raw_path);55 static const std::string clang_resource_path =56 clang::GetResourcesPath("bin/lldb");57 58 static const llvm::StringRef kResourceDirSuffixes[] = {59 // LLVM.org's build of LLDB uses the clang resource directory placed60 // in $install_dir/lib{,64}/clang/$clang_version or61 // $install_dir/bin/$CLANG_RESOURCE_DIR62 clang_resource_path,63 // swift-lldb uses the clang resource directory copied from swift, which64 // by default is placed in $install_dir/lib{,64}/lldb/clang. LLDB places65 // it there, so we use LLDB_INSTALL_LIBDIR_BASENAME.66 LLDB_INSTALL_LIBDIR_BASENAME "/lldb/clang",67 };68 69 for (const auto &Suffix : kResourceDirSuffixes) {70 llvm::SmallString<256> clang_dir(parent_dir);71 llvm::SmallString<32> relative_path(Suffix);72 llvm::sys::path::native(relative_path);73 llvm::sys::path::append(clang_dir, relative_path);74 if (!verify || VerifyClangPath(clang_dir)) {75 LLDB_LOG(log,76 "DefaultComputeClangResourceDir: Setting ClangResourceDir "77 "to \"{0}\", verify = {1}",78 clang_dir.str(), verify ? "true" : "false");79 file_spec.SetDirectory(clang_dir);80 FileSystem::Instance().Resolve(file_spec);81 return true;82 }83 }84 85 return false;86}87 88bool lldb_private::ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,89 FileSpec &file_spec,90 bool verify) {91#if !defined(__APPLE__)92 return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,93 verify);94#else95 std::string raw_path = lldb_shlib_spec.GetPath();96 97 auto rev_it = llvm::sys::path::rbegin(raw_path);98 auto r_end = llvm::sys::path::rend(raw_path);99 100 // Check for a Posix-style build of LLDB.101 while (rev_it != r_end) {102 if (*rev_it == "LLDB.framework")103 break;104 ++rev_it;105 }106 107 // We found a non-framework build of LLDB108 if (rev_it == r_end)109 return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,110 verify);111 112 // Inside Xcode and in Xcode toolchains LLDB is always in lockstep113 // with the Swift compiler, so it can reuse its Clang resource114 // directory. This allows LLDB and the Swift compiler to share the115 // same Clang module cache.116 llvm::SmallString<256> clang_path;117 const char *swift_clang_resource_dir = "usr/lib/swift/clang";118 auto parent = std::next(rev_it);119 if (parent != r_end && *parent == "SharedFrameworks") {120 // This is the top-level LLDB in the Xcode.app bundle.121 // E.g., "Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A"122 raw_path.resize(parent - r_end);123 llvm::sys::path::append(clang_path, raw_path,124 "Developer/Toolchains/XcodeDefault.xctoolchain",125 swift_clang_resource_dir);126 if (!verify || VerifyClangPath(clang_path)) {127 file_spec.SetDirectory(clang_path);128 FileSystem::Instance().Resolve(file_spec);129 return true;130 }131 } else if (parent != r_end && *parent == "PrivateFrameworks" &&132 std::distance(parent, r_end) > 2) {133 ++parent;134 ++parent;135 if (*parent == "System") {136 // This is LLDB inside an Xcode toolchain.137 // E.g., "Xcode.app/Contents/Developer/Toolchains/" \138 // "My.xctoolchain/System/Library/PrivateFrameworks/LLDB.framework"139 raw_path.resize(parent - r_end);140 llvm::sys::path::append(clang_path, raw_path, swift_clang_resource_dir);141 if (!verify || VerifyClangPath(clang_path)) {142 file_spec.SetDirectory(clang_path);143 FileSystem::Instance().Resolve(file_spec);144 return true;145 }146 }147 }148 149 // Fall back to the Clang resource directory inside the framework.150 raw_path = lldb_shlib_spec.GetPath();151 raw_path.resize(rev_it - r_end);152 raw_path.append("LLDB.framework/Resources/Clang");153 file_spec.SetDirectory(raw_path);154 FileSystem::Instance().Resolve(file_spec);155 return true;156#endif // __APPLE__157}158 159FileSpec lldb_private::GetClangResourceDir() {160 static FileSpec g_cached_resource_dir;161 static llvm::once_flag g_once_flag;162 llvm::call_once(g_once_flag, []() {163 if (FileSpec lldb_file_spec = HostInfo::GetShlibDir())164 ComputeClangResourceDirectory(lldb_file_spec, g_cached_resource_dir,165 true);166 Log *log = GetLog(LLDBLog::Host);167 LLDB_LOGF(log, "GetClangResourceDir() => '%s'",168 g_cached_resource_dir.GetPath().c_str());169 });170 return g_cached_resource_dir;171}172