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