swiftlang / swift-docc-plugin

Swift Package Manager command plugin for Swift-DocC
https://swiftpackageindex.com/swiftlang/swift-docc-plugin/documentation/swiftdoccplugin
Apache License 2.0
322 stars 53 forks source link

swift-docc-plugin produces documentation for last target only #12

Closed JosephDuffy closed 2 months ago

JosephDuffy commented 2 years ago

Describe the bug When using swift-docc-plugin to generate documentation any target's documentation will overwrite previously generated documentation. For example if a package has "TargetA" and "TargetB" and the command swift package --allow-writing-to-directory "./docs" generate-documentation --disable-indexing --output-path "./docs" --transform-for-static-hosting --hosting-base-path "PackageName" is run it will generate the documentation for "TargetA" and then "TargetB", but the documentation for "TargetB" will overwrite "TargetA".

To Reproduce Steps to reproduce the behavior:

  1. Create a package with multiple targets
  2. Add swift-docc-plugin as a dependency
  3. Run swift package --allow-writing-to-directory "./docs" generate-documentation --disable-indexing --output-path "./docs" --transform-for-static-hosting --hosting-base-path "PackageName"

Expected behavior A directory suitable for static hosting should be generated, which contains the documentation for "TargetA" and "TargetB".

I think the root index.html should provide links to both of these targets, but I'm not sure if this is an expected feature or a new feature request.

Screenshots N/A

Environment (please complete the following information):

Additional context N/A

ffried commented 2 years ago

There's a use-case where this creates even more confusion. Assume the following Package.swift:

// swift-tools-version:5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyFramework",
            targets: ["SomeTarget", "SomeOtherTarget"]),
        .library(
            name: "MyIrrelevantFramework",
            targets: ["IrrelevantTarget"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
    ],
    targets: [
        .target(name: "SomeTarget"),
        .target(name: "SomeOtherTarget"),
        .target(name: "IrrelevantTarget"),
    ]
)

Now I'd like to build the documentation for the MyFramework product (users will import MyFramework, so I'd expect one documentation page for MyFramework...):

swift package --allow-writing-to-directory ./docs generate-documentation --disable-indexing --transform-for-static-hosting --output-path ./docs --hosting-base-path MyPackage --product MyFramework

This generates the documentation for SomeTarget and SomeOtherTarget, but the latter overwrites the documentation of SomeTarget (similar to the case originally described).

ffried commented 2 years ago

I think the root index.html should provide links to both of these targets, but I'm not sure if this is an expected feature or a new feature request.

This is probably related to this discussion.

JosephDuffy commented 2 years ago

I think the root index.html should provide links to both of these targets, but I'm not sure if this is an expected feature or a new feature request.

This is probably related to this discussion.

Having a root index.html that links to all the targets would certainly fix that issue. If this issue is addressed but doesn't include the root index.html change I'll open another issue.

ethan-kusters commented 2 years ago

Hi @JosephDuffy! Thanks for opening this.

I think there's a couple of things going on here.

The first is that today Swift-DocC only supports generating documentation for a single target at a time. I would definitely be interested in better supporting the use case where you'd like to host documentation for multiple targets on one site, but we'd need to address that in Swift-DocC itself first and then just adopt the feature in the plugin. Filing an issue on the Swift-DocC repo for this would be really appreciated.

Because of this current limitation, the --output-path command-line argument is only supported when also passing a specific --target to the plugin. Otherwise, as you've noted here, each subsequent run of Swift-DocC will overwrite the previous work.

So as things stand today you can either:

  1. Run the plugin without the --output-path flag. In this the case the plugin will create individual directories corresponding to each target in your package so that documentation is never overwritten.
  2. Run the plugin with the --output-path flag and the --target flag to limit the documentation generation to a single target.

I think we should improve the documentation to note this and add a diagnostic that's emitted when the --output-path flag has been passed on an invocation of the plugin that will apply to more than one target.

Is it okay with you if we use this issue to track the diagnostic/documentation improvement as the resolution? And we can look into expanding Swift-DocC to support multi-target documentation in parallel.

ethan-kusters commented 2 years ago

Now I'd like to build the documentation for the MyFramework product (users will import MyFramework, so I'd expect one documentation page for MyFramework...):

@ffried this is incorrect. While users will be able to add a single "MyFramework" product dependency in their Package.swift, they'll still need to import SomeTarget and import SomeOtherTarget since those are distinct frameworks. But this is a great use case to consider when this is implemented in Swift-DocC, we should definitely look at multi-target libraries and see if it makes to automatically generate bundled documentation sites for them. Definitely for the use case you mentioned of passing --product MyFramework I think this would be a much better UX.

ffried commented 2 years ago

this is incorrect. While users will be able to add a single "MyFramework" product dependency in their Package.swift, they'll still need to import SomeTarget and import SomeOtherTarget since those are distinct frameworks.

@ethan-kusters You're absolutely right - my bad! I still wonder how more complex packages would build their documentation, though (looking for example at SwiftNIO which still uses jazzy). Things like @_exported import ABC within a target DEF can "hide" ABC behind DEF. In this case, users could actually expect to find the documentation of ABC.MyObject inside the documentation for DEF.

ffried commented 2 years ago

Filing an issue on the Swift-DocC repo for this would be really appreciated.

Done: https://github.com/apple/swift-docc/issues/255

ffried commented 2 years ago

Just for reference here as well: We're already merging the documentation for multiple targets. It's far from nice, but it works for now. We could save ourselves a lot of work though, if docc is able to do this on its own.

For details, see this comment.

JosephDuffy commented 2 years ago

Hi @JosephDuffy! Thanks for opening this.

I think there's a couple of things going on here.

The first is that today Swift-DocC only supports generating documentation for a single target at a time. I would definitely be interested in better supporting the use case where you'd like to host documentation for multiple targets on one site, but we'd need to address that in Swift-DocC itself first and then just adopt the feature in the plugin. Filing an issue on the Swift-DocC repo for this would be really appreciated.

I've created https://github.com/apple/swift-docc/issues/261 Closed as dupe of https://github.com/apple/swift-docc/issues/255 🤦

Because of this current limitation, the --output-path command-line argument is only supported when also passing a specific --target to the plugin. Otherwise, as you've noted here, each subsequent run of Swift-DocC will overwrite the previous work.

So as things stand today you can either:

1. Run the plugin **without** the `--output-path` flag. In this the case the plugin will create individual directories corresponding to each target in your package so that documentation is never overwritten.

2. Run the plugin **with** the `--output-path` flag **and** the `--target` flag to limit the documentation generation to a single target.

That makes sense, thank you for explaining!

I think we should improve the documentation to note this and add a diagnostic that's emitted when the --output-path flag has been passed on an invocation of the plugin that will apply to more than one target.

That would be a good idea. I had noticed that package plugins supported multiple targets and that the generated static website included the name of the target so I assumed it would all work.

Is it okay with you if we use this issue to track the diagnostic/documentation improvement as the resolution? And we can look into expanding Swift-DocC to support multi-target documentation in parallel.

That's ok. Is it likely that we'll need another issue to actually support the multiple targets once support is added to Swift-DocC? Do you think having a homepage linking to all the targets would fall under that work, or tracked separately?

adam-rocska commented 1 year ago

Is this issue something that a skilled outsider could help resolve?

It would be really lively to have multiple products from one Swift Package covered somehow 😌

navanchauhan commented 10 months ago

At some point I will look into the actual Swift code and see how docc is generating the documentation, but this is the bash script I am using to generate documentation for all the targets:

#!/bin/bash

mkdir -p docs/

for target in "$@"
do
    echo "Generating docs for $target"
    swift package --allow-writing-to-directory "$target-docs" generate-documentation --disable-indexing --transform-for-static-hosting --hosting-base-path swift-gopher --output-path "$target-docs" --target "$target"
    cp -r $target-docs/* docs/
    modified_target=$(echo $target | tr '-' '_' | tr '[:upper:]' '[:lower:]')
    cp -r $target-docs/index/index.json "docs/index/$modified_target.json"
done

echo "<!DOCTYPE html><html><head><meta http-equiv=\"refresh\" content=\"0; url=/swift-gopher/documentation/\" /></head><body><ol>" > docs/index.html

for target in "$@"
do
    cp -R $target-docs/data/documentation/* docs/data/documentation/
    cp -R $target-docs/documentation/* docs/documentation/
    rm -r "$target-docs"
    modified_target=$(echo $target | tr '-' '_' | tr '[:upper:]' '[:lower:]')
    echo "<li><a href=\"/swift-gopher/documentation/$modified_target/\">$target</a></li>" >> docs/index.html
done

echo "</ol></body></html>" >> docs/index.html

custom_javascript="window.location.pathname.split('documentation/')[1].split('/')[0]"
file_to_modify=$(ls docs/js/documentation-topic\~topic\~tutorials-overview.*.js)

sed  -i '' 's/"index.json"/window.location.pathname.split("documentation\/")[1].split("\/")[0]+".json"/g' $file_to_modify
echo "Modified $file_to_modify"

Earlier you could just copy everything from $target/documentation/ and $target/data/documentation, but the JavaScript file for loading the sidebar relies on index.json being accurate. This bash script keeps a copy of each target's index.json and modifies the JavaScript file to load the correct file:

window.location.pathname.split('documentation/')[1].split('/')[0]

This bash script works for deploying the static documentation on GitHub pages using the docs dir. Modify as needed

d-ronnqvist commented 2 months ago

This was recently fixed by https://github.com/swiftlang/swift-docc-plugin/pull/89 which is available in the 1.4 release:

Using a custom --output-path when building documentation for more than one target no longer causes one target to write over the output of the other target. Instead, both targets write their output in subdirectories of the specified output path. When building documentation for a single target, or when building combined documentation, the plugin continues to write the output directly to the specified output path.