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 17 using namespace llvm; 18 using namespace llvm::MachO; 19 20 void InterfaceFileRef::addTarget(const Target &Target) { 21 addEntry(Targets, Target); 22 } 23 24 void 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 32 void 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 40 void 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 55 void 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 67 void InterfaceFile::addTarget(const Target &Target) { 68 addEntry(Targets, Target); 69 } 70 71 InterfaceFile::const_filtered_target_range 72 InterfaceFile::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 79 void 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 92 void 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 120 Expected<std::unique_ptr<InterfaceFile>> 121 InterfaceFile::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 else 165 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 216 Expected<std::unique_ptr<InterfaceFile>> 217 InterfaceFile::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 are 237 // 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 the 277 // 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 done 282 // 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 299 Expected<std::unique_ptr<InterfaceFile>> 300 InterfaceFile::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 357 void 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 385 static bool isYAMLTextStub(const FileType &Kind) { 386 return (Kind >= FileType::TBD_V1) && (Kind < FileType::TBD_V5); 387 } 388 389 bool 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 express 416 // 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 (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(), 425 O.Documents.end(), 426 [](const std::shared_ptr<InterfaceFile> LHS, 427 const std::shared_ptr<InterfaceFile> RHS) { 428 return *LHS == *RHS; 429 })) 430 return false; 431 return true; 432 } 433