[2.x] feat: Support doc / skip := true (#8824)

This commit is contained in:
Dream 2026-02-27 08:41:04 -05:00 committed by GitHub
parent 2ee7c26d1e
commit 034834bd71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 89 additions and 73 deletions

View File

@ -117,7 +117,6 @@ import xsbti.compile.{
Compilers,
DefinesClass,
IncOptions,
IncToolOptionsUtil,
Inputs,
MiniSetup,
PerClasspathEntryLookup,
@ -2044,77 +2043,7 @@ object Defaults extends BuildCommon {
Seq("-project", project)
} else Seq.empty
},
(TaskZero / key) := Def.uncached {
val s = streams.value
val cs: Compilers = compilers.value
val srcs = sources.value
val out = target.value
val sOpts = scalacOptions.value
val xapis = apiMappings.value
val hasScala = srcs.exists(_.name.endsWith(".scala"))
val hasJava = srcs.exists(_.name.endsWith(".java"))
val cp = data(dependencyClasspath.value).toList
val label = nameForSrc(configuration.value.name)
val reporter = (compile / bspReporter).value
val converter = fileConverter.value
val tFiles = tastyFiles.value
val sv = scalaVersion.value
val allDeps = allDependencies.value
(hasScala, hasJava) match {
case (true, _) =>
val xapisFiles = xapis.map { (k, v) =>
converter.toPath(k).toFile() -> v
}
val externalApiOpts =
if (ScalaArtifacts.isScala3(sv)) Opts.doc.externalAPIScala3(xapisFiles)
else Opts.doc.externalAPI(xapisFiles)
val options = sOpts ++ externalApiOpts
def convertVfRef(value: String): String =
if !value.contains("$") then value
else converter.toPath(VirtualFileRef.of(value)).toString
val resolvedOptions = options.map { x =>
if !x.contains("$") then x
else x.split(":").map(_.split(",").map(convertVfRef).mkString(",")).mkString(":")
}
val scalac = cs.scalac match
case ac: AnalyzingCompiler => ac.onArgs(Compiler.exported(s, "scaladoc"))
val docSrcFiles = if ScalaArtifacts.isScala3(sv) then tFiles else srcs
// todo: cache this
if docSrcFiles.nonEmpty then
IO.delete(out)
IO.createDirectory(out)
// use PlainVirtualFile since Scaladoc currently doesn't handle actual VirtualFiles
scalac.doc(
docSrcFiles.map(_.toPath()).map(new sbt.internal.inc.PlainVirtualFile(_)),
cp.map(converter.toPath).map(new sbt.internal.inc.PlainVirtualFile(_)),
converter,
out.toPath(),
resolvedOptions,
maxErrors.value,
s.log,
)
else ()
case (_, true) =>
import sbt.internal.inc.javac.JavaCompilerArguments
val javaSourcesOnly: VirtualFile => Boolean = _.id.endsWith(".java")
val classpath = cp.map(converter.toPath).map(converter.toVirtualFile)
val options = javacOptions.value.toList
cs.javaTools.javadoc.run(
srcs.toArray
.map { x =>
converter.toVirtualFile(x.toPath)
}
.filter(javaSourcesOnly),
JavaCompilerArguments(Nil, classpath, options).toArray,
CompileOutput(out.toPath),
IncToolOptionsUtil.defaultIncToolOptions(),
reporter,
s.log,
)
case _ => () // do nothing
}
out
}
(TaskZero / key) := Def.uncached(Compiler.docTask(key).value)
) ++ compilersSetting
)

View File

@ -672,7 +672,7 @@ object Keys {
val sbtDependency = settingKey[ModuleID]("Provides a definition for declaring the current version of sbt.").withRank(BMinusSetting)
val sbtVersion = settingKey[String]("Provides the version of sbt. This setting should not be modified.").withRank(AMinusSetting)
val sbtBinaryVersion = settingKey[String]("Defines the binary compatibility version substring.").withRank(BPlusSetting)
val skip = taskKey[Boolean]("For tasks that support it (currently only 'compile', 'update', 'publish' and 'publishLocal'), setting skip to true will force the task to not to do its work. The exact semantics may vary depending on the task.").withRank(BSetting)
val skip = taskKey[Boolean]("For tasks that support it (currently 'compile', 'update', 'publish', 'publishLocal', and 'doc'), setting skip to true will force the task to not to do its work. The exact semantics may vary depending on the task.").withRank(BSetting)
val templateResolverInfos = settingKey[Seq[TemplateResolverInfo]]("Template resolvers used for 'new'.").withRank(BSetting)
val templateDescriptions = settingKey[Seq[(String, String)]]("List of templates with description used for 'new' / 'init'.")
val templateRunLocal = inputKey[Unit]("Runs a local template.").withRank(DTask)

View File

@ -417,6 +417,82 @@ object Compiler:
private def jsonRpcRequest(id: Long, method: String, params: String): String =
s"""{ "jsonrpc": "2.0", "method": "$method", "params": $params, "id": $id }"""
def docTask(key: TaskKey[File]): Def.Initialize[Task[File]] =
Def.taskIf {
if (key / Keys.skip).value then
val out = (key / Keys.target).value
Keys.streams.value.log.debug(s"Skipping doc for ${Keys.thisProjectRef.value.project}")
out
else
val s = Keys.streams.value
val cs: xsbti.compile.Compilers = Keys.compilers.value
val srcs = Keys.sources.value
val out = (key / Keys.target).value
val sOpts = Keys.scalacOptions.value
val xapis = Keys.apiMappings.value
val hasScala = srcs.exists(_.getName.endsWith(".scala"))
val hasJava = srcs.exists(_.getName.endsWith(".java"))
val cp = Attributed.data(Keys.dependencyClasspath.value).toList
val reporter = (Keys.compile / Keys.bspReporter).value
val converter = Keys.fileConverter.value
val tFiles = Keys.tastyFiles.value
val sv = Keys.scalaVersion.value
(hasScala, hasJava) match {
case (true, _) =>
val xapisFiles = xapis.map { (k, v) =>
converter.toPath(k).toFile() -> v
}
val externalApiOpts =
if (ScalaArtifacts.isScala3(sv)) Opts.doc.externalAPIScala3(xapisFiles)
else Opts.doc.externalAPI(xapisFiles)
val options = sOpts ++ externalApiOpts
def convertVfRef(value: String): String =
if !value.contains("$") then value
else converter.toPath(xsbti.VirtualFileRef.of(value)).toString
val resolvedOptions = options.map { x =>
if !x.contains("$") then x
else x.split(":").map(_.split(",").map(convertVfRef).mkString(",")).mkString(":")
}
val scalac = cs.scalac match
case ac: AnalyzingCompiler => ac.onArgs(exported(s, "scaladoc"))
val docSrcFiles = if ScalaArtifacts.isScala3(sv) then tFiles else srcs
// todo: cache this
if docSrcFiles.nonEmpty then
IO.delete(out)
IO.createDirectory(out)
// use PlainVirtualFile since Scaladoc currently doesn't handle actual VirtualFiles
scalac.doc(
docSrcFiles.map(_.toPath()).map(new sbt.internal.inc.PlainVirtualFile(_)),
cp.map(converter.toPath).map(new sbt.internal.inc.PlainVirtualFile(_)),
converter,
out.toPath(),
resolvedOptions,
Keys.maxErrors.value,
s.log,
)
else ()
case (_, true) =>
import sbt.internal.inc.javac.JavaCompilerArguments
val javaSourcesOnly: xsbti.VirtualFile => Boolean = _.id.endsWith(".java")
val classpath = cp.map(converter.toPath).map(converter.toVirtualFile)
val options = Keys.javacOptions.value.toList
cs.javaTools.javadoc.run(
srcs.toArray
.map { x =>
converter.toVirtualFile(x.toPath)
}
.filter(javaSourcesOnly),
JavaCompilerArguments(Nil, classpath, options).toArray,
sbt.internal.inc.CompileOutput(out.toPath),
xsbti.compile.IncToolOptionsUtil.defaultIncToolOptions(),
reporter,
s.log,
)
case _ => () // do nothing
}
out
}
def consoleForkOptions: Def.Initialize[Task[ForkOptions]] = Def.task {
// Build environment variables for proper terminal handling
val termEnv = sys.env.get("TERM").getOrElse("xterm-256color")

View File

@ -0,0 +1,5 @@
lazy val root = (project in file("."))
.settings(
scalaVersion := "3.7.4",
doc / skip := true,
)

View File

@ -0,0 +1,2 @@
object A:
def hello: String = "hello"

View File

@ -0,0 +1,4 @@
# doc should succeed even with skip := true
> doc
# The target/scala-3.7.4/api directory should not contain generated docs
$ absent target/scala-3.7.4/api/index.html