OSDN Git Service

android: ExecutionEngine/Orc: update sources list
[android-x86/external-llvm.git] / tools / dsymutil / CFBundle.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-exception
6 //
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 #endif
20
21 namespace llvm {
22 namespace dsymutil {
23
24 #ifdef __APPLE__
25 /// Deleter that calls CFRelease rather than deleting the pointer.
26 template <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() on
34 /// any valid pointer it owns unless that pointer is explicitly released using
35 /// the release() member function.
36 template <typename T>
37 using CFReleaser =
38     std::unique_ptr<typename std::remove_pointer<T>::type,
39                     CFDeleter<typename std::remove_pointer<T>::type>>;
40
41 /// RAII wrapper around CFBundleRef.
42 class CFString : public CFReleaser<CFStringRef> {
43 public:
44   CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {}
45
46   const char *UTF8(std::string &Str) const {
47     return CFString::UTF8(get(), Str);
48   }
49
50   CFIndex GetLength() const {
51     if (CFStringRef Str = get())
52       return CFStringGetLength(Str);
53     return 0;
54   }
55
56   static const char *UTF8(CFStringRef CFStr, std::string &Str);
57 };
58
59 /// Static function that puts a copy of the UTF-8 contents of CFStringRef into
60 /// std::string and returns the C string pointer that is contained in the
61 /// std::string when successful, nullptr otherwise.
62 ///
63 /// This allows the std::string parameter to own the extracted string, and also
64 /// allows that string to be returned as a C string pointer that can be used.
65 const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) {
66   if (!CFStr)
67     return nullptr;
68
69   const CFStringEncoding Encoding = kCFStringEncodingUTF8;
70   CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr);
71   MaxUTF8StrLength =
72       CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding);
73   if (MaxUTF8StrLength > 0) {
74     Str.resize(MaxUTF8StrLength);
75     if (!Str.empty() &&
76         CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {
77       Str.resize(strlen(Str.c_str()));
78       return Str.c_str();
79     }
80   }
81
82   return nullptr;
83 }
84
85 /// RAII wrapper around CFBundleRef.
86 class CFBundle : public CFReleaser<CFBundleRef> {
87 public:
88   CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); }
89
90   CFBundle(CFURLRef Url)
91       : CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url)
92                                     : nullptr) {}
93
94   /// Return the bundle identifier.
95   CFStringRef GetIdentifier() const {
96     if (CFBundleRef bundle = get())
97       return ::CFBundleGetIdentifier(bundle);
98     return nullptr;
99   }
100
101   /// Return value for key.
102   CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const {
103     if (CFBundleRef bundle = get())
104       return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
105     return nullptr;
106   }
107
108 private:
109   /// Helper to initialize this instance with a new bundle created from the
110   /// given path. This function will recursively remove components from the
111   /// path in its search for the nearest Info.plist.
112   void SetFromPath(StringRef Path);
113 };
114
115 void CFBundle::SetFromPath(StringRef Path) {
116   // Start from an empty/invalid CFBundle.
117   reset();
118
119   if (Path.empty() || !sys::fs::exists(Path))
120     return;
121
122   SmallString<256> RealPath;
123   sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true);
124
125   do {
126     // Create a CFURL from the current path and use it to create a CFBundle.
127     CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation(
128         kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(),
129         false));
130     reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get()));
131
132     // If we have a valid bundle and find its identifier we are done.
133     if (get() != nullptr) {
134       if (GetIdentifier() != nullptr)
135         return;
136       reset();
137     }
138
139     // Remove the last component of the path and try again until there's
140     // nothing left but the root.
141     sys::path::remove_filename(RealPath);
142   } while (RealPath != sys::path::root_name(RealPath));
143 }
144 #endif
145
146 /// On Darwin, try and find the original executable's Info.plist to extract
147 /// information about the bundle. Return default values on other platforms.
148 CFBundleInfo getBundleInfo(StringRef ExePath) {
149   CFBundleInfo BundleInfo;
150
151 #ifdef __APPLE__
152   auto PrintError = [&](CFTypeID TypeID) {
153     CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID));
154     std::string TypeIDStr;
155     errs() << "The Info.plist key \"CFBundleShortVersionString\" is"
156            << "a " << TypeIDCFStr.UTF8(TypeIDStr)
157            << ", but it should be a string in: " << ExePath << ".\n";
158   };
159
160   CFBundle Bundle(ExePath);
161   if (CFStringRef BundleID = Bundle.GetIdentifier()) {
162     CFString::UTF8(BundleID, BundleInfo.IDStr);
163     if (CFTypeRef TypeRef =
164             Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) {
165       CFTypeID TypeID = ::CFGetTypeID(TypeRef);
166       if (TypeID == ::CFStringGetTypeID())
167         CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr);
168       else
169         PrintError(TypeID);
170     }
171     if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey(
172             CFSTR("CFBundleShortVersionString"))) {
173       CFTypeID TypeID = ::CFGetTypeID(TypeRef);
174       if (TypeID == ::CFStringGetTypeID())
175         CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr);
176       else
177         PrintError(TypeID);
178     }
179   }
180 #endif
181
182   return BundleInfo;
183 }
184
185 } // end namespace dsymutil
186 } // end namespace llvm