diff --git a/Plugins/Swift-DocC Convert/SwiftDocCConvert.swift b/Plugins/Swift-DocC Convert/SwiftDocCConvert.swift index db5ba3a..d7664da 100644 --- a/Plugins/Swift-DocC Convert/SwiftDocCConvert.swift +++ b/Plugins/Swift-DocC Convert/SwiftDocCConvert.swift @@ -64,7 +64,18 @@ import PackagePlugin let intermediateArchivesDirectory = URL(fileURLWithPath: context.pluginWorkDirectory.appending("intermediates").string) try? FileManager.default.createDirectory(at: intermediateArchivesDirectory, withIntermediateDirectories: true) - + + // An inner function that configures the process running docc executable with the given + // process and environment values. + func configureDoccProcess(arguments: [String], environment: [String: String]) -> Process { + let process = Process() + process.executableURL = doccExecutableURL + process.arguments = arguments + process.environment = ProcessInfo.processInfo.environment + .merging(environment) { _, new in new } + return process + } + // An inner function that defines the work to build documentation for a given target. func performBuildTask(_ task: DocumentationBuildGraph.Task) throws -> URL? { let target = task.target @@ -128,16 +139,20 @@ import PackagePlugin dependencyArchivePaths: dependencyArchivePaths ) + let doccEnvironment = parsedArguments.doccEnvironment() + if verbose { let arguments = doccArguments.joined(separator: " ") print("docc invocation: '\(doccExecutableURL.path) \(arguments)'") + print("add'l environment: '\(doccEnvironment)'") } - + print("Building documentation for '\(target.name)'...") let conversionStartTime = DispatchTime.now() // Run `docc convert` with the generated arguments and wait until the process completes - let process = try Process.run(doccExecutableURL, arguments: doccArguments) + let process = configureDoccProcess(arguments: doccArguments, environment: doccEnvironment) + try process.run() process.waitUntilExit() // Check whether the `docc convert` invocation was successful. @@ -221,14 +236,19 @@ import PackagePlugin // Remove the combined archive if it already exists try? FileManager.default.removeItem(at: combinedArchiveOutput) - + + let remainingArguments = mergeCommandArguments.remainingArguments + let doccEnvironment = parsedArguments.doccEnvironment() + if verbose { - let arguments = mergeCommandArguments.remainingArguments.joined(separator: " ") + let arguments = remainingArguments.joined(separator: " ") print("docc invocation: '\(doccExecutableURL.path) \(arguments)'") + print("add'l environment: '\(doccEnvironment)'") } - + // Create a new combined archive - let process = try Process.run(doccExecutableURL, arguments: mergeCommandArguments.remainingArguments) + let process = configureDoccProcess(arguments: remainingArguments, environment: doccEnvironment) + try process.run() process.waitUntilExit() print(""" diff --git a/Plugins/Swift-DocC Preview/SwiftDocCPreview.swift b/Plugins/Swift-DocC Preview/SwiftDocCPreview.swift index 3aa9c15..fd089b5 100644 --- a/Plugins/Swift-DocC Preview/SwiftDocCPreview.swift +++ b/Plugins/Swift-DocC Preview/SwiftDocCPreview.swift @@ -115,18 +115,22 @@ import PackagePlugin symbolGraphDirectoryPath: symbolGraphs.unifiedSymbolGraphsDirectory.path, outputPath: parsedArguments.outputDirectory?.path ?? target.doccArchiveOutputPath(in: context) ) - + + let doccEnvironment = parsedArguments.doccEnvironment() + if verbose { let arguments = doccArguments.joined(separator: " ") print("docc invocation: '\(doccExecutableURL.path) \(arguments)'") + print("add'l environment: '\(doccEnvironment)'") } - // Configure the `docc preview` process with the generated arguments + // Configure the `docc preview` process with the generated arguments and environment. let previewProcess = Process() previewProcess.executableURL = doccExecutableURL previewProcess.arguments = doccArguments - - + previewProcess.environment = ProcessInfo.processInfo.environment + .merging(doccEnvironment) { _, new in new } + func stopPreviewProcess() { #if canImport(Darwin) previewProcess.interrupt() diff --git a/Sources/SwiftDocCPluginUtilities/CommandLineArguments/ParsedPluginArguments.swift b/Sources/SwiftDocCPluginUtilities/CommandLineArguments/ParsedPluginArguments.swift index 962c397..a391bc4 100644 --- a/Sources/SwiftDocCPluginUtilities/CommandLineArguments/ParsedPluginArguments.swift +++ b/Sources/SwiftDocCPluginUtilities/CommandLineArguments/ParsedPluginArguments.swift @@ -12,6 +12,7 @@ import Foundation struct ParsedPluginArguments { var enableCombinedDocumentation: Bool var disableLMDBIndex: Bool + var jsonPrettyPrint: Bool var verbose: Bool var help: Bool @@ -19,6 +20,7 @@ struct ParsedPluginArguments { init(extractingFrom arguments: inout CommandLineArguments) { enableCombinedDocumentation = arguments.extractFlag(.enableCombinedDocumentation) ?? false disableLMDBIndex = arguments.extractFlag(.disableLMDBIndex) ?? false + jsonPrettyPrint = arguments.extractFlag(.jsonPrettyPrint) ?? false verbose = arguments.extractFlag(.verbose) ?? false help = arguments.extract(Self.help).last ?? false } diff --git a/Sources/SwiftDocCPluginUtilities/DocumentedPluginFlags/DocumentedArgument.swift b/Sources/SwiftDocCPluginUtilities/DocumentedPluginFlags/DocumentedArgument.swift index 0d1efcc..01e815b 100644 --- a/Sources/SwiftDocCPluginUtilities/DocumentedPluginFlags/DocumentedArgument.swift +++ b/Sources/SwiftDocCPluginUtilities/DocumentedPluginFlags/DocumentedArgument.swift @@ -72,6 +72,16 @@ extension DocumentedArgument { """ ) + /// A plugin feature flag to enable pretty-printed and sorted JSON output. + static let jsonPrettyPrint = Self( + flag: .init(preferred: "--json-prettyprint"), + abstract: "Pretty-print the JSON output of the documentation archive.", + discussion: """ + Formats the JSON files in the documentation archive with spacing, indentation, and sorted \ + keys for a deterministic output. + """ + ) + /// A plugin feature flag to enable verbose logging. static let verbose = Self( flag: .init(preferred: "--verbose"), diff --git a/Sources/SwiftDocCPluginUtilities/HelpInformation.swift b/Sources/SwiftDocCPluginUtilities/HelpInformation.swift index 83087c0..3a9f0a7 100644 --- a/Sources/SwiftDocCPluginUtilities/HelpInformation.swift +++ b/Sources/SwiftDocCPluginUtilities/HelpInformation.swift @@ -45,6 +45,7 @@ public enum HelpInformation { var supportedPluginFlags = [ DocumentedArgument.disableLMDBIndex, + DocumentedArgument.jsonPrettyPrint, DocumentedArgument.verbose, ] diff --git a/Sources/SwiftDocCPluginUtilities/ParsedArguments.swift b/Sources/SwiftDocCPluginUtilities/ParsedArguments.swift index a76e5f3..2ed07e5 100644 --- a/Sources/SwiftDocCPluginUtilities/ParsedArguments.swift +++ b/Sources/SwiftDocCPluginUtilities/ParsedArguments.swift @@ -134,6 +134,20 @@ struct ParsedArguments { return [action.rawValue] + arguments.remainingArguments } + + + /// Returns the enviroment values that should be set in the `docc` process. + /// + /// The returned dictionary contains settings for the `docc` executable that are configured + /// through the process environment, instead of command-line arguments. Merge the values in the + /// dictionary to the process environment running the `docc` executable. + func doccEnvironment() -> [String: String] { + var environment: [String: String] = [:] + if pluginArguments.jsonPrettyPrint { + environment["DOCC_JSON_PRETTYPRINT"] = "YES" + } + return environment + } } enum DocCArguments { diff --git a/Tests/SwiftDocCPluginUtilitiesTests/HelpInformationTests.swift b/Tests/SwiftDocCPluginUtilitiesTests/HelpInformationTests.swift index fab66a0..eda81c6 100644 --- a/Tests/SwiftDocCPluginUtilitiesTests/HelpInformationTests.swift +++ b/Tests/SwiftDocCPluginUtilitiesTests/HelpInformationTests.swift @@ -40,6 +40,8 @@ final class HelpInformationTests: XCTestCase { --disable-indexing, --no-indexing Disable indexing for the produced DocC archive. Produces a DocC archive that is best-suited for hosting online but incompatible with Xcode. + --json-prettyprint Pretty-print the JSON output of the documentation archive. + Formats the JSON files in the documentation archive with spacing, indentation, and sorted keys for a deterministic output. --verbose Increase verbosity to include informational output. SYMBOL GRAPH OPTIONS: @@ -187,6 +189,8 @@ final class HelpInformationTests: XCTestCase { --disable-indexing, --no-indexing Disable indexing for the produced DocC archive. Produces a DocC archive that is best-suited for hosting online but incompatible with Xcode. + --json-prettyprint Pretty-print the JSON output of the documentation archive. + Formats the JSON files in the documentation archive with spacing, indentation, and sorted keys for a deterministic output. --verbose Increase verbosity to include informational output. SYMBOL GRAPH OPTIONS: diff --git a/Tests/SwiftDocCPluginUtilitiesTests/ParsedArgumentsTests.swift b/Tests/SwiftDocCPluginUtilitiesTests/ParsedArgumentsTests.swift index b1c3367..a93ab43 100644 --- a/Tests/SwiftDocCPluginUtilitiesTests/ParsedArgumentsTests.swift +++ b/Tests/SwiftDocCPluginUtilitiesTests/ParsedArgumentsTests.swift @@ -89,6 +89,8 @@ final class ParsedArgumentsTests: XCTestCase { "--output-path", "/my/output-path" ] ) + + XCTAssertEqual(arguments.doccEnvironment(), [:]) } func testDocCArgumentsForOneArgument() { @@ -444,7 +446,46 @@ final class ParsedArgumentsTests: XCTestCase { XCTAssertFalse(doccArguments.contains("--include-extended-types")) XCTAssertFalse(doccArguments.contains("--experimental-skip-synthesized-symbols")) } - + + func testDoccEnvironmentWithJSONPrettyPrint() { + let prettyPrintArguments = ParsedArguments( + ["--json-prettyprint"] + ) + + XCTAssertTrue(prettyPrintArguments.pluginArguments.jsonPrettyPrint) + + XCTAssertEqual( + prettyPrintArguments.doccEnvironment(), + ["DOCC_JSON_PRETTYPRINT": "YES"] + ) + + XCTAssertEqual( + prettyPrintArguments.doccArguments( + action: .convert, + targetKind: .library, + doccCatalogPath: "/my/catalog.docc", + targetName: "MyTarget", + symbolGraphDirectoryPath: "/my/symbol-graph", + outputPath: "/my/output-path" + ), + [ + "convert", + "/my/catalog.docc", + "--emit-lmdb-index", + "--fallback-display-name", "MyTarget", + "--fallback-bundle-identifier", "MyTarget", + "--additional-symbol-graph-dir", "/my/symbol-graph", + "--output-path", "/my/output-path" + ] + ) + + + let defaultArguments = ParsedArguments([]) + + XCTAssertFalse(defaultArguments.pluginArguments.jsonPrettyPrint) + XCTAssertEqual(defaultArguments.doccEnvironment(), [:]) + } + func testSymbolGraphArguments() { do { let arguments = ParsedArguments(["--include-extended-types", "--experimental-skip-synthesized-symbols", "--symbol-graph-minimum-access-level", "internal"])