432 lines · cpp
1//===- InterfaceFile.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 the Interface File.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/TextAPI/InterfaceFile.h"14#include "llvm/TextAPI/RecordsSlice.h"15#include "llvm/TextAPI/TextAPIError.h"16 17using namespace llvm;18using namespace llvm::MachO;19 20void InterfaceFileRef::addTarget(const Target &Target) {21 addEntry(Targets, Target);22}23 24void InterfaceFile::addAllowableClient(StringRef InstallName,25 const Target &Target) {26 if (InstallName.empty())27 return;28 auto Client = addEntry(AllowableClients, InstallName);29 Client->addTarget(Target);30}31 32void InterfaceFile::addReexportedLibrary(StringRef InstallName,33 const Target &Target) {34 if (InstallName.empty())35 return;36 auto Lib = addEntry(ReexportedLibraries, InstallName);37 Lib->addTarget(Target);38}39 40void InterfaceFile::addParentUmbrella(const Target &Target_, StringRef Parent) {41 if (Parent.empty())42 return;43 auto Iter = lower_bound(ParentUmbrellas, Target_,44 [](const std::pair<Target, std::string> &LHS,45 Target RHS) { return LHS.first < RHS; });46 47 if ((Iter != ParentUmbrellas.end()) && !(Target_ < Iter->first)) {48 Iter->second = std::string(Parent);49 return;50 }51 52 ParentUmbrellas.emplace(Iter, Target_, std::string(Parent));53}54 55void InterfaceFile::addRPath(StringRef RPath, const Target &InputTarget) {56 if (RPath.empty())57 return;58 using RPathEntryT = const std::pair<Target, std::string>;59 RPathEntryT Entry(InputTarget, RPath);60 61 if (is_contained(RPaths, Entry))62 return;63 64 RPaths.emplace_back(Entry);65}66 67void InterfaceFile::addTarget(const Target &Target) {68 addEntry(Targets, Target);69}70 71InterfaceFile::const_filtered_target_range72InterfaceFile::targets(ArchitectureSet Archs) const {73 std::function<bool(const Target &)> fn = [Archs](const Target &Target_) {74 return Archs.has(Target_.Arch);75 };76 return make_filter_range(Targets, fn);77}78 79void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {80 auto Pos = llvm::lower_bound(Documents, Document,81 [](const std::shared_ptr<InterfaceFile> &LHS,82 const std::shared_ptr<InterfaceFile> &RHS) {83 return LHS->InstallName < RHS->InstallName;84 });85 assert((Pos == Documents.end() ||86 (*Pos)->InstallName != Document->InstallName) &&87 "Unexpected duplicate document added");88 Document->Parent = this;89 Documents.insert(Pos, Document);90}91 92void InterfaceFile::inlineLibrary(std::shared_ptr<InterfaceFile> Library,93 bool Overwrite) {94 auto AddFwk = [&](std::shared_ptr<InterfaceFile> &&Reexport) {95 auto It = lower_bound(96 Documents, Reexport->getInstallName(),97 [](std::shared_ptr<InterfaceFile> &Lhs, const StringRef Rhs) {98 return Lhs->getInstallName() < Rhs;99 });100 101 if (Overwrite && It != Documents.end() &&102 Reexport->getInstallName() == (*It)->getInstallName()) {103 llvm::replace(Documents, *It, std::move(Reexport));104 return;105 }106 107 if ((It != Documents.end()) &&108 !(Reexport->getInstallName() < (*It)->getInstallName()))109 return;110 111 Documents.emplace(It, std::move(Reexport));112 };113 for (auto Doc : Library->documents())114 AddFwk(std::move(Doc));115 116 Library->Documents.clear();117 AddFwk(std::move(Library));118}119 120Expected<std::unique_ptr<InterfaceFile>>121InterfaceFile::merge(const InterfaceFile *O) const {122 // Verify files can be merged.123 if (getInstallName() != O->getInstallName()) {124 return make_error<StringError>("install names do not match",125 inconvertibleErrorCode());126 }127 128 if (getCurrentVersion() != O->getCurrentVersion()) {129 return make_error<StringError>("current versions do not match",130 inconvertibleErrorCode());131 }132 133 if (getCompatibilityVersion() != O->getCompatibilityVersion()) {134 return make_error<StringError>("compatibility versions do not match",135 inconvertibleErrorCode());136 }137 138 if ((getSwiftABIVersion() != 0) && (O->getSwiftABIVersion() != 0) &&139 (getSwiftABIVersion() != O->getSwiftABIVersion())) {140 return make_error<StringError>("swift ABI versions do not match",141 inconvertibleErrorCode());142 }143 144 if (isTwoLevelNamespace() != O->isTwoLevelNamespace()) {145 return make_error<StringError>("two level namespace flags do not match",146 inconvertibleErrorCode());147 }148 149 if (isApplicationExtensionSafe() != O->isApplicationExtensionSafe()) {150 return make_error<StringError>(151 "application extension safe flags do not match",152 inconvertibleErrorCode());153 }154 155 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());156 IF->setFileType(std::max(getFileType(), O->getFileType()));157 IF->setPath(getPath());158 IF->setInstallName(getInstallName());159 IF->setCurrentVersion(getCurrentVersion());160 IF->setCompatibilityVersion(getCompatibilityVersion());161 162 if (getSwiftABIVersion() == 0)163 IF->setSwiftABIVersion(O->getSwiftABIVersion());164 else165 IF->setSwiftABIVersion(getSwiftABIVersion());166 167 IF->setTwoLevelNamespace(isTwoLevelNamespace());168 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());169 IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());170 171 for (const auto &It : umbrellas()) {172 if (!It.second.empty())173 IF->addParentUmbrella(It.first, It.second);174 }175 for (const auto &It : O->umbrellas()) {176 if (!It.second.empty())177 IF->addParentUmbrella(It.first, It.second);178 }179 IF->addTargets(targets());180 IF->addTargets(O->targets());181 182 for (const auto &Lib : allowableClients())183 for (const auto &Target : Lib.targets())184 IF->addAllowableClient(Lib.getInstallName(), Target);185 186 for (const auto &Lib : O->allowableClients())187 for (const auto &Target : Lib.targets())188 IF->addAllowableClient(Lib.getInstallName(), Target);189 190 for (const auto &Lib : reexportedLibraries())191 for (const auto &Target : Lib.targets())192 IF->addReexportedLibrary(Lib.getInstallName(), Target);193 194 for (const auto &Lib : O->reexportedLibraries())195 for (const auto &Target : Lib.targets())196 IF->addReexportedLibrary(Lib.getInstallName(), Target);197 198 for (const auto &[Target, Path] : rpaths())199 IF->addRPath(Path, Target);200 for (const auto &[Target, Path] : O->rpaths())201 IF->addRPath(Path, Target);202 203 for (const auto *Sym : symbols()) {204 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),205 Sym->getFlags());206 }207 208 for (const auto *Sym : O->symbols()) {209 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(),210 Sym->getFlags());211 }212 213 return std::move(IF);214}215 216Expected<std::unique_ptr<InterfaceFile>>217InterfaceFile::remove(Architecture Arch) const {218 if (getArchitectures() == Arch)219 return make_error<StringError>("cannot remove last architecture slice '" +220 getArchitectureName(Arch) + "'",221 inconvertibleErrorCode());222 223 if (!getArchitectures().has(Arch)) {224 bool Found = false;225 for (auto &Doc : Documents) {226 if (Doc->getArchitectures().has(Arch)) {227 Found = true;228 break;229 }230 }231 232 if (!Found)233 return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);234 }235 236 // FIXME: Figure out how to keep these attributes in sync when new ones are237 // added.238 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());239 IF->setFileType(getFileType());240 IF->setPath(getPath());241 IF->addTargets(targets(ArchitectureSet::All().clear(Arch)));242 IF->setInstallName(getInstallName());243 IF->setCurrentVersion(getCurrentVersion());244 IF->setCompatibilityVersion(getCompatibilityVersion());245 IF->setSwiftABIVersion(getSwiftABIVersion());246 IF->setTwoLevelNamespace(isTwoLevelNamespace());247 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());248 IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());249 for (const auto &It : umbrellas())250 if (It.first.Arch != Arch)251 IF->addParentUmbrella(It.first, It.second);252 253 for (const auto &Lib : allowableClients()) {254 for (const auto &Target : Lib.targets())255 if (Target.Arch != Arch)256 IF->addAllowableClient(Lib.getInstallName(), Target);257 }258 259 for (const auto &Lib : reexportedLibraries()) {260 for (const auto &Target : Lib.targets())261 if (Target.Arch != Arch)262 IF->addReexportedLibrary(Lib.getInstallName(), Target);263 }264 265 for (const auto *Sym : symbols()) {266 auto Archs = Sym->getArchitectures();267 Archs.clear(Arch);268 if (Archs.empty())269 continue;270 271 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs),272 Sym->getFlags());273 }274 275 for (auto &Doc : Documents) {276 // Skip the inlined document if the to be removed architecture is the277 // only one left.278 if (Doc->getArchitectures() == Arch)279 continue;280 281 // If the document doesn't contain the arch, then no work is to be done282 // and it can be copied over.283 if (!Doc->getArchitectures().has(Arch)) {284 auto NewDoc = Doc;285 IF->addDocument(std::move(NewDoc));286 continue;287 }288 289 auto Result = Doc->remove(Arch);290 if (!Result)291 return Result;292 293 IF->addDocument(std::move(Result.get()));294 }295 296 return std::move(IF);297}298 299Expected<std::unique_ptr<InterfaceFile>>300InterfaceFile::extract(Architecture Arch) const {301 if (!getArchitectures().has(Arch)) {302 return make_error<StringError>("file doesn't have architecture '" +303 getArchitectureName(Arch) + "'",304 inconvertibleErrorCode());305 }306 307 std::unique_ptr<InterfaceFile> IF(new InterfaceFile());308 IF->setFileType(getFileType());309 IF->setPath(getPath());310 IF->addTargets(targets(Arch));311 IF->setInstallName(getInstallName());312 IF->setCurrentVersion(getCurrentVersion());313 IF->setCompatibilityVersion(getCompatibilityVersion());314 IF->setSwiftABIVersion(getSwiftABIVersion());315 IF->setTwoLevelNamespace(isTwoLevelNamespace());316 IF->setApplicationExtensionSafe(isApplicationExtensionSafe());317 IF->setOSLibNotForSharedCache(isOSLibNotForSharedCache());318 for (const auto &It : umbrellas())319 if (It.first.Arch == Arch)320 IF->addParentUmbrella(It.first, It.second);321 322 for (const auto &It : rpaths())323 if (It.first.Arch == Arch)324 IF->addRPath(It.second, It.first);325 326 for (const auto &Lib : allowableClients())327 for (const auto &Target : Lib.targets())328 if (Target.Arch == Arch)329 IF->addAllowableClient(Lib.getInstallName(), Target);330 331 for (const auto &Lib : reexportedLibraries())332 for (const auto &Target : Lib.targets())333 if (Target.Arch == Arch)334 IF->addReexportedLibrary(Lib.getInstallName(), Target);335 336 for (const auto *Sym : symbols()) {337 if (Sym->hasArchitecture(Arch))338 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch),339 Sym->getFlags());340 }341 342 for (auto &Doc : Documents) {343 // Skip documents that don't have the requested architecture.344 if (!Doc->getArchitectures().has(Arch))345 continue;346 347 auto Result = Doc->extract(Arch);348 if (!Result)349 return Result;350 351 IF->addDocument(std::move(Result.get()));352 }353 354 return std::move(IF);355}356 357void InterfaceFile::setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA,358 const Target &Targ) {359 if (getFileType() != BA.File)360 setFileType(BA.File);361 if (getInstallName().empty())362 setInstallName(BA.InstallName);363 if (BA.AppExtensionSafe && !isApplicationExtensionSafe())364 setApplicationExtensionSafe();365 if (BA.TwoLevelNamespace && !isTwoLevelNamespace())366 setTwoLevelNamespace();367 if (BA.OSLibNotForSharedCache && !isOSLibNotForSharedCache())368 setOSLibNotForSharedCache();369 if (getCurrentVersion().empty())370 setCurrentVersion(BA.CurrentVersion);371 if (getCompatibilityVersion().empty())372 setCompatibilityVersion(BA.CompatVersion);373 if (getSwiftABIVersion() == 0)374 setSwiftABIVersion(BA.SwiftABI);375 if (getPath().empty())376 setPath(BA.Path);377 if (!BA.ParentUmbrella.empty())378 addParentUmbrella(Targ, BA.ParentUmbrella);379 for (const auto &Client : BA.AllowableClients)380 addAllowableClient(Client, Targ);381 for (const auto &Lib : BA.RexportedLibraries)382 addReexportedLibrary(Lib, Targ);383}384 385static bool isYAMLTextStub(const FileType &Kind) {386 return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5);387}388 389bool InterfaceFile::operator==(const InterfaceFile &O) const {390 if (Targets != O.Targets)391 return false;392 if (InstallName != O.InstallName)393 return false;394 if ((CurrentVersion != O.CurrentVersion) ||395 (CompatibilityVersion != O.CompatibilityVersion))396 return false;397 if (SwiftABIVersion != O.SwiftABIVersion)398 return false;399 if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)400 return false;401 if (IsAppExtensionSafe != O.IsAppExtensionSafe)402 return false;403 if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache)404 return false;405 if (HasSimSupport != O.HasSimSupport)406 return false;407 if (ParentUmbrellas != O.ParentUmbrellas)408 return false;409 if (AllowableClients != O.AllowableClients)410 return false;411 if (ReexportedLibraries != O.ReexportedLibraries)412 return false;413 if (*SymbolsSet != *O.SymbolsSet)414 return false;415 // Don't compare run search paths for older filetypes that cannot express416 // them.417 if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) {418 if (RPaths != O.RPaths)419 return false;420 if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets))421 return false;422 }423 424 if (!llvm::equal(Documents, O.Documents,425 [](const std::shared_ptr<InterfaceFile> &LHS,426 const std::shared_ptr<InterfaceFile> &RHS) {427 return *LHS == *RHS;428 }))429 return false;430 return true;431}432