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