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