Idiomatic Gradle script for creating "debug" and "release" JAR files

I am trying to create a Gradle build script construct that will build a Java .jar file in release or debug mode, and I am having problems parameterizing the script.

Question: What is the idiomatic way to do this in a Gradle script using a Java plugin? (or, if there is no idiomatic way, what really harmful solution works?)

I am not opposed to the parameterization method, as long as command line calls and IDEs can easily choose between two output parameters. The jar file will be used as a library in other projects, for example. Android app and JavaFX app, so I would like the parameterization method to be invokable / dependend-on from their own Gradle scripts.

Ideally, I would like to "imitate" the capabilities of the Android Gradle plugin to have a Debug / Release version for each task, i.e.

 $ ./gradlew build $ ./gradlew assembleRelease $ ./gradlew checkDebug 

but it would not be possible to use even the top level buildDebug and buildRelease.


The material I tried

This section is not relevant.

starting point

I have the following Gradle file / project:

 group 'TestGradleProjectGroup' apply plugin: 'java' sourceCompatibility = 1.8 version '1.0-release' compileJava { options.debug = false } repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' } 

This works fine and creates a jar file:

 $ ls TestGradleModule/build/libs/ TestGradleModule-1.0-release.jar 

which, when tested classes are checked using javap , does not contain debugging information. Hooray. No, we need a way to make a debug version.

Add debug and release tasks

 version '1.0-release' compileJava { options.debug = false } task buildRelease(type: GradleBuild, dependsOn: build) { project.version = '1.0-release' compileJava { options.debug = false } } task buildDebug(type: GradleBuild, dependsOn: build) { project.version = '1.0-debug' compileJava { options.debug = true } } 

This did not work, since the debugging project is always built, even if buildRelease is specified on the command line. I assume that this is because the code for both tasks runs during configuration ( Gradle build lifecycle ), while I only want it to run. So I probably want to run them at runtime?

Add some doLast tasks

 version '1.0-release' compileJava { options.debug = false } task buildRelease(type: GradleBuild, dependsOn: build) { doLast { project.version = '1.0-release' compileJava { options.debug = false } } } task buildDebug(type: GradleBuild, dependsOn: build) { doLast { project.version = '1.0-debug' compileJava { options.debug = true } } } 

This is even worse. The output file is always 1.0-release, and this is because of the "default" at the top level. If I comment on this, then the jar version will not be created, instead the default TestGradleModule.jar will be performed. It seems that the contents of the doLast blocks are completely useless in their effect on the compileJava task (but there are no warnings about this?). I assume that these changes are too late for execution at runtime or that there is something else that needs to be done so that compileJava tasks are "configured" differently?

Using configurations?

I noticed the manual for the Java plugin contained a link to buildConfigName and uploadConfigName and claimed that they depend on "Tasks that produce artifacts in the ConfigName configuration." Given that I could not parameterize the material during setup, the plugin's ability to do this looked promising:

 group 'TestGradleProjectGroup' apply plugin: 'java' sourceCompatibility = 1.8 configurations { debug release } version '1.0-release' compileJava { options.debug = false } repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' } 

but

  • This did not add buildRelease or buildDebug to the output ./gradlew tasks --all as I expected. But I could do these tasks.
  • It seemed that only buildRelease and buildDebug , and not, for example, assembleRelease , etc.
  • buildRelease didn't seem to depend on anything when it started, so it had no useful effect.

Iterate tasks?

As a last attempt, I tried to create all the relevant tasks and link the dependencies for everything. I tried iterating over tasks and adding dependencies:

 gradle.taskGraph.whenReady { taskGraph -> taskGraph.allTasks.each { taskIter -> println("iterating" + taskIter) def releaseTask = project.task(taskIter.name + "Release") def debugTask = project.task(taskIter.name + "Debug") taskIter.dependsOn += [releaseTask, debugTask].toSet() println("new taskIter.dependsOn:" + taskIter.dependsOn) /* set debug mode here, copy over effects of task to debug/release disable effects of task */ } } 

but

  • This did not seem to create the tasks properly, they were not accessible from the command line, and the “build” was not run by “buildRelease”, etc.
  • I would also have to “move” all “actions” from current, existing tasks to debug and release tasks, to avoid duplicating the effects of each of them. And I don’t know how to do it.

In short, I have no idea what I'm doing. As a last resort, I could just create all the tasks manually, but it seems to make the use of the java plugin and very spam senseless?

+7
java jar release-management gradle
source share
4 answers

After many hours of searching ...

The “Configuring with DAG” section of the Gradle Guide to the Basics of Build Scripts describes how to do this, i.e.

https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#configure-by-dag

Quotation from the textbook

"Gradle has a configuration phase and a execution phase. After the configuration phase, Gradle knows all the tasks that must be completed. Gradle suggests that you use this information as a trap. The use case for this is to check the release of a task that is among the tasks that must be completed. . Depending on this, you can assign different values ​​to some variables. "

Here is an example of using gradle.taskGraph.whenReady{} using buildRelease() and buildDebug() ...

 gradle.taskGraph.whenReady { taskGraph -> if (taskGraph.hasTask(buildRelease)) { compileJava.options.debug = false project.version = '1.0-release' } else if (taskGraph.hasTask(buildDebug)) { compileJava.options.debug = true project.version = '1.0-debug' } } task buildRelease(type: GradleBuild, dependsOn: build) { } task buildDebug(type: GradleBuild, dependsOn: build) { } 

IMO, Gradle is just a bear to learn. I'm sure I miss the good old.

+4
source share

Do I need to create both banks in one pass? If not, it could be as simple as using some sort of optional argument for gradle, such as

 compileJava { options.debug = project.hasProperty('debugBuild') } gradle assemble -PdebugBuild 

See the documentation here: https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_properties_and_system_properties

It's also not that you can get the best help on the forums.

+2
source share

Your question is described in detail since you have deeply analyzed the problem. Although I believe that this issue can be resolved if you would have thought it easier.
Therefore, if we consider it simple, a possible solution for your business is to start the debugging task after the release is completed. See the example below:

 compileJava { options.debug = false } task buildRelease(type: GradleBuild, dependsOn: build) { doLast { project.version = '1.0-release' compileJava { options.debug = false } } } task buildDebug(type: GradleBuild, dependsOn: build) { doLast { project.version = '1.0-debug' compileJava { options.debug = true } } } // Here you are telling the buildDebug task to be executed right after the // buildRelease task is finalized. buildDebug.mustRunAfter buildRelease 

Thus, you control the order of the task without introducing an explicit relationship between them.

+1
source share

Not sure, but maybe my gradle-java-flavors might help here. For example:

 plugins { id "com.lazan.javaflavours" version "1.2" } javaFlavours { flavour 'debug' flavour 'release' } debugJar { version = "${project.version}-debug" } releaseJar { version = "${project.version}-release" } 

Each taste gets its own Jar and JavaCompile tasks, etc., and you can also specify your own sources, resources, tests, dependencies for each taste.

See here for a more detailed overview of tasks, directories, and configurations and here for a test that covers functionality.

This approach is different from your GradleBuild style, since the “fragrances” can happily live together in the same project, which creates several artifacts, rather than starting the project twice with two different parameters.

+1
source share

All Articles