blob: c541645044955cc121af09c1a355faada3855134 [file] [log] [blame]
//===- 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"
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;
// 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.find(C) != StringRef::npos)
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);
}