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.PathBasedFile
|
||||||
import xsbti.VirtualFile
|
import xsbti.VirtualFile
|
||||||
import xsbti.VirtualFileRef
|
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.
|
* 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],
|
builtinImports: Seq[String],
|
||||||
offset: Int
|
offset: Int
|
||||||
): ParsedFile = {
|
): ParsedFile = {
|
||||||
|
def loseTree(l: (String, Tree, LineRange)): (String, LineRange) = (l._1, l._3)
|
||||||
val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines)
|
val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines)
|
||||||
val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements)
|
val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements)
|
||||||
val (definitions, settings) = splitSettingsDefinitions(
|
val (definitions, settings) = splitSettingsDefinitions(
|
||||||
addOffsetToRange(offset, settingsAndDefinitions)
|
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) =
|
private def resolveBase(f: File, p: Project) =
|
||||||
p.copy(base = IO.resolve(f, p.base))
|
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) }
|
lines.map { (s, i) => (s, i + offset) }
|
||||||
|
|
||||||
def addOffsetToRange(offset: Int, ranges: Seq[(String, LineRange)]): Seq[(String, LineRange)] =
|
private def addOffsetToRange(
|
||||||
ranges.map { (s, r) => (s, r.shift(offset)) }
|
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.
|
* 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(
|
private[sbt] def splitExpressions(
|
||||||
file: VirtualFileRef,
|
file: VirtualFileRef,
|
||||||
lines: Seq[String]
|
lines: Seq[String]
|
||||||
): (Seq[(String, Int)], Seq[(String, LineRange)]) =
|
): (Seq[(String, Int)], Seq[(String, Tree, LineRange)]) =
|
||||||
val split = SbtParser(file, lines)
|
val split = SbtParser(file, lines)
|
||||||
// TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different
|
// TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different
|
||||||
// scala compiler rather than re-parsing.
|
// 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(
|
private def splitSettingsDefinitions(
|
||||||
lines: Seq[(String, LineRange)]
|
lines: Seq[(String, Tree, LineRange)]
|
||||||
): (Seq[(String, LineRange)], Seq[(String, LineRange)]) =
|
): (Seq[(String, Tree, LineRange)], Seq[(String, Tree, LineRange)]) =
|
||||||
lines partition { case (line, _) => isDefinition(line) }
|
lines partition { case (_, tree, _) => isDefinition(tree) }
|
||||||
|
|
||||||
private def isDefinition(line: String): Boolean = {
|
private def isDefinition(tree: Tree): Boolean = {
|
||||||
val trimmed = line.trim
|
tree match {
|
||||||
DefinitionKeywords.exists(trimmed.startsWith(_))
|
case Annotated(arg, annot) => isDefinition(arg)
|
||||||
|
case _: ValOrDefDef => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def extractedValTypes: Seq[String] =
|
private def extractedValTypes: Seq[String] =
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ package parser
|
||||||
|
|
||||||
import sbt.internal.util.LineRange
|
import sbt.internal.util.LineRange
|
||||||
import xsbti.VirtualFileRef
|
import xsbti.VirtualFileRef
|
||||||
|
import dotty.tools.dotc.ast.untpd.Tree
|
||||||
|
|
||||||
object SplitExpressions:
|
object SplitExpressions:
|
||||||
type SplitExpression =
|
type SplitExpression =
|
||||||
(VirtualFileRef, Seq[String]) => (Seq[(String, Int)], Seq[(String, LineRange)])
|
(VirtualFileRef, Seq[String]) => (Seq[(String, Int)], Seq[(String, Tree, LineRange)])
|
||||||
end SplitExpressions
|
end SplitExpressions
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ package parser
|
||||||
|
|
||||||
import sbt.internal.util.LineRange
|
import sbt.internal.util.LineRange
|
||||||
import xsbti.VirtualFileRef
|
import xsbti.VirtualFileRef
|
||||||
|
import dotty.tools.dotc.ast.untpd.Tree
|
||||||
|
|
||||||
trait SplitExpression {
|
trait SplitExpression {
|
||||||
extension (splitter: SplitExpressions.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)
|
splitter(VirtualFileRef.of("noFile"), s.split('\n').toSeq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +54,16 @@ trait SplitExpressionsBehavior extends SplitExpression { this: verify.BasicTestS
|
||||||
assert(imports.size == 2)
|
assert(imports.size == 2)
|
||||||
assert(settingsAndDefs.size == 1)
|
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