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) {
58   m_name = other.m_name;
59   return *this;
60 }
61 
62 bool XcodeSDK::operator==(const XcodeSDK &other) {
63   return m_name == other.m_name;
64 }
65 
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 
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 
107 static bool ParseAppleInternalSDK(llvm::StringRef &name) {
108   return name.consume_front("Internal.") || name.consume_front(".Internal.");
109 }
110 
111 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 
120 bool XcodeSDK::IsAppleInternalSDK() const {
121   llvm::StringRef input(m_name);
122   ParseSDKName(input);
123   ParseSDKVersion(input);
124   return ParseAppleInternalSDK(input);
125 }
126 
127 llvm::VersionTuple XcodeSDK::GetVersion() const {
128   llvm::StringRef input(m_name);
129   ParseSDKName(input);
130   return ParseSDKVersion(input);
131 }
132 
133 XcodeSDK::Type XcodeSDK::GetType() const {
134   llvm::StringRef input(m_name);
135   return ParseSDKName(input);
136 }
137 
138 llvm::StringRef XcodeSDK::GetString() const { return m_name; }
139 
140 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 
145 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 
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 
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 
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 
225 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 
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 
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 
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