mirror of https://github.com/sbt/sbt.git
Merge pull request #8205 from Duhemm/mduhem/build-sbt-annotations
Support annotated definitions in build.sbt
This commit is contained in:
commit
16269ba88e
|
|
@ -22,6 +22,7 @@ import scala.jdk.CollectionConverters.*
|
|||
import xsbti.PathBasedFile
|
||||
import xsbti.VirtualFile
|
||||
import xsbti.VirtualFileRef
|
||||
import dotty.tools.dotc.ast.untpd.{ Annotated, ValOrDefDef, Tree }
|
||||
|
||||
/**
|
||||
* This file is responsible for compiling the .sbt files used to configure sbt builds.
|
||||
|
|
@ -97,12 +98,13 @@ private[sbt] object EvaluateConfigurations {
|
|||
builtinImports: Seq[String],
|
||||
offset: Int
|
||||
): ParsedFile = {
|
||||
def loseTree(l: (String, Tree, LineRange)): (String, LineRange) = (l._1, l._3)
|
||||
val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines)
|
||||
val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements)
|
||||
val (definitions, settings) = splitSettingsDefinitions(
|
||||
addOffsetToRange(offset, settingsAndDefinitions)
|
||||
)
|
||||
new ParsedFile(allImports, definitions, settings)
|
||||
new ParsedFile(allImports, definitions.map(loseTree), settings.map(loseTree))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,11 +196,14 @@ private[sbt] object EvaluateConfigurations {
|
|||
private def resolveBase(f: File, p: Project) =
|
||||
p.copy(base = IO.resolve(f, p.base))
|
||||
|
||||
def addOffset(offset: Int, lines: Seq[(String, Int)]): Seq[(String, Int)] =
|
||||
private def addOffset(offset: Int, lines: Seq[(String, Int)]): Seq[(String, Int)] =
|
||||
lines.map { (s, i) => (s, i + offset) }
|
||||
|
||||
def addOffsetToRange(offset: Int, ranges: Seq[(String, LineRange)]): Seq[(String, LineRange)] =
|
||||
ranges.map { (s, r) => (s, r.shift(offset)) }
|
||||
private def addOffsetToRange(
|
||||
offset: Int,
|
||||
ranges: Seq[(String, Tree, LineRange)]
|
||||
): Seq[(String, Tree, LineRange)] =
|
||||
ranges.map { (s, t, r) => (s, t, r.shift(offset)) }
|
||||
|
||||
/**
|
||||
* The name of the class we cast DSL "setting" (vs. definition) lines to.
|
||||
|
|
@ -286,20 +291,28 @@ private[sbt] object EvaluateConfigurations {
|
|||
private[sbt] def splitExpressions(
|
||||
file: VirtualFileRef,
|
||||
lines: Seq[String]
|
||||
): (Seq[(String, Int)], Seq[(String, LineRange)]) =
|
||||
): (Seq[(String, Int)], Seq[(String, Tree, LineRange)]) =
|
||||
val split = SbtParser(file, lines)
|
||||
// TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different
|
||||
// scala compiler rather than re-parsing.
|
||||
(split.imports, split.settings)
|
||||
(
|
||||
split.imports,
|
||||
split.settings.zip(split.settingsTrees).map { case ((s, r), (_, t)) =>
|
||||
(s, t, r)
|
||||
}
|
||||
)
|
||||
|
||||
private def splitSettingsDefinitions(
|
||||
lines: Seq[(String, LineRange)]
|
||||
): (Seq[(String, LineRange)], Seq[(String, LineRange)]) =
|
||||
lines partition { case (line, _) => isDefinition(line) }
|
||||
lines: Seq[(String, Tree, LineRange)]
|
||||
): (Seq[(String, Tree, LineRange)], Seq[(String, Tree, LineRange)]) =
|
||||
lines partition { case (_, tree, _) => isDefinition(tree) }
|
||||
|
||||
private def isDefinition(line: String): Boolean = {
|
||||
val trimmed = line.trim
|
||||
DefinitionKeywords.exists(trimmed.startsWith(_))
|
||||
private def isDefinition(tree: Tree): Boolean = {
|
||||
tree match {
|
||||
case Annotated(arg, annot) => isDefinition(arg)
|
||||
case _: ValOrDefDef => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
private def extractedValTypes: Seq[String] =
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ package parser
|
|||
|
||||
import sbt.internal.util.LineRange
|
||||
import xsbti.VirtualFileRef
|
||||
import dotty.tools.dotc.ast.untpd.Tree
|
||||
|
||||
object SplitExpressions:
|
||||
type SplitExpression =
|
||||
(VirtualFileRef, Seq[String]) => (Seq[(String, Int)], Seq[(String, LineRange)])
|
||||
(VirtualFileRef, Seq[String]) => (Seq[(String, Int)], Seq[(String, Tree, LineRange)])
|
||||
end SplitExpressions
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ package parser
|
|||
|
||||
import sbt.internal.util.LineRange
|
||||
import xsbti.VirtualFileRef
|
||||
import dotty.tools.dotc.ast.untpd.Tree
|
||||
|
||||
trait SplitExpression {
|
||||
extension (splitter: SplitExpressions.SplitExpression)
|
||||
def apply(s: String): (Seq[(String, Int)], Seq[(String, LineRange)]) =
|
||||
def apply(s: String): (Seq[(String, Int)], Seq[(String, Tree, LineRange)]) =
|
||||
splitter(VirtualFileRef.of("noFile"), s.split('\n').toSeq)
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +54,16 @@ trait SplitExpressionsBehavior extends SplitExpression { this: verify.BasicTestS
|
|||
assert(imports.size == 2)
|
||||
assert(settingsAndDefs.size == 1)
|
||||
}
|
||||
|
||||
test("parse a config containing an annotated definition") {
|
||||
val (imports, settingsAndDefs) = splitter(
|
||||
"""|import foo.Bar
|
||||
|@foo
|
||||
|lazy val root = (project in file(".")).enablePlugins(PlayScala)""".stripMargin
|
||||
)
|
||||
assert(imports.size == 1)
|
||||
assert(settingsAndDefs.size == 1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
import scala.annotation.{tailrec, nowarn}
|
||||
import sbt.util.cacheLevel
|
||||
|
||||
@tailrec
|
||||
def even(x: Int): Boolean = Math.abs(x) match
|
||||
case 0 => true
|
||||
case 1 => false
|
||||
case n => even(n - 2)
|
||||
|
||||
@transient val foo = 4
|
||||
|
||||
@cacheLevel(include = Array.empty)
|
||||
lazy val myTask = taskKey[Boolean]("...")
|
||||
|
||||
@nowarn
|
||||
lazy val myProject = project.settings(
|
||||
myTask := {
|
||||
assert(!file("ran").exists)
|
||||
println("running")
|
||||
IO.touch(file("ran"))
|
||||
even(2)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
$ absent ran
|
||||
> myTask
|
||||
$ exists ran
|
||||
|
||||
# verify the task is not cached (i.e. annotation is not lost)
|
||||
-> myTask
|
||||
Loading…
Reference in New Issue