How can I write a settings plugin as Gradle script - gradle

I have some code in my settings.gradle file to dynamically create references to version catalogs:
// list of catalogs is actually loaded from a file
def catalogs = [ "com.example:mycatalog:1.0" ]
dependencyResolutionManagement {
versionCatalogs {
catalogs.each { String catalog ->
def catalogName = catalog.split(":")[1] + "Libs"
logger.info "Add version catalog ${catalogName} = ${catalog}"
create(catalogName, { from(catalog) })
}
}
}
And now I want to share that as a Gradle plugin.
I know that I can write a project plugin as a precompiled script plugin with *.gradle files in src/main/groovy directory.
But then Gradle always builds a Plugin<Project> plugin.
How can I declare that I want to a have Plugin<Settings> plugin? Or is this not even possible?

Related

setting properties for Gradle subproject

I have a multi-project Gradle build. In each subproject I have a properties.gradle file like the following:
def usefulMethod() {
return 'foo'
}
ext {
foo = 'bar'
usefulMethod = this.&usefulMethod
}
And then I import it into the subproject build.gradle using apply from: './properties.gradle'.
However, if two subprojects import a variable with the same name, I get this error:
Cannot add extension with name 'foo', as there is an extension already registered with that name.
It seems that adding to ext affects the entire project instead of just the subproject like I wanted. What is the correct way to import properties and variables from an external file in a subproject without leaking them into the entire project build?
The plain ext is the extension for ENTIRE project, the root project and all subprojects. To avoid polluting the root namespace whenever you include a file via apply from..., you should instead use project.ext. project refers to the current project or subproject being built. For example, the below file could be apply from'd to add a downloadFile function to the current project:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'de.undercouch:gradle-download-task:3.4.3'
}
}
project.ext {
apply plugin: de.undercouch.gradle.tasks.download.DownloadTaskPlugin
downloadFile = { String url, File destination ->
if (destination.exists()) {
println "Skipping download because ${destination} already exists"
} else {
download {
src url
dest destination
}
}
}
}

Gradle - "apply from" a ZIP dependency

In my Gradle build script I want to import a ZIP dependency that contains static analysis configuration (CheckStyle, PMD etc.) and then "apply from" the files in that ZIP. When anyone runs the "check" task, my custom static analysis configuration should be used then.
I've tried the somewhat convoluted solution below, but I can't get it to work. The files are retrieved and unpacked into the "config" directory, but "apply from" does not work - Gradle complains it cannot find the files; I assume this is due to "apply from" being run during the build configuration phase.
Is there a simpler way to do this?
repositories {
maven { url MY_MAVEN_REPO }
}
configurations {
staticAnalysis {
description = "Static analysis configuration"
}
}
dependencies {
staticAnalysis group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
// Unzip static analysis conf files to "config" in root project dir.
// This is the Gradle default location.
task prepareStaticAnalysisConf(type: Copy) {
def confDir = new File(rootProject.projectDir, "config")
if (!confDir.exists()) {
confDir.mkdirs()
}
from {
configurations.staticAnalysis.collect { zipTree(it) }
}
into confDir
apply from: 'config/quality.gradle'
}
check.dependsOn('prepareStaticAnalysisConf')
You are perfectly right: Gradle runs apply during evaluation phase, but the prepareStaticAnalysisConf was not executed yet and the archive is not unpacked.
Instead of a task, just write some top-level code. It should do the trick. Also, you'd better use the buildscript level dependency, so that it is resolved before script is executed.
Here is the full script
buildScript {
repositories {
maven { url MY_MAVEN_REPO }
}
dependencies {
classpath group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
}
def zipFile = buildscript.configurations.classpath.singleFile
copy {
from zipTree(it)
into 'config'
}
apply from: 'config/quality.gradle'

Not Publishing the mentioned file(war/tar/zip) to artifactory in gradle script

I wrote a gradle script where I am creating the zip and war file and then I need to upload/publish it to the artifactory but the issue is I specified the war file in my artifact task even after that it is publishing everything to the artifactory zip,tar and war instead of only war file.
apply plugin: 'war'
apply plugin: 'java'
apply plugin: 'distribution'
//-- set the group for publishing
group = 'com.xxx.discovery'
/**
* Initializing GAVC settings
*/
def buildProperties = new Properties()
file("version.properties").withInputStream {
stream -> buildProperties.load(stream)
}
//add the jenkins build version to the version
def env = System.getenv()
if (env["BUILD_NUMBER"]) buildProperties.coveryadBuildVersion += "_${env["BUILD_NUMBER"]}"
version = buildProperties.coveryadBuildVersion
println "${version}"
//name is set in the settings.gradle file
group = "com.aaa.covery"
version = buildProperties.discoveryadBuildVersion
println "Building ${project.group}:${project.name}:${project.version}"
repositories {
maven {
url "http://cxxxxt.tshaaaaa.tho.com:9000/artifactory/libselease"
}
maven {
url "http://cxxxxt.tshaaa.tho.com:9000/artifactory/cache"
}
}
dependencies {
compile ([
"com.uters.omni:HermesSessionAPI:1.2",
"com.uters.proxy:ProxyResources:1.1",
"com.uters.omni:SeshataDirectory:1.0.1" ,
"com.euters.omni:CybeleInfrastructure:1.1.2",
"com.euters:JSONBus:1.4.1",
"javaee:javaee-api:5"
])
}
distributions {
main {
contents {
from {
war.outputs
}
}
}
}
// for publishing to artifactory
artifacts {
archives war
}
According to gradle distribution plugin documentation:
All of the files in the “src/$distribution.name/dist” directory will automatically be included in the distribution.
And also,
The distribution plugin adds the distribution archives as candidate for default publishing artifacts.
In other words, by default all the files will be published so this explains the behavior you're experiencing.
What you can probably do in order to workaround this behavior is to define the contents copySpec more accurately by explicitly exclude the unwanted files, i.e.:
distributions {
main {
contents {
exclude('**/.zip')
exclude('**/.tar')
from {
war.outputs
}
}
}
}
Note that I didn't try the above by myself though so some fine tuning might be needed. However I believe that you can find the data you need in the CopySpec Interface documentation

How to add gradle generated source folder to Eclipse project?

My gradle project generates some java code inside gen/main/java using annotation processor. When I import this project into Eclipse, Eclipse will not automatically add gen/main/java as source folder to buildpath. I can do it manually. But is there a way to automate this?
Thanks.
You can easily add the generated folder manually to the classpath by
eclipse {
classpath {
file.whenMerged { cp ->
cp.entries.add( new org.gradle.plugins.ide.eclipse.model.SourceFolder('gen/main/java', null) )
}
}
}
whereby null as a second constructor arg means that Eclipse should put the compiled "class" files within the default output folder. If you want to change this, just provide a String instead, e.g. 'bin-gen'.
I think it's a little bit cleaner just to add a second source directory to the main source set.
Add this to your build.gradle:
sourceSets {
main {
java {
srcDirs += ["src/gen/java"]
}
}
}
This results in the following line generated in your .classpath:
<classpathentry kind="src" path="src/gen/java"/>
I've tested this with Gradle 4.1, but I suspect it'd work with older versions as well.
Andreas' answer works if you generate Eclipse project from command line using gradle cleanEclipse eclipse. If you use STS Eclipse Gradle plugin, then you have to implement afterEclipseImport task. Below is my full working snippet:
project.ext {
genSrcDir = projectDir.absolutePath + '/gen/main/java'
}
compileJava {
options.compilerArgs += ['-s', project.genSrcDir]
}
compileJava.doFirst {
task createGenDir << {
ant.mkdir(dir: project.genSrcDir)
}
createGenDir.execute()
println 'createGenDir DONE'
}
eclipse.classpath.file.whenMerged {
classpath - >
def genSrc = new org.gradle.plugins.ide.eclipse.model.SourceFolder('gen/main/java', null)
classpath.entries.add(genSrc)
}
task afterEclipseImport(description: "Post processing after project generation", group: "IDE") {
doLast {
compileJava.execute()
def classpath = new XmlParser().parse(file(".classpath"))
new Node(classpath, "classpathentry", [kind: 'src', path: 'gen/main/java']);
def writer = new FileWriter(file(".classpath"))
def printer = new XmlNodePrinter(new PrintWriter(writer))
printer.setPreserveWhitespace(true)
printer.print(classpath)
}
}

how to download external files in gradle?

I have a gradle project which requires some data files available somewhere on the internet using http. The goal is that this immutable remote file is pulled once upon first build. Subsequent build should not download again.
How can I instruct gradle to fetch the given file to a local directory?
I've tried
task fetch(type:Copy) {
from 'http://<myurl>'
into 'data'
}
but it seems that copy task type cannot deal with http.
Bonus question: is there a way to resume a previously aborted/interrupted download just like wget -c does?
How about just:
def f = new File('the file path')
if (!f.exists()) {
new URL('the url').withInputStream{ i -> f.withOutputStream{ it << i }}
}
You could probably use the Ant task Get for this. I believe this Ant task does not support resuming a download.
In order to do so, you can create a custom task with name MyDownload. That can be any class name basically. This custom task defines inputs and outputs that determine whether the task need to be executed. For example if the file was already downloaded to the specified directory then the task is marked UP-TO-DATE. Internally, this custom task uses the Ant task Get via the built-in AntBuilder.
With this custom task in place, you can create a new enhanced task of type MyDownload (your custom task class). This task set the input and output properties. If you want this task to be executed, hook it up to the task you usually run via task dependencies (dependsOn method). The following code snippet should give you the idea:
task downloadSomething(type: MyDownload) {
sourceUrl = 'http://www.someurl.com/my.zip'
target = new File('data')
}
someOtherTask.dependsOn downloadSomething
class MyDownload extends DefaultTask {
#Input
String sourceUrl
#OutputFile
File target
#TaskAction
void download() {
ant.get(src: sourceUrl, dest: target)
}
}
Try like that:
plugins {
id "de.undercouch.download" version "1.2"
}
apply plugin: 'java'
apply plugin: 'de.undercouch.download'
import de.undercouch.gradle.tasks.download.Download
task downloadFile(type: Download) {
src 'http://localhost:8081/example/test-jar-test_1.jar'
dest 'localDir'
}
You can check more here: https://github.com/michel-kraemer/gradle-download-task
For me works fine..
The suggestion in Ben Manes's comment has the advantage that it can take advantage of maven coordinates and maven dependency resolution. For example, for downloading a Derby jar:
Define a new configuration:
configurations {
derby
}
In the dependencies section, add a line for the custom configuration
dependencies {
derby "org.apache.derby:derby:10.12.1.1"
}
Then you can add a task which will pull down the right files when needed (while taking advantage of the maven cache):
task deployDependencies() << {
String derbyDir = "${some.dir}/derby"
new File(derbyDir).mkdirs();
configurations.derby.resolve().each { file ->
//Copy the file to the desired location
copy {
from file
into derbyDir
// Strip off version numbers
rename '(.+)-[\\.0-9]+\\.(.+)', '$1.$2'
}
}
}
(I learned this from https://jiraaya.wordpress.com/2014/06/05/download-non-jar-dependency-in-gradle/).
Using following plugin:
plugins {
id "de.undercouch.download" version "3.4.3"
}
For a task which has the purpose of only downloading
task downloadFile(type: Download) {
src DownloadURL
dest destDir
}
For including download option into your task:
download {
src DownloadURL
dest destDir
}
For including download option with multiple downloads into your task:
task downloadFromURLs(){
download {
src ([
DownloadURL1,
DownloadURL2,
DownloadURL3
])
dest destDir
}
}
Hope it helped :)
just now ran into post on upcoming download task on gradle forum.
Looks like the perfect solution to me.. Not (yet) available in an official gradle release though
Kotlin Version of #Benjamin's Answer
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:4.0.1")
}
}
tasks.register("downloadPdf"){
val path = "myfile.pdf"
val sourceUrl = "https://file-examples-com.github.io/uploads/2017/10/file-sample_150kB.pdf"
download(sourceUrl,path)
}
fun download(url : String, path : String){
val destFile = File(path)
ant.invokeMethod("get", mapOf("src" to url, "dest" to destFile))
}

Resources