| //===-- XcodeSDK.cpp ------------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "lldb/Utility/XcodeSDK.h" | 
 | #include "lldb/Utility/FileSpec.h" | 
 |  | 
 | #include "lldb/lldb-types.h" | 
 |  | 
 | #include "llvm/ADT/Triple.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 |  | 
 | static llvm::StringRef GetName(XcodeSDK::Type type) { | 
 |   switch (type) { | 
 |   case XcodeSDK::MacOSX: | 
 |     return "MacOSX"; | 
 |   case XcodeSDK::iPhoneSimulator: | 
 |     return "iPhoneSimulator"; | 
 |   case XcodeSDK::iPhoneOS: | 
 |     return "iPhoneOS"; | 
 |   case XcodeSDK::AppleTVSimulator: | 
 |     return "AppleTVSimulator"; | 
 |   case XcodeSDK::AppleTVOS: | 
 |     return "AppleTVOS"; | 
 |   case XcodeSDK::WatchSimulator: | 
 |     return "WatchSimulator"; | 
 |   case XcodeSDK::watchOS: | 
 |     return "WatchOS"; | 
 |   case XcodeSDK::bridgeOS: | 
 |     return "bridgeOS"; | 
 |   case XcodeSDK::Linux: | 
 |     return "Linux"; | 
 |   case XcodeSDK::unknown: | 
 |     return {}; | 
 |   } | 
 |   llvm_unreachable("Unhandled sdk type!"); | 
 | } | 
 |  | 
 | XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) { | 
 |   if (!m_name.empty()) { | 
 |     if (!info.version.empty()) | 
 |       m_name += info.version.getAsString(); | 
 |     if (info.internal) | 
 |       m_name += ".Internal"; | 
 |     m_name += ".sdk"; | 
 |   } | 
 | } | 
 |  | 
 | XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) { | 
 |   m_name = other.m_name; | 
 |   return *this; | 
 | } | 
 |  | 
 | bool XcodeSDK::operator==(const XcodeSDK &other) { | 
 |   return m_name == other.m_name; | 
 | } | 
 |  | 
 | static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) { | 
 |   if (name.consume_front("MacOSX")) | 
 |     return XcodeSDK::MacOSX; | 
 |   if (name.consume_front("iPhoneSimulator")) | 
 |     return XcodeSDK::iPhoneSimulator; | 
 |   if (name.consume_front("iPhoneOS")) | 
 |     return XcodeSDK::iPhoneOS; | 
 |   if (name.consume_front("AppleTVSimulator")) | 
 |     return XcodeSDK::AppleTVSimulator; | 
 |   if (name.consume_front("AppleTVOS")) | 
 |     return XcodeSDK::AppleTVOS; | 
 |   if (name.consume_front("WatchSimulator")) | 
 |     return XcodeSDK::WatchSimulator; | 
 |   if (name.consume_front("WatchOS")) | 
 |     return XcodeSDK::watchOS; | 
 |   if (name.consume_front("bridgeOS")) | 
 |     return XcodeSDK::bridgeOS; | 
 |   if (name.consume_front("Linux")) | 
 |     return XcodeSDK::Linux; | 
 |   static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1, | 
 |                 "New SDK type was added, update this list!"); | 
 |   return XcodeSDK::unknown; | 
 | } | 
 |  | 
 | static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) { | 
 |   unsigned i = 0; | 
 |   while (i < name.size() && name[i] >= '0' && name[i] <= '9') | 
 |     ++i; | 
 |   if (i == name.size() || name[i++] != '.') | 
 |     return {}; | 
 |   while (i < name.size() && name[i] >= '0' && name[i] <= '9') | 
 |     ++i; | 
 |   if (i == name.size() || name[i++] != '.') | 
 |     return {}; | 
 |  | 
 |   llvm::VersionTuple version; | 
 |   version.tryParse(name.slice(0, i - 1)); | 
 |   name = name.drop_front(i); | 
 |   return version; | 
 | } | 
 |  | 
 | static bool ParseAppleInternalSDK(llvm::StringRef &name) { | 
 |   return name.consume_front("Internal.") || name.consume_front(".Internal."); | 
 | } | 
 |  | 
 | XcodeSDK::Info XcodeSDK::Parse() const { | 
 |   XcodeSDK::Info info; | 
 |   llvm::StringRef input(m_name); | 
 |   info.type = ParseSDKName(input); | 
 |   info.version = ParseSDKVersion(input); | 
 |   info.internal = ParseAppleInternalSDK(input); | 
 |   return info; | 
 | } | 
 |  | 
 | bool XcodeSDK::IsAppleInternalSDK() const { | 
 |   llvm::StringRef input(m_name); | 
 |   ParseSDKName(input); | 
 |   ParseSDKVersion(input); | 
 |   return ParseAppleInternalSDK(input); | 
 | } | 
 |  | 
 | llvm::VersionTuple XcodeSDK::GetVersion() const { | 
 |   llvm::StringRef input(m_name); | 
 |   ParseSDKName(input); | 
 |   return ParseSDKVersion(input); | 
 | } | 
 |  | 
 | XcodeSDK::Type XcodeSDK::GetType() const { | 
 |   llvm::StringRef input(m_name); | 
 |   return ParseSDKName(input); | 
 | } | 
 |  | 
 | llvm::StringRef XcodeSDK::GetString() const { return m_name; } | 
 |  | 
 | bool XcodeSDK::Info::operator<(const Info &other) const { | 
 |   return std::tie(type, version, internal) < | 
 |          std::tie(other.type, other.version, other.internal); | 
 | } | 
 |  | 
 | bool XcodeSDK::Info::operator==(const Info &other) const { | 
 |   return std::tie(type, version, internal) == | 
 |          std::tie(other.type, other.version, other.internal); | 
 | } | 
 |  | 
 | void XcodeSDK::Merge(const XcodeSDK &other) { | 
 |   // The "bigger" SDK always wins. | 
 |   auto l = Parse(); | 
 |   auto r = other.Parse(); | 
 |   if (l < r) | 
 |     *this = other; | 
 |   else { | 
 |     // The Internal flag always wins. | 
 |     if (llvm::StringRef(m_name).endswith(".sdk")) | 
 |       if (!l.internal && r.internal) | 
 |         m_name = | 
 |             m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk"); | 
 |   } | 
 | } | 
 |  | 
 | std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) { | 
 |   std::string name; | 
 |   switch (info.type) { | 
 |   case MacOSX: | 
 |     name = "macosx"; | 
 |     break; | 
 |   case iPhoneSimulator: | 
 |     name = "iphonesimulator"; | 
 |     break; | 
 |   case iPhoneOS: | 
 |     name = "iphoneos"; | 
 |     break; | 
 |   case AppleTVSimulator: | 
 |     name = "appletvsimulator"; | 
 |     break; | 
 |   case AppleTVOS: | 
 |     name = "appletvos"; | 
 |     break; | 
 |   case WatchSimulator: | 
 |     name = "watchsimulator"; | 
 |     break; | 
 |   case watchOS: | 
 |     name = "watchos"; | 
 |     break; | 
 |   case bridgeOS: | 
 |     name = "bridgeos"; | 
 |     break; | 
 |   case Linux: | 
 |     name = "linux"; | 
 |     break; | 
 |   case unknown: | 
 |     return {}; | 
 |   } | 
 |   if (!info.version.empty()) | 
 |     name += info.version.getAsString(); | 
 |   if (info.internal) | 
 |     name += ".internal"; | 
 |   return name; | 
 | } | 
 |  | 
 | bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, | 
 |                                   llvm::VersionTuple version) { | 
 |   switch (sdk_type) { | 
 |   case Type::MacOSX: | 
 |     return version >= llvm::VersionTuple(10, 10); | 
 |   case Type::iPhoneOS: | 
 |   case Type::iPhoneSimulator: | 
 |   case Type::AppleTVOS: | 
 |   case Type::AppleTVSimulator: | 
 |     return version >= llvm::VersionTuple(8); | 
 |   case Type::watchOS: | 
 |   case Type::WatchSimulator: | 
 |     return version >= llvm::VersionTuple(6); | 
 |   default: | 
 |     return false; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | bool XcodeSDK::SupportsSwift() const { | 
 |   XcodeSDK::Info info = Parse(); | 
 |   switch (info.type) { | 
 |   case Type::MacOSX: | 
 |     return info.version.empty() || info.version >= llvm::VersionTuple(10, 10); | 
 |   case Type::iPhoneOS: | 
 |   case Type::iPhoneSimulator: | 
 |     return info.version.empty() || info.version >= llvm::VersionTuple(8); | 
 |   case Type::AppleTVSimulator: | 
 |   case Type::AppleTVOS: | 
 |     return info.version.empty() || info.version >= llvm::VersionTuple(9); | 
 |   case Type::WatchSimulator: | 
 |   case Type::watchOS: | 
 |     return info.version.empty() || info.version >= llvm::VersionTuple(2); | 
 |   case Type::Linux: | 
 |     return true; | 
 |   default: | 
 |     return false; | 
 |   } | 
 | } | 
 |  | 
 | bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, | 
 |                                   const FileSpec &sdk_path) { | 
 |   ConstString last_path_component = sdk_path.GetLastPathComponent(); | 
 |  | 
 |   if (!last_path_component) | 
 |     return false; | 
 |  | 
 |   XcodeSDK sdk(last_path_component.GetStringRef().str()); | 
 |   if (sdk.GetType() != desired_type) | 
 |     return false; | 
 |   return SDKSupportsModules(sdk.GetType(), sdk.GetVersion()); | 
 | } | 
 |  | 
 | XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) { | 
 |   using namespace llvm; | 
 |   switch (triple.getOS()) { | 
 |   case Triple::MacOSX: | 
 |   case Triple::Darwin: | 
 |     return XcodeSDK::MacOSX; | 
 |   case Triple::IOS: | 
 |     switch (triple.getEnvironment()) { | 
 |     case Triple::MacABI: | 
 |       return XcodeSDK::MacOSX; | 
 |     case Triple::Simulator: | 
 |       return XcodeSDK::iPhoneSimulator; | 
 |     default: | 
 |       return XcodeSDK::iPhoneOS; | 
 |     } | 
 |   case Triple::TvOS: | 
 |     if (triple.getEnvironment() == Triple::Simulator) | 
 |       return XcodeSDK::AppleTVSimulator; | 
 |     return XcodeSDK::AppleTVOS; | 
 |   case Triple::WatchOS: | 
 |     if (triple.getEnvironment() == Triple::Simulator) | 
 |       return XcodeSDK::WatchSimulator; | 
 |     return XcodeSDK::watchOS; | 
 |   case Triple::Linux: | 
 |     return XcodeSDK::Linux; | 
 |   default: | 
 |     return XcodeSDK::unknown; | 
 |   } | 
 | } | 
 |  | 
 | std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) { | 
 |   auto begin = llvm::sys::path::begin(path); | 
 |   auto end = llvm::sys::path::end(path); | 
 |  | 
 |   // Iterate over the path components until we find something that ends with | 
 |   // .app. If the next component is Contents then we've found the Contents | 
 |   // directory. | 
 |   for (auto it = begin; it != end; ++it) { | 
 |     if (it->endswith(".app")) { | 
 |       auto next = it; | 
 |       if (++next != end && *next == "Contents") { | 
 |         llvm::SmallString<128> buffer; | 
 |         llvm::sys::path::append(buffer, begin, ++next, | 
 |                                 llvm::sys::path::Style::posix); | 
 |         return buffer.str().str(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return {}; | 
 | } |