Skip to content

Commit

Permalink
Allow clang source module targets to be documented using the plugin (#95
Browse files Browse the repository at this point in the history
)

* Allow clang source module targets to be documented using the plugin

With this change you can now treat C/C++ targets through clang in
the same way as Swift. They can be included in generated doccarchive
and previewed in the same way.

Update the uses of SwiftSourceModuleTarget to use the more general
SourceModuleTarget.

* Merge the patch with the latest swift-docc-plugin changes

* Code review feedback - rename swiftSourceModuleTarget to sourceModuleTarget
  • Loading branch information
cmcgee1024 authored Sep 5, 2024
1 parent af7cb03 commit 300ee6c
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import PackagePlugin
enum ArgumentParsingError: LocalizedError, CustomStringConvertible {
case unknownProduct(_ productName: String, compatibleProducts: String)
case unknownTarget(_ targetName: String, compatibleTargets: String)
case productDoesNotContainSwiftSourceModuleTargets(String)
case packageDoesNotContainSwiftSourceModuleTargets
case targetIsNotSwiftSourceModule(String)
case productDoesNotContainSourceModuleTargets(String)
case packageDoesNotContainSourceModuleTargets
case targetIsNotSourceModule(String)
case testTarget(String)

var description: String {
Expand All @@ -31,13 +31,13 @@ enum ArgumentParsingError: LocalizedError, CustomStringConvertible {
compatible targets: \(compatibleTargets)
"""
case .productDoesNotContainSwiftSourceModuleTargets(let string):
case .productDoesNotContainSourceModuleTargets(let string):
return "product '\(string)' does not contain any Swift source modules"
case .targetIsNotSwiftSourceModule(let string):
case .targetIsNotSourceModule(let string):
return "target '\(string)' is not a Swift source module"
case .testTarget(let string):
return "target '\(string)' is a test target; only library and executable targets are supported by Swift-DocC"
case .packageDoesNotContainSwiftSourceModuleTargets:
case .packageDoesNotContainSourceModuleTargets:
return "the current package does not contain any compatible Swift source modules"
}
}
Expand All @@ -48,11 +48,11 @@ enum ArgumentParsingError: LocalizedError, CustomStringConvertible {
}

extension ArgumentExtractor {
mutating func extractSpecifiedTargets(in package: Package) throws -> [SwiftSourceModuleTarget] {
mutating func extractSpecifiedTargets(in package: Package) throws -> [SourceModuleTarget] {
let specifiedProducts = extractOption(named: "product")
let specifiedTargets = extractOption(named: "target")

let productTargets = try specifiedProducts.flatMap { specifiedProduct -> [SwiftSourceModuleTarget] in
let productTargets = try specifiedProducts.flatMap { specifiedProduct -> [SourceModuleTarget] in
let product = package.allProducts.first { product in
product.name == specifiedProduct
}
Expand All @@ -64,21 +64,21 @@ extension ArgumentExtractor {
)
}

let supportedSwiftSourceModuleTargets = product.targets.compactMap { target in
target as? SwiftSourceModuleTarget
let supportedSourceModuleTargets = product.targets.compactMap { target in
target as? SourceModuleTarget
}
.filter { swiftSourceModuleTarget in
return swiftSourceModuleTarget.kind != .test
.filter { sourceModuleTarget in
return sourceModuleTarget.kind != .test
}

guard !supportedSwiftSourceModuleTargets.isEmpty else {
throw ArgumentParsingError.productDoesNotContainSwiftSourceModuleTargets(specifiedProduct)
guard !supportedSourceModuleTargets.isEmpty else {
throw ArgumentParsingError.productDoesNotContainSourceModuleTargets(specifiedProduct)
}

return supportedSwiftSourceModuleTargets
return supportedSourceModuleTargets
}

let targets = try specifiedTargets.map { specifiedTarget -> SwiftSourceModuleTarget in
let targets = try specifiedTargets.map { specifiedTarget -> SourceModuleTarget in
let target = package.allTargets.first { target in
target.name == specifiedTarget
}
Expand All @@ -90,15 +90,15 @@ extension ArgumentExtractor {
)
}

guard let swiftSourceModuleTarget = target as? SwiftSourceModuleTarget else {
throw ArgumentParsingError.targetIsNotSwiftSourceModule(specifiedTarget)
guard let sourceModuleTarget = target as? SourceModuleTarget else {
throw ArgumentParsingError.targetIsNotSourceModule(specifiedTarget)
}

guard swiftSourceModuleTarget.kind != .test else {
guard sourceModuleTarget.kind != .test else {
throw ArgumentParsingError.testTarget(specifiedTarget)
}

return swiftSourceModuleTarget
return sourceModuleTarget
}

return productTargets + targets
Expand Down
12 changes: 6 additions & 6 deletions Plugins/SharedPackagePluginExtensions/PackageExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ extension Package {

/// All targets defined in this package and its dependencies that
/// can produce documentation.
var allDocumentableTargets: [SwiftSourceModuleTarget] {
var allDocumentableTargets: [SourceModuleTarget] {
return allTargets.documentableTargets
}

Expand All @@ -83,7 +83,7 @@ extension Package {
/// All targets defined directly in this package that produce documentation.
///
/// Excludes targets defined in dependencies.
var topLevelDocumentableTargets: [SwiftSourceModuleTarget] {
var topLevelDocumentableTargets: [SourceModuleTarget] {
var insertedTargetIds = Set<Target.ID>()
var topLevelTargets = [Target]()

Expand Down Expand Up @@ -125,25 +125,25 @@ extension Package {
return allDocumentableProducts.map(\.name.singleQuoted).joined(separator: ", ")
}

func package(for target: SwiftSourceModuleTarget) -> Package? {
func package(for target: SourceModuleTarget) -> Package? {
let possiblePackages = dependencies.map(\.package) + [self]

return possiblePackages.first { package in
package.containsTarget(target)
}
}

func containsTarget(_ target: SwiftSourceModuleTarget) -> Bool {
func containsTarget(_ target: SourceModuleTarget) -> Bool {
return targets.contains { packageTarget in
packageTarget.id == target.id
}
}
}

private extension Collection where Element == Target {
var documentableTargets: [SwiftSourceModuleTarget] {
var documentableTargets: [SourceModuleTarget] {
return compactMap { target in
guard let swiftSourceModuleTarget = target as? SwiftSourceModuleTarget else {
guard let swiftSourceModuleTarget = target as? SourceModuleTarget else {
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension PackageManager {

/// Returns the relevant symbols graphs for Swift-DocC documentation generation for the given target.
func doccSymbolGraphs(
for target: SwiftSourceModuleTarget,
for target: SourceModuleTarget,
context: PluginContext,
verbose: Bool,
snippetExtractor: SnippetExtractor?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import PackagePlugin

extension SnippetExtractor {
func generateSnippets(
for target: SwiftSourceModuleTarget,
for target: SourceModuleTarget,
context: PluginContext
) throws -> URL? {
guard let package = context.package.package(for: target) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import PackagePlugin

extension SwiftSourceModuleTarget {
extension SourceModuleTarget {
/// Returns the default options that should be used for generating a symbol graph for the
/// current target in the given package.
func defaultSymbolGraphOptions(in package: Package) -> PackageManager.SymbolGraphOptions {
Expand Down
44 changes: 29 additions & 15 deletions Plugins/Swift-DocC Convert/SwiftDocCConvert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import PackagePlugin
var argumentExtractor = ArgumentExtractor(arguments)
let specifiedTargets = try argumentExtractor.extractSpecifiedTargets(in: context.package)

let swiftSourceModuleTargets: [SwiftSourceModuleTarget]
let sourceModuleTargets: [SourceModuleTarget]
if specifiedTargets.isEmpty {
swiftSourceModuleTargets = context.package.allDocumentableTargets
sourceModuleTargets = context.package.allDocumentableTargets
} else {
swiftSourceModuleTargets = specifiedTargets
sourceModuleTargets = specifiedTargets
}

guard !swiftSourceModuleTargets.isEmpty else {
throw ArgumentParsingError.packageDoesNotContainSwiftSourceModuleTargets
guard !sourceModuleTargets.isEmpty else {
throw ArgumentParsingError.packageDoesNotContainSourceModuleTargets
}

// Parse the given command-line arguments
Expand Down Expand Up @@ -66,13 +66,13 @@ import PackagePlugin
try? FileManager.default.createDirectory(at: intermediateArchivesDirectory, withIntermediateDirectories: true)

// An inner function that defines the work to build documentation for a given target.
func performBuildTask(_ task: DocumentationBuildGraph<SwiftSourceModuleTarget>.Task) throws -> URL? {
func performBuildTask(_ task: DocumentationBuildGraph<SourceModuleDocumentationBuildGraphTarget>.Task) throws -> URL? {
let target = task.target
print("Extracting symbol information for '\(target.name)'...")
let symbolGraphGenerationStartTime = DispatchTime.now()

let symbolGraphs = try packageManager.doccSymbolGraphs(
for: target,
for: target.sourceTarget,
context: context,
verbose: verbose,
snippetExtractor: snippetExtractor,
Expand All @@ -88,7 +88,7 @@ import PackagePlugin
DocC catalog and will not produce documentation
"""

if swiftSourceModuleTargets.count > 1 {
if sourceModuleTargets.count > 1 {
// We're building multiple targets, just emit a warning for this
// one target that does not produce documentation.
Diagnostics.warning(message)
Expand All @@ -107,8 +107,8 @@ import PackagePlugin
intermediateArchivesDirectory.appendingPathComponent("\(target.name).doccarchive", isDirectory: true).path
}

let archiveOutputPath = archiveOutputDir(for: target)
let dependencyArchivePaths: [String] = isCombinedDocumentationEnabled ? task.dependencies.map { archiveOutputDir(for: $0.target) } : []
let archiveOutputPath = archiveOutputDir(for: target.sourceTarget)
let dependencyArchivePaths: [String] = isCombinedDocumentationEnabled ? task.dependencies.map { archiveOutputDir(for: $0.target.sourceTarget) } : []

if verbose {
print("documentation archive output path: '\(archiveOutputPath)'")
Expand All @@ -120,7 +120,7 @@ import PackagePlugin
// provided.
let doccArguments = parsedArguments.doccArguments(
action: .convert,
targetKind: target.kind == .executable ? .executable : .library,
targetKind: target.sourceTarget.kind == .executable ? .executable : .library,
doccCatalogPath: target.doccCatalogPath,
targetName: target.name,
symbolGraphDirectoryPath: symbolGraphs.unifiedSymbolGraphsDirectory.path,
Expand Down Expand Up @@ -150,7 +150,7 @@ import PackagePlugin
return URL(fileURLWithPath: archiveOutputPath)
}

let buildGraphRunner = DocumentationBuildGraphRunner(buildGraph: .init(targets: swiftSourceModuleTargets))
let buildGraphRunner = DocumentationBuildGraphRunner(buildGraph: .init(targets: sourceModuleTargets.map( {SourceModuleDocumentationBuildGraphTarget(sourceTarget: $0)})))
let intermediateDocumentationArchives = try buildGraphRunner.perform(performBuildTask)
.compactMap { $0 }

Expand Down Expand Up @@ -232,18 +232,32 @@ import PackagePlugin
}
}

// We add the conformance here so that 'DocumentationBuildGraphTarget' doesn't need to know about 'SwiftSourceModuleTarget' or import 'PackagePlugin'.
extension SwiftSourceModuleTarget: DocumentationBuildGraphTarget {
// We add the conformance here so that 'DocumentationBuildGraphTarget' doesn't need to know about 'SourceModuleTarget' or import 'PackagePlugin'.
struct SourceModuleDocumentationBuildGraphTarget: DocumentationBuildGraphTarget {
var sourceTarget: SourceModuleTarget

var dependencyIDs: [String] {
// List all the target dependencies in a flat list.
dependencies.flatMap {
sourceTarget.dependencies.flatMap {
switch $0 {
case .target(let target): return [target.id]
case .product(let product): return product.targets.map { $0.id }
@unknown default: return []
}
}
}

var id: String {
sourceTarget.id
}

var name: String {
sourceTarget.name
}

var doccCatalogPath: String? {
sourceTarget.doccCatalogPath
}
}

private extension String {
Expand Down
2 changes: 1 addition & 1 deletion Plugins/Swift-DocC Preview/SwiftDocCPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import PackagePlugin
var argumentExtractor = ArgumentExtractor(arguments)
let specifiedTargets = try argumentExtractor.extractSpecifiedTargets(in: context.package)

let possibleTargets: [SwiftSourceModuleTarget]
let possibleTargets: [SourceModuleTarget]
if specifiedTargets.isEmpty {
possibleTargets = context.package.topLevelDocumentableTargets
} else {
Expand Down

0 comments on commit 300ee6c

Please sign in to comment.