| //===- Utils.cpp ----------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Implements utility functions for TextAPI Darwin operations. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/TextAPI/Utils.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/TextAPI/TextAPIError.h" | |
| using namespace llvm; | |
| using namespace llvm::MachO; | |
| void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, | |
| const Twine &Extension) { | |
| StringRef P(Path.begin(), Path.size()); | |
| auto ParentPath = sys::path::parent_path(P); | |
| auto Filename = sys::path::filename(P); | |
| if (!ParentPath.ends_with(Filename.str() + ".framework")) { | |
| sys::path::replace_extension(Path, Extension); | |
| return; | |
| } | |
| // Framework dylibs do not have a file extension, in those cases the new | |
| // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: | |
| // "tbd", the result is "Foo.framework/Foo.tbd". | |
| SmallString<8> Storage; | |
| StringRef Ext = Extension.toStringRef(Storage); | |
| // Append '.' if needed. | |
| if (!Ext.empty() && Ext[0] != '.') | |
| Path.push_back('.'); | |
| // Append extension. | |
| Path.append(Ext.begin(), Ext.end()); | |
| } | |
| std::error_code llvm::MachO::shouldSkipSymLink(const Twine &Path, | |
| bool &Result) { | |
| Result = false; | |
| SmallString<PATH_MAX> Storage; | |
| auto P = Path.toNullTerminatedStringRef(Storage); | |
| sys::fs::file_status Stat1; | |
| auto EC = sys::fs::status(P.data(), Stat1); | |
| if (EC == std::errc::too_many_symbolic_link_levels) { | |
| Result = true; | |
| return {}; | |
| } | |
| if (EC) | |
| return EC; | |
| StringRef Parent = sys::path::parent_path(P); | |
| while (!Parent.empty()) { | |
| sys::fs::file_status Stat2; | |
| if (auto ec = sys::fs::status(Parent, Stat2)) | |
| return ec; | |
| if (sys::fs::equivalent(Stat1, Stat2)) { | |
| Result = true; | |
| return {}; | |
| } | |
| Parent = sys::path::parent_path(Parent); | |
| } | |
| return {}; | |
| } | |
| std::error_code | |
| llvm::MachO::make_relative(StringRef From, StringRef To, | |
| SmallVectorImpl<char> &RelativePath) { | |
| SmallString<PATH_MAX> Src = From; | |
| SmallString<PATH_MAX> Dst = To; | |
| if (auto EC = sys::fs::make_absolute(Src)) | |
| return EC; | |
| if (auto EC = sys::fs::make_absolute(Dst)) | |
| return EC; | |
| SmallString<PATH_MAX> Result; | |
| Src = sys::path::parent_path(From); | |
| auto IT1 = sys::path::begin(Src), IT2 = sys::path::begin(Dst), | |
| IE1 = sys::path::end(Src), IE2 = sys::path::end(Dst); | |
| // Ignore the common part. | |
| for (; IT1 != IE1 && IT2 != IE2; ++IT1, ++IT2) { | |
| if (*IT1 != *IT2) | |
| break; | |
| } | |
| for (; IT1 != IE1; ++IT1) | |
| sys::path::append(Result, "../"); | |
| for (; IT2 != IE2; ++IT2) | |
| sys::path::append(Result, *IT2); | |
| if (Result.empty()) | |
| Result = "."; | |
| RelativePath.swap(Result); | |
| return {}; | |
| } | |
| bool llvm::MachO::isPrivateLibrary(StringRef Path, bool IsSymLink) { | |
| // Remove the iOSSupport and DriverKit prefix to identify public locations. | |
| Path.consume_front(MACCATALYST_PREFIX_PATH); | |
| Path.consume_front(DRIVERKIT_PREFIX_PATH); | |
| // Also /Library/Apple prefix for ROSP. | |
| Path.consume_front("/Library/Apple"); | |
| if (Path.starts_with("/usr/local/lib")) | |
| return true; | |
| if (Path.starts_with("/System/Library/PrivateFrameworks")) | |
| return true; | |
| if (Path.starts_with("/System/Library/SubFrameworks")) | |
| return true; | |
| // Everything in /usr/lib/swift (including sub-directories) are considered | |
| // public. | |
| if (Path.consume_front("/usr/lib/swift/")) | |
| return false; | |
| // Only libraries directly in /usr/lib are public. All other libraries in | |
| // sub-directories are private. | |
| if (Path.consume_front("/usr/lib/")) | |
| return Path.contains('/'); | |
| // "/System/Library/Frameworks/" is a public location. | |
| if (Path.starts_with("/System/Library/Frameworks/")) { | |
| StringRef Name, Rest; | |
| std::tie(Name, Rest) = | |
| Path.drop_front(sizeof("/System/Library/Frameworks")).split('.'); | |
| // Allow symlinks to top-level frameworks. | |
| if (IsSymLink && Rest == "framework") | |
| return false; | |
| // Only top level framework are public. | |
| // /System/Library/Frameworks/Foo.framework/Foo ==> true | |
| // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true | |
| // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false | |
| // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar | |
| // ==> false | |
| // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo | |
| // ==> false | |
| return !(Rest.starts_with("framework/") && | |
| (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) || | |
| (IsSymLink && Rest.ends_with("Current")))); | |
| } | |
| return false; | |
| } | |
| static StringLiteral RegexMetachars = "()^$|+.[]\\{}"; | |
| llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) { | |
| SmallString<128> RegexString("^"); | |
| unsigned NumWildcards = 0; | |
| for (unsigned i = 0; i < Glob.size(); ++i) { | |
| char C = Glob[i]; | |
| switch (C) { | |
| case '?': | |
| RegexString += '.'; | |
| break; | |
| case '*': { | |
| const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr; | |
| NumWildcards = 1; | |
| ++i; | |
| while (i < Glob.size() && Glob[i] == '*') { | |
| ++NumWildcards; | |
| ++i; | |
| } | |
| const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr; | |
| if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') && | |
| (NextChar == nullptr || *NextChar == '/')) { | |
| RegexString += "(([^/]*(/|$))*)"; | |
| } else | |
| RegexString += "([^/]*)"; | |
| break; | |
| } | |
| default: | |
| if (RegexMetachars.contains(C)) | |
| RegexString.push_back('\\'); | |
| RegexString.push_back(C); | |
| } | |
| } | |
| RegexString.push_back('$'); | |
| if (NumWildcards == 0) | |
| return make_error<StringError>("not a glob", inconvertibleErrorCode()); | |
| llvm::Regex Rule = Regex(RegexString); | |
| std::string Error; | |
| if (!Rule.isValid(Error)) | |
| return make_error<StringError>(Error, inconvertibleErrorCode()); | |
| return std::move(Rule); | |
| } | |
| Expected<AliasMap> | |
| llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) { | |
| SmallVector<StringRef, 16> Lines; | |
| AliasMap Aliases; | |
| Buffer->getBuffer().split(Lines, "\n", /*MaxSplit=*/-1, | |
| /*KeepEmpty=*/false); | |
| for (const StringRef Line : Lines) { | |
| StringRef L = Line.trim(); | |
| if (L.empty()) | |
| continue; | |
| // Skip comments. | |
| if (L.starts_with("#")) | |
| continue; | |
| StringRef Symbol, Remain, Alias; | |
| // Base symbol is separated by whitespace. | |
| std::tie(Symbol, Remain) = getToken(L); | |
| // The Alias symbol ends before a comment or EOL. | |
| std::tie(Alias, Remain) = getToken(Remain, "#"); | |
| Alias = Alias.trim(); | |
| if (Alias.empty()) | |
| return make_error<TextAPIError>( | |
| TextAPIError(TextAPIErrorCode::InvalidInputFormat, | |
| ("missing alias for: " + Symbol).str())); | |
| SimpleSymbol AliasSym = parseSymbol(Alias); | |
| SimpleSymbol BaseSym = parseSymbol(Symbol); | |
| Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(), | |
| BaseSym.Kind}; | |
| } | |
| return Aliases; | |
| } | |
| PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths, | |
| PlatformType Platform) { | |
| PathSeq Result; | |
| for (const auto &[Path, CurrP] : Paths) { | |
| if (!CurrP.has_value() || CurrP.value() == Platform) | |
| Result.push_back(Path); | |
| } | |
| return Result; | |
| } |