Skip to content

Commit a3e46ba

Browse files
authored
Show how to integrate with Groovy and Scala plugins (#1338)
* Declare JvmName for writeClass for Kotlin * Replace isJava * Fix file suffixes * Add GroovyPluginTest * Add ScalaPluginTest * Add docs
1 parent b5bcde9 commit a3e46ba

File tree

7 files changed

+252
-39
lines changed

7 files changed

+252
-39
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Integrating with Groovy and Scala Plugins
2+
3+
Shadow also works well for Groovy and Scala, here are integration examples:
4+
5+
For Groovy:
6+
7+
=== "Kotlin"
8+
9+
```kotlin
10+
plugins {
11+
groovy
12+
id("com.gradleup.shadow")
13+
}
14+
15+
dependencies {
16+
// If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`.
17+
implementation(localGroovy())
18+
}
19+
20+
tasks.shadowJar {
21+
manifest {
22+
// Optionally, set the main class for the shadowed JAR.
23+
attributes["Main-Class"] = "com.example.Main"
24+
}
25+
}
26+
```
27+
28+
=== "Groovy"
29+
30+
```groovy
31+
plugins {
32+
id 'groovy'
33+
id 'com.gradleup.shadow'
34+
}
35+
36+
dependencies {
37+
// If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`.
38+
implementation localGroovy()
39+
}
40+
41+
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
42+
manifest {
43+
// Optionally, set the main class for the shadowed JAR.
44+
attributes 'Main-Class': 'com.example.Main'
45+
}
46+
}
47+
```
48+
49+
For Scala:
50+
51+
=== "Kotlin"
52+
53+
```kotlin
54+
plugins {
55+
scala
56+
id("com.gradleup.shadow")
57+
}
58+
59+
dependencies {
60+
// If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`.
61+
implementation("org.scala-lang:scala-library:2.13.16")
62+
}
63+
64+
tasks.shadowJar {
65+
manifest {
66+
// Optionally, set the main class for the shadowed JAR.
67+
attributes["Main-Class"] = "com.example.Main"
68+
}
69+
}
70+
```
71+
72+
=== "Groovy"
73+
74+
```groovy
75+
plugins {
76+
id 'scala'
77+
id 'com.gradleup.shadow'
78+
}
79+
80+
dependencies {
81+
// If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`.
82+
implementation 'org.scala-lang:scala-library:2.13.16'
83+
}
84+
85+
tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
86+
manifest {
87+
// Optionally, set the main class for the shadowed JAR.
88+
attributes 'Main-Class': 'com.example.Main'
89+
}
90+
}
91+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ nav:
7171
- 'Custom Tasks': custom-tasks/README.md
7272
- 'Application Plugin': application-plugin/README.md
7373
- 'KMP Plugin': kmp-plugin/README.md
74+
- 'Groovy and Scala Plugins': groovy-and-scala-plugins/README.md
7475
- 'Publishing': publishing/README.md
7576
- 'Multi-Project': multi-project/README.md
7677
- 'Plugins': plugins/README.md

src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransform
1414
import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository
1515
import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder
1616
import com.github.jengelman.gradle.plugins.shadow.util.JarPath
17+
import com.github.jengelman.gradle.plugins.shadow.util.JvmLang
1718
import java.io.Closeable
1819
import java.nio.file.Path
1920
import java.util.Properties
@@ -202,46 +203,64 @@ abstract class BasePluginTest {
202203
packageName: String = "my",
203204
withImports: Boolean = false,
204205
className: String = "Main",
205-
isJava: Boolean = true,
206-
classContent: () -> String = {
207-
if (isJava) {
208-
val imports = if (withImports) "import junit.framework.Test;" else ""
209-
val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\""
210-
"""
211-
package $packageName;
212-
$imports
213-
public class $className {
214-
public static void main(String[] args) {
215-
if (args.length == 0) throw new IllegalArgumentException("No arguments provided.");
216-
String content = String.format("Hello, World! (%s) from $className", (Object[]) args);
217-
System.out.println(content);
218-
System.out.println($classRef);
206+
jvmLang: JvmLang = JvmLang.Java,
207+
content: () -> String = {
208+
when (jvmLang) {
209+
JvmLang.Groovy,
210+
JvmLang.Java,
211+
-> {
212+
val imports = if (withImports) "import junit.framework.Test;" else ""
213+
val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\""
214+
"""
215+
package $packageName;
216+
$imports
217+
public class $className {
218+
public static void main(String[] args) {
219+
if (args.length == 0) throw new IllegalArgumentException("No arguments provided.");
220+
String content = String.format("Hello, World! (%s) from $className", (Object[]) args);
221+
System.out.println(content);
222+
System.out.println($classRef);
223+
}
219224
}
220-
}
221-
""".trimIndent()
222-
} else {
223-
val imports = if (withImports) "import junit.framework.Test;" else ""
224-
val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\""
225-
"""
226-
package $packageName
227-
$imports
228-
fun main(vararg args: String) {
229-
if (args.isEmpty()) throw IllegalArgumentException("No arguments provided.")
230-
val content ="Hello, World! (%s) from $className".format(*args)
231-
println(content)
232-
println($classRef)
233-
}
234-
""".trimIndent()
225+
""".trimIndent()
226+
}
227+
JvmLang.Kotlin -> {
228+
val imports = if (withImports) "import junit.framework.Test;" else ""
229+
val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\""
230+
"""
231+
@file:JvmName("$className")
232+
package $packageName
233+
$imports
234+
fun main(vararg args: String) {
235+
if (args.isEmpty()) throw IllegalArgumentException("No arguments provided.")
236+
val content ="Hello, World! (%s) from $className".format(*args)
237+
println(content)
238+
println($classRef)
239+
}
240+
""".trimIndent()
241+
}
242+
JvmLang.Scala -> {
243+
val imports = if (withImports) "import junit.framework.Test" else ""
244+
val classRef = if (withImports) "\"Refs: \" + classOf[Test].getName" else "\"Refs: null\""
245+
"""
246+
package $packageName
247+
$imports
248+
object $className {
249+
def main(args: Array[String]): Unit = {
250+
if (args.isEmpty) throw new IllegalArgumentException("No arguments provided.")
251+
val content = s"Hello, World! (%s) from $className".format(args: _*)
252+
println(content)
253+
println($classRef)
254+
}
255+
}
256+
""".trimIndent()
257+
}
235258
}
236259
},
237260
): String {
238-
if (isJava) {
239-
path("src/$sourceSet/java/$packageName/$className.java").writeText(classContent())
240-
} else {
241-
path("src/$sourceSet/kotlin/$packageName/$className.kt").writeText(classContent())
242-
}
243-
val baseClassPath = packageName.replace('.', '/') + "/$className"
244-
return if (isJava) "$baseClassPath.class" else "${baseClassPath}Kt.class"
261+
val basePath = packageName.replace('.', '/') + "/$className"
262+
path("src/$sourceSet/$jvmLang/$basePath.${jvmLang.suffix}").writeText(content())
263+
return "$basePath.class"
245264
}
246265

247266
fun writeClientAndServerModules(
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.jengelman.gradle.plugins.shadow
2+
3+
import assertk.assertThat
4+
import com.github.jengelman.gradle.plugins.shadow.util.JvmLang
5+
import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
6+
import kotlin.io.path.appendText
7+
import kotlin.io.path.writeText
8+
import org.junit.jupiter.api.BeforeEach
9+
import org.junit.jupiter.api.Test
10+
11+
class GroovyPluginTest : BasePluginTest() {
12+
@BeforeEach
13+
override fun setup() {
14+
super.setup()
15+
val projectBuildScript = getDefaultProjectBuildScript(
16+
plugin = "groovy",
17+
withGroup = true,
18+
withVersion = true,
19+
)
20+
projectScriptPath.writeText(projectBuildScript)
21+
}
22+
23+
@Test
24+
fun compatGroovy() {
25+
val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Groovy)
26+
projectScriptPath.appendText(
27+
"""
28+
dependencies {
29+
compileOnly localGroovy()
30+
implementation 'junit:junit:3.8.2'
31+
}
32+
""".trimIndent(),
33+
)
34+
35+
run(shadowJarTask)
36+
37+
assertThat(outputShadowJar).useAll {
38+
containsEntries(
39+
mainClassEntry,
40+
*junitEntries,
41+
)
42+
}
43+
}
44+
}

src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow
33
import assertk.assertThat
44
import assertk.assertions.isEqualTo
55
import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey
6+
import com.github.jengelman.gradle.plugins.shadow.util.JvmLang
67
import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
78
import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr
89
import kotlin.io.path.appendText
@@ -26,7 +27,7 @@ class KmpPluginTest : BasePluginTest() {
2627

2728
@Test
2829
fun compatKmpJvmTarget() {
29-
val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false)
30+
val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin)
3031
projectScriptPath.appendText(
3132
"""
3233
kotlin {
@@ -60,8 +61,8 @@ class KmpPluginTest : BasePluginTest() {
6061
@ParameterizedTest
6162
@ValueSource(booleans = [false, true])
6263
fun canSetMainClassAttribute(useShadowAttr: Boolean) {
63-
val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false)
64-
val main2ClassEntry = writeClass(sourceSet = "jvmMain", isJava = false, className = "Main2")
64+
val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin)
65+
val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2")
6566
val mainClassName = "my.Main"
6667
val main2ClassName = "my.Main2"
6768
val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else ""
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.jengelman.gradle.plugins.shadow
2+
3+
import assertk.assertThat
4+
import com.github.jengelman.gradle.plugins.shadow.util.JvmLang
5+
import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
6+
import kotlin.io.path.appendText
7+
import kotlin.io.path.writeText
8+
import org.junit.jupiter.api.BeforeEach
9+
import org.junit.jupiter.api.Test
10+
11+
class ScalaPluginTest : BasePluginTest() {
12+
@BeforeEach
13+
override fun setup() {
14+
super.setup()
15+
val projectBuildScript = getDefaultProjectBuildScript(
16+
plugin = "scala",
17+
withGroup = true,
18+
withVersion = true,
19+
)
20+
projectScriptPath.writeText(projectBuildScript)
21+
}
22+
23+
@Test
24+
fun compatScala() {
25+
val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Scala)
26+
projectScriptPath.appendText(
27+
"""
28+
dependencies {
29+
compileOnly 'org.scala-lang:scala-library:2.13.16'
30+
implementation 'junit:junit:3.8.2'
31+
}
32+
""".trimIndent(),
33+
)
34+
35+
run(shadowJarTask)
36+
37+
assertThat(outputShadowJar).useAll {
38+
containsEntries(
39+
mainClassEntry,
40+
*junitEntries,
41+
)
42+
}
43+
}
44+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.jengelman.gradle.plugins.shadow.util
2+
3+
enum class JvmLang(
4+
val suffix: String,
5+
) {
6+
Groovy("groovy"),
7+
Java("java"),
8+
Kotlin("kt"),
9+
Scala("scala"),
10+
;
11+
12+
override fun toString(): String = name.lowercase()
13+
}

0 commit comments

Comments
 (0)