I'm using Gradle to build my software. However, I find the output it proceduces a bit to minimal. I don't want to use --debug or --info, since that logging is much to verbose. I just want to know what the result in terms of artifacts (zip, jar, dmg, etc) of the Gradle buid is. For example, when I run 'gradle jar', I'd like to print where the jar is created.
I did that using:
jar {
doLast {
println "Jar has been created in ${archivePath}"
}
}
And it nicely prints that the jar has been created in the build/lib directory. However, when I run 'gradle distZip', the artifact is not created in the lib dir, but in the distributions directory. The above however is still printed, but I'd rather not have that: when I run the distZip, I'd like to know where I can find the output of that command, not of every step the distZip depends on.
Never mind, the following will work just nicely:
def artifacts = []
addListener(new TaskExecutionListener() {
void afterExecute(Task task, TaskState state) {
if(task in AbstractArchiveTask) {
artifacts << task.outputs.files.singleFile
}
}
void beforeExecute(Task task) { }
})
addBuildListener(new BuildAdapter() {
void buildFinished(BuildResult result) {
if(artifacts) {
println "\nOutput location: ${artifacts.last()}\n"
}
}
})
This is also available as a gist here.
Related
I've created a custom Gradle task in Java (just put within buildSrc, so a local custom task)
public class ImageMergerTask extends DefaultTask{
public File directory;
#TaskAction
public void greet() {
System.out.println("hello from ImageMergerTask " + directory.toString());
}
#InputDirectory
public File getDirectory(){
return directory;
}
public void setDirectory(File directory){
this.directory = directory;
}
}
Used in the build.gradle like
task imageMerger(type : ImageMergerTask) {
directory file('src/main/resources/someFolder')
}
processResources.dependsOn(imageMerger);
My impression is that by annotating getDirectory() with #InputDirectory, it ought to only run this task if the contents of src/main/resources/someFolder changes, but it seems to run every time, always including in the console:
> Task :imageMerger
hello from ImageMergerTask C:\Users\richa\Documents\Development\starlight\src\main\resources\someFolder
How should a custom task declare its inputs to properly take advantage of up-to-date detection?
It seems that gradle doesn't make the up-to-date detection unless there is also an output directory. Adding this to the task made it correctly only run the task when the contents of the input directory changed
#OutputDirectory
public File getOutDirectory(){
return new File(getProject().getBuildDir(), "someFolder");
}
As found by #ThomasKläger the Gradle documentation confirms this
Also note that incremental build won’t work unless a task has at least one task output
My project root directory is:
D:/Project/Node_Project
I am using a gradle plugin to install nodejs temporarily in my project root directory so that some nodejs command can run in the project while the thoject builds. The plugin is as below:
plugins {
id "com.github.node-gradle.node" version "2.2.4"
}
node {
download = true
version = "10.10.0"
distBaseUrl = 'https://nodejs.org/dist'
workDir = file("${project.buildDir}/nodejs")
}
So, nodejs is getting installed inside the project in the location:
D:/Project/Node_Project/build/nodejs/node-v10.10.0-win-x64
Now, I am using a .execute(String[] "path to set at environment variable", String path of file to be executed which is in the project root directory) method to run a windows command with node dependency. Code below:
cmd = "node connect.js"
def process = cmd.execute(["PATH=${project.projectDir}/build/nodejs/node-v10.10.0-win-x64"],null)
In the above .execute method, is there a way to auto-populate the "build/nodejs/node-v10.10.0-win-x64" part of the string instead of hardcoding it into the method?
Something like:
def process = cmd.execute(["PATH=${project.projectDir}/.*"],null)
Syntax of .execute method:
https://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/String.html#execute(java.lang.String[],%20java.io.File)
All the codes are inside "build.gradle" file. Please help!
I asked why you don't just write a task of type NodeTask, but I understand that you like to run a it in the background, which you can't do with that.
You could list the content of a directory and use that as part of the command. But you could also just grab it from the extension provided by the plugin.
This is not documented and it might break in future releases of the plugin, but you can do something like this (Groovy DSL):
task connectJS {
dependsOn nodeSetup
doFirst {
def connectProcess = "$node.variant.nodeExec $projectDir/src/js/connect.js".execute()
// Blocking readers (if async, pipe to a log file instead)
connectProcess.in.eachLine { logger.info(it) }
connectProcess.err.eachLine { logger.err(it) }
}
}
Since there is no Gradle plugin for axis2 (a wsdl code generator), I called an Ant task in a custom Gradle task.
As of now ./gradlew build generates the code, and ./gradlew clean deletes it. Also, the code is only generated if changes in the input file(s) or in the output directory are detected.
The only problem I'm having is that the code is not generated automatically when the project is imported into an IDE.
How do I need to change the build.gradle.kts below in order to have the IDEs (currently IntelliJ, but I would also like support for Eclipse) generate the code on import?
plugins {
id("base") // needed for delete
}
val axis2 by configurations.creating
dependencies {
axis2("org.apache.axis2:axis2-ant-plugin:$axis2Version")
axis2("org.apache.axis2:axis2-xmlbeans:$axis2Version")
}
val wsdl2Java by tasks.registering {
group = "build"
description = "Creates Java classes and resources from WSDL schema."
inputs.files(fileTree("$projectDir/src/main/resources/wsdl"))
outputs.dir("$projectDir/generated/")
doLast {
ant.withGroovyBuilder {
"echo"("message" to "Generating Classes from WSDL!")
"taskdef"("name" to "codegen", "classname" to "org.apache.axis2.tool.ant.AntCodegenTask", "classpath" to axis2.asPath)
"codegen"(
"wsdlfilename" to "$projectDir/src/main/resources/wsdl/MP12N-H-HOST-WEB-SOAP.wsdl",
"output" to "$projectDir/generated/",
"targetSourceFolderLocation" to "src/main/java",
"targetResourcesFolderLocation" to "src/main/resources",
"packageName" to "de.hanel.com.jws.main",
"databindingName" to "xmlbeans")
}
}
}
val deleteGenerated by tasks.registering(Delete::class) {
delete("$projectDir/generated/")
}
tasks {
compileJava {
dependsOn(wsdl2Java)
}
clean {
dependsOn(deleteGenerated)
}
}
java {
sourceSets["main"].java {
srcDir("generated/src/main/java")
}
sourceSets["main"].resources {
srcDir("generated/src/main/resources")
}
}
You can mark any task or run configuration to be activated before/after Gradle import or IDE make:
I have a working solution now. Both Eclipse and IntelliJ generate the source code on import.
First we add the IDE-specific plugins.
apply {
plugin("idea")
plugin("eclipse")
}
Then we get the corresponding IDE tasks and add our own task, that was defined in val wsdl2Java, as dependency
// find by name (in tasks container), since a module is also called 'idea'
project.tasks.findByName("idea")?.dependsOn(wsdl2Java)
project.tasks.findByName("eclipse")?.dependsOn(wsdl2Java)
The only problem is that apparently Eclipse can't handle
java {
sourceSets["main"].java {
srcDir("generated/src/main/java")
}
sourceSets["main"].resources {
srcDir("generated/src/main/resources")
}
}
But that's a different question.
UPDATE
The code block below tells Eclipse to include the generated sources
eclipse {
classpath {
plusConfigurations.add(configurations.findByName("compile"))
}
}
and this tells IntelliJ to mark the generated, and already included, sources as generated
idea {
module {
generatedSourceDirs.add(file("generated/src/main/java"))
}
}
I have a Gradle build that has some dependencies of the form
compile files('path/to/local/lib.jar')
(the build is being migrated - eventually these will be replaced)
The build failed because one of these paths was incorrectly specified. But it failed due to a compile error - it looked like Gradle silently ignored the missing dependency.
Is there a simple option or switch that will force Gradle to fail the build if any dependency (particularly local file dependencies) cannot be resolved (eg., file missing)?
Edit: to clarify further:
If a dependency cannot be found in the configured repositories, Gradle will fail the build when attempting to resolve them, as expected.
BUT - if a dependency is defined as "compile files ....", and the file specified does not exist at build time, Gradle will IGNORE that error, and attempt compilation anyway. That seems spectacularly wrong-headed and inconsistent default behaviour.
My question is - is there a Gradle option or switch or environment variable or system property that I can set to force Gradle to verify that file dependencies exist? (E.g,, behave in a sane and rational way?)
This is a bit of an old thread, but given that none of the currently proposed solutions actually works, and the solution appears to be trivial (collating two of them), I am leaving it here for future reference.
The point here is that we simply want to ensure that the files do exist, so we can just use the exists() method of the File class:
task ensureDepsExist() {
doLast {
configurations.implementation.canBeResolved(true)
Set<File> impFiles = configurations.implementation.resolve()
impFiles.forEach { f ->
if (!f.exists()) {
ant.fail "${f} could not be found"
}
}
}
}
compileJava.dependsOn ensureDepsExist
The canBeResolved() call is required, or Gradle will complain that configurations dependencies cannot be resolved.
Here's how you can check transitive dependencies using Gradle 7.3 (example: Fail if the project depends on log4j directly or transitively).
Kotlin DSL
configurations {
all {
relsolutionStrategy {
eachDependency {
if (requested.name == "log4j") {
throw RuntimeException("Project depends on log4j")
}
}
}
}
}
Groovy DSL
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.name == 'log4j') {
throw new RuntimeException("Project depends on log4j")
}
}
}
You could do something as shown below. It is not a built-in Gradle function but does not require code to check each dependency specifically (it checks all in the compile configuration):
apply plugin: 'java'
dependencies {
compile files('lib/abc.jar')
compile files('lib/def.jar')
}
task checkDependencies() {
doLast {
configurations.compile.each { file ->
assert file.exists()
}
}
}
compileJava.dependsOn checkDependencies
To fail the build you can:
ant.fail('message why it failed')
Then you can craft a condition then fail the build with nice message ;)
I would suggest to create a task that will bring the file to the project first with a condition to check if the file is available etc if not then throw a Gradle exception and fail the build with a message, and execute the task first in the execution phase.
I have no chance to test it now but it could be something like this, correct me if any syntax is wrong - but you should get the idea.
def yourDep = $/\path\to\your\depdendency/$
task bringDeps << {
if (yourDep.exists()){
copy {
from yourDep
into $projectDir/depsOrSmthg
}
} else{
ant.fail('message why it failed')
}
}
task ensureDependenciesExist() {
doLast {
configurations.implementation.canBeResolved(true)
DependencySet deps = configurations.implementation.getDependencies()
Set<File> impFiles = configurations.implementation.resolve()
deps.each { d ->
boolean depWasResolved = impFiles.any { impFile -> impFile.name.find(".*${d.name}.*${d.version}") }
if (!depWasResolved) {
println "${d} was not resolved"
assert depWasResolved
}
}
}
}
compileJava.dependsOn ensureDependenciesExist
We have around 80 jars in our applications. All are created using javac task and jar task in ant.
I would like to introduce findbug checks. One option was to create single findbug check ant project. This has all jars , all source paths defined in it. This works -- require lot of space. Analysis of result too not very straight forward. There are thousands of warnings to start with.
One option I am considering is to run ant with special listener on javac task ant , extract source and class location, call findbug task with source and class file information. Any other way introduce findbug to a large project.
tweaked taskFinished()... Fine for my usage.
public class JavacListener implements BuildListener
public void taskFinished(BuildEvent be) {
if ( be.getTask() instanceof UnknownElement ) {
UnknownElement ue= (UnknownElement) be.getTask();
ue.maybeConfigure();
if ( ue.getTask() instanceof Javac ) {
Javac task = (Javac)ue.getTask();
final Path sourcepath = task.getSrcdir();
FindBugsTask fbtask = new FindBugsTask();
System.out.println ("Trying FindBugs");
fbtask.setSourcePath(sourcepath);
fbtask.setAuxClasspath(task.getClasspath());
Path destPath = new Path( task.getProject() );
destPath.setPath(task.getDestdir().getAbsolutePath());
fbtask.setAuxAnalyzepath(destPath);
fbtask.setOutputFile(getFileName(task.getProject()));
fbtask.setProject(task.getProject());
fbtask.setHome(new File("C:\\apps\\findbugs-1.3.0"));
fbtask.execute();
}
} else {
System.out.println(be.getTask().getClass().getName());
System.out.println(be.getTask().getTaskName());
}
}
..