2

I'd like to load my custom plugin from a local jar. The jar file compiles fine and when I check it, the manifest and the plugin class are there.

gradlePlugin {
    plugins {
        create("asdf") { // <-- I really call it "asdf" in the kts script
            id = "asdf"
            implementationClass = "pluginTest.TestPlugin"
            version = "1.4.0"
        }
    }
}

The plugin doesn't do anything useful yet as it should be a proof-of-concept to make sure it actually works at all:

class TestPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        println("Hallo TestPlugin!")
    }
}

I then try to use it like this in another project:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath(files("..\\..\\path\\to\\pluginTest.jar"))
    }
}

plugins {
    id("asdf") version "1.4.0"
}

but it keeps telling me that:

Plugin [id: 'asdf', version: '1.4.0'] was not found in any of the following sources:

What am I missing here? I use Gradle v6.5.

t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • 1
    Might be that the repositories sections requires a `flatDir` "repository"? I'm not used to dealing with local files, but I guess it might be similar to https://stackoverflow.com/questions/20700053/how-to-add-local-jar-file-dependency-to-build-gradle-file – Joffrey Jan 11 '21 at 11:20
  • 1
    Another option might be that the `plugins {}` block with versioned plugin doesn't work for plain files like this. Instead, maybe you can just `apply()` instead of the whole `plugins` block? They do it like this at the beginning of this tutorial: https://docs.gradle.org/current/userguide/custom_plugins.html#sec:writing_a_simple_plugin – Joffrey Jan 11 '21 at 11:46
  • @Joffrey I tried both suggestions. Unfortunatlly Gradle didn't buy them. I guess I'll have to setup a local maven after all :-( eh, this is so frustrating. – t3chb0t Jan 11 '21 at 11:54
  • 1
    @Joffrey you might want to see my answer ;-] it is possible to use jar-files. – t3chb0t Jan 11 '21 at 17:14

2 Answers2

3

With @BjørnVester's answer I figured it out!

You need to put the buildscript in settings.gradle.kts as it doesn't get executed in the build.gradle.kts even when placed before plugins.

buildscript {
    repositories {
        flatDir {
            dirs("..\\reusable-kotlin\\build\\libs") // <-- folder with jars
        }
    }
    dependencies {        
        classpath("com.hedev.kotlin:reusable-kotlin:1.4.0")
    }
}

But there's a catch! You must use the file-name of the jar in the classpath's name identifier that goes like this:

group:file-name:version

The file gradle will look for will be file-name-version.jar or file-name.jar which you'll see in the error message if you make a mistake (I added the _ on purpose to trigger the error):

Could not resolve all artifacts for configuration 'classpath'.
Could not find com.hedev.kotlin:reusable-kotlin_:1.4.0. Searched in the following locations:
- file:/C:/some/path/reusable-kotlin/build/libs/reusable-kotlin_-1.4.0.jar
- file:/C:/some/path/reusable-kotlin/build/libs/reusable-kotlin_.jar

In order for this to work I also had to add the group property to the plugin itself:

gradlePlugin {
    plugins {
        create("asdf") {
            id = "asdf"
            implementationClass = "com.hedev.kotlin.gradle.TestPlugin"
            version = "1.4.0"
            group = "com.hedev.kotlin"
        }
    }
}

Finally you can apply it in build.gradle.kts with (no version here):

plugins {
    id("asdf")
}
t3chb0t
  • 16,340
  • 13
  • 78
  • 118
2

When you have the plugin jar on the classpath, you can't have a version number in the plugin application. I guess this is because you can't have multiple jars with different versions on the classpath in the first place, so specifying a version here doesn't make any sense (except perhaps to validate that you are using the correct one). This won't fix the problem, but it is a start.

To be honest, I don't know why your approach still won't work. The buildscript block is supposed to set up dependencies for that particular script, and that should make the plugin visible to it. It doesn't for some reason.

Perhaps this is a bug or perhaps this is just an undocumented limitation on the use of the plugin {} block. Maybe you could ask over at the Gradle forums or create an issue for it. However, there are workarounds that don't involve publishing to a (local) Maven repository, which I agree can be a bit annoying.

If you use "apply from" instead of "plugins {}", it works. For some reason, the former can see the buildscript classpath whereas the latter can't:

// build.gradle (Groovy DSL)
buildscript {
    dependencies {
        classpath(files("..\\..\\path\\to\\pluginTest.jar"))
    }
}

apply from: "asdf"

Alternatively, move the buildscript plugin from the build.gradle file to the settings.gradle file. This makes is available to the entire build classpath and will make it work with the plugin block:

// settings.gradle (Groovy DSL):
buildscript {
    dependencies {
        classpath(files("..\\..\\path\\to\\pluginTest.jar"))
    }
}

// build.gradle (Groovy DSL)
plugins {
    id("asdf")
}

Lastly, just in case you haven't considered it already, you may be able to add the plugin as a composite build. This will create a source dependency to the plugin and has the advantage that transitive dependencies will be carried over (the ones you put in the plugin's own dependency block) and that it will be built automatically if not up-to-date. I use this approach for integration testing my plugins and also sometimes to apply them to my other real projects to test them in a bigger setting before publishing new versions.

Do that with either:

// settings.gradle (Groovy DSL):
includeBuild("..\\..\\path\\to\\plugin")

// build.gradle (Groovy DSL):
plugins {
    id("asdf")
}

Or without hard-coding it in the build (so you can dynamically switch between local and published versions):

// build.gradle (Groovy DSL):
plugins {
    id("asdf") version "1.4.0" // Version is optional (will be ignored when the command line switch below)
}

// Run with:
./gradlew --include-build "..\\..\\path\\to\\plugin" build
Bjørn Vester
  • 6,851
  • 1
  • 20
  • 20
  • Very informative, thanks for showing so many ideas! I finally made it to work with `apply(plugin="asdf")` (Kotlin DSL). By default with `from` it didn't work. I moved the `buildscript` around but without success so I put it back in the `build.gradle.kts` and started to experiment with `apply`. Then I jumped to the source and saw it has more than one argument, I gave it a shot and bam! :) – t3chb0t Jan 11 '21 at 15:19
  • I was curious about the difference between the two methods and this answer explains this pretty good: [What the difference in applying gradle plugin](https://stackoverflow.com/a/32353244/235671) Gradle didn't move the functionality to the new api yet. – t3chb0t Jan 11 '21 at 15:59
  • Unfreakinbelievable, see my answer, I found a way :-) – t3chb0t Jan 11 '21 at 17:12