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 #include "clang/Driver/Driver.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 
29 using namespace lldb_private;
30 
31 static 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 was
44 /// 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 ///
49 static 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::driver::Driver::GetResourcesPath("bin/lldb", CLANG_RESOURCE_DIR);
57 
58   static const llvm::StringRef kResourceDirSuffixes[] = {
59       // LLVM.org's build of LLDB uses the clang resource directory placed
60       // in $install_dir/lib{,64}/clang/$clang_version or
61       // $install_dir/bin/$CLANG_RESOURCE_DIR
62       clang_resource_path,
63       // swift-lldb uses the clang resource directory copied from swift, which
64       // by default is placed in $install_dir/lib{,64}/lldb/clang. LLDB places
65       // 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 
88 bool 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 #else
95   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 LLDB
108   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 lockstep
113   // with the Swift compiler, so it can reuse its Clang resource
114   // directory. This allows LLDB and the Swift compiler to share the
115   // 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 
159 FileSpec 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