How to make gradle download dependencies without actually building things - java

On a new environment gradle build takes quite a while because all dependencies have to be downloaded.
Is there a way to only download dependencies in order to speed up the following build?
That way we could for example already prefill a CI build environment.

Edit: Updated for Gradle 6+.
Some notes:
This new approach downloads jars into a folder, and then deletes the folder. So the result of having the jars in the Gradle cache is a side-effect.
It currently uses jars configured for the main source-set but could be generalized.
Even though it is neither efficient nor elegant, it can be useful if you actually want the jars (and transitive dependencies): simply comment-out the deletion of the runtime folder.
This solution can be handy when you want the jars (and transitive dependencies), as you simply have to comment-out deleting the folder.
Consider this build.gradle (as an arbitrary, concrete example):
apply plugin: 'java'
dependencies {
implementation 'org.apache.commons:commons-io:1.3.2'
implementation 'org.kie.modules:org-apache-commons-lang3:6.2.0.Beta2'
}
repositories {
jcenter()
}
task getDeps(type: Copy) {
from sourceSets.main.runtimeClasspath
into 'runtime/'
doFirst {
ant.delete(dir: 'runtime')
ant.mkdir(dir: 'runtime')
}
doLast {
ant.delete(dir: 'runtime')
}
}
Example run:
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
$ gradle getDeps
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
/Users/measter/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/1.3.2/[snip]/commons-io-1.3.2.jar

I've found ./gradlew dependencies (as suggested by this user) to be very handy for Docker builds.

You can create a custom task that resolves all the configurations( in doing so, it will also download the dependencies without building the project)
task downloadDependencies {
doLast {
configurations.findAll{it.canBeResolved}.each{it.resolve()}
}
}
Run command ./gradlew downloadDependencies

My answer will favor the gradle plugins and built-in tasks.
I would use "gradle assemble" in the command-line.
It is a minor version of "gradle build".
This way, you may reduce the time of your preparations before running or building anything.
Check the link bellow for the documentation:
https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks
In general, what is my recipe when I clone a new repository:
-gradle assemble
-do some coding
-gradle run (and basically test until done)
-gradle build (to make distributable files)
note: this last step may have adicional configurations for .jar files as outputs (depends on you).

Related

How to run a Gradle task on a compiled jar without having its sources?

I have a compiled jar with JUnit tests that I want to run from a docker container.
I want to do it with a Gradle task.
First, I will compile the jar and copy it with all its dependencies to a Gradle-based image.
(Or I can create a fat jar which will contain all the third party compiled to a .class).
Then I want to run the task - this task will only run tests according to a test name, JUnit tag, etc.
Is it possible to run a Gradle task on a compiled jar without having its sources?
What should I include in this image beside the gradle.build file for it to work?
Thank you
Gradle fetch transitive dependencies and run its tests in test process by default, so you can use this feature in your case.
Note that abstract classes are not executed. In addition, be aware that Gradle scans up the inheritance tree into jar files on the test classpath. So if those JARs contain test classes, they will also be run.
Gradle tests detection document.
Here is how to do it in your case :
Create an empty Gradle project and apply the java plugin.
import the test dependencies tools with the needed scopes in the dependencies section.
import myApp.jar as a local dependency.
Configure the test task (add needed properties and args).
Run Gradle test with the specific properties.
build.gradle example :
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
// just examples change with needed unit tests dependencies
testImplementation('org.junit.jupiter:junit-jupiter-api:5.4.2')
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
// the target jar file
compile file("/myApp.jar")
}
project.ext.testName = project.hasProperty("testName") ?
project.property("testName") : "*"
test {
useJUnitPlatform()
filter {
//include specific method in any of the tests
includeTestsMatching "$testName"
}
}
Now the Gradle test command will run the target tests in myApp.jar.
For more information about it check the Testing in Java & JVM projects official Gradle documents

Compile a groovy script with all it's dependencies which are managed by gradle and then run it as a standalone application via the command line

I have a simple groovy script with a single java library dependency:
package com.mrhacki.myApp
import me.tongfei.progressbar.ProgressBar
class Loading {
static void main(String[] arguments) {
List list = ["file1", "file2", "file3"]
for (String x : ProgressBar.wrap(list, "TaskName")) {
println(x)
}
}
}
I'm using gradle to manage the dependencies of the project. The gradle configuration for the project is pretty straightforward too:
plugins {
id 'groovy'
}
group 'com.mrhacki'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
compile 'me.tongfei:progressbar:0.7.2'
}
If I run the script from the Intellij IDE, the script is executed as expected.
What I would like to do now is to compile the script with this dependency into one single .jar file, so I can distribute it as such, and run the application from any filesystem path, as the script logic will be dependent on the path from which the execution was called.
I've tried with a few gradle fat jars examples out there but none have worked for me since the .jar file is constantly throwing Could not find or load main class Loading when I try it to run.
If anyone would be so kind to give a hint or to show an example of a gradle task that would do a build that fits my described needs I would be very gratefull.
I'm aware of the groovy module Grape with the #Grab annotation too, but I would leave that as a last resort since I don't want the users to wait for the dependencies download, and would like to bundle them with the app.
I'm using groovy 2.5.6 and gradle 4.10 for the project
Thanks
You can simply create the fat-jar yourself, without any extra plugin, using the jar Task. For a simple/small project like yours, it should be straightforward :
jar {
manifest {
// required attribute "Main-Class"
attributes "Main-Class": "com.mrhacki.myApp.Loading"
}
// collect (and unzip) dependencies into the fat jar
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
EDIT : pleas take other comments into consideration: if you have more that one external lib you might have issues with this solution, so you should go for a solution with "shadow" plugins in this case.

Maven-publish gradle plugin skips the version

I have gradle multi-module project configured with kotlin-script. I'd like to add publishing to maven repository and I found maven-publish plugin for it. But it seems to skip the version configured for each project:
MyProject/build.gradle.kts:
subprojects {
apply {
plugin("maven-publish")
}
configure<PublishingExtension>() {
publications {
repositories { ... }
create<MavenPublication>("myPublication") {
from(components.getByName("java"))
logger.lifecycle("test: ${project.group} ${project.name} ${project.version}")
}
}
MyProject/subproject1/build.gradle.kts:
version = "1.0.0-SNAPSHOT"
gradle publish output:
test: my.project subproject1 unspecified
artifact file does not exist: '.../MyProject/subproject1/build/libs/subproject1.jar'
File subproject1.jar doesn't exist, but subproject1-1.0.0-SNAPSHOT.jar does. How to make gradle get the correct version of module?
I found a similar problem while using the maven-publish plugin:
I was trying to set the repository URL depending on project version as described in the gradle docs here and this answer.
But I found the version always resolved to (as in the question) as the default (un-set) value: unspecified.
So I guess those documentation examples are for a project's build.gradle and not a general gradle script.
Anyway, I believe the problem is due to the timing of the execution of the blocks in the gradle script. The project.version could not be accessed where I wanted it. So I ended up passing a parameter to the gradlew command with the -Pparameter flag.
Gradle has a configuration and then an execution stage.
Refer to documentation:
https://docs.gradle.org/current/userguide/build_lifecycle.html
https://www.oreilly.com/library/view/gradle-beyond-the/9781449373801/ch03.html
and an apparently similar problem, https://discuss.gradle.org/t/maven-publication-closure-is-evaluated-too-early/19911
About your problem, it may be the same as I have described, or perhaps the reason is simpler:
Looking at the structure of your gradle file, it does not appear to match the hierarchy specified in the maven-publish documentation. In particular, repositories {} block should be at the same level as publications {}, not inside of it.
Possibly related:
Gradle maven publish plugin config has reference to dynamically created gradle task
Gradle shouldRunAfter not available for a task

Using Gradle with native dependencies

I am trying to use Sigar in a Gradle project. Sigar distribution is by default provided with 2 types of files:
a JAR that contains classes
some native files (.so, dylib, .dll)
My purpose is to repackage these files so that I can use them as dependencies deployed and downloaded on-demand from a personal Maven repository.
My first try was to define dependencies as files in order to check that my application is working as expected before to repackage. Below is the Gradle code I used for my first test that works:
dependencies {
compile files("${rootDir}/lib/sigar/sigar.jar")
runtime fileTree(dir: "${rootDir}/lib/sigar/", exclude: "*.jar")
}
Then, I have repackaged Sigar native files into a JAR and renamed the other one to match rules for maven artifacts since I want to deploy them in a Maven repository. Below is what I get:
sigar-1.6.4.jar (contains .class files)
sigar-1.6.4-native.jar (contains .dylib, .so, and .dll files at the root)
The next step was to deploy these files in my custom repository. Then, I have updated my build.gradle as follows:
dependencies {
compile 'sigar:sigar:1.6.4'
runtime 'sigar:sigar:1.6.4:native'
}
Unfortunately, when I do a gradle clean build, new dependencies are fetched but native libraries can no longer be found at runtime since now I get the following exception:
Error thrown in postRegister method: rethrowing <java.lang.UnsatisfiedLinkError: org.hyperic.sigar.Sigar.getCpuInfoList()[Lorg/hyperic/sigar/CpuInfo;>
Consequently, I am looking for a solution to fetch and to link native files to my Java app like for other dependencies. Any advice, comment, suggestion, help, solution, etc. are welcome ;)
A solution is to define a new gradle configuration that unzips JAR files at the desired location:
project.ext.set('nativeLibsDir', "$buildDir/libs/natives")
configurations {
nativeBundle
}
dependencies {
nativeBundle 'sigar:sigar:1.6.4:native'
}
task extractNativeBundle(type: Sync) {
from {
configurations.nativeBundle.collect { zipTree(it) }
}
into file(project.nativeLibsDir)
}
dist.dependsOn extractNativeBundle
Then, this location must be put in java.library.path for tasks that depend on native libraries:
systemProperty "java.library.path", project.nativeLibsDir

Gradle: how to copy subproject jar to another subproject when task is run?

I have the following multiproject structure in Gradle:
- root-project
- acceptance-tests
- sub-java-proj
acceptance-tests has a task cucumber (provided by Gradle Cucumber-plugin) which runs the Cucumber features in the project. However, I need to do some bootstrapping before the features are run. In particular, before thecucumber task Gradle should do the following:
Run tests in sub-java-proj and if they pass produce its jar file (if not fail)
Copy the jar file from 1) into the directory accentance-tests/proglib
Run the Cucumber features like normal
Delete the jar file copied in 2)
I'm completely new to Gradle so I am not sure how to properly implement this. My first thought was to make acceptance-tests depend on sub-java-proj (with testRuntime config), but I feel this is an abuse of the dependency mechanism: the only reason to do this is to ensure sub-java-proj is compiled before acceptance-tests is run, but in reality this also messes with the classpath of acceptance-tests by adding the jar from sub-java-proj and there's also problems with transitive dependencies. In short: acceptance-tests should not have anything to do with sub-java-proj besides copying it's jar around.
Suggestions on how to accomplish this are very much appreciated.
Update
So far I have the followingin acceptance-tests/build.gradle:
task fix(dependsOn: ':sub-java-proj:jar', type: Copy) {
from tasks.getByPath(':sub-java-proj:jar')
to 'proglib'
}
task unfix(type: Delete) {
delete fix
}
cucumber.dependsOn fix
cucumber.finalizedBy unfix
However, this works except that unfix deletes the proglib directory too. It should only delete the jar file. Any ideas how to fix this?

Categories

Resources