1dda28197Spatrick //===-- XcodeSDK.cpp ------------------------------------------------------===// 2dda28197Spatrick // 3dda28197Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4dda28197Spatrick // See https://llvm.org/LICENSE.txt for license information. 5dda28197Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6dda28197Spatrick // 7dda28197Spatrick //===----------------------------------------------------------------------===// 8dda28197Spatrick 9dda28197Spatrick #include "lldb/Utility/XcodeSDK.h" 10dda28197Spatrick #include "lldb/Utility/FileSpec.h" 11dda28197Spatrick 12dda28197Spatrick #include "lldb/lldb-types.h" 13dda28197Spatrick 14dda28197Spatrick #include "llvm/ADT/Triple.h" 15dda28197Spatrick 16dda28197Spatrick #include <string> 17dda28197Spatrick 18dda28197Spatrick using namespace lldb; 19dda28197Spatrick using namespace lldb_private; 20dda28197Spatrick 21dda28197Spatrick static llvm::StringRef GetName(XcodeSDK::Type type) { 22dda28197Spatrick switch (type) { 23dda28197Spatrick case XcodeSDK::MacOSX: 24dda28197Spatrick return "MacOSX"; 25dda28197Spatrick case XcodeSDK::iPhoneSimulator: 26dda28197Spatrick return "iPhoneSimulator"; 27dda28197Spatrick case XcodeSDK::iPhoneOS: 28dda28197Spatrick return "iPhoneOS"; 29dda28197Spatrick case XcodeSDK::AppleTVSimulator: 30dda28197Spatrick return "AppleTVSimulator"; 31dda28197Spatrick case XcodeSDK::AppleTVOS: 32dda28197Spatrick return "AppleTVOS"; 33dda28197Spatrick case XcodeSDK::WatchSimulator: 34dda28197Spatrick return "WatchSimulator"; 35dda28197Spatrick case XcodeSDK::watchOS: 36dda28197Spatrick return "WatchOS"; 37dda28197Spatrick case XcodeSDK::bridgeOS: 38dda28197Spatrick return "bridgeOS"; 39dda28197Spatrick case XcodeSDK::Linux: 40dda28197Spatrick return "Linux"; 41dda28197Spatrick case XcodeSDK::unknown: 42dda28197Spatrick return {}; 43dda28197Spatrick } 44dda28197Spatrick llvm_unreachable("Unhandled sdk type!"); 45dda28197Spatrick } 46dda28197Spatrick 47dda28197Spatrick XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) { 48dda28197Spatrick if (!m_name.empty()) { 49dda28197Spatrick if (!info.version.empty()) 50dda28197Spatrick m_name += info.version.getAsString(); 51dda28197Spatrick if (info.internal) 52dda28197Spatrick m_name += ".Internal"; 53dda28197Spatrick m_name += ".sdk"; 54dda28197Spatrick } 55dda28197Spatrick } 56dda28197Spatrick 57*be691f3bSpatrick XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) { 58dda28197Spatrick m_name = other.m_name; 59dda28197Spatrick return *this; 60dda28197Spatrick } 61dda28197Spatrick 62*be691f3bSpatrick bool XcodeSDK::operator==(const XcodeSDK &other) { 63dda28197Spatrick return m_name == other.m_name; 64dda28197Spatrick } 65dda28197Spatrick 66dda28197Spatrick static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) { 67dda28197Spatrick if (name.consume_front("MacOSX")) 68dda28197Spatrick return XcodeSDK::MacOSX; 69dda28197Spatrick if (name.consume_front("iPhoneSimulator")) 70dda28197Spatrick return XcodeSDK::iPhoneSimulator; 71dda28197Spatrick if (name.consume_front("iPhoneOS")) 72dda28197Spatrick return XcodeSDK::iPhoneOS; 73dda28197Spatrick if (name.consume_front("AppleTVSimulator")) 74dda28197Spatrick return XcodeSDK::AppleTVSimulator; 75dda28197Spatrick if (name.consume_front("AppleTVOS")) 76dda28197Spatrick return XcodeSDK::AppleTVOS; 77dda28197Spatrick if (name.consume_front("WatchSimulator")) 78dda28197Spatrick return XcodeSDK::WatchSimulator; 79dda28197Spatrick if (name.consume_front("WatchOS")) 80dda28197Spatrick return XcodeSDK::watchOS; 81dda28197Spatrick if (name.consume_front("bridgeOS")) 82dda28197Spatrick return XcodeSDK::bridgeOS; 83dda28197Spatrick if (name.consume_front("Linux")) 84dda28197Spatrick return XcodeSDK::Linux; 85dda28197Spatrick static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1, 86dda28197Spatrick "New SDK type was added, update this list!"); 87dda28197Spatrick return XcodeSDK::unknown; 88dda28197Spatrick } 89dda28197Spatrick 90dda28197Spatrick static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) { 91dda28197Spatrick unsigned i = 0; 92dda28197Spatrick while (i < name.size() && name[i] >= '0' && name[i] <= '9') 93dda28197Spatrick ++i; 94dda28197Spatrick if (i == name.size() || name[i++] != '.') 95dda28197Spatrick return {}; 96dda28197Spatrick while (i < name.size() && name[i] >= '0' && name[i] <= '9') 97dda28197Spatrick ++i; 98dda28197Spatrick if (i == name.size() || name[i++] != '.') 99dda28197Spatrick return {}; 100dda28197Spatrick 101dda28197Spatrick llvm::VersionTuple version; 102dda28197Spatrick version.tryParse(name.slice(0, i - 1)); 103dda28197Spatrick name = name.drop_front(i); 104dda28197Spatrick return version; 105dda28197Spatrick } 106dda28197Spatrick 107dda28197Spatrick static bool ParseAppleInternalSDK(llvm::StringRef &name) { 108dda28197Spatrick return name.consume_front("Internal.") || name.consume_front(".Internal."); 109dda28197Spatrick } 110dda28197Spatrick 111dda28197Spatrick XcodeSDK::Info XcodeSDK::Parse() const { 112dda28197Spatrick XcodeSDK::Info info; 113dda28197Spatrick llvm::StringRef input(m_name); 114dda28197Spatrick info.type = ParseSDKName(input); 115dda28197Spatrick info.version = ParseSDKVersion(input); 116dda28197Spatrick info.internal = ParseAppleInternalSDK(input); 117dda28197Spatrick return info; 118dda28197Spatrick } 119dda28197Spatrick 120dda28197Spatrick bool XcodeSDK::IsAppleInternalSDK() const { 121dda28197Spatrick llvm::StringRef input(m_name); 122dda28197Spatrick ParseSDKName(input); 123dda28197Spatrick ParseSDKVersion(input); 124dda28197Spatrick return ParseAppleInternalSDK(input); 125dda28197Spatrick } 126dda28197Spatrick 127dda28197Spatrick llvm::VersionTuple XcodeSDK::GetVersion() const { 128dda28197Spatrick llvm::StringRef input(m_name); 129dda28197Spatrick ParseSDKName(input); 130dda28197Spatrick return ParseSDKVersion(input); 131dda28197Spatrick } 132dda28197Spatrick 133dda28197Spatrick XcodeSDK::Type XcodeSDK::GetType() const { 134dda28197Spatrick llvm::StringRef input(m_name); 135dda28197Spatrick return ParseSDKName(input); 136dda28197Spatrick } 137dda28197Spatrick 138dda28197Spatrick llvm::StringRef XcodeSDK::GetString() const { return m_name; } 139dda28197Spatrick 140dda28197Spatrick bool XcodeSDK::Info::operator<(const Info &other) const { 141dda28197Spatrick return std::tie(type, version, internal) < 142dda28197Spatrick std::tie(other.type, other.version, other.internal); 143dda28197Spatrick } 144dda28197Spatrick 145dda28197Spatrick bool XcodeSDK::Info::operator==(const Info &other) const { 146dda28197Spatrick return std::tie(type, version, internal) == 147dda28197Spatrick std::tie(other.type, other.version, other.internal); 148dda28197Spatrick } 149dda28197Spatrick 150*be691f3bSpatrick void XcodeSDK::Merge(const XcodeSDK &other) { 151dda28197Spatrick // The "bigger" SDK always wins. 152dda28197Spatrick auto l = Parse(); 153dda28197Spatrick auto r = other.Parse(); 154dda28197Spatrick if (l < r) 155dda28197Spatrick *this = other; 156dda28197Spatrick else { 157dda28197Spatrick // The Internal flag always wins. 158dda28197Spatrick if (llvm::StringRef(m_name).endswith(".sdk")) 159dda28197Spatrick if (!l.internal && r.internal) 160dda28197Spatrick m_name = 161dda28197Spatrick m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk"); 162dda28197Spatrick } 163dda28197Spatrick } 164dda28197Spatrick 165dda28197Spatrick std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) { 166dda28197Spatrick std::string name; 167dda28197Spatrick switch (info.type) { 168dda28197Spatrick case MacOSX: 169dda28197Spatrick name = "macosx"; 170dda28197Spatrick break; 171dda28197Spatrick case iPhoneSimulator: 172dda28197Spatrick name = "iphonesimulator"; 173dda28197Spatrick break; 174dda28197Spatrick case iPhoneOS: 175dda28197Spatrick name = "iphoneos"; 176dda28197Spatrick break; 177dda28197Spatrick case AppleTVSimulator: 178dda28197Spatrick name = "appletvsimulator"; 179dda28197Spatrick break; 180dda28197Spatrick case AppleTVOS: 181dda28197Spatrick name = "appletvos"; 182dda28197Spatrick break; 183dda28197Spatrick case WatchSimulator: 184dda28197Spatrick name = "watchsimulator"; 185dda28197Spatrick break; 186dda28197Spatrick case watchOS: 187dda28197Spatrick name = "watchos"; 188dda28197Spatrick break; 189dda28197Spatrick case bridgeOS: 190dda28197Spatrick name = "bridgeos"; 191dda28197Spatrick break; 192dda28197Spatrick case Linux: 193dda28197Spatrick name = "linux"; 194dda28197Spatrick break; 195dda28197Spatrick case unknown: 196dda28197Spatrick return {}; 197dda28197Spatrick } 198dda28197Spatrick if (!info.version.empty()) 199dda28197Spatrick name += info.version.getAsString(); 200dda28197Spatrick if (info.internal) 201dda28197Spatrick name += ".internal"; 202dda28197Spatrick return name; 203dda28197Spatrick } 204dda28197Spatrick 205dda28197Spatrick bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, 206dda28197Spatrick llvm::VersionTuple version) { 207dda28197Spatrick switch (sdk_type) { 208dda28197Spatrick case Type::MacOSX: 209dda28197Spatrick return version >= llvm::VersionTuple(10, 10); 210dda28197Spatrick case Type::iPhoneOS: 211dda28197Spatrick case Type::iPhoneSimulator: 212dda28197Spatrick case Type::AppleTVOS: 213dda28197Spatrick case Type::AppleTVSimulator: 214dda28197Spatrick return version >= llvm::VersionTuple(8); 215dda28197Spatrick case Type::watchOS: 216dda28197Spatrick case Type::WatchSimulator: 217dda28197Spatrick return version >= llvm::VersionTuple(6); 218dda28197Spatrick default: 219dda28197Spatrick return false; 220dda28197Spatrick } 221dda28197Spatrick 222dda28197Spatrick return false; 223dda28197Spatrick } 224dda28197Spatrick 225dda28197Spatrick bool XcodeSDK::SupportsSwift() const { 226dda28197Spatrick XcodeSDK::Info info = Parse(); 227dda28197Spatrick switch (info.type) { 228dda28197Spatrick case Type::MacOSX: 229dda28197Spatrick return info.version.empty() || info.version >= llvm::VersionTuple(10, 10); 230dda28197Spatrick case Type::iPhoneOS: 231dda28197Spatrick case Type::iPhoneSimulator: 232dda28197Spatrick return info.version.empty() || info.version >= llvm::VersionTuple(8); 233dda28197Spatrick case Type::AppleTVSimulator: 234dda28197Spatrick case Type::AppleTVOS: 235dda28197Spatrick return info.version.empty() || info.version >= llvm::VersionTuple(9); 236dda28197Spatrick case Type::WatchSimulator: 237dda28197Spatrick case Type::watchOS: 238dda28197Spatrick return info.version.empty() || info.version >= llvm::VersionTuple(2); 239dda28197Spatrick case Type::Linux: 240dda28197Spatrick return true; 241dda28197Spatrick default: 242dda28197Spatrick return false; 243dda28197Spatrick } 244dda28197Spatrick } 245dda28197Spatrick 246dda28197Spatrick bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, 247dda28197Spatrick const FileSpec &sdk_path) { 248dda28197Spatrick ConstString last_path_component = sdk_path.GetLastPathComponent(); 249dda28197Spatrick 250dda28197Spatrick if (!last_path_component) 251dda28197Spatrick return false; 252dda28197Spatrick 253dda28197Spatrick XcodeSDK sdk(last_path_component.GetStringRef().str()); 254dda28197Spatrick if (sdk.GetType() != desired_type) 255dda28197Spatrick return false; 256dda28197Spatrick return SDKSupportsModules(sdk.GetType(), sdk.GetVersion()); 257dda28197Spatrick } 258dda28197Spatrick 259dda28197Spatrick XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) { 260dda28197Spatrick using namespace llvm; 261dda28197Spatrick switch (triple.getOS()) { 262dda28197Spatrick case Triple::MacOSX: 263dda28197Spatrick case Triple::Darwin: 264dda28197Spatrick return XcodeSDK::MacOSX; 265dda28197Spatrick case Triple::IOS: 266dda28197Spatrick switch (triple.getEnvironment()) { 267dda28197Spatrick case Triple::MacABI: 268dda28197Spatrick return XcodeSDK::MacOSX; 269dda28197Spatrick case Triple::Simulator: 270dda28197Spatrick return XcodeSDK::iPhoneSimulator; 271dda28197Spatrick default: 272dda28197Spatrick return XcodeSDK::iPhoneOS; 273dda28197Spatrick } 274dda28197Spatrick case Triple::TvOS: 275dda28197Spatrick if (triple.getEnvironment() == Triple::Simulator) 276dda28197Spatrick return XcodeSDK::AppleTVSimulator; 277dda28197Spatrick return XcodeSDK::AppleTVOS; 278dda28197Spatrick case Triple::WatchOS: 279dda28197Spatrick if (triple.getEnvironment() == Triple::Simulator) 280dda28197Spatrick return XcodeSDK::WatchSimulator; 281dda28197Spatrick return XcodeSDK::watchOS; 282dda28197Spatrick case Triple::Linux: 283dda28197Spatrick return XcodeSDK::Linux; 284dda28197Spatrick default: 285dda28197Spatrick return XcodeSDK::unknown; 286dda28197Spatrick } 287dda28197Spatrick } 288dda28197Spatrick 289dda28197Spatrick std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) { 290dda28197Spatrick auto begin = llvm::sys::path::begin(path); 291dda28197Spatrick auto end = llvm::sys::path::end(path); 292dda28197Spatrick 293dda28197Spatrick // Iterate over the path components until we find something that ends with 294dda28197Spatrick // .app. If the next component is Contents then we've found the Contents 295dda28197Spatrick // directory. 296dda28197Spatrick for (auto it = begin; it != end; ++it) { 297dda28197Spatrick if (it->endswith(".app")) { 298dda28197Spatrick auto next = it; 299dda28197Spatrick if (++next != end && *next == "Contents") { 300dda28197Spatrick llvm::SmallString<128> buffer; 301dda28197Spatrick llvm::sys::path::append(buffer, begin, ++next, 302dda28197Spatrick llvm::sys::path::Style::posix); 303dda28197Spatrick return buffer.str().str(); 304dda28197Spatrick } 305dda28197Spatrick } 306dda28197Spatrick } 307dda28197Spatrick 308dda28197Spatrick return {}; 309dda28197Spatrick } 310