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