Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi committed Apr 3, 2023
2 parents 1a9686e + d02ba59 commit a534e90
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 138 deletions.
33 changes: 0 additions & 33 deletions example/misc/1-other-task-types/build.sc

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object InvalidMetaModuleTests extends IntegrationTestSuite {
test("success"){
val res = evalStdout("resolve", "_")
assert(res.isSuccess == false)
assert(res.err.contains("Top-level module in mill-build/build.sc must be of class mill.runner.MillBuildRootModule"))
assert(res.err.contains("Root module in mill-build/build.sc must be of class mill.runner.MillBuildRootModule"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object MultipleTopLevelModulesTests extends IntegrationTestSuite {
test("success"){
val res = evalStdout("resolve", "_")
assert(!res.isSuccess)
assert(res.err.contains("Only one BaseModule can be defined in a build, not 2: millbuild.build$bar$,millbuild.build$foo$"))
assert(res.err.contains("Only one RootModule can be defined in a build, not 2: millbuild.build$bar$,millbuild.build$foo$"))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

def invalidTarget = T{ "..." }

object invalidModule extends Module

object bar extends RootModule
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package mill.integration

import utest._

import scala.util.matching.Regex

object ThingsOutsideTopLevelModuleTests extends IntegrationTestSuite {
val tests = Tests {
val workspaceRoot = initWorkspace()

test("success"){
val res = evalStdout("resolve", "_")
assert(!res.isSuccess)
assert(
res.err.contains(
"RootModule bar$ cannot have other modules defined outside of it: invalidModule"
)
)
}
}
}
106 changes: 105 additions & 1 deletion main/core/src/mill/define/Discover.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
package mill.define

import language.experimental.macros
import scala.collection.mutable
import scala.reflect.macros.blackbox

/**
* Macro to walk the module tree and generate `mainargs` entrypoints for any
* `T.command` methods that it finds.
*
* Note that unlike the rest of Mill's module-handling logic which uses Java
* reflection, generation of entrypoints requires typeclass resolution, and so
* needs to be done at compile time. Thus we walk the entire module tree,
* collecting all the module `Class[_]`s we can find, and for each one generate
* the `mainargs.MainData` containing metadata and resolved typeclasses for all
* the `T.command` methods we find. This mapping from `Class[_]` to `MainData`
* can then be used later to look up the `MainData` for any module.
*/
case class Discover[T] private (value: Map[Class[_], Seq[(Int, mainargs.MainData[_, _])]]) {
private[mill] def copy(value: Map[Class[_], Seq[(Int, mainargs.MainData[_, _])]] = value)
: Discover[T] =
Expand All @@ -10,7 +24,97 @@ case class Discover[T] private (value: Map[Class[_], Seq[(Int, mainargs.MainData
object Discover {
def apply[T](value: Map[Class[_], Seq[(Int, mainargs.MainData[_, _])]]): Discover[T] =
new Discover[T](value)
def apply[T]: Discover[T] = macro mill.define.Router.applyImpl[T]
def apply[T]: Discover[T] = macro Router.applyImpl[T]

private def unapply[T](discover: Discover[T])
: Option[Map[Class[_], Seq[(Int, mainargs.MainData[_, _])]]] = Some(discover.value)

class Router(val ctx: blackbox.Context) extends mainargs.Macros(ctx) {
import c.universe._

def applyImpl[T: WeakTypeTag]: Expr[Discover[T]] = {
val seen = mutable.Set.empty[Type]
def rec(tpe: Type): Unit = {
if (!seen(tpe)) {
seen.add(tpe)
for {
m <- tpe.members
memberTpe = m.typeSignature
if memberTpe.resultType <:< typeOf[mill.define.Module] && memberTpe.paramLists.isEmpty
} rec(memberTpe.resultType)

if (tpe <:< typeOf[mill.define.Cross[_]]) {
val inner = typeOf[Cross[_]]
.typeSymbol
.asClass
.typeParams
.head
.asType
.toType
.asSeenFrom(tpe, typeOf[Cross[_]].typeSymbol)

rec(inner)
}
}
}
rec(weakTypeOf[T])

def assertParamListCounts(
methods: Iterable[MethodSymbol],
cases: (Type, Int, String)*
): Unit = {
for (m <- methods.toList) {
cases
.find{case (tt, n, label) => m.returnType <:< tt}
.foreach{case (tt, n, label) =>
if (m.paramLists.length != n) c.abort(
m.pos,
s"$label definitions must have $n parameter list" + (if (n == 1) "" else "s")
)
}
}
}
val mapping = for {
discoveredModuleType <- seen
curCls = discoveredModuleType
methods = getValsOrMeths(curCls)
overridesRoutes = {
assertParamListCounts(
methods,
(weakTypeOf[mill.define.Command[_]], 1, "`T.command`"),
(weakTypeOf[mill.define.Target[_]], 0, "Target"),
)

for {
m <- methods.toList
if m.returnType <:< weakTypeOf[mill.define.Command[_]]
} yield (
m.overrides.length,
extractMethod(
m.name,
m.paramLists.flatten,
m.pos,
m.annotations.find(_.tree.tpe =:= typeOf[mainargs.main]),
curCls,
weakTypeOf[Any]
)
)

}
if overridesRoutes.nonEmpty
} yield {
// by wrapping the `overridesRoutes` in a lambda function we kind of work around
// the problem of generating a *huge* macro method body that finally exceeds the
// JVM's maximum allowed method size
val overridesLambda = q"(() => $overridesRoutes)()"
val lhs = q"classOf[${discoveredModuleType.typeSymbol.asClass}]"
q"$lhs -> $overridesLambda"
}

c.Expr[Discover[T]](
q"_root_.mill.define.Discover(_root_.scala.collection.immutable.Map(..$mapping))"
)
}
}

}
93 changes: 0 additions & 93 deletions main/core/src/mill/define/Router.scala

This file was deleted.

28 changes: 19 additions & 9 deletions runner/src/mill/runner/MillBuildBootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,29 @@ class MillBuildBootstrap(projectRoot: os.Path,
.millModuleDirectChildren
.collect { case b: RootModule => b }

val baseModuleOrErr = childRootModules match {
val rootModuleOrErr = childRootModules match {
case Seq() => Right(rootModule0)
case Seq(child) => Right(child)
case Seq(child) =>
val invalidChildModules = rootModule0.millModuleDirectChildren.filter(_ ne child)
if (invalidChildModules.isEmpty) Right(child)
else Left(
// We can't get use `child.toString` here, because as a RootModule
// it's segments are empty and it's toString is ""
s"RootModule ${child.getClass.getSimpleName} cannot have other " +
s"modules defined outside of it: ${invalidChildModules.mkString(",")}"
)


case multiple =>
Left(
s"Only one BaseModule can be defined in a build, not " +
s"Only one RootModule can be defined in a build, not " +
s"${multiple.size}: ${multiple.map(_.getClass.getName).mkString(",")}"
)
}

val validatedRootModuleOrErr = baseModuleOrErr.filterOrElse( baseModule =>
depth == 0 || baseModule.isInstanceOf[MillBuildRootModule],
s"Top-level module in ${recRoot(depth).relativeTo(projectRoot)}/build.sc must be of ${classOf[mill.runner.MillBuildRootModule]}"
val validatedRootModuleOrErr = rootModuleOrErr.filterOrElse(rootModule =>
depth == 0 || rootModule.isInstanceOf[MillBuildRootModule],
s"Root module in ${recRoot(depth).relativeTo(projectRoot)}/build.sc must be of ${classOf[mill.runner.MillBuildRootModule]}"
)

validatedRootModuleOrErr match{
Expand Down Expand Up @@ -138,7 +148,7 @@ class MillBuildBootstrap(projectRoot: os.Path,
/**
* Handles the compilation of `build.sc` or one of the meta-builds. These
* cases all only need us to run evaluate `runClasspath` and
* `scriptImportGraph` to instantiate their classloader/`BaseModule` to feed
* `scriptImportGraph` to instantiate their classloader/`RootModule` to feed
* into the next level's [[Evaluator]].
*
* Note that if the `runClasspath` doesn't change, we re-use the previous
Expand Down Expand Up @@ -233,7 +243,7 @@ class MillBuildBootstrap(projectRoot: os.Path,

def makeEvaluator(workerCache: Map[Segments, (Int, Any)],
scriptImportGraph: Map[Path, (Int, Seq[Path])],
baseModule: RootModule,
rootModule: RootModule,
millClassloaderSigHash: Int,
depth: Int) = {

Expand All @@ -245,7 +255,7 @@ class MillBuildBootstrap(projectRoot: os.Path,
config.home,
recOut(depth),
recOut(depth),
baseModule,
rootModule,
PrefixLogger(logger, "", tickerContext = bootLogPrefix),
millClassloaderSigHash
)
Expand Down

0 comments on commit a534e90

Please sign in to comment.