cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
433 lines
14 KiB
C++
433 lines
14 KiB
C++
//===- InterfaceFile.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 the Interface File.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/TextAPI/InterfaceFile.h"
|
|
#include "llvm/TextAPI/RecordsSlice.h"
|
|
#include "llvm/TextAPI/TextAPIError.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::MachO;
|
|
|
|
void InterfaceFileRef::addTarget(const Target &Target) {
|
|
addEntry(Targets, Target);
|
|
}
|
|
|
|
void InterfaceFile::addAllowableClient(StringRef InstallName,
|
|
const Target &Target) {
|
|
if (InstallName.empty())
|
|
return;
|
|
auto Client = addEntry(AllowableClients, InstallName);
|
|
Client->addTarget(Target);
|
|
}
|
|
|
|
void InterfaceFile::addReexportedLibrary(StringRef InstallName,
|
|
const Target &Target) {
|
|
if (InstallName.empty())
|
|
return;
|
|
auto Lib = addEntry(ReexportedLibraries, InstallName);
|
|
Lib->addTarget(Target);
|
|
}
|
|
|
|
void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {
|
|
if (Parent.empty())
|
|
return;
|
|
auto Iter = lower_bound(ParentUmbrellas, Target_,
|
|
[](const std::pair<Target, std::string> &LHS,
|
|
Target RHS) { return LHS.first < RHS; });
|
|
|
|
if ((Iter != ParentUmbrellas.end()) && !(Target_ < Iter->first)) {
|
|
Iter->second = std::string(Parent);
|
|
return;
|
|
}
|
|
|
|
ParentUmbrellas.emplace(Iter, Target_, std::string(Parent));
|
|
}
|
|
|
|
void InterfaceFile::addRPath(StringRef RPath, const Target &InputTarget) {
|
|
if (RPath.empty())
|
|
return;
|
|
using RPathEntryT = const std::pair<Target, std::string>;
|
|
RPathEntryT Entry(InputTarget, RPath);
|
|
|
|
if (is_contained(RPaths, Entry))
|
|
return;
|
|
|
|
RPaths.emplace_back(Entry);
|
|
}
|
|
|
|
void InterfaceFile::addTarget(const Target &Target) {
|
|
addEntry(Targets, Target);
|
|
}
|
|
|
|
InterfaceFile::const_filtered_target_range
|
|
InterfaceFile::targets(ArchitectureSet Archs) const {
|
|
std::function<bool(const Target &)> fn = [Archs](const Target &Target_) {
|
|
return Archs.has(Target_.Arch);
|
|
};
|
|
return make_filter_range(Targets, fn);
|
|
}
|
|
|
|
void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
|
|
auto Pos = llvm::lower_bound(Documents, Document,
|
|
[](const std::shared_ptr<InterfaceFile> &LHS,
|
|
const std::shared_ptr<InterfaceFile> &RHS) {
|
|
return LHS->InstallName < RHS->InstallName;
|
|
});
|
|
assert((Pos == Documents.end() ||
|
|
(*Pos)->InstallName != Document->InstallName) &&
|
|
"Unexpected duplicate document added");
|
|
Document->Parent = this;
|
|
Documents.insert(Pos, Document);
|
|
}
|
|
|
|
void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,
|
|
bool Overwrite) {
|
|
auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {
|
|
auto It = lower_bound(
|
|
Documents, Reexport->getInstallName(),
|
|
[](std::shared_ptr<InterfaceFile> &Lhs, const StringRef Rhs) {
|
|
return Lhs->getInstallName() < Rhs;
|
|
});
|
|
|
|
if (Overwrite && It != Documents.end() &&
|
|
Reexport->getInstallName() == (*It)->getInstallName()) {
|
|
llvm::replace(Documents, *It, std::move(Reexport));
|
|
return;
|
|
}
|
|
|
|
if ((It != Documents.end()) &&
|
|
!(Reexport->getInstallName() < (*It)->getInstallName()))
|
|
return;
|
|
|
|
Documents.emplace(It, std::move(Reexport));
|
|
};
|
|
for (auto Doc : Library->documents())
|
|
AddFwk(std::move(Doc));
|
|
|
|
Library->Documents.clear();
|
|
AddFwk(std::move(Library));
|
|
}
|
|
|
|
Expected<std::unique_ptr<InterfaceFile>>
|
|
InterfaceFile::merge(const InterfaceFile *O) const {
|
|
// Verify files can be merged.
|
|
if (getInstallName() != O->getInstallName()) {
|
|
return make_error<StringError>("install names do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if (getCurrentVersion() != O->getCurrentVersion()) {
|
|
return make_error<StringError>("current versions do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if (getCompatibilityVersion() != O->getCompatibilityVersion()) {
|
|
return make_error<StringError>("compatibility versions do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&
|
|
(getSwiftABIVersion() != O->getSwiftABIVersion())) {
|
|
return make_error<StringError>("swift ABI versions do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {
|
|
return make_error<StringError>("two level namespace flags do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {
|
|
return make_error<StringError>(
|
|
"application extension safe flags do not match",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
|
|
IF->setFileType(std::max(getFileType(), O->getFileType()));
|
|
IF->setPath(getPath());
|
|
IF->setInstallName(getInstallName());
|
|
IF->setCurrentVersion(getCurrentVersion());
|
|
IF->setCompatibilityVersion(getCompatibilityVersion());
|
|
|
|
if (getSwiftABIVersion() == 0)
|
|
IF->setSwiftABIVersion(O->getSwiftABIVersion());
|
|
else
|
|
IF->setSwiftABIVersion(getSwiftABIVersion());
|
|
|
|
IF->setTwoLevelNamespace(isTwoLevelNamespace());
|
|
IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
|
|
IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
|
|
|
|
for (const auto &It : umbrellas()) {
|
|
if (!It.second.empty())
|
|
IF->addParentUmbrella(It.first, It.second);
|
|
}
|
|
for (const auto &It : O->umbrellas()) {
|
|
if (!It.second.empty())
|
|
IF->addParentUmbrella(It.first, It.second);
|
|
}
|
|
IF->addTargets(targets());
|
|
IF->addTargets(O->targets());
|
|
|
|
for (const auto &Lib : allowableClients())
|
|
for (const auto &Target : Lib.targets())
|
|
IF->addAllowableClient(Lib.getInstallName(), Target);
|
|
|
|
for (const auto &Lib : O->allowableClients())
|
|
for (const auto &Target : Lib.targets())
|
|
IF->addAllowableClient(Lib.getInstallName(), Target);
|
|
|
|
for (const auto &Lib : reexportedLibraries())
|
|
for (const auto &Target : Lib.targets())
|
|
IF->addReexportedLibrary(Lib.getInstallName(), Target);
|
|
|
|
for (const auto &Lib : O->reexportedLibraries())
|
|
for (const auto &Target : Lib.targets())
|
|
IF->addReexportedLibrary(Lib.getInstallName(), Target);
|
|
|
|
for (const auto &[Target, Path] : rpaths())
|
|
IF->addRPath(Path, Target);
|
|
for (const auto &[Target, Path] : O->rpaths())
|
|
IF->addRPath(Path, Target);
|
|
|
|
for (const auto *Sym : symbols()) {
|
|
IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
|
|
Sym->getFlags());
|
|
}
|
|
|
|
for (const auto *Sym : O->symbols()) {
|
|
IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),
|
|
Sym->getFlags());
|
|
}
|
|
|
|
return std::move(IF);
|
|
}
|
|
|
|
Expected<std::unique_ptr<InterfaceFile>>
|
|
InterfaceFile::remove(Architecture Arch) const {
|
|
if (getArchitectures() == Arch)
|
|
return make_error<StringError>("cannot remove last architecture slice '" +
|
|
getArchitectureName(Arch) + "'",
|
|
inconvertibleErrorCode());
|
|
|
|
if (!getArchitectures().has(Arch)) {
|
|
bool Found = false;
|
|
for (auto &Doc : Documents) {
|
|
if (Doc->getArchitectures().has(Arch)) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found)
|
|
return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
|
|
}
|
|
|
|
// FIXME: Figure out how to keep these attributes in sync when new ones are
|
|
// added.
|
|
std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
|
|
IF->setFileType(getFileType());
|
|
IF->setPath(getPath());
|
|
IF->addTargets(targets(ArchitectureSet::All().clear(Arch)));
|
|
IF->setInstallName(getInstallName());
|
|
IF->setCurrentVersion(getCurrentVersion());
|
|
IF->setCompatibilityVersion(getCompatibilityVersion());
|
|
IF->setSwiftABIVersion(getSwiftABIVersion());
|
|
IF->setTwoLevelNamespace(isTwoLevelNamespace());
|
|
IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
|
|
IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
|
|
for (const auto &It : umbrellas())
|
|
if (It.first.Arch != Arch)
|
|
IF->addParentUmbrella(It.first, It.second);
|
|
|
|
for (const auto &Lib : allowableClients()) {
|
|
for (const auto &Target : Lib.targets())
|
|
if (Target.Arch != Arch)
|
|
IF->addAllowableClient(Lib.getInstallName(), Target);
|
|
}
|
|
|
|
for (const auto &Lib : reexportedLibraries()) {
|
|
for (const auto &Target : Lib.targets())
|
|
if (Target.Arch != Arch)
|
|
IF->addReexportedLibrary(Lib.getInstallName(), Target);
|
|
}
|
|
|
|
for (const auto *Sym : symbols()) {
|
|
auto Archs = Sym->getArchitectures();
|
|
Archs.clear(Arch);
|
|
if (Archs.empty())
|
|
continue;
|
|
|
|
IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs),
|
|
Sym->getFlags());
|
|
}
|
|
|
|
for (auto &Doc : Documents) {
|
|
// Skip the inlined document if the to be removed architecture is the
|
|
// only one left.
|
|
if (Doc->getArchitectures() == Arch)
|
|
continue;
|
|
|
|
// If the document doesn't contain the arch, then no work is to be done
|
|
// and it can be copied over.
|
|
if (!Doc->getArchitectures().has(Arch)) {
|
|
auto NewDoc = Doc;
|
|
IF->addDocument(std::move(NewDoc));
|
|
continue;
|
|
}
|
|
|
|
auto Result = Doc->remove(Arch);
|
|
if (!Result)
|
|
return Result;
|
|
|
|
IF->addDocument(std::move(Result.get()));
|
|
}
|
|
|
|
return std::move(IF);
|
|
}
|
|
|
|
Expected<std::unique_ptr<InterfaceFile>>
|
|
InterfaceFile::extract(Architecture Arch) const {
|
|
if (!getArchitectures().has(Arch)) {
|
|
return make_error<StringError>("file doesn't have architecture '" +
|
|
getArchitectureName(Arch) + "'",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
std::unique_ptr<InterfaceFile> IF(new InterfaceFile());
|
|
IF->setFileType(getFileType());
|
|
IF->setPath(getPath());
|
|
IF->addTargets(targets(Arch));
|
|
IF->setInstallName(getInstallName());
|
|
IF->setCurrentVersion(getCurrentVersion());
|
|
IF->setCompatibilityVersion(getCompatibilityVersion());
|
|
IF->setSwiftABIVersion(getSwiftABIVersion());
|
|
IF->setTwoLevelNamespace(isTwoLevelNamespace());
|
|
IF->setApplicationExtensionSafe(isApplicationExtensionSafe());
|
|
IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());
|
|
for (const auto &It : umbrellas())
|
|
if (It.first.Arch == Arch)
|
|
IF->addParentUmbrella(It.first, It.second);
|
|
|
|
for (const auto &It : rpaths())
|
|
if (It.first.Arch == Arch)
|
|
IF->addRPath(It.second, It.first);
|
|
|
|
for (const auto &Lib : allowableClients())
|
|
for (const auto &Target : Lib.targets())
|
|
if (Target.Arch == Arch)
|
|
IF->addAllowableClient(Lib.getInstallName(), Target);
|
|
|
|
for (const auto &Lib : reexportedLibraries())
|
|
for (const auto &Target : Lib.targets())
|
|
if (Target.Arch == Arch)
|
|
IF->addReexportedLibrary(Lib.getInstallName(), Target);
|
|
|
|
for (const auto *Sym : symbols()) {
|
|
if (Sym->hasArchitecture(Arch))
|
|
IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),
|
|
Sym->getFlags());
|
|
}
|
|
|
|
for (auto &Doc : Documents) {
|
|
// Skip documents that don't have the requested architecture.
|
|
if (!Doc->getArchitectures().has(Arch))
|
|
continue;
|
|
|
|
auto Result = Doc->extract(Arch);
|
|
if (!Result)
|
|
return Result;
|
|
|
|
IF->addDocument(std::move(Result.get()));
|
|
}
|
|
|
|
return std::move(IF);
|
|
}
|
|
|
|
void InterfaceFile::setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA,
|
|
const Target &Targ) {
|
|
if (getFileType() != BA.File)
|
|
setFileType(BA.File);
|
|
if (getInstallName().empty())
|
|
setInstallName(BA.InstallName);
|
|
if (BA.AppExtensionSafe && !isApplicationExtensionSafe())
|
|
setApplicationExtensionSafe();
|
|
if (BA.TwoLevelNamespace && !isTwoLevelNamespace())
|
|
setTwoLevelNamespace();
|
|
if (BA.OSLibNotForSharedCache && !isOSLibNotForSharedCache())
|
|
setOSLibNotForSharedCache();
|
|
if (getCurrentVersion().empty())
|
|
setCurrentVersion(BA.CurrentVersion);
|
|
if (getCompatibilityVersion().empty())
|
|
setCompatibilityVersion(BA.CompatVersion);
|
|
if (getSwiftABIVersion() == 0)
|
|
setSwiftABIVersion(BA.SwiftABI);
|
|
if (getPath().empty())
|
|
setPath(BA.Path);
|
|
if (!BA.ParentUmbrella.empty())
|
|
addParentUmbrella(Targ, BA.ParentUmbrella);
|
|
for (const auto &Client : BA.AllowableClients)
|
|
addAllowableClient(Client, Targ);
|
|
for (const auto &Lib : BA.RexportedLibraries)
|
|
addReexportedLibrary(Lib, Targ);
|
|
}
|
|
|
|
static bool isYAMLTextStub(const FileType &Kind) {
|
|
return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);
|
|
}
|
|
|
|
bool InterfaceFile::operator==(const InterfaceFile &O) const {
|
|
if (Targets != O.Targets)
|
|
return false;
|
|
if (InstallName != O.InstallName)
|
|
return false;
|
|
if ((CurrentVersion != O.CurrentVersion) ||
|
|
(CompatibilityVersion != O.CompatibilityVersion))
|
|
return false;
|
|
if (SwiftABIVersion != O.SwiftABIVersion)
|
|
return false;
|
|
if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
|
|
return false;
|
|
if (IsAppExtensionSafe != O.IsAppExtensionSafe)
|
|
return false;
|
|
if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache)
|
|
return false;
|
|
if (HasSimSupport != O.HasSimSupport)
|
|
return false;
|
|
if (ParentUmbrellas != O.ParentUmbrellas)
|
|
return false;
|
|
if (AllowableClients != O.AllowableClients)
|
|
return false;
|
|
if (ReexportedLibraries != O.ReexportedLibraries)
|
|
return false;
|
|
if (*SymbolsSet != *O.SymbolsSet)
|
|
return false;
|
|
// Don't compare run search paths for older filetypes that cannot express
|
|
// them.
|
|
if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {
|
|
if (RPaths != O.RPaths)
|
|
return false;
|
|
if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))
|
|
return false;
|
|
}
|
|
|
|
if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
|
|
O.Documents.end(),
|
|
[](const std::shared_ptr<InterfaceFile> LHS,
|
|
const std::shared_ptr<InterfaceFile> RHS) {
|
|
return *LHS == *RHS;
|
|
}))
|
|
return false;
|
|
return true;
|
|
}
|