gradle: avoid same custom task in multiple project - gradle

I have following folder structure...
project/
project/library
project/app1
project/app2
Now I have build one custom task publish (copy my android apks to a server folder). This task is in /app1/build.gradle and /app2/build.gradle.
My task is in both app same, except I have set project.ext variables. How can I put this task in /build.gradle(in root), because I don't want to change every single build.gradle in every app. The problem is, that /library should not have this task.
Any ideas?

Have a look at this document section: 14.3. Configuring the project using an external build script
UPDATE
Structure
p1
build.gradle - empty file
p2
build.gradle
build.gradle - empty file
settings.gradle
custom.gradle
custom.gradle:
task hello << {
println "hello!"
println project.sample_prop
}
settings.gradle:
include 'p1', 'p2'
p2/build.gradle
apply from: '../custom.gradle'
project.ext.sample_prop = '1'

Related

Conditionally skip subproject in multi-module Gradle build

Consider the following Gradle project structure:
- root
|- build.gradle
|- barProject
|- build.gradle
|- fooProject
|- build.gradle
where the root build.gradle configures its subprojects like so:
subprojects {
apply plugin: 'java'
//bunch of other stuff
}
Now when I call gradlew build on the root project it automatically configures both subprojects and then builds them - all well and good.
I also know that I can skip a specific task with various means (onlyIf(), [taskName].enabled = false, etc.) but when I utilize any of those on build Gradle still runs all the dependent tasks (compileJava, processResources, classes, etc.) until finally hitting build which it then skips.
My question is, is there any way to have Gradle stop and go to the next subproject right after the configuration phase?
EDIT:
To clarify; each subproject has a property called skip that is evaluated in the configuration phase.
Now when I call gradlew build on the root project I want Gradle to, ideally, check that property and if it's true then to completely skip the corresponding project.
Executing external task 'build'...
//check project.skip -> IF true -> skip project, go to :foo ELSE continue
:bar:compileJava
:bar:processResources UP-TO-DATE
:bar:classes
:bar:jar
:bar:startScripts
:bar:distTar
:bar:distZip
:bar:assemble
:bar:compileTestJava UP-TO-DATE
:bar:processTestResources UP-TO-DATE
:bar:testClasses UP-TO-DATE
:bar:test UP-TO-DATE
:bar:check UP-TO-DATE
:bar:build
//check project.skip -> IF true -> skip project, go to end ELSE continue
:foo:compileJava
:foo:processResources UP-TO-DATE
:foo:classes
:foo:jar
:foo:startScripts
:foo:distTar
:foo:distZip
:foo:assemble
:foo:compileTestJava UP-TO-DATE
:foo:processTestResources UP-TO-DATE
:foo:testClasses UP-TO-DATE
:foo:test UP-TO-DATE
:foo:check UP-TO-DATE
:foo:build
BUILD SUCCESSFUL
I hope this makes more sense
Well, first of all: An easy solution would be calling exactly the task(s) you need. In simple Gradle builds, task names are unique. However, in multi-project builds, each project can have a task with a specific name. This is the reason why Gradle introduced task paths. Task paths combine the unique project paths with the intra-project unique task names: :projectX:taskY.
Using project paths, you can easily specify the project-specific task you want to execute: :build for the build task in the root project and :<subproject>:build for the build task in a subproject. If a task name, e.g. build, is provided for a multi-project build, Gradle will search through any project (root and subs) for a task with the specified name and execute them all. This explains the current behaviour.
The task names for execution are managed by a StartParameter object of the Gradle Settings. These Settings can be modified in your settings.gradle file, where you also include subprojects:
include ':foo', ':bar'
startParameter.excludedTaskNames += ':foo:build'
This line excludes the build task of the foo subproject from the execution. You could add the subproject task path (even independent from the task name) to the excluded task names, if a specific condition is met. But, I did not find a way to access the Settings in your build file, so this solution can not be used during configuration phase. However, if your condition is solely based on project / system properties, they can be accessed from settings.gradle.
Another solution for the configuration phase came to my mind, but it's basically what you already mentioned, because it simply skips the tasks:
if (project.skip) {
project.tasks.all { task -> task.enabled = false }
}
This way, all tasks from the project (not only the build task) will be skipped.
Please consider, that tasks can also be executed, because they create a dependency for another project. Even gradle :bar:build will execute the task :foo:jar and its task dependencies, if foo is a project dependency of bar. You can disable this behaviour with the Gradle command line options -a or --no-rebuild or the following entry in your settings.gradle:
startParameter.buildProjectDependencies = false

Complex packaging in gradle for servers with different binaries

I have the following dependency structure
prodwebserver -> prod.jar -> transitive prod dependencies
devwebserver -> prodwebserver.jar, runtimeCompiler.jar, dev-router.jar -> more transitive dependencies
My devwebserver 'will' have ZERO source code. In gradle, currently I have a target called embeddabledevwebserver which depends on those.
Now I need to package up a release into a format of
release
|
|--prod - contains all prod jars
|
|--development - contains ONLY the extra jars that prod is missing
How can I get the difference in jar sets between the two targets such that I only put the extra jars needed for development in the development directory?
(this is nitpicky and not too important) Is there a way I can do this without having this empty embeddabledevwebserver project which is sort of an annoying shell project?
My actual current build file ...(WIP)...
https://github.com/deanhiller/webpieces/blob/gradlePackaging/build.gradle
(comments on that file welcome and appreciated)
EDIT: More specifically, I have these sections to start copying/syncing files over
task stageTemplate(type: Copy) {
from '.'
into buildDir
include stagingDirName + '/**'
}
task stageWebServer(type: Sync, dependsOn: [':embeddablewebserver:assemble', 'stageTemplate']) {
from childProjects.embeddablewebserver.toStagingDir
into new File(outputStagingDir, 'prod')
}
task stageDevServer(type: Sync, dependsOn: [':http-router-dev:assemble', 'stageWebServer']) {
from childProjects['http-router-dev'].toStagingDir
into new File(outputStagingDir, 'development')
exclude stageWebServer
}
and I can't exclude stageWebServer from stageDevServer but basically for all the jars that stageWebServer task moved over, I want to filter out all the jars with those same names for the development one.
thanks,
Dean
ok, I finally worked through this adding println to print every object and got to this which works for copy at least...I am not sure if Sync is fully working(ie. if I remove a file from prod and it is still in development, will it show up or not).
task stageWebServer(type: Sync, dependsOn: [':embeddablewebserver:assemble', 'stageTemplate']) {
from childProjects.embeddablewebserver.toStagingDir
into new File(outputStagingDir, 'prod')
}
task stageDevServer(type: Sync, dependsOn: [':http-router-dev:assemble', 'stageWebServer']) {
from childProjects['http-router-dev'].toStagingDir
into new File(outputStagingDir, 'development')
exclude { details ->
def fileNames = stageWebServer.source.collect{ entry -> entry.getName()}
fileNames.contains(details.file.getName())
}
}

Understanding gradle multi project build.gradle execution order

I'm doing some experiments on below gradle multi project structure
I have added println in all the build.gradle & settings.gradle files to see in what order they execute. I'm seeing that projectA -> build.gradle file is executing after its subproject pA1->build.gradle as seen in the output below
I'm not understanding why projectA->build.gradle is executing after its sub projects ? Should it not execute before its subproject, just like the build.gradle file on root.
location on project Multi project sample
First off, putting a naked println line in your build.gradle files does not give you execution order. The println will be invoked in the configuration phase, so if anything you're looking at the configuration order.
If you want to investigate execution order add a task with the same name to all your build.gradle files, maybe something like this:
task action << {
println("In project: ${project.name}")
}
and then run gradle action from the root folder.

Gradle multi-project custom build.gradle file name

I have a multi-project Gradle build, which is currently configured through a single build.gradle file.
There are over 70 modules in this project, and the single (gigantic) build.gradle file has become cumbersome to use, so I'd like to split it into small per-module buildscript files.
Now, I don't want to have 70 small build.gradle files (one in each module), as that would make navigating to a specific build.gradle a pain in the IDE (the only difference between the files is their path).
What I want is my per-module buildscript files to be named after the module name.
Instead of this:
root
|--foo\
|--| build.gradle
|--bar\
|--| build.gradle
I want this:
root
|--foo\
|--| foo.gradle
|--bar\
|--| bar.gradle
Since this doesn't seem to be officially supported, I tried hacking around the root build.gradle a bit, but it seems that applying a .gradle file happens before the projects are configured, so this gives an error for projects that depend on other projects:
in root build.gradle:
subprojects { subProject ->
rootProject.apply from: "${subProject.name}/${subProject.name}.gradle"
}
foo.gradle, which is not a standard build.gradle file:
project('foo') {
dependencies {
compile project(':bar')
}
}
Is there any way of making it work like this?
A web search for "gradle rename build.gradle" rendered the below example settings.gradle file:
rootProject.buildFileName = 'epub-organizer.gradle'
rootProject.children.each { project ->
String fileBaseName = project.name.replaceAll("\p{Upper}") { "-${it.toLowerCase()}" }
project.buildFileName = "${fileBaseName}.gradle"
}
Note that the author is here also renaming the root project's build script, which you may or may not want.
One of the authors of Gradle, Hans Dockter, has said somewhere (I believe it was in his "Rocking the Gradle" demo from 2012), that he felt one of their biggest mistakes was using build.gradle as the default file name.
You can customize name of your build scripts in settings.gradle file. Check recent presentation from Ben Muschko about multi-project builds or look at Gradle sources where similar customization is done.
rootProject.children.each {
it.buildFileName = it.name + '.gradle'
}
You can find this content in Gradle in action, manning

How to copy properties from a parent project in gradle

I'm trying to share one messages.properties (of each language) among multiple subprojects in gradle, one of which is a war and the rest are jars. My directory structure looks like this:
Top Level Project
+ Project War/
+ Project Jar/
+ Project Jar/
...
+ common/resources/properties files
+ build.gradle
I am trying to do something like this in the main project level build.gradle (which doesn't look to be working for me):
task copyProperties(type: Copy) {
description = 'Copies the messages.properties to individual projects.'
from relativePath('./common/resources')
into output.resourceDir
include '*.properties'
}
I'm calling this from a subproject's compileJava.dependsOn and I don't see any errors, but the copy doesn't happen.
You can inject a copy task like this into each sub-project. Instead of specifying the 'from' as a relative path, base it on $rootDir.
Each copy task should then be called as needed when building each sub-project.

Resources