11

How to amend Gradle plugins {} management repository for custom plugins? is not duplicated in this post, because it does not cover use of flatDir.


Question

How do I use a Gradle plugin defined in a local JAR, using the new plugin {} semantics, instead of the deprecated apply() semantics?

Current Status

Not having any resolution, after posting the question and searching at considerable length, I filed an issue, wondering whether this use, which ought to be common and straightforward, is unsupported, either by design or oversight, within Gradle's revised plugin semantics.

Unfortunately, my report was closed, with no useful information provided.

I requested clarification in a new issue, but am still waiting.

I am frustrated, having expected that the community would be interested in at least discussing this problem.

If you can contribute information, please do so.

First Update

Following the clarification about the new style for configuring plugin sources, I updated my settings.gradle file to open with the following block. However, I regret that I see no improvement by this change alone. (For the plugin id field referenced in the build.gradle file, I have tried both the global ID published in the JAR metadata, and the basename of the JAR fie. Both fail equally.)

pluginManagement {
    repositories {
        gradlePluginPortal()
        jcenter()
        flatDir {
            dirs 'lib`'
        }
    }
}

The documentation explains how to use custom repositories, but appears to overlook the case of a trivial flat directory.

Second Update

I get some improvement if I add a version number to the JAR file and to the corresponding statement in the plugins {} block. In this case, the message becomes:

Plugin [id: 'plugin-id', version: '1.0.0'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (could not resolve plugin artifact 'plugin-id:plugin-id.gradle.plugin:1.0.0')
  Searched in the following repositories:
    Gradle Central Plugin Repository
    BintrayJCenter
    flatDir(/absolute/path/to/lib)

In this case, the directory is added to the list of sources searched.

It is strange that the .gradle.plugin suffix is being appended to my ID in the printed artifact. It is also strange that adding the version number to what is being searched for affects the list of places being searched.

So my project still cannot build. I appreciate any further help.

Original Background

I placed a JAR file containing a custom plugin definition in the lib directory of a project. With the build.gradle build file as below, the build runs successfully.

buildscript {
  repositories {
      flatDir {
          dirs 'lib'
      }
  }
}

apply plugin: 'plugin-id'

However, the apply() semantics are deprecated, favoring a plugins {} block, so I tried updating the build file as below.

plugins {
    id 'plugin-id'
}

repositories {
    flatDir {
        dirs 'lib'
    }
}

I understand that the plugins {} contents can draw from the repositories {} definitions.

However, the change creates a failure:

* What went wrong:
Plugin [id: 'plugin-id'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (plugin dependency must include a version number for this source)

Keeping the plugin {} block but moving the repositories {} block back into a leading buildscript {} block does not resolve the error. That is, the error persists even if I revert to the earlier version only replacing the apply() statement with the plugin {} block.

Finally, it has no effect to add to the repositories {} block a dependencies { classpath: ':jarname' } block, which some sources suggest is necessary, though I don't know why it would be.

brainchild
  • 754
  • 1
  • 7
  • 21
  • Your deprecated syntax solution doesn't work for me unless I add the `buildscript { dependencies { classpath '...' }}}` part. It is not enough to specify a repository in order to resolve a plugin, you also need to specify the jar file that provides it. Compare the flatDir repo to a maven central: gradle would not download every single jar out there unless you specify explicitly in which one to look for. – Alex Apr 14 '20 at 13:10
  • @Alex Please clarify "solution doesn't work". The case tests discovery of plugins. If the plugins are discovered, then the cases works. Offhand, I would think that class path is an orthogonal question relevant during the later stages of build and run. – brainchild Apr 14 '20 at 13:26
  • Doesn't work as in plugin fails to be resolved: `> Plugin with id 'plugin-id' not found.`. I'm not sure what test cases you're referring to. Wrt classpath, it applies to both compilation and runtime: classes your code refers to need to be resolved both when compiling your code and again when running it later. – Alex Apr 14 '20 at 13:48
  • Understand that class path is needed for compiling, but compiling is generating bytecode, whereas the immediate issue is plugin resolution. Is commenting and uncommenting the `classpath` block the only difference that accounts for the appearance or disappearance of the *pluign not found* error? – brainchild Apr 14 '20 at 13:59
  • > Is commenting and uncommenting the classpath block the only difference that accounts for the appearance or disappearance of the pluign not found error?: yes. I don't get why you're talking about plugin resolution as if it's any different than class file resolution: plugins are classes distributed in jar files (with the additional descriptor in META-INF). – Alex Apr 14 '20 at 14:14
  • @Alex Because class path is used by the compiler and VM to determine where to find class files. JAR files package class files. Plugin resolution finds the location of JAR files. A build process could find a JAR file without knowing to look inside that JAR file for some later compile or run stage. – brainchild Apr 14 '20 at 14:24
  • "Plugin resolution finds the location of JAR files." Couldn't disagree more. Plugins are classes, in order to find them one must inspect all JAR files on the classpath until a descriptor for that plugin is found, together with its _implementation class_. I agree that documentation is not explicit enough on this "Resolving a plugin means finding the correct version of the jar which contains a given plugin and adding it the script classpath." (from https://docs.gradle.org/current/userguide/plugins.html#sec:using_plugins) – Alex Apr 14 '20 at 14:57
  • @Alex Fair enough. The terminology may be overloaded or non-intuitive but what would seem relevant to the discussion seems to be, from the same document: "*Resolving* a plugin means finding the correct version of the jar which contains a given plugin and adding it the script classpath." – brainchild Apr 14 '20 at 16:09
  • @Alex the original comments (see question) included: "Finally, it has no effect to add to the `repositories {}` block a `dependencies { classpath: ':jarname' }` block, which some sources suggest is necessary, though I don't know why it would be." – brainchild Apr 14 '20 at 16:18
  • And I dutifully pointed out why you certainly need that clause. Regardless of the documentation, we must use different test approaches in irder to not agree on such fundamentals. This discussion is becoming a dead end. I will wait for others to elaborate. After all I have a solution with latest gradle and have long ago merged it in my projects. – Alex Apr 15 '20 at 08:38
  • You might actually be getting confused about importing dependencies from flat dir vs importing plugins, i.e. https://stackoverflow.com/a/20700183/410939 . Unlike dependencies which refer to the jar name, plugins do not, and therefore need an explicit pointer in which jar from the whole repo a plugin can be found. – Alex Apr 15 '20 at 09:02
  • Read more about Plugin Marker Artifact https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers – Alex May 08 '20 at 10:29

1 Answers1

5

This works. Tested with gradle 6.3.

build.gradle:

plugins {
    id 'plugin-id'
}

settings.gradle:

pluginManagement {
    buildscript {
        repositories {
            flatDir {
                dirs '/plugin-folder/build/libs'
            }
        }
        dependencies {
            classpath ':plugin-jar:0.0.1'
        }
    }
}

Update: I just found out today that it is possible to have your plugin jar resolved without using the dependencies block above. In that case you should name your plugin jar as [plugin-id].gradle.plugin[-version].jar. Note that the [-version] part is optional and plugin-id.gradle.plugin.jar will also work.

NB: Flat dir repositories are discouraged and local maven repo folder should be used instead. Especially in the case when you want to override locally an artifact which exists on a remote repo. See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver. It seems impossible to fully move away from maven in favour of gradle, considering that installing local artifacts in a maven repo folder is not supported by gradle itself.

Alex
  • 1,313
  • 14
  • 28
  • Since the version number reported in the response is more recent than that in the question, it would be useful to clarify the significance of version for the solution. – brainchild Apr 29 '20 at 08:06
  • is there something left preventing you from marking this response as an Answer? the same approach is reported here too: https://stackoverflow.com/a/61842621/410939 – Alex Oct 21 '20 at 16:02
  • Mostly that we still lack a clear indication of what makes your solution successful. Is it tool version, build layout, or something else? – brainchild Oct 21 '20 at 20:01
  • My suggestion is that it would be clearer and more helpful generally to develop a complete response directly in your answer, if, as you seem to suggest, you believe you have not done. If you are not so inclined, then so be it, but if you would ask (and you may not be asking) that I mark your answer as a solution based on content not contained within it, then I would hesitate. – brainchild Oct 24 '20 at 02:48
  • The solution I found is based on my inspecting gradle's source code. There's nothing more to explain than that. There's no specific material online outlining why you need the 'dependencies' block which you wrongly specify in your question as unnecessary. But you do, unless you go via the second approach in my answer. – Alex Oct 28 '20 at 10:43
  • Generally, an answer that receives a good appraisal is one that offers a robust understanding, through a clear structure, of a resolution, for someone starting with only the material in the question, and any assumptions or background carried into that question. I imagine you could rework your presentation to achieve such an effect. I, personally, though others may announce divergent opinions, do not experience such from the current form of your answer. – brainchild Oct 28 '20 at 21:03
  • 1
    E.g. "This works. Tested with gradle 6.3.": It's hard to gain a clear understanding of the scope of the issue based on such a casual train of remarks. – brainchild Oct 28 '20 at 21:07