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 | #include "llvm/ADT/StringExtras.h" |
15 | #include "llvm/TextAPI/TextAPIError.h" |
16 | |
17 | using namespace llvm; |
18 | using namespace llvm::MachO; |
19 | |
20 | void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, |
21 | const Twine &Extension) { |
22 | StringRef P(Path.begin(), Path.size()); |
23 | auto ParentPath = sys::path::parent_path(path: P); |
24 | auto Filename = sys::path::filename(path: P); |
25 | |
26 | if (!ParentPath.ends_with(Suffix: Filename.str() + ".framework" )) { |
27 | sys::path::replace_extension(path&: Path, extension: Extension); |
28 | return; |
29 | } |
30 | // Framework dylibs do not have a file extension, in those cases the new |
31 | // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: |
32 | // "tbd", the result is "Foo.framework/Foo.tbd". |
33 | SmallString<8> Storage; |
34 | StringRef Ext = Extension.toStringRef(Out&: Storage); |
35 | |
36 | // Append '.' if needed. |
37 | if (!Ext.empty() && Ext[0] != '.') |
38 | Path.push_back(Elt: '.'); |
39 | |
40 | // Append extension. |
41 | Path.append(in_start: Ext.begin(), in_end: Ext.end()); |
42 | } |
43 | |
44 | std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path, |
45 | bool &Result) { |
46 | Result = false; |
47 | SmallString<PATH_MAX> Storage; |
48 | auto P = Path.toNullTerminatedStringRef(Out&: Storage); |
49 | sys::fs::file_status Stat1; |
50 | auto EC = sys::fs::status(path: P.data(), result&: Stat1); |
51 | if (EC == std::errc::too_many_symbolic_link_levels) { |
52 | Result = true; |
53 | return {}; |
54 | } |
55 | |
56 | if (EC) |
57 | return EC; |
58 | |
59 | StringRef Parent = sys::path::parent_path(path: P); |
60 | while (!Parent.empty()) { |
61 | sys::fs::file_status Stat2; |
62 | if (auto ec = sys::fs::status(path: Parent, result&: Stat2)) |
63 | return ec; |
64 | |
65 | if (sys::fs::equivalent(A: Stat1, B: Stat2)) { |
66 | Result = true; |
67 | return {}; |
68 | } |
69 | |
70 | Parent = sys::path::parent_path(path: Parent); |
71 | } |
72 | return {}; |
73 | } |
74 | |
75 | std::error_code |
76 | llvm::MachO::make_relative(StringRef From, StringRef To, |
77 | SmallVectorImpl<char> &RelativePath) { |
78 | SmallString<PATH_MAX> Src = From; |
79 | SmallString<PATH_MAX> Dst = To; |
80 | if (auto EC = sys::fs::make_absolute(path&: Src)) |
81 | return EC; |
82 | |
83 | if (auto EC = sys::fs::make_absolute(path&: Dst)) |
84 | return EC; |
85 | |
86 | SmallString<PATH_MAX> Result; |
87 | Src = sys::path::parent_path(path: From); |
88 | auto IT1 = sys::path::begin(path: Src), IT2 = sys::path::begin(path: Dst), |
89 | IE1 = sys::path::end(path: Src), IE2 = sys::path::end(path: Dst); |
90 | // Ignore the common part. |
91 | for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) { |
92 | if (*IT1 != *IT2) |
93 | break; |
94 | } |
95 | |
96 | for (; IT1 != IE1; ++IT1) |
97 | sys::path::append(path&: Result, a: "../" ); |
98 | |
99 | for (; IT2 != IE2; ++IT2) |
100 | sys::path::append(path&: Result, a: *IT2); |
101 | |
102 | if (Result.empty()) |
103 | Result = "." ; |
104 | |
105 | RelativePath.swap(RHS&: Result); |
106 | |
107 | return {}; |
108 | } |
109 | |
110 | bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) { |
111 | // Remove the iOSSupport and DriverKit prefix to identify public locations. |
112 | Path.consume_front(MACCATALYST_PREFIX_PATH); |
113 | Path.consume_front(DRIVERKIT_PREFIX_PATH); |
114 | // Also /Library/Apple prefix for ROSP. |
115 | Path.consume_front(Prefix: "/Library/Apple" ); |
116 | |
117 | if (Path.starts_with(Prefix: "/usr/local/lib" )) |
118 | return true; |
119 | |
120 | if (Path.starts_with(Prefix: "/System/Library/PrivateFrameworks" )) |
121 | return true; |
122 | |
123 | // Everything in /usr/lib/swift (including sub-directories) are considered |
124 | // public. |
125 | if (Path.consume_front(Prefix: "/usr/lib/swift/" )) |
126 | return false; |
127 | |
128 | // Only libraries directly in /usr/lib are public. All other libraries in |
129 | // sub-directories are private. |
130 | if (Path.consume_front(Prefix: "/usr/lib/" )) |
131 | return Path.contains(C: '/'); |
132 | |
133 | // "/System/Library/Frameworks/" is a public location. |
134 | if (Path.starts_with(Prefix: "/System/Library/Frameworks/" )) { |
135 | StringRef Name, Rest; |
136 | std::tie(args&: Name, args&: Rest) = |
137 | Path.drop_front(N: sizeof("/System/Library/Frameworks" )).split(Separator: '.'); |
138 | |
139 | // Allow symlinks to top-level frameworks. |
140 | if (IsSymLink && Rest == "framework" ) |
141 | return false; |
142 | |
143 | // Only top level framework are public. |
144 | // /System/Library/Frameworks/Foo.framework/Foo ==> true |
145 | // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true |
146 | // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false |
147 | // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar |
148 | // ==> false |
149 | // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo |
150 | // ==> false |
151 | return !(Rest.starts_with(Prefix: "framework/" ) && |
152 | (Rest.ends_with(Suffix: Name) || Rest.ends_with(Suffix: (Name + ".tbd" ).str()) || |
153 | (IsSymLink && Rest.ends_with(Suffix: "Current" )))); |
154 | } |
155 | return false; |
156 | } |
157 | |
158 | static StringLiteral RegexMetachars = "()^$|+.[]\\{}" ; |
159 | |
160 | llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) { |
161 | SmallString<128> RegexString("^" ); |
162 | unsigned NumWildcards = 0; |
163 | for (unsigned i = 0; i < Glob.size(); ++i) { |
164 | char C = Glob[i]; |
165 | switch (C) { |
166 | case '?': |
167 | RegexString += '.'; |
168 | break; |
169 | case '*': { |
170 | const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr; |
171 | NumWildcards = 1; |
172 | ++i; |
173 | while (i < Glob.size() && Glob[i] == '*') { |
174 | ++NumWildcards; |
175 | ++i; |
176 | } |
177 | const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr; |
178 | |
179 | if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') && |
180 | (NextChar == nullptr || *NextChar == '/')) { |
181 | RegexString += "(([^/]*(/|$))*)" ; |
182 | } else |
183 | RegexString += "([^/]*)" ; |
184 | break; |
185 | } |
186 | default: |
187 | if (RegexMetachars.contains(C)) |
188 | RegexString.push_back(Elt: '\\'); |
189 | RegexString.push_back(Elt: C); |
190 | } |
191 | } |
192 | RegexString.push_back(Elt: '$'); |
193 | if (NumWildcards == 0) |
194 | return make_error<StringError>(Args: "not a glob" , Args: inconvertibleErrorCode()); |
195 | |
196 | llvm::Regex Rule = Regex(RegexString); |
197 | std::string Error; |
198 | if (!Rule.isValid(Error)) |
199 | return make_error<StringError>(Args&: Error, Args: inconvertibleErrorCode()); |
200 | |
201 | return std::move(Rule); |
202 | } |
203 | |
204 | Expected<AliasMap> |
205 | llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) { |
206 | SmallVector<StringRef, 16> Lines; |
207 | AliasMap Aliases; |
208 | Buffer->getBuffer().split(A&: Lines, Separator: "\n" , /*MaxSplit=*/-1, |
209 | /*KeepEmpty=*/false); |
210 | for (const StringRef Line : Lines) { |
211 | StringRef L = Line.trim(); |
212 | if (L.empty()) |
213 | continue; |
214 | // Skip comments. |
215 | if (L.starts_with(Prefix: "#" )) |
216 | continue; |
217 | StringRef Symbol, Remain, Alias; |
218 | // Base symbol is separated by whitespace. |
219 | std::tie(args&: Symbol, args&: Remain) = getToken(Source: L); |
220 | // The Alias symbol ends before a comment or EOL. |
221 | std::tie(args&: Alias, args&: Remain) = getToken(Source: Remain, Delimiters: "#" ); |
222 | Alias = Alias.trim(); |
223 | if (Alias.empty()) |
224 | return make_error<TextAPIError>( |
225 | Args: TextAPIError(TextAPIErrorCode::InvalidInputFormat, |
226 | ("missing alias for: " + Symbol).str())); |
227 | SimpleSymbol AliasSym = parseSymbol(SymName: Alias); |
228 | SimpleSymbol BaseSym = parseSymbol(SymName: Symbol); |
229 | Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(), |
230 | BaseSym.Kind}; |
231 | } |
232 | |
233 | return Aliases; |
234 | } |
235 | |
236 | PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths, |
237 | PlatformType Platform) { |
238 | PathSeq Result; |
239 | for (const auto &[Path, CurrP] : Paths) { |
240 | if (!CurrP.has_value() || CurrP.value() == Platform) |
241 | Result.push_back(x: Path); |
242 | } |
243 | return Result; |
244 | } |
245 | |