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