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