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