[llvm] r319416 - [dsymutil] Upstream getBundleInfo implementation

Jonas Devlieghere via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 30 02:25:28 PST 2017


Author: jdevlieghere
Date: Thu Nov 30 02:25:28 2017
New Revision: 319416

URL: http://llvm.org/viewvc/llvm-project?rev=319416&view=rev
Log:
[dsymutil] Upstream getBundleInfo implementation

This patch implements `getBundleInfo`, which uses CoreFoundation to
obtain information about the CFBundle. This information is needed to
populate the Plist in the dSYM bundle.

This change only applies to darwin and is an NFC as far as other
platforms are concerned.

Differential revision: https://reviews.llvm.org/D40244

Added:
    llvm/trunk/test/tools/dsymutil/Inputs/Info.plist
    llvm/trunk/test/tools/dsymutil/X86/darwin-bundle.test
    llvm/trunk/tools/dsymutil/CFBundle.cpp
    llvm/trunk/tools/dsymutil/CFBundle.h
Modified:
    llvm/trunk/tools/dsymutil/CMakeLists.txt
    llvm/trunk/tools/dsymutil/dsymutil.cpp

Added: llvm/trunk/test/tools/dsymutil/Inputs/Info.plist
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/Inputs/Info.plist?rev=319416&view=auto
==============================================================================
--- llvm/trunk/test/tools/dsymutil/Inputs/Info.plist (added)
+++ llvm/trunk/test/tools/dsymutil/Inputs/Info.plist Thu Nov 30 02:25:28 2017
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleDevelopmentRegion</key>
+		<string>English</string>
+		<key>CFBundleIdentifier</key>
+		<string>custom</string>
+		<key>CFBundleInfoDictionaryVersion</key>
+		<string>6.0</string>
+		<key>CFBundlePackageType</key>
+		<string>dSYM</string>
+		<key>CFBundleSignature</key>
+		<string>????</string>
+		<key>CFBundleShortVersionString</key>
+		<string>2.0</string>
+		<key>CFBundleVersion</key>
+		<string>2</string>
+	</dict>
+</plist>

Added: llvm/trunk/test/tools/dsymutil/X86/darwin-bundle.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/X86/darwin-bundle.test?rev=319416&view=auto
==============================================================================
--- llvm/trunk/test/tools/dsymutil/X86/darwin-bundle.test (added)
+++ llvm/trunk/test/tools/dsymutil/X86/darwin-bundle.test Thu Nov 30 02:25:28 2017
@@ -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: <?xml version="1.0" encoding="UTF-8"?>
+CHECK-NEXT: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+CHECK-NEXT: <plist version="1.0">
+CHECK-NEXT:         <dict>
+CHECK-NEXT:                 <key>CFBundleDevelopmentRegion</key>
+CHECK-NEXT:                 <string>English</string>
+CHECK-NEXT:                 <key>CFBundleIdentifier</key>
+CHECK-NEXT:                 <string>com.apple.xcode.dsym.custom</string>
+CHECK-NEXT:                 <key>CFBundleInfoDictionaryVersion</key>
+CHECK-NEXT:                 <string>6.0</string>
+CHECK-NEXT:                 <key>CFBundlePackageType</key>
+CHECK-NEXT:                 <string>dSYM</string>
+CHECK-NEXT:                 <key>CFBundleSignature</key>
+CHECK-NEXT:                 <string>????</string>
+CHECK-NEXT:                 <key>CFBundleShortVersionString</key>
+CHECK-NEXT:                 <string>2.0</string>
+CHECK-NEXT:                 <key>CFBundleVersion</key>
+CHECK-NEXT:                 <string>2</string>
+CHECK-NEXT:         </dict>
+CHECK-NEXT: </plist>

Added: llvm/trunk/tools/dsymutil/CFBundle.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/CFBundle.cpp?rev=319416&view=auto
==============================================================================
--- llvm/trunk/tools/dsymutil/CFBundle.cpp (added)
+++ llvm/trunk/tools/dsymutil/CFBundle.cpp Thu Nov 30 02:25:28 2017
@@ -0,0 +1,205 @@
+//===- 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 <CoreFoundation/CoreFoundation.h>
+#include <assert.h>
+#include <glob.h>
+#include <memory>
+
+namespace llvm {
+namespace dsymutil {
+
+/// Deleter that calls CFRelease rather than deleting the pointer.
+template <typename T> struct CFDeleter {
+  void operator()(T *P) {
+    if (P)
+      ::CFRelease(P);
+  }
+};
+
+/// This helper owns any CoreFoundation pointer and will call CFRelease() on
+/// any valid pointer it owns unless that pointer is explicitly released using
+/// the release() member function.
+template <typename T>
+using CFReleaser =
+    std::unique_ptr<typename std::remove_pointer<T>::type,
+                    CFDeleter<typename std::remove_pointer<T>::type>>;
+
+/// RAII wrapper around CFBundleRef.
+class CFString : public CFReleaser<CFStringRef> {
+public:
+  CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {}
+
+  const char *UTF8(std::string &Str) const {
+    return CFString::UTF8(get(), Str);
+  }
+
+  CFIndex GetLength() const {
+    if (CFStringRef Str = get())
+      return CFStringGetLength(Str);
+    return 0;
+  }
+
+  static const char *UTF8(CFStringRef CFStr, std::string &Str);
+};
+
+/// Static function that puts a copy of the UTF8 contents of CFStringRef into
+/// std::string and returns the C string pointer that is contained in the
+/// std::string when successful, nullptr otherwise.
+///
+/// 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() &&
+        CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {
+      Str.resize(strlen(Str.c_str()));
+      return Str.c_str();
+    }
+  }
+
+  return nullptr;
+}
+
+/// RAII wrapper around CFBundleRef.
+class CFBundle : public CFReleaser<CFBundleRef> {
+public:
+  CFBundle(const char *Path = nullptr) : CFReleaser<CFBundleRef>() {
+    if (Path && Path[0])
+      SetFromPath(Path);
+  }
+
+  CFBundle(CFURLRef url)
+      : CFReleaser<CFBundleRef>(url ? ::CFBundleCreate(nullptr, url)
+                                    : nullptr) {}
+
+  /// Return the bundle identifier.
+  CFStringRef GetIdentifier() const {
+    if (CFBundleRef bundle = get())
+      return ::CFBundleGetIdentifier(bundle);
+    return nullptr;
+  }
+
+  /// Return value for key.
+  CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const {
+    if (CFBundleRef bundle = get())
+      return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
+    return nullptr;
+  }
+
+private:
+  /// Update this instance with a new bundle created from the given path.
+  bool SetFromPath(const char *Path);
+};
+
+bool CFBundle::SetFromPath(const char *InPath) {
+  // Release our old bundle and URL.
+  reset();
+
+  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<CFURLRef> 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;
+}
+
+#endif
+
+/// On Darwin, 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. Return default
+/// CFBundleInfo on other platforms.
+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";
+  };
+
+  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

Added: llvm/trunk/tools/dsymutil/CFBundle.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/CFBundle.h?rev=319416&view=auto
==============================================================================
--- llvm/trunk/tools/dsymutil/CFBundle.h (added)
+++ llvm/trunk/tools/dsymutil/CFBundle.h Thu Nov 30 02:25:28 2017
@@ -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 <string>
+
+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

Modified: llvm/trunk/tools/dsymutil/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/CMakeLists.txt?rev=319416&r1=319415&r2=319416&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/CMakeLists.txt (original)
+++ llvm/trunk/tools/dsymutil/CMakeLists.txt Thu Nov 30 02:25:28 2017
@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_tool(llvm-dsymutil
   dsymutil.cpp
   BinaryHolder.cpp
+  CFBundle.cpp
   DebugMap.cpp
   DwarfLinker.cpp
   MachODebugMapParser.cpp
@@ -20,3 +21,6 @@ add_llvm_tool(llvm-dsymutil
   intrinsics_gen
   )
 
+IF(APPLE)
+  target_link_libraries(llvm-dsymutil "-framework CoreFoundation")
+ENDIF(APPLE)

Modified: llvm/trunk/tools/dsymutil/dsymutil.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/dsymutil.cpp?rev=319416&r1=319415&r2=319416&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/dsymutil.cpp (original)
+++ llvm/trunk/tools/dsymutil/dsymutil.cpp Thu Nov 30 02:25:28 2017
@@ -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 @@ static opt<bool> InputIsYAMLDebugMap(
     "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 @@ static bool createPlistFile(llvm::String
     return false;
   }
 
-  // FIXME: Use CoreFoundation to get executable bundle info. Use
-  // dummy values for now.
-  std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
-              bundleIDStr;
-
-  llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
-  if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
-    bundleIDStr = llvm::sys::path::stem(BundleID);
-  else
-    bundleIDStr = BundleID;
+  CFBundleInfo BI = getBundleInfo(Bin);
+
+  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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
@@ -148,17 +148,20 @@ static bool createPlistFile(llvm::String
      << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
      << "\t\t<string>English</string>\n"
      << "\t\t<key>CFBundleIdentifier</key>\n"
-     << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
+     << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
      << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
      << "\t\t<string>6.0</string>\n"
      << "\t\t<key>CFBundlePackageType</key>\n"
      << "\t\t<string>dSYM</string>\n"
      << "\t\t<key>CFBundleSignature</key>\n"
-     << "\t\t<string>\?\?\?\?</string>\n"
-     << "\t\t<key>CFBundleShortVersionString</key>\n"
-     << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
-     << "\t\t<key>CFBundleVersion</key>\n"
-     << "\t\t<string>" << bundleVersionStr << "</string>\n"
+     << "\t\t<string>\?\?\?\?</string>\n";
+
+  if (!BI.OmitShortVersion())
+    PL << "\t\t<key>CFBundleShortVersionString</key>\n"
+       << "\t\t<string>" << BI.ShortVersionStr << "</string>\n";
+
+  PL << "\t\t<key>CFBundleVersion</key>\n"
+     << "\t\t<string>" << BI.VersionStr << "</string>\n"
      << "\t</dict>\n"
      << "</plist>\n";
 
@@ -206,7 +209,7 @@ static std::string getOutputFileName(llv
   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",




More information about the llvm-commits mailing list