I am attempting to update to Kotlin 1.4. In my build.gradle file, I have the following:
buildscript {
allprojects {
ext {
kotlin_version = "1.3.70"
ktor_version = "1.2.2"
junit_version = "5.4.2"
log4j_version = "2.11.2"
jackson_version = "2.9.9"
kafka_version = "2.3.0"
}
}
repositories {
maven {
url 'https://smartward.jfrog.io/smartward/gradle-dev'
credentials {
username = "${artifactory_user}"
password = "${artifactory_password}"
}
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.9.7"
}
}
and later on:
implementation(
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version",
"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version",
"org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version",
"org.apache.logging.log4j:log4j-api:$log4j_version",
// For JSON mapping
"com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version",
"com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version",
"com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version",
"com.natpryce:konfig:1.6.10.0",
"org.apache.kafka:kafka-clients:$kafka_version",
"io.ktor:ktor-server-netty:$ktor_version",
"io.ktor:ktor-locations:$ktor_version",
"io.ktor:ktor-jackson:$ktor_version",
"io.ktor:ktor-client-core:$ktor_version",
"io.ktor:ktor-client-apache:$ktor_version",
"io.ktor:ktor-client-json:$ktor_version"
)
My first step was to change kotlin_version to be "1.4.0". When running the build script, I was informed that Gradle needed to be updated as well. I did this, changing my gradle-wrapper.properties file (diff below):
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip
This now means that some of my log4j imports no longer work. Namely:
import org.apache.logging.log4j.core.Logger
import org.apache.logging.log4j.core.config.Configurator
I have attempted reverting to Kotlin 1.3.70, without reverting the Gradle update, and the issue persists, so I suspect a problem with Gradle, or my build script, but I'm not sure why or how to fix it. I have also attempted using Gradle 6.6, with the 4.17.1 version of org.jfrog.buildinfo:build-info-extractor-gradle, but the problem persists.
Use dependencyInsight to see what's going wrong
It sounds like what's happening is that the version of log4j that ends up being used isn't the version you were expecting.
Dependency version resolution can get pretty complicated, especially when you have lots of dependencies. Different things want different versions of the same dependency, but Gradle has to pick one version that will end up on the classpath. In general, it will pick the newest version from among all the versions that have been requested.
There are two reasons I can think of that upgrading Gradle might have changed the version of log4j that ends up being used:
Something in Gradle itself could be adding a dependency on log4j, and might now be requesting a newer version than was used in the older Gradle distribution.
On the other hand, it's possible that the way version conflicts are resolved has actually subtly changed in the newer version of Gradle.
Luckily, Gradle gives you some tools to help figure out what's going on. I would suggest comparing the output of the following command both before and after updating the Gradle version.
gradle dependencyInsight --dependency log4j
This will print out a tree-like report of everything that's using log4j, and will tell you why a particular version was selected. It might take some time to understand the report, especially if it's long, but it's worth reading through it carefully.
Use platform constraints to force the correct version
Projects like log4j are made up of several artifacts (log4j-api, log4j-core, etc). The process of resolving the various transitive dependency versions in your build can end up introducing versions that don't match each other. It's important to make sure that all the artifacts have matching versions.
To help solve this, log4j provides an additional 'bill of materials' artifact, log4j-bom. BOM artifacts don't contain any code, but they specify a list of dependencies, along with the versions that should be used.
Since version 5, Gradle lets you use BOM files to suggest or enforce versions for a set of dependencies. Applying a 'platform' dependency of this sort doesn't add or remove any actual dependencies to your build, but it does influence or control the versions of the dependencies you already have.
In your case, you could add the following to your dependencies:
dependencies {
implementation enforcedPlatform("org.apache.logging.log4j:log4j-bom:$log4j_version")
}
This adds the log4j-bom as an enforcedPlatform dependency, guaranteeing that every log4j dependency used in your application will always have the version you specify. This is a powerful tool and should help make sure you don't run into problems like this in future.
As per the official documentation of Log4j you need to link to both log4j-api and log4j-core to consume the package properly.
Related
Problem
I have a setup of various distinct repos/projects (i.e. app1, app2, app3) that all depend on shared functionality in my base package.
The projects also use various other third-party dependencies (i.e. app1 and app3 use spring, all of them use kotlinx-serialization).
I want to synchronise the versions of all third-party dependencies, so that any project using my base package uses the same version of every third-party dependency. However, I don't want to introduce new dependencies to projects that do not use them (i.e. app2 does not use spring)
Solution attempts
For libraries, I have been able to solve this with the help of a gradle platform, which does exactly what I want - I specify the versions in my base package, then add the platform as a dependency to my projects and can then simply add dependencies by name (i.e. implementation("org.springframework.boot:some-package")) without having to specify a version number, because it uses the provided value from my platform.
However, for plugins, I have not been able to do this. Many libraries come with plugins and naturally the plugin should be at the same version as the library. I have tried various approaches, including writing a standalone plugin, but none have worked.
Current best idea
I added implementation("org.springframework.boot:spring-boot-gradle-plugin:3.0.2") to the dependencies of my standalone plugin. Then, I added the following code to my standalone plugin:
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.plugins.apply("org.springframework.boot")
}
}
This works and applies the plugin to my main project at the correct version. However, there are 2 major problems with this:
a) Now every project applies the spring plugin, including app2 (which does not use spring).
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
I just want to manage the versions of plugins and libraries of multiple projects/repos in a central place - this feels like a fairly basic use case - why is this so hard?
There are some great and detailed answers about dependency management, but unfortunately none worked to perform cross-project version management for plugins.
It seems that there is no gradle functionality to do this, but I got it working with a bit of a workaround. Here is my (working) approach, in hope that it helps someone else with this:
Create a Standalone gradle Plugin
In the build.gradle.kts of the plugin, include the maven coordinates (not its ID) of every other plugin whose version you want to manage in any of your projects in the dependency block with the api keyword. i.e. api("org.springframework:spring-web:6.0.2")
In the main projects, remove every other plugin from the plugins block, so that your custom standalone plugin is the only one remaining.
Create a file (i.e. a plugins.json or whatever you want) in the project root directory of all main projects and in there supply the plugin IDs of the plugins that you actually intend to use in that project. Just the IDs, no version numbers, i.e. "org.springframework.boot" for Spring's plugin. (Keep in mind that for plugins declared as kotlin("abc") you will have to add the prefix "org.jetbrains.com.", as the kotlin method is just syntactic sugar for that)
In your plugin source code, in the overriden apply method, look for. a file named plugins.json (or whatever you chose) in the project.buildFile.parent directory (which will be the directory of the project using this plugin, NOT of the plugin itself). From this file, read the plugin IDs
for every pluginID in the file, call project.plugins.apply(id)
How/Why it works:
The main project build.gradle.kts is executed, looks at the plugin block and applies your standalone plugin (which is the only one), which calls its apply method.
This plugin then applies other plugins based on their ID from the file.
Normally, this will throw an error because these plugins are not found, but because we defined them as dependencies with the api keyword in our standalone plugin, they are now available on the classpath and in exactly the version of that import statement.
Hope it helps someone!
I use version numbers in a gradle.properties file for this purpose. Since the introduction of Gradle version catalogs, my approach is probably a bit out of date, but I'll share it here anyway. It's based on the fact that plugin versions can be managed in settings.gradle.kts by reading values from the properties file.
In gradle.properties:
springBootVersion=3.0.2
In settings.gradle.kts:
pluginManagement {
val springBootVersion: String by settings
plugins {
id("org.springframework.boot") version springBootVersion
}
}
And finally in build.gradle.kts:
plugins {
id("org.springframework.boot")
}
dependencies {
val springBootVersion: String by project
implementation(platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"))
}
Notice that the plugin version is omitted in the build script because it is already specified in the settings file.
And note also that the method for accessing the property in the settings script is slightly different from that in the build script.
a) Now every project applies the spring plugin, including app2 (which does not use spring).
It is indeed better to avoid applying too many plugins - and that's why Gradle encourages reacting to plugins.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
import org.springframework.boot.gradle.plugin.SpringBootPlugin
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
// don't apply
//target.plugins.apply("org.springframework.boot")
// instead, react!
target.plugins.withType<SpringBootPlugin>().configureEach {
// this configuration will only trigger if the project applies both
// BasePlugin *and* the Spring Boot pluging
}
// you can also react based on the plugin ID
target.pluginManager.withPlugin("org.springframework.boot") {
}
}
}
Using the class is convenient if you want to access the plugin, or the plugin's extension, in a typesafe manner.
You can find the Plugin's class by
looking in the source code for the class that implements Plugin<Project>,
in the plugin's build config for the implementationClass,
or in the published plugin JAR - in the META-INF/gradle-plugins directory there will be a file that has the implementationClass.
This doesn't help your version alignment problem - but I thought it was worth mentioning!
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
You're on the right track with the "long implementation string" as you call it. I'll refer to those as the 'Maven coordinates' of the plugin.
Gradle Plugin Maven Coordinates
The plugin id of the Kotlin JVM plugin is org.jetbrains.kotlin.jvm, but the Maven coordinates are org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0 .
The 'legacy' part refers to how the plugins are applied, using the apply(plugin = "...") syntax. The new way uses the plugin {} block, but under the hood, both methods still use the Maven coordinates of the plugin.
If you add those Maven coordinates (with versions) to your Java Platform, then you can import the platform into your project. But where?
Defining plugin versions
There are a lot of ways to define plugins, so I'll only describe one, and coincidentally it will be compatible with defining the version using a Java Platform.
If you're familiar with buildSrc convention plugins, you'll know that they can apply plugins, but they can't define versions.
// ./buildSrc/src/main/kotlin/kotlin-jvm-convention.gradle.kts
plugins {
kotlin("jvm") version "1.8.0" // error: pre-compiled script plugins can't set plugin versions!
}
Instead, plugin versions must be defined in the build config for buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// the Maven coordinates of the Kotlin JVM plugin - including the version
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
}
This looks a lot more traditional, and so I hope the next step is clean: use your Java Platform!
Applying a Java Platform to buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// import your Java Platform
implementation(platform("my.group:my-platform:1.2.3"))
// no version necessary - it will be supplied by my.group:my-platform
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
Note that this same method will also apply if your projects an 'included build' instead of buildSrc.
Once the plugin versions are defined in ./buildSrc/build.gradle.kts, you can use them throughout your project (whether in convention plugins, or in subprojects), they will be aligned.
// ./subproject-alpha/build.gradle.kts
plugins {
kotlin("jvm") // no version here - it's defined in buildSrc/build.gradle.kts
}
It's a .jar available in various versions at Maven Repo.
But what category is it? It is published by org.codehaus.groovy, the same outfit that I get my groovy-all dependency from. I also find that import groovy.sql doesn't work in a script unless I specifically include this dependency. So it would appear not to be part of the core language.
Outside a Gradle context I find that I have to manually put the .jar file under ~/.groovy/lib in order to use it. If I put the wrong version (e.g. 2.5.9 for 3.0.2) under ~/.groovy/lib the script won't run... even if I'm not using groovy.sql at all!
Is this a "dependency"? It seems a typically powerful and hassle-free Groovy way of manipulating databases. Are there any other powerful add-on (non-core) Groovy .jar file modules like this, which have to be manually placed under ~/.groovy/lib, that I should know about?
groovy consists of subprojects:
https://github.com/apache/groovy/tree/master/subprojects
and groovy-sql one of the subprojects
all subprojects are published in maven as separate libraries
prior to version 2.5 there was groovy-all-XX.jar that includes all other groovy libraries
https://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/2.4.19/
however starting from v 2.5 groovy-all represented by groovy-all-XX.pom that depends on all other groovy libraries
https://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/2.5.0/
so, to include all groovy features you have to specify groovy-all in your maven/gradle/... dependency
and finally useful site to dig dependencies:
https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all/3.0.2
I am developing an application using Spring Boot 1.5.9.RELEASE, and I am using Gradle as build tool.
I want to use SelenumHQ 3.8.1 in the project.
When I was building the project I noticed that Selenium 2.53.1 was added to project (not 3.8.1), so I investigated and found out the reason.
There exists following statement in my build.gradle:
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:1.5.9.RELEASE")
}
}
and in that file selenium.version property is set to '2.53.1'.
So I have changed the statement to
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:1.5.9.RELEASE") {
bomProperty 'selenium.version', '3.8.1'
}
}
}
but now IDEA shows both 3.8.1 and 2.53.1 as dependencies of the project, and when I build the artifact using gradle, there only exists 2.53.1 dependencies and no sign of 3.8.1.
How can I change this behavior and use Selenium 3.8.1?
P.S. The Selenium is made of multiple jar files, so it is not just a single Jar file, and I want to update all of them in the most minimized using gradle.
I have found the answer in here.
I shall override the property using: ext['selenium.version']='3.8.1' statement.
When IntelliJ imports the dependencies, it will not remove old dependencies like this.
You would have to clean it out, then re-import the dependencies. Of course, another dependency might also have a dependency on 2.53.1 that would bring it in again.
I don't know Gradle, but maven you can do a mvn tree dependency that would list all the dependencies and further down and you can look through it to see if selenium is a dependency for another dependency too.
PROBLEM
I have a custom plugin successfully published into Artifactory and I have successfully loaded it into my application by using the following syntax in my BuildConfig.groovy below. However, as I make changes to the plugin, and publish them of course, I want to get those latest changes into my application that is using the plugin?
I thought that doing a grails compile or grails refresh-dependencies would pick up the lasted code but when I go to grails-app.domain folder I do not see my newer Domain classes.
CURRENT WORKAROUND
I had to go to my .grails/2.3.4/projects/myProject/plugins delete the plugin and then run the grails refresh-dependencies in order to get the latest version of the plugin.
QUESTION
Is there a quicker/better way to do this without having to haunt the plugin in the .grails directory?
Attached are the relevant sections of my BuildConfig.groovy in case are needed to assist:
repositories {
inherits true // Whether to inherit repository definitions from plugins
//This is to be able to download our own custom plugins
String serverRoot = 'http://serverRoot/artifactory'
mavenRepo serverRoot + '/plugins-snapshot-local/'
mavenRepo serverRoot + '/plugins-release-local/'
grailsPlugins()
grailsHome()
mavenLocal()
grailsCentral()
mavenCentral()
mavenRepo 'http://repo.spring.io/milestone'
}
plugins {
// plugins for the build system only
build ":tomcat:7.0.47"
// plugins for the compile step
compile ":scaffolding:2.0.1"
compile ':cache:1.1.1'
// plugins needed at runtime but not for compilation
runtime ":hibernate:3.6.10.6" // or ":hibernate4:4.1.11.6"
runtime ":database-migration:1.3.8"
runtime ":jquery:1.10.2.2"
runtime ":resources:1.2.1"
compile ':spring-security-core:2.0-RC2'
compile ":spring-security-ldap:2.0-RC2"
compile ":spring-security-ui:1.0-RC1"
compile ":mycustomplugin:0.1" //This is the plugin that I want get the latest code for
}
Best practice to manage plugins is to change the plugin version whenever you modify the plugin code.
For example, your current version is 0.1 and when you change something in domain or somewhere else then change the version to 0.2 or something else in your plugin descriptor file and then release the plugin. Now use that new version 0.2 in your BuildConfig.groovy as compile ":mycustomplugin:0.1".
But sometime it is required to make some changes in already published version, then there are two ways to do it. First is to add -SNAPSHOT in your plugin version then Grails will always pull the latest version of that plugin and second is to add changing flag to it.
compile(":mycustomplugin:0.1") {
changing = true
}
Please have a look at http://grails.org/doc/latest/guide/conf.html#changingDependencies for more info.
Thanks,
SA
This may seem like an odd question. I want to compile LESS into CSS without installing a plug-in into my local gradle. There are several gradle plug-ins out there that compile LESS into CSS, which is great, but I would like to compile and use the plug-ins at runtime rather than install them prior.
Is this possible? If there was a standard plug-in like for CoffeeScript, it would be no big deal and easy, but there isn't one for LESS. I'm rather new to Gradle, so I'm unsure of how to move forward. The obvious solution is to bloody install the plug-in, but given a constraint that I can't, is it possible to compile/use it at runtime?
https://github.com/koenongena/lesscss-gradle-plugin
https://github.com/skhome/gradle-less-plugin
https://github.com/msgilligan/gradle-lesscss-plugin
In most cases you don't install plugins into your Gradle installation. Rather, you declare the usage of the plugin as part of your build.gradle. At build runtime, it then resolves any plugins that are needed for your build script, loads them, and then runs the build. The particular plugins you listed are a bit unusual in that (as far as I can tell) they haven't yet been published in a publicly accessible Maven repository (such as Maven Central or Bintray). After a quick search, I found one that appears to be in Maven Central:
https://github.com/obecker/gradle-lesscss-plugin
To use it, you don't need to install anything ahead of time. Instead, you declare it in your build.gradle like this:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'de.obqo.gradle:gradle-lesscss-plugin:1.0-1.3.3'
}
}
apply plugin: 'lesscss'
If you don't want to use a plugin, you can pretty easily compile LESS files in Gradle without using any plugin at all. That's because the Gradle plugin architecture builds on exactly the same DSL and deep API as you use in build scripts. The main difference is one of intent; a plugin has been packaged in a way that is intended for re-use in other projects.
The bare minimum needed is access to a library that performs LESS compilation, and a declaration of a task that uses it. Here's a quick example using lesscss-java. It only compiles a single LESS file, but it should be easy to extend for whatever your requirements are.
build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.lesscss:lesscss:1.3.0"
}
}
task lessCompile << {
def compiler = new org.lesscss.LessCompiler()
compiler.compile(file("some.less"), file("some.css"))
}
some.less:
#mainColor: black;
body {
background-color: #mainColor;
}
After running gradle lessCompile, the result is some.css:
body {
background-color: #000000;
}