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
18using namespace lldb;
19using namespace lldb_private;
20
21static 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
51XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(type: 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
61XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) = default;
62
63bool XcodeSDK::operator==(const XcodeSDK &other) const {
64 return m_name == other.m_name;
65}
66
67static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) {
68 if (name.consume_front(Prefix: "MacOSX"))
69 return XcodeSDK::MacOSX;
70 if (name.consume_front(Prefix: "iPhoneSimulator"))
71 return XcodeSDK::iPhoneSimulator;
72 if (name.consume_front(Prefix: "iPhoneOS"))
73 return XcodeSDK::iPhoneOS;
74 if (name.consume_front(Prefix: "AppleTVSimulator"))
75 return XcodeSDK::AppleTVSimulator;
76 if (name.consume_front(Prefix: "AppleTVOS"))
77 return XcodeSDK::AppleTVOS;
78 if (name.consume_front(Prefix: "WatchSimulator"))
79 return XcodeSDK::WatchSimulator;
80 if (name.consume_front(Prefix: "WatchOS"))
81 return XcodeSDK::watchOS;
82 if (name.consume_front(Prefix: "XRSimulator"))
83 return XcodeSDK::XRSimulator;
84 if (name.consume_front(Prefix: "XROS"))
85 return XcodeSDK::XROS;
86 if (name.consume_front(Prefix: "bridgeOS"))
87 return XcodeSDK::bridgeOS;
88 if (name.consume_front(Prefix: "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
95static 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(string: name.slice(Start: 0, End: i - 1));
108 name = name.drop_front(N: i);
109 return version;
110}
111
112static bool ParseAppleInternalSDK(llvm::StringRef &name) {
113 return name.consume_front(Prefix: "Internal.") || name.consume_front(Prefix: ".Internal.");
114}
115
116XcodeSDK::Info XcodeSDK::Parse() const {
117 XcodeSDK::Info info;
118 llvm::StringRef input(m_name);
119 info.type = ParseSDKName(name&: input);
120 info.version = ParseSDKVersion(name&: input);
121 info.internal = ParseAppleInternalSDK(name&: input);
122 return info;
123}
124
125bool XcodeSDK::IsAppleInternalSDK() const {
126 llvm::StringRef input(m_name);
127 ParseSDKName(name&: input);
128 ParseSDKVersion(name&: input);
129 return ParseAppleInternalSDK(name&: input);
130}
131
132llvm::VersionTuple XcodeSDK::GetVersion() const {
133 llvm::StringRef input(m_name);
134 ParseSDKName(name&: input);
135 return ParseSDKVersion(name&: input);
136}
137
138XcodeSDK::Type XcodeSDK::GetType() const {
139 llvm::StringRef input(m_name);
140 return ParseSDKName(name&: input);
141}
142
143llvm::StringRef XcodeSDK::GetString() const { return m_name; }
144
145bool XcodeSDK::Info::operator<(const Info &other) const {
146 return std::tie(args: type, args: version, args: internal) <
147 std::tie(args: other.type, args: other.version, args: other.internal);
148}
149
150bool XcodeSDK::Info::operator==(const Info &other) const {
151 return std::tie(args: type, args: version, args: internal) ==
152 std::tie(args: other.type, args: other.version, args: other.internal);
153}
154
155void XcodeSDK::Merge(const XcodeSDK &other) {
156 // The "bigger" SDK always wins.
157 auto l = Parse();
158 auto r = other.Parse();
159 if (l < r)
160 *this = other;
161 else {
162 // The Internal flag always wins.
163 if (llvm::StringRef(m_name).ends_with(Suffix: ".sdk"))
164 if (!l.internal && r.internal)
165 m_name =
166 m_name.substr(pos: 0, n: m_name.size() - 3) + std::string("Internal.sdk");
167 }
168}
169
170std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
171 std::string name;
172 switch (info.type) {
173 case MacOSX:
174 name = "macosx";
175 break;
176 case iPhoneSimulator:
177 name = "iphonesimulator";
178 break;
179 case iPhoneOS:
180 name = "iphoneos";
181 break;
182 case AppleTVSimulator:
183 name = "appletvsimulator";
184 break;
185 case AppleTVOS:
186 name = "appletvos";
187 break;
188 case WatchSimulator:
189 name = "watchsimulator";
190 break;
191 case watchOS:
192 name = "watchos";
193 break;
194 case XRSimulator:
195 name = "xrsimulator";
196 break;
197 case XROS:
198 name = "xros";
199 break;
200 case bridgeOS:
201 name = "bridgeos";
202 break;
203 case Linux:
204 name = "linux";
205 break;
206 case unknown:
207 return {};
208 }
209 if (!info.version.empty())
210 name += info.version.getAsString();
211 if (info.internal)
212 name += ".internal";
213 return name;
214}
215
216bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type,
217 llvm::VersionTuple version) {
218 switch (sdk_type) {
219 case Type::MacOSX:
220 return version >= llvm::VersionTuple(10, 10);
221 case Type::iPhoneOS:
222 case Type::iPhoneSimulator:
223 case Type::AppleTVOS:
224 case Type::AppleTVSimulator:
225 return version >= llvm::VersionTuple(8);
226 case Type::watchOS:
227 case Type::WatchSimulator:
228 return version >= llvm::VersionTuple(6);
229 case Type::XROS:
230 case Type::XRSimulator:
231 return true;
232 default:
233 return false;
234 }
235
236 return false;
237}
238
239bool XcodeSDK::SupportsSwift() const {
240 XcodeSDK::Info info = Parse();
241 switch (info.type) {
242 case Type::MacOSX:
243 return info.version.empty() || info.version >= llvm::VersionTuple(10, 10);
244 case Type::iPhoneOS:
245 case Type::iPhoneSimulator:
246 return info.version.empty() || info.version >= llvm::VersionTuple(8);
247 case Type::AppleTVSimulator:
248 case Type::AppleTVOS:
249 return info.version.empty() || info.version >= llvm::VersionTuple(9);
250 case Type::WatchSimulator:
251 case Type::watchOS:
252 return info.version.empty() || info.version >= llvm::VersionTuple(2);
253 case Type::XROS:
254 case Type::XRSimulator:
255 case Type::Linux:
256 return true;
257 default:
258 return false;
259 }
260}
261
262bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type,
263 const FileSpec &sdk_path) {
264 ConstString last_path_component = sdk_path.GetFilename();
265
266 if (!last_path_component)
267 return false;
268
269 XcodeSDK sdk(last_path_component.GetStringRef().str());
270 if (sdk.GetType() != desired_type)
271 return false;
272 return SDKSupportsModules(sdk_type: sdk.GetType(), version: sdk.GetVersion());
273}
274
275XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) {
276 using namespace llvm;
277 switch (triple.getOS()) {
278 case Triple::MacOSX:
279 case Triple::Darwin:
280 return XcodeSDK::MacOSX;
281 case Triple::IOS:
282 switch (triple.getEnvironment()) {
283 case Triple::MacABI:
284 return XcodeSDK::MacOSX;
285 case Triple::Simulator:
286 return XcodeSDK::iPhoneSimulator;
287 default:
288 return XcodeSDK::iPhoneOS;
289 }
290 case Triple::TvOS:
291 if (triple.getEnvironment() == Triple::Simulator)
292 return XcodeSDK::AppleTVSimulator;
293 return XcodeSDK::AppleTVOS;
294 case Triple::WatchOS:
295 if (triple.getEnvironment() == Triple::Simulator)
296 return XcodeSDK::WatchSimulator;
297 return XcodeSDK::watchOS;
298 case Triple::XROS:
299 if (triple.getEnvironment() == Triple::Simulator)
300 return XcodeSDK::XRSimulator;
301 return XcodeSDK::XROS;
302 case Triple::Linux:
303 return XcodeSDK::Linux;
304 default:
305 return XcodeSDK::unknown;
306 }
307}
308
309std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) {
310 auto begin = llvm::sys::path::begin(path);
311 auto end = llvm::sys::path::end(path);
312
313 // Iterate over the path components until we find something that ends with
314 // .app. If the next component is Contents then we've found the Contents
315 // directory.
316 for (auto it = begin; it != end; ++it) {
317 if (it->ends_with(Suffix: ".app")) {
318 auto next = it;
319 if (++next != end && *next == "Contents") {
320 llvm::SmallString<128> buffer;
321 llvm::sys::path::append(path&: buffer, begin, end: ++next,
322 style: llvm::sys::path::Style::posix);
323 return buffer.str().str();
324 }
325 }
326 }
327
328 return {};
329}
330

source code of lldb/source/Utility/XcodeSDK.cpp