I currently struggle with probably very simple problem: How can I use one JavaFXPorts specific project as a dependency within another project of the same kind?
With pure separated projects, I have no idea how to combine them (have to admit, I'm not as up-to-date with Gradle, as I probably need to...).
I thought about using the apply plugin: 'maven' to install and grab the library to and from the local maven cache. But then there would be no separation of the platform specific code (everything - main, android, ios, desktop - would be merged together into one single JAR file.
Maybe if I reduce the problem to the pure part of the main source tree, I could create a standalone JavaFX-only project, this should be easy...
Or I could access the other project via a relative path. But for whatever reason I kind of dislike that idea.
What is the best practice for JavaFXPorts? I simply don't what to copy some stuff over and over into new projects (obviously).
Thanks in advance,
Daniel
This is a very simple example of how you can set a Gradle multi project, containing two subprojects:
Common: a regular JavaFX (gradle) project, with common code that can be reused later on in other projects.
GluonApplication: a simple Gluon Mobile project, that makes use of the common one.
I've used NetBeans to create the Gradle Root project and add the subproject, but it can be done from other IDEs or command line as well.
1. Gradle Root Project
Create a Gradle Root Project. Set the project name (GradleProject in this case), the location, and a Maven Group ID (com.gluonhq in this case), and a Maven Version (default 1.0-SNAPSHOT).
2. Gradle Common Subproject
Create a new Gradle Subproject. Choose a name (Common), and make sure that the location of this project is the GradleProject folder. Select a main class (com.gluonhq.common.Common).
Add some code:
package com.gluonhq.common;
public class Common {
public static double sum(double a, double b) {
return a + b;
}
}
3. Gluon Mobile Subproject
Add a Gluon Mobile Subproject with the Gluon plugin for your IDE. For instance select Single View project. Choose a name (GluonApplication), and again make sure that the location of this project is the GradleProject folder. Select the package name (com.gluonhq.application) and the main class (GluonApplication).
You can run this project as is, from command line in the project root: gradle :GluonApplication:run, or from your IDE (tasks -> Run).
4. Include a Common dependency
Edit the build.gradle file from the Gluon Mobile subproject, and add the Common dependency.
Since both subprojects belong to the same root project you can simple do:
dependencies {
compile 'com.gluonhq:charm:4.3.7'
compile project(":Common")
}
Save and reload the project. Now on your code you can call Common.sum:
button.setOnAction(e -> label.setText("3 + 4: " + Common.sum(3, 4)));
Run again and see that it works. You can deploy to mobile as well.
5. Installing the Common module
If you plan to reuse the Common project in this or other Gluon Mobile projects, you can install it in your .m2 repository.
On command line, from the project's root, run:
gradle :Common:install
and you'll see that the project is installed under <user>/.m2/repository/com/gluonhq/common/1.0-SNAPSHOT, including the common-1.0-SNAPSHOT.jar file.
6. Reusing the Common jar
Finally, you can include the common jar in any of your projects.
For that, edit the build.gradle file from the Gluon Mobile subproject, include the local repository, and add the Common dependency.
repositories {
mavenLocal()
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
dependencies {
compile 'com.gluonhq:charm:4.3.7'
compile 'com.gluonhq:common:1.0-SNAPSHOT'
}
Save and reload the project. And your common code will be available.
Of course, this works locally on your machine, but you can publish the artifact to a private or public repo as well, and then you should just include the proper repo in the above list.
Related
I am trying to get Intellij to recognize my properties using gradle. I have followed the steps here. So this means I have a #ConfigurationProperties annotated class with some properties.
I added the spring dependency to process them:
dependencies {
optional "org.springframework.boot:spring-boot-configuration-processor"
}
compileJava.dependsOn(processResources)
I added the plugin (I've tried not using the plugin and just making it a compile dependency, no change)
buildscript {
repositories {
maven { url 'http://repo.spring.io/plugins-release' }
}
dependencies { classpath 'io.spring.gradle:propdeps-plugin:0.0.9.RELEASE' }
}
apply plugin: 'propdeps'
apply plugin: 'propdeps-maven'
apply plugin: 'propdeps-idea'
When I run the build, I see a build/classes/java/main/META-INF/spring-configuration-metadata.json file is created based off of my properties.
When I try to use the property in either application.yml or application.properties, Intellij says it cannot resolve it.
The docs does say it should be called additional-spring-configuration-metadata.json and may expect it to be called that to process it, but I do not see a way to make the build name it that way nor configure Intellij to expect otherwise.
Has anyone got this working with gradle? Or is this a bug.
Edit I created a repo with a pair of projects to demonstrate this. One for gradle and one for maven. I created the projects from start.spring.io and basically just added the properties configuration. I also used a straight compile dependency in both cases instead of optional / compileOnly.
I had not confirmed this before, but the code assist does work for maven, but not gradle. Both create a spring-configuration-metadata.json in the META-INF in their respective build folders. I am not exactly sure who is not picking it up.
Misc relevant versions
Intellij: 2017.3.4
Springboot: 1.5.9
Gradle: 4.4.1
Java: 8.161
Turn the annotation processing on
Do not delegate IDE build/run actions to Gradle
Rebuild your project in IDE: Build -> Rebuild Project
As far as I can tell, IntelliJ (at the time of this writing, 2018.1.2) wants the spring-configuration-metadata.json file to either be in a main source root (src/main/resources/META-INF/ or src/main/java/META-INF/) or in its default output directory for it to pick it up for autocompletion of properties in your source tree. To expand on phospodka's comment, you can add something like this to your build.gradle to satisfy IntelliJ.
task copyConfigurationMetadata(type: Copy) {
from(compileJava) {
include 'META-INF/spring-configuration-metadata.json'
}
into "out/production/classes"
}
compileJava {
dependsOn processResources
finalizedBy copyConfigurationMetadata
}
This answer is a combination of the (at this time) other two answers with a minor twist. In my case this is what "fixed" the issue: (in 2019.03.01-Ultimate)
Turn on the default annotation processing (File>Settings>Build, Execution, Deployment>Annotation Processors>Enable Annotation processing
Select Obtain processors from project classpath
Select Store generated sources relative to module output directory
keep other defaults
click OK
Add the code in #thyme's answer to your build.gradle
EXCEPT instead of into "out/production/classes"
use: into "build/generated/sources/annotationProcessor"
Now you should be able to run gradle clean/build and Intellij should be able to find your "additional metadata" definitions.
Notice that even though the build.gradle code doesn't explicitly mention 'additional-spring-configuration-metadata.json', it is exactly that "additional metadata" that ends up in the annotationProcessor folder as "spring-configuration-metatdata.json" where Intellij finds it.
EDIT: Also note, you need to clean / rebuild after adding any new "additional metadata" items before Intellij will see the new entries (in the freshly regenerated file).
I'm currently trying to improve the way our projects share their configuration. We have lots of different multi-module gradle projects for all of our libraries and microservices (i.e. many git repos).
My main goals are:
To not have my Nexus repository config duplicated in every project (also, I can safely assume that the URL won't change)
To make my custom Gradle plugins (published to Nexus) available to every project with minimal boilerplate / duplication (they should be available to every project, and the only thing the project cares about is the version it's using)
No magic - it should be obvious to developers how everything is configured
My current solution is a custom gradle distribution with an init script that:
adds mavenLocal() and our Nexus repository to the project repos (very similar to the Gradle init script documentation example, except it adds repos as well as validating them)
configures an extension that allows our gradle plugins to be added to the buildscript classpath (using this workaround). It also adds our Nexus repo as a buildscript repo as that's where the plugins are hosted. We have quite a few plugins (built upon Netflix's excellent nebula plugins) for various boilerplate: standard project setup (kotlin setup, test setup, etc), releasing, publishing, documentation, etc and it means our project build.gradle files are pretty much just for dependencies.
Here is the init script (sanitised):
/**
* Gradle extension applied to all projects to allow automatic configuration of Corporate plugins.
*/
class CorporatePlugins {
public static final String NEXUS_URL = "https://example.com/repository/maven-public"
public static final String CORPORATE_PLUGINS = "com.example:corporate-gradle-plugins"
def buildscript
CorporatePlugins(buildscript) {
this.buildscript = buildscript
}
void version(String corporatePluginsVersion) {
buildscript.repositories {
maven {
url NEXUS_URL
}
}
buildscript.dependencies {
classpath "$CORPORATE_PLUGINS:$corporatePluginsVersion"
}
}
}
allprojects {
extensions.create('corporatePlugins', CorporatePlugins, buildscript)
}
apply plugin: CorporateInitPlugin
class CorporateInitPlugin implements Plugin<Gradle> {
void apply(Gradle gradle) {
gradle.allprojects { project ->
project.repositories {
all { ArtifactRepository repo ->
if (!(repo instanceof MavenArtifactRepository)) {
project.logger.warn "Non-maven repository ${repo.name} detected in project ${project.name}. What are you doing???"
} else if(repo.url.toString() == CorporatePlugins.NEXUS_URL || repo.name == "MavenLocal") {
// Nexus and local maven are good!
} else if (repo.name.startsWith("MavenLocal") && repo.url.toString().startsWith("file:")){
// Duplicate local maven - remove it!
project.logger.warn("Duplicate mavenLocal() repo detected in project ${project.name} - the corporate gradle distribution has already configured it, so you should remove this!")
remove repo
} else {
project.logger.warn "External repository ${repo.url} detected in project ${project.name}. You should only be using Nexus!"
}
}
mavenLocal()
// define Nexus repo for downloads
maven {
name "CorporateNexus"
url CorporatePlugins.NEXUS_URL
}
}
}
}
}
Then I configure each new project by adding the following to the root build.gradle file:
buildscript {
// makes our plugins (and any others in Nexus) available to all build scripts in the project
allprojects {
corporatePlugins.version "1.2.3"
}
}
allprojects {
// apply plugins relevant to all projects (other plugins are applied where required)
apply plugin: 'corporate.project'
group = 'com.example'
// allows quickly updating the wrapper for our custom distribution
task wrapper(type: Wrapper) {
distributionUrl = 'https://com.example/repository/maven-public/com/example/corporate-gradle/3.5/corporate-gradle-3.5.zip'
}
}
While this approach works, allows reproducible builds (unlike our previous setup which applied a build script from a URL - which at the time wasn't cacheable), and allows working offline, it does make it a little magical and I was wondering if I could do things better.
This was all triggered by reading a comment on Github by Gradle dev Stefan Oehme stating that a build should work without relying on an init script, i.e. init scripts should just be decorative and do things like the documented example - preventing unauthorised repos, etc.
My idea was to write some extension functions that would allow me to add our Nexus repo and plugins to a build in a way that looked like they were built into gradle (similar to the extension functions gradleScriptKotlin() and kotlin-dsl() provided by the Gradle Kotlin DSL.
So I created my extension functions in a kotlin gradle project:
package com.example
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
fun RepositoryHandler.corporateNexus(): MavenArtifactRepository {
return maven {
with(it) {
name = "Nexus"
setUrl("https://example.com/repository/maven-public")
}
}
}
fun DependencyHandler.corporatePlugins(version: String) : Any {
return "com.example:corporate-gradle-plugins:$version"
}
With the plan to use them in my project's build.gradle.kts as follows:
import com.example.corporateNexus
import com.example.corporatePlugins
buildscript {
repositories {
corporateNexus()
}
dependencies {
classpath(corporatePlugins(version = "1.2.3"))
}
}
However, Gradle was unable to see my functions when used in the buildscript block (unable to compile script). Using them in the normal project repos/dependencies worked fine though (they are visible and work as expected).
If this worked, I was hoping to bundle the jar into my custom distribution , meaning my init script could just do simple validation instead of hiding away the magical plugin and repo configuration. The extension functions wouldn't need to change, so it wouldn't require releasing a new Gradle distribution when plugins change.
What I tried:
adding my jar to the test project's buildscript classpath (i.e. buildscript.dependencies) - doesn't work (maybe this doesn't work by design as it doesn't seem right to be adding a dependency to buildscript that's referred to in the same block)
putting the functions in buildSrc (which works for normal project deps/repos but not buildscript, but is not a real solution as it just moves the boilerplate)
dropping the jar in the lib folder of the distribution
So my question really boils down to:
Is what I'm trying to achieve possible (is it possible to make custom classes/functions visible to the buildScript block)?
Is there a better approach to configuring a corporate Nexus repo and making custom plugins (published to Nexus) available across lots of separate projects (i.e. totally different codebases) with minimal boilerplate configuration?
If you want to benefit from all the Gradle Kotlin DSL goodness you should strive to apply all plugins using the plugins {} block. See https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md
You can manage plugin repositories and resolution strategies (e.g. their version) in your settings files. Starting with Gradle 4.4 this file can be written using the Kotlin DSL, aka settings.gradle.kts. See https://docs.gradle.org/4.4-rc-1/release-notes.html.
With this in mind you could then have a centralized Settings script plugin that sets things up and apply it in your builds settings.gradle.kts files:
// corporate-settings.gradle.kts
pluginManagement {
repositories {
maven {
name = "Corporate Nexus"
url = uri("https://example.com/repository/maven-public")
}
gradlePluginPortal()
}
}
and:
// settings.gradle.kts
apply(from = "https://url.to/corporate-settings.gradle.kts")
Then in your project build scripts you can simply request plugins from your corporate repository:
// build.gradle.kts
plugins {
id("my-corporate-plugin") version "1.2.3"
}
If you want your project build scripts in a multi-project build to not repeat the plugin version you can do so with Gradle 4.3 by declaring versions in your root project. Note that you also could set the versions in settings.gradle.kts using pluginManagement.resolutionStrategy if having all builds use the same plugins version is what you need.
Also note that for all this to work, your plugins must be published with their plugin marker artifact. This is easily done by using the java-gradle-plugin plugin.
I promised #eskatos that I would come back and give feedback on his answer - so here it is!
My final solution consists of:
Gradle 4.7 wrapper per project (pointed at a mirror of http://services.gradle.org/distributions setup in Nexus as a raw proxy repository, i.e. it's vanilla Gradle but downloaded via Nexus)
Custom Gradle plugins published to our Nexus repo along with plugin markers (generated by the Java Gradle Plugin Development Plugin)
Mirroring the Gradle Plugin Portal in our Nexus repo (i.e. a proxy repo pointing at https://plugins.gradle.org/m2)
A settings.gradle.kts file per project that configures our maven repo and gradle plugin portal mirror (both in Nexus) as plugin management repositories.
The settings.gradle.kts file contains the following:
pluginManagement {
repositories {
// local maven to facilitate easy testing of our plugins
mavenLocal()
// our plugins and their markers are now available via Nexus
maven {
name = "CorporateNexus"
url = uri("https://nexus.example.com/repository/maven-public")
}
// all external gradle plugins are now mirrored via Nexus
maven {
name = "Gradle Plugin Portal"
url = uri("https://nexus.example.com/repository/gradle-plugin-portal")
}
}
}
This means that all plugins and their dependencies are now proxied via Nexus, and Gradle will find our plugins by id as the plugin markers are published to Nexus as well. Having mavenLocal in there as well facilitates easy testing of our plugin changes locally.
Each project's root build.gradle.kts file then applies the plugins as follows:
plugins {
// plugin markers for our custom plugins allow us to apply our
// plugins by id as if they were hosted in gradle plugin portal
val corporatePluginsVersion = "1.2.3"
id("corporate-project") version corporatePluginsVersion
// 'apply false` means this plugin can be applied in a subproject
// without having to specify the version again
id("corporate-publishing") version corporatePluginsVersion apply false
// and so on...
}
And configures the gradle wrapper to use our mirrored distribution, which when combined with the above means that everything (gradle, plugins, dependencies) all come via Nexus):
tasks {
"wrapper"(Wrapper::class) {
distributionUrl = "https://nexus.example.com/repository/gradle-distributions/gradle-4.7-bin.zip"
}
}
I was hoping to avoid the boilerplate in the settings files using #eskatos's suggestion of applying a script from a remote URL in settings.gradle.kts. i.e.
apply { from("https://nexus.example.com/repository/maven-public/com/example/gradle/corporate-settings/1.2.3/corporate-settings-1.2.3.kts" }
I even managed to generate a templated script (published alongside our plugins) that:
configured the plugin repos (as in the above settings script)
used a resolution strategy to apply the version of the plugins associated with the script if the requested plugin id was one of our plugins and the version wasn't supplied (so you can just apply them by id)
However, even though it removed the boilerplate, it meant our builds were reliant on having a connection to our Nexus repo, as it seems that even though scripts applied from a URL are cached, Gradle does a HEAD request anyway to check for changes. It also made it annoying to test plugin changes locally, as I had to point it manually at the script in my local maven directory. With my current config, I can simply publish the plugins to maven local and update the version in my project.
I'm quite happy with the current setup - I think it's far more obvious to developers now how the plugins are applied. And it's made it far easier to upgrade Gradle and our plugins independently now that there's no dependency between the two (and no custom gradle distribution required).
I've been doing something like this in my build
buildscript {
project.apply {
from("${rootProject.projectDir}/sharedValues.gradle.kts")
}
val configureRepository: (Any) -> Unit by extra
configureRepository.invoke(repositories)
}
In my sharedValues.gradle.kts file I have code like this:
/**
* This method configures the repository handler to add all of the maven repos that your company relies upon.
* When trying to pull this method out of the [ExtraPropertiesExtension] use the following code:
*
* For Kotlin:
* ```kotlin
* val configureRepository : (Any) -> Unit by extra
* configureRepository.invoke(repositories)
* ```
* Any other casting will cause a compiler error.
*
* For Groovy:
* ```groovy
* def configureRepository = project.configureRepository
* configureRepository.invoke(repositories)
* ```
*
* #param repoHandler The RepositoryHandler to be configured with the company repositories.
*/
fun repositoryConfigurer(repoHandler : RepositoryHandler) {
repoHandler.apply {
// Do stuff here
}
}
var configureRepository : (RepositoryHandler) -> Unit by extra
configureRepository = this::repositoryConfigurer
I follow a similar patter for configuring the resolution strategy for plugins.
The nice thing about this pattern is that anything you configure in sharedValues.gradle.kts can also be used from your buildSrc project meaning that you can reuse repository declarations.
Updated:
You can apply another script from a URL, for example doing this:
apply {
// This was actually a plugin that I used at one point.
from("http://dl.bintray.com/shemnon/javafx-gradle/8.1.1/javafx.plugin")
}
Simply host your script that you want all your builds to share on some http server (would highly recommend using HTTPS so your build can't be targeted by a man in the middle attack).
The downside of this is that I don't think that scripts applied from urls aren't cached so they will be re-downloaded every time you run your build.
This may have been fixed by now, I'm not certain.
A solution offered to me by Stefan Oehme when I was having a similar problem was to vendor my own custom distribution of Gradle. According to him this is a common thing to do at large companies.
Simply create a custom fork of the gradle repo, add your companies special sauce to every project using this custom version of gradle.
I encountered a similar problem when common config is replicated in each and every project. Solved it by a custom gradle distribution with the common settings defined in init script.
Created a gradle plugin for preparing such custom distributions - custom-gradle-dist. It works perfectly for my projects, e.g. a build.gradle for a library project looks like this (this is a complete file):
dependencies {
compile 'org.springframework.kafka:spring-kafka'
}
So if I build a jar in Maven, say for example jackson-core-2.5.1.jar, I find the following in the artifact:
META-INF/maven/
META-INF/maven/com.fasterxml.jackson.core/
META-INF/maven/com.fasterxml.jackson.core/jackson-core/
META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.properties
META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.xml
Gradle, however, does not seem to create this data. Problem is that we have several components of our build, including a parent project, that aren't hosted in the same SCM location. For our large and complex build, how would Gradle know that a locally built artifact in one SCM location depends on a locally built artifact in another, if there's no metadata? What is the Gradle way to manage this situation?
Repositories contain a separate copy of pom.xml. It usually lives next to the JAR file in the physical structure on the disk. This applies to binary repositories like Nexus or Artifatory and also to your local Maven repository (under $HOME/.m2/repo).
If for some reason you want to copy the behavior of Maven you can tell Gradle to do create those files. We use this customization in subprojects closure that configures our multi-project Gradle build.
jar {
// until we have better solution
// https://discuss.gradle.org/t/jar-task-does-not-see-dependency-when-using-maven-publish/11091
if (project.tasks.findByName('generatePomFileForMavenJavaPublication')) {
dependsOn 'generatePomFileForMavenJavaPublication'
} else {
project.tasks.whenTaskAdded { addedTask ->
if (addedTask.name == 'generatePomFileForMavenJavaPublication') {
project.tasks.jar.dependsOn 'generatePomFileForMavenJavaPublication'
}
}
}
into("META-INF/maven/$project.group/$project.archivesBaseName") {
/*
from generatePomFileForMavenJavaPublication
*/
from new File(project.buildDir, 'publications/mavenJava')
rename ".*", "pom.xml"
}
}
It would be simpler if there wasn't a problem with generatePomFileForMavenJavaPublication task being created lazily sometimes. And you need to check how to create your properties file. I guess you can dump properties from a running Gradle process there.
I would like to include retrofit as a module in my Android Studio project. The problem is that retrofit is a maven project and so Android Studio won't let me import it. Is there a way around this?
A similar question has been asked before, but it received no answers.
Use a custom group and/or artifact in the POM of your clone, so your clone cannot be confused with the original.
Build and install your clone of Retrofit using Maven as usual: mvn install. (Using the command line or an IDE other than Android Studio.) You have to build your Retrofit clone manually after each change you make to it, for Gradle to see the changes.
Add the local Maven repository to your Gradle script. See https://docs.gradle.org/2.5/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html#org.gradle.api.artifacts.dsl.RepositoryHandler:mavenLocal():
repositories {
mavenLocal()
}
Add the GAV of your clone as a dependency to your Gradle script:
dependencies {
compile 'com.yourgroup:retrofit:1.9.0-custom'
}
Go to your project then goto the app. You will see a build.gradle file under app (DO NOT use the gradle under gradle folder but the ine under app folder). Add this line.
dependencies {
....
compile 'com.squareup.retrofit:retrofit:1.9.0'
...
}
Then, make sure that you define the repository details in directory and add the url.
repositories {
flatDir {
dirs 'libs'
}
maven { url 'http://download.crashlytics.com/maven' }
}``
See Migrating from Maven to Gradle. Just execute gradle init.
Just add it to the dependencies { } block of your application's build.gradle file.
dependencies {
compile 'com.squareup.retrofit:retrofit:1.9.0'
}
In android studio simply go to project structure -> module you want to add retrofit -> dependencies tab -> plus(add) -> library dependency and then type retrofit in text box and select com.squareup.retrofit:retrofit and add to your dependencies
another soluation , just download latest jar file from https://github.com/google/retrofit then
goto new module ->create module of jar->select the path of that jar file->then on your project module gradle dependency add(implementation(':name retrofit module').
I've started to create a Gradle plugin. The build is finishing and producing a valid plugin jar (I think), but the implementation is skeletal. I have a couple of unit tests, but I want to have a very simple way to "system test" the plugin.
The original plugin is in my Eclipse workspace. I want to create a second Eclipse standalone project with a build.gradle that references the jar produced by the other project, just using a relative path to the jar in the other project's "build" folder.
My first stab at this is:
buildscript {
repositories {
jcenter()
mavenCentral()
flatDir {
dirs "../GradleYangPlugin/build/libs"
}
}
dependencies {
classpath files("GradleYangPlugin-1.0.0-SNAPSHOT.jar")
}
}
apply plugin:"com.att.opnfv.yang.gradle"
I then just tried "gradle tasks" to see what it said. It fails with "Plugin with id 'com.att.opnfv.yang.gradle' not found."
You might ask whether I constructed the plugin jar correctly. That's a valid question, but I can show that's not even relevant yet. I ran a File IO monitor (SysInternals ProcessMonitor) while I ran the build, looking for any occurrences of "GradleYangPlugin-1.0.0-SNAPSHOT.jar". The only place it looked for it was in the current directory (the root directory of the second project). This shows that the syntax I used for my "flatDir" element isn't groked by Gradle, and it's also not complaining about it. It just ignores it, seemingly.