I using Gradle with the Application plugin.
I am trying to tune the the startScripts task. I don't think that the tuning demands a lot of changes, so I would like to change the standard task:
The complete distribution includes its own JRE so I want the script to refer to that library instead of using JAVA_HOME
If possible, remove the UNIX start script. I only need the Windows script.
If possible I prefer understanding what variables in the task to change rather rewriting the task from scratch.
My question is how to do it. Looking at the documentation I saw accessors only for applicationName, classpath, defaultJvmOpts, exitEnvironmentVar, mainClassName, optsEnvironmentVar, outputDir, which all look irrelevant for me.
Can you guide me to a documentation describing how to do this?
To remove a UNIX script, you can configure the startScripts task as:
startScripts {
doLast {
delete unixScript
}
}
As for the script content, not really sure, whether it is possible to do with the custom script generator, which could be used in startScripts as the WindowsStartScriptGenerator. But any way, you are able to modify the content of the start scripts in doLast, just referring to it as windowsScript.text:
startScripts {
doLast {
windowsScript.text = windowsScript.text.replace("set JAVA_EXE=%JAVA_HOME%/bin/java.exe",
"set JAVA_EXE=../relative/path/to/java/bin/java.exe")
}
}
Related
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.
I'm using the shadowJar plugin for Gradle (4.2.1) to build the so-called fatJar or uberJar. This works as expected but I would like to to add some actions after the building is done. More precisely, I'd like to make the resulting file executable (in Unix terms, i.e. chmod +x) and then copy it to a certain directory. I've googled a lot and I know that both task are possible. I'd like to know whether I should write a script (task) that runs shadowJar and then does what I want, or I should change the shadowJar itself to embed the operations I need.
I think a good criteria to decide about such situation is to ask yourself if the new features are really part of the responsibility of the shoadowJar. If the answer is no, then it would be better to (as you mentioned) have another task that runs on top of that. By doing so you can reuse the shadowJar in much more different scenarios by combining it to other tasks. If you define the new one to be dependent on the shadowJar, then you would be sure that you can still call shadowJar task individually while calling the new task would always trigger the shadowJar. Your new task would be something like this:
task afterShadowJar (dependesOn: 'shadowJar') {
// manipulate file permissions, etc.
}
I'm building a Java desktop application, using JavaFX, Gradle, javafx-gradle-plugin. This application connects to a server that I also build. When I compile a release version, running gradle jfxNative, I want it to talk to the production server; but otherwise, I want it to talk to localhost.
What's the proper Java/Gradle way of handling this? Some sort of compilation profile?
You can use Gradle's source sets for this:
Sample build.gradle:
apply plugin: 'java'
sourceSets {
prod {
java {
srcDirs = ['src/main/java', 'src/prod/java']
}
}
dev {
java {
srcDirs = ['src/main/java', 'src/dev/java']
}
}
}
task devJar(type: Jar) {
from sourceSets.dev.output
manifest {
attributes("Main-Class": "MyPackage.MyClass")
}
}
task prodJar(type: Jar) {
from sourceSets.prod.output
manifest {
attributes("Main-Class": "MyPackage.MyClass")
}
}
Now you can create two configuration classes for your dev and prod versions:
src/dev/java/MyPackage/Configuration.java
src/prod/java/MyPackage/Configuration.java
All the common code will be in the main source set:
src/main/java/MyPackage/MyClass.java
MyClass can get some values from the configuration class (e.g. Configuration.getBaseUrl())
Running gradle devJar/ gradle prodJar builds one of the variants.
Note: you may need to extend jfxNative/jfxJar instead of Jar in your case.
The simplest solution: Have a configuration file containing such information.
You either compile it into the application as a java resource or place it next to the jar file so it can be easily looked up via the filesystem.
With gradle all you need to do is define two build tasks with different input properties and insert the values into your properties file with groovy templating.
application.properties in src/main/resources:
server.address=${serverAddress}
add to your build.gradle
task setProductionServerAddress {
processResources.expand([serverAddress: "https://app.example.com/v1"])
}
jfxJar.dependsOn(setProductionServerAddress)
jfxNative.dependsOn(setProductionServerAddress)
And then on the application:
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/application.properties"));
if (properties.getProperty("server.address").equals("${serverAddress}")) {
setUrl("http://localhost:8080/v1");
} else {
setUrl(properties.getProperty("server.address"));
}
Have it check environment variables for names of configuration files. Nothing to do with gradle or build. The same program should run properly wherever it is deployed.
See, e.g., Properties for dev and production
The easiest approach is to define a system property which specifies where the file system location for your data is. The production appserver would define one value (using java -D in the startup script), and your dev app server would define another value. Your application source would consult the system property value (using System.getProperty()) to discover the appropriate location
Also, this makes sense.
Put the information you need in JNDI - that's what it is designed for.
Consider letting your application refuse to do anything if the information is not there.
Another reference: What is the best way to manage configuration data
EDIT: Well, what you're asking is logically not possible then, it seems to me. "It should connect to production, unless a specific someone wants to connect to development, but that feature should only be available to unknown persons" The start menu is only a shortcut for running the application, so you can install a "dev" shortcut with command line settings that are read as environment vars.
I would go with the one of the "12 factor app" concept which can be read here
One of its main concept is to use system environment variable which should determine whether you are working on a prod or dev or qa env etc.
each project/environment/machine should contain its relevant env property, which after then can be retrieved through the gradle process similar to maven profile plugin.
An example for how to detect:
`if (project.hasProperty('env') && project.getProperty('env') == 'prod') {
apply from: 'gradle/production.gradle'
} else {
apply from: 'gradle/development.gradle'
}`
more on this approach using gradle can be found: gradle profile
In my opinion and like others have suggested, this has little to do with the build and more to do with Run Time.
Therefore you could resort to checking for some kind of run time flag - a convenient and often used approach is to use System Properties.
On your Dev box, you could set an Environment variable - lets say FX _DESKTOP_APP_ENV = DEV or some such.
From your code you can look this up and decide the URL you want to use.
String env = System.getenv("FX _DESKTOP_APP_ENV");
String url = env == null ? "Production" : env;
On windows systems you can set up your system environment variables like so -- enter link description here
On *nix systems enter link description here
Hope this helps
You need to pick a configuration scheme (if JavaFX doesn't pick one for you).
I like https://github.com/typesafehub/config .
The config library will have instructions on how to make your "production" config differ from your "development" config.
See also JavaFX:Editable Configuration Files After Packaging
What's the proper Java/Gradle way of handling this? Some sort of compilation profile?
No, I would strongly recommend against compiling different code for production v.s. test. It will invalidate your testing. This should be handled in configuration, not in code. The conventional Java way of doing this is with configuration files (which can be compiled into the JAR as resources).
How to do this with Typesafe Config
I've had a bit of a look, and I am surprised not to find a good quality tutorial I can link you to here, sorry (I found a few rubbish ones). Perhaps this question will become a reference for others.
I would do something like this:
Create a "dev" and "prod" config files, along the lines of https://stackoverflow.com/a/33261928/8261
Arrange for your app to use the "dev" config file when running in your IDE and "prod" config file when running from a compiled JAR, as per Can you tell on runtime if you're running java from within a jar?
My guess as to why there aren't many tutorials for this is that all application or web Frameworks handle this for you.
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.
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