xref: /freebsd/contrib/llvm-project/llvm/lib/TextAPI/Utils.cpp (revision 3d9fd9fcb432750f3716b28f6ccb0104cd9d351a)
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  
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  
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
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  
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  
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>
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  
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