//===- 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 &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 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 &RelativePath) { SmallString Src = From; SmallString Dst = To; if (auto EC = sys::fs::make_absolute(Src)) return EC; if (auto EC = sys::fs::make_absolute(Dst)) return EC; SmallString 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; // 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 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("not a glob", inconvertibleErrorCode()); llvm::Regex Rule = Regex(RegexString); std::string Error; if (!Rule.isValid(Error)) return make_error(Error, inconvertibleErrorCode()); return std::move(Rule); } Expected llvm::MachO::parseAliasList(std::unique_ptr &Buffer) { SmallVector 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(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; }