Skip to content

Commit c802e90

Browse files
committed
Intial Groovy setup
1 parent c93e196 commit c802e90

File tree

22 files changed

+823
-11
lines changed

22 files changed

+823
-11
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],
10+
classpath: Seq[os.Path],
11+
outputDir: os.Path)(implicit ctx: TaskCtx): Result[CompilationResult]
12+
}

libs/groovylib/package.mill

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

0 commit comments

Comments
 (0)