Skip to content

Simplify argument parsing and processing in the plugin #88

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

Merged
merged 10 commits into from
Aug 22, 2024
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -32,44 +32,34 @@ extension PackageManager {
}
}

/// Returns the relevant symbols graphs for Swift-DocC documentation generation for
/// the given target.
/// Returns the relevant symbols graphs for Swift-DocC documentation generation for the given target.
func doccSymbolGraphs(
for target: SwiftSourceModuleTarget,
context: PluginContext,
verbose: Bool,
snippetExtractor: SnippetExtractor?,
customSymbolGraphOptions: [PluginFlag],
minimumAccessLevel: SymbolGraphOptions.AccessLevel? = nil
customSymbolGraphOptions: ParsedSymbolGraphArguments
) throws -> DocCSymbolGraphResult {
// First generate the primary symbol graphs containing information about the
// symbols defined in the target itself.

var symbolGraphOptions = target.defaultSymbolGraphOptions(in: context.package)
if let minimumAccessLevel {

if let rawMinimumAccessLevel = customSymbolGraphOptions.minimumAccessLevel,
let minimumAccessLevel = SymbolGraphOptions.AccessLevel(rawValue: rawMinimumAccessLevel)
{
symbolGraphOptions.minimumAccessLevel = minimumAccessLevel
}

// Modify the symbol graph options with the custom ones
for customSymbolGraphOption in customSymbolGraphOptions {
switch customSymbolGraphOption {
case .extendedTypes.positive:
#if swift(>=5.8)
symbolGraphOptions.emitExtensionBlocks = true
#else
print("warning: detected '--include-extended-types' option, which is incompatible with your swift version (required: 5.8)")
#endif
case .extendedTypes.negative:
#if swift(>=5.8)
symbolGraphOptions.emitExtensionBlocks = false
if customSymbolGraphOptions.skipSynthesizedSymbols == true {
symbolGraphOptions.includeSynthesized = false
}

if let includeExtendedTypes = customSymbolGraphOptions.includeExtendedTypes {
#if swift(<5.8)
print("warning: detected '--\(includeExtendedTypes ? "include" : "exclude")-extended-types' option, which is incompatible with your swift version (required: 5.8)")
#else
print("warning: detected '--exclude-extended-types' option, which is incompatible with your swift version (required: 5.8)")
symbolGraphOptions.emitExtensionBlocks = includeExtendedTypes
#endif
case .skipSynthesizedSymbols:
symbolGraphOptions.includeSynthesized = false
default:
fatalError("error: unknown PluginFlag (\(customSymbolGraphOption.parsedValues.joined(separator: ", "))) detected in symbol graph generation - please create an issue at https://github.com/swiftlang/swift-docc-plugin")
}
}

if verbose {
Expand Down
48 changes: 18 additions & 30 deletions Plugins/Swift-DocC Convert/SwiftDocCConvert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,31 @@ import PackagePlugin
throw ArgumentParsingError.packageDoesNotContainSwiftSourceModuleTargets
}

let verbose = argumentExtractor.extractFlag(named: "verbose") > 0
let isCombinedDocumentationEnabled = argumentExtractor.extractFlag(named: PluginFlag.enableCombinedDocumentationSupportFlagName) > 0
// Parse the given command-line arguments
let parsedArguments = ParsedArguments(argumentExtractor.remainingArguments)

// If the `--help` or `-h` flag was passed, print the plugin's help information and exit.
guard !parsedArguments.pluginArguments.help else {
let helpInfo = try HelpInformation.forAction(.convert, doccExecutableURL: doccExecutableURL)
print(helpInfo)
return
}

let verbose = parsedArguments.pluginArguments.verbose
let isCombinedDocumentationEnabled = parsedArguments.pluginArguments.enableCombinedDocumentation

if isCombinedDocumentationEnabled {
let doccFeatures = try? DocCFeatures(doccExecutable: doccExecutableURL)
guard doccFeatures?.contains(.linkDependencies) == true else {
// The developer uses the combined documentation plugin flag with a DocC version that doesn't support combined documentation.
Diagnostics.error("""
Unsupported use of '--\(PluginFlag.enableCombinedDocumentationSupportFlagName)'. \
Unsupported use of '\(DocumentedFlag.enableCombinedDocumentation.names.preferred)'. \
DocC version at '\(doccExecutableURL.path)' doesn't support combined documentation.
""")
return
}
}

// Parse the given command-line arguments
let parsedArguments = ParsedArguments(argumentExtractor.remainingArguments)

// If the `--help` or `-h` flag was passed, print the plugin's help information
// and exit.
guard !parsedArguments.help else {
let helpInformation = try HelpInformation.forAction(
.convert,
doccExecutableURL: doccExecutableURL
)

print(helpInformation)
return
}

#if swift(>=5.7)
let snippetExtractTool = try context.tool(named: "snippet-extract")
let snippetExtractor = SnippetExtractor(
Expand All @@ -79,8 +74,7 @@ import PackagePlugin
context: context,
verbose: verbose,
snippetExtractor: snippetExtractor,
customSymbolGraphOptions: parsedArguments.symbolGraphArguments,
minimumAccessLevel: parsedArguments.arguments.symbolGraphMinimumAccessLevel.flatMap { .init(rawValue: $0) }
customSymbolGraphOptions: parsedArguments.symbolGraphArguments
)

if target.doccCatalogPath == nil,
Expand Down Expand Up @@ -120,16 +114,9 @@ import PackagePlugin
doccCatalogPath: target.doccCatalogPath,
targetName: target.name,
symbolGraphDirectoryPath: symbolGraphs.unifiedSymbolGraphsDirectory.path,
outputPath: doccArchiveOutputPath
outputPath: doccArchiveOutputPath,
dependencyArchivePaths: task.dependencies.map { $0.target.doccArchiveOutputPath(in: context) }
)
if isCombinedDocumentationEnabled {
doccArguments.append(CommandLineOption.enableExternalLinkSupport.defaultName)

for taskDependency in task.dependencies {
let dependencyArchivePath = taskDependency.target.doccArchiveOutputPath(in: context)
doccArguments.append(contentsOf: [CommandLineOption.externalLinkDependency.defaultName, dependencyArchivePath])
}
}

if verbose {
let arguments = doccArguments.joined(separator: " ")
Expand All @@ -149,7 +136,8 @@ import PackagePlugin
if process.terminationReason == .exit && process.terminationStatus == 0 {
print("Conversion complete! (\(conversionDuration.descriptionInSeconds))")

let describedOutputPath = doccArguments.outputPath ?? "unknown location"
var arguments = CommandLineArguments(doccArguments)
let describedOutputPath = arguments.extractOption(named: DocCArguments.outputPath).last ?? "unknown location"
print("Generated DocC archive at '\(describedOutputPath)'")
} else {
Diagnostics.error("'docc convert' invocation failed with a nonzero exit code: '\(process.terminationStatus)'")
Expand Down
14 changes: 6 additions & 8 deletions Plugins/Swift-DocC Preview/SwiftDocCPreview.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -28,19 +28,18 @@ import PackagePlugin
possibleTargets = specifiedTargets
}

let verbose = argumentExtractor.extractFlag(named: "verbose") > 0

// Parse the given command-line arguments
let parsedArguments = ParsedArguments(argumentExtractor.remainingArguments)

// If the `--help` or `-h` flag was passed, print the plugin's help information
// and exit.
guard !parsedArguments.help else {
// If the `--help` or `-h` flag was passed, print the plugin's help information and exit.
guard !parsedArguments.pluginArguments.help else {
let helpInfo = try HelpInformation.forAction(.preview, doccExecutableURL: doccExecutableURL)
print(helpInfo)
return
}

let verbose = parsedArguments.pluginArguments.verbose

// Confirm that at least one compatible target was provided.
guard let target = possibleTargets.first else {
Diagnostics.error("""
Expand Down Expand Up @@ -84,8 +83,7 @@ import PackagePlugin
context: context,
verbose: verbose,
snippetExtractor: snippetExtractor,
customSymbolGraphOptions: parsedArguments.symbolGraphArguments,
minimumAccessLevel: parsedArguments.arguments.symbolGraphMinimumAccessLevel.flatMap { .init(rawValue: $0) }
customSymbolGraphOptions: parsedArguments.symbolGraphArguments
)

if try FileManager.default.contentsOfDirectory(atPath: symbolGraphs.targetSymbolGraphsDirectory.path).isEmpty {
Expand Down
30 changes: 0 additions & 30 deletions Sources/SwiftDocCPluginUtilities/Arguments+outputPath.swift

This file was deleted.

This file was deleted.

10 changes: 0 additions & 10 deletions Sources/SwiftDocCPluginUtilities/Arguments.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors

/// A named command line argument; either a flag or an option with a value.
public struct CommandLineArgument {
/// The names of this command line argument.
public var names: Names
/// The kind of command line argument.
public var kind: Kind

/// A collection of names for a command line argument.
public struct Names: Hashable {
/// The preferred name for this command line argument.
public var preferred: String
/// All possible names for this command line argument.
public var all: Set<String>

/// Creates a new command line argument collection of names.
///
/// - Parameters:
/// - preferred: The preferred name for this command line argument.
/// - alternatives: A collection of alternative names for this command line argument.
public init(preferred: String, alternatives: Set<String> = []) {
self.all = alternatives.union([preferred])
self.preferred = preferred
}
}

/// A kind of command line argument.
public enum Kind {
/// A flag argument without an associated value.
///
/// For example: `"--some-flag"`.
case flag
/// An option argument with an associated value.
///
/// For example: `"--some-option", "value"` or `"--some-option=value"`.
case option(value: String)
}

/// Creates a new command line flag with the given names.
/// - Parameters:
/// - names: The names for the new command line flag.
public static func flag(_ names: Names) -> Self {
.init(names: names, kind: .flag)
}

/// Creates a new command option with the given names and associated value.
/// - Parameters:
/// - names: The names for the new command line option.
/// - value: The value that's associated with this command line option.
public static func option(_ names: Names, value: String) -> Self {
.init(names: names, kind: .option(value: value))
}
}
Loading