File dependency Gradle - java

I am a gmake user transitioning to Gradle. I have a multi-project structure, where one sub-project is a Java project and the other a home-brewed language. The home-brewed language does not use any Gradle plugins. Now I want to add a task that runs a Java program to generate XML when any of my home-brewed source files have been modified. In make, I would just declare a dependency on inputFile.mine or *.mine next to the target name, but I could not easily find how to do this basic thing with Gradle. Currently, I force the task to always execute using the potentially ugly work-around below. I want to replace this with some dependsOn *.mine . The Gradle user guide has a whole chapter dedicated to explaining different ways of specifying files, but I did not see how to declare a dependency.
task generateXML(type: Exec) {
generateXML.getOutputs().upToDateWhen({false}) // Force it to execute always
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
Thanks for helping a newbie out.

You can define task inputs and outputs in Gradle.
For example:
task generateXML(type: Exec) {
inputs.file ("src/inputFile.mine")
executable("java.exe")
args("-jar", "resources/generateXml.jar", "src/inputFile.mine")
}
See https://docs.gradle.org/current/userguide/more_about_tasks.html and https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html for more information.
Side note: When you run your build with -i, Gradle will tell you what has happened during the up-to-date check.

Related

Design Question - Gradle plugin for release packaging?

gradle newbie here,
I have began to implement a small gradle plugin. The goal of the plugin is to create a release zip for projects, with the intention of using across a couple of different projects.
Each project has a yaml file with a zipping structure loosely as such;
---
zip:
task:
- name:
zip:
task:
- name:
- name:
- name:
zip denotes that a given working directory should be zipped at this point
task denotes the name of a gradle task that should be executed preparing a particular directory in preparation of being zipped
The best pro that comes to mind is, with this approach I can reuse gradle tasks between projects and if needed include custom gradle tasks at a per project level and reference them here in the yml. Using a yaml also assists with creating release zips making them easy to build and read..
I have a manager gradle task that is called at runtime from cli ./gradlew myReleaseZipTask ( I think I achieve this by making it an exec task and putting the logic into the exec action). This manager task is responsible for parsing the yaml file into DTOs. Traversing from the lowest task I traverse up the graph executing each gradle task as defined preparing files into a specified directory and then once all tasks are completed zip the current workspace. Continue up the graph
Im halfway through my implementation and Im a little bit worried im abusing gradle in a way that its not intended. Some of my main concerns are;
I have one task that is orchestrating many other gradle tasks, I either find or create a task on the fly, then retrieve its actions and execute them from inside my manager task, this isnt something I see often in gradle examples. Everyone seems to have very static linear task dependencies and dont "reflectively" create or find tasks to execute..
When dealing with a task entry from the yml my manager task tries to lookup the gradle task by name, if it doesnt exist, it tries to create the task. Given a task is created/found I set the properties from the YmlTaskDTO onto the task and then call the task to execute. Will I run into issues with reusing the same tasks with different properties? For example if I have something like;
zip:
task:
- name: taskA
argA: A
argB: B
- name: taskA
argA: A
argB: C
Could anyone give me some feedback on my design and if its inline with correct standards of gradle use? Please raise any concerns you may have!
I think you have realised some of the answers your self.
The one guidance here is that 'plugins' are to create a new functionalities, that are not available in native gradle.
What you are doing here is composition of existing tasks in a specific order for your use case. Plugin for this is an overkill.
What you are looking for can be achieved with simple tasks and task chaining.

How do I make an alias for a Gradle task?

As a part of a TDD workflow, I want to be able to check if my Java codebase compiles, but not if the tests pass.
Currently, if I run gradle build it runs the compile tasks (for source and tests) and then also executes the test task (and returns a non-zero exit code since the tests fail).
So I find that I have to run gradle build -x test to exclude the test task, and get a successful zero exit code.
What do I add to my build.gradle to define a new task, say compile that is an alias for build x test?
So far I have this, but it doesn't seem like dependsOn takes any arguments to customize the build task I want to execute:
task compile {
dependsOn build
}
I've been reading the docs here, I see different kinds of dependency chaining mechanisms, but not to disable/exclude a particular task. How does the -x flag even work then? I assumed there would be a way to control it programmatically too.
Thanks to Bjørn Vester's answer and reading the docs, I have implemented my task as follows:
task compile {
dependsOn classes
dependsOn testClasses
}
There are lots of different tasks you can run individually. For instance:
gradle classes: Will compile your "main" code.
gradle testClasses: Will compile your "main" code as well as test code.
gradle jar: Will compile your "main" code and assemble it into a jar.
None of the above will run your unit tests. On the other hand, the build task depends on all of the above, as well as the test task and more.
In general, if you like to run a particular set of tasks, you do that by defining a new task and then make dependencies to those other tasks you like to run with it. You tried that already, but instead of build you should have used something like compileJava or classes whatever other tasks you need. But always check if there isn't one already that satisfies your needs, like there are in this case. You can read about what tasks are available in Java projects in the documentation for the Gradle java plugin.

In Buildship: how can I substitute a Gradle project with a built jar?

I would like to be a able to get Eclipse to ignore one Gradle project, and instead use a pre-built version of it.
Background
I have a project "parser" written in Scala, and a dozen others written in Java. The weakest link in my tool-set is Scala IDE. I use this plugin to edit & compile Scala code, but unfortunately it breaks the Java (JDT) tooling quite badly in mixed-language projects*.
Specifically: Call-hierarchy is missing results, searches crash and so on. Also Scala IDE appears to have lost funding and the issues sound fairly fundamental, so I'm not holding my breath for these issues to be fixed.
With Maven (m2e) I had a workaround I was quite happy with:
Build as a .jar put into my local .m2 repository:
cd parser; mvn install
In Eclipse, close the "parser" project
"Like magic", m2e simply picked up the most recent 'installed' .jar and used it in place of the closed project.
An awesome answer would be how to get Gradle to do that!
However all I wish for is any solution that meets these...
Requirements
That I can open Project parser when necessary (which is seldom),
to edit and build changes via the Gradle command-line.
I will close it when done.
Other projects use the built .jar from my local .m2 repo.
(It's fine if they always do so.)
The change must not affect others who don't use Eclipse
(ideally) the change can be used by other Eclipse users
Approaches
A similar question had this good answer by #lance-java with a number of general suggestions. I think I can rule out these ideas:
composite build support / multiple repos. Other team members wouldn't think it makes sense to build this project separately, as it is quite closely integrated with the others.
dependency substitution rules - doesn't appear to meet requirement 3.
Something along the lines of lance-java's idea #4 sounds viable. Paraphrasing...
"use the eclipse plugin [in conjunction with] Buildship, e.g. using the whenMerged hook to tweak the generated .classpath [of all the Java projects]."
UPDATE: [18 Apr]: I had hit a brick wall in this approach. Buildship was not putting the built .jar onto the runtime classpath. (UPDATE 2: Now resolved - see my answer.)
Questions
The main question: How can I structure a solution to this, that will actually work & avoid any major pitfalls?
Note that the project itself has a few dependencies, specifically:
dependencies {
compile 'org.scala-lang:scala-library:2.12.4'
compileOnly 'com.google.code.findbugs:jsr305:1.3.9'
antlr 'org.antlr:antlr4:4.5.3'
}
So a sub-question may be: How to pull these in into the other projects without duplicating the definition? (If that doesn't work automatically.)
So the solution was a bit involved. After adding 'maven-publish' to create the library, I then implemented the following to force Eclipse to use the prebuilt library:
subprojects {
// Additional configuration to manipulate the Eclipse classpaths
configurations {
parserSubstitution
}
dependencies {
parserSubstitution module("com.example:parser:${project.version}")
}
apply plugin: 'eclipse'
eclipse {
classpath {
plusConfigurations += [ configurations.pseLangSubstitution ]
file {
whenMerged { cp ->
// Get Gradle to add the depedency upon
// parser-xxx.jar via 'plusConfigurations' above.
// Then this here if we have a dependency on Project(':parser')
// - If so, remove it (completing the project -> jar substitution).
// - If not, remove the .jar dependency: it wasn't needed.
def usesParser = entries.removeAll {
it instanceof ProjectDependency && it.path.startsWith('/parser')
}
def parserJar =
cp.entries.find { it instanceof Library && it.path.contains('parser-') }
if (usesParser) {
// This trick stops Buildship deleting it from the runtime classpath
parserJar ?. entryAttributes ?. remove("gradle_used_by_scope")
} else {
cp.entries.remove { parserJar }
}
}
}
}
So there are 2 parts to this:
Using 'plusConfigurations' felt a bit round-about. I ended up doing this because I could not see how to construct class Library classpath entries directly. However it could well be that this is required to implement the 'transient dependencies' correctly anyway. (See the end of the question.)
The trick to stop Buildship removing the .jar from the runtime classpath (thus deviating from a Gradle command-line launch) was provided to me by a Gradle developer in this discussion.
Usage
The solution works just as I hoped. Every time some code in this library is modified, I execute the following task of mine on the command line (which also does some other code & resource generation steps, in addition to building the parser jar):
./gradlew generateEclipse
Then in Eclipse I press keyboard shortcuts for "Gradle -> Refresh Gradle Projects", Build.
And harmony is restored. :-)
Navigating to the (prebuilt) source of parser works.
If I need to edit the source, I can open the parser project and edit it. Scala-IDE still does a good job for this.
When I'm done I execute the command, close the project and my Java tools are happy.
In parser project
You shoud use the maven-publish plugin with the publishToMavenLocal task
apply plugin: 'maven-publish'
group = 'your.company'
version = '1.0.0'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
pom.withXml {
def root = asNode()
root.appendNode('name', 'Your parser project name')
root.appendNode('description', 'Your parser project description')
}
}
}
}
Everytime you make a modification, just change the version number if necessary and go with gradle publishToMavenLocal
In other java project using parser
Just use parser as a regular dependency :
repositories {
mavenLocal()
...
}
compile 'your.company:parser:1.0.0'
If my understanding of your situation is good, it should do the trick.

create gradle task in java based plugin

I have a java based gradle plugin that does some common configurations for our gradle projects. Now I want to add tasks like (currently present in our build.gradle for each project)
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
What I need is a hint on how to do this in a java based gradle plugin.
In another plugin I already register tasks using project.getTasks().create("myTask", MyTask.class); where MyTask extends AbstractTask and has a #TaskAction method to do its duty. But I'm found no way to adapt this to work with what i want to do with the sample above.
I tried to google for a solution but until now i did not find a helpfull soution as everything i find is using groovy or something similar to my snipped above directly in the build.gradle.
Thanks in advance!
Looks like I found a solution, at least for the javadoc task...
Task javadocTask = project.getTasks().getByName("javadoc");
project.getTasks().create("javadocJar", Jar.class, task -> {
task.dependsOn(javadocTask);
task.setClassifier("javadoc");
task.from(javadocTask);
});
this seems to work... but now i have an issue with the next gradle task:
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
until now i haven't found a way to get a hand on the sourceSets varialbe in Java.
Thanks in advance! :)
response to asettoufs comment:
The docs you have linked are related to projects with sub projects. What I have are multiple single projects that are not related. For those we have a plugin that already applies some plugins and configures them. so the projects have a common configuration by just applying our plugin.
Our plugin is written in java. And should continue to be java. Now we want to move some more common build config stuff to the plugin - for example some of those basic tasks as above.
To get your the source set you can ask the convention
project.getTasks().create("sourceJar", Jar.class, task -> {
task.setClassifier("sources");
// grab the convention that holds the sourceSets
JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class);
// find our source set
SourceSet mainSourceSet = javaConvention.sourceSets.findByName('main');
// user `from` like normal
task.from(mainSourceSet.getAllSource());
});
Some source code to see how the JavaPlugin creates the sourceSet written in Java
Creating the java convention
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L266
Creating the sourceSets
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L298

How to embed Java code into Gradle build using JavaExec task

I have a Gradle-driven project to which I want to add a simple Java task. This task is very specific to the project and if it can be helped I don't want to develop it as a separate plugin. So the question is can I define such custom task within the same build.gradle I'm using for my project? Or is it inevitable that I need to package it as a separate project (plugin) and install to the local repo?
Also it's probably important to note that the original project is not Java related (no other Java code needs to be build)
P.S. Based on comments below:
I would like to add src/main/java/SomeUsefulStuff.java to the existing project and have that file compiled and used as a custom task. I do understand that it needs to be compiled each time I run the build but again - the code will be small. However it will have some external dependencies such as Commons IO
Thanks to RaGe who pointed to JavaExec this turned out to be pretty simple. Here's what you do:
Put your Java code in /src/main/java just as you would in the regular Gradle-driven Java project. Make sure it has main method in the file you are going to call
Add apply plugin: 'java' to the build.gradle
If your Java code has any dependencies on 3rd party libs add these to dependencies section
Add new task section to build.gradle like so:
task usefulStuff(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.me.gradle.UsefulStuff'
// arguments to pass to the application
args 'OhmyGod!'
}
Now you can refer to that task as any task in your build. For example imporantTask.dependsOn usefulStuff

Categories

Resources