Migrating from ant to gradle - macrodef alternatives? - java

We are migrating from Ant to Gradle. In the starting Gradle looked promising, when I started migrating targets from Ant to Gradle, facing similar issues, earlier having with Ant.
The Ant Build Flow looks like -
We have a Java application - Ant used to clean, compile and build jar of the project.
Next we have few targets to copy built jar and some other resources files to windows server.Its just a windows server not a nexus like repo.
These copy targets are release based(internally using macrodefs) - if alpha, it has to copy to different folder and for stable release different folder.
Another macrodef was to just backup the existing files present in server to a backup folder before copying new files there.
Migrating to Gradle-
I liked the way Gradle does the tasks of clean, compile and build. I am using Java plugin for this.
I am facing issues in next tasks to copy files to windows server and taking backup of old files before copying new files.
In Gradle I created few custom tasks like copyJarAlpha, copyJarStable,backupJarAlpha,backupJarStable etc..
Internally they are doing the same thing just copying files from source to destination and some print statements. But it looks repetitive. I was looking for ways like macrodef in ant.
task deployAlpha {
doFirst {
isJenkinsAvailable(deployDirAlpha)
}
doFirst {
if(isDeployLibsTaskRequired(outputDir,deployDirAlpha)) {
tasks.findByName('deployLibsAlpha').execute()
}
}
doLast {
println "Deploying corejar jar to : ${deployDirAlpha}"
copy {
from "${outputDir}/${corejar}"
into "${deployDirAlpha}"
}
println "Deploying evan-core jar to : ${deployDirAlpha}/lib"
copy {
from "${externalLibsDir}/core2.jar"
into "${deployDirAlpha}/lib"
}
println "Deploying test resource files to : ${deployDirAlpha}"
copy {
from "./test-resources/logback-test.xml", "./test-resources/JUnit-TestSuiteResults.xsl"
into "${deployDirAlpha}"
}
}
}
I have similar snippets at few places. Its not looking nice.
Is there any way we can reuse methods/functions in gradle? or I am assuming wrong things here.. Or I am trying to use gradle for things its not inteded for? What could be the best way to accomplish these tasks of copying and backing up files if not using custom tasks in gradle?

Here's a couple of options.
Create a plugin (See writing custom plugins)
public class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.with {
apply plugin: 'x'
dependencies {...}
task deployAlpha{...}
}
}
}
Usage
apply plugin: MyPlugin
Or a script plugin
apply from: 'path/to/script.gradle'
You can also attach functions to domain objects via the ext of a domain object (see ExtraPropertiesExtension)
allprojects {
ext.doProjectStuff = { String arg -> return "project${arg}"}
dependencies.ext.doSependenciesStuff = { String arg -> return "dependencies${arg}"}
}
Usage
def foo = doProjectStuff('x')
dependencies {
def bar = doDependenciesStuff('y')
}

Related

How to add OpenAPI client as a subproject?

I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.
So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}
and settings.gradle:
rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'
I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.
Eclipse then shows me a problem:
Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.
I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.
You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.
Simply change output directory to something else other than build/:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
Then include the project in your build with the correct syntax:
// settings.gradle
include("openapi-java-client")
However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' # line 23, column 35.
main.java.srcDirs = ['src/main\java']
This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:
plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.
Even if the plugin resides in the root project, the destination needs to be a module.
The point is, that the root project always executes, opposite to module configutions.
I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:
openApiGenerate {
// …
groupId = "com.example"
id = "api"
version = "1.0"
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your simple-java-app project.
./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …

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.

Is there any Gradle -Sqlj plugin available to do sqlj translation as we do using maven sqlj plugin?

I had converted my maven build project to gradle and now one of the plugins that we use in maven is SQLj plugin. The sqlj plugin has additional configuration that takes the source (.sqlj) files from specific folder path and then compiles them to .java and .ser specific target directory.
I am not sure on configuring this in the gradle build.
Can you please help me how to write this configurations in build.gradle file .
I just included the dependency line equivalent to gradle that i got from mvncentral.
// https://mvnrepository.com/artifact/org.codehaus.mojo/sqlj-maven-plugin
compile group: 'org.codehaus.mojo', name: 'sqlj-maven-plugin', version: '1.3'
but it just not compiling the new .sqlj from specific file location.
I had included the lines like
applyplugin: java and maven.
Let me know if any additional thing need to be done.
You could use the source code from the maven plugin as a guide to develop a Gradle task. Here's a simple starting point
class SqljTask extends DefaultTask {
#Input
String encoding
#Input
List<String> additionalArgs = []
#InputDirectory
File sqljDir
#OutputDirectory
File generatedJava
#OutputDirectory
File generatedResources
#TaskAction
void generate() {
project.mkdir generatedJava
project.mkdir generatedResources
project.fileTree(sqljDir).visit { FileVisitDetails fvd ->
if (!fvd.directory) {
List<String> sqljArgs = []
sqljArgs << "-dir=$generatedJava"
sqljArgs << "-d=$generatedResources"
sqljArgs << "-encoding=$encoding"
sqljArgs << "-compile=false"
sqljArgs << fvd.file.absolutePath
sqljArgs.addAll(additionalArgs)
int result = sqlj.tools.Sql.statusMain(sqljArgs as String[])
if (result != 0) throw new RuntimeException("Can't translate $fvd.file ($returnCode)"
}
}
}
}
Usage in a build.gradle
apply plugin: 'java'
task sqlj(type: SqljTask) {
encoding = 'UTF-8'
additionalArgs = ['-status']
sqljDir = file('src/main/sqlj')
generatedJava = file("$buildDir/sqlj/java")
generatedResources = file("$buildDir/sqlj/resources")
}
compileJava.dependsOn sqlj
sourceSets {
main {
java {
srcDir sqlj.generatedJava
}
resources {
srcDir sqlj.generatedResources
}
}
}
Note: This task will only run if a task input / output has changed since the last successful run. So it will be considered UP-TO-DATE if nothing has changed since the last build
Unfortunately I can't see an officially supported sqlj ant task, someone posted source for an ant task here so one option is to wrap that using Gradle's ant integration
Having a look at the source code for the Maven plugin it seems like most of the logic is in the translate method which calls sqlj.tools.Sqlj.statusMain(). I notice there's logic in the Maven task which only updates the files which have changed, this could be done in Gradle via an incremental task
Hope this helps you to get started

How to create a debian distro for a java project using gradle

I see someone has asked this question before. I would have loved to have seen the answer, but it was removed. At the risk of getting down-voted like that post..., I really need help with this, as I've spent a few days on it already and I'm thoroughly at a loss...
I have a java project that's fairly mature. We're preparing to go from alpha phase into a beta release. As a part of that, we want to release installable packages with a proper app with an icon, etc. Creating a (dmg) package for distribution on Mac was extremely easy using the macAppBundle gradle plugin and it works beautifully. I'm now attempting to address distribution on Linux. Ideally, the setupbuilder plugin would be the way to go, but there's a bug that's preventing me from creating a .deb or .rpm package. I submitted the bug to the developer and am currently trying to work around the issue by following this blog post.
I am running an Ubuntu 16.04.3 vm in VirtualBox on my Mac and I can successfully create a working executable by running gradle debianPrepareappname. But when I try to run gradle debian to create the .deb file, the build always fails (currently with this error:).
Process 'command 'debuild'' finished with non-zero exit value 255
When I run debuild manually, I see the following:
debuild: fatal error at line 679:
found debian/changelog in directory
/home/username/appname/build/debian/appname
but there's no debian/rules there! Are you in the source code tree?
No rules file is getting created by gradle. I know that the rules file is basically a makefile... and I'm not very familiar with makefiles in general, let alone creating .deb distros. I know makefiles do compilations and copy files to places in the system, but I don't know what needs to be done to create a .deb file or where things need to go. I mean, the necessary components are there and they work:
appname/build/debian/appname/debian/{bin,lib}
The bin has the working executable and the lib has all the necessary jar files. I just don't know what I need to do in the gradle build script to create the .deb file. Here's what I've got in the gradle build file (I've omitted the macAppBundle and setupbuilder stuff that's just vestigial in there right now, just to keep it simple):
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
defaultTasks "clean", "fatJar", "eclipse"
version = getVersionName()
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral()
}
dependencies {
compile 'com.miglayout:miglayout-swing:5.0'
compile 'com.googlecode.plist:dd-plist:1.3'
compile 'org.freehep:freehep-graphicsio:2.4'
compile 'org.freehep:freehep-graphicsio-pdf:2.4'
compile 'org.freehep:freehep-graphicsio-ps:2.4'
compile 'org.freehep:freehep-graphicsio-svg:2.4'
compile 'org.freehep:freehep-graphics2d:2.4'
compile 'org.swinglabs.swingx:swingx-autocomplete:1.6.5-1'
}
sourceSets {
main {
java {
srcDir 'src/main/java/'
}
}
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class':'com.placeholder.appname'
}
baseName = project.name + '-all'
from {configurations.compile.collect {it.isDirectory() ? it : zipTree(it)}}
with jar
}
def getVersionName() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
String applicationVersionFull = getVersionName()
task debianClean(type: Delete) {
delete 'build/debian'
}
tasks.addRule("Pattern: debianPrepare<distribution>") { String taskName ->
if (taskName.startsWith("debianPrepare")) {
task(taskName, dependsOn: [installDist, debianClean]){
String debianDistribution = (taskName - "debianPrepare").toLowerCase()
String debianApplicationVersionFull = getVersionName()
doLast {
copy {
from rootProject.files("build/install/appname")
into rootProject.file("build/debian/appname")
}
copy {
from rootProject.files("gradle/debian/debian")
into rootProject.file("build/debian/appname/debian")
}
}
}
}
}
task debian { // depends on debianPrepare*
doLast {
exec {
workingDir rootProject.file("build/debian/appname")
commandLine "debuild -i -us -uc -b".split()
}
}
}
Everything I've read says this is supposed to be really easy with gradle. The macAppBundle was definitely very easy - it was like 5 lines of code. I barely had to read anything to figure it out and it creates a dmg that has an executable with an icon and everything. I just copied & edited the example in the macAppBundle readme. setupbuilder looked similarly easy, if not for the bug I encountered. Is there a similar example out there for building .deb packages for java projects that doesn't use setupbuilder? I've tried a couple other plugins with no success. I've been googling and I can't find anything straightforward other than the blog post I mentioned. I eventually would like to apply an icon to the executable and other niceties, but first thing is to just get it to build. So why does the rules file not get created? That seems like a good place to start.
I think what you're missing is a "debian" directory with all the related files already present. If you look at syncany's repo https://github.com/syncany/syncany/tree/74c737d871d21dff5283edaac8c187a42c020b20/gradle/debian/debian on github from the blog post you mentioned, you'll see he has 8 files.
At the end of the day, debuild is just bundling a set of files up into an installer. They all have to be there to begin with. His scripts don't create any of these files, just modify some such as the changelog.

Gradle: multiple JARs without multiple projects

I have a library which is used to build a number of CLI tools using Gradle. Each CLI tool is a separate JAR. At the moment every tool requires a separate Gradle project, with an associated set of directories, like this:
Having all of this structure is resulting in the whole collection of tools becoming very unwieldy and difficult to work with. Is there any way to collect all of the different Mains into a single folder (suitably renamed) and configure Gradle to turn each one into a separate JAR?
FWIW, the JARs are currently created using https://github.com/johnrengelman/shadow . JAR size doesn't matter.
Thanks in advance.
Jars are just zip files with META-INF folder inside. Use Zip tasks to create them and dependsOn to run tasks as part of your build sequence.
I had the code like below for changing jar files:
task changeJar (type: Zip) {
baseName project.name
extension 'jar'
destinationDir new File('build')
entryCompression ZipEntryCompression.STORED
from { zipTree(new File(core.libsDir, core.name + '.jar')) }
from ( <somewhere else> ) {
exclude 'META-INF/'
}
}
I'm not sure if it's a good fit but you might be interested in my gradle-java-flavours plugin.
eg:
apply plugin: 'com.lazan.javaflavours'
javaFlavours {
flavour 'tool1'
flavour 'tool2'
}
dependencies {
compile 'a:a:1.0' // common to all tools
compileTool1 'b:b:2.0' // compile deps for tool1 only
runtimeTool2 'c:c:2.0' // runtime deps for tool2 only
}
Directories
src/main/java, src/test/java, src/main/resources, src/test/resources - common code & tests
src/tool1/java, src/testTool1/java, src/tool1/resources, src/testTool1/resources - tool1 only sources
src/tool2/java, src/testTool2/java, src/tool2/resources, src/testTool2/resources - tool2 only sources
Jars
projectName.jar
projectName-tool1.jar
projectName-tool2.jar

Categories

Resources