diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 8cde45ad6c784..08a858858ebce 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -875,6 +875,11 @@ class Module : public std::enable_shared_from_this, std::vector GetASTData(lldb::LanguageType language); + /// Return the Xcode SDK this module was compiled against. This + /// is computed by merging the SDKs from each compilation unit in + /// the module. + XcodeSDK GetXcodeSDK() const { return m_xcode_sdk; } + /// Update the ArchSpec to a more specific variant. bool MergeArchitecture(const ArchSpec &arch_spec); diff --git a/lldb/include/lldb/Utility/XcodeSDK.h b/lldb/include/lldb/Utility/XcodeSDK.h index 9b9f1d226bf09..f7516215e1bbe 100644 --- a/lldb/include/lldb/Utility/XcodeSDK.h +++ b/lldb/include/lldb/Utility/XcodeSDK.h @@ -21,6 +21,9 @@ class XcodeSDK { 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"); } @@ -37,7 +40,6 @@ class XcodeSDK { 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. @@ -47,15 +49,27 @@ class XcodeSDK { XcodeSDK &operator=(XcodeSDK other); 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 index c09339e8c6731..e495c752cb193 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -298,37 +298,66 @@ static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { } 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/Symbol/SwiftASTContext.cpp b/lldb/source/Symbol/SwiftASTContext.cpp index ebb7fe8285245..e0f507acc05bf 100644 --- a/lldb/source/Symbol/SwiftASTContext.cpp +++ b/lldb/source/Symbol/SwiftASTContext.cpp @@ -998,7 +998,9 @@ static SDKTypeMinVersion GetSDKType(const llvm::Triple &target, StringRef SwiftASTContext::GetSwiftStdlibOSDir(const llvm::Triple &target, const llvm::Triple &host) { auto sdk = GetSDKType(target, host); - llvm::StringRef sdk_name = XcodeSDK::GetSDKNameForType(sdk.sdk_type); + XcodeSDK::Info sdk_info; + sdk_info.type = sdk.sdk_type; + llvm::StringRef sdk_name = XcodeSDK::GetCanonicalName(sdk_info); if (!sdk_name.empty()) return sdk_name; return target.getOSName(); @@ -1668,17 +1670,21 @@ lldb::TypeSystemSP SwiftASTContext::CreateInstance(lldb::LanguageType language, set_triple = true; } + // SDK path setup. llvm::StringRef serialized_sdk_path = swift_ast_sp->GetCompilerInvocation().getSDKPath(); - if (serialized_sdk_path.empty()) { + if (serialized_sdk_path.empty()) LOG_PRINTF(LIBLLDB_LOG_TYPES, "No serialized SDK path."); - } else { - LOG_PRINTF(LIBLLDB_LOG_TYPES, "Got serialized SDK path %s.", + else + LOG_PRINTF(LIBLLDB_LOG_TYPES, "Serialized SDK path is %s.", serialized_sdk_path.str().c_str()); - FileSpec sdk_spec(serialized_sdk_path.str().c_str()); - if (FileSystem::Instance().Exists(sdk_spec)) { - swift_ast_sp->SetPlatformSDKPath(serialized_sdk_path); - } + XcodeSDK sdk = module.GetXcodeSDK(); + PlatformSP platform = + Platform::GetPlatformForArchitecture(module.GetArchitecture(), nullptr); + std::string sdk_path = platform->GetSDKPath(sdk); + LOG_PRINTF(LIBLLDB_LOG_TYPES, "Host SDK path is %s.", sdk_path.c_str()); + if (FileSystem::Instance().Exists(sdk_path)) { + swift_ast_sp->SetPlatformSDKPath(sdk_path); } } @@ -2668,14 +2674,6 @@ void SwiftASTContext::InitializeSearchPathOptions( // someone is passing this to us on the command line (e.g., for the // REPL), they probably know what they're doing. - set_sdk = true; - } - } else if (!m_platform_sdk_path.empty()) { - FileSpec platform_sdk(m_platform_sdk_path.c_str()); - - if (FileSystem::Instance().Exists(platform_sdk) && - SDKSupportsSwift(platform_sdk, XcodeSDK::Type::unknown)) { - invocation.setSDKPath(m_platform_sdk_path.c_str()); set_sdk = true; } } @@ -2694,13 +2692,19 @@ void SwiftASTContext::InitializeSearchPathOptions( auto sdk = GetSDKType(triple, HostInfo::GetArchitecture().GetTriple()); // Explicitly leave the SDKPath blank on other platforms. if (sdk.sdk_type != XcodeSDK::Type::unknown) { - auto dir = GetSDKDirectory(sdk.sdk_type, sdk.min_version_major, - sdk.min_version_minor); + std::string sdk_path = m_platform_sdk_path; + if (sdk_path.empty() || !FileSystem::Instance().Exists(sdk_path) || + !SDKSupportsSwift(FileSpec(sdk_path), sdk.sdk_type)) { + sdk_path = GetSDKDirectory(sdk.sdk_type, sdk.min_version_major, + sdk.min_version_minor) + .GetStringRef() + .str(); + } // Note that calling setSDKPath() also recomputes all paths that // depend on the SDK path including the // RuntimeLibraryImportPaths, which are *only* initialized // through this mechanism. - invocation.setSDKPath(dir.AsCString("")); + invocation.setSDKPath(sdk_path); } std::vector &lpaths = diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp index 7ad0090f85e2d..a34eac6b2c95b 100644 --- a/lldb/source/Utility/XcodeSDK.cpp +++ b/lldb/source/Utility/XcodeSDK.cpp @@ -64,13 +64,24 @@ static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) { 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 @@ XcodeSDK::Type XcodeSDK::GetType() const { 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 @@ bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, 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/test/API/lang/swift/xcode_sdk/Makefile b/lldb/test/API/lang/swift/xcode_sdk/Makefile new file mode 100644 index 0000000000000..13d4d1a086bff --- /dev/null +++ b/lldb/test/API/lang/swift/xcode_sdk/Makefile @@ -0,0 +1,4 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -Xfrontend -no-serialize-debugging-options + +include Makefile.rules diff --git a/lldb/test/API/lang/swift/xcode_sdk/TestXcodeSDK.py b/lldb/test/API/lang/swift/xcode_sdk/TestXcodeSDK.py new file mode 100644 index 0000000000000..5b118f0b3192f --- /dev/null +++ b/lldb/test/API/lang/swift/xcode_sdk/TestXcodeSDK.py @@ -0,0 +1,38 @@ +import lldb +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbtest as lldbtest +import lldbsuite.test.lldbutil as lldbutil +import os +import unittest2 + + +class TestSwiftAppleInternalSDK(lldbtest.TestBase): + + mydir = lldbtest.TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + @swiftTest + @skipUnlessDarwin + @skipIfDarwinEmbedded + def test(self): + """Test that we can detect an Xcode SDK from the DW_AT_APPLE_sdk attribute.""" + self.build() + log = self.getBuildArtifact("types.log") + command_result = lldb.SBCommandReturnObject() + interpreter = self.dbg.GetCommandInterpreter() + interpreter.HandleCommand("log enable lldb types -f "+log, command_result) + + target, process, thread, bkpt = lldbutil.run_to_name_breakpoint( + self, 'main') + + self.expect("p 1") + logfile = open(log, "r") + in_expr_log = 0 + found = 0 + for line in logfile: + if line.startswith(" SwiftASTContextForExpressions::LogConfiguration"): + in_expr_log += 1 + if in_expr_log and "SDK path" in line and ".sdk" in line: + found += 1 + self.assertEqual(in_expr_log, 1) + self.assertEqual(found, 1) diff --git a/lldb/test/API/lang/swift/xcode_sdk/main.swift b/lldb/test/API/lang/swift/xcode_sdk/main.swift new file mode 100644 index 0000000000000..4fdc9b23df9d8 --- /dev/null +++ b/lldb/test/API/lang/swift/xcode_sdk/main.swift @@ -0,0 +1 @@ +print("I have loaded Swift successfully - break here") diff --git a/lldb/unittests/Host/HostInfoTest.cpp b/lldb/unittests/Host/HostInfoTest.cpp index 450cb9edc5cd6..24a178ab64058 100644 --- a/lldb/unittests/Host/HostInfoTest.cpp +++ b/lldb/unittests/Host/HostInfoTest.cpp @@ -50,3 +50,13 @@ TEST_F(HostInfoTest, GetHostname) { 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 index a316516a16758..95b909e700184 100644 --- a/lldb/unittests/Utility/XcodeSDKTest.cpp +++ b/lldb/unittests/Utility/XcodeSDKTest.cpp @@ -30,6 +30,11 @@ TEST(XcodeSDKTest, ParseTest) { 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 @@ TEST(XcodeSDKTest, MergeTest) { 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 @@ TEST(XcodeSDKTest, SDKSupportsModules) { 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 @@ TEST(XcodeSDKTest, SDKSupportsModules) { 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)); }