Index: test/tools/dsymutil/X86/darwin-bundle.test
===================================================================
--- /dev/null
+++ test/tools/dsymutil/X86/darwin-bundle.test
@@ -0,0 +1,30 @@
+REQUIRES: system-darwin
+
+RUN: rm -rf %t
+RUN: mkdir -p %t/dsymdest
+RUN: cat %p/../Inputs/basic.macho.x86_64 > %t/basic.macho.x86_64
+RUN: cat %p/../Inputs/Info.plist > %t/Info.plist
+
+RUN: llvm-dsymutil -oso-prepend-path=%p/.. %t/basic.macho.x86_64 -o %t/dsymdest/basic.macho.x86_64.dSYM
+RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist
+
+CHECK:
+CHECK-NEXT:
+CHECK-NEXT:
+CHECK-NEXT:
+CHECK-NEXT: CFBundleDevelopmentRegion
+CHECK-NEXT: English
+CHECK-NEXT: CFBundleIdentifier
+CHECK-NEXT: com.apple.xcode.dsym.custom
+CHECK-NEXT: CFBundleInfoDictionaryVersion
+CHECK-NEXT: 6.0
+CHECK-NEXT: CFBundlePackageType
+CHECK-NEXT: dSYM
+CHECK-NEXT: CFBundleSignature
+CHECK-NEXT: ????
+CHECK-NEXT: CFBundleShortVersionString
+CHECK-NEXT: 2.0
+CHECK-NEXT: CFBundleVersion
+CHECK-NEXT: 2
+CHECK-NEXT:
+CHECK-NEXT:
Index: tools/dsymutil/CFBundle.h
===================================================================
--- /dev/null
+++ tools/dsymutil/CFBundle.h
@@ -0,0 +1,26 @@
+//===- tools/dsymutil/CFBundle.h - CFBundle helper --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include
+
+namespace llvm {
+namespace dsymutil {
+
+struct CFBundleInfo {
+ std::string VersionStr = "1";
+ std::string ShortVersionStr = "1.0";
+ std::string IDStr;
+ bool OmitShortVersion() const { return ShortVersionStr.empty(); }
+};
+
+CFBundleInfo getBundleInfo(llvm::StringRef ExePath);
+
+} // end namespace dsymutil
+} // end namespace llvm
Index: tools/dsymutil/CFBundle.cpp
===================================================================
--- /dev/null
+++ tools/dsymutil/CFBundle.cpp
@@ -0,0 +1,296 @@
+//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+
+#ifdef __APPLE__
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include
+#include
+#include
+#include
+
+namespace llvm {
+namespace dsymutil {
+
+template struct CFDeleter {
+ void operator()(T *P) {
+ if (P)
+ ::CFRelease(P);
+ }
+};
+
+/// This CF helper owns any CF pointer and will call CFRelease() on any valid
+/// pointer it owns unless that pointer is explicitly released using the
+/// release() member function.
+template
+using CFReleaser =
+ std::unique_ptr::type,
+ CFDeleter::type>>;
+
+class CFString : public CFReleaser {
+public:
+ CFString(CFStringRef CFStr = nullptr);
+ CFString(const char *s, CFStringEncoding Encoding);
+
+ const char *GetFileSystemRepresentation(std::string &Str);
+ CFStringRef SetFileSystemRepresentation(const char *Path);
+ CFStringRef SetFileSystemRepresentationFromCFType(CFTypeRef CFType);
+ CFStringRef SetFileSystemRepresentationAndExpandTilde(const char *Path);
+ const char *UTF8(std::string &Str);
+ CFIndex GetLength() const;
+ static const char *UTF8(CFStringRef CFStr, std::string &Str);
+ static const char *FileSystemRepresentation(CFStringRef CFStr,
+ std::string &Str);
+};
+
+class CFBundle : public CFReleaser {
+public:
+ CFBundle(const char *Path = nullptr);
+ CFBundle(CFURLRef url);
+
+ bool SetPath(const char *Path);
+ CFStringRef GetIdentifier() const;
+ CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const;
+};
+
+static const char *CreatePathByExpandingTildePath(const char *Path,
+ std::string &ExpandedPath) {
+ glob_t GlobBuffer;
+ if (glob(Path, GLOB_TILDE, nullptr, &GlobBuffer) == 0) {
+ ExpandedPath = GlobBuffer.gl_pathv[0];
+ globfree(&GlobBuffer);
+ } else
+ ExpandedPath.clear();
+
+ return ExpandedPath.c_str();
+}
+
+CFString::CFString(CFStringRef s) : CFReleaser(s) {}
+
+CFString::CFString(const char *cstr, CFStringEncoding cstr_encoding)
+ : CFReleaser() {
+ if (cstr && cstr[0]) {
+ reset(
+ ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+const char *CFString::GetFileSystemRepresentation(std::string &Str) {
+ return CFString::FileSystemRepresentation(get(), Str);
+}
+
+CFStringRef CFString::SetFileSystemRepresentation(const char *Path) {
+ CFStringRef NewValue = nullptr;
+ if (Path && Path[0])
+ NewValue =
+ ::CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, Path);
+ reset(NewValue);
+ return get();
+}
+
+CFStringRef CFString::SetFileSystemRepresentationFromCFType(CFTypeRef CFType) {
+ CFStringRef NewValue = nullptr;
+ if (CFType != nullptr) {
+ CFTypeID TypeID = ::CFGetTypeID(CFType);
+
+ if (TypeID == ::CFStringGetTypeID()) {
+ // Retain since we are using the existing object.
+ NewValue = (CFStringRef)::CFRetain(CFType);
+ } else if (TypeID == ::CFURLGetTypeID()) {
+ NewValue =
+ ::CFURLCopyFileSystemPath((CFURLRef)CFType, kCFURLPOSIXPathStyle);
+ }
+ }
+ reset(NewValue);
+ return get();
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentationAndExpandTilde(const char *Path) {
+ std::string ExpandedPath;
+ if (CreatePathByExpandingTildePath(Path, ExpandedPath))
+ SetFileSystemRepresentation(ExpandedPath.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *CFString::UTF8(std::string &Str) {
+ return CFString::UTF8(get(), Str);
+}
+
+/// Static function that puts a copy of the UTF8 contents of CFStr into Str and
+/// returns the C string pointer that is contained in Str when successful, else
+/// nullptr is returned. This allows the std::string parameter to own the
+/// extracted string, and also allows that string to be returned as a C string
+/// pointer that can be used.
+const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) {
+ if (!CFStr)
+ return nullptr;
+
+ const CFStringEncoding Encoding = kCFStringEncodingUTF8;
+ CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr);
+ MaxUTF8StrLength =
+ CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding);
+ if (MaxUTF8StrLength > 0) {
+ Str.resize(MaxUTF8StrLength);
+ if (!Str.empty()) {
+ if (CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {
+ Str.resize(strlen(Str.c_str()));
+ return Str.c_str();
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+/// Static function that puts a copy of the file system representation of
+/// CFStr into Str and returns the C string pointer that is contained in Str
+/// when successful, else nullptr is returned. This allows the std::string
+/// parameter to own the extracted string, and also allows that string to be
+/// returned as a C string pointer that can be used.
+const char *CFString::FileSystemRepresentation(CFStringRef CFStr,
+ std::string &Str) {
+ if (!CFStr)
+ return nullptr;
+
+ CFIndex max_length =
+ ::CFStringGetMaximumSizeOfFileSystemRepresentation(CFStr);
+ if (max_length > 0) {
+ Str.resize(max_length);
+ if (!Str.empty()) {
+ if (::CFStringGetFileSystemRepresentation(CFStr, &Str[0], Str.size())) {
+ Str.erase(::strlen(Str.c_str()));
+ return Str.c_str();
+ }
+ }
+ }
+
+ Str.erase();
+ return nullptr;
+}
+
+CFIndex CFString::GetLength() const {
+ if (CFStringRef Str = get())
+ return CFStringGetLength(Str);
+ return 0;
+}
+
+CFBundle::CFBundle(const char *Path) : CFReleaser() {
+ if (Path && Path[0])
+ SetPath(Path);
+}
+
+CFBundle::CFBundle(CFURLRef url)
+ : CFReleaser(url ? CFBundleCreate(nullptr, url) : nullptr) {}
+
+bool CFBundle::SetPath(const char *InPath) {
+ // Release our old bundle and ULR
+ reset(); // This class is a CFReleaser
+
+ if (InPath && InPath[0]) {
+ char ResolvedPath[PATH_MAX];
+ const char *Path = ::realpath(InPath, ResolvedPath);
+ if (Path == nullptr)
+ Path = InPath;
+
+ CFAllocatorRef Allocator = kCFAllocatorDefault;
+ // Make our Bundle URL
+ CFReleaser BundleURL(::CFURLCreateFromFileSystemRepresentation(
+ Allocator, (const UInt8 *)Path, strlen(Path), false));
+ if (BundleURL.get()) {
+ CFIndex LastLength = LONG_MAX;
+
+ while (BundleURL.get() != nullptr) {
+ // Check the Path range and make sure we didn't make it to just
+ // "/", ".", or ".."
+ CFRange rangeIncludingSeparators;
+ CFRange range = ::CFURLGetByteRangeForComponent(
+ BundleURL.get(), kCFURLComponentPath, &rangeIncludingSeparators);
+ if (range.length > LastLength)
+ break;
+
+ reset(::CFBundleCreate(Allocator, BundleURL.get()));
+ if (get() != nullptr) {
+ if (GetIdentifier() != nullptr)
+ break;
+ reset();
+ }
+ BundleURL.reset(::CFURLCreateCopyDeletingLastPathComponent(
+ Allocator, BundleURL.get()));
+
+ LastLength = range.length;
+ }
+ }
+ }
+ return get() != nullptr;
+}
+
+CFStringRef CFBundle::GetIdentifier() const {
+ if (CFBundleRef bundle = get())
+ return ::CFBundleGetIdentifier(bundle);
+ return nullptr;
+}
+
+CFTypeRef CFBundle::GetValueForInfoDictionaryKey(CFStringRef key) const {
+ if (CFBundleRef bundle = get())
+ return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
+ return nullptr;
+}
+#endif
+
+CFBundleInfo getBundleInfo(StringRef ExePath) {
+ CFBundleInfo BundleInfo;
+
+#ifdef __APPLE__
+ if (ExePath.empty() || !sys::fs::exists(ExePath))
+ return BundleInfo;
+
+ auto PrintError = [&](CFTypeID TypeID) {
+ CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID));
+ std::string TypeIDStr;
+ errs() << "The Info.plist key \"CFBundleShortVersionString\" is"
+ << "a " << TypeIDCFStr.UTF8(TypeIDStr)
+ << ", but it should be a string in: " << ExePath << ".\n";
+ };
+
+ // Try and find the original executable's Info.plist information using
+ // CoreFoundation calls by creating a URL for the executable and chopping off
+ // the last Path component. The CFBundle can then get the identifier and grab
+ // any needed information from it directly.
+ CFBundle Bundle(ExePath.data());
+ if (CFStringRef BundleID = Bundle.GetIdentifier()) {
+ CFString::UTF8(BundleID, BundleInfo.IDStr);
+ if (CFTypeRef TypeRef =
+ Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) {
+ CFTypeID TypeID = ::CFGetTypeID(TypeRef);
+ if (TypeID == ::CFStringGetTypeID())
+ CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr);
+ else
+ PrintError(TypeID);
+ }
+ if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey(
+ CFSTR("CFBundleShortVersionString"))) {
+ CFTypeID TypeID = ::CFGetTypeID(TypeRef);
+ if (TypeID == ::CFStringGetTypeID())
+ CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr);
+ else
+ PrintError(TypeID);
+ }
+ }
+#endif
+
+ return BundleInfo;
+}
+
+} // end namespace dsymutil
+} // end namespace llvm
Index: tools/dsymutil/CMakeLists.txt
===================================================================
--- tools/dsymutil/CMakeLists.txt
+++ tools/dsymutil/CMakeLists.txt
@@ -11,6 +11,7 @@
add_llvm_tool(llvm-dsymutil
dsymutil.cpp
BinaryHolder.cpp
+ CFBundle.cpp
DebugMap.cpp
DwarfLinker.cpp
MachODebugMapParser.cpp
@@ -20,3 +21,6 @@
intrinsics_gen
)
+IF(APPLE)
+ target_link_libraries(llvm-dsymutil "-framework CoreFoundation")
+ENDIF(APPLE)
Index: tools/dsymutil/dsymutil.cpp
===================================================================
--- tools/dsymutil/dsymutil.cpp
+++ tools/dsymutil/dsymutil.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "dsymutil.h"
+#include "CFBundle.h"
#include "DebugMap.h"
#include "MachOUtils.h"
#include "llvm/ADT/SmallString.h"
@@ -113,7 +114,7 @@
"y", desc("Treat the input file is a YAML debug map rather than a binary."),
init(false), cat(DsymCategory));
-static bool createPlistFile(llvm::StringRef BundleRoot) {
+static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
if (NoOutput)
return true;
@@ -128,16 +129,15 @@
return false;
}
- // FIXME: Use CoreFoundation to get executable bundle info. Use
- // dummy values for now.
- std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
- bundleIDStr;
+ CFBundleInfo BI = getBundleInfo(Bin);
- llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
- if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
- bundleIDStr = llvm::sys::path::stem(BundleID);
- else
- bundleIDStr = BundleID;
+ if (BI.IDStr.empty()) {
+ llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
+ if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
+ BI.IDStr = llvm::sys::path::stem(BundleID);
+ else
+ BI.IDStr = BundleID;
+ }
// Print out information to the plist file.
PL << "\n"
@@ -148,17 +148,20 @@
<< "\t\tCFBundleDevelopmentRegion\n"
<< "\t\tEnglish\n"
<< "\t\tCFBundleIdentifier\n"
- << "\t\tcom.apple.xcode.dsym." << bundleIDStr << "\n"
+ << "\t\tcom.apple.xcode.dsym." << BI.IDStr << "\n"
<< "\t\tCFBundleInfoDictionaryVersion\n"
<< "\t\t6.0\n"
<< "\t\tCFBundlePackageType\n"
<< "\t\tdSYM\n"
<< "\t\tCFBundleSignature\n"
- << "\t\t\?\?\?\?\n"
- << "\t\tCFBundleShortVersionString\n"
- << "\t\t" << bundleShortVersionStr << "\n"
- << "\t\tCFBundleVersion\n"
- << "\t\t" << bundleVersionStr << "\n"
+ << "\t\t\?\?\?\?\n";
+
+ if (!BI.OmitShortVersion())
+ PL << "\t\tCFBundleShortVersionString\n"
+ << "\t\t" << BI.ShortVersionStr << "\n";
+
+ PL << "\t\tCFBundleVersion\n"
+ << "\t\t" << BI.VersionStr << "\n"
<< "\t\n"
<< "\n";
@@ -206,7 +209,7 @@
llvm::SmallString<128> BundleDir(OutputFileOpt);
if (BundleDir.empty())
BundleDir = DwarfFile + ".dSYM";
- if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
+ if (!createBundleDir(BundleDir) || !createPlistFile(DwarfFile, BundleDir))
return "";
llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",