I'm working on a gradle multi project (java), where some of the sub-projects are applications (have a main class, and are executable) and some others are libraries (e.g, ends up being packaged as a jar, which some other sub-projects define a dependency on). It's working fine.
I'd like to be able to package (tar) the entire project for production, according to some structure that will make it easy for my users to deploy and use later on.
Currently, the distTar task creates a build/distribution/project-name.tar for each application project, and a build/libs/project-name.jar for each non application project, under each project build directory. That's a step in the right direction, but I'd like to consolidate the contents into one thing I can distribute.
As an example, right now after running distTar:
myapp/
README
docs/
services/
service1/
build/
libs/service1.jar
<other build dirs I don't want to distribute>
service2/
build/
distributions/service2.tar
<other build dirs I don't want to distribute>
service3/
build/
distributions/service3.tar
<other build dirs I don't want to distribute>
and the contents of service2.tar are:
service2/lib/service2.jar
service2/lib/some-service2-dependency.jar
service2/bin/service2 (start script)
service2/config.yml
(and similarly for service3.tar).
I'd like my final result to be a single myapp.tar(.gz) that includes a similar directory structure, but only with the production files:
README
docs/
services/
service1/
lib/service1.jar
service2/
lib/service2.jar
lib/some-service-dependency.jar
bin/service2 (start script)
config.yml
service3/
lib/service3.jar
lib/some-service-dependency.jar
bin/service3 (start script)
config.yml
Not sure what is the best way to achieve such a task. Do I create a parent level task that depends on distTar and copies files around, untar'ing stuff, etc? (tried that unsuccessfully).
Many thanks!
UPDATE:
I started doing something along these lines:
distributions {
main {
contents {
into('scripts') {
from {'scripts/sbin'}
}
into('service1') {
from {tarTree(tarFileForProject("service1"))}
}
into('service2') {
from {tarTree(tarFileForProject("service2"))}
}
into ...
}
}
}
distTar.dependsOn([subprojects.clean, subprojects.distTar])
(where tarFileForProject is a simple function that returns the path to the build/distributions/*.tar file of the given subproject name).
It seems to work, but also seems ugly. I wonder if there's a cleaner way to do this.
Related
During my gradle build, I generate a temporary buildinfo.properties file containing things like Git commit info, build time, etc. I would like to include this file in my output *.jar / *.war files as a resource. However, I do not want to put this file in my project src/ folder (this would require fiddling with .gitignore and in general it just seems unnecessary to me). Ideally, the developer shouldn't even see this file at all, it should just be contained in the output archive.
How would you include a dynamically generated text file in a Gradle build?
Add that file in a jar task (Kotin DSL):
tasks {
val jar by getting(Jar::class) {
from("build/buildinfo.properties")
}
}
It will add build/buildinfo.properties file (assuming you generate it there with another taks) to the root of your JAR.
For dynamically generated file, standard gradle way to process resources is the gradle task called processResources. You can do something like this:
processResources {
dependsOn taskThatGeneratesYourBuildinfo
from("build/buildinfo.properties") {
into("desired/path/in/jar")
}
}
I have a multi-module project in Gradle
root
-- ProjectA
-- ProjectB
Both ProjectA and ProjectB use the application plugin to create a zip in "ProjectA/build/distributions" and "ProjectB/build/distributions" respectively.
Now I want to copy the two zip files into "root/build/distributions".
I have tried various approaches, e.g. adding this in the build.gradle of the root project:
subprojects {
task copyFiles(type: Copy) {
includeEmptyDirs = true
from "$buildDir/distributions"
include '*.zip'
into "$parent.buildDir/distributions"
}
copyFiles.dependsOn(build)
}
or just adding a task to the root project:
task copyFiles(type: Copy) {
from "ProjectA/build/distributions"
from "ProjectB/build/distributions"
include "*.zip"
into "$buildDir/distributions"
}
build.dependsOn(copyFiles)
However, in both cases, nothing happens. No file gets copied.
What am I doing wrong?
I can see two things you are doing wrong:
You have relative paths to the subprojects. This is discouraged as it means you will always have to invoke Gradle from the root folder. And if a Gradle daemon was started from somehere else, it will fail. You could fix it by using the rootDir property (e.g. `from "$rootDir/ProjectA/...") but there is a better way...
The other problem is that you have no dependencies from your copyFiles task in your root project to the required distZip tasks in the sub-projects. So if the distributions have not already been built previously, there are no guarantees that it will work (which it apparently doesn't).
To fix it, you can have a look at the question "Referencing the outputs of a task in another project in Gradle", which covers the more general use case of what you ask. There are currently two answers, both of which are good.
So in your case, you can probably do either this:
task copyFiles(type: Copy) {
from tasks.getByPath(":ProjectA:distZip").outputs
from tasks.getByPath(":ProjectB:distZip").outputs
into "$buildDir/distributions"
}
or this:
task copyFiles(type: Copy) {
subprojects {
from(tasks.withType(Zip)) {
include "*.zip"
}
}
into "$buildDir/distributions"
}
Gradle will implicitly make the copy task depend on the other tasks automatically, so you don't need to do that yourself.
Also note that the currently accepted answer to the question I referenced is about configuration variants, and this is probably the most correct way to model the relationships (see here for more documentation on the topic). But I prefer the simplicity of the direct access to the tasks over the verbose and arguably more complex way to model it through configurations. Let's hope it gets simpler in a later release.
I have a Gradle project which has a lot of resources in the /src/main/resources folder. For performance reasons, some resources have to be generated from other resources at build time. So I split everything up into sub-projects:
/MainProj/src/main/java - The application
/MainProj/src/main/resources
/InternalProj/src/main/java - Code for generating additional resources for MainProj
/InternalProj/src/main/resources
When I run InternalProj using a special Gradle task, it generates some files to /InternalProj/output which I then copy to /MainProj/src/main/resources. Needless to say, this is really ugly and I'd like to know how to do this in a better way. Should I put it somewhere into build or directly into /MainProj/src/main/resources? Maybe use a symlink?
Edit
I now use a relative symlink from /src/main/generated-resources to /build/something and so far it works fine. But now I have a different problem: I have task A that generates some resources and task B that depends on those resources and thus on A. If I run gradle B or gradle A B, B will still fail since the resources generated by A didn't get updated into its build folder. How can I force Gradle to update the resources?
You can add the output file to the main source set of the InternalProj:
Example Groovy Code:
sourceSets {
main {
resources {
srcDirs "src/main/resources", "output"
}
}
}
Related answer: https://stackoverflow.com/a/38982006/3708426
I have a gradle project which at main is using java plugin. At subproj1 - thru subproj4 the build.gradle files are also using application plugin. It works well in sub projects to compile the jars (with a main class) and create a distribution zipfile (using resources and dist files).
Now I want to build a 'main' dist zip file, comprising of the contents of all those subproj contents. I found I can run installDist to unzip to each of the subprojects build/install/subprojN
Now at a loss howto in the main only, have a task and/or dependency to create a "main" dist zip file containing: subproj1/** subproj2/** subproj3/** subproj4/**
My thoughts are to do a copy from('.').include('subproj*/build/install//')
then zip that up. but havent figured out howto add the task only at main level, plus have it not complain: NO SOURCE
thanks in advance
Here's an example that just uses two sub-projects, and does not show the compilation of the main code. That stuff should be easy to add. The key to the solution is the zip and zipfileset via the AntBuilder.
Consider this in the main build.gradle:
task uberBuild(dependsOn: ['subProj1:build',
'subProj2:build']) {
doLast {
ant.mkdir(dir: "${projectDir}/dist")
def PATH = "build/distributions"
ant.zip(destfile: "${projectDir}/dist/uber.zip") {
zipfileset(src: "${projectDir}/subProj1/${PATH}/subProj1.zip")
zipfileset(src: "${projectDir}/subProj2/${PATH}/subProj2.zip")
}
}
}
This will write ~/dist/uber.zip, which has the contents:
$ jar tf dist/uber.zip
subProj1/
subProj1/bin/
subProj1/lib/
subProj1/bin/subProj1
subProj1/bin/subProj1.bat
subProj1/lib/subProj1.jar
subProj2/
subProj2/bin/
subProj2/lib/
subProj2/bin/subProj2
subProj2/bin/subProj2.bat
subProj2/lib/subProj2.jar
Is it possible to embed multiple projects in a single build.gradle?
Something along the lines of:
project projX {
task a << {
}
}
project projY {
task a << {
}
}
Both within the same build.gradle. Is this possible?
I am asking this because I have multiple projects with equivalent task names which I want to execute from the root project, e.g.
gradle a
However the projects contain only automation tasks, which require no source files or resource files at all. Creating subdirectories just for the build.gradle files to be stored seems very ugly to me.
I could live with a solution with different .gradle files for each project, such as: build.gradle (root)
projA.gradle, projB.gradle within the same directory, however embedding project objects in the root build.gradle seems like the better option, if it is available.
project(":projX") { ... }
project(":projY") { ... }
Note that you still need a settings.gradle.
PS: It's not clear to me why you would want multiple projects in your case.