diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -79,15 +79,27 @@ static std::tuple ParseVersionBuildDir(llvm::StringRef str); - enum SDKType : unsigned { + enum SDKType : int { MacOSX = 0, iPhoneSimulator, iPhoneOS, + AppleTVSimulator, + AppleTVOS, + WatchSimulator, + watchOS, + bridgeOS, + Linux, + numSDKTypes, + unknown = -1 }; llvm::Expected FetchExtendedCrashInformation(lldb_private::Process &process) override; + static llvm::StringRef GetSDKNameForType(SDKType type); + static lldb_private::FileSpec GetXcodeSDK(SDKType type); + static lldb_private::FileSpec GetXcodeContentsDirectory(); + protected: struct CrashInfoAnnotations { uint64_t version; // unsigned long @@ -154,13 +166,15 @@ const char *GetDeveloperDirectory(); - lldb_private::Status - FindBundleBinaryInExecSearchPaths (const lldb_private::ModuleSpec &module_spec, lldb_private::Process *process, - lldb::ModuleSP &module_sp, const lldb_private::FileSpecList *module_search_paths_ptr, - lldb::ModuleSP *old_module_sp_ptr, bool *did_create_ptr); + lldb_private::Status FindBundleBinaryInExecSearchPaths( + const lldb_private::ModuleSpec &module_spec, + lldb_private::Process *process, lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, bool *did_create_ptr); - std::string m_developer_directory; + static std::string FindXcodeContentsDirectoryInPath(llvm::StringRef path); + std::string m_developer_directory; private: DISALLOW_COPY_AND_ASSIGN(PlatformDarwin); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -1268,76 +1268,6 @@ m_trap_handlers.push_back(ConstString("_sigtramp")); } -static const char *const sdk_strings[] = { - "MacOSX", "iPhoneSimulator", "iPhoneOS", -}; - -static FileSpec CheckPathForXcode(const FileSpec &fspec) { - if (FileSystem::Instance().Exists(fspec)) { - const char substr[] = ".app/Contents"; - - std::string path_to_shlib = fspec.GetPath(); - size_t pos = path_to_shlib.rfind(substr); - if (pos != std::string::npos) { - path_to_shlib.erase(pos + strlen(substr)); - FileSpec ret(path_to_shlib); - - FileSpec xcode_binary_path = ret; - xcode_binary_path.AppendPathComponent("MacOS"); - xcode_binary_path.AppendPathComponent("Xcode"); - - if (FileSystem::Instance().Exists(xcode_binary_path)) { - return ret; - } - } - } - return FileSpec(); -} - -static FileSpec GetXcodeContentsPath() { - static FileSpec g_xcode_filespec; - static llvm::once_flag g_once_flag; - llvm::call_once(g_once_flag, []() { - - FileSpec fspec; - - // First get the program file spec. If lldb.so or LLDB.framework is running - // in a program and that program is Xcode, the path returned with be the - // path to Xcode.app/Contents/MacOS/Xcode, so this will be the correct - // Xcode to use. - fspec = HostInfo::GetProgramFileSpec(); - - if (fspec) { - // Ignore the current binary if it is python. - std::string basename_lower = fspec.GetFilename().GetCString(); - std::transform(basename_lower.begin(), basename_lower.end(), - basename_lower.begin(), tolower); - if (basename_lower != "python") { - g_xcode_filespec = CheckPathForXcode(fspec); - } - } - - // Next check DEVELOPER_DIR environment variable - if (!g_xcode_filespec) { - const char *developer_dir_env_var = getenv("DEVELOPER_DIR"); - if (developer_dir_env_var && developer_dir_env_var[0]) { - FileSpec developer_dir_spec = FileSpec(developer_dir_env_var); - FileSystem::Instance().Resolve(developer_dir_spec); - g_xcode_filespec = CheckPathForXcode(developer_dir_spec); - } - - // Fall back to using "xcode-select" to find the selected Xcode - if (!g_xcode_filespec) { - FileSpec xcode_select_path(GetXcodeSelectPath()); - xcode_select_path.RemoveLastPathComponent(); - g_xcode_filespec = CheckPathForXcode(xcode_select_path); - } - } - }); - - return g_xcode_filespec; -} - static FileSpec GetCommandLineToolsLibraryPath() { static FileSpec g_command_line_tools_filespec; @@ -1359,7 +1289,14 @@ return version >= llvm::VersionTuple(10, 10); case SDKType::iPhoneOS: case SDKType::iPhoneSimulator: + case SDKType::AppleTVOS: + case SDKType::AppleTVSimulator: return version >= llvm::VersionTuple(8); + case SDKType::watchOS: + case SDKType::WatchSimulator: + return version >= llvm::VersionTuple(6); + default: + return false; } return false; @@ -1372,10 +1309,12 @@ if (last_path_component) { const llvm::StringRef sdk_name = last_path_component.GetStringRef(); - if (!sdk_name.startswith(sdk_strings[desired_type])) + const std::string sdk_name_lower = sdk_name.lower(); + const llvm::StringRef sdk_string = GetSDKNameForType(desired_type); + if (!llvm::StringRef(sdk_name_lower).startswith(sdk_string)) return false; - auto version_part = - sdk_name.drop_front(strlen(sdk_strings[desired_type])); + + auto version_part = sdk_name.drop_front(sdk_string.size()); version_part.consume_back(".sdk"); llvm::VersionTuple version; @@ -1427,14 +1366,7 @@ } FileSpec PlatformDarwin::GetSDKDirectoryForModules(SDKType sdk_type) { - switch (sdk_type) { - case SDKType::MacOSX: - case SDKType::iPhoneSimulator: - case SDKType::iPhoneOS: - break; - } - - FileSpec sdks_spec = GetXcodeContentsPath(); + FileSpec sdks_spec = GetXcodeContentsDirectory(); sdks_spec.AppendPathComponent("Developer"); sdks_spec.AppendPathComponent("Platforms"); @@ -1448,6 +1380,8 @@ case SDKType::iPhoneOS: sdks_spec.AppendPathComponent("iPhoneOS.platform"); break; + default: + llvm_unreachable("unsupported sdk"); } sdks_spec.AppendPathComponent("Developer"); @@ -1656,6 +1590,8 @@ use_current_os_version = false; #endif break; + default: + break; } llvm::VersionTuple version; @@ -1685,6 +1621,9 @@ case SDKType::MacOSX: minimum_version_option.PutCString("-mmacosx-version-min="); minimum_version_option.PutCString(version.getAsString()); + break; + default: + llvm_unreachable("unsupported sdk"); } options.push_back(std::string(minimum_version_option.GetString())); } @@ -1744,8 +1683,7 @@ lldb_private::FileSpec PlatformDarwin::LocateExecutable(const char *basename) { // A collection of SBFileSpec whose SBFileSpec.m_directory members are filled - // in with - // any executable directories that should be searched. + // in with any executable directories that should be searched. static std::vector g_executable_dirs; // Find the global list of directories that we will search for executables @@ -1754,7 +1692,7 @@ llvm::call_once(g_once_flag, []() { // When locating executables, trust the DEVELOPER_DIR first if it is set - FileSpec xcode_contents_dir = GetXcodeContentsPath(); + FileSpec xcode_contents_dir = GetXcodeContentsDirectory(); if (xcode_contents_dir) { FileSpec xcode_lldb_resources = xcode_contents_dir; xcode_lldb_resources.AppendPathComponent("SharedFrameworks"); @@ -1816,12 +1754,10 @@ return PlatformPOSIX::LaunchProcess(launch_info); } -lldb_private::Status -PlatformDarwin::FindBundleBinaryInExecSearchPaths (const ModuleSpec &module_spec, Process *process, - ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, - ModuleSP *old_module_sp_ptr, bool *did_create_ptr) -{ +lldb_private::Status PlatformDarwin::FindBundleBinaryInExecSearchPaths( + const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) { const FileSpec &platform_file = module_spec.GetFileSpec(); // See if the file is present in any of the module_search_paths_ptr // directories. @@ -1892,3 +1828,133 @@ } return Status(); } + +std::string +PlatformDarwin::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); + return buffer.str().str(); + } + } + } + + return {}; +} + +llvm::StringRef PlatformDarwin::GetSDKNameForType(SDKType type) { + switch (type) { + case MacOSX: + return "macosx"; + case iPhoneSimulator: + return "iphonesimulator"; + case iPhoneOS: + return "iphoneos"; + case AppleTVSimulator: + return "appletvsimulator"; + case AppleTVOS: + return "appletvos"; + case WatchSimulator: + return "watchsimulator"; + case watchOS: + return "watchos"; + case bridgeOS: + return "bridgeos"; + case Linux: + return "linux"; + case numSDKTypes: + case unknown: + return ""; + } + llvm_unreachable("unhandled switch case"); +} + +FileSpec PlatformDarwin::GetXcodeSDK(SDKType type) { + std::string xcrun_cmd = + "xcrun --show-sdk-path --sdk " + GetSDKNameForType(type).str(); + + 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); + + // Whatever is left in output should be a valid path. + if (!FileSystem::Instance().Exists(output)) + return {}; + + // Find the contents dir in the xcrun provided path. + std::string xcode_contents_dir = FindXcodeContentsDirectoryInPath(output); + if (xcode_contents_dir.empty()) + return {}; + + return FileSpec(xcode_contents_dir); +} + +FileSpec PlatformDarwin::GetXcodeContentsDirectory() { + static FileSpec g_xcode_contents_path; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, [&]() { + // Try the shlib dir first. + if (FileSpec fspec = HostInfo::GetShlibDir()) { + if (FileSystem::Instance().Exists(fspec)) { + std::string xcode_contents_dir = + FindXcodeContentsDirectoryInPath(fspec.GetPath()); + if (!xcode_contents_dir.empty()) { + g_xcode_contents_path = FileSpec(xcode_contents_dir); + return; + } + } + } + + if (const char *developer_dir_env_var = getenv("DEVELOPER_DIR")) { + FileSpec fspec(developer_dir_env_var); + if (FileSystem::Instance().Exists(fspec)) { + std::string xcode_contents_dir = + FindXcodeContentsDirectoryInPath(fspec.GetPath()); + if (!xcode_contents_dir.empty()) { + g_xcode_contents_path = FileSpec(xcode_contents_dir); + return; + } + } + } + + if (FileSpec fspec = GetXcodeSDK(SDKType::MacOSX)) { + if (FileSystem::Instance().Exists(fspec)) { + std::string xcode_contents_dir = + FindXcodeContentsDirectoryInPath(fspec.GetPath()); + if (!xcode_contents_dir.empty()) { + g_xcode_contents_path = FileSpec(xcode_contents_dir); + return; + } + } + } + }); + return g_xcode_contents_path; +} diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp @@ -185,73 +185,35 @@ ConstString PlatformMacOSX::GetSDKDirectory(lldb_private::Target &target) { ModuleSP exe_module_sp(target.GetExecutableModule()); - if (exe_module_sp) { - ObjectFile *objfile = exe_module_sp->GetObjectFile(); - if (objfile) { - std::string xcode_contents_path; - std::string default_xcode_sdk; - FileSpec fspec; - llvm::VersionTuple version = objfile->GetSDKVersion(); - if (!version.empty()) { - fspec = HostInfo::GetShlibDir(); - if (fspec) { - std::string path; - xcode_contents_path = fspec.GetPath(); - size_t pos = xcode_contents_path.find("/Xcode.app/Contents/"); - if (pos != std::string::npos) { - // LLDB.framework is inside an Xcode app bundle, we can locate the - // SDK from here - xcode_contents_path.erase(pos + strlen("/Xcode.app/Contents/")); - } else { - xcode_contents_path.clear(); - // Use the selected Xcode - int status = 0; - int signo = 0; - std::string output; - const char *command = "xcrun -sdk macosx --show-sdk-path"; - lldb_private::Status error = RunShellCommand( - command, // shell command to run - FileSpec(), // current working directory - &status, // Put the exit status of the process in here - &signo, // Put the signal that caused the process to exit in - // here - &output, // Get the output from the command and place it in this - // string - std::chrono::seconds(3)); - if (status == 0 && !output.empty()) { - size_t first_non_newline = output.find_last_not_of("\r\n"); - if (first_non_newline != std::string::npos) - output.erase(first_non_newline + 1); - default_xcode_sdk = output; - - pos = default_xcode_sdk.find("/Xcode.app/Contents/"); - if (pos != std::string::npos) - xcode_contents_path = default_xcode_sdk.substr( - 0, pos + strlen("/Xcode.app/Contents/")); - } - } - } - - if (!xcode_contents_path.empty()) { - StreamString sdk_path; - sdk_path.Printf("%sDeveloper/Platforms/MacOSX.platform/Developer/" - "SDKs/MacOSX%u.%u.sdk", - xcode_contents_path.c_str(), version.getMajor(), - version.getMinor().getValue()); - fspec.SetFile(sdk_path.GetString(), FileSpec::Style::native); - if (FileSystem::Instance().Exists(fspec)) - return ConstString(sdk_path.GetString()); - } + if (!exe_module_sp) + return {}; + + ObjectFile *objfile = exe_module_sp->GetObjectFile(); + if (!objfile) + return {}; + + llvm::VersionTuple version = objfile->GetSDKVersion(); + if (version.empty()) + return {}; + + // First try to find an SDK that matches the given SDK version. + if (FileSpec fspec = GetXcodeContentsDirectory()) { + StreamString sdk_path; + sdk_path.Printf("%s/Developer/Platforms/MacOSX.platform/Developer/" + "SDKs/MacOSX%u.%u.sdk", + fspec.GetPath().c_str(), version.getMajor(), + version.getMinor().getValue()); + if (FileSystem::Instance().Exists(fspec)) + return ConstString(sdk_path.GetString()); + } - if (!default_xcode_sdk.empty()) { - fspec.SetFile(default_xcode_sdk, FileSpec::Style::native); - if (FileSystem::Instance().Exists(fspec)) - return ConstString(default_xcode_sdk); - } - } - } + // Use the default SDK as a fallback. + if (FileSpec fspec = GetXcodeSDK(SDKType::MacOSX)) { + if (FileSystem::Instance().Exists(fspec)) + return ConstString(fspec.GetPath()); } - return ConstString(); + + return {}; } Status PlatformMacOSX::GetSymbolFile(const FileSpec &platform_file, diff --git a/lldb/unittests/Platform/PlatformDarwinTest.cpp b/lldb/unittests/Platform/PlatformDarwinTest.cpp --- a/lldb/unittests/Platform/PlatformDarwinTest.cpp +++ b/lldb/unittests/Platform/PlatformDarwinTest.cpp @@ -18,6 +18,8 @@ using namespace lldb_private; struct PlatformDarwinTester : public PlatformDarwin { +public: + using PlatformDarwin::FindXcodeContentsDirectoryInPath; static bool SDKSupportsModules(SDKType desired_type, const lldb_private::FileSpec &sdk_path) { return PlatformDarwin::SDKSupportsModules(desired_type, sdk_path); @@ -69,3 +71,64 @@ PlatformDarwin::SDKType::MacOSX, FileSpec(base + "MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk"))); } + +TEST(PlatformDarwinTest, FindXcodeContentsDirectoryInPath) { + std::string standard = + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX.sdk"; + EXPECT_EQ("/Applications/Xcode.app/Contents", + PlatformDarwinTester::FindXcodeContentsDirectoryInPath(standard)); + + std::string standard_version = + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX10.15.sdk"; + EXPECT_EQ( + "/Applications/Xcode.app/Contents", + PlatformDarwinTester::FindXcodeContentsDirectoryInPath(standard_version)); + + std::string beta = "/Applications/Xcode-beta.app/Contents/Developer/" + "Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX10.15.sdk"; + EXPECT_EQ("/Applications/Xcode-beta.app/Contents", + PlatformDarwinTester::FindXcodeContentsDirectoryInPath(beta)); + + std::string no_app = + "/Applications/Xcode/Contents/Developer/Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX10.15.sdk"; + EXPECT_EQ("", PlatformDarwinTester::FindXcodeContentsDirectoryInPath(no_app)); + + std::string no_contents = + "/Applications/Xcode.app/Developer/Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX10.15.sdk"; + EXPECT_EQ( + "", PlatformDarwinTester::FindXcodeContentsDirectoryInPath(no_contents)); + + std::string no_capitalization = + "/Applications/Xcode.app/contents/Developer/Platforms/MacOSX.platform/" + "Developer/SDKs/MacOSX10.15.sdk"; + EXPECT_EQ("", PlatformDarwinTester::FindXcodeContentsDirectoryInPath( + no_capitalization)); +} + +TEST(PlatformDarwinTest, GetSDKNameForType) { + EXPECT_EQ("macosx", + PlatformDarwin::GetSDKNameForType(PlatformDarwin::SDKType::MacOSX)); + EXPECT_EQ("iphonesimulator", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::iPhoneSimulator)); + EXPECT_EQ("iphoneos", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::iPhoneOS)); + EXPECT_EQ("appletvsimulator", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::AppleTVSimulator)); + EXPECT_EQ("appletvos", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::AppleTVOS)); + EXPECT_EQ("watchsimulator", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::WatchSimulator)); + EXPECT_EQ("watchos", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::watchOS)); + EXPECT_EQ("linux", + PlatformDarwin::GetSDKNameForType(PlatformDarwin::SDKType::Linux)); + EXPECT_EQ("", PlatformDarwin::GetSDKNameForType( + PlatformDarwin::SDKType::numSDKTypes)); + EXPECT_EQ( + "", PlatformDarwin::GetSDKNameForType(PlatformDarwin::SDKType::unknown)); +}