How to Make a Gradle Plugin and Library Coexist in One Project
When developing an Android Library that also needs a corresponding Gradle Plugin, it is not easy for Gradle beginners to integrate an Android Library, a Gradle Plugin, and an Example App all in a single Gradle project. The main issue is that the Android App module cannot reference a Gradle Plugin module within the same project, because the Gradle Plugin must be configured and compiled before all other modules. As a result, many engineers develop and publish the Gradle Plugin as a separate project. For those who frequently develop and debug Gradle Plugins, this is extremely painful – every change to the Gradle Plugin requires publishing to Maven Local first, then debugging across projects. The efficiency is terrible. So is there an elegant solution?
The buildSrc Module
To let an Android App reference a Gradle Plugin module in the same project, the Gradle Plugin module cannot be a regular module. Fortunately, Gradle provides the buildSrc mechanism – Gradle automatically compiles and tests the buildSrc directory under the project root as a Composite Build, and adds its output to the buildscript classpath. This makes the Gradle Plugin defined in buildSrc accessible.
Although referencing the Gradle Plugin is solved, publishing the Gradle Plugin module and the Android Library module from the same project is still a problem. For buildSrc, even though it is in the same project, Gradle treats it as a completely isolated project. This means:
- buildSrc modules and regular modules cannot be published simultaneously
- All configurations and properties cannot be shared between buildSrc and regular modules, e.g., version numbers
So how do we solve these problems?
The Shadow Module
To have our cake and eat it too, we must turn the buildSrc module into a regular module. We can create an additional Java/Kotlin Library module to handle the Gradle Plugin publishing problem. The project structure looks like:
1 | . |
But the question is, how do we let this regular module share code from buildSrc?
Symbolic Links
Those familiar with Linux might think of a convenient approach – symbolic links (soft links), somewhat like “shortcuts” in Windows. We can create a src symlink in the plugin module pointing to buildSrc/src using the ln command:
1 | $ cd plugin |
The project structure then looks like:
1 | . |
This neatly solves the code-sharing problem between a regular Gradle module and the buildSrc module.
Source Set
Due to cross-platform differences – for example, it is uncertain whether Windows and *nix systems both handle symlinks well (I have not tested on Windows) – is there a more compatible solution? The answer is yes: Source Set. We can configure an additional SourceSet for the plugin module:
1 | sourceSets { |
This perfectly solves all the problems. For the version number issue, we can configure it uniformly through allprojects or subprojects in the root build.gradle.kts:
1 | allprojects { |
So the plugin module handles Gradle Plugin publishing, while buildSrc handles Gradle Plugin referencing within the same project. The plugin module and the buildSrc module share the same code, except for build.gradle.kts, which has slight differences:
- Since buildSrc is a completely independent project, using the
pluginsDSL to enable plugins requires specifying the version number - The plugin module does not need to specify the version number in the
pluginsDSL (because it is already specified in the root build.gradle.kts) - The buildSrc module does not need publishing, so it does not need to enable publishing-related plugins like
maven-publish
buildSrc/build.gradle.kts
1 | plugins { |
plugin/build.gradle.kts
1 | plugins { |
- Blog Link: https://johnsonlee.io/2021/10/06/integrate-gradle-plugin-and-library-into-one-project.en/
- Copyright Declaration: 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
