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/FileSpec.h"
10 #include "lldb/Utility/XcodeSDK.h"
11 
12 #include "lldb/lldb-types.h"
13 
14 using namespace lldb;
15 using namespace lldb_private;
16 
17 XcodeSDK &XcodeSDK::operator=(XcodeSDK other) {
18   m_name = other.m_name;
19   return *this;
20 }
21 
22 bool XcodeSDK::operator==(XcodeSDK other) {
23   return m_name == other.m_name;
24 }
25 
26 static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) {
27   if (name.consume_front("MacOSX"))
28     return XcodeSDK::MacOSX;
29   if (name.consume_front("iPhoneSimulator"))
30     return XcodeSDK::iPhoneSimulator;
31   if (name.consume_front("iPhoneOS"))
32     return XcodeSDK::iPhoneOS;
33   if (name.consume_front("AppleTVSimulator"))
34     return XcodeSDK::AppleTVSimulator;
35   if (name.consume_front("AppleTVOS"))
36     return XcodeSDK::AppleTVOS;
37   if (name.consume_front("WatchSimulator"))
38     return XcodeSDK::WatchSimulator;
39   if (name.consume_front("WatchOS"))
40     return XcodeSDK::watchOS;
41   if (name.consume_front("bridgeOS"))
42     return XcodeSDK::bridgeOS;
43   if (name.consume_front("Linux"))
44     return XcodeSDK::Linux;
45   static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1,
46                 "New SDK type was added, update this list!");
47   return XcodeSDK::unknown;
48 }
49 
50 static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) {
51   unsigned i = 0;
52   while (i < name.size() && name[i] >= '0' && name[i] <= '9')
53     ++i;
54   if (i == name.size() || name[i++] != '.')
55     return {};
56   while (i < name.size() && name[i] >= '0' && name[i] <= '9')
57     ++i;
58   if (i == name.size() || name[i++] != '.')
59     return {};
60 
61   llvm::VersionTuple version;
62   version.tryParse(name.slice(0, i - 1));
63   name = name.drop_front(i);
64   return version;
65 }
66 
67 static bool ParseAppleInternalSDK(llvm::StringRef &name) {
68   return name.consume_front("Internal.");
69 }
70 
71 XcodeSDK::Info XcodeSDK::Parse() const {
72   XcodeSDK::Info info;
73   llvm::StringRef input(m_name);
74   info.type = ParseSDKName(input);
75   info.version = ParseSDKVersion(input);
76   info.internal = ParseAppleInternalSDK(input);
77   return info;
78 }
79 
80 bool XcodeSDK::IsAppleInternalSDK() const {
81   llvm::StringRef input(m_name);
82   ParseSDKName(input);
83   ParseSDKVersion(input);
84   return ParseAppleInternalSDK(input);
85 }
86 
87 llvm::VersionTuple XcodeSDK::GetVersion() const {
88   llvm::StringRef input(m_name);
89   ParseSDKName(input);
90   return ParseSDKVersion(input);
91 }
92 
93 XcodeSDK::Type XcodeSDK::GetType() const {
94   llvm::StringRef input(m_name);
95   return ParseSDKName(input);
96 }
97 
98 llvm::StringRef XcodeSDK::GetString() const { return m_name; }
99 
100 bool XcodeSDK::Info::operator<(const Info &other) const {
101   return std::tie(type, version, internal) <
102          std::tie(other.type, other.version, other.internal);
103 }
104 void XcodeSDK::Merge(XcodeSDK other) {
105   // The "bigger" SDK always wins.
106   auto l = Parse();
107   auto r = other.Parse();
108   if (l < r)
109     *this = other;
110   else {
111     // The Internal flag always wins.
112     if (llvm::StringRef(m_name).endswith(".sdk"))
113       if (!l.internal && r.internal)
114         m_name =
115             m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk");
116   }
117 }
118 
119 std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
120   std::string name;
121   switch (info.type) {
122   case MacOSX:
123     name = "macosx";
124     break;
125   case iPhoneSimulator:
126     name = "iphonesimulator";
127     break;
128   case iPhoneOS:
129     name = "iphoneos";
130     break;
131   case AppleTVSimulator:
132     name = "appletvsimulator";
133     break;
134   case AppleTVOS:
135     name = "appletvos";
136     break;
137   case WatchSimulator:
138     name = "watchsimulator";
139     break;
140   case watchOS:
141     name = "watchos";
142     break;
143   case bridgeOS:
144     name = "bridgeos";
145     break;
146   case Linux:
147     name = "linux";
148     break;
149   case numSDKTypes:
150   case unknown:
151     return {};
152   }
153   if (!info.version.empty())
154     name += info.version.getAsString();
155   if (info.internal)
156     name += ".internal";
157   return name;
158 }
159 
160 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type,
161                                   llvm::VersionTuple version) {
162   switch (sdk_type) {
163   case Type::MacOSX:
164     return version >= llvm::VersionTuple(10, 10);
165   case Type::iPhoneOS:
166   case Type::iPhoneSimulator:
167   case Type::AppleTVOS:
168   case Type::AppleTVSimulator:
169     return version >= llvm::VersionTuple(8);
170   case Type::watchOS:
171   case Type::WatchSimulator:
172     return version >= llvm::VersionTuple(6);
173   default:
174     return false;
175   }
176 
177   return false;
178 }
179 
180 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type,
181                                   const FileSpec &sdk_path) {
182   ConstString last_path_component = sdk_path.GetLastPathComponent();
183 
184   if (last_path_component) {
185     const llvm::StringRef sdk_name = last_path_component.GetStringRef();
186 
187     const std::string sdk_name_lower = sdk_name.lower();
188     Info info;
189     info.type = desired_type;
190     const llvm::StringRef sdk_string = GetCanonicalName(info);
191     if (!llvm::StringRef(sdk_name_lower).startswith(sdk_string))
192       return false;
193 
194     auto version_part = sdk_name.drop_front(sdk_string.size());
195     version_part.consume_back(".sdk");
196     version_part.consume_back(".Internal");
197 
198     llvm::VersionTuple version;
199     if (version.tryParse(version_part))
200       return false;
201     return SDKSupportsModules(desired_type, version);
202   }
203 
204   return false;
205 }
206