I'm reading up on Gradle and am very interested in it, specifically because (it appears) that it allows the introduction of inheritance into the build process. For instance, if I have a Java web app that might be packaged and deployed to Google App Engine instances as well as Amazon EC2 instances, I need a sophisticated build that can take the same Java, XML, PROPERTIES, CSS and image files and package/deploy them into 2 drastically-differently packaged WAR files.
GAE apps are very specific as to how they are packaged; EC2 (pretty much) just require that you conform to servlet specs. GAE apps get "deployed" by running an update command from the appcfg.sh script that comes with your SDK; EC2 has their own way to deploy apps. The point is, they are very different packaging/deployment processes for both PaaS providers:
public abstract class PackageTask {
// ...
}
// Package my Eclipse project for deployment to GAE.
public class AppEnginePackageTask extends PackageTask {
// ...
}
// Package my Eclipse project for deployment to EC2 instances.
public class AmazonPackageTask extends PackageTask {
// ...
}
public abstract class DeployTask {
// ...
}
// Deployment to GAE.
public class AppEngineDeployTask extends DeployTask {
// ...
}
// Deployment to EC2.
public class AmazonDeployTask extends DeployTask {
// ...
}
Then, I might have a myapp.gradle buildfile that templates the build order of tasks:
clean()
compile()
package()
deploy()
...and somehow, I can configure/inject AppEnginePackageTask/AppEngineDeployTask in place of package()/deploy() for a GAE-based build, or I can configure/inject AmazonPackageTask/AmazoneDeployTask into those templated tasks. Again, I'm not sure how to do this (or even if Gradle can do this), but it's what I'm after.
My understanding was that Gradle can do this. Ant can also be forced to have highly-modular, elegant builds that work this way, but being XML-based, it takes some finessing, whereas an OOP-based language like Groovy makes this cleaner and simpler.
However, all the examples I see of Gradle tasks take the following form:
task package(dependsOn: 'compile') {
// ...
}
task deploy(dependsOn: 'package') {
// ...
}
So I ask: these look/feel like non-OOP task definitions. Is my understanding of Gradle (and its OOP nature) fundamentally incorrect? What am I missing here? How can I accomplish these notions of "configurable/injectable build templates" and inheritance-based tasks? Thanks in advance!
Edit I re-tagged this question with "groovy" because Gradle buildscripts are written in a Groovy DSL, and someone who happens to be a Groovy-guru (say that 5 times fast) might also be able to chime in even if they know little about Gradle.
As described here, there are simple tasks and enhanced tasks. The latter are much more flexible and powerful.
The following example isn't exactly what you describe, re:injection, but it illustrates OOP.
Here is the sample build.gradle file. It avoids "package" as that is a keyword in Java/Groovy. The 'build' target depends on 'compile' and some flavour of 'doPack', depending on a property called 'pkgTarget'.
task compile << {
println "compiling..."
}
task build() << {
}
build.dependsOn {
compile
}
build.dependsOn {
if (pkgTarget == "Amazon") {
task doPack(type: AmazonPackageTask)
} else if (pkgTarget == "Google") {
task doPack(type: GooglePackageTask)
} else {
task doPack(type: MyPackageTask)
}
}
where the tasks are defined later in the same file. (Per doc, this code can go into a "build src" directory):
// -----
class MyPackageTask extends DefaultTask {
def init() { println 'common stuff' }
#TaskAction
def doPackage() {
println 'hello from MyPackageTask'
}
}
class AmazonPackageTask extends MyPackageTask {
#TaskAction
def doPackage() {
init()
println 'hello from AmazonPackageTask'
}
}
class GooglePackageTask extends MyPackageTask {
#TaskAction
def doPackage() {
init()
println 'hello from GooglePackageTask'
}
}
and here is the gradle.properties file:
pkgTarget=Amazon
Related
I'm trying to deal with annotation processors. I followed tutorials. Here is my code:
#ExampleAnnotation
private void someMethod(){
System.out.println("hi");
}
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.METHOD)
public #interface ExampleAnnotation {
}
#SupportedAnnotationTypes("org.example.ExampleAnnotation")
public class Processor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> anots, RoundEnvironment roundEnvironment) {
anots.forEach(System.out::println);
return true;
}
}
I created META-INF/SERVICES/javax.annotation.processing.Processor
and registered my processor: org.example.Processor. It seems like everything is OK, but block of code in the processor just dont start. I have no idea what is wrong. P.S.: I use Gradle and Java 11.
i fixed my issue and decided to make a little step-by-step tutorial to create simple JAP:
Java-Annotation-Processor-guide
This is guide for annotation processing using gradle
I spent 3 days trying to deal with it, but with this little tutorial you will made JAP in 5 minutes.
sorry for bad english :)
So, first you should do is create gradle subproject for your project:
create project itself with gradle init or intellij
add to your main project's settings.gradle file this line include "*your subproject name, lets say:*annotation-processor"
Congrats, now let go to the processor itself
here is so much tutorials on the web about this part, so you can read something like this https://www.baeldung.com/java-annotation-processing-builde,
or this (Если ты русский): https://habr.com/ru/company/e-legion/blog/206208/
Final step is very easy - you will add your annotation processor to main project.
!!! instead of annotation-processor you should use your subproject name !!!
kotlin (or java + kotlin) -> {
add this plugin to your plugins: id "org.jetbrains.kotlin.kapt" version "1.7.21"
add this to your dependencies:
kapt project('annotation-processor')
compileOnly project('annotation-processor')
}
java -> {
add this to your dependencies:
annotationProcessor project('annotation-processor')
compileOnly project('annotation-processor')
}
(or you can read this on github: https://github.com/Blu3cr0ss/Java-Annotation-Processor-guide)
I'm writing a custom gradle plugin in which I want to have a bunch of common for several of my projects tasks and a sort of a 'main' task to control which of these tasks to turn on.
Regular tasks in the plugin are e.g.:
CopyDockerResourcesTask
CopyContainerFilesTask
PerformAnalysisTask
and the 'main' task is:
BaseProjectTask
so then in the project in build.gradle I'd like to be able to do this:
BaseProjectTask {
copyDockerResources = true
copyContainerFiles = true
performAnalysis = true
}
I want the default behaviour of the plugin to be to not to do anything, only add certain tasks if they are turned on in BaseProjectTask.
I wanted to achieve this with adding task dependency in #TaskAction method of BaseProjectTask:
class BaseProjectTask extends DefaultTask {
private final BaseProjectExtension extension
private final Project project
#Optional
#Input
Boolean copyContainerFiles = false
...
#Inject
BaseProjectTask(Project project, BaseProjectExtension extension) {
this.project = project
this.extension = extension
}
#TaskAction
def execute() {
if (copyContainerFiles) {
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
}
...
}
}
Creating task dependency, this line:
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
doesn't work.
Edit:
My current findings are that defining task dependency in #TaskAction is too late as this is execution phase. I could do it in the constructor (this way it works) but its too early as property copyContainerFiles isn't set yet.
Does anyone know a way of adding code in the task class that would be fired in the configuration phase? I think this is what I'm missing.
You need to configure task dependencies during the build configuration phase, as you surmised.
It's not possible to do it in the #TaskAction method. It's fundamental to the way Gradle works that it needs to know how tasks depend on each other before it starts executing the build. That allows Gradle to do some useful things, such as only executing the tasks that are not up to date, or working out what tasks will execute without actually executing them.
In general, tasks should not be aware of one another1.
When you are trying to do this in a plugin using values in a project extension, you must wait until after the project has evaluated so that the build script code executes first. You can do this with project.afterEvaluate()2.
So you can do the following (using Kotlin DSL3):
project.afterEvaluate {
tasks.register("baseTask") {
if (baseProjectExtension.copyDockerResources)
dependsOn(tasks.getByName("copyDockerResources"))
if (baseProjectExtension.copyContainerFiles)
dependsOn(tasks.getByName("copyContainerFiles"))
if (baseProjectExtension.performAnalysis)
dependsOn(tasks.getByName("performAnalysis"))
}
}
1See How to declare dependencies of a Gradle custom task?
2See https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:project_evaluation
3What I am familiar with. Hopefully not too much trouble to convert to Groovy.
I'm creating a Gradle plugin with its corresponding objects for use in the Groovy DSL. I'm confused between the difference and extension and a task and how configuration should be passed between the two along with where the input and out annotations should be put. Here's my task
abstract public class UrlVerify extends DefaultTask {
#Input
abstract public Property<String> getUrl();
#TaskAction
public void verify() {
System.out.println(getUrl().get().toString());
}
}
Here's the extension
abstract public class UrlVerifierExtension {
abstract public Property<String> getUrl();
abstract public Property<Configuration> getConfiguration();
abstract public Property<Boolean> getIgnoreFailures();
public Set<ConflictCategory> getIncludeCategories() {
return includeCategories;
}
}
This plugin simply accepts a URL and validates it.
verification {
url = 'https://www.moooooereee.com/'
configuration = configurations.runtimeClasspath
ignoreFailures = false
}
I have the following plugin. I manually needed to pass the URL from the extension to the task and wondered if this is the correct way?
public class UrlVerifierPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getPluginManager().apply(JavaLibraryPlugin.class);
UrlVerifierExtension extension = project.getExtensions().create("verification", UrlVerifierExtension.class);
UrlVerify verifyUrlTask = project.getTasks().create("verifyUrl", UrlVerify.class);
verifyUrlTask.getUrl().set(extension.getUrl());
}
}
Along with this, it is also unclear whether the #Input annotation belongs to the properties of the extension or the task?
You seem to have followed the examples from the Gradle documentation very precisely. This is the correct way to configure your custom tasks. The exact purpose of extensions is to have user-provided settings which are then consumed by your plugin to configure it and tasks.
Extensions are for the user to provide settings.
Tasks are for executing an action while Gradle is running.
The #Input annotation is used by Gradle to determine if the tasks needs to run. If the tasks has not run before, or if the input value has changed since the previous execution, then the tasks will run again.
Outputs declare some result produced by running the task. An example is a task that compiles Java files. The outputs would the the class files produced from the compilation process. If output files are modified or deleted by something other than the task that created them, then the task that created them is out-of-date, and Gradle will run it again.
Also, a task can declare the outputs of another task as its input. If task A creates some output files, and task B uses the outputs of task A as an input, then task B will be run when task A updates or creates its files.
In your case with the #Input annotation, my guess is that you do not want that in this case, because it tells Gradle that your tasks only needs to run once, then after that, only if the user updates the setting.
I have to run Sonarqube analysis for a Java project which is a subproject among 4 others, something like below:
Parent-project
-> serviceA
-> src/main/java/...
-> serviceB
-> src/main/java/...
-> serviceC
-> src/main/java/...
-> serviceD
-> src/main/java/...
-> gradle.properties
-> sonar.gradle
I know that I can exclude unwanted services as below:
project(":serviceB") {
sonarqube {
skipProject = true
}
}
However the problem is that I do not own the parent project and hence cannot make changes in the global sonar.gradle file as this is being shared other services as well.
Is there a way to have a sonar.gradle only for my project (serviceA), and a gradle task that runs analysis only for my service?
I am completely lost here because the documentation does not seem to tell anything about this.
Any help will be highly appreciated!
I found a solution to this problem. You can actually exclude the unwanted projects under the parent project using following setting in your sonar.gradle:
project(":project-1") {
sonarqube {
skipProject = true
}
}
project(":project-2") {
sonarqube {
skipProject = true
}
}
If you want even more control to a level that you don't want to exclude a whole sub-project but want to include only a part of the sub-project (for example a common shared library where multiple teams contribute and you want to analyze just your team's code), you can do it something like below:
project(":common-project") {
sonarqube {
properties {
property "sonar.inclusions", "src/main/java/**/common/**"
}
}
}
Say we are running mvn test.
I am wondering if there is a way to configure Maven to run some files before executing tests. In my case, I want to configure a library, but don't want to have to configure this library for every entrypoint in my app/tests. I am just looking to configure the lib for every mvn lifecycle hook which invokes a runtime.
Something like this:
#MavenRuntimeLifecycle
public class Whatever {
public void runtimeBegin(){
// right when the java process starts up
Mylib.configure("foo");
}
public void runtimeEnd(){
// right before the process shuts down
}
}
I assume this would be a Maven specific thing - not that it has to be in the same Java process as my server or tests etc.
Note that using Node.js, I would simply do it like so:
export class MyLib {
isConfigLoaded = false;
static loadConfig(){
// ...
}
static void run(){
if(!this.isConfigLoaded){
MyLib.loadConfig(require('../some/path/to/.mylib.config.js'));
this.isConfigLoaded = true;
}
this.doTheThing();
}
}
I could do the same thing with Java or Maven project, and just store a .java file in the resources directory. It's more manual, but it could be done.