Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make mill.define.Module a trait to allow abstract/virtual module lazy vals to be traits rather than classes #2536

Merged
merged 23 commits into from
May 22, 2023
Merged
2 changes: 1 addition & 1 deletion bsp/worker/src/mill/bsp/worker/MillJvmBuildServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private trait MillJvmBuildServer extends JvmBuildServer { this: MillBuildServer
m.forkWorkingDir(),
m.forkEnv(),
m.mainClass(),
m.zincWorker.worker(),
m.zincWorker().worker(),
m.compile()
)
}
Expand Down
2 changes: 1 addition & 1 deletion bsp/worker/src/mill/bsp/worker/MillScalaBuildServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private trait MillScalaBuildServer extends ScalaBuildServer { this: MillBuildSer
hint = "buildTargetScalaMainClasses",
targetIds = _ => p.getTargets.asScala.toSeq,
tasks = { case m: JavaModule =>
T.task((m.zincWorker.worker(), m.compile(), m.forkArgs(), m.forkEnv()))
T.task((m.zincWorker().worker(), m.compile(), m.forkArgs(), m.forkEnv()))
}
) {
case (state, id, m: JavaModule, (worker, compile, forkArgs, forkEnv)) =>
Expand Down
21 changes: 20 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,26 @@ trait MillJavaModule extends JavaModule {
)
}

trait MillMimaConfig extends Module {
trait MillMimaConfig extends mima.Mima {
override def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions
override def mimaPreviousArtifacts: T[Agg[Dep]] = T {
Agg.from(
Settings.mimaBaseVersions
.filter(v => !skipPreviousVersions().contains(v))
.map(version =>
ivy"${pomSettings().organization}:${artifactId()}:${version}"
)
)
}
override def mimaExcludeAnnotations: T[Seq[String]] = Seq(
"mill.api.internal",
"mill.api.experimental"
)
override def mimaCheckDirection: Target[CheckDirection] = T { CheckDirection.Backward }
override def mimaBinaryIssueFilters: Target[Seq[ProblemFilter]] = T {
issueFilterByModule.getOrElse(this, Seq())
}
lazy val issueFilterByModule: Map[MillMimaConfig, Seq[ProblemFilter]] = Map()
def skipPreviousVersions: T[Seq[String]] = T(Seq.empty[String])
}

Expand Down
235 changes: 235 additions & 0 deletions ci/mill-bootstrap.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
diff --git a/build.sc b/build.sc
index 20fe84f7e4..869c3ba31e 100644
--- a/build.sc
+++ b/build.sc
@@ -2,23 +2,23 @@
import $file.ci.shared
import $file.ci.upload
import $ivy.`org.scalaj::scalaj-http:2.4.2`
-import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.3.1-6-e80da7`
+//import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.3.1-6-e80da7`

-import $ivy.`com.github.lolgab::mill-mima::0.0.20`
+//import $ivy.`com.github.lolgab::mill-mima::0.0.20`
import $ivy.`net.sourceforge.htmlcleaner:htmlcleaner:2.25`

// imports
-import com.github.lolgab.mill.mima
-import com.github.lolgab.mill.mima.{
- CheckDirection,
- DirectMissingMethodProblem,
- IncompatibleMethTypeProblem,
- IncompatibleSignatureProblem,
- ProblemFilter,
- ReversedMissingMethodProblem
-}
+//import com.github.lolgab.mill.mima
+//import com.github.lolgab.mill.mima.{
+// CheckDirection,
+// DirectMissingMethodProblem,
+// IncompatibleMethTypeProblem,
+// IncompatibleSignatureProblem,
+// ProblemFilter,
+// ReversedMissingMethodProblem
+//}
import coursier.maven.MavenRepository
-import de.tobiasroeser.mill.vcs.version.VcsVersion
+//import de.tobiasroeser.mill.vcs.version.VcsVersion
import mill._
import mill.eval.Evaluator
import mill.main.MainModule
@@ -167,11 +167,12 @@ object Deps {
val requests = ivy"com.lihaoyi::requests:0.8.0"
}

-def millVersion: T[String] = T { VcsVersion.vcsState().format() }
+def millVersion: T[String] = T { "dev"/*VcsVersion.vcsState().format()*/ }
def millLastTag: T[String] = T {
- VcsVersion.vcsState().lastTag.getOrElse(
- sys.error("No (last) git tag found. Your git history seems incomplete!")
- )
+// VcsVersion.vcsState().lastTag.getOrElse(
+// sys.error("No (last) git tag found. Your git history seems incomplete!")
+// )
+ "dev"
}
def millBinPlatform: T[String] = T {
val tag = millLastTag()
@@ -319,26 +320,26 @@ trait MillJavaModule extends JavaModule {
)
}

-trait MillMimaConfig extends mima.Mima {
- override def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions
- override def mimaPreviousArtifacts: T[Agg[Dep]] = T {
- Agg.from(
- Settings.mimaBaseVersions
- .filter(v => !skipPreviousVersions().contains(v))
- .map(version =>
- ivy"${pomSettings().organization}:${artifactId()}:${version}"
- )
- )
- }
- override def mimaExcludeAnnotations: T[Seq[String]] = Seq(
- "mill.api.internal",
- "mill.api.experimental"
- )
- override def mimaCheckDirection: Target[CheckDirection] = T { CheckDirection.Backward }
- override def mimaBinaryIssueFilters: Target[Seq[ProblemFilter]] = T {
- issueFilterByModule.getOrElse(this, Seq())
- }
- lazy val issueFilterByModule: Map[MillMimaConfig, Seq[ProblemFilter]] = Map()
+trait MillMimaConfig extends Module/*mima.Mima*/ {
+// override def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions
+// override def mimaPreviousArtifacts: T[Agg[Dep]] = T {
+// Agg.from(
+// Settings.mimaBaseVersions
+// .filter(v => !skipPreviousVersions().contains(v))
+// .map(version =>
+// ivy"${pomSettings().organization}:${artifactId()}:${version}"
+// )
+// )
+// }
+// override def mimaExcludeAnnotations: T[Seq[String]] = Seq(
+// "mill.api.internal",
+// "mill.api.experimental"
+// )
+// override def mimaCheckDirection: Target[CheckDirection] = T { CheckDirection.Backward }
+// override def mimaBinaryIssueFilters: Target[Seq[ProblemFilter]] = T {
+// issueFilterByModule.getOrElse(this, Seq())
+// }
+// lazy val issueFilterByModule: Map[MillMimaConfig, Seq[ProblemFilter]] = Map()
def skipPreviousVersions: T[Seq[String]] = T(Seq.empty[String])
}

@@ -425,10 +426,9 @@ trait BaseMillTestsModule extends TestModule {

/** A MillScalaModule with default set up test module. */
trait MillAutoTestSetup extends MillScalaModule {
- // instead of `object test` which can't be overridden, we hand-made a val+class singleton
/** Default tests module. */
- val test = new Tests(implicitly)
- class Tests(ctx0: mill.define.Ctx) extends mill.Module()(ctx0) with super.MillScalaModuleTests
+ lazy val test: Tests = new Tests {}
+ trait Tests extends super.MillScalaModuleTests
}

/** Published module which does not contain strictly handled API. */
@@ -917,8 +917,8 @@ object bsp extends MillModule with BuildInfo {
)
}

- override val test = new Test(implicitly)
- class Test(ctx0: mill.define.Ctx) extends Tests(ctx0) {
+ override lazy val test: Test = new Test {}
+ trait Test extends Tests{
override def forkEnv: Target[Map[String, String]] = T {
// We try to fetch this dependency with coursier in the tests
bsp.worker.publishLocal()()
@@ -1066,7 +1066,7 @@ object example extends MillScalaModule {
val title =
if (seenCode) ""
else {
- val label = VcsVersion.vcsState().format()
+ val label = millVersion()
val exampleDashed = examplePath.segments.mkString("-")
val download = s"{mill-download-url}/$label-$exampleDashed.zip[download]"
val browse = s"{mill-example-url}/$examplePath[browse]"
@@ -1744,7 +1744,7 @@ def exampleZips: Target[Seq[PathRef]] = T {
examplePath = exampleMod.millSourcePath
} yield {
val example = examplePath.subRelativeTo(T.workspace)
- val exampleStr = VcsVersion.vcsState().format() + "-" + example.segments.mkString("-")
+ val exampleStr = millVersion() + "-" + example.segments.mkString("-")
os.copy(examplePath, T.dest / exampleStr, createFolders = true)
os.copy(bootstrapLauncher().path, T.dest / exampleStr / "mill")
val zip = T.dest / s"$exampleStr.zip"
@@ -1754,47 +1754,47 @@ def exampleZips: Target[Seq[PathRef]] = T {
}

def uploadToGithub(authKey: String) = T.command {
- val vcsState = VcsVersion.vcsState()
- val label = vcsState.format()
- if (label != millVersion()) sys.error("Modified mill version detected, aborting upload")
- val releaseTag = vcsState.lastTag.getOrElse(sys.error(
- "Incomplete git history. No tag found.\nIf on CI, make sure your git checkout job includes enough history."
- ))
-
- if (releaseTag == label) {
- // TODO: check if the tag already exists (e.g. because we created it manually) and do not fail
- scalaj.http.Http(
- s"https://api.github.com/repos/${Settings.githubOrg}/${Settings.githubRepo}/releases"
- )
- .postData(
- ujson.write(
- ujson.Obj(
- "tag_name" -> releaseTag,
- "name" -> releaseTag
- )
- )
- )
- .header("Authorization", "token " + authKey)
- .asString
- }
-
- val examples = exampleZips().map(z => (z.path, z.path.last))
-
- val zips = examples ++ Seq(
- (dev.assembly().path, label + "-assembly"),
- (bootstrapLauncher().path, label)
- )
-
- for ((zip, name) <- zips) {
- upload.apply(
- zip,
- releaseTag,
- name,
- authKey,
- Settings.githubOrg,
- Settings.githubRepo
- )
- }
+// val vcsState = VcsVersion.vcsState()
+// val label = vcsState.format()
+// if (label != millVersion()) sys.error("Modified mill version detected, aborting upload")
+// val releaseTag = vcsState.lastTag.getOrElse(sys.error(
+// "Incomplete git history. No tag found.\nIf on CI, make sure your git checkout job includes enough history."
+// ))
+//
+// if (releaseTag == label) {
+// // TODO: check if the tag already exists (e.g. because we created it manually) and do not fail
+// scalaj.http.Http(
+// s"https://api.github.com/repos/${Settings.githubOrg}/${Settings.githubRepo}/releases"
+// )
+// .postData(
+// ujson.write(
+// ujson.Obj(
+// "tag_name" -> releaseTag,
+// "name" -> releaseTag
+// )
+// )
+// )
+// .header("Authorization", "token " + authKey)
+// .asString
+// }
+//
+// val examples = exampleZips().map(z => (z.path, z.path.last))
+//
+// val zips = examples ++ Seq(
+// (dev.assembly().path, label + "-assembly"),
+// (bootstrapLauncher().path, label)
+// )
+//
+// for ((zip, name) <- zips) {
+// upload.apply(
+// zip,
+// releaseTag,
+// name,
+// authKey,
+// Settings.githubOrg,
+// Settings.githubRepo
+// )
+// }
}

def validate(ev: Evaluator): Command[Unit] = T.command {
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
)
}

val scoverage: ScoverageData = new ScoverageData(implicitly)
lazy val scoverage = new ScoverageData {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only to be lazy, or is it fixing some early initialization stuff? If this is a (required) pattern, we should probably document it in Module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to be lazy, but I thought that's probably what we should tell people to do. After all, objects are lazy, so using lazy val for these would avoid unnecessarily changing the evaluation model. It also helps avoid NullPointerExceptions from popping up in surprising places, especially once multiple trait inheritance and overriding is involved. Not a problem with this specific definition, but definitely a potential problem in general


class ScoverageData(ctx0: mill.define.Ctx) extends Module()(ctx0) with ScalaModule {
trait ScoverageData extends ScalaModule {

def doReport(reportType: ReportType): Task[Unit] = T.task {
ScoverageReportWorker
Expand Down
5 changes: 2 additions & 3 deletions contrib/twirllib/src/mill/twirllib/TwirlModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ trait TwirlModule extends mill.Module { twirlModule =>
* Class instead of an object, to allow re-configuration.
* @since Mill after 0.10.5
*/
class TwirlResolver()(implicit ctx0: mill.define.Ctx) extends mill.Module()(ctx0)
with CoursierModule {
trait TwirlResolver extends CoursierModule {
override def resolveCoursierDependency: Task[Dep => Dependency] = T.task { d: Dep =>
Lib.depToDependency(d, twirlScalaVersion())
}
Expand All @@ -61,7 +60,7 @@ trait TwirlModule extends mill.Module { twirlModule =>
/**
* @since Mill after 0.10.5
*/
val twirlCoursierResolver = new TwirlResolver()
lazy val twirlCoursierResolver = new TwirlResolver {}

def twirlClasspath: T[Loose.Agg[PathRef]] = T {
twirlCoursierResolver.resolveDeps(T.task {
Expand Down
3 changes: 2 additions & 1 deletion example/scalamodule/11-repository-config/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// your own resolvers by overriding the `repositoriesTask` task in the module:

import mill._, scalalib._
import mill.define.ModuleRef
import coursier.maven.MavenRepository

val sonatypeReleases = Seq(
Expand All @@ -26,7 +27,7 @@ object CustomZincWorkerModule extends ZincWorkerModule with CoursierModule {

object bar extends ScalaModule {
def scalaVersion = "2.13.8"
def zincWorker = CustomZincWorkerModule
def zincWorker = ModuleRef(CustomZincWorkerModule)
// ... rest of your build definitions

def repositoriesTask = T.task {super.repositoriesTask() ++ sonatypeReleases}
Expand Down
10 changes: 3 additions & 7 deletions main/define/src/mill/define/BaseModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ abstract class BaseModule(
)(implicit
millModuleEnclosing0: sourcecode.Enclosing,
millModuleLine0: sourcecode.Line,
millName0: sourcecode.Name,
millFile0: sourcecode.File,
caller: Caller
) extends Module()(
) extends Module.BaseClass()(
mill.define.Ctx.make(
implicitly,
implicitly,
implicitly,
Ctx.BasePath(millSourcePath0),
Expand All @@ -26,7 +24,7 @@ abstract class BaseModule(
millFile0,
caller
)
) {
) with Module {
// A BaseModule should provide an empty Segments list to it's children, since
// it is the root of the module tree, and thus must not include it's own
// sourcecode.Name as part of the list,
Expand All @@ -40,13 +38,11 @@ abstract class BaseModule(

abstract class ExternalModule(implicit
millModuleEnclosing0: sourcecode.Enclosing,
millModuleLine0: sourcecode.Line,
millName0: sourcecode.Name
millModuleLine0: sourcecode.Line
) extends BaseModule(os.pwd, external0 = true, foreign0 = None)(
implicitly,
implicitly,
implicitly,
implicitly,
Caller(())
) {

Expand Down
2 changes: 1 addition & 1 deletion main/define/src/mill/define/Cross.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ object Cross {
*/
class Cross[M <: Cross.Module[_]](factories: Cross.Factory[M]*)(implicit
ctx: mill.define.Ctx
) extends mill.define.Module()(ctx) {
) extends mill.define.Module {

trait Item {
def crossValues: List[Any]
Expand Down
8 changes: 6 additions & 2 deletions main/define/src/mill/define/Ctx.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ object Ctx {
implicit def make(implicit
millModuleEnclosing0: sourcecode.Enclosing,
millModuleLine0: sourcecode.Line,
millName0: sourcecode.Name,
millModuleBasePath0: BasePath,
segments0: Segments,
external0: External,
Expand All @@ -94,7 +93,12 @@ object Ctx {
Ctx(
millModuleEnclosing0.value,
millModuleLine0.value,
Segment.Label(millName0.value),
Segment.Label(
// Manually break apart `sourcecode.Enclosing` instead of using
// `sourcecode.Name` to work around bug with anonymous classes
// returning `$anon` names
millModuleEnclosing0.value.split("\\.|#| ").filter(!_.startsWith("$anon")).last
),
millModuleBasePath0.value,
segments0,
external0.value,
Expand Down
Loading