diff --git a/lldb/include/lldb/Utility/XcodeSDK.h b/lldb/include/lldb/Utility/XcodeSDK.h --- a/lldb/include/lldb/Utility/XcodeSDK.h +++ b/lldb/include/lldb/Utility/XcodeSDK.h @@ -22,6 +22,9 @@ public: XcodeSDK() = default; + /// Initialize an XcodeSDK object with an SDK name. The SDK name is the last + /// directory component of a path one would pass to clang's -isysroot + /// parameter. For example, "MacOSX.10.14.sdk". XcodeSDK(std::string &&name) : m_name(std::move(name)) {} static XcodeSDK GetAnyMacOS() { return XcodeSDK("MacOSX.sdk"); } @@ -38,7 +41,6 @@ numSDKTypes, unknown = -1 }; - static llvm::StringRef GetNameForType(Type type); /// The merge function follows a strict order to maintain monotonicity: /// 1. SDK with the higher SDKType wins. @@ -49,15 +51,27 @@ XcodeSDK(const XcodeSDK&) = default; bool operator==(XcodeSDK other); - /// Return parsed SDK number, and SDK version number. - std::tuple Parse() const; + /// A parsed SDK directory name. + struct Info { + Type type = unknown; + llvm::VersionTuple version; + bool internal = false; + + Info() = default; + bool operator<(const Info &other) const; + }; + + /// Return parsed SDK type and version number. + Info Parse() const; + bool IsAppleInternalSDK() const; llvm::VersionTuple GetVersion() const; Type GetType() const; llvm::StringRef GetString() const; static bool SDKSupportsModules(Type type, llvm::VersionTuple version); static bool SDKSupportsModules(Type desired_type, const FileSpec &sdk_path); - static llvm::StringRef GetSDKNameForType(Type type); + /// Return the canonical SDK name, such as "macosx" for the macOS SDK. + static std::string GetCanonicalName(Info info); }; } // namespace lldb_private diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -298,37 +298,66 @@ } std::string HostInfoMacOSX::GetXcodeSDK(XcodeSDK sdk) { - std::string xcrun_cmd = "xcrun --show-sdk-path --sdk " + - XcodeSDK::GetSDKNameForType(sdk.GetType()).str(); - llvm::VersionTuple version = sdk.GetVersion(); - if (!version.empty()) - xcrun_cmd += version.getAsString(); - - int status = 0; - int signo = 0; - std::string output_str; - lldb_private::Status error = - Host::RunShellCommand(xcrun_cmd.c_str(), FileSpec(), &status, &signo, - &output_str, std::chrono::seconds(15)); - - // Check that xcrun return something useful. - if (status != 0 || output_str.empty()) - return {}; - - // Convert to a StringRef so we can manipulate the string without modifying - // the underlying data. - llvm::StringRef output(output_str); - - // Remove any trailing newline characters. - output = output.rtrim(); + XcodeSDK::Info info = sdk.Parse(); + std::string sdk_name = XcodeSDK::GetCanonicalName(info); + auto find_sdk = [](std::string sdk_name) -> std::string { + std::string xcrun_cmd = "xcrun --show-sdk-path --sdk " + sdk_name; + int status = 0; + int signo = 0; + std::string output_str; + lldb_private::Status error = + Host::RunShellCommand(xcrun_cmd.c_str(), FileSpec(), &status, &signo, + &output_str, std::chrono::seconds(15)); + + // Check that xcrun return something useful. + if (status != 0 || output_str.empty()) + return {}; + + // Convert to a StringRef so we can manipulate the string without modifying + // the underlying data. + llvm::StringRef output(output_str); + + // Remove any trailing newline characters. + output = output.rtrim(); + + // Strip any leading newline characters and everything before them. + const size_t last_newline = output.rfind('\n'); + if (last_newline != llvm::StringRef::npos) + output = output.substr(last_newline + 1); + + return output.str(); + }; + + std::string path = find_sdk(sdk_name); + while (path.empty()) { + // Try an alternate spelling of the name ("macosx10.9internal"). + if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() && + info.internal) { + llvm::StringRef fixed(sdk_name); + if (fixed.consume_back(".internal")) + sdk_name = fixed.str() + "internal"; + path = find_sdk(sdk_name); + if (!path.empty()) + break; + } + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); + LLDB_LOGF(log, "Couldn't find SDK %s on host", sdk_name.c_str()); + + // Try without the version. + if (!info.version.empty()) { + info.version = {}; + sdk_name = XcodeSDK::GetCanonicalName(info); + path = find_sdk(sdk_name); + if (!path.empty()) + break; + } - // Strip any leading newline characters and everything before them. - const size_t last_newline = output.rfind('\n'); - if (last_newline != llvm::StringRef::npos) - output = output.substr(last_newline + 1); + LLDB_LOGF(log, "Couldn't find any matching SDK on host"); + return {}; + } // Whatever is left in output should be a valid path. - if (!FileSystem::Instance().Exists(output)) + if (!FileSystem::Instance().Exists(path)) return {}; - return output.str(); + return path; } diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp --- a/lldb/source/Utility/XcodeSDK.cpp +++ b/lldb/source/Utility/XcodeSDK.cpp @@ -64,13 +64,24 @@ return version; } +static bool ParseAppleInternalSDK(llvm::StringRef &name) { + return 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; +} -std::tuple XcodeSDK::Parse() const { +bool XcodeSDK::IsAppleInternalSDK() const { llvm::StringRef input(m_name); - XcodeSDK::Type sdk = ParseSDKName(input); - llvm::VersionTuple version = ParseSDKVersion(input); - return std::make_tuple( - std::move(sdk), std::move(version)); + ParseSDKName(input); + ParseSDKVersion(input); + return ParseAppleInternalSDK(input); } llvm::VersionTuple XcodeSDK::GetVersion() const { @@ -86,37 +97,64 @@ 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); +} void XcodeSDK::Merge(XcodeSDK other) { // The "bigger" SDK always wins. - if (Parse() < other.Parse()) + 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"); + } } -llvm::StringRef XcodeSDK::GetSDKNameForType(XcodeSDK::Type type) { - switch (type) { +std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) { + std::string name; + switch (info.type) { case MacOSX: - return "macosx"; + name = "macosx"; + break; case iPhoneSimulator: - return "iphonesimulator"; + name = "iphonesimulator"; + break; case iPhoneOS: - return "iphoneos"; + name = "iphoneos"; + break; case AppleTVSimulator: - return "appletvsimulator"; + name = "appletvsimulator"; + break; case AppleTVOS: - return "appletvos"; + name = "appletvos"; + break; case WatchSimulator: - return "watchsimulator"; + name = "watchsimulator"; + break; case watchOS: - return "watchos"; + name = "watchos"; + break; case bridgeOS: - return "bridgeos"; + name = "bridgeos"; + break; case Linux: - return "linux"; + name = "linux"; + break; case numSDKTypes: case unknown: - return ""; + return {}; } - llvm_unreachable("unhandled switch case"); + if (!info.version.empty()) + name += info.version.getAsString(); + if (info.internal) + name += ".internal"; + return name; } bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, @@ -147,12 +185,15 @@ const llvm::StringRef sdk_name = last_path_component.GetStringRef(); const std::string sdk_name_lower = sdk_name.lower(); - const llvm::StringRef sdk_string = GetSDKNameForType(desired_type); + Info info; + info.type = desired_type; + const llvm::StringRef sdk_string = GetCanonicalName(info); if (!llvm::StringRef(sdk_name_lower).startswith(sdk_string)) return false; auto version_part = sdk_name.drop_front(sdk_string.size()); version_part.consume_back(".sdk"); + version_part.consume_back(".Internal"); llvm::VersionTuple version; if (version.tryParse(version_part)) diff --git a/lldb/unittests/Host/HostInfoTest.cpp b/lldb/unittests/Host/HostInfoTest.cpp --- a/lldb/unittests/Host/HostInfoTest.cpp +++ b/lldb/unittests/Host/HostInfoTest.cpp @@ -50,3 +50,13 @@ std::string s("abc"); EXPECT_TRUE(HostInfo::GetHostname(s)); } + +#if defined(__APPLE__) +TEST_F(HostInfoTest, GetXcodeSDK) { + EXPECT_FALSE(HostInfo::GetXcodeSDK(XcodeSDK("MacOSX.sdk")).empty()); + // These are expected to fall back to an available version. + EXPECT_FALSE(HostInfo::GetXcodeSDK(XcodeSDK("MacOSX9999.sdk")).empty()); + // This is expected to fail. + EXPECT_TRUE(HostInfo::GetXcodeSDK(XcodeSDK("CeciNestPasUnOS.sdk")).empty()); +} +#endif diff --git a/lldb/unittests/Utility/XcodeSDKTest.cpp b/lldb/unittests/Utility/XcodeSDKTest.cpp --- a/lldb/unittests/Utility/XcodeSDKTest.cpp +++ b/lldb/unittests/Utility/XcodeSDKTest.cpp @@ -30,6 +30,11 @@ EXPECT_EQ(XcodeSDK("MacOSX.sdk").GetVersion(), llvm::VersionTuple()); EXPECT_EQ(XcodeSDK("MacOSX10.9.sdk").GetVersion(), llvm::VersionTuple(10, 9)); EXPECT_EQ(XcodeSDK("MacOSX10.15.4.sdk").GetVersion(), llvm::VersionTuple(10, 15)); + EXPECT_EQ(XcodeSDK("MacOSX.sdk").IsAppleInternalSDK(), false); + EXPECT_EQ(XcodeSDK("MacOSX10.15.Internal.sdk").GetType(), XcodeSDK::MacOSX); + EXPECT_EQ(XcodeSDK("MacOSX10.15.Internal.sdk").GetVersion(), + llvm::VersionTuple(10, 15)); + EXPECT_EQ(XcodeSDK("MacOSX10.15.Internal.sdk").IsAppleInternalSDK(), true); EXPECT_EQ(XcodeSDK().GetType(), XcodeSDK::unknown); EXPECT_EQ(XcodeSDK().GetVersion(), llvm::VersionTuple()); } @@ -46,6 +51,12 @@ EXPECT_EQ(sdk.GetVersion(), llvm::VersionTuple(1, 1)); sdk.Merge(XcodeSDK("WatchOS2.0.sdk")); EXPECT_EQ(sdk.GetVersion(), llvm::VersionTuple(2, 0)); + sdk.Merge(XcodeSDK("WatchOS1.1.Internal.sdk")); + EXPECT_EQ(sdk.GetVersion(), llvm::VersionTuple(2, 0)); + EXPECT_EQ(sdk.IsAppleInternalSDK(), true); + XcodeSDK empty; + empty.Merge(XcodeSDK("MacOSX10.14.Internal.sdk")); + EXPECT_EQ(empty.GetString(), llvm::StringRef("MacOSX10.14.Internal.sdk")); } TEST(XcodeSDKTest, SDKSupportsModules) { @@ -55,6 +66,10 @@ FileSpec( base + "iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.0.sdk"))); + EXPECT_TRUE(XcodeSDK::SDKSupportsModules( + XcodeSDK::Type::iPhoneSimulator, + FileSpec(base + "iPhoneSimulator.platform/Developer/SDKs/" + "iPhoneSimulator12.0.Internal.sdk"))); EXPECT_FALSE(XcodeSDK::SDKSupportsModules( XcodeSDK::Type::iPhoneSimulator, FileSpec( @@ -68,19 +83,65 @@ FileSpec(base + "MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk"))); } -TEST(XcodeSDKTest, GetSDKNameForType) { - EXPECT_EQ("macosx", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::MacOSX)); - EXPECT_EQ("iphonesimulator", - XcodeSDK::GetSDKNameForType(XcodeSDK::Type::iPhoneSimulator)); - EXPECT_EQ("iphoneos", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::iPhoneOS)); - EXPECT_EQ("appletvsimulator", - XcodeSDK::GetSDKNameForType(XcodeSDK::Type::AppleTVSimulator)); - EXPECT_EQ("appletvos", - XcodeSDK::GetSDKNameForType(XcodeSDK::Type::AppleTVOS)); - EXPECT_EQ("watchsimulator", - XcodeSDK::GetSDKNameForType(XcodeSDK::Type::WatchSimulator)); - EXPECT_EQ("watchos", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::watchOS)); - EXPECT_EQ("linux", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::Linux)); - EXPECT_EQ("", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::numSDKTypes)); - EXPECT_EQ("", XcodeSDK::GetSDKNameForType(XcodeSDK::Type::unknown)); +TEST(XcodeSDKTest, GetCanonicalName) { + XcodeSDK::Info info; + info.type = XcodeSDK::Type::MacOSX; + EXPECT_EQ("macosx", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::iPhoneSimulator; + EXPECT_EQ("iphonesimulator", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::iPhoneOS; + EXPECT_EQ("iphoneos", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::AppleTVSimulator; + EXPECT_EQ("appletvsimulator", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::AppleTVOS; + EXPECT_EQ("appletvos", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::WatchSimulator; + EXPECT_EQ("watchsimulator", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::watchOS; + EXPECT_EQ("watchos", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::Linux; + EXPECT_EQ("linux", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::numSDKTypes; + EXPECT_EQ("", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::unknown; + EXPECT_EQ("", XcodeSDK::GetCanonicalName(info)); + + info.internal = true; + info.type = XcodeSDK::Type::MacOSX; + EXPECT_EQ("macosx.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::iPhoneSimulator; + EXPECT_EQ("iphonesimulator.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::iPhoneOS; + EXPECT_EQ("iphoneos.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::AppleTVSimulator; + EXPECT_EQ("appletvsimulator.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::AppleTVOS; + EXPECT_EQ("appletvos.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::WatchSimulator; + EXPECT_EQ("watchsimulator.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::watchOS; + EXPECT_EQ("watchos.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::MacOSX; + info.version = llvm::VersionTuple(10, 9); + EXPECT_EQ("macosx10.9.internal", XcodeSDK::GetCanonicalName(info)); + + info.type = XcodeSDK::Type::iPhoneOS; + info.version = llvm::VersionTuple(7, 0); + EXPECT_EQ("iphoneos7.0.internal", XcodeSDK::GetCanonicalName(info)); }