Skip to content

Commit 9ba8854

Browse files
Unify generation of Scala 3 API and Reference documentations (#23606)
This PR does unify how snapshot and stable scaladoc artifacts are published: - `docs/sidebar.yml` now contains only entries visible for stable releases - `docs/sidebar.nightly.template.yml` contains entries specific for Snapshot builds (contributing guide, blog posted at https://dotty.epfl.ch/) - it does not contain `reference` subsection entries - this part is now generated when executing `generateScalaDocumentation` task. It emits a patch to `docs/sidebar.yml` containg combined inputs from both files - `project/resources/referenceReplacements/_layouts/static-site-main.html` was removed, instead we now inject the warning only when building snapshot docs using `generateScalaDocumentation` - `project/resources/referenceReplacements/sidebar.yml` is removed, we would no longer need to keep 2 files in sync - `generateStableScala3Documentation` no longer uses calculated paths to obtain compilation outputs - it was only needed in the past to allow for generation of new scaladoc for older compiler/library
1 parent 5d43d02 commit 9ba8854

File tree

8 files changed

+430
-527
lines changed

8 files changed

+430
-527
lines changed

docs/_layouts/static-site-main.html

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,7 @@
44

55
<div class="site-container">
66
<div id="site-header"></div>
7-
{% if page.nightlyOf %}
8-
<aside class="warning">
9-
<div class='icon'></div>
10-
<div class='content'>
11-
This is a nightly documentation. The content of this page may not be
12-
consistent with the current stable version of language. Click
13-
<a href="{{ page.nightlyOf }}">here</a> to find the stable version of this
14-
page.
15-
</div>
16-
</aside>
17-
{% endif %} {{ content }}
7+
{{ content }}
188
<div class="divider" />
199
<nav class="arrow-navigation" aria-label="Page navigation">
2010
{% if page.previous %}

docs/sidebar.nightly.template.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
index: index.md
2+
subsection:
3+
- title: Reference
4+
directory: docs/reference
5+
# Embed sidebar.reference.yml, performed by `generateScalaDocumentation` sbt task
6+
- title: Contributing
7+
directory: docs/contributing
8+
index: contributing/index.md
9+
subsection:
10+
- page: contributing/getting-started.md
11+
- page: contributing/setting-up-your-ide.md
12+
- index: contributing/issues/diagnosing-your-issue.md
13+
subsection:
14+
- page: contributing/issues/reproduce.md
15+
- page: contributing/issues/cause.md
16+
- page: contributing/issues/areas.md
17+
- index: contributing/debugging/debugging.md
18+
subsection:
19+
- page: contributing/debugging/ide-debugging.md
20+
- page: contributing/debugging/inspection.md
21+
- page: contributing/debugging/other-debugging.md
22+
- page: contributing/testing.md
23+
- page: contributing/scaladoc.md
24+
- page: contributing/community-build.md
25+
- page: contributing/sending-in-a-pr.md
26+
- page: contributing/cheatsheet.md
27+
- title: Procedures
28+
directory: procedures
29+
index: contributing/procedures/index.md
30+
subsection:
31+
- page: contributing/procedures/release.md
32+
- page: contributing/procedures/vulpix.md
33+
- title: High Level Architecture
34+
directory: architecture
35+
index: contributing/architecture/index.md
36+
subsection:
37+
- page: contributing/architecture/lifecycle.md
38+
- page: contributing/architecture/context.md
39+
- page: contributing/architecture/phases.md
40+
- page: contributing/architecture/types.md
41+
- page: contributing/architecture/time.md
42+
- page: contributing/architecture/symbols.md
43+
- title: Internals
44+
directory: docs/internals
45+
index: internals/index.md
46+
subsection:
47+
- page: internals/backend.md
48+
- page: internals/classpaths.md
49+
- page: internals/contexts.md
50+
- page: internals/dotc-scalac.md
51+
- page: internals/higher-kinded-v2.md
52+
- page: internals/overall-structure.md
53+
- page: internals/explicit-nulls.md
54+
- page: internals/periods.md
55+
- page: internals/syntax.md
56+
- page: internals/type-system.md
57+
- page: internals/dotty-internals-1-notes.md
58+
- page: internals/debug-macros.md
59+
- page: internals/gadts.md
60+
- page: internals/coverage.md
61+
- page: internals/best-effort-compilation.md
62+
- page: release-notes-0.1.2.md # Referenced from https://www.scala-lang.org/blog/2017/05/31/first-dotty-milestone-release.html
63+
hidden: true

docs/sidebar.yml

Lines changed: 186 additions & 248 deletions
Large diffs are not rendered by default.

project/Build.scala

Lines changed: 149 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3394,11 +3394,17 @@ object Build {
33943394
val testcasesSourceRoot = taskKey[String]("Root directory where tests sources are generated")
33953395
val testDocumentationRoot = taskKey[String]("Root directory where tests documentation are stored")
33963396
val generateSelfDocumentation = taskKey[Unit]("Generate example documentation")
3397-
// Note: the two tasks below should be one, but a bug in Tasty prevents that
3398-
val generateScalaDocumentation = inputKey[Unit]("Generate documentation for dotty lib")
3399-
val generateStableScala3Documentation = inputKey[Unit]("Generate documentation for stable dotty lib")
34003397
val generateTestcasesDocumentation = taskKey[Unit]("Generate documentation for testcases, useful for debugging tests")
34013398

3399+
// Published on https://dotty.epfl.ch/ by nightly builds
3400+
// Contains additional internal/contributing docs
3401+
val generateScalaDocumentation = inputKey[Unit]("Generate documentation for snapshot release")
3402+
3403+
// Published on https://docs.scala-lang.org/api/all.html
3404+
val generateStableScala3Documentation = inputKey[Unit]("Generate documentation for stable release")
3405+
3406+
// Published on https://docs.scala-lang.org/scala3/reference/
3407+
// Does not produce API docs, contains additional redirects for improved stablity
34023408
val generateReferenceDocumentation = inputKey[Unit]("Generate language reference documentation for Scala 3")
34033409

34043410
lazy val `scaladoc-testcases` = project.in(file("scaladoc-testcases")).
@@ -3525,31 +3531,135 @@ object Build {
35253531
val outputDirOverride = extraArgs.headOption.fold(identity[GenerationConfig](_))(newDir => {
35263532
config: GenerationConfig => config.add(OutputDir(newDir))
35273533
})
3528-
val justAPIArg: Option[String] = extraArgs.drop(1).find(_ == "--justAPI")
3529-
val justAPI = justAPIArg.fold(identity[GenerationConfig](_))(_ => {
3530-
config: GenerationConfig => config.remove[SiteRoot]
3531-
})
3532-
val overrideFunc = outputDirOverride.andThen(justAPI)
3534+
val justAPI = extraArgs.contains("--justAPI")
3535+
def justAPIOverride(config: GenerationConfig): GenerationConfig = {
3536+
if (!justAPI) config
3537+
else {
3538+
val siteRoot = IO.createTemporaryDirectory.getAbsolutePath()
3539+
config.add(SiteRoot(siteRoot))
3540+
}
3541+
}
3542+
3543+
// It would be the easiest to create a temp directory and apply patches there, but this task would be used frequently during development
3544+
// If we'd build using copy the emitted warnings would point developers to copies instead of original sources. Any fixes made in there would be lost.
3545+
// Instead let's apply revertable patches to the files as part snapshot doc generation process
3546+
abstract class SourcePatch(val file: File) {
3547+
def apply(): Unit
3548+
def revert(): Unit
3549+
}
3550+
val docs = file("docs")
3551+
val sourcePatches = if (justAPI) Nil else Seq(
3552+
// Generate full sidebar.yml based on template and reference content
3553+
new SourcePatch(docs / "sidebar.yml") {
3554+
val referenceSideBarCopy = IO.temporaryDirectory / "sidebar.yml.copy"
3555+
IO.copyFile(file, referenceSideBarCopy)
3556+
3557+
override def apply(): Unit = {
3558+
val yaml = new org.yaml.snakeyaml.Yaml()
3559+
type YamlObject = java.util.Map[String, AnyRef]
3560+
type YamlList[T] = java.util.List[T]
3561+
def loadYaml(file: File): YamlObject = {
3562+
val reader = Files.newBufferedReader(file.toPath)
3563+
try yaml.load(reader).asInstanceOf[YamlObject]
3564+
finally reader.close()
3565+
}
3566+
// Ensure to always operate on original (Map, List) instances
3567+
val template = loadYaml(docs / "sidebar.nightly.template.yml")
3568+
template.get("subsection")
3569+
.asInstanceOf[YamlList[YamlObject]]
3570+
.stream()
3571+
.filter(_.get("title") == "Reference")
3572+
.findFirst()
3573+
.orElseThrow(() => new IllegalStateException("Reference subsection not found in sidebar.nightly.template.yml"))
3574+
.putAll(loadYaml(referenceSideBarCopy))
3575+
3576+
val sidebarWriter = Files.newBufferedWriter(this.file.toPath)
3577+
try yaml.dump(template, sidebarWriter)
3578+
finally sidebarWriter.close()
3579+
}
3580+
override def revert(): Unit = IO.move(referenceSideBarCopy, file)
3581+
},
3582+
// Add patch about nightly version usage
3583+
new SourcePatch(docs / "_layouts" / "static-site-main.html") {
3584+
lazy val originalContent = IO.read(file)
3585+
3586+
val warningMessage = """{% if page.nightlyOf %}
3587+
| <aside class="warning">
3588+
| <div class='icon'></div>
3589+
| <div class='content'>
3590+
| This is a nightly documentation. The content of this page may not be consistent with the current stable version of language.
3591+
| Click <a href="{{ page.nightlyOf }}">here</a> to find the stable version of this page.
3592+
| </div>
3593+
| </aside>
3594+
|{% endif %}""".stripMargin
3595+
3596+
override def apply(): Unit = {
3597+
IO.write(file,
3598+
originalContent
3599+
.replace("{{ content }}", s"$warningMessage {{ content }}")
3600+
.ensuring(_.contains(warningMessage), "patch to static-site-main layout not applied!")
3601+
)
3602+
}
3603+
override def revert(): Unit = IO.write(file, originalContent)
3604+
}
3605+
)
35333606

35343607
val config = Def.task {
3535-
overrideFunc(Scala3.value)
3608+
outputDirOverride
3609+
.andThen(justAPIOverride)
3610+
.apply(Scala3.value)
35363611
}
35373612

35383613
val writeAdditionalFiles = Def.task {
35393614
val dest = file(config.value.get[OutputDir].get.value)
3540-
if (justAPIArg.isEmpty) {
3615+
if (!justAPI) {
35413616
IO.write(dest / "versions" / "latest-nightly-base", majorVersion)
35423617
// This file is used by GitHub Pages when the page is available in a custom domain
35433618
IO.write(dest / "CNAME", "dotty.epfl.ch")
35443619
}
35453620
}
3621+
val applyPatches = Def.task {
3622+
streams.value.log.info(s"Generating snapshot scaladoc, would apply patches to ${sourcePatches.map(_.file)}")
3623+
sourcePatches.foreach(_.apply())
3624+
}
3625+
val revertPatches = Def.task {
3626+
streams.value.log.info(s"Generated snapshot scaladoc, reverting changes made to ${sourcePatches.map(_.file)}")
3627+
sourcePatches.foreach(_.revert())
3628+
}
35463629

3547-
writeAdditionalFiles.dependsOn(generateDocumentation(config))
3630+
writeAdditionalFiles.dependsOn(
3631+
revertPatches.dependsOn(
3632+
generateDocumentation(config)
3633+
.dependsOn(applyPatches)
3634+
)
3635+
)
35483636
}.evaluated,
35493637

35503638
generateStableScala3Documentation := Def.inputTaskDyn {
35513639
val extraArgs = spaceDelimited("<version>").parsed
3552-
val config = stableScala3(extraArgs.head)
3640+
val version = baseVersion
3641+
// In the early days of scaladoc there was a practice to precompile artifacts of Scala 3 and generate docs using different version of scaladoc
3642+
// It's no longer needed after its stablisation.
3643+
// Allow to use explcit version check to detect using incorrect revision during release process
3644+
extraArgs.headOption.foreach { explicitVersion =>
3645+
assert(
3646+
explicitVersion == version,
3647+
s"Version of the build ($version) does not match the explicit verion ($explicitVersion)"
3648+
)
3649+
}
3650+
3651+
val docs = IO.createTemporaryDirectory
3652+
IO.copyDirectory(file("docs"), docs)
3653+
IO.delete(docs / "_blog")
3654+
3655+
val config = Def.task {
3656+
Scala3.value
3657+
.add(ProjectVersion(version))
3658+
.add(Revision(version))
3659+
.add(OutputDir(s"scaladoc/output/${version}"))
3660+
.add(SiteRoot(docs.getAbsolutePath))
3661+
.remove[ApiSubdirectory]
3662+
}
35533663
generateDocumentation(config)
35543664
}.evaluated,
35553665

@@ -3564,20 +3674,13 @@ object Build {
35643674
generateStaticAssetsTask.value
35653675

35663676
// Move all the source files to a temporary directory and apply some changes specific to the reference documentation
3567-
val temp = IO.createTemporaryDirectory
3568-
IO.copyDirectory(file("docs"), temp / "docs")
3569-
IO.delete(temp / "docs" / "_blog")
3570-
3571-
// Overwrite the main layout and the sidebar
3572-
IO.copyDirectory(
3573-
file("project") / "resources" / "referenceReplacements",
3574-
temp / "docs",
3575-
overwrite = true
3576-
)
3677+
val docs = IO.createTemporaryDirectory
3678+
IO.copyDirectory(file("docs"), docs)
3679+
IO.delete(docs / "_blog")
35773680

35783681
// Add redirections from previously supported URLs, for some pages
35793682
for (name <- Seq("changed-features", "contextual", "dropped-features", "metaprogramming", "other-new-features")) {
3580-
val path = temp / "docs" / "_docs" / "reference" / name / s"${name}.md"
3683+
val path = docs / "_docs" / "reference" / name / s"${name}.md"
35813684
val contentLines = IO.read(path).linesIterator.to[collection.mutable.ArrayBuffer]
35823685
contentLines.insert(1, s"redirectFrom: /${name}.html") // Add redirection
35833686
val newContent = contentLines.mkString("\n")
@@ -3587,12 +3690,12 @@ object Build {
35873690
val languageReferenceConfig = Def.task {
35883691
Scala3.value
35893692
.add(OutputDir("scaladoc/output/reference"))
3590-
.add(SiteRoot(s"${temp.getAbsolutePath}/docs"))
3693+
.add(SiteRoot(docs.getAbsolutePath))
35913694
.add(ProjectName("Scala 3 Reference"))
35923695
.add(ProjectVersion(baseVersion))
35933696
.remove[VersionsDictionaryUrl]
35943697
.add(SourceLinks(List(
3595-
s"${temp.getAbsolutePath}=github://scala/scala3/language-reference-stable"
3698+
s"${docs.getParentFile().getAbsolutePath}=github://scala/scala3/language-reference-stable"
35963699
)))
35973700
.withTargets(List("___fake___.scala"))
35983701
}
@@ -4063,57 +4166,40 @@ object ScaladocConfigs {
40634166
}
40644167

40654168
lazy val Scala3 = Def.task {
4169+
val stdlib = { // relative path to the stdlib directory ('library/')
4170+
val projectRoot = (ThisBuild/baseDirectory).value.toPath
4171+
val stdlibRoot = (`scala-library-bootstrapped` / baseDirectory).value
4172+
projectRoot.relativize(stdlibRoot.toPath.normalize())
4173+
}
4174+
40664175
DefaultGenerationSettings.value
40674176
.add(ProjectName("Scala 3"))
40684177
.add(OutputDir(file("scaladoc/output/scala3").getAbsoluteFile.getAbsolutePath))
40694178
.add(Revision("main"))
40704179
.add(ExternalMappings(List(javaExternalMapping)))
4071-
.add(DocRootContent(((`scala-library-bootstrapped` / baseDirectory).value / "src" / "rootdoc.txt").toString))
4180+
.add(DocRootContent((stdlib / "src" / "rootdoc.txt").toString))
40724181
.add(CommentSyntax(List(
4073-
//s"${dottyLibRoot}=markdown",
4074-
//s"${stdLibRoot}=wiki",
4182+
// Only the files below use markdown syntax (Scala 3 specific sources)
4183+
s"$stdlib/src/scala/NamedTuple.scala=markdown",
4184+
s"$stdlib/src/scala/Tuple.scala=markdown",
4185+
s"$stdlib/src/scala/compiletime=markdown",
4186+
s"$stdlib/src/scala/quoted=markdown",
4187+
s"$stdlib/src/scala/util/boundary.scala=markdown",
4188+
// Scala 2 sources use wiki syntax, we keep it as the default
40754189
"wiki"
40764190
)))
40774191
.add(VersionsDictionaryUrl("https://scala-lang.org/api/versions.json"))
40784192
.add(DocumentSyntheticTypes(true))
4079-
//.add(SnippetCompiler(List(
4080-
//s"$dottyLibRoot/src/scala=compile",
4081-
//s"$dottyLibRoot/src/scala/compiletime=compile",
4082-
//s"$dottyLibRoot/src/scala/util=compile",
4083-
//s"$dottyLibRoot/src/scala/util/control=compile"
4084-
//)))
4193+
.add(SnippetCompiler(List(
4194+
s"$stdlib/src/scala/compiletime=compile",
4195+
s"$stdlib/src/scala/quoted=compile",
4196+
s"$stdlib/src/scala/util/control=compile",
4197+
s"$stdlib/src/scala/util=compile",
4198+
s"$stdlib/src/scala=compile",
4199+
)))
40854200
.add(SiteRoot("docs"))
40864201
.add(ApiSubdirectory(true))
40874202
.withTargets((`scala-library-bootstrapped` / Compile / products).value.map(_.getAbsolutePath))
40884203
}
40894204

4090-
def stableScala3(version: String) = Def.task {
4091-
val scalaLibrarySrc = s"out/bootstrap/scala2-library-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/src_managed"
4092-
val dottyLibrarySrc = "library/src"
4093-
Scala3.value
4094-
.add(defaultSourceLinks(version = version))
4095-
.add(ProjectVersion(version))
4096-
.add(SnippetCompiler(
4097-
List(
4098-
s"$dottyLibrarySrc/scala/quoted=compile",
4099-
s"$dottyLibrarySrc/scala/compiletime=compile",
4100-
s"$dottyLibrarySrc/scala/util=compile",
4101-
s"$dottyLibrarySrc/scala/util/control=compile"
4102-
)
4103-
))
4104-
.add(CommentSyntax(List(
4105-
s"$dottyLibrarySrc=markdown",
4106-
s"$scalaLibrarySrc=wiki",
4107-
"wiki"
4108-
)))
4109-
.add(DocRootContent(s"$scalaLibrarySrc/rootdoc.txt"))
4110-
.withTargets(
4111-
Seq(
4112-
s"tmp/interfaces/target/classes",
4113-
s"out/bootstrap/tasty-core-bootstrapped/scala-$version-bin-SNAPSHOT-nonbootstrapped/classes"
4114-
)
4115-
)
4116-
.remove[SiteRoot]
4117-
.remove[ApiSubdirectory]
4118-
}
41194205
}

project/build.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.0.201803080745-r"
33

44
libraryDependencies += Dependencies.`jackson-databind`
5+
6+
// Used for manipulating YAML files in sidebar generation script
7+
libraryDependencies += "org.yaml" % "snakeyaml" % "2.4"

0 commit comments

Comments
 (0)