Skip to content

Commit 20dadc7

Browse files
authored
Merge pull request #14489 from dotty-staging/scaladoc/generation-refactor
Refactor generation configuration in sbt. Generate reference documentation using Scaladoc
2 parents 3ca087c + e92666b commit 20dadc7

File tree

7 files changed

+553
-89
lines changed

7 files changed

+553
-89
lines changed

library/src/scala/quoted/Exprs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Exprs:
55
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
66
*
77
* Usage:
8-
* ```scala
8+
* ```scala sc:nocompile
99
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
1010
* def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = argsExpr match
1111
* case Varargs(Exprs(args)) =>

project/Build.scala

Lines changed: 184 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import java.io.File
22
import java.nio.file._
33

44
import Modes._
5+
import ScaladocGeneration._
56
import com.jsuereth.sbtpgp.PgpKeys
67
import sbt.Keys._
78
import sbt._
@@ -59,6 +60,8 @@ object DottyJSPlugin extends AutoPlugin {
5960
}
6061

6162
object Build {
63+
import ScaladocConfigs._
64+
6265
val referenceVersion = "3.1.2-RC1"
6366

6467
val baseVersion = "3.1.3-RC1"
@@ -1253,6 +1256,8 @@ object Build {
12531256
val generateScalaDocumentation = inputKey[Unit]("Generate documentation for dotty lib")
12541257
val generateTestcasesDocumentation = taskKey[Unit]("Generate documentation for testcases, usefull for debugging tests")
12551258

1259+
val generateReferenceDocumentation = taskKey[Unit]("Generate language reference documentation for Scala 3")
1260+
12561261
lazy val `scaladoc-testcases` = project.in(file("scaladoc-testcases")).
12571262
dependsOn(`scala3-compiler-bootstrapped`).
12581263
settings(commonBootstrappedSettings)
@@ -1287,39 +1292,15 @@ object Build {
12871292
libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").cross(CrossVersion.for3Use2_13)
12881293
)
12891294

1290-
def generateDocumentation(targets: Seq[String], name: String, outDir: String, ref: String, params: Seq[String] = Nil, includeExternalMappings: Boolean = true) =
1295+
def generateDocumentation(configTask: Def.Initialize[Task[GenerationConfig]]) =
12911296
Def.taskDyn {
1292-
val distLocation = (dist / pack).value
1293-
val projectVersion = version.value
1294-
IO.createDirectory(file(outDir))
1295-
val stdLibVersion = stdlibVersion(NonBootstrapped)
1296-
val scalaLib = findArtifactPath(externalCompilerClasspathTask.value, "scala-library")
1297-
val dottyLib = (`scala3-library` / Compile / classDirectory).value
1298-
// TODO add versions etc.
1299-
def srcManaged(v: String, s: String) = s"out/bootstrap/stdlib-bootstrapped/scala-$v/src_managed/main/$s-library-src"
1300-
def scalaSrcLink(v: String, s: String) = s"-source-links:${s}github://scala/scala/v$v#src/library"
1301-
def dottySrcLink(v: String, sourcesPrefix: String = "", outputPrefix: String = "") =
1302-
sys.env.get("GITHUB_SHA") match {
1303-
case Some(sha) =>
1304-
s"-source-links:${sourcesPrefix}github://${sys.env("GITHUB_REPOSITORY")}/$sha$outputPrefix"
1305-
case None => s"-source-links:${sourcesPrefix}github://lampepfl/dotty/$v$outputPrefix"
1306-
}
1307-
1308-
val revision = Seq("-revision", ref, "-project-version", projectVersion)
1309-
val cmd = Seq(
1310-
"-d",
1311-
outDir,
1312-
"-project",
1313-
name,
1314-
scalaSrcLink(stdLibVersion, srcManaged(dottyNonBootstrappedVersion, "scala") + "="),
1315-
dottySrcLink(referenceVersion, srcManaged(dottyNonBootstrappedVersion, "dotty") + "=", "#library/src"),
1316-
dottySrcLink(referenceVersion),
1317-
"-Ygenerate-inkuire",
1318-
) ++ scalacOptionsDocSettings(includeExternalMappings) ++ revision ++ params ++ targets
1319-
import _root_.scala.sys.process._
1320-
val escapedCmd = cmd.map(arg => if(arg.contains(" ")) s""""$arg"""" else arg)
1297+
val config = configTask.value
1298+
config.get[OutputDir].foreach { outDir =>
1299+
IO.createDirectory(file(outDir.value))
1300+
}
1301+
val command = generateCommand(config)
13211302
Def.task {
1322-
(Compile / run).toTask(escapedCmd.mkString(" ", " ", "")).value
1303+
(Compile / run).toTask(command).value
13231304
}
13241305
}
13251306

@@ -1363,66 +1344,65 @@ object Build {
13631344
Test / testcasesSourceRoot := ((`scaladoc-testcases` / baseDirectory).value / "src").getAbsolutePath.toString,
13641345
run / baseDirectory := (ThisBuild / baseDirectory).value,
13651346
generateSelfDocumentation := Def.taskDyn {
1366-
generateDocumentation(
1367-
(Compile / classDirectory).value.getAbsolutePath :: Nil,
1368-
"scaladoc", "scaladoc/output/self", VersionUtil.gitHash, Seq("-usejavacp")
1369-
)
1347+
generateDocumentation(Scaladoc)
13701348
}.value,
1349+
13711350
generateScalaDocumentation := Def.inputTaskDyn {
1372-
val extraArgs = spaceDelimited("[output]").parsed
1373-
val dest = file(extraArgs.headOption.getOrElse("scaladoc/output/scala3")).getAbsoluteFile
1374-
val justAPI = extraArgs.drop(1).headOption == Some("--justAPI")
13751351
val majorVersion = (LocalProject("scala3-library-bootstrapped") / scalaBinaryVersion).value
1376-
val dottyJars: Seq[java.io.File] = Seq(
1377-
(`stdlib-bootstrapped`/Compile/products).value,
1378-
(`scala3-interfaces`/Compile/products).value,
1379-
(`tasty-core-bootstrapped`/Compile/products).value,
1380-
).flatten
1381-
1382-
val roots = dottyJars.map(_.getAbsolutePath)
1383-
1384-
val managedSources =
1385-
(`stdlib-bootstrapped`/Compile/sourceManaged).value / "scala-library-src"
1386-
val projectRoot = (ThisBuild/baseDirectory).value.toPath
1387-
val stdLibRoot = projectRoot.relativize(managedSources.toPath.normalize())
1388-
val docRootFile = stdLibRoot.resolve("rootdoc.txt")
1389-
1390-
val dottyManagesSources =
1391-
(`stdlib-bootstrapped`/Compile/sourceManaged).value / "dotty-library-src"
1392-
1393-
val dottyLibRoot = projectRoot.relativize(dottyManagesSources.toPath.normalize())
1394-
1395-
def generateDocTask =
1396-
generateDocumentation(
1397-
roots, "Scala 3", dest.getAbsolutePath, "main",
1398-
Seq(
1399-
"-comment-syntax", "wiki",
1400-
s"-source-links:docs=github://lampepfl/dotty/main#docs",
1401-
"-doc-root-content", docRootFile.toString,
1402-
"-versions-dictionary-url",
1403-
"https://scala-lang.org/api/versions.json",
1404-
"-Ydocument-synthetic-types",
1405-
s"-snippet-compiler:${dottyLibRoot}/scala/quoted=compile,${dottyLibRoot}/scala/compiletime=compile"
1406-
) ++ (if (justAPI) Nil else Seq("-siteroot", "docs", "-Yapi-subdirectory")), includeExternalMappings = false)
1407-
1408-
if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") }
1409-
else if (justAPI) generateDocTask
1410-
else Def.task{
1411-
IO.write(dest / "versions" / "latest-nightly-base", majorVersion)
1412-
1413-
// This file is used by GitHub Pages when the page is available in a custom domain
1414-
IO.write(dest / "CNAME", "dotty.epfl.ch")
1415-
}.dependsOn(generateDocTask)
1352+
1353+
val extraArgs = spaceDelimited("[<output-dir>] [--justAPI]").parsed
1354+
val outputDirOverride = extraArgs.headOption.fold(identity[GenerationConfig](_))(newDir => {
1355+
config: GenerationConfig => config.add(OutputDir(newDir))
1356+
})
1357+
val justAPIArg: Option[String] = extraArgs.drop(1).find(_ == "--justAPI")
1358+
val justAPI = justAPIArg.fold(identity[GenerationConfig](_))(_ => {
1359+
config: GenerationConfig => config.remove[SiteRoot]
1360+
})
1361+
val overrideFunc = outputDirOverride.andThen(justAPI)
1362+
1363+
val config = Def.task {
1364+
overrideFunc(Scala3.value)
1365+
}
1366+
1367+
val writeAdditionalFiles = Def.task {
1368+
val dest = file(config.value.get[OutputDir].get.value)
1369+
if (justAPIArg.isEmpty) {
1370+
IO.write(dest / "versions" / "latest-nightly-base", majorVersion)
1371+
// This file is used by GitHub Pages when the page is available in a custom domain
1372+
IO.write(dest / "CNAME", "dotty.epfl.ch")
1373+
}
1374+
}
1375+
1376+
writeAdditionalFiles.dependsOn(generateDocumentation(config))
14161377
}.evaluated,
14171378

14181379
generateTestcasesDocumentation := Def.taskDyn {
1419-
generateDocumentation(
1420-
(Test / Build.testcasesOutputDir).value,
1421-
"scaladoc testcases",
1422-
"scaladoc/output/testcases",
1423-
"main",
1424-
Seq("-usejavacp", "-snippet-compiler:scaladoc-testcases/docs=compile", "-siteroot", "scaladoc-testcases/docs")
1380+
generateDocumentation(Testcases)
1381+
}.value,
1382+
1383+
generateReferenceDocumentation := Def.taskDyn {
1384+
val temp = IO.createTemporaryDirectory
1385+
IO.copyDirectory(file("docs"), temp / "docs")
1386+
IO.delete(temp / "docs" / "_blog")
1387+
1388+
IO.copyDirectory(
1389+
file("project") / "resources" / "referenceReplacements",
1390+
temp / "docs",
1391+
overwrite = true
14251392
)
1393+
1394+
val languageReferenceConfig = Def.task {
1395+
Scala3.value
1396+
.add(OutputDir("scaladoc/output/reference"))
1397+
.add(SiteRoot(s"${temp.getAbsolutePath}/docs"))
1398+
.add(ProjectName("Scala 3 Reference"))
1399+
.add(SourceLinks(List(
1400+
dottySrcLink(referenceVersion, temp.getAbsolutePath + "=")
1401+
)))
1402+
.withTargets(List("___fake___.scala"))
1403+
}
1404+
1405+
generateDocumentation(languageReferenceConfig)
14261406
}.value,
14271407

14281408
Test / buildInfoKeys := Seq[BuildInfoKey](
@@ -1803,3 +1783,123 @@ object Build {
18031783
})
18041784
}
18051785
}
1786+
1787+
object ScaladocConfigs {
1788+
import Build._
1789+
private lazy val currentYear: String = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR).toString
1790+
1791+
def dottyExternalMapping = ".*scala/.*::scaladoc3::https://dotty.epfl.ch/api/"
1792+
def javaExternalMapping = ".*java/.*::javadoc::https://docs.oracle.com/javase/8/docs/api/"
1793+
def scalaSrcLink(v: String, s: String) = s"${s}github://scala/scala/v$v#src/library"
1794+
def dottySrcLink(v: String, sourcesPrefix: String = "", outputPrefix: String = "") =
1795+
sys.env.get("GITHUB_SHA") match {
1796+
case Some(sha) =>
1797+
s"${sourcesPrefix}github://${sys.env("GITHUB_REPOSITORY")}/$sha$outputPrefix"
1798+
case None => s"${sourcesPrefix}github://lampepfl/dotty/$v$outputPrefix"
1799+
}
1800+
1801+
lazy val DefaultGenerationConfig = Def.task {
1802+
def distLocation = (dist / pack).value
1803+
def projectVersion = version.value
1804+
def stdLibVersion = stdlibVersion(NonBootstrapped)
1805+
def scalaLib = findArtifactPath(externalCompilerClasspathTask.value, "scala-library")
1806+
def dottyLib = (`scala3-library` / Compile / classDirectory).value
1807+
def srcManaged(v: String, s: String) = s"out/bootstrap/stdlib-bootstrapped/scala-$v/src_managed/main/$s-library-src"
1808+
1809+
def defaultSourceLinks: SourceLinks = SourceLinks(
1810+
List(
1811+
scalaSrcLink(stdLibVersion, srcManaged(dottyNonBootstrappedVersion, "scala") + "="),
1812+
dottySrcLink(referenceVersion, srcManaged(dottyNonBootstrappedVersion, "dotty") + "=", "#library/src"),
1813+
dottySrcLink(referenceVersion),
1814+
"docs=github://lampepfl/dotty/main#docs"
1815+
)
1816+
)
1817+
def socialLinks = SocialLinks(List(
1818+
"github::https://github.com/lampepfl/dotty",
1819+
"discord::https://discord.com/invite/scala",
1820+
"twitter::https://twitter.com/scala_lang",
1821+
))
1822+
def projectLogo = ProjectLogo("docs/_assets/images/logo.svg")
1823+
def skipByRegex = SkipByRegex(List(".+\\.internal($|\\..+)", ".+\\.impl($|\\..+)"))
1824+
def skipById = SkipById(List(
1825+
"scala.runtime.stdLibPatches",
1826+
"scala.runtime.MatchCase"
1827+
))
1828+
def projectFooter = ProjectFooter(s"Copyright (c) 2002-$currentYear, LAMP/EPFL")
1829+
def defaultTemplate = DefaultTemplate("static-site-main")
1830+
GenerationConfig(
1831+
List(),
1832+
ProjectVersion(projectVersion),
1833+
GenerateInkuire(true),
1834+
defaultSourceLinks,
1835+
skipByRegex,
1836+
skipById,
1837+
projectLogo,
1838+
socialLinks,
1839+
projectFooter,
1840+
defaultTemplate,
1841+
Author(true),
1842+
Groups(true)
1843+
)
1844+
}
1845+
1846+
lazy val Scaladoc = Def.task {
1847+
DefaultGenerationConfig.value
1848+
.add(UseJavacp(true))
1849+
.add(ProjectName("scaladoc"))
1850+
.add(OutputDir("scaladoc/output/self"))
1851+
.add(Revision(VersionUtil.gitHash))
1852+
.add(ExternalMappings(List(dottyExternalMapping, javaExternalMapping)))
1853+
.withTargets((Compile / classDirectory).value.getAbsolutePath :: Nil)
1854+
}
1855+
1856+
lazy val Testcases = Def.task {
1857+
val tastyRoots = (Test / Build.testcasesOutputDir).value
1858+
DefaultGenerationConfig.value
1859+
.add(UseJavacp(true))
1860+
.add(OutputDir("scaladoc/output/testcases"))
1861+
.add(ProjectName("scaladoc testcases"))
1862+
.add(Revision("main"))
1863+
.add(SnippetCompiler(List("scaladoc-testcases/docs=compile")))
1864+
.add(SiteRoot("scaladoc-testcases/docs"))
1865+
.add(ExternalMappings(List(dottyExternalMapping, javaExternalMapping)))
1866+
.withTargets(tastyRoots)
1867+
}
1868+
1869+
lazy val Scala3 = Def.task {
1870+
val dottyJars: Seq[java.io.File] = Seq(
1871+
(`stdlib-bootstrapped`/Compile/products).value,
1872+
(`scala3-interfaces`/Compile/products).value,
1873+
(`tasty-core-bootstrapped`/Compile/products).value,
1874+
).flatten
1875+
1876+
val roots = dottyJars.map(_.getAbsolutePath)
1877+
1878+
val managedSources =
1879+
(`stdlib-bootstrapped`/Compile/sourceManaged).value / "scala-library-src"
1880+
val projectRoot = (ThisBuild/baseDirectory).value.toPath
1881+
val stdLibRoot = projectRoot.relativize(managedSources.toPath.normalize())
1882+
val docRootFile = stdLibRoot.resolve("rootdoc.txt")
1883+
1884+
val dottyManagesSources =
1885+
(`stdlib-bootstrapped`/Compile/sourceManaged).value / "dotty-library-src"
1886+
1887+
val dottyLibRoot = projectRoot.relativize(dottyManagesSources.toPath.normalize())
1888+
DefaultGenerationConfig.value
1889+
.add(ProjectName("Scala 3"))
1890+
.add(OutputDir(file("scaladoc/output/scala3").getAbsoluteFile.getAbsolutePath))
1891+
.add(Revision("main"))
1892+
.add(ExternalMappings(List(javaExternalMapping)))
1893+
.add(DocRootContent(docRootFile.toString))
1894+
.add(CommentSyntax("wiki"))
1895+
.add(VersionsDictionaryUrl("https://scala-lang.org/api/versions.json"))
1896+
.add(DocumentSyntheticTypes(true))
1897+
.add(SnippetCompiler(List(
1898+
s"${dottyLibRoot}/scala/quoted=compile",
1899+
s"${dottyLibRoot}/scala/compiletime=compile"
1900+
)))
1901+
.add(SiteRoot("docs"))
1902+
.add(ApiSubdirectory(true))
1903+
.withTargets(roots)
1904+
}
1905+
}

0 commit comments

Comments
 (0)