xref: /llvm-project/llvm/lib/TextAPI/Utils.cpp (revision 2d40f179124f874aca4cf1145fdbc42fb8fb17f3)
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 
15 using namespace llvm;
16 using namespace llvm::MachO;
17 
18 void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path,
19                                     const Twine &Extension) {
20   StringRef P(Path.begin(), Path.size());
21   auto ParentPath = sys::path::parent_path(P);
22   auto Filename = sys::path::filename(P);
23 
24   if (!ParentPath.ends_with(Filename.str() + ".framework")) {
25     sys::path::replace_extension(Path, Extension);
26     return;
27   }
28   // Framework dylibs do not have a file extension, in those cases the new
29   // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension:
30   // "tbd", the result is "Foo.framework/Foo.tbd".
31   SmallString<8> Storage;
32   StringRef Ext = Extension.toStringRef(Storage);
33 
34   // Append '.' if needed.
35   if (!Ext.empty() && Ext[0] != '.')
36     Path.push_back('.');
37 
38   // Append extension.
39   Path.append(Ext.begin(), Ext.end());
40 }
41 
42 std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path,
43                                                bool &Result) {
44   Result = false;
45   SmallString<PATH_MAX> Storage;
46   auto P = Path.toNullTerminatedStringRef(Storage);
47   sys::fs::file_status Stat1;
48   auto EC = sys::fs::status(P.data(), Stat1);
49   if (EC == std::errc::too_many_symbolic_link_levels) {
50     Result = true;
51     return {};
52   }
53 
54   if (EC)
55     return EC;
56 
57   StringRef Parent = sys::path::parent_path(P);
58   while (!Parent.empty()) {
59     sys::fs::file_status Stat2;
60     if (auto ec = sys::fs::status(Parent, Stat2))
61       return ec;
62 
63     if (sys::fs::equivalent(Stat1, Stat2)) {
64       Result = true;
65       return {};
66     }
67 
68     Parent = sys::path::parent_path(Parent);
69   }
70   return {};
71 }
72 
73 std::error_code
74 llvm::MachO::make_relative(StringRef From, StringRef To,
75                            SmallVectorImpl<char> &RelativePath) {
76   SmallString<PATH_MAX> Src = From;
77   SmallString<PATH_MAX> Dst = To;
78   if (auto EC = sys::fs::make_absolute(Src))
79     return EC;
80 
81   if (auto EC = sys::fs::make_absolute(Dst))
82     return EC;
83 
84   SmallString<PATH_MAX> Result;
85   Src = sys::path::parent_path(From);
86   auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst),
87        IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst);
88   // Ignore the common part.
89   for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) {
90     if (*IT1 != *IT2)
91       break;
92   }
93 
94   for (; IT1 != IE1; ++IT1)
95     sys::path::append(Result, "../");
96 
97   for (; IT2 != IE2; ++IT2)
98     sys::path::append(Result, *IT2);
99 
100   if (Result.empty())
101     Result = ".";
102 
103   RelativePath.swap(Result);
104 
105   return {};
106 }
107 
108 bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) {
109   // Remove the iOSSupport and DriverKit prefix to identify public locations.
110   Path.consume_front(MACCATALYST_PREFIX_PATH);
111   Path.consume_front(DRIVERKIT_PREFIX_PATH);
112   // Also /Library/Apple prefix for ROSP.
113   Path.consume_front("/Library/Apple");
114 
115   if (Path.starts_with("/usr/local/lib"))
116     return true;
117 
118   if (Path.starts_with("/System/Library/PrivateFrameworks"))
119     return true;
120 
121   // Everything in /usr/lib/swift (including sub-directories) are considered
122   // public.
123   if (Path.consume_front("/usr/lib/swift/"))
124     return false;
125 
126   // Only libraries directly in /usr/lib are public. All other libraries in
127   // sub-directories are private.
128   if (Path.consume_front("/usr/lib/"))
129     return Path.contains('/');
130 
131   // "/System/Library/Frameworks/" is a public location.
132   if (Path.starts_with("/System/Library/Frameworks/")) {
133     StringRef Name, Rest;
134     std::tie(Name, Rest) =
135         Path.drop_front(sizeof("/System/Library/Frameworks")).split('.');
136 
137     // Allow symlinks to top-level frameworks.
138     if (IsSymLink && Rest == "framework")
139       return false;
140 
141     // Only top level framework are public.
142     // /System/Library/Frameworks/Foo.framework/Foo ==> true
143     // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
144     // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
145     // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar
146     // ==> false
147     // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo
148     // ==> false
149     return !(Rest.starts_with("framework/") &&
150              (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) ||
151               (IsSymLink && Rest.ends_with("Current"))));
152   }
153   return false;
154 }
155 
156 static StringLiteral RegexMetachars = "()^$|+.[]\\{}";
157 
158 Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) {
159   SmallString<128> RegexString("^");
160   unsigned NumWildcards = 0;
161   for (unsigned i = 0; i < Glob.size(); ++i) {
162     char C = Glob[i];
163     switch (C) {
164     case '?':
165       RegexString += '.';
166       break;
167     case '*': {
168       const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr;
169       NumWildcards = 1;
170       ++i;
171       while (i < Glob.size() && Glob[i] == '*') {
172         ++NumWildcards;
173         ++i;
174       }
175       const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr;
176 
177       if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') &&
178           (NextChar == nullptr || *NextChar == '/')) {
179         RegexString += "(([^/]*(/|$))*)";
180       } else
181         RegexString += "([^/]*)";
182       break;
183     }
184     default:
185       if (RegexMetachars.find(C) != StringRef::npos)
186         RegexString.push_back('\\');
187       RegexString.push_back(C);
188     }
189   }
190   RegexString.push_back('$');
191   if (NumWildcards == 0)
192     return make_error<StringError>("not a glob", inconvertibleErrorCode());
193 
194   auto Rule = Regex(RegexString);
195   std::string Error;
196   if (!Rule.isValid(Error))
197     return make_error<StringError>(Error, inconvertibleErrorCode());
198 
199   return Rule;
200 }
201