1 //===- Utils.cpp ----------------------------------------------------------===// 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 // Implements utility functions for TextAPI Darwin operations. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/TextAPI/Utils.h" 14 15 using namespace llvm; 16 using namespace llvm::MachO; 17 18 void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, 19 const Twine &Extension) { 20 StringRef P(Path.begin(), Path.size()); 21 auto ParentPath = sys::path::parent_path(P); 22 auto Filename = sys::path::filename(P); 23 24 if (!ParentPath.ends_with(Filename.str() + ".framework")) { 25 sys::path::replace_extension(Path, Extension); 26 return; 27 } 28 // Framework dylibs do not have a file extension, in those cases the new 29 // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: 30 // "tbd", the result is "Foo.framework/Foo.tbd". 31 SmallString<8> Storage; 32 StringRef Ext = Extension.toStringRef(Storage); 33 34 // Append '.' if needed. 35 if (!Ext.empty() && Ext[0] != '.') 36 Path.push_back('.'); 37 38 // Append extension. 39 Path.append(Ext.begin(), Ext.end()); 40 } 41 42 std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path, 43 bool &Result) { 44 Result = false; 45 SmallString<PATH_MAX> Storage; 46 auto P = Path.toNullTerminatedStringRef(Storage); 47 sys::fs::file_status Stat1; 48 auto EC = sys::fs::status(P.data(), Stat1); 49 if (EC == std::errc::too_many_symbolic_link_levels) { 50 Result = true; 51 return {}; 52 } 53 54 if (EC) 55 return EC; 56 57 StringRef Parent = sys::path::parent_path(P); 58 while (!Parent.empty()) { 59 sys::fs::file_status Stat2; 60 if (auto ec = sys::fs::status(Parent, Stat2)) 61 return ec; 62 63 if (sys::fs::equivalent(Stat1, Stat2)) { 64 Result = true; 65 return {}; 66 } 67 68 Parent = sys::path::parent_path(Parent); 69 } 70 return {}; 71 } 72 73 std::error_code 74 llvm::MachO::make_relative(StringRef From, StringRef To, 75 SmallVectorImpl<char> &RelativePath) { 76 SmallString<PATH_MAX> Src = From; 77 SmallString<PATH_MAX> Dst = To; 78 if (auto EC = sys::fs::make_absolute(Src)) 79 return EC; 80 81 if (auto EC = sys::fs::make_absolute(Dst)) 82 return EC; 83 84 SmallString<PATH_MAX> Result; 85 Src = sys::path::parent_path(From); 86 auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst), 87 IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst); 88 // Ignore the common part. 89 for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) { 90 if (*IT1 != *IT2) 91 break; 92 } 93 94 for (; IT1 != IE1; ++IT1) 95 sys::path::append(Result, "../"); 96 97 for (; IT2 != IE2; ++IT2) 98 sys::path::append(Result, *IT2); 99 100 if (Result.empty()) 101 Result = "."; 102 103 RelativePath.swap(Result); 104 105 return {}; 106 } 107 108 bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) { 109 // Remove the iOSSupport and DriverKit prefix to identify public locations. 110 Path.consume_front(MACCATALYST_PREFIX_PATH); 111 Path.consume_front(DRIVERKIT_PREFIX_PATH); 112 // Also /Library/Apple prefix for ROSP. 113 Path.consume_front("/Library/Apple"); 114 115 if (Path.starts_with("/usr/local/lib")) 116 return true; 117 118 if (Path.starts_with("/System/Library/PrivateFrameworks")) 119 return true; 120 121 // Everything in /usr/lib/swift (including sub-directories) are considered 122 // public. 123 if (Path.consume_front("/usr/lib/swift/")) 124 return false; 125 126 // Only libraries directly in /usr/lib are public. All other libraries in 127 // sub-directories are private. 128 if (Path.consume_front("/usr/lib/")) 129 return Path.contains('/'); 130 131 // "/System/Library/Frameworks/" is a public location. 132 if (Path.starts_with("/System/Library/Frameworks/")) { 133 StringRef Name, Rest; 134 std::tie(Name, Rest) = 135 Path.drop_front(sizeof("/System/Library/Frameworks")).split('.'); 136 137 // Allow symlinks to top-level frameworks. 138 if (IsSymLink && Rest == "framework") 139 return false; 140 141 // Only top level framework are public. 142 // /System/Library/Frameworks/Foo.framework/Foo ==> true 143 // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true 144 // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false 145 // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar 146 // ==> false 147 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo 148 // ==> false 149 return !(Rest.starts_with("framework/") && 150 (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) || 151 (IsSymLink && Rest.ends_with("Current")))); 152 } 153 return false; 154 } 155 156 static StringLiteral RegexMetachars = "()^$|+.[]\\{}"; 157 158 Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) { 159 SmallString<128> RegexString("^"); 160 unsigned NumWildcards = 0; 161 for (unsigned i = 0; i < Glob.size(); ++i) { 162 char C = Glob[i]; 163 switch (C) { 164 case '?': 165 RegexString += '.'; 166 break; 167 case '*': { 168 const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr; 169 NumWildcards = 1; 170 ++i; 171 while (i < Glob.size() && Glob[i] == '*') { 172 ++NumWildcards; 173 ++i; 174 } 175 const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr; 176 177 if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') && 178 (NextChar == nullptr || *NextChar == '/')) { 179 RegexString += "(([^/]*(/|$))*)"; 180 } else 181 RegexString += "([^/]*)"; 182 break; 183 } 184 default: 185 if (RegexMetachars.find(C) != StringRef::npos) 186 RegexString.push_back('\\'); 187 RegexString.push_back(C); 188 } 189 } 190 RegexString.push_back('$'); 191 if (NumWildcards == 0) 192 return make_error<StringError>("not a glob", inconvertibleErrorCode()); 193 194 auto Rule = Regex(RegexString); 195 std::string Error; 196 if (!Rule.isValid(Error)) 197 return make_error<StringError>(Error, inconvertibleErrorCode()); 198 199 return Rule; 200 } 201