1 //===- Utils.cpp ----------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Implements utility functions for TextAPI Darwin operations.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/TextAPI/Utils.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/TextAPI/TextAPIError.h"
18 using namespace llvm::MachO
;
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
);
26 if (!ParentPath
.ends_with(Filename
.str() + ".framework")) {
27 sys::path::replace_extension(Path
, Extension
);
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
);
36 // Append '.' if needed.
37 if (!Ext
.empty() && Ext
[0] != '.')
41 Path
.append(Ext
.begin(), Ext
.end());
44 std::error_code
llvm::MachO::shouldSkipSymLink(const Twine
&Path
,
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
) {
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
))
65 if (sys::fs::equivalent(Stat1
, Stat2
)) {
70 Parent
= sys::path::parent_path(Parent
);
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
))
83 if (auto EC
= sys::fs::make_absolute(Dst
))
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
) {
96 for (; IT1
!= IE1
; ++IT1
)
97 sys::path::append(Result
, "../");
99 for (; IT2
!= IE2
; ++IT2
)
100 sys::path::append(Result
, *IT2
);
105 RelativePath
.swap(Result
);
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");
117 if (Path
.starts_with("/usr/local/lib"))
120 if (Path
.starts_with("/System/Library/PrivateFrameworks"))
123 // Everything in /usr/lib/swift (including sub-directories) are considered
125 if (Path
.consume_front("/usr/lib/swift/"))
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('/');
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('.');
139 // Allow symlinks to top-level frameworks.
140 if (IsSymLink
&& Rest
== "framework")
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
149 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo
151 return !(Rest
.starts_with("framework/") &&
152 (Rest
.ends_with(Name
) || Rest
.ends_with((Name
+ ".tbd").str()) ||
153 (IsSymLink
&& Rest
.ends_with("Current"))));
158 static StringLiteral RegexMetachars
= "()^$|+.[]\\{}";
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
) {
170 const char *PrevChar
= i
> 0 ? Glob
.data() + i
- 1 : nullptr;
173 while (i
< Glob
.size() && Glob
[i
] == '*') {
177 const char *NextChar
= i
< Glob
.size() ? Glob
.data() + i
: nullptr;
179 if ((NumWildcards
> 1) && (PrevChar
== nullptr || *PrevChar
== '/') &&
180 (NextChar
== nullptr || *NextChar
== '/')) {
181 RegexString
+= "(([^/]*(/|$))*)";
183 RegexString
+= "([^/]*)";
187 if (RegexMetachars
.find(C
) != StringRef::npos
)
188 RegexString
.push_back('\\');
189 RegexString
.push_back(C
);
192 RegexString
.push_back('$');
193 if (NumWildcards
== 0)
194 return make_error
<StringError
>("not a glob", inconvertibleErrorCode());
196 llvm::Regex Rule
= Regex(RegexString
);
198 if (!Rule
.isValid(Error
))
199 return make_error
<StringError
>(Error
, inconvertibleErrorCode());
201 return std::move(Rule
);
205 llvm::MachO::parseAliasList(std::unique_ptr
<llvm::MemoryBuffer
> &Buffer
) {
206 SmallVector
<StringRef
, 16> Lines
;
208 Buffer
->getBuffer().split(Lines
, "\n", /*MaxSplit=*/-1,
209 /*KeepEmpty=*/false);
210 for (const StringRef Line
: Lines
) {
211 StringRef L
= Line
.trim();
215 if (L
.starts_with("#"))
217 StringRef Symbol
, Remain
, Alias
;
218 // Base symbol is seperated 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();
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(),