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/ADT/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::bridgeOS:
38 return "bridgeOS";
39 case XcodeSDK::Linux:
40 return "Linux";
41 case XcodeSDK::unknown:
42 return {};
43 }
44 llvm_unreachable("Unhandled sdk type!");
45 }
46
XcodeSDK(XcodeSDK::Info info)47 XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) {
48 if (!m_name.empty()) {
49 if (!info.version.empty())
50 m_name += info.version.getAsString();
51 if (info.internal)
52 m_name += ".Internal";
53 m_name += ".sdk";
54 }
55 }
56
operator =(const XcodeSDK & other)57 XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) {
58 m_name = other.m_name;
59 return *this;
60 }
61
operator ==(const XcodeSDK & other)62 bool XcodeSDK::operator==(const XcodeSDK &other) {
63 return m_name == other.m_name;
64 }
65
ParseSDKName(llvm::StringRef & name)66 static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) {
67 if (name.consume_front("MacOSX"))
68 return XcodeSDK::MacOSX;
69 if (name.consume_front("iPhoneSimulator"))
70 return XcodeSDK::iPhoneSimulator;
71 if (name.consume_front("iPhoneOS"))
72 return XcodeSDK::iPhoneOS;
73 if (name.consume_front("AppleTVSimulator"))
74 return XcodeSDK::AppleTVSimulator;
75 if (name.consume_front("AppleTVOS"))
76 return XcodeSDK::AppleTVOS;
77 if (name.consume_front("WatchSimulator"))
78 return XcodeSDK::WatchSimulator;
79 if (name.consume_front("WatchOS"))
80 return XcodeSDK::watchOS;
81 if (name.consume_front("bridgeOS"))
82 return XcodeSDK::bridgeOS;
83 if (name.consume_front("Linux"))
84 return XcodeSDK::Linux;
85 static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1,
86 "New SDK type was added, update this list!");
87 return XcodeSDK::unknown;
88 }
89
ParseSDKVersion(llvm::StringRef & name)90 static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) {
91 unsigned i = 0;
92 while (i < name.size() && name[i] >= '0' && name[i] <= '9')
93 ++i;
94 if (i == name.size() || name[i++] != '.')
95 return {};
96 while (i < name.size() && name[i] >= '0' && name[i] <= '9')
97 ++i;
98 if (i == name.size() || name[i++] != '.')
99 return {};
100
101 llvm::VersionTuple version;
102 version.tryParse(name.slice(0, i - 1));
103 name = name.drop_front(i);
104 return version;
105 }
106
ParseAppleInternalSDK(llvm::StringRef & name)107 static bool ParseAppleInternalSDK(llvm::StringRef &name) {
108 return name.consume_front("Internal.") || name.consume_front(".Internal.");
109 }
110
Parse() const111 XcodeSDK::Info XcodeSDK::Parse() const {
112 XcodeSDK::Info info;
113 llvm::StringRef input(m_name);
114 info.type = ParseSDKName(input);
115 info.version = ParseSDKVersion(input);
116 info.internal = ParseAppleInternalSDK(input);
117 return info;
118 }
119
IsAppleInternalSDK() const120 bool XcodeSDK::IsAppleInternalSDK() const {
121 llvm::StringRef input(m_name);
122 ParseSDKName(input);
123 ParseSDKVersion(input);
124 return ParseAppleInternalSDK(input);
125 }
126
GetVersion() const127 llvm::VersionTuple XcodeSDK::GetVersion() const {
128 llvm::StringRef input(m_name);
129 ParseSDKName(input);
130 return ParseSDKVersion(input);
131 }
132
GetType() const133 XcodeSDK::Type XcodeSDK::GetType() const {
134 llvm::StringRef input(m_name);
135 return ParseSDKName(input);
136 }
137
GetString() const138 llvm::StringRef XcodeSDK::GetString() const { return m_name; }
139
operator <(const Info & other) const140 bool XcodeSDK::Info::operator<(const Info &other) const {
141 return std::tie(type, version, internal) <
142 std::tie(other.type, other.version, other.internal);
143 }
144
operator ==(const Info & other) const145 bool XcodeSDK::Info::operator==(const Info &other) const {
146 return std::tie(type, version, internal) ==
147 std::tie(other.type, other.version, other.internal);
148 }
149
Merge(const XcodeSDK & other)150 void XcodeSDK::Merge(const XcodeSDK &other) {
151 // The "bigger" SDK always wins.
152 auto l = Parse();
153 auto r = other.Parse();
154 if (l < r)
155 *this = other;
156 else {
157 // The Internal flag always wins.
158 if (llvm::StringRef(m_name).endswith(".sdk"))
159 if (!l.internal && r.internal)
160 m_name =
161 m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk");
162 }
163 }
164
GetCanonicalName(XcodeSDK::Info info)165 std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
166 std::string name;
167 switch (info.type) {
168 case MacOSX:
169 name = "macosx";
170 break;
171 case iPhoneSimulator:
172 name = "iphonesimulator";
173 break;
174 case iPhoneOS:
175 name = "iphoneos";
176 break;
177 case AppleTVSimulator:
178 name = "appletvsimulator";
179 break;
180 case AppleTVOS:
181 name = "appletvos";
182 break;
183 case WatchSimulator:
184 name = "watchsimulator";
185 break;
186 case watchOS:
187 name = "watchos";
188 break;
189 case bridgeOS:
190 name = "bridgeos";
191 break;
192 case Linux:
193 name = "linux";
194 break;
195 case unknown:
196 return {};
197 }
198 if (!info.version.empty())
199 name += info.version.getAsString();
200 if (info.internal)
201 name += ".internal";
202 return name;
203 }
204
SDKSupportsModules(XcodeSDK::Type sdk_type,llvm::VersionTuple version)205 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type,
206 llvm::VersionTuple version) {
207 switch (sdk_type) {
208 case Type::MacOSX:
209 return version >= llvm::VersionTuple(10, 10);
210 case Type::iPhoneOS:
211 case Type::iPhoneSimulator:
212 case Type::AppleTVOS:
213 case Type::AppleTVSimulator:
214 return version >= llvm::VersionTuple(8);
215 case Type::watchOS:
216 case Type::WatchSimulator:
217 return version >= llvm::VersionTuple(6);
218 default:
219 return false;
220 }
221
222 return false;
223 }
224
SupportsSwift() const225 bool XcodeSDK::SupportsSwift() const {
226 XcodeSDK::Info info = Parse();
227 switch (info.type) {
228 case Type::MacOSX:
229 return info.version.empty() || info.version >= llvm::VersionTuple(10, 10);
230 case Type::iPhoneOS:
231 case Type::iPhoneSimulator:
232 return info.version.empty() || info.version >= llvm::VersionTuple(8);
233 case Type::AppleTVSimulator:
234 case Type::AppleTVOS:
235 return info.version.empty() || info.version >= llvm::VersionTuple(9);
236 case Type::WatchSimulator:
237 case Type::watchOS:
238 return info.version.empty() || info.version >= llvm::VersionTuple(2);
239 case Type::Linux:
240 return true;
241 default:
242 return false;
243 }
244 }
245
SDKSupportsModules(XcodeSDK::Type desired_type,const FileSpec & sdk_path)246 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type,
247 const FileSpec &sdk_path) {
248 ConstString last_path_component = sdk_path.GetLastPathComponent();
249
250 if (!last_path_component)
251 return false;
252
253 XcodeSDK sdk(last_path_component.GetStringRef().str());
254 if (sdk.GetType() != desired_type)
255 return false;
256 return SDKSupportsModules(sdk.GetType(), sdk.GetVersion());
257 }
258
GetSDKTypeForTriple(const llvm::Triple & triple)259 XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) {
260 using namespace llvm;
261 switch (triple.getOS()) {
262 case Triple::MacOSX:
263 case Triple::Darwin:
264 return XcodeSDK::MacOSX;
265 case Triple::IOS:
266 switch (triple.getEnvironment()) {
267 case Triple::MacABI:
268 return XcodeSDK::MacOSX;
269 case Triple::Simulator:
270 return XcodeSDK::iPhoneSimulator;
271 default:
272 return XcodeSDK::iPhoneOS;
273 }
274 case Triple::TvOS:
275 if (triple.getEnvironment() == Triple::Simulator)
276 return XcodeSDK::AppleTVSimulator;
277 return XcodeSDK::AppleTVOS;
278 case Triple::WatchOS:
279 if (triple.getEnvironment() == Triple::Simulator)
280 return XcodeSDK::WatchSimulator;
281 return XcodeSDK::watchOS;
282 case Triple::Linux:
283 return XcodeSDK::Linux;
284 default:
285 return XcodeSDK::unknown;
286 }
287 }
288
FindXcodeContentsDirectoryInPath(llvm::StringRef path)289 std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) {
290 auto begin = llvm::sys::path::begin(path);
291 auto end = llvm::sys::path::end(path);
292
293 // Iterate over the path components until we find something that ends with
294 // .app. If the next component is Contents then we've found the Contents
295 // directory.
296 for (auto it = begin; it != end; ++it) {
297 if (it->endswith(".app")) {
298 auto next = it;
299 if (++next != end && *next == "Contents") {
300 llvm::SmallString<128> buffer;
301 llvm::sys::path::append(buffer, begin, ++next,
302 llvm::sys::path::Style::posix);
303 return buffer.str().str();
304 }
305 }
306 }
307
308 return {};
309 }
310