-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrap various convert subtasks in signpost intervals #1112
Changes from 8 commits
f10598e
31d5c3c
7f7b106
e0c04f5
66dd6bf
ac8a510
ad18fa8
b9ef2d3
40bb242
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,6 +83,7 @@ public typealias BundleIdentifier = String | |
/// - ``parents(of:)`` | ||
/// | ||
public class DocumentationContext { | ||
private let signposter = ConvertActionConverter.signposter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we pass this into the initializer? ...instead of silently coupling this to Or if you'd prefer to avoid the noise in all the initializers, we could use a global, singleton class/value instead of referring to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very much an implementation detail so it shouldn't show up in initializers or other API. A global would be one option but preferred to namespace it under a type instead of putting that information in the variable name. |
||
|
||
/// An error that's encountered while interacting with a ``SwiftDocC/DocumentationContext``. | ||
public enum ContextError: DescribedError { | ||
|
@@ -563,6 +564,11 @@ public class DocumentationContext { | |
Attempt to resolve links in curation-only documentation, converting any ``TopicReferences`` from `.unresolved` to `.resolved` where possible. | ||
*/ | ||
private func resolveLinks(curatedReferences: Set<ResolvedTopicReference>, bundle: DocumentationBundle) { | ||
let signpostHandle = signposter.beginInterval("Resolve links", id: signposter.makeSignpostID()) | ||
defer { | ||
signposter.endInterval("Resolve links", signpostHandle) | ||
} | ||
|
||
let references = Array(curatedReferences) | ||
let results = Synchronized<[LinkResolveResult]>([]) | ||
results.sync({ $0.reserveCapacity(references.count) }) | ||
|
@@ -708,6 +714,11 @@ public class DocumentationContext { | |
tutorialArticles: [SemanticResult<TutorialArticle>], | ||
bundle: DocumentationBundle | ||
) { | ||
let signpostHandle = signposter.beginInterval("Resolve links", id: signposter.makeSignpostID()) | ||
defer { | ||
signposter.endInterval("Resolve links", signpostHandle) | ||
} | ||
|
||
let sourceLanguages = soleRootModuleReference.map { self.sourceLanguages(for: $0) } ?? [.swift] | ||
|
||
// Tutorial table-of-contents | ||
|
@@ -1147,6 +1158,11 @@ public class DocumentationContext { | |
) throws { | ||
// Making sure that we correctly let decoding memory get released, do not remove the autorelease pool. | ||
try autoreleasepool { | ||
let signpostHandle = signposter.beginInterval("Register symbols", id: signposter.makeSignpostID()) | ||
defer { | ||
signposter.endInterval("Register symbols", signpostHandle) | ||
} | ||
|
||
/// We need only unique relationships so we'll collect them in a set. | ||
var combinedRelationshipsBySelector = [UnifiedSymbolGraph.Selector: Set<SymbolGraph.Relationship>]() | ||
/// Also track the unique relationships across all languages and platforms | ||
|
@@ -1157,7 +1173,9 @@ public class DocumentationContext { | |
var moduleReferences = [String: ResolvedTopicReference]() | ||
|
||
// Build references for all symbols in all of this module's symbol graphs. | ||
let symbolReferences = linkResolver.localResolver.referencesForSymbols(in: symbolGraphLoader.unifiedGraphs, bundle: bundle, context: self) | ||
let symbolReferences = signposter.withIntervalSignpost("Disambiguate references") { | ||
linkResolver.localResolver.referencesForSymbols(in: symbolGraphLoader.unifiedGraphs, bundle: bundle, context: self) | ||
} | ||
|
||
// Set the index and cache storage capacity to avoid ad-hoc storage resizing. | ||
documentationCache.reserveCapacity(symbolReferences.count) | ||
|
@@ -1223,7 +1241,9 @@ public class DocumentationContext { | |
let moduleSymbolReference = SymbolReference(moduleName, interfaceLanguages: moduleInterfaceLanguages, defaultSymbol: moduleSymbol) | ||
moduleReference = ResolvedTopicReference(symbolReference: moduleSymbolReference, moduleName: moduleName, bundle: bundle) | ||
|
||
addSymbolsToTopicGraph(symbolGraph: unifiedSymbolGraph, url: fileURL, symbolReferences: symbolReferences, moduleReference: moduleReference) | ||
signposter.withIntervalSignpost("Add symbols to topic graph", id: signposter.makeSignpostID()) { | ||
addSymbolsToTopicGraph(symbolGraph: unifiedSymbolGraph, url: fileURL, symbolReferences: symbolReferences, moduleReference: moduleReference) | ||
} | ||
|
||
// For inherited symbols we remove the source docs (if inheriting docs is disabled) before creating their documentation nodes. | ||
for (_, relationships) in unifiedSymbolGraph.relationshipsByLanguage { | ||
|
@@ -1375,15 +1395,17 @@ public class DocumentationContext { | |
) | ||
|
||
// Parse and prepare the nodes' content concurrently. | ||
let updatedNodes = Array(documentationCache.symbolReferences).concurrentMap { finalReference in | ||
// Match the symbol's documentation extension and initialize the node content. | ||
let match = uncuratedDocumentationExtensions[finalReference] | ||
let updatedNode = nodeWithInitializedContent(reference: finalReference, match: match) | ||
|
||
return (( | ||
node: updatedNode, | ||
matchedArticleURL: match?.source | ||
)) | ||
let updatedNodes = signposter.withIntervalSignpost("Parse symbol markup", id: signposter.makeSignpostID()) { | ||
Array(documentationCache.symbolReferences).concurrentMap { finalReference in | ||
// Match the symbol's documentation extension and initialize the node content. | ||
let match = uncuratedDocumentationExtensions[finalReference] | ||
let updatedNode = nodeWithInitializedContent(reference: finalReference, match: match) | ||
|
||
return (( | ||
node: updatedNode, | ||
matchedArticleURL: match?.source | ||
)) | ||
} | ||
} | ||
|
||
// Update cache with up-to-date nodes | ||
|
@@ -2177,9 +2199,16 @@ public class DocumentationContext { | |
) | ||
|
||
do { | ||
try symbolGraphLoader.loadAll() | ||
let pathHierarchy = PathHierarchy(symbolGraphLoader: symbolGraphLoader, bundleName: urlReadablePath(bundle.displayName), knownDisambiguatedPathComponents: configuration.convertServiceConfiguration.knownDisambiguatedSymbolPathComponents) | ||
hierarchyBasedResolver = PathHierarchyBasedLinkResolver(pathHierarchy: pathHierarchy) | ||
try signposter.withIntervalSignpost("Load symbols", id: signposter.makeSignpostID()) { | ||
try symbolGraphLoader.loadAll() | ||
} | ||
hierarchyBasedResolver = signposter.withIntervalSignpost("Build PathHierarchy", id: signposter.makeSignpostID()) { | ||
PathHierarchyBasedLinkResolver(pathHierarchy: PathHierarchy( | ||
symbolGraphLoader: symbolGraphLoader, | ||
bundleName: urlReadablePath(bundle.displayName), | ||
knownDisambiguatedPathComponents: configuration.convertServiceConfiguration.knownDisambiguatedSymbolPathComponents | ||
)) | ||
} | ||
} catch { | ||
// Pipe the error out of the dispatch queue. | ||
discoveryError.sync({ | ||
|
@@ -2191,7 +2220,9 @@ public class DocumentationContext { | |
// First, all the resources are added since they don't reference anything else. | ||
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in | ||
do { | ||
try self.registerMiscResources(from: bundle) | ||
try signposter.withIntervalSignpost("Load resources", id: signposter.makeSignpostID()) { | ||
try self.registerMiscResources(from: bundle) | ||
} | ||
} catch { | ||
// Pipe the error out of the dispatch queue. | ||
discoveryError.sync({ | ||
|
@@ -2215,7 +2246,9 @@ public class DocumentationContext { | |
|
||
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in | ||
do { | ||
result = try self.registerDocuments(from: bundle) | ||
result = try signposter.withIntervalSignpost("Load documents", id: signposter.makeSignpostID()) { | ||
try self.registerDocuments(from: bundle) | ||
} | ||
} catch { | ||
// Pipe the error out of the dispatch queue. | ||
discoveryError.sync({ | ||
|
@@ -2226,7 +2259,9 @@ public class DocumentationContext { | |
|
||
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in | ||
do { | ||
try linkResolver.loadExternalResolvers(dependencyArchives: configuration.externalDocumentationConfiguration.dependencyArchives) | ||
try signposter.withIntervalSignpost("Load external resolvers", id: signposter.makeSignpostID()) { | ||
try linkResolver.loadExternalResolvers(dependencyArchives: configuration.externalDocumentationConfiguration.dependencyArchives) | ||
} | ||
} catch { | ||
// Pipe the error out of the dispatch queue. | ||
discoveryError.sync({ | ||
|
@@ -2361,7 +2396,9 @@ public class DocumentationContext { | |
try shouldContinueRegistration() | ||
|
||
// Fourth, automatically curate all symbols that haven't been curated manually | ||
let automaticallyCurated = autoCurateSymbolsInTopicGraph() | ||
let automaticallyCurated = signposter.withIntervalSignpost("Auto-curate symbols ", id: signposter.makeSignpostID()) { | ||
autoCurateSymbolsInTopicGraph() | ||
} | ||
|
||
// Crawl the rest of the symbols that haven't been crawled so far in hierarchy pre-order. | ||
allCuratedReferences = try crawlSymbolCuration(in: automaticallyCurated.map(\.symbol), bundle: bundle, initial: allCuratedReferences) | ||
|
@@ -2407,7 +2444,9 @@ public class DocumentationContext { | |
} | ||
|
||
// Seventh, the complete topic graph—with all nodes and all edges added—is analyzed. | ||
topicGraphGlobalAnalysis() | ||
signposter.withIntervalSignpost("Analyze topic graph", id: signposter.makeSignpostID()) { | ||
topicGraphGlobalAnalysis() | ||
} | ||
|
||
preResolveModuleNames() | ||
} | ||
|
@@ -2606,6 +2645,11 @@ public class DocumentationContext { | |
/// - Returns: The references of all the symbols that were curated. | ||
@discardableResult | ||
func crawlSymbolCuration(in references: [ResolvedTopicReference], bundle: DocumentationBundle, initial: Set<ResolvedTopicReference> = []) throws -> Set<ResolvedTopicReference> { | ||
let signpostHandle = signposter.beginInterval("Curate symbols", id: signposter.makeSignpostID()) | ||
defer { | ||
signposter.endInterval("Curate symbols", signpostHandle) | ||
} | ||
|
||
var crawler = DocumentationCurator(in: self, bundle: bundle, initial: initial) | ||
|
||
for reference in references { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,9 @@ import SymbolKit | |
#if canImport(FoundationNetworking) | ||
import FoundationNetworking | ||
#endif | ||
#if canImport(UniformTypeIdentifiers) | ||
import UniformTypeIdentifiers | ||
#endif | ||
#if os(Windows) | ||
import WinSDK | ||
#endif | ||
|
@@ -116,15 +119,7 @@ public class FileServer { | |
|
||
#if os(macOS) | ||
|
||
let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext as CFString, nil) | ||
guard let fileUTI = unmanagedFileUTI?.takeRetainedValue() else { | ||
return defaultMimeType | ||
} | ||
guard let mimeType = UTTypeCopyPreferredTagWithClass (fileUTI, kUTTagClassMIMEType)?.takeRetainedValue() else { | ||
return defaultMimeType | ||
} | ||
|
||
return (mimeType as NSString) as String | ||
return UTType(filenameExtension: ext)?.preferredMIMEType ?? defaultMimeType | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated improvement? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was a deprecation warning (see the 7f7b106 commit message) once we increased the platform versions to macOS 12 / iOS 15. |
||
|
||
#elseif os(Windows) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any implication here for who can run DocC? Or is DocC already associated with a certain version of Swift that precludes older versions of MacOS and iOS anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but not in practice.
Since most developers use the DocC that comes with Xcode—rather than build DocC from source—this doesn't change what version of macOS they need to use because Xcode requires a newer version of macOS than what DocC does. Xcode 16.1 (the latest release as of this writing) requires macOS 14.5 and the last release of Xcode to support macOS 12 was Xcode 13.4.1.
Linux or Windows are unaffected by this. It is possible that someone would build DocC from source and run it directly (not through Xcode or Swift Package Manager) on older macOS versions in which case they'd need to pin to the 6.1 release branch for the last version of DocC to support those macOS versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@franklinsch and I talked about this in the past and didn't have any concerns about increasing these platform versions.