1
1
// This source file is part of the Swift.org open source project
2
2
//
3
- // Copyright (c) 2022 Apple Inc. and the Swift project authors
3
+ // Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
4
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
5
//
6
6
// See https://swift.org/LICENSE.txt for license information
@@ -56,14 +56,9 @@ import PackagePlugin
56
56
let snippetExtractor : SnippetExtractor ? = nil
57
57
#endif
58
58
59
-
60
- // Iterate over the Swift source module targets we were given.
61
- for (index, target) in swiftSourceModuleTargets. enumerated ( ) {
62
- if index != 0 {
63
- // Emit a line break if this is not the first target being built.
64
- print ( )
65
- }
66
-
59
+ // An inner function that defines the work to build documentation for a given target.
60
+ func performBuildTask( _ task: DocumentationBuildGraph < SwiftSourceModuleTarget > . Task ) throws {
61
+ let target = task. target
67
62
print ( " Generating documentation for ' \( target. name) '... " )
68
63
69
64
let symbolGraphs = try packageManager. doccSymbolGraphs (
@@ -88,12 +83,11 @@ import PackagePlugin
88
83
// We're building multiple targets, just throw a warning for this
89
84
// one target that does not produce documentation.
90
85
Diagnostics . warning ( message)
91
- continue
92
86
} else {
93
87
// This is the only target being built so throw an error
94
88
Diagnostics . error ( message)
95
- return
96
89
}
90
+ return
97
91
}
98
92
}
99
93
@@ -138,15 +132,56 @@ import PackagePlugin
138
132
let describedOutputPath = doccArguments. outputPath ?? " unknown location "
139
133
print ( " Generated DocC archive at ' \( describedOutputPath) ' " )
140
134
} else {
141
- Diagnostics . error ( """
142
- 'docc convert' invocation failed with a nonzero exit code: ' \( process. terminationStatus) '
143
- """
144
- )
135
+ Diagnostics . error ( " 'docc convert' invocation failed with a nonzero exit code: ' \( process. terminationStatus) ' " )
145
136
}
137
+
146
138
}
147
139
140
+ // Create a build graph for all the documentation build tasks.
141
+ let buildGraph = DocumentationBuildGraph ( targets: swiftSourceModuleTargets)
142
+ // Create a serial queue to perform each documentation build task
143
+ let queue = OperationQueue ( )
144
+ queue. maxConcurrentOperationCount = 1
145
+
146
+ // Operations can't raise errors. Instead we catch the error from 'performBuildTask(_:)'
147
+ // and cancel the remaining tasks.
148
+ let errorLock = NSLock ( )
149
+ var caughtError : Error ?
150
+
151
+ let operations = buildGraph. makeOperations { [ performBuildTask] task in
152
+ do {
153
+ try performBuildTask ( task)
154
+ } catch {
155
+ errorLock. withLock {
156
+ caughtError = error
157
+ queue. cancelAllOperations ( )
158
+ }
159
+ }
160
+ }
161
+ // If any of the build tasks raised an error. Rethrow that error.
162
+ if let caughtError {
163
+ throw caughtError
164
+ }
165
+
166
+ // Run all the documentation build tasks in reverse dependency order (dependencies before dependents).
167
+ queue. addOperations ( operations, waitUntilFinished: true )
168
+
148
169
if swiftSourceModuleTargets. count > 1 {
149
170
print ( " \n Multiple DocC archives generated at ' \( context. pluginWorkDirectory. string) ' " )
150
171
}
151
172
}
152
173
}
174
+
175
+ // We add the conformance here so that 'DocumentationBuildGraphTarget' doesn't need to know about 'SwiftSourceModuleTarget' or import 'PackagePlugin'.
176
+ extension SwiftSourceModuleTarget : DocumentationBuildGraphTarget {
177
+ var dependencyIDs : [ String ] {
178
+ // List all the target dependencies in a flat list.
179
+ dependencies. flatMap {
180
+ switch $0 {
181
+ case . target( let target) : return [ target. id]
182
+ case . product( let product) : return product. targets. map { $0. id }
183
+ @unknown default : return [ ]
184
+ }
185
+ }
186
+ }
187
+ }
0 commit comments