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