
* Have clang always append & pass System/Library/SubFrameworks when determining default sdk search paths. * Teach clang-installapi to traverse there for framework input. * Teach llvm-readtapi that the library files (TBD or binary) in there should be considered private. resolves: rdar://137457006
248 lines
7.6 KiB
C++
248 lines
7.6 KiB
C++
//===- 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;
|
|
}
|