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 176 for (const auto &It : umbrellas()) { 177 if (!It.second.empty()) 178 IF->addParentUmbrella(It.first, It.second); 179 } 180 for (const auto &It : O->umbrellas()) { 181 if (!It.second.empty()) 182 IF->addParentUmbrella(It.first, It.second); 183 } 184 IF->addTargets(targets()); 185 IF->addTargets(O->targets()); 186 187 for (const auto &Lib : allowableClients()) 188 for (const auto &Target : Lib.targets()) 189 IF->addAllowableClient(Lib.getInstallName(), Target); 190 191 for (const auto &Lib : O->allowableClients()) 192 for (const auto &Target : Lib.targets()) 193 IF->addAllowableClient(Lib.getInstallName(), Target); 194 195 for (const auto &Lib : reexportedLibraries()) 196 for (const auto &Target : Lib.targets()) 197 IF->addReexportedLibrary(Lib.getInstallName(), Target); 198 199 for (const auto &Lib : O->reexportedLibraries()) 200 for (const auto &Target : Lib.targets()) 201 IF->addReexportedLibrary(Lib.getInstallName(), Target); 202 203 for (const auto &[Target, Path] : rpaths()) 204 IF->addRPath(Path, Target); 205 for (const auto &[Target, Path] : O->rpaths()) 206 IF->addRPath(Path, Target); 207 208 for (const auto *Sym : symbols()) { 209 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(), 210 Sym->getFlags()); 211 } 212 213 for (const auto *Sym : O->symbols()) { 214 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(), 215 Sym->getFlags()); 216 } 217 218 return std::move(IF); 219 } 220 221 Expected<std::unique_ptr<InterfaceFile>> 222 InterfaceFile::remove(Architecture Arch) const { 223 if (getArchitectures() == Arch) 224 return make_error<StringError>("cannot remove last architecture slice '" + 225 getArchitectureName(Arch) + "'", 226 inconvertibleErrorCode()); 227 228 if (!getArchitectures().has(Arch)) { 229 bool Found = false; 230 for (auto &Doc : Documents) { 231 if (Doc->getArchitectures().has(Arch)) { 232 Found = true; 233 break; 234 } 235 } 236 237 if (!Found) 238 return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture); 239 } 240 241 std::unique_ptr<InterfaceFile> IF(new InterfaceFile()); 242 IF->setFileType(getFileType()); 243 IF->setPath(getPath()); 244 IF->addTargets(targets(ArchitectureSet::All().clear(Arch))); 245 IF->setInstallName(getInstallName()); 246 IF->setCurrentVersion(getCurrentVersion()); 247 IF->setCompatibilityVersion(getCompatibilityVersion()); 248 IF->setSwiftABIVersion(getSwiftABIVersion()); 249 IF->setTwoLevelNamespace(isTwoLevelNamespace()); 250 IF->setApplicationExtensionSafe(isApplicationExtensionSafe()); 251 for (const auto &It : umbrellas()) 252 if (It.first.Arch != Arch) 253 IF->addParentUmbrella(It.first, It.second); 254 255 for (const auto &Lib : allowableClients()) { 256 for (const auto &Target : Lib.targets()) 257 if (Target.Arch != Arch) 258 IF->addAllowableClient(Lib.getInstallName(), Target); 259 } 260 261 for (const auto &Lib : reexportedLibraries()) { 262 for (const auto &Target : Lib.targets()) 263 if (Target.Arch != Arch) 264 IF->addReexportedLibrary(Lib.getInstallName(), Target); 265 } 266 267 for (const auto *Sym : symbols()) { 268 auto Archs = Sym->getArchitectures(); 269 Archs.clear(Arch); 270 if (Archs.empty()) 271 continue; 272 273 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Archs), 274 Sym->getFlags()); 275 } 276 277 for (auto &Doc : Documents) { 278 // Skip the inlined document if the to be removed architecture is the 279 // only one left. 280 if (Doc->getArchitectures() == Arch) 281 continue; 282 283 // If the document doesn't contain the arch, then no work is to be done 284 // and it can be copied over. 285 if (!Doc->getArchitectures().has(Arch)) { 286 auto NewDoc = Doc; 287 IF->addDocument(std::move(NewDoc)); 288 continue; 289 } 290 291 auto Result = Doc->remove(Arch); 292 if (!Result) 293 return Result; 294 295 IF->addDocument(std::move(Result.get())); 296 } 297 298 return std::move(IF); 299 } 300 301 Expected<std::unique_ptr<InterfaceFile>> 302 InterfaceFile::extract(Architecture Arch) const { 303 if (!getArchitectures().has(Arch)) { 304 return make_error<StringError>("file doesn't have architecture '" + 305 getArchitectureName(Arch) + "'", 306 inconvertibleErrorCode()); 307 } 308 309 std::unique_ptr<InterfaceFile> IF(new InterfaceFile()); 310 IF->setFileType(getFileType()); 311 IF->setPath(getPath()); 312 IF->addTargets(targets(Arch)); 313 IF->setInstallName(getInstallName()); 314 IF->setCurrentVersion(getCurrentVersion()); 315 IF->setCompatibilityVersion(getCompatibilityVersion()); 316 IF->setSwiftABIVersion(getSwiftABIVersion()); 317 IF->setTwoLevelNamespace(isTwoLevelNamespace()); 318 IF->setApplicationExtensionSafe(isApplicationExtensionSafe()); 319 for (const auto &It : umbrellas()) 320 if (It.first.Arch == Arch) 321 IF->addParentUmbrella(It.first, It.second); 322 323 for (const auto &It : rpaths()) 324 if (It.first.Arch == Arch) 325 IF->addRPath(It.second, It.first); 326 327 for (const auto &Lib : allowableClients()) 328 for (const auto &Target : Lib.targets()) 329 if (Target.Arch == Arch) 330 IF->addAllowableClient(Lib.getInstallName(), Target); 331 332 for (const auto &Lib : reexportedLibraries()) 333 for (const auto &Target : Lib.targets()) 334 if (Target.Arch == Arch) 335 IF->addReexportedLibrary(Lib.getInstallName(), Target); 336 337 for (const auto *Sym : symbols()) { 338 if (Sym->hasArchitecture(Arch)) 339 IF->addSymbol(Sym->getKind(), Sym->getName(), Sym->targets(Arch), 340 Sym->getFlags()); 341 } 342 343 for (auto &Doc : Documents) { 344 // Skip documents that don't have the requested architecture. 345 if (!Doc->getArchitectures().has(Arch)) 346 continue; 347 348 auto Result = Doc->extract(Arch); 349 if (!Result) 350 return Result; 351 352 IF->addDocument(std::move(Result.get())); 353 } 354 355 return std::move(IF); 356 } 357 358 void InterfaceFile::setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA, 359 const Target &Targ) { 360 if (getFileType() != BA.File) 361 setFileType(BA.File); 362 if (getInstallName().empty()) 363 setInstallName(BA.InstallName); 364 if (BA.AppExtensionSafe && !isApplicationExtensionSafe()) 365 setApplicationExtensionSafe(); 366 if (BA.TwoLevelNamespace && !isTwoLevelNamespace()) 367 setTwoLevelNamespace(); 368 if (BA.OSLibNotForSharedCache && !isOSLibNotForSharedCache()) 369 setOSLibNotForSharedCache(); 370 if (getCurrentVersion().empty()) 371 setCurrentVersion(BA.CurrentVersion); 372 if (getCompatibilityVersion().empty()) 373 setCompatibilityVersion(BA.CompatVersion); 374 if (getSwiftABIVersion() == 0) 375 setSwiftABIVersion(BA.SwiftABI); 376 if (getPath().empty()) 377 setPath(BA.Path); 378 if (!BA.ParentUmbrella.empty()) 379 addParentUmbrella(Targ, BA.ParentUmbrella); 380 for (const auto &Client : BA.AllowableClients) 381 addAllowableClient(Client, Targ); 382 for (const auto &Lib : BA.RexportedLibraries) 383 addReexportedLibrary(Lib, Targ); 384 } 385 386 static bool isYAMLTextStub(const FileType &Kind) { 387 return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5); 388 } 389 390 bool InterfaceFile::operator==(const InterfaceFile &O) const { 391 if (Targets != O.Targets) 392 return false; 393 if (InstallName != O.InstallName) 394 return false; 395 if ((CurrentVersion != O.CurrentVersion) || 396 (CompatibilityVersion != O.CompatibilityVersion)) 397 return false; 398 if (SwiftABIVersion != O.SwiftABIVersion) 399 return false; 400 if (IsTwoLevelNamespace != O.IsTwoLevelNamespace) 401 return false; 402 if (IsAppExtensionSafe != O.IsAppExtensionSafe) 403 return false; 404 if (IsOSLibNotForSharedCache != O.IsOSLibNotForSharedCache) 405 return false; 406 if (HasSimSupport != O.HasSimSupport) 407 return false; 408 if (ParentUmbrellas != O.ParentUmbrellas) 409 return false; 410 if (AllowableClients != O.AllowableClients) 411 return false; 412 if (ReexportedLibraries != O.ReexportedLibraries) 413 return false; 414 if (*SymbolsSet != *O.SymbolsSet) 415 return false; 416 // Don't compare run search paths for older filetypes that cannot express 417 // them. 418 if (!(isYAMLTextStub(FileKind)) && !(isYAMLTextStub(O.FileKind))) { 419 if (RPaths != O.RPaths) 420 return false; 421 if (mapToPlatformVersionSet(Targets) != mapToPlatformVersionSet(O.Targets)) 422 return false; 423 } 424 425 if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(), 426 O.Documents.end(), 427 [](const std::shared_ptr<InterfaceFile> LHS, 428 const std::shared_ptr<InterfaceFile> RHS) { 429 return *LHS == *RHS; 430 })) 431 return false; 432 return true; 433 } 434