How can I configure the Jib Extension inside a buildSrc kotlin file? - gradle

I'm trying to modularize my build.gradle.kts. It was suggested to me to create a buildSrc folder.
After some research and som asking I found this article I hated Gradle!... so this was my try:
buildSrc tree:
buildSrc/
├── build.gradle.kts
├── settings.gradle.kts
└── src
└── main
├── kotlin
│   ├── Docker.kt
│   ├── MyProjectExtensions.kt
│   └── Versions.kt
└── resources
└── META-INF
└── gradle-plugins
└── pt.branden.brandenportal.properties
My build.gradle.kts:
plugins {
`kotlin-dsl`
id("com.google.cloud.tools.jib") version Versions.jib
}
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
implementation("gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:${Versions.jib}")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50")
implementation(gradleApi())
implementation(localGroovy())
}
And finally Docker.kt:
import org.gradle.api.Plugin
import org.gradle.api.Project
open class JibConfigPlugin : Plugin<Project> {
override fun apply(target: Project) {
//configureJib()
TODO("not implemented")
}
}
//internal fun Project.configureJib() = this.extensions.getByType<JibExtension>().run {}
internal fun Project.configureJib() = project.configure<JibExtension>() {
TODO("not implemented")
}
My problem is that I can't find the JibExtension, so when I try to implement and configure the Jib it doesn't work but in the build.gradle.kts everything works.

My problem is that I can't find the JibExtension
Plugins or extensions can be applied in a variety of different ways. You can "react" to the plugin being applied by using the withPlugin method of PluginManager:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
// Configuration happens inside this Action block.
}
}
}
Using this method you can be certain that a plugin exists/has been applied without forcing a user/project to use the plugin.
The Jib plugin offers a single extension and a variety of tasks.
Configuring the extension can be done with the following:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
project.extensions.configure<JibExtension> {
// Example configuring the `container`
container {
creationTime = "USE_CURRENT_TIMESTAMP"
}
}
}
}
}
Looking over the source of the Gradle plugin for Jib, the authors used lazy configuration for the tasks, so it is best to also use the same method to configure those tasks.
For example, to configure the jib task:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
project.tasks.named<BuildImageTask>("jib") {
to {
setTargetImage("my_acr_name.azurecr.io/my-app")
}
}
}
}
}
The above uses the named method which returns a TaskProvider
Then simply apply your plugin as documented here: https://guides.gradle.org/writing-gradle-plugins/#apply_the_plugin_to_the_host_project
Source of the build.gradle.kts I used to test:
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
dependencies {
implementation("gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:1.7.0")
}

Related

Task 'run' not found in root project

My "build.gradle" file:
apply plugin: 'java'
sourceSets {
main {
java {
srcDir 'src'
}
}
test {
java {
srcDir 'test'
}
}
}
My "Main.java" file in "./src/" directory:
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) {
System.out.println("Hello Gradle");
}
}
I get this error:
Task 'run' not found in root project 'gradleNew'
Sorry for the stupid question... I didn't use Gradle before
The java plugin does not include the run task, which I suppose your gradle build requires somewhere in your app.
Change the java plugin to application and you should be fine.
To verify this, you can go to your project folder inside you terminal and run gradle tasks. You should see the run task listed among other tasks.
I hope that helps.
I just add this code and my app run:
plugins {
id 'application'
}
Try this instead:
group 'com.example'
version '1.0'
apply plugin: 'application' // implicitly includes the java plugin
sourceCompatibility = 1.11 // java version 11
repositories {
mavenCentral()
}
dependencies {
// your dependencies
}
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/resources']
}
}
}
I typically use something similar to this for my projects. You can then explicitly create the relevant dirs for src/test code, or sometimes the IDE will do it for you when you point it to the relevant build.gradle file (you can typically restart the IDE and it will pick up the file, or you can open a new project and select the build.gradle file as the project to open if it doesn't identify it right away).

Separate Gradle source set for integration tests using Kotlin DSL

I'm working on a Spring Boot application implemented in Kotlin, and would like to migrate the Gradle build to use the Gradle Kotlin DSL.
The one thing I cannot figure out is how to set up a separate source set and task for my integration tests.
My source tree looks like this:
src
├── integrationTest
│ ├── kotlin
│ └── resources
├── main
│ ├── kotlin
│ └── resources
└── test
├── kotlin
└── resources
And the source set and task are set up like this with Gradle's Groovy DSL:
// build.gradle
sourceSets {
integrationTest {
kotlin {
compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
I've found many examples for using the Gradle Kotlin DSL, and for additional source sets - but nothing for the combination.
Can anyone help?
Here's how you can translate the Groovy script to the Kotlin DSL:
java {
sourceSets {
val integrationTest by creating {
kotlin.apply {
compileClasspath += sourceSets["main"].output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
}
configurations["integrationTestCompile"].extendsFrom(configurations["testCompile"])
configurations["integrationTestRuntime"].extendsFrom(configurations["testRuntime"])
val integrationTest by tasks.creating(Test::class) {
val integrationTestSourceSet = java.sourceSets["integrationTest"]
testClassesDirs = integrationTestSourceSet.output.classesDirs
classpath = integrationTestSourceSet.runtimeClasspath
}
Also see: the Migrating build logic from Groovy to Kotlin guide by Gradle

Gradle: how to make rule-created ZipTask as maven publication artifact

I want to create a maven publication from inside a RuleSource that will be published via the maven-publish plugin. The artifacts of the publication are the outputs from a series of Zip tasks that are created from rules. When I try to add the artifacts, I get a circular rule exception.
Here is my very simple build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
}
}
task wrapper(type: Wrapper) {
gradleVersion = '3.3'
}
apply plugin: 'groovy'
apply plugin: 'testpub'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.7'
}
The testpub plugin exists in the buildSrc directory. To be able to apply it as above, it requires the following properties file:
// buildSrc/src/main/resources/META_INF/gradle-plugins/testpub.properties
implementation-class=TestPubPlugin
Here is the very simple plugin file:
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.model.RuleSource
import org.gradle.api.Task
import org.gradle.model.Mutate
import org.gradle.model.Finalize
import org.gradle.api.tasks.bundling.Zip
import org.gradle.model.ModelMap
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
class TestPubPlugin implements Plugin<Project> {
void apply(Project project) {
project.configure(project) {
apply plugin: 'maven-publish'
publishing {
repositories {
maven {
url "someUrl"
}
}
}
}
}
static class TestPubPluginRules extends RuleSource {
#Mutate
public void createSomeTasks(final ModelMap<Task> tasks) {
5.times { suffix ->
tasks.create("someTask${suffix}", Zip) {
from "src"
destinationDir(new File("build"))
baseName "someZip${suffix}"
}
}
}
#Mutate
public void configurePublishingPublications(final PublishingExtension publishing, final ModelMap<Task> tasks) {
// Intention is to create a single publication whose artifacts are formed by the `someTaskx` tasks
// where x = [0..4]
publishing {
publications {
mavPub(MavenPublication) {
tasks.matching {it.name.startsWith('someTask')}.each { task ->
artifact(task)
}
}
}
}
}
}
}
The plugin creates a number of tasks called someTaskx where x=[0..4]. They simply zip up the src directory. I want to add the output files as artifacts to the single MavenPublication. However, I get the following exception:
* What went wrong:
A problem occurred configuring root project 'testpub'.
> A cycle has been detected in model rule dependencies. References forming the cycle:
tasks
\- TestPubPlugin.TestPubPluginRules#createSomeTasks(ModelMap<Task>)
\- MavenPublishPlugin.Rules#realizePublishingTasks(ModelMap<Task>, PublishingExtension, File)
\- PublishingPlugin.Rules#tasksDependOnProjectPublicationRegistry(ModelMap<Task>, ProjectPublicationRegistry)
\- projectPublicationRegistry
\- PublishingPlugin.Rules#addConfiguredPublicationsToProjectPublicationRegistry(ProjectPublicationRegistry, PublishingExtension, ProjectIdentifier)
\- publishing
\- TestPubPlugin.TestPubPluginRules#configurePublishingPublications(PublishingExtension, ModelMap<Task>)
\- tasks
What is wrong and how do I fix it?
I don't fully understand why is this a "cycle", but the rule methods always have one mutable part (the subject) and zero or more immutable (the inputs). In your second method, you are passing the publishing as the subject you want to change and the tasks as the input. I thought that would be ok, but obviously it isn't.
You might have tried to switch the method arguments, pass the tasks first and then the PublishingExtension, but you would likely not be able to change it (as gradle docs say it's immutable).
I am not sure what exactly is your use case and there might be an easier solution that doesn't use the rules, or plugin at all. Maybe you could ask another question with the original requirement instead of this specific problem.
But back to your issue. The solution to your problem might be something like this:
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.bundling.Zip
import org.gradle.model.Defaults
import org.gradle.model.ModelMap
import org.gradle.model.Mutate
import org.gradle.model.RuleSource
class TestPubPlugin implements Plugin<Project> {
void apply(Project project) {
project.configure(project) {
apply plugin: 'maven-publish'
publishing {
publications {
maven(MavenPublication) {
groupId 'com.example'
artifactId 'artifact'
}
}
repositories {
maven {
url "someUrl"
}
}
}
}
}
static class TestPubPluginRules extends RuleSource {
static final def buffer = []
#Defaults
public void createSomeTasks(final ModelMap<Task> tasks) {
5.times { suffix ->
tasks.create("someTask${suffix}", Zip) {
from "src"
destinationDir(new File("build"))
baseName "someZip${suffix}"
}
}
tasks.each { task ->
if (task.name.startsWith('someTask'))
buffer << task
}
}
#Mutate
public void configurePublishingPublications(PublishingExtension extension) {
MavenPublication p = extension.publications[0]
buffer.each { task ->
p.artifact(task)
}
}
}
}
The hack here is to run the mutator of the tasks first (#Defaults phase should run before #Mutate) and save the tasks, so we don't need to ask for them later. Rules can include static final fields, so we use a list here.
Then we run the publication enhancer. The code you have used won't work. It works in the config part, but not in the groovy class. So I have prepared the publication and then just added the artifacts from the buffer.
I ran gradlew publish and got:
Execution failed for task ':publishMavenPublicationToMavenRepository'.
> Failed to publish publication 'maven' to repository 'maven'
> Invalid publication 'maven': artifact file does not exist: 'build\someZip0.zip'
So it seems it's working.

gradle common code for multiproject build

I have some common class definitions for a multiproject build in folder buildSrc:
root
build.gradle
settings.gradle
a/
build.gradle
b/
build.gradle
buildSrc/
common.gradle
where
settings.gradle says
include ':a', ':b'
a/build.gradle
project(":a") {
task someTask(type: CommonTask) {
println "Running some task"
}
}
buildSrc/common.gradle
class CommonTask extends DefaultTask {
#TaskAction
def someAction() {
println "Running common task"
}
}
According to docs:
https://docs.gradle.org/current/userguide/organizing_build_logic.html
buildSrc project. Drop the source for your build classes into a
certain directory and Gradle automatically compiles them and includes
them in the classpath of your build script.
However when running gradle build I get an error:
Could not find property 'CommonTask' on project ':a'.
So how can we have common class definitions for a multiproject build?
Edit
Adding apply from: "$rootDir/buildSrc/common.gradle" to project a does not help
Edit2
I put the file common.gradle to buildSrc/src/main/groovy/org/gradle/Common.groovy
and in the root projects' build.gradle
repositories {
mavenLocal()
}
dependencies {
compile group: 'org.gradle'
}
A problem occurred evaluating root project 'root'.
Could not find method compile() for arguments [{group=org.gradle}] on root project 'root'.
Edit 3
I have 2 files:
buildSrc/src/main/groovy/com/iggy/gradle/A.groovy:
package com.iggy.gradle
String someA() {
return "a"
}
buildSrc/src/main/groovy/com/iggy/gradle/B.groovy:
package com.iggy.gradle
String someB() {
String a = someA()
return a
}
But I get an error: Could not find method someA()
Shouldn't methods from same package be resolved? Adding #PackageScope or public doesn't help. So how can one import a method from another groovy file?

Why Does gradle eclipseClasspath task does not include transitive project dependencies?

I created a simple example(the names don't really mean anything):
folder structure:
gradleMultiProject
build.gradle
core
build.gradle
src
main
java
Core.java
database
build.gradle
src
main
java
Database.java
settings.gradle
webapp
build.gradle
src
main
java
Webapp.java
Below are the files mentioned above:
build.gradle (root gradle build file under gradleMultiProject folder)
subprojects {
apply plugin: 'java'
apply plugin: 'eclipse'
}
settings.gradle
include 'core', 'database', 'webapp'
gradleMultiProject\core\src\main\java\Core.java
package main.java;
public class Core
{
public static String HELLO_MESSAGE = "hello world!";
}
Core's build.gradle
task hello {
println "hello from core!"
}
gradleMultiProject\database\src\main\java\Database.java
package main.java;
public class Database
{
public void saveMessage()
{
System.out.println(Core.HELLO_MESSAGE);
}
}
Database's build.gradle
dependencies {
compile project(':core')
}
gradleMultiProject\webapp\src\main\java
package main.java;
public class Webapp
{
public static void main(String[] args)
{
Database database = new Database();
database.saveMessage();
System.out.println(Core.HELLO_MESSAGE);
}
}
Webapp's build.gradle
dependencies {
compile project(':database')
}
Gradle compiles everything just fine(gradle build). It even lists the transitive dependencies just fine(gradle dependencies). But when I try to generate a .classpath file(gradle eclipseClasspath) for the webapp project it doesn't include core. WHY?!?!?!?!?!
Actually this isn't a problem because eclipse "exports" core in database. What that means is that it will show up in any project that require the database project.
I just created this exact project and ran "gradle eclipse" and there are no build errors because the webapp project requires the database project and the database project requires and exports the core project. To see more look at the build path in eclipse.

Resources