I'm writing a custom plugin which adds some data to manifest of Java project.
It looks something like this:
package com.example.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
public class ExamplePlugin implements Plugin<Project> {
def apply(Project project) {
project.jar() {
manifest {
attributes 'buildServer': checkIfIsBuildServer()
attributes 'personalBuild': checkIfIsPersonalBuild()
}
}
}
def checkIfIsBuildServer() {
return 'some result'
}
def checkIfIsPersonalBuild() {
return 'some result'
}
}
When I'm trying to apply it to some project, I get an error:
Could not find method jar() for arguments [com.example.gradle.ExamplePlugin$_apply_closure1#411e4f5e] on project ':SomeProject' of type org.gradle.api.Project.
I am reasonably sure this is some missing import. I don't have any idea how to determine what import it should be.
jar() isn’t a method on Project.
If I’m understanding your code correctly, what you are trying to do is configure the jar task that is created from the Java Plugin.
So you need to:
Get the task
Configure the task
Something like:
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Jar
public class ExamplePlugin implements Plugin<Project> {
def apply(Project project) {
project.afterEvaluate {
project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar) {
it.manifest {
attributes 'buildServer': checkIfIsBuildServer()
attributes 'personalBuild': checkIfIsPersonalBuild()
}
}
}
}
def checkIfIsBuildServer() {
'some result'
}
def checkIfIsPersonalBuild() {
'some result'
}
}
I highly recommend switch to Kotlin or Java for your plugin. It will make errors like this trivial to resolve and you will fully understand where things are coming from compared to Groovy’s dynamic nature.
Related
I've got a method in my Gradle plugin which declares a new "scratch" source set. I'm trying to convert this method from Groovy to Kotlin, but I cannot figure out how I'm meant to declare the new SourceSet using Kotlin.
class JavaConventionsPlugin : Plugin<Project> {
// ...
def scratchConfiguration(project) {
project.sourceSets {
scratch {
}
}
project.configurations {
// make scratch configurations include all normal dependencies automatically
scratchCompile.extendsFrom mainCompile
scratchRuntime.extendsFrom mainRuntime
scratchCompileOnly.extendsFrom compileOnly
scratchImplementation.extendsFrom implementation
scratchRuntimeOnly.extendsFrom runtimeOnly
}
project.dependencies {
// add dependency on main java code from scratch java code
scratchImplementation project.extensions.getByType(JavaPluginExtension).sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).output
}
}
}
Within the converted scratchConfiguration method, all of these seem to be unresolved references:
sourceSets
project.sourceSets
java.sourceSets
So what should the new SourceSet declaration look like?
They are unresolved because you are moving from a weakly typed/dynamic language (Groovy) to a strongly typed one (Kotlin). At its core, Gradle is written in plain Java. So, you need to make use of the Java APIs instead of the syntactic sugar that the Groovy DSL had provided.
Your snippet above would moreorless translate to the following:
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer
class ScratchGradlePlugin: Plugin<Project> {
override fun apply(project: Project) {
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
val main = sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME)
sourceSets.register("scratch") {
it.compileClasspath.plus(main.get().output)
it.runtimeClasspath.plus(main.get().output)
}
val configurations = project.configurations
configurations.named("scratchImplementation") {
it.extendsFrom(configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).get())
}
configurations.named("scratchRuntimeOnly") {
it.extendsFrom(configurations.named(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME).get())
}
}
}
I have omitted the project.dependencies {} part in your snippet since the sample above has scratch extend from main which is your main Java code.
Ref: https://docs.gradle.org/current/userguide/java_gradle_plugin.html#java_gradle_plugin
I have directory structure as below:
src
--foo
--A.groovy
--test
--groovy
buildTest.groovy
This is the A.groovy class :
package foo
class A {
void execute() {
println "Inside A"
}
}
This my buildTest.groovy
class buildTest {
#Test
void should_execute(){
A a = new A()
def result = a.execute()
}
}
When I run ./gradlew test, it throws error :
Unable to resolve class A
A a = new A()
^
This is my build.gradle :
plugins {
id "groovy"
}
repositories {
mavenCentral()
maven { url 'https://repo.jenkins-ci.org/releases/' }
}
dependencies {
compile 'org.eclipse.hudson:hudson-core:3.2.1'
implementation 'org.codehaus.groovy:groovy-all:3.0.7'
testImplementation 'junit:junit:4.12'
testImplementation "com.lesfurets:jenkins-pipeline-unit:1.3"
}
sourceSets {
main {
groovy {
srcDirs = ['src/foo']
}
}
}
Could someone help me in resolving this error.
You have declared a class A in package foo. (Note that the layout doesn't reflect this as the file should be in src/foo/foo/A.groovy, although Groovy doesn't really care).
However, the test class is not in a package and therefore can't see foo.A. Either import it or move the test class to package foo as well. Alternatively, remove the package declaration from class A, though I don't think that is good practice.
I also agree with the comment about not changing the layout unless you have a very good reason for it (e.g. you are working on a legacy project). This is probably what has caused confusion about what package the class is actually in.
Tried to understand how to create custom plugins. But I can't get my new task working. I get this error:
Failed to apply plugin [id 'code4reference']
No such property: printTask for class: libs.gradle.NewPlugin
But it's working when I run println from NewPlugin class.
There are two groovy files, one for plugin initialization and one for task. They are located:
/home/boilerplate/boilerPlate/src/main/groovy/libs/gradle/build.groovy
package libs.gradle;
import org.gradle.api.*;
apply plugin: NewPlugin
class NewPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('myTask', type: printTask)
}
}
/home/boilerplate/boilerPlate/src/main/groovy/libs/gradle/printTask.groovy
package libs.gradle;
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class printTask extends DefaultTask {
#TaskAction
def showMessage() {
println '------------showMessage-------------------'
}
}
Implementation:
/home/boilerplate/boilerPlate/src/main/resources/META-INF/gradle-plugins/code4reference.properties
implementation-class=libs.gradle.NewPlugin
Update:
Now I faced another issue when executing my new task that should sign all RPMs found on path. I know this works just fine when in build.gradle:
package libs.gradle;
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
class Sign extends DefaultTask {
def workdir = project.projectDir.getPath()
#TaskAction
def showMessage() {
ext.signfiles = files{
fileTree(dir: "$workdir").matching{ include 'build/**/*.rpm' }
}
doLast {
signfiles.each{ File file->
def args = /echo -e "spawn rpm --resign ${file}\nexpect \"Enter pass phrase:\"\nsend \"\\r\"\nexpect" | expect/
exec {
commandLine 'bash', '-c', "$args"
}
}
}
}
}
Could not find method files() for arguments [libs.gradle.Sign$_showMessage_closure1#4bf8b77] on task ':myTask' of type libs.gradle.Sign.
ext.signfiles = project.fileTree(dir: "$workdir").matching{ include 'build/**/*.rpm' }
But not sure what workdir is.
The exec below should also be
project.exec {
commandLine 'bash', '-c', "$args"
}
Can a groovy class (located in buildSrc/src/main/groovy) access the project directly, or does the project have to be passed in explicitly?
I am able to access the project by explicitly passing it in as a method parameter, but I do not want to have to pass it in. For an example, I would like to be able to get access to the project via a static method call. Is this type of implicit access possible?
Explicit Access
import org.gradle.api.Project
class MyClazz {
static void foo(Project project) {
println project.version
}
}
Task in build.gradle
task foo() << {
MyClazz.foo(project)
}
Implicit Access via Static Method Call (this is the desired access pattern)
import org.gradle.api.Project
class MyClazz {
static void foo() {
println Project.getProject().version
}
}
Task in build.gradle
task foo() << {
MyClazz.foo()
}
You can use Groovy extension methods to do this.
here's a self-contained example, but should work with Gradle too:
class Project {
// we add this method dynamically
//static getProject() { [ version: 2.3 ] }
}
class MyClazz {
static void foo() {
println Project.getProject().version
}
}
class Gradle {
static def main(args) {
Project.metaClass.static.getProject = { [ version: 4.2 ] }
MyClazz.foo()
}
}
I'm trying to write a plugin which adds dependencies to project.dependencies according to informations gathered in the plugin extension object. But it seems to be impossible.
Indeed, the data from extension object is only available in a new task or in project.afterEvaluate closure, but dependencies added in those places are ignored.
The following code tries to add the dependency in afterEvaluate but the dependency is ignored :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
project.afterEvaluate {
def version = project.myplugin.version
project.dependencies.add("compile", "org.foo:bar:$version") // --> ignored
}
}
}
class MyPluginExtension {
def version
}
In the following code the dependency injection works but I don't have access to the extension object :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
def version = project.myplugin.version // == null
project.dependencies.add("compile", "org.foo:bar:$version") // --> fail because $version is null
}
}
class MyPluginExtension {
def version
}
Is there a solution ?
Update: I managed to figure this out since my original answer. The way do this is to add a DependencyResolutionListener in which you add the dependencies and then remove the listener so it doesn't try to add them on later resolution steps.
compileDeps = project.getConfigurations().getByName("compile").getDependencies()
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileDeps.add(project.getDependencies().create("org.foo:bar:$version"))
project.getGradle().removeListener(this)
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {}
})
I have a working example of a plugin that uses this here
Original Answer:
This is also late but for anyone dropping in. With the latest gradle (2.6 at the time of writing), you can add a DependencyResolutionListener and add any dependencies before dependencies are resolved.
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
depsToAdd.each { dep ->
compileConfig.getDependencies()
.add(project.getDependencies().create(dep))
}
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
}
})
However, as of this writing I was having some issues getting this to work with Android Studio IDE. The problem is tracked in my question here
I originally implemented this solution using the DependencyResolutionListener approach by Saad. However, the listener itself is called only when something iterates over the configuration associated with the dependency. For example, if you want to dynamically add a dependency to compile, you have to make sure that something later on does something like:
project.configurations.compile.each {
...
}
But this is something that happens as a matter of course, since compile is a known configuration for any project that uses the java plugin. However, if you are using a custom configuration (as I was), then the listener approach won't work unless you explicitly iterate over your custom configuration.
I was able to find a better way to do this, and within afterEvaluate as the OP originally wanted. I'm using a custom configuration here, but I don't see a reason why it wouldn't work for compile either:
project.afterEvaluate {
def version = project.myPlugin.version
project.configurations.myConfig.dependencies.add(
project.dependencies.add("myConfig", "org.foo:bar:$version")
)
}
Of course, at some point something still has to iterate over the dependencies for them to actually get resolved.
The easiest way to do this:
project.dependencies {
delegate.compile("com.android.support:appcompat-v7:25.0.1")
}
Don't know if that's still relevant, but you can workaround this by explicitly adding your compile configuration to Java classpath in doFirst:
variant.javaCompile.doFirst {
variant.javaCompile.classpath += project.configurations.myconfiguration
}