/// Emits a Make-style dependencies file. static bool emitMakeDependencies(DiagnosticEngine &diags, DependencyTracker &depTracker, const FrontendOptions &opts) { std::error_code EC; llvm::raw_fd_ostream out(opts.DependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.DependenciesFilePath, EC.message()); out.clear_error(); return true; } // Declare a helper for escaping file names for use in Makefiles. llvm::SmallString<256> pathBuf; auto escape = [&](StringRef raw) -> StringRef { pathBuf.clear(); static const char badChars[] = " $#:\n"; size_t prev = 0; for (auto index = raw.find_first_of(badChars); index != StringRef::npos; index = raw.find_first_of(badChars, index+1)) { pathBuf.append(raw.slice(prev, index)); if (raw[index] == '$') pathBuf.push_back('$'); else pathBuf.push_back('\\'); prev = index; } pathBuf.append(raw.substr(prev)); return pathBuf; }; // FIXME: Xcode can't currently handle multiple targets in a single // dependency line. opts.forAllOutputPaths([&](StringRef targetName) { out << escape(targetName) << " :"; // First include all other files in the module. Make-style dependencies // need to be conservative! for (StringRef path : opts.InputFilenames) out << ' ' << escape(path); // Then print dependencies we've picked up during compilation. for (StringRef path : depTracker.getDependencies()) out << ' ' << escape(path); out << '\n'; }); return false; }
static void collectFileDependencies(llvm::SetVector<const clang::FileEntry *> &result, const DependencyTracker &dependencyTracker, ModuleDecl *module, clang::FileManager &fileMgr) { for (auto *F : module->getFiles()) { if (auto *SF = dyn_cast<SourceFile>(F)) { if (auto *dep = fileMgr.getFile(SF->getFilename())) { result.insert(dep); } } } for (StringRef filename : dependencyTracker.getDependencies()) { if (auto *F = fileMgr.getFile(filename)) result.insert(F); } }
/// Emits a Swift-style dependencies file. static bool emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, DependencyTracker &depTracker, const FrontendOptions &opts) { if (!SF) { diags.diagnose(SourceLoc(), diag::emit_reference_dependencies_without_primary_file); return true; } std::error_code EC; llvm::raw_fd_ostream out(opts.ReferenceDependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.ReferenceDependenciesFilePath, EC.message()); out.clear_error(); return true; } auto escape = [](Identifier name) -> std::string { return llvm::yaml::escape(name.str()); }; out << "### Swift dependencies file v0 ###\n"; llvm::MapVector<const NominalTypeDecl *, bool> extendedNominals; llvm::SmallVector<const ExtensionDecl *, 8> extensionsWithJustMembers; out << "provides-top-level:\n"; for (const Decl *D : SF->Decls) { switch (D->getKind()) { case DeclKind::Module: break; case DeclKind::Import: // FIXME: Handle re-exported decls. break; case DeclKind::Extension: { auto *ED = cast<ExtensionDecl>(D); auto *NTD = ED->getExtendedType()->getAnyNominal(); if (!NTD) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() == Accessibility::Private) { break; } bool justMembers = std::all_of(ED->getInherited().begin(), ED->getInherited().end(), extendedTypeIsPrivate); if (justMembers) { if (std::all_of(ED->getMembers().begin(), ED->getMembers().end(), declIsPrivate)) { break; } else { extensionsWithJustMembers.push_back(ED); } } extendedNominals[NTD] |= !justMembers; findNominals(extendedNominals, ED->getMembers()); break; } case DeclKind::InfixOperator: case DeclKind::PrefixOperator: case DeclKind::PostfixOperator: out << "- \"" << escape(cast<OperatorDecl>(D)->getName()) << "\"\n"; break; case DeclKind::Enum: case DeclKind::Struct: case DeclKind::Class: case DeclKind::Protocol: { auto *NTD = cast<NominalTypeDecl>(D); if (!NTD->hasName()) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() == Accessibility::Private) { break; } out << "- \"" << escape(NTD->getName()) << "\"\n"; extendedNominals[NTD] |= true; findNominals(extendedNominals, NTD->getMembers()); break; } case DeclKind::TypeAlias: case DeclKind::Var: case DeclKind::Func: { auto *VD = cast<ValueDecl>(D); if (!VD->hasName()) break; if (VD->hasAccessibility() && VD->getFormalAccess() == Accessibility::Private) { break; } out << "- \"" << escape(VD->getName()) << "\"\n"; break; } case DeclKind::PatternBinding: case DeclKind::TopLevelCode: case DeclKind::IfConfig: // No action necessary. break; case DeclKind::EnumCase: case DeclKind::GenericTypeParam: case DeclKind::AssociatedType: case DeclKind::Param: case DeclKind::Subscript: case DeclKind::Constructor: case DeclKind::Destructor: case DeclKind::EnumElement: llvm_unreachable("cannot appear at the top level of a file"); } } out << "provides-nominal:\n"; for (auto entry : extendedNominals) { if (!entry.second) continue; out << "- \""; mangleTypeAsContext(out, entry.first); out << "\"\n"; } out << "provides-member:\n"; for (auto entry : extendedNominals) { out << "- [\""; mangleTypeAsContext(out, entry.first); out << "\", \"\"]\n"; } // This is also part of "provides-member". for (auto *ED : extensionsWithJustMembers) { SmallString<32> mangledName; mangleTypeAsContext(llvm::raw_svector_ostream(mangledName), ED->getExtendedType()->getAnyNominal()); for (auto *member : ED->getMembers()) { auto *VD = dyn_cast<ValueDecl>(member); if (!VD || !VD->hasName() || VD->getFormalAccess() == Accessibility::Private) { continue; } out << "- [\"" << mangledName.str() << "\", \"" << escape(VD->getName()) << "\"]\n"; } } if (SF->getASTContext().LangOpts.EnableObjCInterop) { // FIXME: This requires a traversal of the whole file to compute. // We should (a) see if there's a cheaper way to keep it up to date, // and/or (b) see if we can fast-path cases where there's no ObjC involved. out << "provides-dynamic-lookup:\n"; class ValueDeclPrinter : public VisibleDeclConsumer { private: raw_ostream &out; std::string (*escape)(Identifier); public: ValueDeclPrinter(raw_ostream &out, decltype(escape) escape) : out(out), escape(escape) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { out << "- \"" << escape(VD->getName()) << "\"\n"; } }; ValueDeclPrinter printer(out, escape); SF->lookupClassMembers({}, printer); } ReferencedNameTracker *tracker = SF->getReferencedNameTracker(); // FIXME: Sort these? out << "depends-top-level:\n"; for (auto &entry : tracker->getTopLevelNames()) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-member:\n"; auto &memberLookupTable = tracker->getUsedMembers(); using TableEntryTy = std::pair<ReferencedNameTracker::MemberPair, bool>; std::vector<TableEntryTy> sortedMembers{ memberLookupTable.begin(), memberLookupTable.end() }; llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(), [](const TableEntryTy *lhs, const TableEntryTy *rhs) -> int { if (lhs->first.first == rhs->first.first) return lhs->first.second.compare(rhs->first.second); if (lhs->first.first->getName() != rhs->first.first->getName()) return lhs->first.first->getName().compare(rhs->first.first->getName()); // Break type name ties by mangled name. SmallString<32> lhsMangledName, rhsMangledName; mangleTypeAsContext(llvm::raw_svector_ostream(lhsMangledName), lhs->first.first); mangleTypeAsContext(llvm::raw_svector_ostream(rhsMangledName), rhs->first.first); return lhsMangledName.str().compare(rhsMangledName.str()); }); for (auto &entry : sortedMembers) { assert(entry.first.first != nullptr); if (entry.first.first->hasAccessibility() && entry.first.first->getFormalAccess() == Accessibility::Private) continue; out << "- "; if (!entry.second) out << "!private "; out << "[\""; mangleTypeAsContext(out, entry.first.first); out << "\", \""; if (!entry.first.second.empty()) out << escape(entry.first.second); out << "\"]\n"; } out << "depends-nominal:\n"; for (auto i = sortedMembers.begin(), e = sortedMembers.end(); i != e; ++i) { bool isCascading = i->second; while (i+1 != e && i[0].first.first == i[1].first.first) { ++i; isCascading |= i->second; } if (i->first.first->hasAccessibility() && i->first.first->getFormalAccess() == Accessibility::Private) continue; out << "- "; if (!isCascading) out << "!private "; out << "\""; mangleTypeAsContext(out, i->first.first); out << "\"\n"; } // FIXME: Sort these? out << "depends-dynamic-lookup:\n"; for (auto &entry : tracker->getDynamicLookupNames()) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-external:\n"; for (auto &entry : depTracker.getDependencies()) { out << "- \"" << llvm::yaml::escape(entry) << "\"\n"; } llvm::SmallString<32> interfaceHash; SF->getInterfaceHash(interfaceHash); out << "interface-hash: \"" << interfaceHash << "\"\n"; return false; }
bool swift::emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF, DependencyTracker &depTracker, const FrontendOptions &opts) { if (!SF) { diags.diagnose(SourceLoc(), diag::emit_reference_dependencies_without_primary_file); return true; } // Before writing to the dependencies file path, preserve any previous file // that may have been there. No error handling -- this is just a nicety, it // doesn't matter if it fails. llvm::sys::fs::rename(opts.ReferenceDependenciesFilePath, opts.ReferenceDependenciesFilePath + "~"); std::error_code EC; llvm::raw_fd_ostream out(opts.ReferenceDependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.ReferenceDependenciesFilePath, EC.message()); out.clear_error(); return true; } auto escape = [](Identifier name) -> std::string { return llvm::yaml::escape(name.str()); }; out << "### Swift dependencies file v0 ###\n"; llvm::MapVector<const NominalTypeDecl *, bool> extendedNominals; llvm::SmallVector<const FuncDecl *, 8> memberOperatorDecls; llvm::SmallVector<const ExtensionDecl *, 8> extensionsWithJustMembers; out << "provides-top-level:\n"; for (const Decl *D : SF->Decls) { switch (D->getKind()) { case DeclKind::Module: break; case DeclKind::Import: // FIXME: Handle re-exported decls. break; case DeclKind::Extension: { auto *ED = cast<ExtensionDecl>(D); auto *NTD = ED->getExtendedType()->getAnyNominal(); if (!NTD) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() <= Accessibility::FilePrivate) { break; } // Check if the extension is just adding members, or if it is // introducing a conformance to a public protocol. bool justMembers = std::all_of(ED->getInherited().begin(), ED->getInherited().end(), extendedTypeIsPrivate); if (justMembers) { if (std::all_of(ED->getMembers().begin(), ED->getMembers().end(), declIsPrivate)) { break; } else { extensionsWithJustMembers.push_back(ED); } } extendedNominals[NTD] |= !justMembers; findNominalsAndOperators(extendedNominals, memberOperatorDecls, ED->getMembers()); break; } case DeclKind::InfixOperator: case DeclKind::PrefixOperator: case DeclKind::PostfixOperator: out << "- \"" << escape(cast<OperatorDecl>(D)->getName()) << "\"\n"; break; case DeclKind::PrecedenceGroup: out << "- \"" << escape(cast<PrecedenceGroupDecl>(D)->getName()) << "\"\n"; break; case DeclKind::Enum: case DeclKind::Struct: case DeclKind::Class: case DeclKind::Protocol: { auto *NTD = cast<NominalTypeDecl>(D); if (!NTD->hasName()) break; if (NTD->hasAccessibility() && NTD->getFormalAccess() <= Accessibility::FilePrivate) { break; } out << "- \"" << escape(NTD->getName()) << "\"\n"; extendedNominals[NTD] |= true; findNominalsAndOperators(extendedNominals, memberOperatorDecls, NTD->getMembers()); break; } case DeclKind::TypeAlias: case DeclKind::Var: case DeclKind::Func: { auto *VD = cast<ValueDecl>(D); if (!VD->hasName()) break; if (VD->hasAccessibility() && VD->getFormalAccess() <= Accessibility::FilePrivate) { break; } out << "- \"" << escape(VD->getName()) << "\"\n"; break; } case DeclKind::PatternBinding: case DeclKind::TopLevelCode: case DeclKind::IfConfig: // No action necessary. break; case DeclKind::EnumCase: case DeclKind::GenericTypeParam: case DeclKind::AssociatedType: case DeclKind::Param: case DeclKind::Subscript: case DeclKind::Constructor: case DeclKind::Destructor: case DeclKind::EnumElement: llvm_unreachable("cannot appear at the top level of a file"); } } // This is also part of "provides-top-level". for (auto *operatorFunction : memberOperatorDecls) out << "- \"" << escape(operatorFunction->getName()) << "\"\n"; out << "provides-nominal:\n"; for (auto entry : extendedNominals) { if (!entry.second) continue; out << "- \""; out << mangleTypeAsContext(entry.first); out << "\"\n"; } out << "provides-member:\n"; for (auto entry : extendedNominals) { out << "- [\""; out << mangleTypeAsContext(entry.first); out << "\", \"\"]\n"; } // This is also part of "provides-member". for (auto *ED : extensionsWithJustMembers) { auto mangledName = mangleTypeAsContext( ED->getExtendedType()->getAnyNominal()); for (auto *member : ED->getMembers()) { auto *VD = dyn_cast<ValueDecl>(member); if (!VD || !VD->hasName() || VD->getFormalAccess() <= Accessibility::FilePrivate) { continue; } out << "- [\"" << mangledName << "\", \"" << escape(VD->getName()) << "\"]\n"; } } if (SF->getASTContext().LangOpts.EnableObjCInterop) { // FIXME: This requires a traversal of the whole file to compute. // We should (a) see if there's a cheaper way to keep it up to date, // and/or (b) see if we can fast-path cases where there's no ObjC involved. out << "provides-dynamic-lookup:\n"; class NameCollector : public VisibleDeclConsumer { private: SmallVector<Identifier, 16> names; public: void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { names.push_back(VD->getName()); } ArrayRef<Identifier> getNames() { llvm::array_pod_sort(names.begin(), names.end(), [](const Identifier *lhs, const Identifier *rhs) { return lhs->compare(*rhs); }); names.erase(std::unique(names.begin(), names.end()), names.end()); return names; } }; NameCollector collector; SF->lookupClassMembers({}, collector); for (Identifier name : collector.getNames()) { out << "- \"" << escape(name) << "\"\n"; } } ReferencedNameTracker *tracker = SF->getReferencedNameTracker(); auto sortedByIdentifier = [](const llvm::DenseMap<Identifier, bool> map) -> SmallVector<std::pair<Identifier, bool>, 16> { SmallVector<std::pair<Identifier, bool>, 16> pairs{map.begin(), map.end()}; llvm::array_pod_sort(pairs.begin(), pairs.end(), [](const std::pair<Identifier, bool> *first, const std::pair<Identifier, bool> *second) -> int { return first->first.compare(second->first); }); return pairs; }; out << "depends-top-level:\n"; for (auto &entry : sortedByIdentifier(tracker->getTopLevelNames())) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-member:\n"; auto &memberLookupTable = tracker->getUsedMembers(); using TableEntryTy = std::pair<ReferencedNameTracker::MemberPair, bool>; std::vector<TableEntryTy> sortedMembers{ memberLookupTable.begin(), memberLookupTable.end() }; llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(), [](const TableEntryTy *lhs, const TableEntryTy *rhs) -> int { if (lhs->first.first == rhs->first.first) return lhs->first.second.compare(rhs->first.second); if (lhs->first.first->getName() != rhs->first.first->getName()) return lhs->first.first->getName().compare(rhs->first.first->getName()); // Break type name ties by mangled name. auto lhsMangledName = mangleTypeAsContext(lhs->first.first); auto rhsMangledName = mangleTypeAsContext(rhs->first.first); return lhsMangledName.compare(rhsMangledName); }); for (auto &entry : sortedMembers) { assert(entry.first.first != nullptr); if (entry.first.first->hasAccessibility() && entry.first.first->getFormalAccess() <= Accessibility::FilePrivate) continue; out << "- "; if (!entry.second) out << "!private "; out << "[\""; out << mangleTypeAsContext(entry.first.first); out << "\", \""; if (!entry.first.second.empty()) out << escape(entry.first.second); out << "\"]\n"; } out << "depends-nominal:\n"; for (auto i = sortedMembers.begin(), e = sortedMembers.end(); i != e; ++i) { bool isCascading = i->second; while (i+1 != e && i[0].first.first == i[1].first.first) { ++i; isCascading |= i->second; } if (i->first.first->hasAccessibility() && i->first.first->getFormalAccess() <= Accessibility::FilePrivate) continue; out << "- "; if (!isCascading) out << "!private "; out << "\""; out << mangleTypeAsContext(i->first.first); out << "\"\n"; } out << "depends-dynamic-lookup:\n"; for (auto &entry : sortedByIdentifier(tracker->getDynamicLookupNames())) { assert(!entry.first.empty()); out << "- "; if (!entry.second) out << "!private "; out << "\"" << escape(entry.first) << "\"\n"; } out << "depends-external:\n"; for (auto &entry : reversePathSortedFilenames(depTracker.getDependencies())) { out << "- \"" << llvm::yaml::escape(entry) << "\"\n"; } llvm::SmallString<32> interfaceHash; SF->getInterfaceHash(interfaceHash); out << "interface-hash: \"" << interfaceHash << "\"\n"; return false; }