248 lines · cpp
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-exception6//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 17using namespace llvm;18using namespace llvm::MachO;19 20void 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 new31 // 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 44std::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 75std::error_code76llvm::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 110bool 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 if (Path.starts_with("/System/Library/SubFrameworks"))124 return true;125 126 // Everything in /usr/lib/swift (including sub-directories) are considered127 // public.128 if (Path.consume_front("/usr/lib/swift/"))129 return false;130 131 // Only libraries directly in /usr/lib are public. All other libraries in132 // sub-directories are private.133 if (Path.consume_front("/usr/lib/"))134 return Path.contains('/');135 136 // "/System/Library/Frameworks/" is a public location.137 if (Path.starts_with("/System/Library/Frameworks/")) {138 StringRef Name, Rest;139 std::tie(Name, Rest) =140 Path.drop_front(sizeof("/System/Library/Frameworks")).split('.');141 142 // Allow symlinks to top-level frameworks.143 if (IsSymLink && Rest == "framework")144 return false;145 146 // Only top level framework are public.147 // /System/Library/Frameworks/Foo.framework/Foo ==> true148 // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true149 // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false150 // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar151 // ==> false152 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo153 // ==> false154 return !(Rest.starts_with("framework/") &&155 (Rest.ends_with(Name) || Rest.ends_with((Name + ".tbd").str()) ||156 (IsSymLink && Rest.ends_with("Current"))));157 }158 return false;159}160 161static StringLiteral RegexMetachars = "()^$|+.[]\\{}";162 163llvm::Expected<Regex> llvm::MachO::createRegexFromGlob(StringRef Glob) {164 SmallString<128> RegexString("^");165 unsigned NumWildcards = 0;166 for (unsigned i = 0; i < Glob.size(); ++i) {167 char C = Glob[i];168 switch (C) {169 case '?':170 RegexString += '.';171 break;172 case '*': {173 const char *PrevChar = i > 0 ? Glob.data() + i - 1 : nullptr;174 NumWildcards = 1;175 ++i;176 while (i < Glob.size() && Glob[i] == '*') {177 ++NumWildcards;178 ++i;179 }180 const char *NextChar = i < Glob.size() ? Glob.data() + i : nullptr;181 182 if ((NumWildcards > 1) && (PrevChar == nullptr || *PrevChar == '/') &&183 (NextChar == nullptr || *NextChar == '/')) {184 RegexString += "(([^/]*(/|$))*)";185 } else186 RegexString += "([^/]*)";187 break;188 }189 default:190 if (RegexMetachars.contains(C))191 RegexString.push_back('\\');192 RegexString.push_back(C);193 }194 }195 RegexString.push_back('$');196 if (NumWildcards == 0)197 return make_error<StringError>("not a glob", inconvertibleErrorCode());198 199 llvm::Regex Rule = Regex(RegexString);200 std::string Error;201 if (!Rule.isValid(Error))202 return make_error<StringError>(Error, inconvertibleErrorCode());203 204 return std::move(Rule);205}206 207Expected<AliasMap>208llvm::MachO::parseAliasList(std::unique_ptr<llvm::MemoryBuffer> &Buffer) {209 SmallVector<StringRef, 16> Lines;210 AliasMap Aliases;211 Buffer->getBuffer().split(Lines, "\n", /*MaxSplit=*/-1,212 /*KeepEmpty=*/false);213 for (const StringRef Line : Lines) {214 StringRef L = Line.trim();215 if (L.empty())216 continue;217 // Skip comments.218 if (L.starts_with("#"))219 continue;220 StringRef Symbol, Remain, Alias;221 // Base symbol is separated by whitespace.222 std::tie(Symbol, Remain) = getToken(L);223 // The Alias symbol ends before a comment or EOL.224 std::tie(Alias, Remain) = getToken(Remain, "#");225 Alias = Alias.trim();226 if (Alias.empty())227 return make_error<TextAPIError>(228 TextAPIError(TextAPIErrorCode::InvalidInputFormat,229 ("missing alias for: " + Symbol).str()));230 SimpleSymbol AliasSym = parseSymbol(Alias);231 SimpleSymbol BaseSym = parseSymbol(Symbol);232 Aliases[{AliasSym.Name.str(), AliasSym.Kind}] = {BaseSym.Name.str(),233 BaseSym.Kind};234 }235 236 return Aliases;237}238 239PathSeq llvm::MachO::getPathsForPlatform(const PathToPlatformSeq &Paths,240 PlatformType Platform) {241 PathSeq Result;242 for (const auto &[Path, CurrP] : Paths) {243 if (!CurrP.has_value() || CurrP.value() == Platform)244 Result.push_back(Path);245 }246 return Result;247}248