75

I have a Gradle build script into which I am trying to include Eric Wendelin's CSS plugin.

It's easy enough to implement, and because I only want minification (rather than combining and gzipping), I've got the pertinent parts of the build script looking like this:

minifyCss {
    source = "src/main/webapp/css/brandA/styles.css"
    dest = "${buildDir}/brandA/styles.css"
    yuicompressor {
        lineBreakPos = -1
    }
}

war {
    baseName = 'ex-ren'
}

war.doFirst {
    tasks.myTask.minifyCss.execute()
}

This is perfect - when I run the gradle war task, it calls the minifyCss task, takes the source css file, and creates a minified version in the buildDir

However, I have a handful of css files which need minify-ing, but not combining into one file (hence I'm not using the combineCss task)

What I'd like to be able to do is make the source and dest properties (assuming that's the correct terminology?) of the minifyCss task reference variables of some sort - either variables passed into the task in the signature, or global variables, or something ...

Something like this I guess (which doesn't work):

minifyCss(sourceFile, destFile) {
    source = sourceFile
    dest = destFile
    yuicompressor {
        lineBreakPos = -1
    }
}

war {
    baseName = 'ex-ren'
}

war.doFirst {
    tasks.myTask.minifyCss.execute("src/main/webapp/css/brandA/styles.css", "${buildDir}/brandA/styles.css")
    tasks.myTask.minifyCss.execute("src/main/webapp/css/brandB/styles.css", "${buildDir}/brandB/styles.css")
    tasks.myTask.minifyCss.execute("src/main/webapp/css/brandC/styles.css", "${buildDir}/brandC/styles.css")
}

This doesn't work either:

def sourceFile = null
def destFile = null

minifyCss {
    source = sourceFile
    dest = destFile
    yuicompressor {
        lineBreakPos = -1
    }
}

war {
    baseName = 'ex-ren'
}

war.doFirst {
    sourceFile = "src/main/webapp/css/brandA/styles.css"
    destFile = "${buildDir}/brandA/styles.css"
    tasks.myTask.minifyCss.execute()
}

For the life of me I cannot work out how to call a task and pass variables in :(

Any help very much appreciated;

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Nathan Russell
  • 3,428
  • 5
  • 30
  • 51
  • Does this answer your question? [How to pass arguments from command line to Gradle](https://stackoverflow.com/questions/11696521/how-to-pass-arguments-from-command-line-to-gradle) – Mahozad May 03 '22 at 12:21

7 Answers7

90

You should consider passing the -P argument in invoking Gradle.

From Gradle Documentation :

--project-prop Sets a project property of the root project, for example -Pmyprop=myvalue. See Section 14.2, “Gradle properties and system properties”.

Considering this build.gradle

task printProp << {
    println customProp
}

Invoking Gradle -PcustomProp=myProp will give this output :

$ gradle -PcustomProp=myProp printProp
:printProp
myProp

BUILD SUCCESSFUL

Total time: 3.722 secs

This is the way I found to pass parameters.

ThomasW
  • 16,981
  • 4
  • 79
  • 106
Bipi
  • 3,429
  • 1
  • 20
  • 22
73

If the task you want to pass parameters to is of type JavaExec and you are using Gradle 5, for example the application plugin's run task, then you can pass your parameters through the --args=... command line option. For example gradle run --args="foo --bar=true".

Otherwise there is no convenient builtin way to do this, but there are 3 workarounds.

1. If few values, task creation function

If the possible values are few and are known in advance, you can programmatically create a task for each of them:

void createTask(String platform) {
   String taskName = "myTask_" + platform;
   task (taskName) {
      ... do what you want
   }
}

String[] platforms = ["macosx", "linux32", "linux64"];
for(String platform : platforms) {
    createTask(platform);
}

You would then call your tasks the following way:

./gradlew myTask_macosx

2. Standard input hack

A convenient hack is to pass the arguments through standard input, and have your task read from it:

./gradlew myTask <<<"arg1 arg2 arg\ in\ several\ parts"

with code below:

String[] splitIntoTokens(String commandLine) {
    String regex = "(([\"']).*?\\2|(?:[^\\\\ ]+\\\\\\s+)+[^\\\\ ]+|\\S+)";
    Matcher matcher = Pattern.compile(regex).matcher(commandLine);
    ArrayList<String> result = new ArrayList<>();
    while (matcher.find()) {
        result.add(matcher.group());
    }
    return result.toArray();   
}

task taskName, {
        doFirst {
            String typed = new Scanner(System.in).nextLine();
            String[] parsed = splitIntoTokens(typed);
            println ("Arguments received: " + parsed.join(" "))
            ... do what you want
        } 
 }

You will also need to add the following lines at the top of your build script:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Scanner;

3. -P parameters

The last option is to pass a -P parameter to Gradle:

./gradlew myTask -PmyArg=hello

You can then access it as myArg in your build script:

task myTask {
    doFirst {
       println myArg
       ... do what you want
    }
}

Credit to @789 for his answer on splitting arguments into tokens

Guy
  • 12,250
  • 6
  • 53
  • 70
Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
  • 2
    Same for this answer, its not really applicable to me. I want to call the task many times pragmatically from within the gradle build script. Is there a way to do this? – Ian Vaughan Dec 07 '18 at 19:48
  • @IanVaughan I think you may be misunderstanding how gradle works. So that we can help you, can you share where the different values would come from? Do you know in advance what they would be? – Vic Seedoubleyew Dec 08 '18 at 20:04
  • @VicSeedoubleyew I am facing the same problem as Ian is facing. I am calling a parent task from the command line and this task has two child tasks that require the parameters to be passed in the script itself. Is there a way to do it? – Parag Kadam Jan 13 '21 at 11:05
  • @ParagKadam what do you mean "child tasks"? What is a "parent" and a "child" for you? – Vic Seedoubleyew Jan 15 '21 at 10:20
  • I didn't know about stdin. Just what the Unix Philosopher in me wants. – Sridhar Sarnobat Jun 07 '23 at 17:47
9

I would suggest the method presented on the Gradle forum:

def createMinifyCssTask(def brand, def sourceFile, def destFile) {
    return tasks.create("minify${brand}Css", com.eriwen.gradle.css.tasks.MinifyCssTask) {
        source = sourceFile
        dest = destFile
    }
}

I have used this method myself to create custom tasks, and it works very well.

AutonomousApps
  • 4,229
  • 4
  • 32
  • 42
7
task mathOnProperties << {
    println Integer.parseInt(a)+Integer.parseInt(b)
    println new Integer(a) * new Integer(b)
}

$ gradle -Pa=3 -Pb=4 mathOnProperties
:mathOnProperties
7
12

BUILD SUCCESSFUL
Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
user3877963
  • 348
  • 3
  • 7
6

Its nothing more easy.

run command: ./gradlew clean -PjobId=9999

and

in gradle use: println(project.gradle.startParameter.projectProperties)

You will get clue.

Milan Jurkulak
  • 557
  • 3
  • 6
  • What is `-P`? And why do we put it together with the arg? can't we do `gradlew clean -P myArg=myValue`? btw thanks for the short and clean answer – Alex Rintt Dec 17 '21 at 17:15
  • -P is to add properties/parameters to gradle call, then you can access them in project.gradle.startParameter.projectProperties – Milan Jurkulak Dec 29 '21 at 08:24
3

I think you probably want to view the minification of each set of css as a separate task

task minifyBrandACss(type: com.eriwen.gradle.css.tasks.MinifyCssTask) {
     source = "src/main/webapp/css/brandA/styles.css"
     dest = "${buildDir}/brandA/styles.css"
}

etc etc

BTW executing your minify tasks in an action of the war task seems odd to me - wouldn't it make more sense to make them a dependency of the war task?

Perryn Fowler
  • 2,192
  • 1
  • 12
  • 20
  • Thanks for your reply, I think I understand ... but I cant get it to work :( - When I define my task as you've said like this: "task minifyBrandACss(type: MinifyCssTask) { ..." I get the this when running it: "Could not find property 'MinifyCssTask' on root project 'ex-ren'". I guess its a classpath issue?? But I dont know where to set it? I already have "classpath 'com.eriwen:gradle-css-plugin:1.8.0'" within my buildscript { dependencies { closeure – Nathan Russell Dec 06 '13 at 08:45
  • 1
    apologies - you probably need to fully qualify the type - I have edited my answer – Perryn Fowler Dec 06 '13 at 12:49
3

Here is a solution for Kotlin DSL (build.gradle.kts).

I first try to get the variable as a property and if it was null try to get it from OS environment variables (can be useful in CIs like GitHub Actions).

tasks.create("MyCustomTask") {
    val songName = properties["songName"]
        ?: System.getenv("SONG_NAME")
        ?: error("""Property "songName" or environment variable "SONG_NAME" not found""")

    println("The song name: $songName")
}

We can then pass a value for the property from command line:

./gradlew MyCustomTask -PsongName="Black Forest"

Or create a file named local.properties at the root of the project and set the property:

songName=Black Forest

We can also add an env variable named SONG_NAME with our desired value and then run the task:

./gradlew MyCustomTask
Mahozad
  • 18,032
  • 13
  • 118
  • 133