how to relocate package inside jar for gradle dependencies - java

currently I am using a package call a.jar. Unfortunately that this jar includes a com.example.b package with some customized changes to code base inside com.example.b.
Now I want to have some latest cool features from com.example.b package in b.jar from github.
I think the best solution (not sure how it can be done) is to relocate latest com.example.b to com.example.standalone.b , so that the a.jar can still use its customized com.exampl.b source, while inside project I can use com.example.standalone.b package.
I did research for shadow plugin, but seems it rename package by package name globally, so that the package (com.example.b) in both jars (a.jar and b.jar) would be also renamed and have conflict.
May I know how to do this for specific jar, like below example?
implementation a.jar:1.0
implementation (b.jar:2.0) {
rename 'com.example.b' to 'com.example.standalone.b'
}

finally i managed to solve it by below configuration in goovy gradle.
configurations {
relocateB // just define a separate configuration
}
task relocateB (type: ShadowJar) {
def pkg = 'com.example.b' // lib to relocate
relocate pkg, "com.example.standalone.b" // we want to relocate the above package
configurations = [project.configurations.relocateB] // our configuration from above
dependencies {
// you must exclude below files in 'kotlin_module' and 'kotlin_builtins' extension,
// otherwise you won't be able to import `com.example.standalone.b` in kotlin files.
// This is a bug from Kotlin, consumed two days for me to solve.
// (https://youtrack.jetbrains.com/issue/KT-25709)
exclude '**/*.kotlin_metadata'
exclude '**/*.kotlin_module'
exclude '**/*.kotlin_builtins'
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn relocateB
}
dependencies {
...
relocateB ('com.example.b:2.2.3')
api tasks.relocateB.outputs.files
api 'com.example.a' // original package that won't be polluted
...
}

Related

Export Gradle subproject in jar without adding it to dependencies

I have a Gradle project with 3 subprojects : core, 1_13, and 1_14. The 1_13 and 1_14 depends on the core project, but the final build has to be done on the core project, and I wanted to include the builds of the 1_13 and 1_14 in the jar.
The 1_13 and 1_14 subprojects have deps that aren't in the core subproject.
Actually, I use the sourceSets to include the source files of the 1_13 and 1_14 projects, but the fact that there are dependencies in the subprojects that doesn't exist in the core subproject, and that I can't use apiElements in the dependencies of the core subproject, because if I do a circular import occurs gets me stuck.
Actually, I use this :
sourceSets {
main {
java {
srcDirs 'src', '../1_13/src', '../1_14/src'
resources {
srcDir 'resources'
}
}
}
}
But because the libs are not present in the core subprojects, the build fails.
I precise that I only need the libs at the compile time, and not at runtime. I also can't add the libs to the core subproject because the submodules uses different versions of the same library. The core module is only here to route on whether one or another package should be used.
After continuing to search for a while, I found the way to do exactly what I want using a Gradle task.
Actually, I created a task to combine all the jar files of the different modules :
task buildPlugin(type: Jar, group: 'build') {
// Change the archive name and include all outputs of build of modules
archiveFileName = "${rootProject.name}.jar"
from getRootProject().allprojects.findAll().sourceSets.main.output
// Delete the old jar of the core module, to keep only the finally build jar file
doLast {
var oldJar = new File(project.buildDir, "libs/" + project.name + "-" + project.version + ".jar")
if (oldJar.exists()) {
oldJar.delete()
}
}
}
The cleanest way would be to extract the common logic that is used by 1_13 and 1_14 to a new project (e.g. common) and add 1_13 and 1_14 as real dependencies to the core project.
You could of course hack something like „use the dependencies of the subprojects as dependencies of the core project“ but hacks like this usually cause more trouble later on.

Nested libraries aar file behavior

We are facing problem while importing the aar file of a library (let's call it library_2) into another library (let's call it library_1). In addition, we need to import in the app project only library_1.aar file and make library_2 methods available at project level. What would it be the folders structure and the corresponding .gradle files?
Description of the problem in the image below:
You need to use api instead implementation for your library_1. First, add the following code to your library_1 project build.gradle:
allprojects {
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
}
then in your library_1 module build.gradle, add the following code to your dependencies block (assuming you have add library_2 aar to library_1 libs folder):
dependencies {
api(name:'library_2', ext:'aar')
}
Now, you can access the library_2 when using library_1 aar with the following dependencies block in your app module:
dependencies {
api(name:'library_1', ext:'aar')
}
For more details about flat aar, read How to manually include external aar package using new Gradle Android Build System.
For more details about the differences between compile, implementation, and api read Gradle Implementation vs API configuration
I have resolved above issue that you are facing. Please have a look below code and let see if it work for you.
In your app.gradle file add below dependecy:
implementation ('package.name.of.aar:modulethree-debug#aar') {
transitive=true
}
Note: modulethree-debug#aar is aar file which you want to access in other module.

Including specific external library to a jar file built by Gradle

I am trying to convert a multi module project build process from Ant to Gradle.
We have a common module which is used by every other module. In common module I need these dependencies to be able compile it (by gradle build)
dependencies {
api 'com.google.guava:guava:18.0'
api 'org.json:json:20131018'
implementation 'org.apache.httpcomponents:httpclient:4.5.5'
implementation 'org.jruby:jruby-complete:1.5.1'
implementation 'org.python:jython:2.2.1'
implementation 'com.google.code.gson:gson:2.6.2'
}
Some of modules should include all dependencies needed in runtime inside result jar files. Because of this I am adding the code of module dependencies to the jar file as well. Like below:
dependencies {
compile project(':core:common')
compile project(':core:installer')
}
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
}
The problem is that I want to add external libraries to the jar file as well so that I have compelete jar file. It is possible to add external libraries by adding a line to jar above like this:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
But then I will have a large jar file containing all of dependencies in common module while some of them are not needed in my specific jar file. What I want is to add specific external libraries to jar file for example I want just add 'org.json:json:20131018' and 'org.apache.httpcomponents:httpclient:4.5.5' to library and ignore rest of dependencies. I couldn't find a solution for this.
I solved this by adding the code below to jar:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
def libraries =['httpclient-4.5.5.jar','json-20131018.jar']
from configurations.runtimeClasspath.
findAll { libraries.contains(it.name)}.
collect { zipTree(it) }
}
But I think that still Gradle should offer a better solution to include or exclude external libraries to jar file.
I updated solution above little bit which I think it is a better way to do this because we don't need specify jar files. We can define a configuration like this:
configurations {
runtimeLibraries
}
dependencies {
compile project(':core:common')
compile project(':core:installer')
runtimeLibraries 'org.apache.httpcomponents:httpclient:4.5.5', 'org.json:json:20131018'
}
Then we can update Jar task as below:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
from configurations.runtimeLibraries.collect { zipTree(it) }
}
One diffference in result of those two methods is that with defining configuration gradle will recognize needed dependency and will add them to jar as well. For example if you are using commons-net:commons-net:1.4.1 as part of runtimeLibraries in created jar files you can find org.apache.oro packages which is used by commons-net

How to use selected classes from other subproject as test dependency in Gradle

To add a simple dependency on test source sets from an another subproject I can do:
testCompile project(':subFoo1').sourceSets.test.output
This solution works, but in many cases it is not intended to add the whole source set as a dependency. For example I would like to use only test data builders and in that case files like test-logback.xml (and regular tests) pollute my test classpath in the master module.
I tried the idea with test JAR (which can have filtered content, but is problematic as a dependency) and some combination with eachFileRecurse, but with no luck.
My question. How can I add only a subset of given source set(s) (e.g. only classes with builders matching **/*Builder.* pattern) as a testCompile dependency in another subproject?
You'll want something along the lines of:
upstream/build.gradle:
apply plugin: "java"
task testJar(type: Jar) {
classifier = "tests"
from sourceSets.test.output
exclude "**/*Test.class"
}
artifacts {
testRuntime testJar
}
downstream/build.gradle:
apply plugin: "java"
dependencies {
testCompile project(path: ":upstream", configuration: "testRuntime")
}
Instead of using testRuntime, you could also declare (e.g. configurations { testFixture }) and use a custom configuration, which would give you more control over which external dependencies are passed on to downstream projects. Yet another option would be to declare a separate source set for the part of the test code that is to be passed on. (This would also give you separate compile and runtime configurations to work with.)
PS: Reaching out into another project's object model (e.g. project(':subFoo1').sourceSets.test.output) is problematic, and should be avoided when possible.

Multi-project test dependencies with gradle

I have a multi-project configuration and I want to use gradle.
My projects are like this:
Project A
-> src/main/java
-> src/test/java
Project B
-> src/main/java (depends on src/main/java on Project A)
-> src/test/java (depends on src/test/java on Project A)
My Project B build.gradle file is like this:
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
}
The task compileJava work great but the compileTestJava does not compile the test file from Project A.
Deprecated - For Gradle 5.6 and above use this answer.
In Project B, you just need to add a testCompile dependency:
dependencies {
...
testCompile project(':A').sourceSets.test.output
}
Tested with Gradle 1.7.
This is now supported as a first class feature in Gradle. 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"
}
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
}
}
Further reading
For more info, see the documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
It was added in 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects
Simple way is to add explicit task dependency in ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Difficult (but more clear) way is to create additional artifact configuration for ProjectA:
task myTestsJar(type: Jar) {
// pack whatever you need...
}
configurations {
testArtifacts
}
artifacts {
testArtifacts myTestsJar
}
and add the testCompile dependency for ProjectB
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
I've come across this problem myself recently, and man is this a tough issues to find answers for.
The mistake you are making is thinking that a project should export its test elements in the same way that it exports its primary artifacts and dependencies.
What I had a lot more success with personally was making a new project in Gradle. In your example, I would name it
Project A_Test
-> src/main/java
I would put into the src/main/java the files that you currently have in Project A/src/test/java. Make any testCompile dependencies of your Project A compile dependencies of Project A_Test.
Then make Project A_Test a testCompile dependency of Project B.
It's not logical when you come at it from the perspective of the author of both projects, but I think it makes a lot of sense when you think about projects like junit and scalatest (and others. Even though those frameworks are testing-related, they are not considered part of the "test" targets within their own frameworks - they produce primary artifacts that other projects just happen to use within their test configuration. You just want to follow that same pattern.
Trying to do the other answers listed here did not work for me personally (using Gradle 1.9), but I've found that the pattern I describe here is a cleaner solution anyway.
I know it's an old question but I just had the same problem and spent some time figuring out what is going on. I'm using Gradle 1.9. All changes should be in ProjectB's build.gradle
To use test classes from ProjectA in tests of ProjectB:
testCompile files(project(':ProjectA').sourceSets.test.output.classesDir)
To make sure that sourceSets property is available for ProjectA:
evaluationDependsOn(':ProjectA')
To make sure test classes from ProjectA are actually there, when you compile ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Please read the update bellow.
Similar problems described by JustACluelessNewbie occurs in IntelliJ IDEA. Problem is that dependency testCompile project(':core').sourceSets.test.output actually means: "depend on classes generated by gradle build task". So if you open clean project where classes are not generated yet IDEA won't recognise them and reports error.
To fix this problem you have to add a dependency on test source files next to dependency on compiled classes.
// First dependency is for IDEA
testCompileOnly files { project(':core').sourceSets.test.java.srcDirs }
// Second is for Gradle
testCompile project(':core').sourceSets.test.output
You can observe dependencies recognised by IDEA in Module Settings -> Dependencies (test scope).
Btw. this is not nice solution so refactoring is worth considering. Gradle itself does have special subproject containing test-support classes only. See https://docs.gradle.org/current/userguide/test_kit.html
Update 2016-06-05
More I am thinking about proposed solution less I like it. There are few problems with it:
It creates two dependencies in IDEA. One points to test sources another to compiled classes. And it is crucial in which order these dependencies are recognised by IDEA. You can play with it by changing dependency order in Module settings -> Dependencies tab.
By declaring these dependencies you are unnecessarily polluting dependency structure.
So what's the better solution? In my opinion it's creating new custom source set and putting shared classes into it. Actually authors of Gradle project did it by creating testFixtures source set.
To do it you just have to:
Create source set and add necessary configurations. Check this script plugin used in Gradle project: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
Declare proper dependency in dependent project:
dependencies {
testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
}
Import Gradle project to IDEA and use the "create separate module per source set" option while importing.
New testJar based (trnsitive dependancies supported) solution available as gradle plugin:
https://github.com/hauner/gradle-plugins/tree/master/jartest
https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
From documentation
In case you have a multi-project gradle build you may have test
dependencies between sub-projects (which probably is a hint that your
projects are not well structured).
For example assume a project where the sub-project Project B depends
on Project A and B does not only have a compile dependency on A but
also a test dependency. To compile and run the tests of B we need some
test helper classes from A.
By default gradle does not create a jar artifact from the test build
output of a project.
This plugin adds a testArchives configuration (based on testCompile)
and a jarTest task to create a jar from the test source set (with the
classifier test added to name of the jar). We can then depend in B on
the testArchives configuration of A (which will also include the
transitive dependencies of A).
In A we would add the plugin to build.gradle:
apply plugin: 'com.github.hauner.jarTest'
In B we reference the
testArchives configuration like this:
dependencies {
...
testCompile project (path: ':ProjectA', configuration: 'testArchives')
}
The Fesler's solution haven't worked for me, when i tried it to build an android project (gradle 2.2.0).
So i had to reference required classes manually :
android {
sourceSets {
androidTest {
java.srcDir project(':A').file("src/androidTest/java")
}
test {
java.srcDir project(':A').file("src/test/java")
}
}
}
Here if you are using Kotlin DSL, you should create your task like that according to Gradle documentation.
Like some previous answer, you need to create a special configuration inside the project that will share its tests class, so that you don't mix test and main classes.
Simple steps
In project A you would need to add in your build.gradle.kts :
configurations {
create("test")
}
tasks.register<Jar>("testArchive") {
archiveBaseName.set("ProjectA-test")
from(project.the<SourceSetContainer>()["test"].output)
}
artifacts {
add("test", tasks["testArchive"])
}
Then in your project B in the dependencies, you will need to add in your build.gradle.kts:
dependencies {
implementation(project(":ProjectA"))
testImplementation(project(":ProjectA", "test"))
}
I'm so late to the party (it is now Gradle v4.4) but for anyone else who finds this:
Assuming:
~/allProjects
|
|-/ProjectA/module-a/src/test/java
|
|-/ProjectB/module-b/src/test/java
Go to the build.gradle of project B (the one that needs some test classes from A) and add the following:
sourceSets {
String sharedTestDir = "${projectDir}"+'/module-b/src/test/java'
test {
java.srcDir sharedTestDir
}
}
or (assuming your project is named ProjectB)
sourceSets {
String sharedTestDir = project(':ProjectB').file("module-b/src/test/java")
test {
java.srcDir sharedTestDir
}
}
Voila!
Creating test-jar For Gradle 6.6.x
I know that there are many sources telling you, that is not OK, fe:
https://github.com/gradle/gradle/issues/11280
https://gradle.org/whats-new/gradle-6/#better-builds
But this is so damn simple and I just don't like the idea of having common test classes separately in testFixtures folder.
So in module A:
task jarTests(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests {
extendsFrom testRuntime
}
}
artifacts {
tests jarTests
}
And in module B:
testImplementation project(':moduleA')
testImplementation project(path: ':moduleA', configuration: 'tests')
And it just works!
If you want to use artifact dependencies to have:
ProjectB's source classes depend on Project A's source classes
ProjectB's test classes depend on Project A's test classes
then ProjectB's dependencies section in build.gradle should look something like this:
dependencies {
compile("com.example:projecta:1.0.0")
testCompile("com.example:projecta:1.0.0:tests")
}
For this to work ProjectA needs to build a -tests jar and include it in the artifacts it produces.
ProjectA's build.gradle should contain configuration like this:
task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests
}
artifacts {
tests testsJar
archives testsJar
}
jar.finalizedBy(testsJar)
When ProjectA's artifacts are published to your artifactory they will include a -tests jar.
The testCompile in ProjectB's dependencies section will bring in the classes in the -tests jar.
If you want to includeFlat ProjectA's source and test classes in ProjectB for development purposes then the dependencies section in ProjectB's build.gradle would look like this:
dependencies {
compile project(':projecta')
testCompile project(path: ':projecta', configuration: 'tests')
}
If you have mock dependencies which you need to share between tests, you can create new project projectA-mock and then add it as test dependency to ProjectA and ProjectB:
dependencies {
testCompile project(':projectA-mock')
}
This is clear solution to share mock dependencies, but if you need to run tests from ProjectA in ProjectB use other solution.
The solution mentioned by Nikita for Android + Kotlin looks like this:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Gradle for project that is going to use dependencies:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
If you are struggling to adapt the solution to the Gradle Kotlin DSL this is the equivalent:
configurations {
register("testClasses") {
extendsFrom(testImplementation.get())
}
}
val testJar = tasks.register<Jar>("testJar") {
archiveClassifier.set("test")
from(sourceSets.test)
}
artifacts.add("testClasses", testJar)
Some of the other answers caused errors one way or another - Gradle did not detect test classes from other projects or Eclipse project had invalid dependencies when imported. If anyone has the same problem, I suggest going with:
testCompile project(':core')
testCompile files(project(':core').sourceSets.test.output.classesDir)
The first line forces the Eclipse to link the other project as dependency, so all sources are included and up to date. The second allows Gradle to actually see the sources, while not causing any invalid dependency errors like testCompile project(':core').sourceSets.test.output does.
in project B:
dependencies {
testCompile project(':projectA').sourceSets.test.output
}
Seems to work in 1.7-rc-2

Categories