1 //===-- XcodeSDK.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 "lldb/Utility/XcodeSDK.h"
10 #include "lldb/Utility/FileSpec.h"
11
12 #include "lldb/lldb-types.h"
13
14 #include "llvm/TargetParser/Triple.h"
15
16 #include <string>
17
18 using namespace lldb;
19 using namespace lldb_private;
20
GetName(XcodeSDK::Type type)21 static llvm::StringRef GetName(XcodeSDK::Type type) {
22 switch (type) {
23 case XcodeSDK::MacOSX:
24 return "MacOSX";
25 case XcodeSDK::iPhoneSimulator:
26 return "iPhoneSimulator";
27 case XcodeSDK::iPhoneOS:
28 return "iPhoneOS";
29 case XcodeSDK::AppleTVSimulator:
30 return "AppleTVSimulator";
31 case XcodeSDK::AppleTVOS:
32 return "AppleTVOS";
33 case XcodeSDK::WatchSimulator:
34 return "WatchSimulator";
35 case XcodeSDK::watchOS:
36 return "WatchOS";
37 case XcodeSDK::XRSimulator:
38 return "XRSimulator";
39 case XcodeSDK::XROS:
40 return "XROS";
41 case XcodeSDK::bridgeOS:
42 return "bridgeOS";
43 case XcodeSDK::Linux:
44 return "Linux";
45 case XcodeSDK::unknown:
46 return {};
47 }
48 llvm_unreachable("Unhandled sdk type!");
49 }
50
XcodeSDK(XcodeSDK::Info info)51 XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) {
52 if (!m_name.empty()) {
53 if (!info.version.empty())
54 m_name += info.version.getAsString();
55 if (info.internal)
56 m_name += ".Internal";
57 m_name += ".sdk";
58 }
59 }
60
61 XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) = default;
62
operator ==(const XcodeSDK & other) const63 bool XcodeSDK::operator==(const XcodeSDK &other) const {
64 return m_name == other.m_name;
65 }
66
ParseSDKName(llvm::StringRef & name)67 static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) {
68 if (name.consume_front("MacOSX"))
69 return XcodeSDK::MacOSX;
70 if (name.consume_front("iPhoneSimulator"))
71 return XcodeSDK::iPhoneSimulator;
72 if (name.consume_front("iPhoneOS"))
73 return XcodeSDK::iPhoneOS;
74 if (name.consume_front("AppleTVSimulator"))
75 return XcodeSDK::AppleTVSimulator;
76 if (name.consume_front("AppleTVOS"))
77 return XcodeSDK::AppleTVOS;
78 if (name.consume_front("WatchSimulator"))
79 return XcodeSDK::WatchSimulator;
80 if (name.consume_front("WatchOS"))
81 return XcodeSDK::watchOS;
82 if (name.consume_front("XRSimulator"))
83 return XcodeSDK::XRSimulator;
84 if (name.consume_front("XROS"))
85 return XcodeSDK::XROS;
86 if (name.consume_front("bridgeOS"))
87 return XcodeSDK::bridgeOS;
88 if (name.consume_front("Linux"))
89 return XcodeSDK::Linux;
90 static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1,
91 "New SDK type was added, update this list!");
92 return XcodeSDK::unknown;
93 }
94
ParseSDKVersion(llvm::StringRef & name)95 static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) {
96 unsigned i = 0;
97 while (i < name.size() && name[i] >= '0' && name[i] <= '9')
98 ++i;
99 if (i == name.size() || name[i++] != '.')
100 return {};
101 while (i < name.size() && name[i] >= '0' && name[i] <= '9')
102 ++i;
103 if (i == name.size() || name[i++] != '.')
104 return {};
105
106 llvm::VersionTuple version;
107 version.tryParse(name.slice(0, i - 1));
108 name = name.drop_front(i);
109 return version;
110 }
111
ParseAppleInternalSDK(llvm::StringRef & name)112 static bool ParseAppleInternalSDK(llvm::StringRef &name) {
113 return name.consume_front("Internal.") || name.consume_front(".Internal.");
114 }
115
Parse() const116 XcodeSDK::Info XcodeSDK::Parse() const {
117 XcodeSDK::Info info;
118 llvm::StringRef input(m_name);
119 info.type = ParseSDKName(input);
120 info.version = ParseSDKVersion(input);
121 info.internal = ParseAppleInternalSDK(input);
122 return info;
123 }
124
IsAppleInternalSDK() const125 bool XcodeSDK::IsAppleInternalSDK() const {
126 llvm::StringRef input(m_name);
127 ParseSDKName(input);
128 ParseSDKVersion(input);
129 return ParseAppleInternalSDK(input);
130 }
131
GetVersion() const132 llvm::VersionTuple XcodeSDK::GetVersion() const {
133 llvm::StringRef input(m_name);
134 ParseSDKName(input);
135 return ParseSDKVersion(input);
136 }
137
GetType() const138 XcodeSDK::Type XcodeSDK::GetType() const {
139 llvm::StringRef input(m_name);
140 return ParseSDKName(input);
141 }
142
GetString() const143 llvm::StringRef XcodeSDK::GetString() const { return m_name; }
144
GetSysroot() const145 const FileSpec &XcodeSDK::GetSysroot() const { return m_sysroot; }
146
operator <(const Info & other) const147 bool XcodeSDK::Info::operator<(const Info &other) const {
148 return std::tie(type, version, internal) <
149 std::tie(other.type, other.version, other.internal);
150 }
151
operator ==(const Info & other) const152 bool XcodeSDK::Info::operator==(const Info &other) const {
153 return std::tie(type, version, internal) ==
154 std::tie(other.type, other.version, other.internal);
155 }
156
Merge(const XcodeSDK & other)157 void XcodeSDK::Merge(const XcodeSDK &other) {
158 // The "bigger" SDK always wins.
159 auto l = Parse();
160 auto r = other.Parse();
161 if (l < r)
162 *this = other;
163 else {
164 // The Internal flag always wins.
165 if (!l.internal && r.internal) {
166 if (llvm::StringRef(m_name).ends_with(".sdk"))
167 m_name =
168 m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk");
169 }
170 }
171
172 // We changed the SDK name. Adjust the sysroot accordingly.
173 if (m_sysroot && m_sysroot.GetFilename().GetStringRef() != m_name)
174 m_sysroot.SetFilename(m_name);
175 }
176
GetCanonicalName(XcodeSDK::Info info)177 std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
178 std::string name;
179 switch (info.type) {
180 case MacOSX:
181 name = "macosx";
182 break;
183 case iPhoneSimulator:
184 name = "iphonesimulator";
185 break;
186 case iPhoneOS:
187 name = "iphoneos";
188 break;
189 case AppleTVSimulator:
190 name = "appletvsimulator";
191 break;
192 case AppleTVOS:
193 name = "appletvos";
194 break;
195 case WatchSimulator:
196 name = "watchsimulator";
197 break;
198 case watchOS:
199 name = "watchos";
200 break;
201 case XRSimulator:
202 name = "xrsimulator";
203 break;
204 case XROS:
205 name = "xros";
206 break;
207 case bridgeOS:
208 name = "bridgeos";
209 break;
210 case Linux:
211 name = "linux";
212 break;
213 case unknown:
214 return {};
215 }
216 if (!info.version.empty())
217 name += info.version.getAsString();
218 if (info.internal)
219 name += ".internal";
220 return name;
221 }
222
SDKSupportsModules(XcodeSDK::Type sdk_type,llvm::VersionTuple version)223 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type,
224 llvm::VersionTuple version) {
225 switch (sdk_type) {
226 case Type::MacOSX:
227 return version >= llvm::VersionTuple(10, 10);
228 case Type::iPhoneOS:
229 case Type::iPhoneSimulator:
230 case Type::AppleTVOS:
231 case Type::AppleTVSimulator:
232 return version >= llvm::VersionTuple(8);
233 case Type::watchOS:
234 case Type::WatchSimulator:
235 return version >= llvm::VersionTuple(6);
236 case Type::XROS:
237 case Type::XRSimulator:
238 return true;
239 default:
240 return false;
241 }
242
243 return false;
244 }
245
SupportsSwift() const246 bool XcodeSDK::SupportsSwift() const {
247 XcodeSDK::Info info = Parse();
248 switch (info.type) {
249 case Type::MacOSX:
250 return info.version.empty() || info.version >= llvm::VersionTuple(10, 10);
251 case Type::iPhoneOS:
252 case Type::iPhoneSimulator:
253 return info.version.empty() || info.version >= llvm::VersionTuple(8);
254 case Type::AppleTVSimulator:
255 case Type::AppleTVOS:
256 return info.version.empty() || info.version >= llvm::VersionTuple(9);
257 case Type::WatchSimulator:
258 case Type::watchOS:
259 return info.version.empty() || info.version >= llvm::VersionTuple(2);
260 case Type::XROS:
261 case Type::XRSimulator:
262 case Type::Linux:
263 return true;
264 default:
265 return false;
266 }
267 }
268
SDKSupportsBuiltinModules(const llvm::Triple & target_triple,llvm::VersionTuple sdk_version)269 bool XcodeSDK::SDKSupportsBuiltinModules(const llvm::Triple &target_triple,
270 llvm::VersionTuple sdk_version) {
271 using namespace llvm;
272
273 switch (target_triple.getOS()) {
274 case Triple::OSType::MacOSX:
275 return sdk_version >= VersionTuple(15U);
276 case Triple::OSType::IOS:
277 return sdk_version >= VersionTuple(18U);
278 case Triple::OSType::TvOS:
279 return sdk_version >= VersionTuple(18U);
280 case Triple::OSType::WatchOS:
281 return sdk_version >= VersionTuple(11U);
282 case Triple::OSType::XROS:
283 return sdk_version >= VersionTuple(2U);
284 default:
285 // New SDKs support builtin modules from the start.
286 return true;
287 }
288 }
289
SDKSupportsModules(XcodeSDK::Type desired_type,const FileSpec & sdk_path)290 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type,
291 const FileSpec &sdk_path) {
292 ConstString last_path_component = sdk_path.GetFilename();
293
294 if (!last_path_component)
295 return false;
296
297 XcodeSDK sdk(last_path_component.GetStringRef().str());
298 if (sdk.GetType() != desired_type)
299 return false;
300 return SDKSupportsModules(sdk.GetType(), sdk.GetVersion());
301 }
302
GetSDKTypeForTriple(const llvm::Triple & triple)303 XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) {
304 using namespace llvm;
305 switch (triple.getOS()) {
306 case Triple::MacOSX:
307 case Triple::Darwin:
308 return XcodeSDK::MacOSX;
309 case Triple::IOS:
310 switch (triple.getEnvironment()) {
311 case Triple::MacABI:
312 return XcodeSDK::MacOSX;
313 case Triple::Simulator:
314 return XcodeSDK::iPhoneSimulator;
315 default:
316 return XcodeSDK::iPhoneOS;
317 }
318 case Triple::TvOS:
319 if (triple.getEnvironment() == Triple::Simulator)
320 return XcodeSDK::AppleTVSimulator;
321 return XcodeSDK::AppleTVOS;
322 case Triple::WatchOS:
323 if (triple.getEnvironment() == Triple::Simulator)
324 return XcodeSDK::WatchSimulator;
325 return XcodeSDK::watchOS;
326 case Triple::XROS:
327 if (triple.getEnvironment() == Triple::Simulator)
328 return XcodeSDK::XRSimulator;
329 return XcodeSDK::XROS;
330 case Triple::Linux:
331 return XcodeSDK::Linux;
332 default:
333 return XcodeSDK::unknown;
334 }
335 }
336
FindXcodeContentsDirectoryInPath(llvm::StringRef path)337 std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) {
338 auto begin = llvm::sys::path::begin(path);
339 auto end = llvm::sys::path::end(path);
340
341 // Iterate over the path components until we find something that ends with
342 // .app. If the next component is Contents then we've found the Contents
343 // directory.
344 for (auto it = begin; it != end; ++it) {
345 if (it->ends_with(".app")) {
346 auto next = it;
347 if (++next != end && *next == "Contents") {
348 llvm::SmallString<128> buffer;
349 llvm::sys::path::append(buffer, begin, ++next,
350 llvm::sys::path::Style::posix);
351 return buffer.str().str();
352 }
353 }
354 }
355
356 return {};
357 }
358