Gradle get dependency programatically without configuration - gradle

Is there an option to get Maven dependency in Gradle without using custom configuration for it? For example in custom plugin to just obtain dependency provided from extension? Something like
class DependencyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("deps", DepsExtension)
project.task('useDependency') {
doLast {
//use Gradle api to resolve dependency without custom configuration
project.resolve(project.deps.dependency)
}
}
}
}
class DepsExtension {
def dependency = 'custom:maven:1.0'
}

Something like this:
Configuration config = project.configurations.create('myPrivateConfig')
Dependency dep = project.dependencies.create('custom:maven:1.0') {
exclude group: 'foo', module: 'bar'
}
config.dependencies.add(dep)
Set<File> files = config.files
I do a similar thing in a gradle plugin here
References
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/DependencySet.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html

Related

Checkstyle config file from dependency jar

I am trying to apply the checkstyle plugin to my gradle project. The configuration of which is in a seperate, shared jar dependency:
project.apply plugin: StaticAnalysisPlugin
class StaticAnalysisPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin: 'checkstyle'
project.configurations {
codingStandardsConfig
}
project.dependencies {
codingStandardsConfig 'com.sample.tools:coding-standards:1.+:#jar'
}
def checkstyleConfigFileLocation = "classpath:sample-checkstyle-config.xml"
project.checkstyle {
toolVersion = '6.3'
project.logger.debug "$project Using checkstyle version $toolVersion."
project.logger.debug "$project Using checkstyle config from: ${checkstyleConfigFileLocation}"
config = project.resources.text.fromFile(checkstyleConfigFileLocation)
}
project.checkstyleMain.source = "src/main/java"
project.checkstyleTest.exclude "**/*"
}
}
the config file is located in the coding-standards jar, but I am unsure how to wire this in to the checkstyle config.
I think you should use the configFile option (which expects a File object) rather than the config option (which expects a text resource with your actual configuration).
Then the problem comes down to determining the correct File object. From this answer, it seems you can do
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("file/test.xml").getFile());
Alternative solutions can be found on StackOverflow. Make sure that the class loader's class path includes your coding-standards.jar. You may have to put that dependency in a buildscript block to make it available, well, to the build script.

How do I set task properties in a Gradle Plugin

I am creating a gradle plugin to apply the sonar-runner plugin and default many of the values such as the sonar host URL and the sonar JDBC URL. I cannot figure out how to set the properties though.
When I set this up in build.gradle I use:
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
My gradle plugin looks like:
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin: 'sonar-runner'
project.configurations {
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
}
}
With this setup I get a No signature of method exception. How should I be setting these properties?
I discovered that I could use project.getExtensions().sonarRunner.sonarProperties{ ... } to set the sonar properties. See example below.
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin:'sonar-runner'
project.getExtensions().sonarRunner.sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
Thank you #mikerylander and #ravikanth! I also had tried the setProperty and .properties solutions but they didn't work for me.
The really tricky thing was that autocomplete did not find the "sonarqube" portion of project.getExtensions().sonarqube.properties for me so I never got to this solution without your post.
I wrote a custom Gradle plugin to run sonarqube for a multi-module Android project and your post helped me. Below is my full custom plugin. Since the plugin is designed to be included in the build.gradle of any submodule of my Android project I prepended "my_product" ${project.path} but of course you can use any values here.
Here is my complete plugin code in case its helpful:
package com.example.gradle.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
class MySonarCodeCoveragePlugin implements Plugin<Project> {
private Project project
void apply(Project project) {
this.project = project
project.apply plugin: 'org.sonarqube'
project.getExtensions().sonarqube.properties
{
property "sonar.sources", "${project.projectDir}/src/main"
property "sonar.organization", "my_org"
property "sonar.projectKey", "my_product${project.path}"
property "sonar.projectName", "my_product${project.path}"
property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml"
property "sonar.scanner.metadataFilePath", "${project.buildDir}/sonar/report-task.txt"
}
}
}

Do gradle configurations merge or override?

Say I define a plugin, like so:
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.Project
import org.gradle.api.Plugin
class MyRepos implements Plugin<Project> {
static final String NEXUS_URL = 'http://nexus.mine.com/nexus/content/repositories/'
static final List<String> NEXUS_REPOS = [
'central',
'build',
'snapshots',
'release-candidates',
'releases',
]
void apply(Project project) {
project.repositories {
NEXUS_REPOS.each {
maven {
url (NEXUS_URL + it)
}
}
mavenLocal()
}
project.buildscript {
repositories {
maven {
url (NEXUS_URL + 'central')
}
mavenLocal()
}
}
}
}
and in a local build.gradle, I write
apply plugin: MyRepos
buildscript {
dependencies {
...
}
}
My desire is for the two buildscript sections to be merged such that the repositories are defined in the plugin and the dependencies in build.gradle, but it appears that the local declaration overrides the plugin and I end up with an error indicating "no repositories defined".
If I do this via configuration injection from the top level build.gradle, is the result the same?
Maybe the right solution is for the plugin to provide an ext closure to define useMyRepos() similar to the way mavenCentral() is used...
Generally configuration items in gradle are merged so you can apply configurations in different locations. You can, for example, configure some of the dependencies in one build script that will be applied to another build script that will add additional dependencies.
However, your case is a bit different since you're using buildScript configuration which is can be used to define the dependencies for the build script itself rather than the root repositories node which is intended for defining the dependencies of the project you build.
In this case, according to Purpose of buildScript in Gradle as these are different configurations you'll have to define your dependency twice.

How to configure a DSL style gradle plugin from a plugin extension

I have implemented a Gradle plugin using the Gradle DSL style. The plugin is adding multiple aspects such as a adding a custom task, and configuring more other tasks. Overall, the plugin is generating some metadata property file in a source folder that must be configured by a plugin extension.
apply plugin: 'artifactMetadata'
// configure the path for the metadata
artifactMetadata {
destinationDirectory = "src/main/generated/otherlocation/resources"
}
I have been able to figure out how to configure the task using the extension properties, however it's tricking me with the remaining stuff. What is a good approach to configure the source set, the clean task and the idea plugin (see the #n: TODO comments in the plugin code below)? The implementation below will always use the default value, not the one injected through the plugin extension.
class ArtifactMetadataPlugin implements Plugin<Project> {
public static final String EXTENSION_NAME = 'artifactMetadata'
public static final String TASK_NAME = 'generateArtifactMetadata'
void apply(Project project) {
createExtension(project)
project.configure (project) {
task (TASK_NAME, type: GenerateArtifactMetadata) {
group = project.group
artifact = project.name
version = project.version.toString()
}
sourceSets {
main {
// #1:TODO to get the plugin extension property current value here output.dir(project.artifactMetadata.destinationDirectory, builtBy: TASK_NAME)
resources.srcDirs += file(project.artifactMetadata.destinationDirectory)
}
}
clean {
// #2:TODO get the plugin extension property here
delete file(project.artifactMetadata.destinationDirectory)
}
if (project.plugins.hasPlugin(IdeaPlugin)) {
idea {
module {
// #3:TODO get the plugin extension property here
sourceDirs += file(project.artifactMetadata.destinationDirectory)
}
}
}
}
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION_NAME)
project.tasks.withType(GenerateArtifactMetadata).all { task ->
task.destinationDirectory = project.file(extension.destinationDirectory)
}
}
}
private static void createExtension(Project project) {
def extension = project.extensions.create(EXTENSION_NAME, ArtifactMetadataPluginExtension)
extension.with {
destinationDirectory = "src/main/generated/artifactinfo/resources"
}
}
}

Gradle custom plugin : add dependency from extension object

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
}

Resources