I want to add all java packages (and classes) from src/main/java folder to the Arquillian deployment, as follow:
#RunWith(Arquillian.class)
public class ArquillianDeployment {
#Deployment
public static Archive<?> deploy() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.// add here all java packages from src/main/java folder
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
You can use addPackages instead.
ShrinkWrap.create(WebArchive.class, "test.war")
.addPackages(true, "my.rootPackage");
Related
I would like to ask if you know a guide or if you know how to use Proguard with Kotlin and Gradle and would like to share your knowledge, I would really appreciate it. Already searched Stack Overflow, but couldn't find a single (answered) question about using Proguard + Kotlin JVM (not android!) + Gradle.
I only found guides on the Internet regarding this matter for android, but I'm not using Kotlin for android, I'm building a Java Plugin (in other words, JVM) with Kotlin and I would like to use Proguard to minify and obfuscate my code. Note that my project is using Gradle Shadow to shadow its dependencies into the final jar (these dependencies don't need to be obfuscated but can be minified, and definitively do need to exist in the obfuscated jar created by Proguard).
I would like to know all the steps, things like how to step up Gradle to automatically minify & obfuscate my code (with a custom task), how to remove Kotlin metadata from java compiled classes, common issues & solutions to that issues, and anything else that you think it can be useful to know, everything is helpful. Thank you very much.
I got this working some time ago, refer to the Proguard manual to learn about the syntax of its configuration file. Also, for some reason, sometimes Proguard just says that the current obfuscated jar is "up-to-date" even when there's no jar, seems to be a bug that I could not find the steps to reproduce. If that happens to you, just change the Proguard version you're using and reload Gradle dependencies.
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// add this
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.guardsquare:proguard-gradle:7.1.1") {
exclude("com.android.tools.build")
}
}
}
plugins {
kotlin("jvm") version "1.5.30"
id("com.github.johnrengelman.shadow") version "7.0.0"
}
group = "com.yourgroup"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
maven("https://oss.sonatype.org/content/groups/public/")
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
// your dependencies here
}
tasks.test {
useJUnitPlatform()
}
// disables the normal jar task
tasks.jar { enabled = false }
// and enables shadowJar task
artifacts.archives(tasks.shadowJar)
tasks.shadowJar {
archiveFileName.set(rootProject.name + ".jar")
val dependencyPackage = "${rootProject.group}.dependencies.${rootProject.name.toLowerCase()}"
// your relocations here
exclude("ScopeJVMKt.class")
exclude("DebugProbesKt.bin")
exclude("META-INF/**")
}
tasks.register<proguard.gradle.ProGuardTask>("proguard") {
// here is where you configure your Proguard stuff, you can include libraries directly
// through here, or in the configuration file, I usually just use the configuration file
// to do everything (it can be any name and extension you want, just using .pro here cause
// that's what Android uses)
configuration("proguard-rules.pro")
}
// keep this if you want run proguard task automatically after building
tasks.build.get().finalizedBy(tasks.getByName("proguard"))
tasks.withType<JavaCompile> {
sourceCompatibility = "16"
targetCompatibility = "16"
options.encoding = "UTF-8"
}
tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "16" }
proguard-rules.pro: This is just an example file used to minify a Minecraft plugin, but the logic applies no matter what kind of jar you're dealing with.
# injars = your shadowed jar
-injars build/libs/YourShadowedJarHere.jar
# outjars = the name of the new obfuscated/minified jar
-outjars build/libs/YourShadowedJarHere-min.jar
# important! you need to add this "rt" file from your JDK as libraryjars!
-libraryjars "C:\Program Files\Java\jdk1.8.0_291\jre\lib\rt.jar"
# add here the jar to any compileOnly dependency you might have (or add "dontwarn" to make proguard ignore the missing classes)
-libraryjars "PathTo\YourProject\SomeLocalLibraries\SomeLocalLibrary.jar"
-dontwarn net.minecraft.**
-dontwarn org.bukkit.**
-dontwarn com.google.**
-dontwarn com.comphenix.**
-dontwarn android.**
-dontwarn org.hibernate.**
-dontwarn com.sk89q.worldedit**
-dontwarn com.sk89q.worldguard**
-dontwarn net.milkbowl.vault.economy**
-keep class yourpackage.dependencies.yourproject.hikari.metrics.**
-dontwarn com.codahale.metrics.**
-keep class com.codahale.metrics.**
-dontwarn **hikari.metrics**
-dontwarn javax.crypto.**
-dontwarn javassist.**
-dontwarn **slf4j**
-dontwarn io.micrometer.core.instrument.MeterRegistry
-dontwarn org.codehaus.mojo.**
-dontwarn **prometheus**
-dontwarn **configurate.**
-dontwarn **koin.core.time.**
-dontwarn net.Indyuce.**
-dontwarn **xseries.**
-keepnames class kotlin.coroutines.** { *; }
-dontwarn **kotlinx.coroutines.**
-dontwarn **org.apache.commons.codec**
#-dontshrink
#-dontobfuscate
#-dontoptimize
# Keep your main class
-keep,allowobfuscation,allowoptimization class * extends org.bukkit.plugin.java.JavaPlugin { *; }
# Keep event handlers
-keep,allowobfuscation,allowoptimization class * extends org.bukkit.event.Listener {
#org.bukkit.event.EventHandler <methods>;
}
# Keep main package name
// -keeppackagenames "your.package"
# Keep public enum names
-keepclassmembers public enum yourpackage.** {
<fields>;
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Keep all ProtocolLib packet listeners (this was rough to get working, don't turn on optimization, it ALWAYS breaks the sensible ProtocolLib)
-keepclassmembers class yourpackage.yourproject.** {
void onPacketSending(com.comphenix.protocol.events.PacketEvent);
void onPacketReceiving(com.comphenix.protocol.events.PacketEvent);
}
# Keep static fields in custom Events
-keepclassmembers,allowoptimization class yourpackage.yourproject.** extends org.bukkit.event.Event {
#yourpackage.dependencies.kotlin.jvm.JvmStatic <fields>;
public static final <fields>;
#yourpackage.dependencies.kotlin.jvm.JvmStatic <methods>;
public static <methods>;
}
# Remove dependencies obsfuscation to remove bugs factor
#-keep,allowshrinking class yourpackage.dependencies.** { *; }
# If your goal is obfuscating and making things harder to read, repackage your classes with this rule
-repackageclasses yourpackage.yourproject
-allowaccessmodification
-mergeinterfacesaggressively
# You can parse any resource files that might contain a reference of your classes here (so they are updated according to the modifications made by Proguard)
-adaptresourcefilecontents **.yml,META-INF/MANIFEST.MF
# Some attributes that you'll need to keep (warning: removing *Annotation* might break some stuff)
-keepattributes Exceptions,Signature,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#-keepattributes Exceptions,Signature,Deprecated,LineNumberTable,*Annotation*,EnclosingMethod
#-keepattributes LocalVariableTable,LocalVariableTypeTable,Exceptions,InnerClasses,Signature,Deprecated,LineNumberTable,*Annotation*,EnclosingMethod
I am stuck with converting a groovy gradle script to kotlin script. I am trying to use pf4j in my project and have started converting their example build.gradle to .gradle.kts. Example can be found here: https://github.com/pf4j/pf4j/blob/master/demo_gradle/plugins/build.gradle
Since I got stuck I am trying it just for one plugin right now, however their example is applying the task to all subprojects. So be aware of the difference.
So I tried to replace their build example with the following gradle.kts file:
val pluginClass: String by project
val pluginId: String by project
val pluginProvider: String by project
val version: String by project
val pf4jVersion: String by project
dependencies {
implementation(project(":api"))
implementation("org.pf4j:pf4j:${pf4jVersion}")
annotationProcessor("org.pf4j:pf4j:${pf4jVersion}")
}
val buildPluginArchive = task("plugin", Jar::class) {
manifest {
attributes["Plugin-Class"] = pluginClass
attributes["Plugin-Id"] = pluginId
attributes["Plugin-Version"] = version
attributes["Plugin-Provider"] = pluginProvider
}
archiveBaseName.set("plugin-${pluginId}")
into("classes") {
from(sourceSets.main.get().output)
}
dependsOn(configurations.runtimeClasspath)
into("lib") {
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
archiveExtension.set("zip")
}
tasks {
"build" {
dependsOn(buildPluginArchive)
}
}
And it works and generates a zip, but the contents do not match the original. Firstly the lib folder does not only contain jar files, but also a folder structure (and more important a META-INF folder with MANIFEST.MF file that confuses the plugin loader). And it is missing the MANIFEST.MF in the classes/META-INF folder.
I suspect the issue being somwhere with this configuration in the original build.gradle:
into('classes') {
with jar
}
I just could not find any meaningful documentation about what "with jar" actually does or how to replicate the behavior in gradle.kts.
How can I get the same output as the demo with a gradle.kts configuration?
I just could not find any meaningful documentation about what "with jar" actually does
with is a method from the Jar task type. See the method details section of the Jar task, scroll to the bottom and you'll find information the with method. It's signature is:
CopySpec with(CopySpec... sourceSpecs)
Now if you were to look at the API documentation for Jar, you'll see that with actually comes from CopySpec which Jar implements thanks to its super class.
The jar part in with jar is referring to the task named jar which is created by the Java plugin.
With all of that said, a more idiomatic approach for the with part would be:
tasks {
val plugin by registering(Jar::class) {
into("classes") {
with(jar.get())
}
}
build {
dependsOn(plugin)
}
}
TestLinkAPIClient cannot be resolved to a type, already I added “testlink-java-api” dependency in pom.xml.
Its maven project by using java
please find the below code
package com.tesco.pom.baseClass;
import br.eti.kinoshita.testlinkjavaapi.TestLinkAPIException;
public class ExecuteTestLinkResults implements TestLinkIntegration {
public static void reportTestCaseResult(String Project, String TestPlan,
String TestCase, String Build, String notes, String teststatus) throws TestLinkAPIException {
TestLinkAPIClient testlinkAPIClient = new TestLinkAPIClient(APIKEY, URL);
testlinkAPIClient.reportTestCaseResult(Project, TestPlan, TestCase, Build, notes,
teststatus);
}
}
TestLinkAPIClient does not provide any Maven dependency because it is a third party tool.
So you have to download and add jar files for that you can follow this link:
https://code.google.com/archive/p/dbfacade-testlink-rpc-api/downloads
Download "testlink-api-client-2.0.tar.gz" and import this jar file and start doing your work.
If you still want jar as Maven dependency then you can try and install jar files by following this link:
https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html
As titled, I'd like to know how to modify the gradle.build.kts in order to have a task to create a unique jar with all the dependencies (kotlin lib included) inside.
I found this sample in Groovy:
//create a single Jar with all dependencies
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.mkyong.DateUtils'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
But I have no idea how I could write that in kotlin, other than:
task("fatJar") {
}
Here is a version that does not use a plugin, more like the Groovy version.
import org.gradle.jvm.tasks.Jar
val fatJar = task("fatJar", type = Jar::class) {
baseName = "${project.name}-fat"
manifest {
attributes["Implementation-Title"] = "Gradle Jar File Example"
attributes["Implementation-Version"] = version
attributes["Main-Class"] = "com.mkyong.DateUtils"
}
from(configurations.runtime.map({ if (it.isDirectory) it else zipTree(it) }))
with(tasks["jar"] as CopySpec)
}
tasks {
"build" {
dependsOn(fatJar)
}
}
Also explained here
Some commenters pointed out that this does not work anymore with newer Gradle versions.
Update tested with Gradle 5.4.1:
import org.gradle.jvm.tasks.Jar
val fatJar = task("fatJar", type = Jar::class) {
baseName = "${project.name}-fat"
manifest {
attributes["Implementation-Title"] = "Gradle Jar File Example"
attributes["Implementation-Version"] = version
attributes["Main-Class"] = "com.mkyong.DateUtils"
}
from(configurations.runtimeClasspath.get().map({ if (it.isDirectory) it else zipTree(it) }))
with(tasks.jar.get() as CopySpec)
}
tasks {
"build" {
dependsOn(fatJar)
}
}
Note the difference in configurations.runtimeClasspath.get() and with(tasks.jar.get() as CopySpec).
Here are 4 ways to do this. Note that the first 3 methods modify the existing Jar task of Gradle.
Method 1: Placing library files beside the result JAR
This method does not need application or any other plugins.
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
Note that Java requires us to use relative URLs for the Class-Path attribute. So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems). If you want to use absolute paths, maybe this workaround will work.
Create the JAR with the following command:
./gradlew jar
The result JAR will be created in build/libs/ directory by default.
After creating your JAR, copy your library JARs in libs/ sub-directory of where you put your result JAR. Make sure your library JAR files do not contain space in their file name (their file name should match the one specified by ${file.name} variable above in the task).
Method 2: Embedding the libraries in the result JAR file (fat or uber JAR)
This method too does not need any Gradle plugin.
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Creating the JAR is exactly the same as the previous method.
Method 3: Using the Shadow plugin (to create a fat or uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these configs are reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
Create the JAR with this command:
./gradlew shadowJar
See Shadow documentations for more information about configuring the plugin.
Method 4: Creating a new task (instead of modifying the Jar task)
tasks.create("MyFatJar", Jar::class) {
group = "my tasks" // OR, for example, "build"
description = "Creates a self-contained fat JAR of the application that can be run."
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree)
from(dependencies)
with(tasks.jar.get())
}
Running the created JAR
java -jar my-artifact.jar
The above solutions were tested with:
Java 17
Gradle 7.1 (which uses Kotlin 1.4.31 for .kts build scripts)
See the official Gradle documentation for creating uber (fat) JARs.
For more information about manifests, see Oracle Java Documentation: Working with Manifest files.
For difference between tasks.create() and tasks.register() see this post.
Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file). To access a resource file in your application, use this code (note the / at the start of names):
Kotlin
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// Alternative ways:
// val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
// val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
// val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
Here is how to do it as of Gradle 6.5.1, Kotlin/Kotlin-Multiplatform 1.3.72, utilizing a build.gradle.kts file and without using an extra plugin which does seem unnecessary and problematic with multiplatform;
Note: in reality, few plugins work well with the multiplatform plugin from what I can tell, which is why I suspect its design philosophy is so verbose itself. It's actually fairly elegant IMHO, but not flexible or documented enough so it takes a ton of trial and error to setup even WITHOUT additional plugins.
Hope this helps others.
kotlin {
jvm {
compilations {
val main = getByName("main")
tasks {
register<Jar>("fatJar") {
group = "application"
manifest {
attributes["Implementation-Title"] = "Gradle Jar File Example"
attributes["Implementation-Version"] = archiveVersion
attributes["Main-Class"] = "[[mainClassPath]]"
}
archiveBaseName.set("${project.name}-fat")
from(main.output.classesDirs, main.compileDependencyFiles)
with(jar.get() as CopySpec)
}
}
}
}
}
You could use the ShadowJar plugin to build a fat jar:
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
buildscript {
repositories {
mavenCentral()
gradleScriptKotlin()
}
dependencies {
classpath(kotlinModule("gradle-plugin"))
classpath("com.github.jengelman.gradle.plugins:shadow:1.2.3")
}
}
apply {
plugin("kotlin")
plugin("com.github.johnrengelman.shadow")
}
repositories {
mavenCentral()
}
val shadowJar: ShadowJar by tasks
shadowJar.apply {
manifest.attributes.apply {
put("Implementation-Title", "Gradle Jar File Example")
put("Implementation-Version" version)
put("Main-Class", "com.mkyong.DateUtils")
}
baseName = project.name + "-all"
}
Simply run the task with 'shadowJar'.
NOTE: This assumes you're using GSK 0.7.0 (latest as of 02/13/2017).
I have two projects, project A and Project B. Both are written in groovy and use gradle as their build system.
Project A requires project B.
This holds for both the compile and test code.
How can I configure that the test classes of project A have access to the test classes of project B?
You can expose the test classes via a 'tests' configuration and then define a testCompile dependency on that configuration.
I have this block for all java projects, which jars all test code:
task testJar(type: Jar, dependsOn: testClasses) {
baseName = "test-${project.archivesBaseName}"
from sourceSets.test.output
}
configurations {
tests
}
artifacts {
tests testJar
}
Then when I have test code I want to access between projects I use
dependencies {
testCompile project(path: ':aProject', configuration: 'tests')
}
This is for Java; I'm assuming it should work for groovy as well.
This is a simpler solution that doesn't require an intermediate jar file:
dependencies {
...
testCompile project(':aProject').sourceSets.test.output
}
There's more discussion in this question: Multi-project test dependencies with gradle
This works for me (Java)
// use test classes from spring-common as dependency to tests of current module
testCompile files(this.project(':spring-common').sourceSets.test.output)
testCompile files(this.project(':spring-common').sourceSets.test.runtimeClasspath)
// filter dublicated dependency for IDEA export
def isClassesDependency(module) {
(module instanceof org.gradle.plugins.ide.idea.model.ModuleLibrary) && module.classes.iterator()[0].url.toString().contains(rootProject.name)
}
idea {
module {
iml.whenMerged { module ->
module.dependencies.removeAll(module.dependencies.grep{isClassesDependency(it)})
module.dependencies*.exported = true
}
}
}
.....
// and somewhere to include test classes
testRuntime project(":spring-common")
This is now supported as a first class feature in Gradle (since 5.6)
Modules with java or java-library plugins can also include a java-test-fixtures plugin which exposes helper classes and resources to be consumed with testFixtures helper. Benefit of this approach against artifacts and classifiers are:
proper dependency management (implementation/api)
nice separation from test code (separate source set)
no need to filter out test classes to expose only utilities
maintained by Gradle
Example:
:modul:one
modul/one/build.gradle
plugins {
id "java-library" // or "java"
id "java-test-fixtures"
}
dependencies {
testFixturesImplementation("your.jar:dependency:0.0.1")
}
or lazyly just add all dependencies of main implementation configuration:
val testFixturesImplementation by configurations.existing
val implementation by configurations.existing
testFixturesImplementation.get().extendsFrom(implementation.get())
modul/one/src/testFixtures/java/com/example/Helper.java
package com.example;
public class Helper {}
:modul:other
modul/other/build.gradle
plugins {
id "java" // or "java-library"
}
dependencies {
testImplementation(testFixtures(project(":modul:one")))
}
modul/other/src/test/java/com/example/other/SomeTest.java
package com.example.other;
import com.example.Helper;
public class SomeTest {
#Test void f() {
new Helper(); // used from :modul:one's testFixtures
}
}
For more info, see the documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
The above solution works, but not for the latest version 1.0-rc3 of Gradle.
task testJar(type: Jar, dependsOn: testClasses) {
baseName = "test-${project.archivesBaseName}"
// in the latest version of Gradle 1.0-rc3
// sourceSets.test.classes no longer works
// It has been replaced with
// sourceSets.test.output
from sourceSets.test.output
}
If ProjectA contains the test code you wish to use in ProjectB and ProjectB wants to use artifacts to include the test code, then ProjectB's build.gradle would look like this:
dependencies {
testCompile("com.example:projecta:1.0.0-SNAPSHOT:tests")
}
Then you need to add an archives command to the artifacts section in ProjectA's build.gradle:
task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests
}
artifacts {
tests testsJar
archives testsJar
}
jar.finalizedBy(testsJar)
Now when ProjectA's artifacts are published to your artifactory they will include a -tests jar. This -tests jar can then be added as a testCompile dependency for ProjectB (as shown above).
For Gradle 1.5
task testJar(type: Jar, dependsOn: testClasses) {
from sourceSets.test.java
classifier "tests"
}
For Android on the latest gradle version (I'm currently on 2.14.1) you just need to add the below in Project B to get all the test dependencies from Project A.
dependencies {
androidTestComplie project(path: ':ProjectA')
}
dependencies {
testImplementation project(':project_name')
}