Skip to content
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

Support --output-path when building documentation for multiple targets. #89

Merged
merged 12 commits into from
Aug 23, 2024

Conversation

d-ronnqvist
Copy link
Contributor

@d-ronnqvist d-ronnqvist commented Aug 20, 2024

Bug/issue #, if applicable:

Summary

Support passing a custom --output-path value when building documentation for multiple targets.

This is technically unrelated to support for combined documentation archives, but it's more likely that developers will pass a custom output path when building combined documentation for multiple targets.

I implemented this PR in steps:

  • First e66ee31 added support for --output-path only when building combined documentation.

  • Then 39d886c extended that support to non-combined documentation builds as well.

    This is technically a change in behavior—each target will output in a subdirectory of the specified output path. However, I considered the old behavior incorrect and not worth preserving because it was broken—each target wrote over the previous target's output, resulting in only one target's output.

Additionally, this PR makes the following changes

  • 78fef3e conditionally customizes the synthesized landing page using Synthesize a minimal landing page for combined archives swift-docc#1011
  • 3aeb675 Refines the command output to add more information to the build tasks and print the resulting archive paths the same when building one target, many targets, or a combined archive.
  • 099fed3 Enables task parallelism when building documentation for more than one target
  • 1df6bb8 adds two additional integration tests that verifies building combined documentation with and without a custom output path.

Dependencies

This builds on top of #87 and #88. The new changes in this PR start with e66ee31 and onwards. You can see that subset of the diff here.

Additionally, this PR conditionally uses some new functionality in swiftlang/swift-docc#1011.

Testing

There are 3 behaviors to test in this PR

  • building documentation for one target with a custom output path
  • building documentation for multiple targets with a custom output path
  • building combined documentation with a custom output path

Note that in all cases you need to disable package sandboxing to use a custom output path.

You can use the package plugin repo itself to test this.

Single target

Build documentation for one target with a custom output path

swift package \
  --disable-sandbox \
  generate-documentation \
  --target Snippets \
  --output-path MyOutputDir

After the build succeeds, the "MyOutputDir" directory should contain the content of the Snippets target's documentation.

Multiple targets

Build documentation for two (or more) targets with a custom output path

swift package \
  --disable-sandbox \
  generate-documentation \
  --target Snippets \
  --target SwiftDocCPlugin \
  --output-path MyOutputDir

After the build succeeds, the "MyOutputDir" directory should contain the two subdirectories named "Snippets.doccarchive" and "SwiftDocCPlugin.doccarchive" that each contain that target's documentation.

Combined targets

Build combined documentation for two (or more) targets with a custom output path

swift package \
  --disable-sandbox \
  generate-documentation \
  --target Snippets \
  --target SwiftDocCPlugin \
  --enable-experimental-combined-documentation \
  --output-path MyOutputDir

After the build succeeds, the "MyOutputDir" directory should contain the content of the combined documentation archive.

The plugin only prints the combined archives's output path. It doesn't print the individual target's output paths, even though they were built to produce the combined archive.

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded
  • [ ] Updated documentation if necessary Not applicable.

@d-ronnqvist
Copy link
Contributor Author

@swift-ci please test

/// Generating symbol graphs is the only task that can't run in parallel.
///
/// We serialize its execution to support build concurrency for the other tasks.
private let symbolGraphGenerationLock = NSLock()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure precisely why. I only know that if we don't serialize the calls to
PackageManager.getSymbolGraph(for:options:) then we consistently this this error, even with only two targets.

I'm suspecting that the plugin script runner doesn't expect another message before it has sent the first response, but I haven't had time to dig through that code yet.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I doubt many package plugins implement parallel task running ;)

@@ -42,8 +42,10 @@ import PackagePlugin
let verbose = parsedArguments.pluginArguments.verbose
let isCombinedDocumentationEnabled = parsedArguments.pluginArguments.enableCombinedDocumentation

let doccFeatures: DocCFeatures?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a precedent for printing timing info like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code printed its timing info like that here. I suspect that it was trying to mimic the way that swift build prints timing:

Build of target: 'SwiftDocCPlugin' complete! (0.32s)

However, the existing code printed two "doing"-style messages ("Generating documentation for '(target.name)'..." and "Converting documentation...") but only measured the latter so I added timing for both of them so that the log would have a balanced number of "doing" and "done" messages.

.compactMap { $0 }
.sorted(by: { $0.lastPathComponent < $1.lastPathComponent })

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason this needs sorting?

Copy link
Contributor Author

@d-ronnqvist d-ronnqvist Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not much. I just wanted a stable sort order in the output of multiple individual targets here. However, I could move the sorting there to signal that the sorting is only for presentation purposes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the sort to where it's used to make it clear it's not needed elsewhere?

@@ -100,6 +109,7 @@ struct ParsedArguments {

arguments.insertIfMissing(.option(DocCArguments.additionalSymbolGraphDirectory, value: symbolGraphDirectoryPath))

assert(arguments.extractOption(named: DocCArguments.outputPath).isEmpty,"The plugin arguments should have extracted the output path.")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if that assertion was at the site where the option should be extracted to keep locality with the potential bug.

@d-ronnqvist d-ronnqvist force-pushed the combined-output-path branch from 5a1607a to 67a795e Compare August 22, 2024 14:38
@d-ronnqvist
Copy link
Contributor Author

@swift-ci please test

@d-ronnqvist d-ronnqvist force-pushed the combined-output-path branch from 67a795e to 4bda3c1 Compare August 22, 2024 15:33
@d-ronnqvist
Copy link
Contributor Author

@swift-ci please test

@d-ronnqvist d-ronnqvist force-pushed the combined-output-path branch from 4bda3c1 to ecb3aed Compare August 23, 2024 07:38
@d-ronnqvist
Copy link
Contributor Author

@swift-ci please test

@d-ronnqvist d-ronnqvist merged commit a255bbd into swiftlang:main Aug 23, 2024
2 checks passed
@d-ronnqvist d-ronnqvist deleted the combined-output-path branch August 23, 2024 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants