Define Kotlin's detekt version as extra property (ext) - gradle

Groovy allows definition of extra properties for the project in ext.
I wanted to define Detekt's version inside groovy's extra properties. Detekt is a static code analysis tool for Kotlin lang.
However when I do it in the folloing way:
buildscript {
// testing, code-style, CI-tools
ext.detect_code_analysis = '1.0.0.RC6-3' //change to 1.0.0 when available
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id "io.gitlab.arturbosch.detekt" version "$detect_code_analysis"
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
detekt {
version = "$detect_code_analysis"
profile("main") {
input = "$projectDir/app/src/main/java"
config = "$projectDir/detekt-config.yml"
filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
}
}
it complaing with:
Error:(17, 0) startup failed:
build file '/Users[...]build.gradle': 17: argument list must be exactly 1 literal non empty string
See https://docs.gradle.org/4.1/userguide/plugins.html#sec:plugins_block for information on the plugins {} block
# line 17, column 5.
id "io.gitlab.arturbosch.detekt" version "$detect_code_analysis"
^
1 error

“New style” Gradle plug-in definition (without including the full dependency on buildscript block) does not allow variables in the version.
For more information refer to the section of documentation that’s referred to in the error message. There is a subsection “Limitations of the plugins dsl” that explains everything.
If you want to continue using the variable version strings you need to return to the “old way” by using the apply plugin: “xxx” syntax.

You cannot use variables in plugins {}: docs
Where «plugin version» and «plugin id» must be constant, literal
This is an open bug: Allow the plugin DSL to expand properties as the version

Just as #Strelok suggested the final workaround (until bug is fixed) is to:
add a classpath in buildscript.dependecies
change plugins to apply plugin: "io.gitlab.arturbosch.detekt"
Solution:
buildscript {
// testing, code-style, CI-tools
ext.detect_code_analysis = '1.0.0.RC6-3' //change to 1.0.0 when available
repositories {
google()
jcenter()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detect_code_analysis"
}
}
apply plugin: "io.gitlab.arturbosch.detekt"
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
detekt {
version = "$detect_code_analysis"
profile("main") {
input = "$projectDir/app/src/main/java"
config = "$projectDir/detekt-config.yml"
filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
}
}

Related

Cannot define anything in publishing block of build.gradle

I'm trying to create a library and publish it to maven local. When I start trying to add a MavenPublication to publications, the IDE gives it a dotted underline like it's an unresolved reference. Same with parameters like from and artifact.
If I uncomment the pom block, gradle sync fails and says Cause: invalid type code: B3, ultimately because of org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException on one of the parameters within.
This build.gradle file is in one of my modules. The top level build.gradle is pretty simple.
buildscript {
repositories {
mavenLocal()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
}
dependencies {
}
}
allprojects {
apply plugin: "idea"
group = 'com.mygroup'
version = '1.0.0'
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
}
}
And here's the module's build.gradle.
plugins {
id 'java-library'
id 'maven-publish'
id 'signing'
}
task sourcesJar(type: Jar) {
from sourceSets.main.allJava
archiveClassifier = 'sources'
}
task javadocJar(type: Jar) {
from javadoc
archiveClassifier = 'javadoc'
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = 'mylibrary'
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = 'MyLibrary'
organization {
name 'MyOrg'
url 'www.myurl.com'
}
}
}
}
}
signing {
sign publishing.publications.mavenJava
}
javadoc {
if(JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption('html5', true)
}
}
I'm using Gradle 5.1.1, IntelliJ Idea 2019.1.2, and JDK 12.0.1.
As it's a bit long explanation, let me clarify the situation here instead of replying in the comments to my question about providing the complete example with a pom-offender block. As the OP has already figured out, the problem is in the missing = sign. It's necessary for pom.organization and must not be present in the plugin.id.
For plugins block, it's a well-known Groovy convention of method call, which allow us to omit parentheses. And you can't use assignment here because id is not a property of a class. Look at the Gradle source code for plugins block.
However, if we look at pom.organization block we see the receiver of Action is MavenPomOrganization which has getName() and getUrl() returning Property<String> and not taking any arguments. From bare Groovy standpoint, it's definitely not the case of omitting parentheses. But I don't know for sure how the assignment syntax (name = 'MyOrg') works here under the hood. At first glance I can just speculate that it's some sort of Gradle magic using Groovy magic, e.g. AST transformation or something

Add dependency and plugin in seperate file using "apply from" in gradle

My build.gradle inside app directory contains:
apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
}
}
allprojects {
repositories {
mavenCentral();
jcenter()
}
}
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
apply from: 'build-extras.gradle'
}
And this is my build-extras.gradle file:
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.github.triplet.gradle:play-publisher:1.2.2'
}
}
apply plugin: 'com.github.triplet.play'
Am I correct to assume that build-extras.gradle should be "merged" inside original file? No matter how I move it around I get Error:Plugin with id 'com.github.triplet.play' not found.
If i move classpath 'com.github.triplet.gradle:play-publisher:1.2.2' up to the main file and leave just apply plugin: 'com.github.triplet.play' inside build-extras.gradle then it seems to work fine. So am I wrongly defining dependencies?
A build.gradle file can only ever have one buildscript block. You are correct with the paragraph explanation. Add the plugin dependency to your main build file's dependencies block within the buildscript, then conditionally apply the plugin using whatever logic you'd like.

Gradle: manage dependency/plugin versions in multi-project builds

I was working with Maven before Gradle and Maven has such things like dependencyManagement and pluginManagement what allows to keep all versions "DRY" in one place with help of "properties".
Gradle supports project properties, so I can declare versions like:
buildscript {
ext.kotlin_version = '1.1.61'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
But this approach cannot be applied to plugins {} DSL. I cannot write something like this:
plugins {
id 'org.jetbrains.kotlin:kotlin-gradle-plugin' version $kotlin_version
}
As according to docs:
values must be literal (e.g. constant strings, not variables)
Is there a way to workaround this limitation?
The latest versions of Gradle allows you to define your versions in a property file, map that version in a pluginManagement block, and then omit the version from all downstream plugin blocks. The pluginManagement block does not have the only-constant restriction.
In fact, once you use this approach, it is a compile time error to even try to declare a version downstream.
After using this approach, there is a good chance you can completely omit your buildscript.
gradle.properties
kotlinVersion=1.3.50
settings.gradle
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == "org.jetbrains.kotlin.jvm") {
useVersion(kotlinVersion)
}
}
}
}
build.gradle
plugins {
id("org.jetbrains.kotlin.jvm")
}
If you are into kotlinscript, get your version from a delegate:
settings.gradle.kts
val kotlinVersion: String by settings
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == "org.jetbrains.kotlin.jvm") {
useVersion(kotlinVersion)
}
}
}
}
Of course, you do not need the properties file. You can just hard code the version in your gradle.settings. But by having the property, you can then declare dependencies to the stdlib using it.
I think you can put the ext closure in a separate properties.gradle, and then reference the properties twice in buildscript as well as project build (buildscript block is evaluated at the very beginning, before any other part of groovy script).
For example, in $projectRoot/gradle/properties.gradle as below:
ext {
kotlinVersion = '1.1.61' // NOTE, the naming convention is better with kotlinVersion instead of kotlin_version
}
And your $projectRoot/build.gradle would look like this:
buildscript {
apply from: "gradle/properties.gradle"
repositories {
jcenter()
}
dependencies {
println "In buildscript, kotlinVersion is $kotlinVersion"
}
}
apply plugin: 'java'
apply from: "gradle/properties.gradle"
repositories {
jcenter()
}
dependencies {
println "In project, kotlinVersion is $kotlinVersion"
}
When you run ./gradlew dependencies, it would show you the populated versions:
> Configure project :
In buildscript, kotlinVersion is 1.1.61
In project, kotlinVersion is 1.1.61

Gradle: buildscript's resolution strategy

Is it possible to set the resolution strategy for the builscript, so that the version of a gradle plugin can be set centrally? For example:
build.gradle:
buildscript {
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin'
}
}
apply from: 'common.gradle'
apply plugin: 'spring-boot'
dependencies {
...
}
common.gradle:
allprojects { project ->
buildscript {
repositories {
jcenter()
}
}
configurations.all {
resolutionStrategy {
eachDependency { details ->
if (details.requested.group == 'org.springframework.boot' and details.requested.name == 'spring-boot-gradle-plugin')
details.useVersion '1.3.1.RELEASE'
}
}
}
}
I've tried about 100 different variants of the above, all result in an error saying the spring boot gradle plugin version can't be resolved (which is still empty)
A slightly different approach to centrally set the plugin version: use init.gradle
In your init.gradle:
allprojects {
ext.springBootGradlePluginVersion = '1.3.1.RELEASE'
println "spring-boot-gradle-plugin version set in init.gradle to $springBootGradlePluginVersion"
}
Then in your individual projects:
buildscript {
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootGradlePluginVersion"
}
}
apply from: 'common.gradle'
apply plugin: 'spring-boot'
You can either use a global init.gradle in your home folder, or invoke it per project while running gradle with the -I command line option.

Gradle Custom buildScriptRepository methods

I would like to make a custom buildScript repository method so I can easily reference our internal maven repo. Right now I'm required to declare a maven block everywhere we use our plugin.
Here is the current setup
buildscript {
repositories {
jcenter()
maven { url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
What I would like to do is something like this
buildscript {
repositories {
jcenter()
myReleaseRepo()
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
How can I make a method available to create a repository anywhere we may use the plugin in the future?
Another solution is to add custom methods on RepositoryHandler using some Groovy goodness. Just chuck this in ~/.gradle/init.gradle
RepositoryHandler.metaClass.myReleaseRepo = {
delegate.maven {
name 'myReleaseRepo'
url 'http://myNexus:8081/nexus/content/repositories/My-Release'
}
}
After that, you can use it just as you described:
buildscript {
repositories {
myReleaseRepo()
}
}
Metaclasses in Groovy are just great. The delegate in this case is pretty much like the javascript this. This code is essentially using the RepositoryHandler instance (delegate keyword) and just calling repositoryHandlerInstance.maven(name, url).
It is possible to add a repo to an init script which would then apply to all gradle invocations that use the init script - without having to individually declare your maven repo in each build.gradle.
Solution 1:
Partial solution, does not do exactly what you're asking for. In init.gradle:
allprojects{
buildscript{
repositories{
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
}
}
Then your build.gradle can skip buildscript repo declaration entirely:
buildscript {
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
Solution 2:
In fact, you can even move your buildscript classpath declaration to init and have the plugin apply to all projects that use the init script:
beefier init.gradle
allprojects{
buildscript{
repositories{
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
}
gives you a lighter build.gradle
apply plugin: 'my-plugin'
I tried to, but apparently you cannot move the apply line to init.gradle as well. see this defect.
Solution 3:
I retract what I said in the comment above, I figured out how to do exactly what you're asking for. Apparently you can create extensions for the buildscript block using the initscript. However I still prefer solution2, because it gives you a cleaner build.gradle.
To create a buildscript extension, in your init.gradle:
class customRepos {
def buildscript
customRepos(buildscript) {
this.buildscript = buildscript
}
void addMyRepo() {
buildscript.repositories {
maven{ url 'http://myNexus:8081/nexus/content/repositories/My-Release' }
}
}
}
allprojects {
extensions.create('myRepo', customRepos, buildscript)
}
which then allows you to do this in your build.gradle
buildscript{
myRepo.addMyRepo()
dependencies {
classpath 'com.example.plugin:my-plugin:1+'
}
}
apply plugin: 'my-plugin'

Resources