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