[2.x] feat: add rootProject macro (#8671)

Add a rootProject convenience macro that expands to a Project with base "."
and the enclosing val name as id. Allows writing `val root = rootProject`
instead of `val root = (project in file("."))` for a stable root project id.
This commit is contained in:
bitloi 2026-02-01 01:07:10 -05:00 committed by GitHub
parent 080ce8f00e
commit 4430d4c553
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 1 deletions

View File

@ -43,6 +43,10 @@ private[sbt] object KeyMacro:
val name = definingValName(errorMsg2)
'{ Project($name, new File($name)) }
def rootProjectImpl(using Quotes): Expr[Project] =
val name = definingValName(errorMsgRootProject)
'{ Project($name, new File(".")) }
private def summonRuntimeClass[A: Type](using Quotes): Expr[Class[?]] =
val classTag = Expr
.summon[ClassTag[A]]
@ -58,6 +62,9 @@ private[sbt] object KeyMacro:
private def errorMsg2: String =
"""project must be directly assigned to a val, such as `val x = project.in(file("core"))`."""
private def errorMsgRootProject: String =
"""rootProject must be directly assigned to a val, such as `val root = rootProject`."""
private[sbt] def definingValName(errorMsg: String)(using Quotes): Expr[String] =
val term = enclosingTerm
if term.isValDef then Expr(term.name)

View File

@ -4618,6 +4618,13 @@ trait BuildExtra extends BuildCommon with DefExtra {
* The name of the val is used as the project ID and the name of the base directory of the project.
*/
inline def project: Project = ${ std.KeyMacro.projectImpl }
/**
* Creates the root project with base directory ".".
* This is a macro that expects to be assigned directly to a val (e.g. `val root = rootProject`).
* The name of the val is used as the project ID; the base is always the build root.
*/
inline def rootProject: Project = ${ std.KeyMacro.rootProjectImpl }
inline def projectMatrix: ProjectMatrix = ${ ProjectMatrix.projectMatrixImpl }
/**

View File

@ -11,7 +11,7 @@ package sbt
import scala.util.control.NonFatal
import org.scalacheck.*
import Prop.*
import sbt.BuildExtra.project
import sbt.BuildExtra.{ project, rootProject }
import java.io.File
class ProjectDefs {
@ -24,6 +24,8 @@ class ProjectDefs {
val z = (project in new File("dir"))
val root = rootProject
val a: Project = project
lazy val aa: Project = project
@ -62,6 +64,10 @@ object ProjectMacro extends Properties("ProjectMacro") {
check(z, "z", "dir")
}
property("rootProject has base .") = secure {
check(root, "root", ".")
}
def check(p: Project, id: String, dir: String): Prop = {
s"Expected id: $id" |:
s"Expected dir: $dir" |: