Skip to content

Commit a7455f8

Browse files
committed
Intial Groovy setup
1 parent c93e196 commit a7455f8

File tree

22 files changed

+809
-3
lines changed

22 files changed

+809
-3
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package mill.api.daemon.internal
2+
3+
trait GroovyModuleApi extends JavaModuleApi

core/api/daemon/src/mill/api/daemon/internal/bsp/BspModuleApi.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ object BspModuleApi {
1515
val Java = "java"
1616
val Scala = "scala"
1717
val Kotlin = "kotlin"
18+
val Groovy = "groovy"
1819
}
1920

2021
/** Used to define the [[BspBuildTarget.tags]] field. */
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package mill.groovylib.worker.api
2+
3+
import mill.api.TaskCtx
4+
import mill.api.Result
5+
import mill.javalib.api.CompilationResult
6+
7+
trait GroovyWorker {
8+
9+
def compile(sourceFiles: Seq[os.Path], classpath: Seq[os.Path], outputDir: os.Path)(implicit
10+
ctx: TaskCtx
11+
): Result[CompilationResult]
12+
}

libs/groovylib/package.mill

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package build.libs.groovylib
2+
3+
// imports
4+
import mill.*
5+
import mill.contrib.buildinfo.BuildInfo
6+
import mill.scalalib.*
7+
import millbuild.*
8+
9+
object `package` extends MillStableScalaModule with BuildInfo {
10+
11+
def moduleDeps = Seq(build.libs.javalib, build.libs.javalib.testrunner, api)
12+
def localTestExtraModules =
13+
super.localTestExtraModules ++ Seq(worker)
14+
15+
def buildInfoPackageName = "mill.groovylib"
16+
def buildInfoObjectName = "Versions"
17+
def buildInfoMembers = Seq(
18+
BuildInfo.Value("groovyVersion", Deps.groovyVersion, "Version of Groovy")
19+
)
20+
21+
trait MillGroovyModule extends MillPublishScalaModule {
22+
override def javacOptions = super.javacOptions() ++ {
23+
val release =
24+
if (scala.util.Properties.isJavaAtLeast(11)) Seq("-release", "8")
25+
else Seq("-source", "1.8", "-target", "1.8")
26+
release ++ Seq("-encoding", "UTF-8", "-deprecation")
27+
}
28+
}
29+
30+
object api extends MillGroovyModule {
31+
def moduleDeps = Seq(build.libs.javalib.testrunner)
32+
33+
override def compileMvnDeps: T[Seq[Dep]] = Seq(
34+
Deps.osLib
35+
)
36+
}
37+
38+
object worker extends MillGroovyModule {
39+
override def compileModuleDeps = Seq(api)
40+
41+
def mandatoryMvnDeps = Seq.empty[Dep]
42+
43+
override def compileMvnDeps: T[Seq[Dep]] =
44+
super.mandatoryMvnDeps() ++ Seq(
45+
Deps.osLib,
46+
Deps.groovyCompiler
47+
)
48+
}
49+
50+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package mill.groovylib
2+
3+
import mill.Task
4+
import mill.javalib.MavenModule
5+
6+
/**
7+
* A [[GroovyModule]] with a Maven compatible directory layout:
8+
* `src/main/groovy`, `src/main/resources`, etc.
9+
*/
10+
trait GroovyMavenModule extends GroovyModule with MavenModule {
11+
private def sources0 = Task.Sources("src/main/groovy")
12+
override def sources = super.sources() ++ sources0()
13+
14+
trait GroovyMavenTests extends GroovyTests with MavenTests {
15+
override def sources = Task.Sources(
16+
moduleDir / "src" / testModuleName / "java",
17+
moduleDir / "src" / testModuleName / "groovy"
18+
)
19+
}
20+
}
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
package mill
2+
package groovylib
3+
4+
import mill.api.{ModuleRef, Result}
5+
import mill.javalib.api.CompilationResult
6+
import mill.javalib.api.JvmWorkerApi as PublicJvmWorkerApi
7+
import mill.api.daemon.internal.{CompileProblemReporter, GroovyModuleApi, internal}
8+
import mill.javalib.{Dep, JavaModule, JvmWorkerModule, Lib}
9+
import mill.*
10+
import mainargs.Flag
11+
import mill.api.daemon.internal.bsp.{BspBuildTarget, BspModuleApi}
12+
import mill.javalib.api.internal.{JavaCompilerOptions, JvmWorkerApi, ZincCompileJava}
13+
14+
/**
15+
* Core configuration required to compile a single Groovy module
16+
*/
17+
trait GroovyModule extends JavaModule with GroovyModuleApi { outer =>
18+
19+
/**
20+
* The Groovy version to be used (for API and Language level settings).
21+
*/
22+
def groovyVersion: T[String]
23+
24+
/**
25+
* The compiler language version. Default is derived from [[groovyVersion]].
26+
*/
27+
def groovyLanguageVersion: T[String] = Task { groovyVersion().split("[.]").take(2).mkString(".") }
28+
29+
override def bomMvnDeps: T[Seq[Dep]] = super.bomMvnDeps() ++ Seq(
30+
mvn"org.apache.groovy:groovy-bom:${groovyVersion()}"
31+
)
32+
33+
/**
34+
* All individual source files fed into the compiler.
35+
*/
36+
override def allSourceFiles: T[Seq[PathRef]] = Task {
37+
Lib.findSourceFiles(allSources(), Seq("groovy", "java")).map(PathRef(_))
38+
}
39+
40+
/**
41+
* All individual Java source files fed into the compiler.
42+
* Subset of [[allSourceFiles]].
43+
*/
44+
private def allJavaSourceFiles: T[Seq[PathRef]] = Task {
45+
allSourceFiles().filter(_.path.ext.toLowerCase() == "java")
46+
}
47+
48+
/**
49+
* All individual Groovy source files fed into the compiler.
50+
* Subset of [[allSourceFiles]].
51+
*/
52+
private def allGroovySourceFiles: T[Seq[PathRef]] = Task {
53+
allSourceFiles().filter(path => Seq("groovy").contains(path.path.ext.toLowerCase()))
54+
}
55+
56+
/**
57+
* The dependencies of this module.
58+
* Defaults to add the groovy-stdlib dependency matching the [[groovyVersion]].
59+
*/
60+
override def mandatoryMvnDeps: T[Seq[Dep]] = Task {
61+
super.mandatoryMvnDeps() ++ Seq(
62+
mvn"org.apache.groovy:groovy:${groovyVersion()}"
63+
)
64+
}
65+
66+
private def jvmWorkerRef: ModuleRef[JvmWorkerModule] = jvmWorker
67+
68+
override def checkGradleModules: T[Boolean] = true
69+
70+
/**
71+
* The Java classpath resembling the Groovy compiler.
72+
* Default is derived from [[groovyCompilerMvnDeps]].
73+
*/
74+
def groovyCompilerClasspath: T[Seq[PathRef]] = Task {
75+
val deps = groovyCompilerMvnDeps() ++ Seq(
76+
Dep.millProjectModule("mill-libs-groovylib-worker")
77+
)
78+
defaultResolver().classpath(
79+
deps,
80+
resolutionParamsMapOpt = None
81+
)
82+
}
83+
84+
/**
85+
* The Ivy/Coursier dependencies resembling the Groovy compiler.
86+
*
87+
* Default is derived from [[groovyCompilerVersion]].
88+
*/
89+
def groovyCompilerMvnDeps: T[Seq[Dep]] = Task {
90+
val gv = groovyVersion()
91+
92+
val compilerDep = mvn"org.apache.groovy:groovy:$gv"
93+
94+
Seq(compilerDep)
95+
}
96+
97+
/**
98+
* Compiler Plugin dependencies.
99+
*/
100+
def groovycPluginMvnDeps: T[Seq[Dep]] = Task { Seq.empty[Dep] }
101+
102+
/**
103+
* The resolved plugin jars
104+
*/
105+
def groovycPluginJars: T[Seq[PathRef]] = Task {
106+
val jars = defaultResolver().classpath(
107+
groovycPluginMvnDeps()
108+
// Don't resolve transitive jars
109+
.map(d => d.exclude("*" -> "*")),
110+
resolutionParamsMapOpt = None
111+
)
112+
jars.toSeq
113+
}
114+
115+
/**
116+
* Compiles all the sources to JVM class files.
117+
*/
118+
override def compile: T[CompilationResult] = Task {
119+
groovyCompileTask()()
120+
}
121+
122+
/**
123+
* The actual Groovy compile task (used by [[compile]] and [[groovycHelp]]).
124+
*/
125+
protected def groovyCompileTask(): Task[CompilationResult] =
126+
Task.Anon {
127+
val ctx = Task.ctx()
128+
val dest = ctx.dest
129+
val classes = dest / "classes"
130+
os.makeDir.all(classes)
131+
132+
val javaSourceFiles = allJavaSourceFiles().map(_.path)
133+
val groovySourceFiles = allGroovySourceFiles().map(_.path)
134+
135+
val isGroovy = groovySourceFiles.nonEmpty
136+
val isJava = javaSourceFiles.nonEmpty
137+
val isMixed = isGroovy && isJava
138+
139+
val compileCp = compileClasspath().map(_.path).filter(os.exists)
140+
val updateCompileOutput = upstreamCompileOutput()
141+
142+
def compileJava: Result[CompilationResult] = {
143+
ctx.log.info(
144+
s"Compiling ${javaSourceFiles.size} Java sources to ${classes} ..."
145+
)
146+
// The compile step is lazy, but its dependencies are not!
147+
internalCompileJavaFiles(
148+
worker = jvmWorkerRef().internalWorker(),
149+
upstreamCompileOutput = updateCompileOutput,
150+
javaSourceFiles = javaSourceFiles,
151+
compileCp = compileCp,
152+
javaHome = javaHome().map(_.path),
153+
javacOptions = javacOptions(),
154+
compileProblemReporter = ctx.reporter(hashCode),
155+
reportOldProblems = internalReportOldProblems()
156+
)
157+
}
158+
159+
if (isMixed || isGroovy) {
160+
ctx.log.info(
161+
s"Compiling ${groovySourceFiles.size} Groovy sources to ${classes} ..."
162+
)
163+
164+
val compileCp = compileClasspath().map(_.path).filter(os.exists)
165+
166+
val workerResult =
167+
GroovyWorkerManager.groovyWorker().withValue(groovyCompilerClasspath()) {
168+
_.compile(groovySourceFiles, compileCp, classes)
169+
}
170+
171+
val analysisFile = dest / "groovy.analysis.dummy" // FIXME
172+
os.write(target = analysisFile, data = "", createFolders = true)
173+
174+
workerResult match {
175+
case Result.Success(_) =>
176+
val cr = CompilationResult(analysisFile, PathRef(classes))
177+
if (!isJava) {
178+
// pure Groovy project
179+
cr
180+
} else {
181+
// also run Java compiler and use it's returned result
182+
compileJava
183+
}
184+
case Result.Failure(reason) => Result.Failure(reason)
185+
}
186+
} else {
187+
// it's Java only
188+
compileJava
189+
}
190+
}
191+
192+
/**
193+
* Additional Groovy compiler options to be used by [[compile]].
194+
*/
195+
def groovycOptions: T[Seq[String]] = Task { Seq.empty[String] }
196+
197+
/**
198+
* Aggregation of all the options passed to the Groovy compiler.
199+
* In most cases, instead of overriding this Target you want to override `groovycOptions` instead.
200+
*/
201+
def allGroovycOptions: T[Seq[String]] = Task {
202+
groovycOptions()
203+
}
204+
205+
private[groovylib] def internalCompileJavaFiles(
206+
worker: JvmWorkerApi,
207+
upstreamCompileOutput: Seq[CompilationResult],
208+
javaSourceFiles: Seq[os.Path],
209+
compileCp: Seq[os.Path],
210+
javaHome: Option[os.Path],
211+
javacOptions: Seq[String],
212+
compileProblemReporter: Option[CompileProblemReporter],
213+
reportOldProblems: Boolean
214+
)(implicit ctx: PublicJvmWorkerApi.Ctx): Result[CompilationResult] = {
215+
val jOpts = JavaCompilerOptions(javacOptions)
216+
worker.compileJava(
217+
ZincCompileJava(
218+
upstreamCompileOutput = upstreamCompileOutput,
219+
sources = javaSourceFiles,
220+
compileClasspath = compileCp,
221+
javacOptions = jOpts.compiler,
222+
incrementalCompilation = true
223+
),
224+
javaHome = javaHome,
225+
javaRuntimeOptions = jOpts.runtime,
226+
reporter = compileProblemReporter,
227+
reportCachedProblems = reportOldProblems
228+
)
229+
}
230+
231+
private[groovylib] def internalReportOldProblems: Task[Boolean] = zincReportCachedProblems
232+
233+
@internal
234+
override def bspBuildTarget: BspBuildTarget = super.bspBuildTarget.copy(
235+
languageIds = Seq(
236+
BspModuleApi.LanguageId.Java,
237+
BspModuleApi.LanguageId.Groovy
238+
),
239+
canCompile = true,
240+
canRun = true
241+
)
242+
243+
override def prepareOffline(all: Flag): Command[Seq[PathRef]] = Task.Command {
244+
(
245+
super.prepareOffline(all)() ++
246+
groovyCompilerClasspath()
247+
).distinct
248+
}
249+
250+
/**
251+
* A test sub-module linked to its parent module best suited for unit-tests.
252+
*/
253+
trait GroovyTests extends JavaTests with GroovyModule {
254+
255+
override def groovyLanguageVersion: T[String] = outer.groovyLanguageVersion()
256+
override def groovyVersion: T[String] = Task { outer.groovyVersion() }
257+
override def groovycPluginMvnDeps: T[Seq[Dep]] =
258+
Task { outer.groovycPluginMvnDeps() }
259+
// TODO: make Xfriend-path an explicit setting
260+
override def groovycOptions: T[Seq[String]] = Task {
261+
// FIXME
262+
outer.groovycOptions().filterNot(_.startsWith("-Xcommon-sources")) ++
263+
Seq(s"-Xfriend-paths=${outer.compile().classes.path.toString()}")
264+
}
265+
}
266+
267+
}
268+
269+
// TODO maybe an StandaloneGroovyTestsModule

0 commit comments

Comments
 (0)