brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.8 KiB · 0625afb Raw
186 lines · cpp
1//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "CFBundle.h"10 11#ifdef __APPLE__12#include "llvm/Support/FileSystem.h"13#include "llvm/Support/Path.h"14#include "llvm/Support/raw_ostream.h"15#include <CoreFoundation/CoreFoundation.h>16#include <assert.h>17#include <glob.h>18#include <memory>19#endif20 21namespace llvm {22namespace dsymutil {23 24#ifdef __APPLE__25/// Deleter that calls CFRelease rather than deleting the pointer.26template <typename T> struct CFDeleter {27  void operator()(T *P) {28    if (P)29      ::CFRelease(P);30  }31};32 33/// This helper owns any CoreFoundation pointer and will call CFRelease() on34/// any valid pointer it owns unless that pointer is explicitly released using35/// the release() member function.36template <typename T>37using CFReleaser = std::unique_ptr<std::remove_pointer_t<T>,38                                   CFDeleter<std::remove_pointer_t<T>>>;39 40/// RAII wrapper around CFBundleRef.41class CFString : public CFReleaser<CFStringRef> {42public:43  CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {}44 45  const char *UTF8(std::string &Str) const {46    return CFString::UTF8(get(), Str);47  }48 49  CFIndex GetLength() const {50    if (CFStringRef Str = get())51      return CFStringGetLength(Str);52    return 0;53  }54 55  static const char *UTF8(CFStringRef CFStr, std::string &Str);56};57 58/// Static function that puts a copy of the UTF-8 contents of CFStringRef into59/// std::string and returns the C string pointer that is contained in the60/// std::string when successful, nullptr otherwise.61///62/// This allows the std::string parameter to own the extracted string, and also63/// allows that string to be returned as a C string pointer that can be used.64const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) {65  if (!CFStr)66    return nullptr;67 68  const CFStringEncoding Encoding = kCFStringEncodingUTF8;69  CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr);70  MaxUTF8StrLength =71      CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding);72  if (MaxUTF8StrLength > 0) {73    Str.resize(MaxUTF8StrLength);74    if (!Str.empty() &&75        CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {76      Str.resize(strlen(Str.c_str()));77      return Str.c_str();78    }79  }80 81  return nullptr;82}83 84/// RAII wrapper around CFBundleRef.85class CFBundle : public CFReleaser<CFBundleRef> {86public:87  CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); }88 89  CFBundle(CFURLRef Url)90      : CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url)91                                    : nullptr) {}92 93  /// Return the bundle identifier.94  CFStringRef GetIdentifier() const {95    if (CFBundleRef bundle = get())96      return ::CFBundleGetIdentifier(bundle);97    return nullptr;98  }99 100  /// Return value for key.101  CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const {102    if (CFBundleRef bundle = get())103      return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);104    return nullptr;105  }106 107private:108  /// Helper to initialize this instance with a new bundle created from the109  /// given path. This function will recursively remove components from the110  /// path in its search for the nearest Info.plist.111  void SetFromPath(StringRef Path);112};113 114void CFBundle::SetFromPath(StringRef Path) {115  // Start from an empty/invalid CFBundle.116  reset();117 118  if (Path.empty() || !sys::fs::exists(Path))119    return;120 121  SmallString<256> RealPath;122  sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true);123 124  do {125    // Create a CFURL from the current path and use it to create a CFBundle.126    CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation(127        kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(),128        false));129    reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get()));130 131    // If we have a valid bundle and find its identifier we are done.132    if (get() != nullptr) {133      if (GetIdentifier() != nullptr)134        return;135      reset();136    }137 138    // Remove the last component of the path and try again until there's139    // nothing left but the root.140    sys::path::remove_filename(RealPath);141  } while (RealPath != sys::path::root_name(RealPath));142}143#endif144 145/// On Darwin, try and find the original executable's Info.plist to extract146/// information about the bundle. Return default values on other platforms.147CFBundleInfo getBundleInfo(StringRef ExePath) {148  CFBundleInfo BundleInfo;149 150#ifdef __APPLE__151  auto PrintError = [&](CFTypeID TypeID) {152    CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID));153    std::string TypeIDStr;154    errs() << "The Info.plist key \"CFBundleShortVersionString\" is"155           << "a " << TypeIDCFStr.UTF8(TypeIDStr)156           << ", but it should be a string in: " << ExePath << ".\n";157  };158 159  CFBundle Bundle(ExePath);160  if (CFStringRef BundleID = Bundle.GetIdentifier()) {161    CFString::UTF8(BundleID, BundleInfo.IDStr);162    if (CFTypeRef TypeRef =163            Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) {164      CFTypeID TypeID = ::CFGetTypeID(TypeRef);165      if (TypeID == ::CFStringGetTypeID())166        CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr);167      else168        PrintError(TypeID);169    }170    if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey(171            CFSTR("CFBundleShortVersionString"))) {172      CFTypeID TypeID = ::CFGetTypeID(TypeRef);173      if (TypeID == ::CFStringGetTypeID())174        CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr);175      else176        PrintError(TypeID);177    }178  }179#endif180 181  return BundleInfo;182}183 184} // end namespace dsymutil185} // end namespace llvm186