How to copy properties from a parent project in gradle - 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.

Related

How to get property of Gradle task from an included build?

I have a composite Gradle build and in the root build I would like to reference a property of a task in one of the included builds, is this possible?
E.g. if I have this structure:
rootBuild
+ includedBuild1
+ project1
+ project2
+ includedBuild2
I know that in the build.gradle of "project1" I can use this statement to output the property I am interested in:
println shadowJar.archiveFile.get()
For some reason I don't understand, from this project's parent build.gradle ("includedBuild1") I can't reference the 'shadowJar' plugin task directly, but I am able to get the property I'm interested in like this:
println tasks.getByPath(':project1:jar').archiveFile.get()
Now I would like to reference this same property in the root build. However, trying something like this indicates that property 'archiveFile' is not set on the refenced task (i.e. outputs "null"):
println gradle.includedBuild('includedBuild1').task(':project1:jar').properties['archiveFile']
Is there a way to reference the property I'm interested in from the root of the composite build?

How do I use Gradle to build a special JAR with only a subset of classes?

I have been given a project A that needs access to class files from another project B. More precisely, A only needs classes compiled from the B/ejb/C/src portion of the B/ tree:
B/ejb/C/src/com/company/admin/Foo.java
B/ejb/C/src/com/company/admin/FooHome.java
B/ejb/C/src/com/company/admin/FooBean.java
B/ejb/NOTNEEDED/src/com/company/data/...
The person who had this A project before used JBuilder and included in the source definition pointers to the parallel project's B/ejb/C/src. The A project builds a jar which includes classes compiled from this other tree. I'm trying to figure out how to do this using Gradle. I want to make a B/build.gradle in the B project that will create a B-C-version.jar of .class files compiled from these sources:
B/ejb/C/src/com/company/admin/Foo.java
B/ejb/C/src/com/company/admin/FooHome.java
B/ejb/C/src/com/company/admin/FooBean.java
that I would then publish to Maven and access from the A project.
i.e., the B-C-version.jar would ideally only have these classes:
com/company/admin/Foo.class
com/company/admin/FooHome.class
but if B-C-version.jar had these classes:
com/company/admin/*.class
that would also be OK. How can I make such a thing using a build.gradle in the B project?
You can simply declare a custom Jar task like
task cJar(type: Jar) {
baseName = project.name + '-C'
from sourceSets.main.output
include 'com/company/admin/Foo.class', 'com/company/admin/FooHome.class'
}
or you can make a dedicated sourceset for your api that you then use from your other B code and from your A code, then you don't need to work with includes and update the include if you need to add files, but you just place them in the source folder of the source set and you are done, something like
sourceSets { c }
task cJar(type: Jar) {
baseName = project.name + '-C'
from sourceSets.c.output
}
Then you could also declare dependencies separately and get the correct ones drawn in transitively and so on. But it might be overkill in your situation.

Gradle - paths in multi-project builds

I have a multi-project gradle project with following directory structure:
+ project_root
+ module1
+ src
build.gradle
+ module2
+ src
build.gradle
+ web
..
build.gradle
settings.gradle
In module1/build.gradle among other things I have specified:
compileKotlin2Js.kotlinOptions {
outputFile = "web/script.js"
}
It is a special Kotlin JS setting that specifies output file path of compiled JS file.
Now my problem is, that when I build the whole project (project_root/build.gradle) the file ends up in the right directory (project_root/web), but when I accidentally run build on the module alone the file is saved in module directory (project_root/module1/web).
How can I fix paths in my build scripts, so file output will be saved in exactly the same directory no matter which build script I run (without specifying full path, I want a relative path)?
I don't know what Gradle plugin requires the path parameter in your code example, but all regular (non-3rd-party) Gradle plugins evaluate path parameters via Project.files(Object...) to avoid different locations when calling Gradle from various working directories.
I would suggest to use the above method (or its single file version Project.file(Object)) as well. You can even omit the project part, because the build.gradle file gets executed in the project scope:
compileKotlin2Js.kotlinOptions {
outputFile = file('web/script.js')
}
This will always evaluate the path relative to the project directory of the project your build.gradle belongs to. To evaluate a file relative to the project directory of the root project, use rootProject.files(Object...), for a path relative to the project directory of a subproject or any project in the build, use project(':path:to:project').files(Object...).

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

Gradle dependency destination on non-jar config file

I can create a dependency to something other than a jar file like this:
dependencies {
compile files("../other-project/config.txt")
}
The above works fine, except that config.txt ends up in the WEB-INF/lib folder of my war file. Instead I need it to be in WEB-INF/classes in the war file, and in src/main/resources for jettyRun.
How can I control where the dependency ends up? Or am I going about this the wrong way?
I can also solve this with a copy task, but this really is a dependency in that I don't need the file updated unless it changes. An unconditional copy would work, but I'd rather do this the right way.
The war task (as configured by the war plugin) puts dependencies into WEB-INF/lib, the web project's own code/resources into WEB-INF/classes, and web app content (which by default goes into src/main/webapp) into WEB-INF. Other content can be added by explicitly configuring the war task. For example:
war {
into("WEB-INF/classes") {
from "../other-project/config.txt"
}
}
One way to make this work with embedded Jetty (though maybe not the most convenient during development) is to use jettyRunWar instead of jettyRun. Another solution that comes to mind, particularly if the content to be added resides in its own directory, is to declare that directory as an additional resource directory of the web project (sourceSets.main.resources.srcDir "../other-project/someResourceDir"). This is in fact an alternative to configuring the war task. If the web project already has a dependency on the other project, you could instead configure an additional resource directory for that project.
Let's say you have configured a multi-project build with the following directory and file structure:
/combined-war
/main-project
/src
/webapp
/WEB-INF
web.xml
build.gradle
/other-project
/resources
/WEB-INF
/classes
config.txt
build.gradle
build.gradle
In order to allow jettyRun to combine the contents of the webapp directory from main-project with the contents of the resources directory in other-project you need to add a workaround to your build.gradle of main-project (I've adapted the one posted by the user siasia on gist).
Adding the same directory content to the war file is quite simple and is documented in the Gradle User Guide and and the DSL reference.
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
import org.gradle.api.plugins.jetty.internal.JettyPluginWebAppContext
def newResourceCollection(File... resources) {
def script = '''
import org.mortbay.resource.ResourceCollection
new ResourceCollection(resources)
'''
def shell = new GroovyShell(JettyPluginWebAppContext.class.classLoader)
shell.setProperty("resources", resources as String[])
return shell.evaluate(script)
}
jettyRun.doFirst {
jettyRun.webAppConfig = new JettyPluginWebAppContext()
jettyRun.webAppConfig.baseResource = newResourceCollection(
// list the folders that should be combined
file(webAppDirName),
file("${project(':other-project').projectDir}/resources")
)
}
war {
from("${project(':other-project').projectDir}/resources")
}
Whenever you execute gradle jettyRun a new ResourceCollection is created that combines the given directories. Per default Jetty locks (at least on Windows) all the files it's serving. So, in case you want to edit those files while Jetty is running take a look at the following solutions.
Update
Since other-project in this case is not another Gradle project the two tasks in build.gradle should look like that:
jettyRun.doFirst {
jettyRun.webAppConfig = new JettyPluginWebAppContext()
jettyRun.webAppConfig.baseResource = newResourceCollection(
file(webAppDirName),
file("$projectDir/../other-project/resources")
)
}
war {
from("$projectDir/../other-project/resources")
}
I'm not aware of any solution that adds only one file (e.g. config.txt). You'll always have to add a complete directory.
As I mentioned above, it's simple enough to do an unconditional copy that solves the problem. Again, not the question I originally asked. But here's my solution that works for both war and jettyRun tasks:
processResources.doFirst {
copy {
from '../other-project/config.txt'
into 'src/main/resources'
}
}

Resources