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