xref: /llvm-project/llvm/lib/TextAPI/Utils.cpp (revision 2d48489cc35ec9bb1c15ff115595e62d67ca8989)
1c6f29dbbSCyndy Ishida //===- Utils.cpp ----------------------------------------------------------===//
2c6f29dbbSCyndy Ishida //
3c6f29dbbSCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c6f29dbbSCyndy Ishida // See https://llvm.org/LICENSE.txt for license information.
5c6f29dbbSCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c6f29dbbSCyndy Ishida //
7c6f29dbbSCyndy Ishida //===----------------------------------------------------------------------===//
8c6f29dbbSCyndy Ishida //
9c6f29dbbSCyndy Ishida // Implements utility functions for TextAPI Darwin operations.
10c6f29dbbSCyndy Ishida //
11c6f29dbbSCyndy Ishida //===----------------------------------------------------------------------===//
12c6f29dbbSCyndy Ishida 
13c6f29dbbSCyndy Ishida #include "llvm/TextAPI/Utils.h"
144c18681aSCyndy Ishida #include "llvm/ADT/StringExtras.h"
154c18681aSCyndy Ishida #include "llvm/TextAPI/TextAPIError.h"
16c6f29dbbSCyndy Ishida 
17c6f29dbbSCyndy Ishida using namespace llvm;
18c6f29dbbSCyndy Ishida using namespace llvm::MachO;
19c6f29dbbSCyndy Ishida 
20c6f29dbbSCyndy Ishida void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path,
21c6f29dbbSCyndy Ishida                                     const Twine &Extension) {
22c6f29dbbSCyndy Ishida   StringRef P(Path.begin(), Path.size());
23c6f29dbbSCyndy Ishida   auto ParentPath = sys::path::parent_path(P);
24c6f29dbbSCyndy Ishida   auto Filename = sys::path::filename(P);
25c6f29dbbSCyndy Ishida 
26c6f29dbbSCyndy Ishida   if (!ParentPath.ends_with(Filename.str() + ".framework")) {
27c6f29dbbSCyndy Ishida     sys::path::replace_extension(Path, Extension);
28c6f29dbbSCyndy Ishida     return;
29c6f29dbbSCyndy Ishida   }
30c6f29dbbSCyndy Ishida   // Framework dylibs do not have a file extension, in those cases the new
31c6f29dbbSCyndy Ishida   // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension:
32c6f29dbbSCyndy Ishida   // "tbd", the result is "Foo.framework/Foo.tbd".
33c6f29dbbSCyndy Ishida   SmallString<8> Storage;
34c6f29dbbSCyndy Ishida   StringRef Ext = Extension.toStringRef(Storage);
35c6f29dbbSCyndy Ishida 
36c6f29dbbSCyndy Ishida   // Append '.' if needed.
37c6f29dbbSCyndy Ishida   if (!Ext.empty() && Ext[0] != '.')
38c6f29dbbSCyndy Ishida     Path.push_back('.');
39c6f29dbbSCyndy Ishida 
40c6f29dbbSCyndy Ishida   // Append extension.
41c6f29dbbSCyndy Ishida   Path.append(Ext.begin(), Ext.end());
42c6f29dbbSCyndy Ishida }
437189219eSCyndy Ishida 
447189219eSCyndy Ishida std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path,
457189219eSCyndy Ishida                                                bool &Result) {
467189219eSCyndy Ishida   Result = false;
477189219eSCyndy Ishida   SmallString<PATH_MAX> Storage;
487189219eSCyndy Ishida   auto P = Path.toNullTerminatedStringRef(Storage);
497189219eSCyndy Ishida   sys::fs::file_status Stat1;
507189219eSCyndy Ishida   auto EC = sys::fs::status(P.data(), Stat1);
517189219eSCyndy Ishida   if (EC == std::errc::too_many_symbolic_link_levels) {
527189219eSCyndy Ishida     Result = true;
537189219eSCyndy Ishida     return {};
547189219eSCyndy Ishida   }
557189219eSCyndy Ishida 
567189219eSCyndy Ishida   if (EC)
577189219eSCyndy Ishida     return EC;
587189219eSCyndy Ishida 
597189219eSCyndy Ishida   StringRef Parent = sys::path::parent_path(P);
607189219eSCyndy Ishida   while (!Parent.empty()) {
617189219eSCyndy Ishida     sys::fs::file_status Stat2;
627189219eSCyndy Ishida     if (auto ec = sys::fs::status(Parent, Stat2))
637189219eSCyndy Ishida       return ec;
647189219eSCyndy Ishida 
657189219eSCyndy Ishida     if (sys::fs::equivalent(Stat1, Stat2)) {
667189219eSCyndy Ishida       Result = true;
677189219eSCyndy Ishida       return {};
687189219eSCyndy Ishida     }
697189219eSCyndy Ishida 
707189219eSCyndy Ishida     Parent = sys::path::parent_path(Parent);
717189219eSCyndy Ishida   }
727189219eSCyndy Ishida   return {};
737189219eSCyndy Ishida }
747189219eSCyndy Ishida 
757189219eSCyndy Ishida std::error_code
767189219eSCyndy Ishida llvm::MachO::make_relative(StringRef From, StringRef To,
777189219eSCyndy Ishida                            SmallVectorImpl<char> &RelativePath) {
787189219eSCyndy Ishida   SmallString<PATH_MAX> Src = From;
797189219eSCyndy Ishida   SmallString<PATH_MAX> Dst = To;
807189219eSCyndy Ishida   if (auto EC = sys::fs::make_absolute(Src))
817189219eSCyndy Ishida     return EC;
827189219eSCyndy Ishida 
837189219eSCyndy Ishida   if (auto EC = sys::fs::make_absolute(Dst))
847189219eSCyndy Ishida     return EC;
857189219eSCyndy Ishida 
867189219eSCyndy Ishida   SmallString<PATH_MAX> Result;
877189219eSCyndy Ishida   Src = sys::path::parent_path(From);
887189219eSCyndy Ishida   auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst),
897189219eSCyndy Ishida        IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst);
907189219eSCyndy Ishida   // Ignore the common part.
917189219eSCyndy Ishida   for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) {
927189219eSCyndy Ishida     if (*IT1 != *IT2)
937189219eSCyndy Ishida       break;
947189219eSCyndy Ishida   }
957189219eSCyndy Ishida 
967189219eSCyndy Ishida   for (; IT1 != IE1; ++IT1)
977189219eSCyndy Ishida     sys::path::append(Result, "../");
987189219eSCyndy Ishida 
997189219eSCyndy Ishida   for (; IT2 != IE2; ++IT2)
1007189219eSCyndy Ishida     sys::path::append(Result, *IT2);
1017189219eSCyndy Ishida 
1027189219eSCyndy Ishida   if (Result.empty())
1037189219eSCyndy Ishida     Result = ".";
1047189219eSCyndy Ishida 
1057189219eSCyndy Ishida   RelativePath.swap(Result);
1067189219eSCyndy Ishida 
1077189219eSCyndy Ishida   return {};
1087189219eSCyndy Ishida }
1097189219eSCyndy Ishida 
1107189219eSCyndy Ishida bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) {
1117189219eSCyndy Ishida   // Remove the iOSSupport and DriverKit prefix to identify public locations.
1127189219eSCyndy Ishida   Path.consume_front(MACCATALYST_PREFIX_PATH);
1137189219eSCyndy Ishida   Path.consume_front(DRIVERKIT_PREFIX_PATH);
1147189219eSCyndy Ishida   // Also /Library/Apple prefix for ROSP.
1157189219eSCyndy Ishida   Path.consume_front("/Library/Apple");
1167189219eSCyndy Ishida 
1177189219eSCyndy Ishida   if (Path.starts_with("/usr/local/lib"))
1187189219eSCyndy Ishida     return true;
1197189219eSCyndy Ishida 
1207189219eSCyndy Ishida   if (Path.starts_with("/System/Library/PrivateFrameworks"))
1217189219eSCyndy Ishida     return true;
1227189219eSCyndy Ishida 
123*2d48489cSCyndy Ishida   if (Path.starts_with("/System/Library/SubFrameworks"))
124*2d48489cSCyndy Ishida     return true;
125*2d48489cSCyndy Ishida 
1267189219eSCyndy Ishida   // Everything in /usr/lib/swift (including sub-directories) are considered
1277189219eSCyndy Ishida   // public.
1287189219eSCyndy Ishida   if (Path.consume_front("/usr/lib/swift/"))
1297189219eSCyndy Ishida     return false;
1307189219eSCyndy Ishida 
1317189219eSCyndy Ishida   // Only libraries directly in /usr/lib are public. All other libraries in
1327189219eSCyndy Ishida   // sub-directories are private.
1337189219eSCyndy Ishida   if (Path.consume_front("/usr/lib/"))
1347189219eSCyndy Ishida     return Path.contains('/');
1357189219eSCyndy Ishida 
1367189219eSCyndy Ishida   // "/System/Library/Frameworks/" is a public location.
1377189219eSCyndy Ishida   if (Path.starts_with("/System/Library/Frameworks/")) {
1387189219eSCyndy Ishida     StringRef Name, Rest;
1397189219eSCyndy Ishida     std::tie(Name, Rest) =
1407189219eSCyndy Ishida         Path.drop_front(sizeof("/System/Library/Frameworks")).split('.');
1417189219eSCyndy Ishida 
1427189219eSCyndy Ishida     // Allow symlinks to top-level frameworks.
1437189219eSCyndy Ishida     if (IsSymLink && Rest == "framework")
1447189219eSCyndy Ishida       return false;
1457189219eSCyndy Ishida 
1467189219eSCyndy Ishida     // Only top level framework are public.
1477189219eSCyndy Ishida     // /System/Library/Frameworks/Foo.framework/Foo ==> true
1487189219eSCyndy Ishida     // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
1497189219eSCyndy Ishida     // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
1507189219eSCyndy Ishida     // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar
1517189219eSCyndy Ishida     // ==> false
1527189219eSCyndy Ishida     // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo
1537189219eSCyndy Ishida     // ==> false
1547189219eSCyndy Ishida     return !(Rest.starts_with("framework/") &&
1557189219eSCyndy Ishida              (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) ||
1567189219eSCyndy Ishida               (IsSymLink && Rest.ends_with("Current"))));
1577189219eSCyndy Ishida   }
1587189219eSCyndy Ishida   return false;
1597189219eSCyndy Ishida }
160487720fcSCyndy Ishida 
161487720fcSCyndy Ishida static StringLiteral RegexMetachars = "()^$|+.[]\\{}";
162487720fcSCyndy Ishida 
163487720fcSCyndy Ishida llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) {
164487720fcSCyndy Ishida   SmallString<128> RegexString("^");
165487720fcSCyndy Ishida   unsigned NumWildcards = 0;
166487720fcSCyndy Ishida   for (unsigned i = 0; i < Glob.size(); ++i) {
167487720fcSCyndy Ishida     char C = Glob[i];
168487720fcSCyndy Ishida     switch (C) {
169487720fcSCyndy Ishida     case '?':
170487720fcSCyndy Ishida       RegexString += '.';
171487720fcSCyndy Ishida       break;
172487720fcSCyndy Ishida     case '*': {
173487720fcSCyndy Ishida       const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr;
174487720fcSCyndy Ishida       NumWildcards = 1;
175487720fcSCyndy Ishida       ++i;
176487720fcSCyndy Ishida       while (i < Glob.size() && Glob[i] == '*') {
177487720fcSCyndy Ishida         ++NumWildcards;
178487720fcSCyndy Ishida         ++i;
179487720fcSCyndy Ishida       }
180487720fcSCyndy Ishida       const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr;
181487720fcSCyndy Ishida 
182487720fcSCyndy Ishida       if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') &&
183487720fcSCyndy Ishida           (NextChar == nullptr || *NextChar == '/')) {
184487720fcSCyndy Ishida         RegexString += "(([^/]*(/|$))*)";
185487720fcSCyndy Ishida       } else
186487720fcSCyndy Ishida         RegexString += "([^/]*)";
187487720fcSCyndy Ishida       break;
188487720fcSCyndy Ishida     }
189487720fcSCyndy Ishida     default:
19089d09373SKazu Hirata       if (RegexMetachars.contains(C))
191487720fcSCyndy Ishida         RegexString.push_back('\\');
192487720fcSCyndy Ishida       RegexString.push_back(C);
193487720fcSCyndy Ishida     }
194487720fcSCyndy Ishida   }
195487720fcSCyndy Ishida   RegexString.push_back('$');
196487720fcSCyndy Ishida   if (NumWildcards == 0)
197487720fcSCyndy Ishida     return make_error<StringError>("not a glob", inconvertibleErrorCode());
198487720fcSCyndy Ishida 
199487720fcSCyndy Ishida   llvm::Regex Rule = Regex(RegexString);
200487720fcSCyndy Ishida   std::string Error;
201487720fcSCyndy Ishida   if (!Rule.isValid(Error))
202487720fcSCyndy Ishida     return make_error<StringError>(Error, inconvertibleErrorCode());
203487720fcSCyndy Ishida 
204487720fcSCyndy Ishida   return std::move(Rule);
205487720fcSCyndy Ishida }
2064c18681aSCyndy Ishida 
2074c18681aSCyndy Ishida Expected<AliasMap>
2084c18681aSCyndy Ishida llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) {
2094c18681aSCyndy Ishida   SmallVector<StringRef, 16> Lines;
2104c18681aSCyndy Ishida   AliasMap Aliases;
2114c18681aSCyndy Ishida   Buffer->getBuffer().split(Lines, "\n", /*MaxSplit=*/-1,
2124c18681aSCyndy Ishida                             /*KeepEmpty=*/false);
2134c18681aSCyndy Ishida   for (const StringRef Line : Lines) {
2144c18681aSCyndy Ishida     StringRef L = Line.trim();
2154c18681aSCyndy Ishida     if (L.empty())
2164c18681aSCyndy Ishida       continue;
2174c18681aSCyndy Ishida     // Skip comments.
2184c18681aSCyndy Ishida     if (L.starts_with("#"))
2194c18681aSCyndy Ishida       continue;
2204c18681aSCyndy Ishida     StringRef Symbol, Remain, Alias;
221d4a01549SJay Foad     // Base symbol is separated by whitespace.
2224c18681aSCyndy Ishida     std::tie(Symbol, Remain) = getToken(L);
2234c18681aSCyndy Ishida     // The Alias symbol ends before a comment or EOL.
2244c18681aSCyndy Ishida     std::tie(Alias, Remain) = getToken(Remain, "#");
2254c18681aSCyndy Ishida     Alias = Alias.trim();
2264c18681aSCyndy Ishida     if (Alias.empty())
2274c18681aSCyndy Ishida       return make_error<TextAPIError>(
2284c18681aSCyndy Ishida           TextAPIError(TextAPIErrorCode::InvalidInputFormat,
2294c18681aSCyndy Ishida                        ("missing alias for: " + Symbol).str()));
2304c18681aSCyndy Ishida     SimpleSymbol AliasSym = parseSymbol(Alias);
2314c18681aSCyndy Ishida     SimpleSymbol BaseSym = parseSymbol(Symbol);
2324c18681aSCyndy Ishida     Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(),
2334c18681aSCyndy Ishida                                                      BaseSym.Kind};
2344c18681aSCyndy Ishida   }
2354c18681aSCyndy Ishida 
2364c18681aSCyndy Ishida   return Aliases;
2374c18681aSCyndy Ishida }
238062f6fe3SCyndy Ishida 
239062f6fe3SCyndy Ishida PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths,
240062f6fe3SCyndy Ishida                                          PlatformType Platform) {
241062f6fe3SCyndy Ishida   PathSeq Result;
242062f6fe3SCyndy Ishida   for (const auto &[Path, CurrP] : Paths) {
243062f6fe3SCyndy Ishida     if (!CurrP.has_value() || CurrP.value() == Platform)
244062f6fe3SCyndy Ishida       Result.push_back(Path);
245062f6fe3SCyndy Ishida   }
246062f6fe3SCyndy Ishida   return Result;
247062f6fe3SCyndy Ishida }
248