I'm building a Java command line application using gradle and have it running when I use gradlew run, however I would like to generate a jar -- which I would assume I would then have users download to invoke the CLI.
However, when I run gradlew jar, nothing is produced (build/lib dir doesn't even exist) even though the build runs with no errors and finishes with BUILD_SUCCESSFUL.
Two questions:
Why is no jar being produced?
Is having users download a jar the best way to ship a CLI for Java?
Below is my full build.gradle.kts
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
application
id("com.diffplug.spotless") version "6.12.0"
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Use JUnit test framework.
testImplementation("junit:junit:4.13.2")
// This dependency is used by the application.
implementation("com.google.guava:guava:30.1-jre")
implementation("info.picocli:picocli:4.7.0")
annotationProcessor("info.picocli:picocli-codegen:4.7.0")
implementation("io.vavr:vavr:0.10.4")
}
application {
// Define the main class for the application.
mainClass.set("testlauncher.command.Runner")
}
subprojects {
apply {
plugin("com.diffplug.spotless")
}
}
spotless {
java {
importOrder()
removeUnusedImports()
googleJavaFormat()
}
}
project.tasks.findByName("build")?.dependsOn(project.tasks.findByName("spotlessApply"))
I'm dumb.
I thought the jar would be in ./build/libs but it's actually in ./app/build/libs.
Related
We have lots of libraries for which we have the following snippets to configure the respective plugins. We would want to avoid code duplication and rather want to pull these definitions from a base repository which can be shared across all projects. How should this be configured?
checkstyle {
showViolations = false
ignoreFailures = true
..
}
pmd {
..
}
license {
..
}
spotless {
..
}
artifactory {
..
}
With an easy approach you can import build script plugins into other build scripts even if they are located in a different location / different repository. The only requirement that I think is necessary that on the machine where you will build the project at the end you have access to those repositories.
Imagine you have GitHub repository that contains main configuration.
https://github.com/user/shared-gradle-config/blob/master/scriptPlugin.gradle
That contains:
checkstyle {
showViolations = false
ignoreFailures = true
..
}
pmd {
..
}
...
Now be sure that you use raw resources in other scripts. Every repository service will have different kind of link. Below example for GitHub:
https://raw.githubusercontent.com/user/shared-gradle-config/master/scriptPlugin.gradle
At the beginning of the Gradle build script for the actual projects you have to include application of the remote script plugin:
// Application of shared remote Gradle script plguin with common configuration
// With this line all the checkStyle, PMD configuration and whatever you have declared in the script will be applied to current project
apply from: "https://raw.githubusercontent.com/user/shared-gradle-config/master/scriptPlugin.gradle"
// Whatever custom logic below
wrapper {
version = "7.0.0"
distributionType = Wrapper.DistributionType.ALL
}
...
I made a prototype project miself as I tried to understand how to share build configuration this way. Maybe it can help you, https://github.com/rivancic/gradle/tree/master/script-plugin. Note its not in final version, still improving it..
I am writing a custom Gradle plugin in Java and it seems to work as I expect when I call explicitly the task it creates.
Now, the task is highly related to tests, so I would like it to execute automatically when the tests are executed (ideally, before the tests actually), but I am having 2 issues. Below is my plugin :
public class MyCustomGradlePlugin implements Plugin<Project> {
public void apply(Project project) {
MyCustomGradleConfig myCustomGradleConfig = project.getExtensions().create("myCustomGradleExtension", MyCustomGradleConfig.class, project);
Task myCustomTask=project.getTasks().create("checkRules", MyCustomRulesTask.class, myCustomGradleConfig);
archUnitTask.setGroup("verification");
project.getTasks().findByName("test").dependsOn(myCustomTask);
}
}
I build and deploy locally this plugin. When I declare and configure it in the build.gradle at the root of a multi-module project :
if I forget to apply java plugin in the project, then the ´test´ task doesn't exist and myCustomTask can't be "attached" to it : is there a way to make sure the java plugin is declared when MyCustomGradlePlugin executes ?
´myCustomTask´executes, but only at the root of the project, where there's nothing interesting : if the project is a multi-module project, I need it to execute for all the modules. Ideally, I would like that the plugin takes care of it, to simplify to the maximum the config in the projects that will use it. How do I achieve that ?
is there a way to make sure the java plugin is declared when MyCustomGradlePlugin executes ?
Yes. You can either apply the Java plugin in your own plugin like this:
project.getPlugins().apply(JavaPlugin.class)
Or you can conditionally add your task if the Java plugin exists like this:
project.getPlugins().withType(JavaPlugin.class) {
MyCustomGradleConfig myCustomGradleConfig = ...
}
if theproject is amulti-module project, I need it to execute for all the modules.
You can achieve that by simply applying your plugin to each individual sub-project, e.g. through the allprojects method:
allprojects {
apply plugin: "your.plugin.id"
myCustomGradleExtension {
// ...
}
}
Alternatively, you can also wrap all your plugin code in an allprojects block. In that case, from the user perspective, they apply your own plugin to the root and it will configure itself for all sub-projects.
I have a gradle project that contains a suit of tests, that can be launched with a command:
gradlew clean :mymodule:test
I need to build a jar, that can be executed in the same way and launch tests. I found instruction for creating a jar of test binaries here Creating a Jar of test binaries - Gradle.
task packageTests(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
}
However the resulting jar contains only test classes and resources, no dependent libraries or main application source code.
How can I create an executable test jar?
If I were you, I wouldn't use classifier='tests' as that is usually reserved for a jar containing test classes without dependencies. Possibly uber-tests or fat-tests is better as uber-jar and fat-jar are common names given to this type of jar. You could do something like
task uberTestJar(type: Jar) {
dependsOn testClasses
classifier = 'uber-tests'
sourceSets.main.output.each {
from it
}
sourceSets.test.output.each {
from it
}
configurations.testRuntime.each { File f ->
if (f.name.endsWith('.jar')) {
from zipTree(f)
} else {
from f
}
}
}
There's also the shadow jar plugin
Currently, my build.gradle has a dependency on an external library built with Ant. To accomplish building the library, I followed the advice here and created a task which builds the external library, and copies it to the libs/ folder.
The task is called as part of a dependency:
build.gradle
dependencies {
compile fileTree('libs') {
include '*.jar'
builtBy 'myTask'
}
}
task myTask (type: GradleBuild) { GradleBuild antBuild ->
antBuild.buildFile('external-stub.gradle')
antBuild.tasks = ['clean', 'ivy.check', 'ivy.download', 'ivy.task', 'ivy',
'init', 'mergeCode', 'compile', 'jar', 'copyJarsToProject']
}
However, when the compile actually runs, the library I just built and copied in is not included in the dependencies, as evidenced by a whole lot of compilation errors.
Am I including the library the wrong way?
The full build.gradle and associated files are over at Github, and I've linked to the specific commit I'm having issues with: Source Repository
Alright, took me a while to get a build I was happy with. But, here's what was changed.
The actual build of the JAR was built using the same style, but moved to the external project (so that the main build project wasn't reaching across to it). I'll give an in-depth explanation below, but the commits are here and here. These are in order.
Basically, we export the jar as an artifact that other projects can depend on, rather than copying over the Jar ourselves. This way, the Ant build runs and other projects can see the Jar we just created. This is the end of the first commit. In the second commit, the task outputs are marked as needing to be regenerated only if the Jar does not exist. This was due to the fact that whenever I tried to build the app, it would take minutes to regen the Jar, and then have to repackage everything else as well. The code is below:
build.gradle External Project
configurations {
buildJSword
}
task doBuildJSword (type: GradleBuild) {
buildFile = 'jsword-stub.gradle'
tasks = ['clean', 'ivy.check', 'ivy.download', 'ivy.task', 'ivy',
'init', 'mergeCode', 'compile', 'jar'] //, 'copyJarsToMinimalBible']
ext.outputJar = file('distribution/jsword.jar')
outputs.upToDateWhen {
ext.outputJar.exists()
}
}
artifacts {
buildJSword(doBuildJSword.ext.outputJar) {
builtBy doBuildJSword
}
}
Then, the main project just has to add this project as a compile-time dependency:
build.gradle Main Project
compile project(path: ':jsword-minimalbible', configuration: 'buildJSword')
Hope this is helpful for anyone with a similar issue, let me know if you have questions!
Note: The build currently does not clean itself properly, so if you change any code in the external project, you need to delete the external Jar for everything to regenerate itself correctly.
I have a gradle project with multiple packages. After the build, each package generates its jar files in build/libs. The external jar dependencies are pulled into ~/.gradle. I would now like to run the service locally from the commandline with the appropriate classpath. For this purpose, I am writing a script that constructs the classpath. The problem is that the script does not understand all the external dependencies and hence cannot construct the classpath. Is there a way for gradle to help with this? Ideally, I would like to dump all the dependencies into a folder at the end of the build.
Firstly, i would suggest using the application plugin if you can, since it takes care of this already.
If you want to dump the classpath to a file yourself, the simplest way is something like:
task writeClasspath << {
buildDir.mkdirs()
new File(buildDir, "classpath.txt").text = configurations.runtime.asPath + "\n"
}
If you want to actually copy all the libraries on the classpath into a directory, you can do:
task copyDependencies(type: Copy) {
from configurations.runtime
into new File(buildDir, "dependencies")
}
You could try something like this in your build script:
// add an action to the build task that creates a startup shell script
build << {
File script = file('start.sh')
script.withPrintWriter {
it.println '#!/bin/sh'
it.println "java -cp ${getRuntimeClasspath()} com.example.Main \"\$#\""
}
// make it executable
ant.chmod(file: script.absolutePath, perm: 'u+x')
}
String getRuntimeClasspath() {
sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':')
}