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