How to use startParameters in BuildGradle task? - gradle

I would like to pass deployDir (with value /my_archive) to uploadArchives task in my_project:
task build (type: GradleBuild) {
buildFile = './my_project/build.gradle'
tasks = ['uploadArchives']
/* startParameter = [deployDir:"/my_archive"] ??? */
}
I do not know how to declare the start parameters. I have tried different ways, e.g.,
startParameter = [deployDir:"/my_archive"]
Without success.
How to declare startParameter in the GradleBuild task?

I assume you mean to pass the deployDir as a project property. In this case, you'll find there is a setProjectProperties(Map) method you can use:
task build (type: GradleBuild) {
buildFile = './my_project/build.gradle'
tasks = ['uploadArchives']
startParameter.projectProperties = [deployDir: "/my_archive"]
}
This will enable you to access deployDir as a variable from the called build script:
uploadArchives {
repositories {
mavenDeployer {
repository(url: deployDir)
// --- or, if deployDir can be empty ---
repository(url: project.properties.get('deployDir', 'file:///default/path'))
}
}
}

we can set the project properties and system properties via the api
setProjectProperties(Map<String,String> projectProperties)
setSystemPropertiesArgs(Map<String,String> systemPropertiesArgs)
here is the sample from my local for startParameter:
task startBuild(type: GradleBuild) {
StartParameter startParameter = project.gradle.startParameter;
Iterable<String> tasks = new ArrayList<String>();
Iterable<String> excludedTasks = new ArrayList<String>();
startParameter.getProjectProperties().each { entry ->
println entry.key + " = " + entry.value;
if(entry.key.startsWith('t_')){
tasks << (entry.key - 't_');
}
if(entry.key.startsWith('build_') && "true" == entry.value){
tasks << (':' + (entry.key - 'build_') +':build');
}
if(entry.key.startsWith('x_') && "true" == entry.value){
excludedTasks << (entry.key - 'x_');
}
}
startParameter.setTaskNames(tasks);
startParameter.setExcludedTaskNames(excludedTasks);
println startParameter.toString();
}
we can reference the api from this link StartParameter
the startparameter is really powerful in gradle when you need to customize your gradle build logic.

Related

Gradle zip task with lazy include property includes itself

Hi I got this zip task which works great:
def dir = new File("${projectDir.parentFile}/test/")
task testZip(type: Zip) {
from dir
destinationDirectory = dir
include 'toast/**'
archiveFileName = 'test.zip'
}
but then when I make the include property lazy (because I need to in my real case)
def dir = new File("${projectDir.parentFile}/test/")
task testZip(type: Zip) {
from dir
destinationDirectory = dir
include {
'toast/**'
}
archiveFileName = 'test.zip'
}
then it creates a zip that includes everything in the folder, (so the generated archive too). In this test case the inner zip is just corrupted (doesn't run infinitely) but in the real world case it does make an infinite zip. (Not sure why, maybe my best case has too few or small files). Either way the test case shows the problem, the generated zip contains a zip even though it should only contain the toast directory and all of its content.
How do I fix this? I need a lazy include because the directory I want to include is computed by other tasks. I get the exact same problem with Tar except it refuses to create the archive since it includes itself.
Using exclude '*.zip' is a dumb workaround which makes the archive include other folders I don't want. I only want to include a specific folder, lazyly.
Here's what the monster looks like in the real world case. I basically need to retrieve the version of the project from Java to then use that version to name the folders I'm packaging. (Making a libGDX game and packaging it with a jre using packr). The problematic tasks are 'makeArchive_' + platform.
String jumpaiVersion;
task fetchVersion(type: JavaExec) {
outputs.upToDateWhen { jumpaiVersion != null }
main = 'net.jumpai.Version'
classpath = sourceSets.main.runtimeClasspath
standardOutput new ByteArrayOutputStream()
doLast {
jumpaiVersion = standardOutput.toString().replaceAll("\\s+", "")
}
}
def names = [
'win64' : "Jumpai-%%VERSION%%-Windows-64Bit",
'win32' : "Jumpai-%%VERSION%%-Windows-32Bit",
'linux64' : "Jumpai-%%VERSION%%-Linux-64Bit",
'linux32' : "Jumpai-%%VERSION%%-Linux-32Bit",
'mac' : "Jumpai-%%VERSION%%-Mac.app"
]
def platforms = names.keySet() as String[]
def jdks = [
'win64' : 'https://cdn.azul.com/zulu/bin/zulu9.0.7.1-jdk9.0.7-win_x64.zip',
'win32' : 'https://cdn.azul.com/zulu/bin/zulu9.0.7.1-jdk9.0.7-win_i686.zip',
'linux64' : 'https://cdn.azul.com/zulu/bin/zulu9.0.7.1-jdk9.0.7-linux_x64.tar.gz',
'linux32' : 'https://cdn.azul.com/zulu/bin/zulu9.0.7.1-jdk9.0.7-linux_i686.tar.gz',
'mac' : 'https://cdn.azul.com/zulu/bin/zulu9.0.7.1-jdk9.0.7-macosx_x64.zip'
]
def formats = [
'win64' : 'ZIP',
'win32' : 'ZIP',
'linux64' : 'TAR_GZ',
'linux32' : 'TAR_GZ',
'mac' : 'ZIP'
]
File jdksDir = new File(project.buildscript.sourceFile.parentFile.parentFile, 'out/jdks')
File gameJar = new File("${projectDir.parentFile}/desktop/build/libs/Jumpai.jar")
File gameData = new File("${projectDir.parentFile}/desktop/build/libs/Jumpai.data")
File packrDir = new File("${projectDir.parentFile}/out/packr/")
File minimalTmpDir = new File("${projectDir.parentFile}/desktop/build/libs/minimal-tmp")
task minimizeGameJar {
dependsOn ':desktop:dist'
doFirst {
minimalTmpDir.mkdirs()
copy {
from zipTree(gameJar)
into minimalTmpDir
}
for(file in minimalTmpDir.listFiles())
if(file.getName().contains("humble"))
file.delete()
}
}
task makeMinimal(type: Zip) {
dependsOn minimizeGameJar
dependsOn fetchVersion
from minimalTmpDir
include '**'
archiveFileName = provider {
"Jumpai-${->jumpaiVersion}-Minimal.jar"
}
destinationDir packrDir
doLast {
minimalTmpDir.deleteDir()
}
}
task copyGameJar(type: Copy) {
outputs.upToDateWhen { gameData.exists() }
dependsOn ':desktop:dist'
from gameJar.getAbsolutePath()
into gameData.getParentFile()
rename("Jumpai.jar", "Jumpai.data")
}
task setWindowsIcons(type: Exec) {
dependsOn fetchVersion
workingDir '.'
commandLine 'cmd', '/c', 'set_windows_icons.bat', "${->jumpaiVersion}"
}
for(platform in platforms) {
task("getJdk_" + platform) {
String url = jdks[platform]
File jdkDir = new File(jdksDir, platform + "-jdk")
File jdkFile = new File(jdkDir, url.split("/").last())
outputs.upToDateWhen { jdkFile.exists() }
doFirst {
if(!jdkDir.exists())
jdkDir.mkdirs()
if(jdkFile.exists())
{
println jdkFile.getName() + " is already present"
return
}
else
{
println "Downloading " + jdkFile.getName()
new URL(url).withInputStream {
i -> jdkFile.withOutputStream { it << i }
}
}
for(file in jdkDir.listFiles()) {
if(file.equals(jdkFile))
continue
if(file.isFile()) {
if (!file.delete())
println "ERROR: could not delete " + file.getAbsoluteFile()
} else if(!file.deleteDir())
println "ERROR: could not delete content of " + file.getAbsoluteFile()
}
if(url.endsWith(".tar.gz"))// don't mix up archive type of what we downloaded vs archive type of what we compress (in formats)
{
copy {
from tarTree(resources.gzip(jdkFile))
into jdkDir
}
}
else if(url.endsWith(".zip"))
{
copy {
from zipTree(jdkFile)
into jdkDir
}
}
}
}
File packrInDir = new File(packrDir, platform)
String platformRawName = names[platform]
task("packr_" + platform, type: JavaExec) {
outputs.upToDateWhen { new File(packrDir, platformRawName.replace("%%VERSION%%", jumpaiVersion)).exists() }
dependsOn fetchVersion
dependsOn copyGameJar
dependsOn 'getJdk_' + platform
main = 'com.badlogicgames.packr.Packr'
classpath = sourceSets.main.runtimeClasspath
args 'tools/res/packr_config/' + platform + '.json'
workingDir = project.buildscript.sourceFile.parentFile.parentFile
doLast {
File packrOutDir = new File(packrDir, platformRawName.replace("%%VERSION%%", jumpaiVersion));
packrOutDir.deleteDir()
if(packrOutDir.exists())
{
println "ERROR Could not delete packr output " + packrOutDir.getAbsolutePath()
return
}
if(!packrInDir.renameTo(packrOutDir))
println "ERROR Could not rename packr output dir for " + packrInDir.getName()
}
}
if(formats[platform] == 'ZIP')
{
task('makeArchive_' + platform, type: Zip) {
if(platform.contains("win"))
dependsOn setWindowsIcons
dependsOn fetchVersion
dependsOn 'packr_' + platform
from packrDir
destinationDirectory = packrDir
include {
platformRawName.replace("%%VERSION%%", jumpaiVersion) + "/"
}
archiveFileName = provider {
platformRawName.replace("%%VERSION%%", jumpaiVersion) + ".zip"
}
}
}
else if(formats[platform] == 'TAR_GZ')
{
task('makeArchive_' + platform, type: Tar) {
dependsOn 'packr_' + platform
from packrDir
destinationDirectory = packrDir
include {
platformRawName.replace("%%VERSION%%", jumpaiVersion) + '/**'
}
archiveFileName = provider {
platformRawName.replace("%%VERSION%%", jumpaiVersion) + ".tar.gz"
}
extension 'tar'
compression = Compression.GZIP
}
}
else
println 'Unsupported format for ' + platform
}
task deploy {
dependsOn makeMinimal
for(platform in platforms)
dependsOn 'makeArchive_' + platform
}
How do I fix this? I need a lazy include because the directory I want to include is computed by other tasks. I get the exact same problem with Tar except it refuses to create the archive since it includes itself.
You can get what you want by using the doFirst method and modifiying the tasks properties with the passed action.
task('makeArchive_' + platform, type: Zip) {
if(platform.contains("win"))
dependsOn setWindowsIcons
dependsOn fetchVersion
dependsOn 'packr_' + platform
from packrDir
destinationDirectory = packrDir
archiveFileName = provider {
platformRawName.replace("%%VERSION%%", jumpaiVersion) + ".zip"
}
doFirst {
def includeDir = platformRawName.replace("%%VERSION%%", jumpaiVersion)
// Include only files and directories from 'includeDir'
include {
it.relativePath.segments[ 0 ].equalsIgnoreCase(includeDir)
}
}
}
Please have also a look at this answer to a similar question. My solution is just a workaround. If you know your version at configuration phase you can achieve what you want more easily. Writing your own custom tasks or plugins can also help to clean up your build script.

Custom gradle script for Detekt with multi-module project

I'm trying to create a custom gradle task that will run the different detekt profiles I have setup.
Here is my Detekt config:
detekt {
version = "1.0.0.RC6-4"
profile("main") {
input = "$projectDir/app/src/main/java"
output = "$projectDir/app/build/reports/detekt"
config = "$projectDir/config/detekt-config.yml"
}
profile("app") {
input = "$projectDir/app/src/main/java"
output = "$projectDir/app/build/reports/detekt"
}
profile("database") {
input = "$projectDir/database/src/main/java"
output = "$projectDir/database/build/reports/detekt"
}
profile("logging") {
input = "$projectDir/logging/src/main/java"
output = "$projectDir/logging/build/reports/detekt"
}
profile("network") {
input = "$projectDir/network/src/main/java"
output = "$projectDir/network/build/reports/detekt"
}
}
And here is what I'm trying for the custom gradle task:
task detektAll {
group = 'verification'
dependsOn 'detektCheck'
doLast {
println "\n##################################################" +
"\n# Detekt'ed all the things! Go you! #" +
"\n##################################################"
}
}
I need to add -Ddetekt.profile=app and the others for each profile.
How can I accomplish this?

Change global ext property in gradle

I'm trying to write a build script for different environments. But the global property is not updated for specific tasks.
Here is the script:
ext {
springProfilesActive = 'development'
angularAppBasePath = '/test/'
}
task setActiveProfiles {
doLast {
if (project.hasProperty('activeProfiles')) {
springProfilesActive = project.property('activeProfiles')
}
}
}
task setProperties(dependsOn: ':setActiveProfiles') {
doLast {
if (springProfilesActive != 'development') {
angularAppBasePath = '/'
}
println springProfilesActive
println angularAppBasePath
}
}
task buildAngular(type: Exec, dependsOn: ':setProperties') {
workingDir angularPath
commandLine 'cmd', '/c', "npm run build.prod -- --base ${angularAppBasePath}"
}
If I run buildAngular -PactiveProfiles=integration the properies are correctly set. But the angularAppBasePath is still the old /test/ value in the npm command. Output:
Executing external task 'buildAngular -PactiveProfiles=integration'...
:setActiveProfiles
:setProperties
integration
/
:buildAngular
> angular-seed#0.0.0 build.prod C:\myapp\src\main\angular
> gulp build.prod --color --env-config prod --build-type prod "--base" "/test/"
Why the propery are changed in the setProperties task but remains the old value in the buildAngular task?
Try to rewrite your setActiveProfiles and setProperties tasks as follows:
task setActiveProfiles {
if (project.hasProperty('activeProfiles')) {
springProfilesActive = project.property('activeProfiles')
}
}
task setProperties(dependsOn: ':setActiveProfiles') {
if (springProfilesActive != 'development') {
angularAppBasePath = '/'
}
println springProfilesActive
println angularAppBasePath
}
This behavior is caused by different build lifecycles. You modify your variable during execution phase (within doLast closure), but use it at the configuration phase, which happens just before the execution.You can read about build lifecycle in the Gradle official user guide.

Task running unnecessarily

I have written a task to run my project using a main class chosen via user input only it is prompting me to choose a main class when I run gradle tasks. Why is this and how do I prevent it?
task run(dependsOn: "classes", type: JavaExec) {
description "Executes the project using the selected main class"
def selection = null
def mainClasses = []
// Select the java files with main classes in
sourceSets.main.allJava.each {
if(it.text.contains("public static void main")) {
def pkg = relativePath(it) - 'src/main/java/' - '.java'
pkg = pkg.tr "/", "."
println "${mainClasses.size()}. $pkg"
mainClasses << pkg
}
}
// Now prompt the user to choose a main class to use
while(selection == null) {
def input = System.console().readLine "#? "
if(input?.isInteger()) {
selection = input as int
if(selection >= 0 && selection < mainClasses.size()) {
break
} else {
selection = null
}
} else if(input?.toLowerCase() == "quit") {
return
}
if(selection == null) {
println "Unknown option."
}
}
main = mainClasses[selection]
classpath = sourceSets.main.runtimeClasspath
}
Gradle has a configuration phase and an execution phase.
The fact that your build logic is actually run when calling "gradle tasks" is because your build logic is in the tasks configuration section. If you want to move it to the execution phase, you should introduce a doFirst or doLast closure
See gradle build script basics for more details or this post

With Gradle, is it possible to call GradleBuild directly instead of specifying it as a type?

I have this in my build.gradle:
task cleanCommon(type: GradleBuild) {
buildFile = 'common/build.gradle'
tasks = ['clean']
}
task cleanCrawler(type: GradleBuild) {
buildFile = 'crawler/build.gradle'
tasks = ['clean']
}
task cleanPortlet(type: GradleBuild) {
buildFile = 'portlet/build.gradle'
tasks = ['clean']
}
task cleanAll(dependsOn: ['cleanCommon', 'cleanCrawler', 'cleanPortlet']) {
}
It's so verbose. Is there some way I could do something like the pseudo code below?
taskCleanAll {
GradleBuild.pleaseRunTask('common/build.gradle', 'clean')
GradleBuild.pleaseRunTask('crawler/build.gradle', 'clean')
GradleBuild.pleaseRunTask('portlet/build.gradle', 'clean')
}
You cannot invoke a task directly, but there are plenty of other ways to abstract over the code. For example:
def createBuildTask(name, buildFile) {
task "$name"(type: GradleBuild) {
buildFile = buildFile
tasks = ['clean']
}
}
createBuildTask("cleanCommon", "common/build.gradle")
createBuildTask("cleanCrawler", "crawler/build.gradle")
createBuildTask("cleanPortlet", "portlet/build.gradle")
I wonder why you use the GradleBuild task in the first place, but that's a different discussion.
def List buildFileList = ['common/build.gradle', 'crawler/build.gradle', 'portlet/build.gradle']
task cleanAll << {
buildFileList.each() {
def tempTask = tasks.create(name: "execute_$it", type: GradleBuild)
tempTask.buildFile="$it"
tempTask.tasks = ['clean']
tempTask.execute()
}
}
Something like this could work... Note this is not tested. But why do you think it is so verbose? I think its rather clean like that.
task cleanAll << {
StartParameter startParameter = project.gradle.startParameter.newBuild()
startParameter.tasks = ['clean']
runGradle('portlet/build.gradle', startParameter)
runGradle('crawler/build.gradle', startParameter)
runGradle('common/build.gradle', startParameter)
}
def runGradle(buildFile, params) {
params.buildFile = buildFile
GradleLauncher.newInstance(params).run().rethrowFailure();
}
Another option would be to extract the task creation:
task cleanAll
def createCleanTask(name) {
task "${name}Clean"(type: GradleBuild) {
tasks = ['clean']
buildFile = "${name}/build.gradle"
}
cleanAll.dependsOn("${name}Clean")
}
createCleanTask("crawler")
createCleanTask("portlet")
createCleanTask("common")

Resources