Multiple configurations for a task in my build.gradle - gradle

I'm using this awesome Gradle plugin to compile my iOS projet.
According to the documentation my build.gradle looks like this :
buildscript {
repositories {
maven {
url('http://openbakery.org/repository/')
}
mavenCentral()
}
dependencies {
classpath group: 'org.openbakery', name: 'xcodePlugin', version: '0.9.+'
}
}
apply plugin: 'xcode'
task wrapper(type: Wrapper) {
gradleVersion = '1.12'
}
xcodebuild {
workspace = 'WORKSPACE'
sdk = 'iphoneos'
target = 'TARGET'
signing {
mobileProvisionURI = 'URI'
certificateURI = 'URI'
certificatePassword = 'PASSWORD'
}
scheme = 'SCHEME'
configuration = 'CONFIGURATION'
}
How can I manage multiple configurations for my xcodebuild task ?
I would like something like
...
xcodebuild-staging {
workspace = 'STAGING_WORKSPACE'
sdk = 'iphoneos'
target = 'STAGING_TARGET'
signing {
mobileProvisionURI = 'URI'
certificateURI = 'URI'
certificatePassword = 'STAGING_PASSWORD'
}
scheme = 'STAGING_SCHEME'
configuration = 'STAGING_CONFIGURATION'
}
xcodebuild-production {
workspace = 'PRODUCTION_WORKSPACE'
sdk = 'iphoneos'
target = 'PRODUCTION_TARGET'
signing {
mobileProvisionURI = 'PRODUCTION_URI'
certificateURI = 'PRODUCTION_URI'
certificatePassword = 'PRODUCTION_PASSWORD'
}
scheme = 'PRODUCTION_SCHEME'
configuration = 'PRODUCTION_CONFIGURATION'
}
...
I tried multiple things : create a new task that calls xcodebuild.execute() or extend xcodebuild. None of them worked like I wanted to.
I ended having different build.gradle files...
Do you know how to do that ?

After a quick investigation of the source it seems that, what You need is impossible (and it seems a bit strange - such a plugin should enable user to build multiple schemes). I suggest You to contact with the author.

It might be possible! Have you tried adding the task type as a property? something like...
task xcodebuild-staging (type: org.openbakery.XcodeBuildTask) {
workspace = 'STAGING_WORKSPACE'
sdk = 'iphoneos'
target = 'STAGING_TARGET'
signing {
mobileProvisionURI = 'URI'
certificateURI = 'URI'
certificatePassword = 'STAGING_PASSWORD'
}
scheme = 'STAGING_SCHEME'
configuration = 'STAGING_CONFIGURATION'
}
task xcodebuild-production (type: org.openbakery.XcodeBuildTask){
workspace = 'PRODUCTION_WORKSPACE'
sdk = 'iphoneos'
target = 'PRODUCTION_TARGET'
signing {
mobileProvisionURI = 'PRODUCTION_URI'
certificateURI = 'PRODUCTION_URI'
certificatePassword = 'PRODUCTION_PASSWORD'
}
scheme = 'PRODUCTION_SCHEME'
configuration = 'PRODUCTION_CONFIGURATION'
}
It is dependent on how the plugin is written but you can override tasks like this as long as there is not significant config supplied by the plugin. I have found that plugins can also be extended https://docs.gradle.org/current/userguide/custom_plugins.html#custom_plugins
but that's more complex.

Related

xjc, generate java classes from xsd (using URL) in gradle 7.2

I use plugin com.github.bjornvester.xjc to generate java classes from xsd:
xjc {
xjcVersion.set("2.3.3")
outputJavaDir = file("${buildDir}/generated-sources/jaxb")
ext.downloaded = file("$buildDir/xjc/downloaded/schema2.wsdl")
doFirst {
mkdir downloaded.parentFile
downloaded.text = new URL("http://www.example.com/foo.xsd").text}
groups {
register("schema1") {
xsdFiles = files(xsdDir.file("${projectDir}/src/main/resources/wsdl/schema1.wsdl"))
defaultPackage.set("pl.com.project.schema1")
}
register("schema2") {
xsdFiles = files(downloaded)
defaultPackage.set("pl.com.project.schema2")
}
}
}
And I got an error in line "xjc {" :
In my previous attempt I incorrectly assumed that xjc was a task. After looking at the github page I can see that "xjc" is an extension object, not a task
So try this:
tasks.register('downloadXsd') {
ext.xsd = file("$buildDir/downloadXsd/foo.xsd")
outputs.file xsd // important!!! configures the task outputs
doLast {
mkdir xsd.parentFile
xsd.text = new URL("http://www.example.com/foo.xsd").text
}
}
xjc {
...
groups {
register("schema1") {
// assuming the plugin is written properly, this should configure a task dependency
xsdFiles = files(tasks.named('downloadXsd'))
...
}
...
}
}
You could improve this using the download task to download the xsd which shows progress of the download and also has caching options

Generalising compile* task in Gradle

How do I generalise``compile` task in a multi-language project?
E.g. I can to generalise this to run on all compile* tasks for each language used.
compileJava {
ajc {
enabled = true
classpath = configurations.aspectj
options {
aspectpath = configurations.aspect
compilerArgs = []
}
}
}
compileTestJava {
ajc {
enabled = true
classpath = configurations.aspectj
options {
aspectpath = configurations.testAspect
compilerArgs = []
}
}
}
Source: FreeFair Gradle Plugin Collection
How can the above be modified so that it can run with any compile* task, e.g. compileGroovy, compileKotlin, compileScala etc. in a multi-language project?
You can use the method matching(...) of the TaskContainer that is available via tasks in your build.gradle:
tasks.matching { task -> task.name.startsWith('compile') }.all {
ajc {
enabled = true
classpath = configurations.aspectj
options {
aspectpath = configurations.testAspect
compilerArgs = []
}
}
}
Please note that this solution will only work if all tasks that start with 'compile' actually have an extension ajc. It also only works because of Groovy, as whether afc is available or not will be evaluated dynamically. This won't work in a Kotlin build script.

Gradle Kotlin DSL: Define Kotlin version in unique place

For describing Gradle build scripts, we can use Kotlin via build.gradle.kts files. It's a common problem to globally define the Kotlin version to be used, both in the dependencies and also in the build plugin section (It's rather uncommon to have different versions in use for the given case).
Consider the following code (Gradle 4.3.1):
plugins {
var pluginVersion = "1.2.30"
kotlin("jvm").version(kotlinVersion)
// more
}
var dependencyVersion = "1.2.30"
dependencies {
compile(kotlin("stdlib", kotlinVersion))
compile(kotlin("reflect", kotlinVersion))
testCompile(kotlin("test", kotlinVersion))
// more
}
As you can see, the kotlin version (1.2.30 in this case) is defined twice: dependencyVersion and pluginVersion, which very often does not differ. Due to DSL restrictions, it is impossible to access the pluginVersion from outside the plugins block or access the dependencyVersion from within the plugins block.
How can the version string, "1.2.30" be extracted to a single place?
In later versions of Gradle you no longer need to specify the version of your kotlin(stdlib|reflect|test) dependencies, the Kotlin plugin will automatically configure them for you.
As for extracting the dependency to a single place, there are two main patterns:
define the constants you want to share in an object within buildSrc/src/main/kotlin/ and use that object in your build script, code from buildSrc is available to the whole script including the plugins block
use a system property, you can define a system property in gradle.properties by prefixing its name with systemProp. and you can access system properties via System.getProperties(), for example:
// build.gradle.kts
plugins {
val kotlinVersion by System.getProperties()
println("Kotlin version is $kotlinVersion")
}
// gradle.properties
systemProp.kotlinVersion=1.2.20
What I just stumbled upon was using Kotlin classes ins my build.gradle.kts.
I had to:
create a module called buildSrc with src/main/kotlin and a build.gradle.kts in its root.
(obsolete) include("buildSrc") in settings.gradle.kts
The buildSrc/build.gradle.kts is very minimal:
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
In buildSrc/src/main/kotlin I've added a Config.kt
const val GROUP_ID = "my-company"
const val VERSION = "0.1.0-SNAPSHOT"
const val POM_NAME = "my-library-name"
const val POM_DESCRIPTION = "A library doing stuff."
const val POM_URL = "https://github.com/${GROUP_ID}/${POM_NAME}/"
const val POM_SCM_URL = POM_URL
const val POM_SCM_CONNECTION = "scm:git:git://github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_SCM_DEV_CONNECTION = "scm:git:ssh://git#github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_LICENCE_NAME = "The Apache Software License, Version 2.0"
const val POM_LICENCE_URL = "http://www.apache.org/licenses/LICENSE-2.0.txt"
const val POM_LICENCE_DIST = "repo"
const val POM_DEVELOPER_ID = "me"
const val POM_DEVELOPER_NAME = "meeee"
const val POM_DEVELOPER_EMAIL = "me#foo.com"
And a Dependencies.kt
#file:Suppress("MemberVisibilityCanBePrivate")
object Jvm {
const val version = "1.8"
}
object Kotlin {
const val version = "1.3.50"
const val stdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
const val jvmId = "jvm"
const val kaptId = "kapt"
}
object MavenPublish {
const val id = "maven-publish"
}
object Arrow {
const val version = "0.10.1"
const val core = "io.arrow-kt:arrow-core:$version"
const val syntax = "io.arrow-kt:arrow-syntax:$version"
const val optics = "io.arrow-kt:arrow-optics:$version"
const val fx = "io.arrow-kt:arrow-fx:$version"
const val meta = "io.arrow-kt:arrow-meta:$version"
}
object Versions {
const val version = "0.27.0"
const val versions = "com.github.ben-manes:gradle-versions-plugin:$version"
const val id = "com.github.ben-manes.versions"
}
So I could use it in my root build.gradle.kts like
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin(Kotlin.jvmId) version Kotlin.version
kotlin(Kotlin.kaptId) version Kotlin.version
id(Versions.id) version Versions.version
id(MavenPublish.id)
}
group = GROUP_ID
version = VERSION
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation(Kotlin.stdlibJdk8)
implementation(Arrow.core)
implementation(Arrow.syntax)
kapt(Arrow.meta)
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = Jvm.version
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
#Suppress("UnstableApiUsage")
pom {
name.set(POM_NAME)
description.set(POM_DESCRIPTION)
url.set(POM_URL)
licenses {
license {
name.set(POM_LICENCE_NAME)
url.set(POM_LICENCE_URL)
distribution.set(POM_LICENCE_DIST)
}
}
developers {
developer {
id.set(POM_DEVELOPER_ID)
name.set(POM_DEVELOPER_NAME)
email.set(POM_DEVELOPER_EMAIL)
}
}
scm {
connection.set(POM_SCM_CONNECTION)
developerConnection.set(POM_SCM_DEV_CONNECTION)
url.set(POM_SCM_URL)
}
}
}
}
}
I am quite happy with this, but when it comes down to automatically increment the version I may fall back to maintain it in the gradle.properties.
Edit:
It is no longer necessary (and allowed) to add buildSrc to the settings.gradle.kts, instead it will automatically get picked up if present.
You can extract the version from the plugin class:
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
plugins {
kotlin("jvm") version "1.2.0"
}
val kotlinVersion = plugins.getPlugin(KotlinPluginWrapper::class.java).kotlinPluginVersion
Once you've defined a version for the Kotlin plugin all other Kotlin libraries will use the same version, and do not need a specific version set.
So the only place you need to set the version is when defining the classpath for the plugin, e.g.:
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
}
}
If you then need the version for some other purpose (such as in a resolutionStrategy or just for information) you can get it from org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
For example:
println("Kotlin version used is ${org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION}")
There's a workaround available, which searches the version defined for the kotlin plugin and assignes this one to the outer variable. The following demonstrates this:
val kotlinVersion: String? by extra {
buildscript.configurations["classpath"]
.resolvedConfiguration.firstLevelModuleDependencies
.find { it.moduleName == "kotlin-gradle-plugin" }?.moduleVersion
}
plugins {
kotlin("jvm").version("1.2.30")
//more
}
The variable kotlinVersion can then be used in the dependencies without further trouble.
Answer for #s1m0nw1 comment (too long for comment):
No you can't use buildSrc/src stuff in buildSrc/build.gradle. I had exactly this problem as I wrote android-based plugin and I need android gradle plugin dependency in buildsSrc but I also declare this dependency in project. So I had two different places and two versions to maintain.
I resolved this by creating gradle.properties file in buildSrc directory.
In it I've created prop androidGradlePluginVersion=3.6.0-rc02
buildSrc/build.gradle:
val androidGradlePluginVersion: String by project
dependencies {
implementation("com.android.tools.build:gradle:$androidGradlePluginVersion")
buildSrc/src/.../Versions.kt:
var ANDROID_PLUGIN = loadAndroidGradlePluginVersion()
Util for props:
val GRADLE_PROPERTIES = "buildSrc/gradle.properties"
val ANDROID_PLUGIN_VERSION_PROP = "androidGradlePluginVersion"
fun loadAndroidGradlePluginVersion(): String {
Properties().apply { load(FileInputStream(GRADLE_PROPERTIES)) }.let {
return it.getProperty(ANDROID_PLUGIN_VERSION_PROP)
}
error("Provide $ANDROID_PLUGIN_VERSION_PROP in $GRADLE_PROPERTIES")
}
Easy and fast workaroud:
In buildscript section set value as system property:
buildscript {
val version = "X.X.X"
System.setProperty("version", version)
}
In plugins section get this property:
plugins {
val version = System.getProperty("version")
}

Using a plugin from separate Gradle Script

I have been using a plugin to generate some documentation. When I place the code for it inside the build.gradle for each package it works perfectly.
However instead of having this plugin in multiple build.gradle files I want to place it in a separate gradle script to keep everything more central.
However when i do this I get an error of :
Plugin with id 'com.benjaminsproule.swagger' not found.
Despite the code being the exact same as when it is in the build.gradle file.
Here is the code to import the plugin and use it:
buildscript {
repositories {
maven {url "http://jcenter.bintray.com"}
}
dependencies {
classpath 'com.benjaminsproule:swagger-gradle-plugin:0.0.8'
}
}
apply plugin: 'com.benjaminsproule.swagger'
swagger {
apiSource {
springmvc = false
locations = ['package1']
info {
title = 'Package 1'
version = 'v1'
description = 'Documentation for Package 1'
}
swaggerDirectory = "${project.rootDir}/reports/Package1"
}
apiSource {
springmvc = false
locations = ['package2']
info {
title = 'Package 2'
version = 'v1'
description = 'Documentation for Package 2'
}
swaggerDirectory = "${project.rootDir}/reports/Package2"
}
}
So I want to move this all from build.gradle to a file called swagger.gradle, is this possible?
I also run the plugin using:
gradle swagger
Figured out a solution myself, might be useful for anyone else who runs into this problem.
I created a Swagger.gradle file which contained the buildscript and the build.gradle then called in the plugin.
For Apply plugin I had to called directly the main class of the plugin.
Swagger.gradle
buildscript {
repositories {
maven {url "http://jcenter.bintray.com"}
}
dependencies {
classpath 'com.benjaminsproule:swagger-gradle-plugin:0.0.8'
}
}
apply plugin: com.benjaminsproule.swagger.gradleplugin.GradleSwaggerPlugin
Build.gradle
apply from: "/scripts/Swagger.gradle"
swagger {
apiSource {
springmvc = false
locations = ['package1']
info {
title = 'Package 1'
version = 'v1'
description = 'Documentation for Package 1'
}
swaggerDirectory = "${project.rootDir}/reports/Package1"
}
apiSource {
springmvc = false
locations = ['package2']
info {
title = 'Package 2'
version = 'v1'
description = 'Documentation for Package 2'
}
swaggerDirectory = "${project.rootDir}/reports/Package2"
}
}
Credit to this Blog: http://mrhaki.blogspot.co.uk/2015/10/gradle-goodness-apply-external-script.html

Copy generated third party licenses to assets

I want to automate inclusion of the latest generated third party licenses in Gradle.
Config:
Gradle 2.x
license-gradle-plugin 0.11.0
Code:
task beforeAssemble(dependsOn: assemble) << {
downloadLicenses.execute()
CopyToAssetsTask.execute()
}
task CopyToAssetsTask(type: Copy) {
description = 'Copies generated Third Party Licenses into assets'
from('$buildDir/reports/license') {
include('dependency-license.html')
}
into '$buildDir/intermediates/assets/debug/legal'
}
apply plugin: 'license'
downloadLicenses {
includeProjectDependencies = true
reportByDependency = true
reportByLicenseType = true
dependencyConfiguration = "compile"
}
In command line:
gradlew clean assembleDebug
The resulting APK does not include the generated file as I was hoping. I'm quite new to Gradle so maybe there is a simple explanation?
First some observations. The solution is at the end:
You are still thinking procedurally: Telling Gradle what to do when (i.e. what you do in task beforeAssemble). Make use of the task graph: Specify what the dependencies of a task are (see below for some of the possibilities). When you need the output of a task, just ask Gradle to execute that task, and Gradle will figure out the transitive tasks that that task depends on. It will execute all of those tasks: only once, and in the correct order.
This declaration: task beforeAssemble(dependsOn: assemble), does not make sense. You want the task to run before assemble, but by depending on assemble you achieved the reverse: assemble will be run before beforeAssemble.
How to specify dependencies? There are multiple options:
Using a direct dependency: dependsOn.
Referring to other tasks in the inputs of a task.
Setting the options of a task, which will automatically create a dependency. For example: If the from attribute of the Copy task is set to the outputs of another task, the Copy task will automatically add that other task as an input.
Here is the solution. You want two achieve two things:
Generate the license information during the build (at some particular point).
Include the license information in the archive(s).
Add this to your build script:
android {
// ...
// Add the output folder of the license plug-in as
// a source folder for resources.
sourceSets {
main {
resources {
srcDir 'build/reports/license'
}
}
}
}
downloadLicenses {
includeProjectDependencies = true
reportByDependency = true
reportByLicenseType = true
dependencyConfiguration = "compile"
}
// Add a dependency after the project has been evaluated, because
// the Android plug-in does not add tasks "generate*Resources" to
// the project immediately.
project.afterEvaluate {
// Download the licenses when (actually: just before) the
// resources are generated (for both the "debug" and the
// "release" build types).
// If you add/change build types, you have to add to/change
// these task names.
generateDebugResources.dependsOn tasks['downloadLicenses']
generateReleaseResources.dependsOn tasks['downloadLicenses']
}
In my project (gradle 3.4.1), it works like this in the Android "Module:app" gradle file:
plugins {
id "com.github.hierynomus.license" version "0.14.0"
}
license {
include "**/*.java"
strictCheck = false
ignoreFailures = true
skipExistingHeaders = true
}
downloadLicenses {
includeProjectDependencies = true
reportByDependency = true
reportByLicenseType = false
dependencyConfiguration = "compile"
licenses = [
(group('com.android.support')) : license('Apache License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0'),
(group('com.android.support.constraint')) : license('Apache License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0'),
(group('com.google.firebase')) : license('Apache License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0'),
(group('com.google.android')) : license('Apache License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0'),
(group('com.google.android.gms')) : license('Apache License, Version 2.0', 'http://www.apache.org/licenses/LICENSE-2.0')
]
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
...
}
sourceSets {
main {
assets.srcDirs = ['src/main/assets', 'build/licenses/']
}
}
buildTypes {
...
}
}
dependencies {
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:multidex:1.0.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.support:design:25.3.1'
...
}
task copyLicenseReport(type: Copy) {
from('build/reports/license') {
include '**/*.html'
include '**/*.xml'
}
into 'build/licenses/thirdPartyLicenses'
}
project.afterEvaluate {
copyLicenseReport.dependsOn project.tasks.getByName('downloadLicenses')
preBuild.dependsOn copyLicenseReport
}
apply plugin: 'com.google.gms.google-services'
Try on changing CopyToAssetsTask into:
task CopyToAssetsTask << {
copy {
description = 'Copies generated Third Party Licenses into assets'
from('$buildDir/reports/license') {
include('dependency-license.html')
}
into '$buildDir/intermediates/assets/debug/legal'
}
}
because right now, your copying is done in configuration phase.
This solution worked for me and is a slightly modified version of the answer that Johan Stuyts posted.
One limitation though: The generated report will end up in the root assets folder instead of the intended subfolder "legal".
buildscript {
// ...
dependencies {
// ...
classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.11.0'
}
}
android {
// ...
// Add the output folder of the license plug-in as
// a source folder for assets.
sourceSets {
main {
// ...
assets.srcDirs = ['src/main/assets', 'build/reports/license']
}
}
}
downloadLicenses {
includeProjectDependencies = true
reportByDependency = true
reportByLicenseType = false
report.html.enabled = true
report.xml.enabled = false
dependencyConfiguration = "compile"
}
// Add a dependency after the project has been evaluated, because
// the Android plug-in does not add tasks "merge*Assets" to
// the project immediately.
project.afterEvaluate {
// Download the licenses when (actually: just before) the
// assets are merged (for both the "debug" and the
// "release" build types).
// If you add/change build types, you have to add to/change
// these task names.
mergeDebugAssets.dependsOn project.tasks.getByName('downloadLicenses')
mergeReleaseAssets.dependsOn project.tasks.getByName('downloadLicenses')
}

Resources