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
VerifyClangPath(const llvm::Twine & clang_path)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 ///
DefaultComputeClangResourceDirectory(FileSpec & lldb_shlib_spec,FileSpec & file_spec,bool verify)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 CLANG_INSTALL_LIBDIR_BASENAME "/clang/" CLANG_VERSION_MAJOR_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_INSTALL_LIBDIR_BASENAME.
62 LLDB_INSTALL_LIBDIR_BASENAME "/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.SetDirectory(clang_dir);
76 FileSystem::Instance().Resolve(file_spec);
77 return true;
78 }
79 }
80
81 return false;
82 }
83
ComputeClangResourceDirectory(FileSpec & lldb_shlib_spec,FileSpec & file_spec,bool verify)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.SetDirectory(clang_path);
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.SetDirectory(clang_path);
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.SetDirectory(raw_path);
149 FileSystem::Instance().Resolve(file_spec);
150 return true;
151 #endif // __APPLE__
152 }
153
GetClangResourceDir()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