Gradle project, sub projects executed repeatedly - gradle

I have a project with sub projects. The layout:
rootproj
--subproj1
----mybuild.number
--subproj2
--build.gradle
--gradle.properties
--settings.gradle
mybuild.number
#Build Number for ANT. Do not edit!
#Wed Nov 210 2121:210:2121 PST 2102121
build.number=1
settings.gradle
include ('subproj1', 'subproj2')
build.gradle
allprojects {
repositories {
mavenLocal()
maven {url "http://repo1.maven.org/maven2"}
}
}
subprojects {
project (':subproj1') {
def oldN = new File("D:/rootproj/subproj1/mybuild.number").text.split("=")[1]
def newN = (oldN.toInteger() + 1).toString()
ant.replace(
file: "mybuild.number",
token: "${oldN}",
value: "${newN}"
)
println "From subproj1 : ${newN}"
task hello(overwrite: true){
doLast{
println "hello from subproject 1"
}
}
}
project (':subproj2'){
println "the build Dir: $buildDir"
task hello(overwrite: true){
doLast{
println "hello from subproject 2"
}
}
}
}
when I run
gradle -q subproj1:hello
or
gradle -q subproj2:hello
or
gradle
from the rootproj, I always get e.g.
....
From subproj1 : 24
the build Dir: D:\rootproj\subproj2\build
From subproj1 : 25
the build Dir: D:\rootproj\subproj2\build
1. Why the two sub projects always get executed twice, therefore the build number is incremented twice, instead of once?
2. why all sub projects get executed even though I explicitly specified the project:task in the command line?
I have searched the Internet, could not find useful information.
Thanks for your help in advance.
EDIT:
Change build.gradle as #JB Nizet suggested:
move the ant.replace to task incr
comment out subprojects
It works exactly as I expected.
allprojects {
repositories {
mavenLocal()
maven {url "http://repo1.maven.org/maven2"}
}
}
//subprojects {
def oldN = new File("E:/hqin/build/gradle/rootproj/subproj1/mybuild.number").text.split("=")[1]
def newN = (oldN.toInteger() + 1).toString()
project (':subproj1') {
task incr {
doLast{
ant.replace(
file: "mybuild.number",
token: "${oldN}",
value: "${newN}"
)
}
println "From subproj1 : ${newN}"
}
task hello(overwrite: true, dependsOn: 'incr'){
doLast{
println "hello from subproject 1"
}
}
}
project (':subproj2'){
task hello(overwrite: true){
doLast{
println "the build Dir: $buildDir"
println "hello from subproject 2"
}
}
}
//}

Regarding the second point: because your code is run as part of the project configuration, which is always run, whatever the task being executed. If you want code being executed when a task is executed, then it should be inside the doLast closure of a task definition:
task someTask {
doLast {
...
}
}
Regarding the first point: the closure passed to subprojects is executed for every subproject. So, it's called once for the subproject 1, and configures subproject 1 and subproject 2, then it's called again for subproject 2, and reconfigures subproject 1 and subproject 2 again. You shouldn't have subprojects at all.

Related

How to run certain task with gradle

I try to investigate a Gradle and follows some tutorials, but I have confused with the following:
I created are a couple of simple tasks:
task startProcess{
println 'startProcess'
}
task doStep2{
println 'Step2'
}
task doStep3{
println 'Step3'
}
task finishProcess{
println 'finishProcesss'
}
And try to execute one of them:
gradle finishProcess
Or with defaultTasks with command gradle build:
defaultTasks `finishProcess`
task startProcess{
println 'startProcess'
}
task doStep2{
println 'Step2'
}
task doStep3{
println 'Step3'
}
task finishProcess{
println 'finishProcesss'
}
In both options, I got the same result:
> Configure project :
startProcess
Step2
Step3
finishProcesss
BUILD SUCCESSFUL in 1s
How to execute exactly one of them?
You have to use register, I think if you did not use it, You're only asking Gradle to execute these tasks.
for example
tasks.register('startProcess') {
doLast {
println 'startProcess'
}
}
tasks.register('doStep2') {
doLast {
println 'Step2'
}
}
tasks.register('doStep3') {
doLast {
println 'Step3'
}
}
tasks.register('finishProcess') {
doLast {
println 'finishProcesss'
}
}
tasks.named("build") { finalizedBy("finishProcess") }
Registering these tasks, you will be able to call each one indivadually.
If you want to link a specific task, with a build task for example.
Then you can use finalizedBy like the following.
tasks.named("build") { finalizedBy("finishProcess") }
This will call the finishProcess task, whenever build is triggered.
I strongly recommend the official gradle documintation for more information about tasks.

How to reference a subproject dir from a root gradle task when calling task from subproject

I have a root project gradle task defined below. I am
task createVersionTxtResourceFile {
doLast {
def webAppVersionFile = new File("$projectDir/src/main/resources/VERSION.txt")
def appVersion = project.ext.$full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
In a few subprojects, I want to run this task and create the VERSION.txt file in the subproject's src/main/resources/VERSION.txt. My problem is that the root level task's $projectDir is the root project.
Is it possible to define a root level task that uses the sub-project directory when invoking it? Or perhaps there's a better approach all together.
You could do it slightly more controlled when you register an action to wait for the java plugin to be applied on the subproject. With this you can create the task only in subprojects who contain the desired compileJava task and configure everything from the root project.
subprojects { sub ->
//register an action which gets executed when the java plugins gets applied.
//if the project is already configured with the java plugin
//then this action gets executed right away.
sub.plugins.withId("java") {
//create the task and save it.
def createVersionTxtResourceFile = sub.tasks.create("createVersionTxtResourceFile") {
doLast {
def webAppVersionFile = new File("${sub.projectDir}/src/main/resources/VERSION.txt")
def appVersion = rootProject.full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
// set the task dependency
sub.tasks.compileJava.dependsOn createVersionTxtResourceFile
}
}
I ended up just defining the task in each subproject, and setting a dependency on it in the appropriate subprojects:
subprojects {
task createVersionTxtResourceFile {
doLast {
def webAppVersionFile = new File("$projectDir/src/main/resources/VERSION.txt")
def appVersion = rootProject.full_version
println "writing VERSION.txt to " + webAppVersionFile + ", containing " + appVersion
webAppVersionFile.delete()
webAppVersionFile.write(appVersion)
}
}
}
then in a subproject build.gradle:
compileJava.dependsOn createVersionTxtResourceFile

How to run a Gradle task after apks are produced in Android Studio?

The following task (in build.gradle of an app's module) seems to run always before the apk is produced:
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def releaseBuildTask = tasks.create(name: "debug") {
println(".................... test ..............................")
}
releaseBuildTask.mustRunAfter variant.assemble
}
}
Could anyone offer a tip on how to run a task after the apks are produced?
Android tasks are typically created in the "afterEvaluate" phase.
Starting from gradle 2.2, those tasks also include "assembleDebug" and
"assembleRelease". To access such tasks, the user will need to use an
afterEvaluate closure:
afterEvaluate {
assembleDebug.dependsOn someTask
}
source: https://code.google.com/p/android/issues/detail?id=219732#c32
try add this in you app/build.gradle
assembleDebug.doLast {
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def releaseBuildTask = tasks.create(name: "debug") {
println(".................... test ..............................")
}
releaseBuildTask.mustRunAfter variant.assemble
}
}
println "build finished"
}
invoke the build command and specify the task assembleDebug
./gradlew assembleDebug
I found a solution that works, to copy the release APK into the project root automatically on build completion.
android {
...
task copyReleaseApk(type: Copy) {
from 'build/outputs/apk'
into '..' // Into the project root, one level above the app folder
include '**/*release.apk'
}
afterEvaluate {
packageRelease.finalizedBy(copyReleaseApk)
}
}

Execute gradle task on sub projects

I have a MultiModule gradle project that I am trying to configure.
Root
projA
projB
other
projC
projD
projE
...
What I want to be able to do is have a task in the root build.gradle which will execute the buildJar task in each of the projects in the other directory.
I know I can do
configure(subprojects.findAll {it.name != 'tropicalFish'}) {
task hello << { task -> println "$task.project.name"}
}
But this will also get projA and projB, I want to only run the task on c,d,e...
Please let me know the best way to achieve this.
Not entirely sure which of these you're after, but they should cover your bases.
1. Calling the tasks directly
You should just be able to call
gradle :other/projC:hello :other/projD:hello
I tested this with:
# Root/build.gradle
allprojects {
task hello << { task -> println "$task.project.name" }
}
and
# Root/settings.gradle
include 'projA'
include 'projB'
include 'other/projC'
include 'other/projD'
2. Only creating tasks in the sub projects
Or is it that you only want the task created on the other/* projects?
If the latter, then the following works:
# Root/build.gradle
allprojects {
if (project.name.startsWith("other/")) {
task hello << { task -> println "$task.project.name" }
}
}
and it can then be called with:
$ gradle hello
:other/projC:hello
other/projC
:other/projD:hello
other/projD
3. Creating a task that runs tasks in the subprojects only
This version matches my reading of your question meaning there's already a task on the subprojects (buildJar), and creating a task in root that will only call the subprojects other/*:buildJar
allprojects {
task buildJar << { task -> println "$task.project.name" }
if (project.name.startsWith("other/")) {
task runBuildJar(dependsOn: buildJar) {}
}
}
This creates a task "buildJar" on every project, and "runBuildJar" on the other/* projects only, so you can call:
$ gradle runBuildJar
:other/projC:buildJar
other/projC
:other/projC:runBuildJar
:other/projD:buildJar
other/projD
:other/projD:runBuildJar
Your question can be read many ways, hope this covers them all :)
All of the ways mentioned by Mark can be used but all of them have some cons. So I am adding one more option:
4. Switching the current project
gradle -p other hello
This switches the "current project" and then runs all tasks named hello under the current project.
Example 5. Defining common behavior of all projects and subprojects,
allprojects {
task hello {
doLast { task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println "- I depend on water"
}
}
}
From the Gradle documentation,
https://docs.gradle.org/current/userguide/multi_project_builds.html

(No such file or directory) how to make non existant file to be ignored by gradle

when i run the command 'gradle tasks' or anything for that fact, i got the following error:
Caused by: java.io.FileNotFoundException: /Users/maxit/workspace/Backbone/modules/contact-form/public/build/contact-src.js (No such file or directory)
Here is my gradle build file:
configurations {
sshAntTask
}
dependencies {
sshAntTask 'org.apache.ant:ant-jsch:1.7.1', 'jsch:jsch:0.1.29'
}
// Pull the plugin from Maven Central
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.eriwen:gradle-js-plugin:1.5.0'
}
}
// Invoke the plugin
apply plugin: 'js'
apply plugin:'base'
def appName = "some"
def version = "0.0.1"
def jsSrcDir = 'public/js'
javascript.source {
dev {
js {
srcDir jsSrcDir
include "*.js"
exclude "*.min.js"
}
}
prod {
js {
srcDir jsSrcDir
include "*.min.js"
}
}
}
task combineSrc(type: com.eriwen.gradle.js.tasks.CombineJsTask) {
source = ["${projectDir}/public/templates/templates.js","${projectDir}/public/js/models/contact_model.js", "${projectDir}/public/js/views/contact_form_view.js", "${projectDir}/public/js/app.js" ]
dest = file("${projectDir}/public/build/${appName}-src.js")
}
task appendJQuery(dependsOn: 'combineSrc') {
String backboneSrc = file(new File("${projectDir}/public/build/${appName}-src.js")).text
new File("${projectDir}/public/build/${appName}-jqueryWraped.js").withWriter{ out ->
out << "(function(\$){" + file("${projectDir}/public/build/${appName}-src.js").text + "})(jQuery); \n"
}
}
It appears, that gradle doesn't ignore a file that is none existant. The task 'combineSrc' hasn't been run, yet to create the file....and i am unable to run the task 'cobineSrc' to create the file in the first place. Its kind a dead end. what am i doing wrong and how to make this work? Thank you
The reason you're failing is, that all the stuff you currently doing during the configuration of the appendJQuery task should be done in the execution phase.
just refactor your appendJQuery task to do:
task appendJQuery(dependsOn: 'combineSrc') {
doLast{
String backboneSrc = file(new File("${projectDir}/public/build/${appName}-src.js")).text
new File("${projectDir}/public/build/${appName}-jqueryWraped.js").withWriter{ out ->
out << "(function(\$){" + file("${projectDir}/public/build/${appName}-src.js").text + "})(jQuery); \n"
}
}
}
hope that helps!
René

Resources