i'm new in gradle and want to create custom gradle plugin that applies maven-publish plugin. Also my plugin should configure maven-publish plugin so that another plugin user should do nothing. And my plugin will automaticly configure maven-publish.
I've tried to find any same tutorial but doesn't find.
How can I configure maven-publish gradle plugin from my custom plugin?
Configuring other plugins from a custom plugin is extremely common. You should be able to reference any custom plugin out there for examples. For maven-publish specifically, I have created the following example:
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.publish.PublishingExtension;
import org.gradle.api.publish.maven.MavenPublication;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import java.net.URI;
public class MyPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getPluginManager().apply(MavenPublishPlugin.class);
project.getExtensions().configure(PublishingExtension.class, publishing -> {
publishing.repositories(repositories -> {
repositories.maven(maven -> {
maven.setUrl(URI.create("https://my-publishing-repo.com"));
});
});
publishing.publications(publications -> {
publications.create("mavenJava", MavenPublication.class, mavenJava -> {
mavenJava.artifact(project.getTasks().named("bootJar"));
});
});
});
}
}
This is equivalent to the following in the Gradle build file (Kotlin DSL):
plugins {
`maven-publish`
}
publishing {
repositories {
maven {
url = uri("https://my-publishing-repo.com")
}
}
publications {
create<MavenPublication>("mavenJava") {
artifact(tasks.named("bootJar").get())
}
}
}
Refer to the following guides from my guidance:
Developing Custom Gradle Plugins
Implementing Gradle plugins
Build Init Plugin -- for generating Gradle plugin project
Related
I created a simple gradle plugin according to the documentation.
class MyBuild implements Plugin<Project> {
#Override
void apply(Project project) {
project.task("test") {
doLast{
println 'Yeaaa boy!'
}
}
}
}
apply plugin: MyBuild
But the plugin does not appear at the Idea Gradle toolar:
I have also tried to add the following code, but it does not make any diffirence:
idea {
project {
jdkName = '1.8'
languageLevel = '1.8'
}
apply plugin: MyBuild
}
What should I do to make it appear there?
What exactly do you expect to see there? For example, I see the custom task, that the plugin adds here, in Gradle tool window:
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.
Is there an option to get Maven dependency in Gradle without using custom configuration for it? For example in custom plugin to just obtain dependency provided from extension? Something like
class DependencyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("deps", DepsExtension)
project.task('useDependency') {
doLast {
//use Gradle api to resolve dependency without custom configuration
project.resolve(project.deps.dependency)
}
}
}
}
class DepsExtension {
def dependency = 'custom:maven:1.0'
}
Something like this:
Configuration config = project.configurations.create('myPrivateConfig')
Dependency dep = project.dependencies.create('custom:maven:1.0') {
exclude group: 'foo', module: 'bar'
}
config.dependencies.add(dep)
Set<File> files = config.files
I do a similar thing in a gradle plugin here
References
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/DependencySet.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html
I am creating a gradle plugin to apply the sonar-runner plugin and default many of the values such as the sonar host URL and the sonar JDBC URL. I cannot figure out how to set the properties though.
When I set this up in build.gradle I use:
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
My gradle plugin looks like:
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin: 'sonar-runner'
project.configurations {
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
}
}
With this setup I get a No signature of method exception. How should I be setting these properties?
I discovered that I could use project.getExtensions().sonarRunner.sonarProperties{ ... } to set the sonar properties. See example below.
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin:'sonar-runner'
project.getExtensions().sonarRunner.sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
Thank you #mikerylander and #ravikanth! I also had tried the setProperty and .properties solutions but they didn't work for me.
The really tricky thing was that autocomplete did not find the "sonarqube" portion of project.getExtensions().sonarqube.properties for me so I never got to this solution without your post.
I wrote a custom Gradle plugin to run sonarqube for a multi-module Android project and your post helped me. Below is my full custom plugin. Since the plugin is designed to be included in the build.gradle of any submodule of my Android project I prepended "my_product" ${project.path} but of course you can use any values here.
Here is my complete plugin code in case its helpful:
package com.example.gradle.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
class MySonarCodeCoveragePlugin implements Plugin<Project> {
private Project project
void apply(Project project) {
this.project = project
project.apply plugin: 'org.sonarqube'
project.getExtensions().sonarqube.properties
{
property "sonar.sources", "${project.projectDir}/src/main"
property "sonar.organization", "my_org"
property "sonar.projectKey", "my_product${project.path}"
property "sonar.projectName", "my_product${project.path}"
property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml"
property "sonar.scanner.metadataFilePath", "${project.buildDir}/sonar/report-task.txt"
}
}
}
I'm trying to encapsulate android plugin in my own plugin, but when I'm trying to apply my plugin build fails with an exception:
A problem occurred evaluating root project 'myproj'.
> Failed to apply plugin [id 'com.mycomp.build']
> Failed to apply plugin [id 'android-library']
> Plugin with id 'android-library' not found.
Here is how I'm applying android plugin inside my own plugin's implementation:
// build.gradle
apply plugin: 'groovy'
version = '1.0'
group = 'com.mycomp'
dependencies {
compile gradleApi()
compile localGroovy()
}
// Build.groovy
package com.mycomp
import org.gradle.api.Plugin
import org.gradle.api.Project
class Build implements Plugin<Project> {
void apply(Project project) {
println 'Hello from com.mycomp.Build'
project.beforeEvaluate {
buildscript.configurations.classpath +=
'com.android.tools.build:gradle:1.0.0-rc1'
}
project.configure(project) {
buildscript.repositories.mavenCentral()
apply plugin: 'android-library'
}
}
}
For some reason a classpath is not being properly loaded, what am I doing wrong?
I guess that at the time you'd like to add the plugin dependencies for the build script have been already resolved, thus it won't work that way. You need to specify the plugin You'd like to apply as a script dependency itself.
It will work that way:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0-rc1'
}
}
apply plugin: 'groovy'
apply plugin: Build
version = '1.0'
group = 'com.mycomp'
dependencies {
compile gradleApi()
compile localGroovy()
}
import org.gradle.api.Plugin
import org.gradle.api.Project
class Build implements Plugin<Project> {
void apply(Project project) {
project.configure(project) {
apply plugin: 'android-library'
}
}
}
Now, android-plugin is found but it fails because of the fact that groovy plugin had been applied earlier and there's a conflict.
Use the project's PluginManager. For example, the war plugin pulls in the java plugin like this:
public class WarPlugin implements Plugin<Project> {
// ...
public void apply(final Project project) {
project.getPluginManager().apply(org.gradle.api.plugins.JavaPlugin.class);
// ...
}
// ...
}