Gradle task not running as demanded (before compiling) - java

INTRODUCTION
The project is in Kotlin and builds using Gradle. I'm trying to generate a basic data class with some build info, let's say for now that I need it [re]generated every time before running.
Here's the Gradle task I have now:
def generatedDir = "$buildDir/generated"
// noinspection GroovyAssignabilityCheck
task generateBuildInfo {
inputs.property "version", rootProject.version.toString()
inputs.property "name", rootProject.name.toString()
outputs.dir generatedDir
outputs.upToDateWhen { false }
doFirst {
def buildInfoFile = file("$generatedDir/BuildInfo.kt")
buildInfoFile.parentFile.mkdirs()
buildInfoFile.text = """
internal data class BuildInfo(
val version: String = "${project.version.toString() ?: "unspecified"}",
val name: String = "${project.name.toString() ?: "unspecified"}"
)
""".replace(" ", "").trim()
}
}
To be able to resolve this from IntelliJ IDEA, I added my new folder to project sources, and obviously wired up the dependencies, like so:
sourceSets.main.kotlin.srcDirs += generatedDir
project.afterEvaluate {
compileJava.dependsOn generateBuildInfo
compileKotlin.dependsOn generateBuildInfo
}
This is all done in a separate file (to avoid polluting my main scripts). Due to this organization, after applying plugins, I just include the generator in my main script, like this:
apply from: "gradle/scripts/build-info-generator.gradle"
THE PROBLEM
It looks like the generator code is executed only once, after running assemble when I first ran clean on this module. This is not what I want, because when I change some of the project properties (like version), the source does not get updated... as if compileJava/compileKotlin and my custom task are not executed.
They do not appear in build logs as executed.
Is there any way I can run this task every time I want to run my module's launcher? Sure, I can do some smart file comparison to see if generation is needed, but for now I just want it done each time. Am I missing something?

IDEA has its own build system, indepenant from Gradle.
You can configure it to run a Gradle task before its own build task.
You can also configure it to delegate all the build/run tasks to Gradle. But that's not the default.

Related

Fail gradle build if file does not exist [duplicate]

I am starting to use Gradle for an Android application. I would like the build to fail if the developer fails to create a file at a specific location such as ./src/res/values/specialfile.xml
A bit of searching led me to believe that a .doFirst would work
android.doFirst {
assert file("./src/res/values/specialfile.txt").exists()
}
However, Gradle says "unsupported Gradle DSL method found: 'doFirst()'!"
What can I do to assert a file's existence?
doFirst only exists on tasks object. android is not a task.
If would want this test to always be done even if the developer doesn't try to build (for example when running the tasks task), you should simply put in your build.gradle
assert file("./src/res/values/specialfile.txt").exists()
However this is really not recommended as this would be executed even for non build tasks, or even when the model is built for IDE integration.
There is a task called preBuild that is executed before anything in the android build, so you can hook your test to it, either through another task or through doFirst:
preBuild.doFirst {
assert file("./src/res/values/specialfile.txt").exists()
}
One may want to check in a module build.gradle ...
below the plugins configuration block
above the android configuration block
eg. in order to determine which Gradle plugins to apply:
plugins {
id "com.android.application"
}
def json_google = "src/google-services.json";
if (project.file(json_google).exists()) {
println "found: ${project.file(json_google)}"
apply plugin: "com.google.gms.google-services"
apply plugin: "com.google.firebase.crashlytics"
...
} else {
def message = "missing: ${project.file(json_google)}"
// throw new GradleException(message)
println message
}
android { ... }
rootProject.file() and project.file() might be more convenient than file() (it depends). Using assert seems a bit harsh - but then one has to take care that it will not crash elsewhere.
My projects usually have a few optional config files, which the environment may or may not provide, because not every product flavor may require the same set of plugins & config files.

gradle: tar task not creating a tar.gz

Hi I have a tar task that I made after looking at numerous methods and some SO posts.
task buildDist(type: Tar, dependsOn: jar) {
print 'here'
archiveName = 'xyz-' + version
destinationDir = file('build/dist')
extension = 'tar.gz'
compression = Compression.GZIP
from 'build/libs'
include 'xyz.jar'
}
buildDist.mustRunAfter jar
I have the java plugin applied and the jar task makes the xyz.jar file available under build/libs. The build/dist directory does not exist yet, but I tried new File("build/dist") as well. That did not work either - I even pointed it to the build directory that exists - doesn't work. I run the entire script with /gradlew clean build. The print in the above code does print.
I am making a few assumptions here as you didn't post the output from running Gradle.
The build task is just a normal Gradle task that doesn't do anything by itself. Instead, it depends on other tasks. If you create your own custom task and you like to have it included when executing build, you have to add a dependency to it. If this is not the problem and you have actually done this, please give some more details as to what makes it "not work" when you run build.
If you want to test your task in isolation (e.g. to make sure it works correctly without running unit tests or whatever else that is unrelated), just run gradlew cleanBuildDist buildDist.
A note about the 'print' statement - it executes doing the configuration phase, but this doesn't mean you can use it to test if the task actually executes. In fact, it will most likely print no matter what task you execute. If you wanted to print something on execution time, you would have to put it in a doLast block.
There is a few other things you should change as well:
It is not a good practice to use relative references. Instead, use the buildDir property to get an absolute reference to the build directory.
Don't use deprecated methods like archiveName and destinationDir. Use archiveFileName and destinationDirectory instead.
The extension property is also deprecated, but it is ignored if you set the full name of the archive yourself. So just remove it. This also means you are missing the extension on the full name.
The from and include is a little fragile. Just use from jar.archivePath if you only want to gzip your application jar.
Example:
task buildDist(type: Tar, dependsOn: jar) {
archiveFileName = "${jar.baseName}-${version}.tar.gz"
destinationDirectory = file("$buildDir/dist")
compression = Compression.GZIP
from jar.archivePath
}
build.dependsOn buildDist
Lastly, if your intention is to create a distribution of your application that is runnable on its own (with all required dependencies), you should consider using the distribution plugin and perhaps also the application plugin.

Recreation of jpa metamodels

I want to use JPA meta-models in my project. I added required dependency to my project, added generation to JavaCompile task and meta-models are successfully generated. If I want to run the code again, It doesn't compile. It fails with:
Error:java: Problem with Filer: Attempt to recreate a file for type project.models.AdministrationUser_
for every single meta-model. I am running it in Idea as spring boot run. If I use gradle task boot run than it will run just fine, no problem, but I need Idea run, because I need to set active profiles. It also shows, that problem is probably not in code but somewhere in run task configuration but I have no idea what to change and I tried to change several things but I'm just firing blanks.
I'm using Gradle 5.4.1., Idea 2019.2 and Java 11.
Here are important parts of my build.gradle file:
dependencies {
annotationProcessor("javax.xml.bind:jaxb-api")
annotationProcessor("org.hibernate:hibernate-jpamodelgen")
}
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Something similar was already asked here but one answer suggest to delete hibernate-jpamodelgen what (if I understand it correctly) seems like absurd solution - because it wont work if you delete it. Other answer suggest using some maven plugin, so not an option for me either.
I'm stuck on this problem for a longer time, have no one to talk to about it and I'm completely out of ideas so I'm pretty desperate and any help will be much appreciated.
First, make modifications to your build.gradle.
You point to the wrong directory in the sourceSets section. Fix it:
sourceSets {
generated {
java {
srcDirs = ['src/generated']
}
}
}
Then make further improvements:
With Gradle 5.2 and Intellij 2019.1 annotationProcessors is the only piece of configuration you need. See https://github.com/tbroyer/gradle-apt-plugin/blob/v0.21/README.md
Get rid of
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Lastly, consider delegating your build and test tasks to Gradle (this is now the default). Go to File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using Gradle

How to change or replace version of gradle using task in java project?

I have a gradle project and I want to UPDATE its version by building a task so that when ever i run that task it UPDATE the version number in gradle.properties file automatically:
THIS:
ServiceVer=1.0.0 ServiceSnapshot=-SNAPSHOT
Should change like:
ServiceVer=2.0.0 ServiceSnapshot=-SNAPSHOT
what task do i need to write or any basic and easy example(link) to understand?
i am new to gradle.
i want to have all these changes in a java project not in an android project.
You could try something like this to update the properties file
task versionPlusPlus() << {
Properties props = new Properties()
props.load(new File('gradle.properties').newDataInputStream())
versionString = props.getProperty('ServiceVer')
// mutate versionString to whatever you want
props.setProperty('ServiceVer', versionString)
props.store(propsFile.newWriter(), null)
}
I haven't tested this, but you may want to be careful about calling this task and running a build in the same gradle invocation (for instance gradle versionPlusPlus build, the new versionString may not take effect; run gradle versionPlusPlus and then gradle build separately after gradle.properties is updated.
For the updated version to take effect within the same gradle invocation set project.version=ServiceVar after changing the version string.

How can I import one Gradle script into another?

I have a complex Gradle script that wraps up a load of functionality around building and deploying a number of NetBeans projects to a number of environments.
The script works very well, but in essence it is all configured through half a dozen maps holding project and environment information.
I want to abstract the tasks away into another file, so that I can simply define my maps in a simple build file, and import the tasks from the other file. In this way, I can use the same core tasks for a number of projects and configure those projects with a simple set of maps.
Can anyone tell me how I can import one Gradle file into another, in a similar manner to Ant's task? I've trawled Gradle's docs to no avail so far.
Additional Info
After Tom's response below, I thought I'd try and clarify exactly what I mean.
Basically I have a Gradle script which runs a number of subprojects. However, the subprojects are all NetBeans projects, and come with their own ant build scripts, so I have tasks in Gradle to call each of these.
My problem is that I have some configuration at the top of the file, such as:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
I then generate tasks such as:
projects.each({
task "checkout_$it.shortname" << {
// Code to for example check module out from cvs using config from 'it'.
}
})
I have many of these sort of task generation snippets, and all of them are generic - they entirely depend on the config in the projects list.
So what I want is a way to put this in a separate script and import it in the following sort of way:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
import("tasks.gradle") // This will import and run the script so that all tasks are generated for the projects given above.
So, in this example, tasks.gradle will have all the generic task generation code in, and will get run for the projects defined in the main build.gradle file. In this way, tasks.gradle is a file that can be used by all large projects that consist of a number of sub-projects with NetBeans ant build files.
There is a new feature in 0.9. You can use apply from: 'other.gradle' command.
Read my question about same thing at: Is there a way to split/factor out common parts of Gradle build
The answer to the question turned out to be in the Plugins system, where you can add the desired functionality in a set of plugins which can be groovy files located in the directory buildSrc/src/main/groovy. Plugins can also be bundled as a Jar though I haven't tried this.
Details here: Custom Plugins
Well, it is hard to tell what serves you best without actually seeing your build file.
I could assume that stetting up your environment as multi-project build should provide you the abstraction you are looking for.
In your project root build.gradle you define all your domain specific stuff as well as the things that apply to all your subprojects:
repositories {
add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
name = 'destRepo'
addIvyPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/ivys/ivy(-[revision]).xml')
addArtifactPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/[type]s/[artifact](-[revision]).[ext]')
descriptor = 'optional'
checkmodified = true
}
...
}
...
subprojects {
sourceCompatibility = 1.5
targetCompatibility = 1.5
group = 'my.group'
version = '1.0'
uploadArchives {
uploadDescriptor = true
repositories {
add rootProject.repositories.destRepo
}
}
apply{ type my.group.gradle.api.plugins.MyPlugin }
...
}
dependsOnChildren()
The project root directory might also contain a gradle.properties file where you define properties used by your projects:
buildDirName=staging
repo.dest.dir=/var/repo
...
Then in an additional file from your project root named settings.gradle you actually point to your subprojects:
include 'my-first-component',
'my-second-component'
...
project(':my-first-component').projectDir = new File(rootDir, 'path/to/first/component')
project(':my-second-component').projectDir = new File(rootDir, 'path/to/second/component')
...
Each sub-project directory contains a build.gradle file containing the sub-project specific stuff only.
No matter if you invoke gradle from your project root or sub-project directory, gradle will automatically consider all your definitions done in the various files.
Also note that no compile task will be executed for your project root as long as you don't load any plugin beyond the default plugin at the root level.
This is an example for Kotlin DSL (build.gradle.kts).
apply(from = "scripts/my-script.gradle.kts")
scripts/my-script.gradle.kts:
println(
"""
I am defined at the top level of the script and
executed at the configuration phase of build process
"""
)
tasks.create("MyTask") {
println(
"""
I am defined in a task and
run at the configration phase of build process"""
)
doLast {
// ...
}
}
See this answer and this answer for how to import a function from another script in Kotlin DSL.
Based off this similar question/answer, the easiest solution I've found after searching for days is using buildscript.sourceFile. It correctly gives the file being run rather than the pwd/cwd/parent-file of said process. I feel like this would solve your issue.

Categories