Get a custom Gradle plugin's version from its own code - plugins

I wrote a Gradle plugin, its version is specified in its build script.
It is possible for this plugin to be aware of its own version when someone is using it? (i.e. when its apply(Project project) method is called)

For my plugins, I embed a field into the MANIFEST.MF file called Implementation-Version during build. Then I read that field in during runtime, by accessing the package like this:
def pkg = MyPlugin.class.getPackage()
return pkg.implementationVersion
Or using a helper class like: https://github.com/nebula-plugins/nebula-core/blob/master/src/main/groovy/nebula/core/ClassHelper.groovy#L16 to grab arbitrary field from the manifest.

You can also find the version by doing this:
def selfVersion = project.buildscript.configurations.classpath.resolvedConfiguration.resolvedArtifacts.collect {
it.moduleVersion.id }.findAll { it.name == '<name of plugin>' }.first().version

Related

Gradle DSL - Eclipse Equivalent for IDEA Module Property

Good localtime,
I am in the process of updating legacy (4.8.1) Gradle build files for a big-McLarge-huge, multimodule project. We utilize an intellij.gradle file which has the following line (marked by comment):
idea {
module {
inheritOutputDirs = true // <-- HOW DO I DO THIS
downloadJavadoc = true
downloadSources = true
}
workspace.iws.withXml { provider ->
def node = provider.asNode()
def dynamicClasspath = node.component.find { it."#name" == "dynamic.classpath" }
if (dynamicClasspath != null) {
dynamicClasspath."#value" = "true"
}
}
From the 4.8.1 DSL docs:
If true, output directories for this module will be located below the
output directory for the project; otherwise, they will be set to the
directories specified by IdeaModule.getOutputDir() and
IdeaModule.getTestOutputDir().
Any ideas on what the Eclipse DSL equivalent of inheritOutputDirs? Should this be handled using the eclipseClasspath API? Right now everything is building fine, but the Eclipse Java builder is is flagging things.
References:
https://docs.gradle.org/4.8.1/dsl/org.gradle.plugins.ide.idea.model.IdeaModule.html
https://docs.gradle.org/4.8.1/dsl/org.gradle.plugins.ide.eclipse.model.EclipseClasspath.html
Usually this would have been picked up through sourceSets but I can't see what your project looks like...
If your subproject uses Gradle to generate sources into /build/cxf/generated-sources directory, then you can tell Eclipse via Gradle DSL to include that as a source folder like this:
plugins { id 'eclipse' }
eclipse.classpath.file.whenMerged {
// this is the brute-force approach; there is likely a better way to add a source folder
entries << new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/cxf/generated-sources', null)
}
Once this is run (via gradle eclipseClasspath) you should see a build/cxf/generated-sources folder under your project node in the Package Explorer or Project Explorer. Sort of like this:
NOTE: This is untested because I don not have a sample project to work with.
There is more discussion here: How to add gradle generated source folder to Eclipse project?

Get relative path from inside a scala library (while developing that library)

I'm currently developing a scala library and wanted to get a file that is inside it, event when compiled as a Jar dependency. The problem is that when executed from another project where the library is imported, the path is relative to that project. Here is the code to get the file :
private val pathToDocker: Path = Paths.get("src", "main", "resources", "docker-compose")
What can I do to look for my file inside the imported dependency ?
The file won't be in a file system -- it'll be in the compiled JAR.
You can get a JAR resource using a class loader. Here's an example of how to do that in another codebase.
Utility function:
https://github.com/hail-is/hail/blob/6db198ae06/hail/src/main/scala/is/hail/utils/package.scala#L468
Usage to load version info:
https://github.com/hail-is/hail/blob/6db198ae06/hail/src/main/scala/is/hail/package.scala#L21
There is a JarUtil - from an answer of access-file-in-jar-file translate to Scala:
import java.util.jar.JarFile
val jar = new JarFile("path_to_jar/shapeless_2.12-2.3.3.jar")
val entry = jar.getEntry("shapeless/Annotation.class")
val inStream = jar.getInputStream(entry)
As you mentioned scala.io.Source.fromResource works only within your project:
Source.fromResource("tstdir/source.txt")
Make sure the file is in the resources directory, like:
The way I found to achieve getting the path inside a jar depedency was creating a singleton (scala object) that has a method that loads the files. Since it is inside the Jar, the resulting path is relative to the jar itself. Here is the code :
object ClassLoaderTest {
def dockerFile: File =
new File(getClass.getClassLoader.getResource("docker/docker-compose.yml").getPath)
}
When called from a Trait (or an interface), the resulting path is :
jar:file:/home/myname/.ivy2/cache/com.mypackage/libraryname/jars/libraryname.libraryversion.jar!/docker/docker-compose.yml

dagger android support to androidx.fragment

How to inject a fragment from the package androidx.fragment.app.Fragment ?
I'm using the dagger-android framework to inject my dependencies in my code.
As the documentation says I do this to inject my fragment
#Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
// ...
}
the problem is that AndroidSupportInjection class accept only fragments of the package android.support.v4.app.Fragment or if I use AndroidInjection class only accept fragments of the package android.app.Fragment and I want to use fragments of the androidx.fragment.app.Fragment package.
Also DaggerFrament extend from android.support.v4.app.Fragment and want to use a fragment from androidx
And If I try to implement HasSupportFragmentInjector also this interface use a fragment from android.support
add the below code to your gradle.properties
android.useAndroidX=true
android.enableJetifier=true
And if you are trying to inject into a Fragment you have to replace AndroidInjection.inject(this) with AndroidSupportInjection.inject(this)
I had the same problem in case of HasFragmentInjector. You need to use HasSupportFragmentInjector for fragment injection. This is because, HasFragmentInjector uses android.app.Fragment which is not effected by jetifier. You need to add android-dagger-support library, jetifier converts all the support packages to androidx in Studio 3.3 (if jetifier is enabled).
If jetifier does not change support packages to androidx packages. You can download jetifier tool from here and convert the android-dagger-support.aar file manually by using the following command.
./jetifier-standalone -i dagger-android-support-<version>.aar -o <output-name>
Then add the library to your project. This is the HasSupportFragment class after conversion
import androidx.fragment.app.Fragment;
import dagger.android.AndroidInjector;
public interface HasSupportFragmentInjector {
AndroidInjector<Fragment> supportFragmentInjector();
}
Somehow, jetifier tool was not converting libraries in AndroidStudio.
I had to do it manually.
I had a similar error and it was due to the Dagger version. On version 2.17 there is an strange issue, but if you roll back to version 2.16 it compiles perfectly (apart from the flags on gradle.properties that Paul posted).
From there using the tutorials you won't have trouble. Forgot to mention that on my project I had the non-androidX version of everything, then I ran the androidX migration that android studio offers, and after that I had to switch the Dagger version, but I suppose that if you do it from the start it's the same.
Hope this helps, if you switch and it doesn't work, post a little bit of your dagger implementation and plugins versions and I will try to help more!
Add the following to your gradle.properties file
android.useAndroidX = true
android.enableJetifier = true
Just for reference. i had the same problem. It was Jetifier issue. please upgrade your gradle build tools plugin to 3.3.0
classpath 'com.android.tools.build:gradle:3.3.0'
Sample code: https://github.com/jega-ms/android-dagger2-mvp-rx
This is what I did to work with androidx namespace for Dagger 2.21
Downloaded the jetifier tool from here: https://developer.android.com/studio/command-line/jetifier
Unzip into a folder and open a terminal pointing into the extracted bin folder
From Android Studio, open a class like DaggerFragment to check the path where the file is stored, for me (in MacOS) is something like this:
From terminal execute this command (replacing with the correct variables and path)
./jetifier-standalone -i /Users/{YOUR_USER}/.gradle/caches/{PATH_TO_DAGGER_ANDROID_SUPPORT_FOLDER}/dagger-android-support-2.21.aar -o dagger-android-support-2.21.aar
The converted dagger-android-support-2.21.aar will appear in your bin folder
Now open your app build.gradle file and change this line
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
adding the , '*.aar' part in the include array
Move the generated dagger-android-support-2.21.aar from bin to libs folder inside your project.
Remove (or comment) this line from the dependencies
implementation "com.google.dagger:dagger-android-support:2.21
Now you can proceed invalidating the cache and rebuild the project and now DaggerFragment will point to your converted version which uses androidx.fragment.app.Fragment
NOTE: Obviously this is a temporary workaround and you should move to the official version as soon this is fixed in Dagger
The solution to my particular problem was to use android dagger classes as interfaces instead of extend of them:
class MyFragment() : HasSupportFragmentInjector {
#Inject
lateinit var childFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return childFragmentInjector
}
........
}
To my Activities
class MyActivity : HasSupportFragmentInjector {
#Inject
internal lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentInjector
......
}
and also I have this in my gradle.properties file:
android.useAndroidX = true
android.enableJetifier = true

Gradle init project for Scala application

I want to create a skeleton console app for Scala i.e. a single entry class with a main function that prints "Hello world".
I was able to create a Scala library init project by executing:
gradle init --type scala-library
however there seems to be no scala-application, running:
gradle init --type scala-application
The requested build setup type 'scala-application' is not supported. Supported types: 'basic', 'groovy-application', 'groovy-library', 'java-application', 'java-library', 'pom', 'scala-library'.
Is there no Scala console app template for Gradle?
No, there is no scala application template, however it is easy to make your generated "scala-library" project into a scala application in two steps:
Create your main class and method under a package under src/main/scala
Add two lines to your gradle.build, to add the 'application' plugin and specify your main class.
For example, you add src/main/scala/com/example/myapp/Main.scala:
package com.example.myapp
object Main {
def main(args: Array[String]): Unit = {
println("Hello")
}
}
Then to your gradle.build you add:
apply plugin: 'application'
mainClassName='com.example.myapp.Main'
On top of what you have for Java you pretty much only need to add apply plugin: 'scala' and change Main to be:
object Main extends App {
println('Hello, World!')
}
I ended up doing just that.
Additionally, if you want joint compilation (and have compiler understand Java and Scala in the same place) you can add the below snippet (one block per source set: main, test, jmh, etc.):
sourceSets.main {
java.srcDirs = []
scala.srcDirs = ['src/main/scala', 'src/main/java']
scala.include '**/*.*'
}
sourceSets.test {
java.srcDirs = []
scala.srcDirs = ['src/test/scala', 'src/test/java']
scala.include '**/*.*'
}
As of Gradle 6.7 the scala-application build type exists now.

Custom Gradle Plugin ID not found

I'm writing a Gradle plugin and I'm failing to get the apply plugin: command to work in the Gradle script that uses the plugin. I'm using Gradle 1.1.
I've build the plugin with clean build and I'm attempting to add it to the Gradle build via a flat repo for now. That seems to be working but Gradle isn't picking up that there is a plugin with the ID test-plugin. The project name in the plugin's settings.gradle is test-plugin and the properties file in META-INF/gradle-plugins is also test-plugin.properties. I'm not sure where else I can specify the plugin ID.
The build.gradle file in the project that is using the test-plugin:
repositories {
flatDir name: 'libs', dirs: "../build/libs"
}
dependencies {
compile 'test:test-plugin:0.1'
}
apply plugin: 'test-plugin'
Error from Gradle:
What went wrong:
A problem occurred evaluating root project 'tmp'.
Plugin with id 'test-plugin' not found.
The plugin Jar has to be added as a build script dependency:
buildscript {
repositories { flatDir name: 'libs', dirs: "../build/libs" }
dependencies { classpath 'test:test-plugin:0.1' }
}
apply plugin: "test-plugin"
If you want to implement a plugin into your buildscript, then you have two options.
Option 1
apply plugin: YourCustomPluginClassName
Option 2
plugins {
id 'your.custom.plugin.id'
}
apply plugin: is used when specifying your plugin by its class name (ex. apply plugin: JavaPlugin)
plugins { } is used when specifying your plugin by its id (ex. plugins { id 'java' })
See Gradle Plugins by tutorialspoint for reference
If you choose Option 1, the your custom plugin will need to be brought into your build script by 1 of 3 ways.
You can code it directly within your Gradle build script.
You can put it under buildSrc (ex. buildSrc/src/main/groovy/MyCustomPlugin).
You can import your custom plugin as a jar in your buildscript method.
See Gradle Goodness by Mr. Haki for information about the buildscript method.
If you choose Option 2, then you need to create a plugin id. Create the following file buildSrc/src/main/resources/META-INF/gradle-plugins/[desired.plugin.id].properties.
Copy and paste implementation-class=package.namespace.YourCustomPluginClassName into your newly created .properties file. Replace package.namespace.YourCustomPluginClassName with the fully-qualified class name of your desired plugin class.
See Custom Plugins by Gradle for more info.
I also had the same problem with a custom plugin id not being found. In my case, I simply forgot to add the 'src/main/resources/META-INF/gradle-plugins' properties file. The name of the properties file must match the name of the plugin id with a '.properties' extension.
The file must contain a the line:
implementation-class=(your fully qualified plugin classpath)
That's the complete mechanism on how plugin id's get resolved to class names.
In addition the plugin needs to be added as a dependency as pointed out in the previous answer. The android documentation states that you should use a name associated with your unique domain name. I.e.: the name 'test-plugin' is not really in good form, but an id like 'com.foo.gradle.test-plugin' would be better.
Ensure that your top-level build.gradle uses the correct classpath to refer to the path to the built *.jar file.
Some plugins, like maven-publish, will build and save the jar to a specific location in mavenLocal, but the path may not be clear to see.
You should look at the file path of the jar file, and ensure it matches your classpath, but the mapping is not immediately obvious:
buildscript {
dependencies {
// This *MUST* match the local file path of the jar file in mavenLocal, which is:
// ~/.m2/repository/com/company/product/plugin/product-gradle-plugin/1.0/product-gradle-plugin-1.0.jar
classpath 'com.company.product.plugin:product-gradle-plugin:1.0'
}
}
Be careful not to use the wrong classpath, which can refer to a directory instead of the actual jar file; like this:
buildscript {
dependencies {
// This is wrong, because it refers to the mavenLocal FOLDER "product-gradle-plugin",
// instead of the jar FILE "product-gradle-plugin-1.0.jar"
// However, a gradle sync will still resolve it as a valid classpath!!
classpath 'com.company.product:product-gradle-plugin:1.0'
}
}
More info:
https://discuss.gradle.org/t/what-is-the-preferred-gradle-approach-to-locally-install-an-artifact-equivalent-to-mavens-install/5592
https://docs.gradle.org/current/userguide/publishing_maven.html
https://blog.codefx.org/tools/snapshots-gradle-maven-publish-plugin/
https://docs.gradle.org/current/userguide/custom_plugins.html#sec:custom_plugins_standalone_project
Adding to what #Bonifacio2 wrote this is a path META-INF/gradle-plugins and shows in IntelliJ as META-INF.gradle-plugins. At all costs don't make the stupid mistake I did creating this directly as a directory META-INF.gradle-plugins because you are based on another sample, and never works. Another tip is copying also from another intelliJ project as this is what is added: gradle-plugins.
hmm perhaps try;
configure <org.jsonschema2pojo.gradle.JsonSchemaExtension> {
this.sourceFiles = files("${project.rootDir}/schemas")
}