Skip to content

Commit

Permalink
Move run-targets into RunModule (#3090)
Browse files Browse the repository at this point in the history
This PR moves the definition of the `run` targets from `JavaModule` into
`RunModule`. Also some other targets where moved, e.g. `mainClass`,
`finalMainClassOpt` and `finalMainClass`.

The advantage of having them in `RunModule` is, that one can easily
define and configure additional runners as sub-modules.

Example: Define a `daemon` sub-module which acts as runner for the
`cli.DaemonTool` class.

```scala
import mill._, scalalib._

object foo extends RootModule with JavaModule {
  def mainClass = "cli.Main"

  object daemon extends RunModule {
    def runClasspath = foo.runClasspath
    def localRunClasspath = foo.localRunClasspath
    def mainClass = "cli.DaemonTool"
  }
}
```

To preserve binary compatibility, all moved `def`s retain an override in
`JavaModule` and forward to their `super`-`def` in `RunModule`.
Therefore traits compiled against older versions of trait `JavaModule`
should still be runnable with newer versions.

Some `run`-targets (`run`, `runLocal` and `runBackground`) previously
required the `compile` target, since that also provided some zinc
analysis which included some main class discovery. Since the goal was to
decouple the `run` targets from the concept of compilation, I
implemented a different discovery logic for main classes which uses the
Scala API of `scalap`. This is completely newly written code but it's
only a couple of lines and all existing tests (which also include main
class discovery) succeeded. The advantage of the new logic is, that it
should work with any given classpath and also for non-Scala classes. The
new code is located in the zinc worker, since this is already shared
between all `RunModule`s, but there is no real need to have it there. To
avoid increased complexity, I resisted to introduce a new shared worker
just for the sake of technical independence, for now. The new
`allLocalMainClasses` target can be used to find all main classes in the
`localRunClasspath`. This is some useful information I needed now and
then in projects, so it makes sense to have it in a dedicated target for
better caching and easy `show`ing.

I also introduced a new `localRunClasspath` target which abstracts away
the classpath that is supposed to be produced by compilation. This is
somewhat analogue to the `testClassapth` introdcued in PR #3064. Since
both typically hold the same classpath, `testClasspath` by default uses
the result of `localRunClasspath`. We probably could remove
`testClasspath` altogether, but it's semantically bound to `TestModule`
and isn't necessarily the same as `localRunClasspath`, so I think it's
legit to keep it.

For consistency, I also added a `localCompileClasspath` which resembles
the part of the `localClasspath` which is feed into the compiler. All
added classpath targets also have a version for BSP (named with prefix
`bsp` and returning a `T[Agg[UnresolvedPath]]`) when appropriate.

Pull request: #3090
  • Loading branch information
lefou authored Mar 26, 2024
1 parent 9a734ab commit 36c5217
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 204 deletions.
34 changes: 29 additions & 5 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ object Deps {
def scalaCompiler(scalaVersion: String) = ivy"org.scala-lang:scala-compiler:${scalaVersion}"
// last scalafmt release supporting Java 8 is 3.7.15
val scalafmtDynamic = ivy"org.scalameta::scalafmt-dynamic:3.7.15" // scala-steward:off
def scalap(scalaVersion: String) = ivy"org.scala-lang:scalap:${scalaVersion}"
def scalaReflect(scalaVersion: String) = ivy"org.scala-lang:scala-reflect:${scalaVersion}"
val scalacScoveragePlugin = ivy"org.scoverage:::scalac-scoverage-plugin:1.4.11"
val scoverage2Version = "2.1.0"
Expand Down Expand Up @@ -448,7 +449,7 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima {
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalanativelib.ScalaNativeModule.mill$scalanativelib$ScalaNativeModule$$super$zincAuxiliaryClassFileExtensions"
),
// (7x) See https://github.com/com-lihaoyi/mill/pull/3064
// (6x) See https://github.com/com-lihaoyi/mill/pull/3064
// Moved targets up in trait hierarchy, but also call them via super, which I think is safe
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$zincWorker"
Expand All @@ -459,9 +460,6 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima {
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runUseArgsFile"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule#JavaModuleTests.mill$scalalib$JavaModule$JavaModuleTests$$super$testClasspath"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$forkArgs"
),
Expand All @@ -470,6 +468,32 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima {
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$forkWorkingDir"
),
// (8x)
// Moved targets up in trait hierarchy, but also call them via super, which I think is safe
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$localRunClasspath"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runLocal"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$run"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$doRunBackground"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runBackgroundLogToConsole"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runMainBackground"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runMainLocal"
),
ProblemFilter.exclude[ReversedMissingMethodProblem](
"mill.scalalib.JavaModule.mill$scalalib$JavaModule$$super$runMain"
)
)
def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions
Expand Down Expand Up @@ -740,7 +764,7 @@ object scalalib extends MillStableScalaModule {

object worker extends MillPublishScalaModule with BuildInfo {
def moduleDeps = Seq(scalalib.api)
def ivyDeps = Agg(Deps.zinc, Deps.log4j2Core)
def ivyDeps = Agg(Deps.zinc, Deps.log4j2Core, Deps.scalap(scalaVersion()))
def buildInfoPackageName = "mill.scalalib.worker"
def buildInfoObjectName = "Versions"
def buildInfoMembers = Seq(
Expand Down
2 changes: 1 addition & 1 deletion example/basic/4-builtin-commands/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ foo.artifactName
/** Usage
> mill inspect foo.run
foo.run(JavaModule.scala:...)
foo.run(RunModule.scala:...)
Runs this module's code in a subprocess and waits for it to finish
Inputs:
foo.finalMainClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object DocAnnotationsTests extends IntegrationTestSuite {

assert(
globMatches(
"""core.run(JavaModule.scala:...)
"""core.run(RunModule.scala:...)
| Runs this module's code in a subprocess and waits for it to finish
|
| args <str>...
Expand Down
11 changes: 11 additions & 0 deletions scalalib/api/src/mill/scalalib/api/ZincWorkerApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ trait ZincWorkerApi {
auxiliaryClassFileExtensions = Seq.empty[String]
)

/**
* Find main classes by inspecting the Zinc compilation analysis file.
*/
def discoverMainClasses(compilationResult: CompilationResult): Seq[String]

def docJar(
Expand All @@ -153,4 +156,12 @@ trait ZincWorkerApi {
scalacPluginClasspath: Agg[PathRef],
args: Seq[String]
)(implicit ctx: ZincWorkerApi.Ctx): Boolean

/**
* Discover main classes by inspecting the classpath.
*/
def discoverMainClasses(classpath: Seq[os.Path]): Seq[String] = {
// We need this default-impl to keep binary compatinility (0.11.x)
Seq.empty
}
}
Loading

0 comments on commit 36c5217

Please sign in to comment.