xref: /freebsd/contrib/llvm-project/llvm/lib/TextAPI/Utils.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
replace_extension(SmallVectorImpl<char> & Path,const Twine & Extension)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(P);
24   auto Filename = sys::path::filename(P);
25 
26   if (!ParentPath.ends_with(Filename.str() + ".framework")) {
27     sys::path::replace_extension(Path, 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(Storage);
35 
36   // Append '.' if needed.
37   if (!Ext.empty() && Ext[0] != '.')
38     Path.push_back('.');
39 
40   // Append extension.
41   Path.append(Ext.begin(), Ext.end());
42 }
43 
shouldSkipSymLink(const Twine & Path,bool & Result)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(Storage);
49   sys::fs::file_status Stat1;
50   auto EC = sys::fs::status(P.data(), 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(P);
60   while (!Parent.empty()) {
61     sys::fs::file_status Stat2;
62     if (auto ec = sys::fs::status(Parent, Stat2))
63       return ec;
64 
65     if (sys::fs::equivalent(Stat1, Stat2)) {
66       Result = true;
67       return {};
68     }
69 
70     Parent = sys::path::parent_path(Parent);
71   }
72   return {};
73 }
74 
75 std::error_code
make_relative(StringRef From,StringRef To,SmallVectorImpl<char> & RelativePath)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(Src))
81     return EC;
82 
83   if (auto EC = sys::fs::make_absolute(Dst))
84     return EC;
85 
86   SmallString<PATH_MAX> Result;
87   Src = sys::path::parent_path(From);
88   auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst),
89        IE1 = sys::path::end(Src), IE2 = sys::path::end(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(Result, "../");
98 
99   for (; IT2 != IE2; ++IT2)
100     sys::path::append(Result, *IT2);
101 
102   if (Result.empty())
103     Result = ".";
104 
105   RelativePath.swap(Result);
106 
107   return {};
108 }
109 
isPrivateLibrary(StringRef Path,bool IsSymLink)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("/Library/Apple");
116 
117   if (Path.starts_with("/usr/local/lib"))
118     return true;
119 
120   if (Path.starts_with("/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("/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("/usr/lib/"))
131     return Path.contains('/');
132 
133   // "/System/Library/Frameworks/" is a public location.
134   if (Path.starts_with("/System/Library/Frameworks/")) {
135     StringRef Name, Rest;
136     std::tie(Name, Rest) =
137         Path.drop_front(sizeof("/System/Library/Frameworks")).split('.');
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("framework/") &&
152              (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) ||
153               (IsSymLink && Rest.ends_with("Current"))));
154   }
155   return false;
156 }
157 
158 static StringLiteral RegexMetachars = "()^$|+.[]\\{}";
159 
createRegexFromGlob(StringRef Glob)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('\\');
189       RegexString.push_back(C);
190     }
191   }
192   RegexString.push_back('$');
193   if (NumWildcards == 0)
194     return make_error<StringError>("not a glob", inconvertibleErrorCode());
195 
196   llvm::Regex Rule = Regex(RegexString);
197   std::string Error;
198   if (!Rule.isValid(Error))
199     return make_error<StringError>(Error, inconvertibleErrorCode());
200 
201   return std::move(Rule);
202 }
203 
204 Expected<AliasMap>
parseAliasList(std::unique_ptr<llvm::MemoryBuffer> & Buffer)205 llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) {
206   SmallVector<StringRef, 16> Lines;
207   AliasMap Aliases;
208   Buffer->getBuffer().split(Lines, "\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("#"))
216       continue;
217     StringRef Symbol, Remain, Alias;
218     // Base symbol is separated by whitespace.
219     std::tie(Symbol, Remain) = getToken(L);
220     // The Alias symbol ends before a comment or EOL.
221     std::tie(Alias, Remain) = getToken(Remain, "#");
222     Alias = Alias.trim();
223     if (Alias.empty())
224       return make_error<TextAPIError>(
225           TextAPIError(TextAPIErrorCode::InvalidInputFormat,
226                        ("missing alias for: " + Symbol).str()));
227     SimpleSymbol AliasSym = parseSymbol(Alias);
228     SimpleSymbol BaseSym = parseSymbol(Symbol);
229     Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(),
230                                                      BaseSym.Kind};
231   }
232 
233   return Aliases;
234 }
235 
getPathsForPlatform(const PathToPlatformSeq & Paths,PlatformType Platform)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(Path);
242   }
243   return Result;
244 }
245