I have a project structure like this:
config/
foo/
build.gradle
settings.gradle
bar/
build.gradle
baz/
build.gradle
I want to add the config directory to the classpath in foo/build.gradle so that the subprojects bar and baz can access it. I've tried doing this in foo/build.gradle:
subprojects {
apply plugin: 'java'
dependencies {
runtime files('../config')
}
}
But that results in the following entry in the classpath:
/home/me/project/foo/config
When what I actually want is:
/home/me/project/config
I believe it's because runtime files('../config') is being evaluated in foo/bar/build.gradle instead of foo/build.gradle. So, how can I get the path to foo/? I could just use runtime files('../../config'), but it doesn't feel quite right.
Or maybe I'm going about this the wrong way and there's a better place to put the config files?
One way to get to your current config folder location irrespective of which project/folder level you're at is:
"$rootDir/../config"
Related
update 05/30/18
I think the technical issue is due to the question that ToYonos linked to.
So, let me broaden the non-technical question:
I have a multi-module project with many parts owned by many different people. We follow similar structures within the modules, but there is no direct inheritance. Let us say something like:
module1/
java/
services/
service1/
common/
library1/
module2/
java/
Each module loosely does the same thing the same way but slightly different ; ) We want to pull out common parts at each layer and put them in a common file but still allow each module to have their own customizations at each layer.
I was hoping 'apply from' could do this, but seems there are limitations (exporting methods, plugins, etc) all due to scoping issues and gradle's top-down command-and-control style.
The one way I see out is through totally yucking up the settings.gradle from this:
include ':module1'
include ':module1:java'
include ':module1:java:services'
include ':module1:java:services:service1'
To this:
include ':root_common'
project(':root_common').projectDir = "$rootDir/common" as File
include ':root_common:module1'
project(':root_common:module1').projectDir = "$rootDir/module1" as File
include ':root_common:module1:module_common'
project(':root_common:module1:module_common').projectDir = "$rootDir/common/module" as File
include ':root_common:module1:module_common:java'
project(':root_common:module1:module_common:java').projectDir = "$rootDir/module1/java" as File
include ':root_common:module1:module_common:java:java_common'
project(':root_common:module1:module_common:java:java_common').projectDir = "$rootDir/common/java" as File
include ':root_common:module1:module_common:java:java_common:services'
project(':root_common:module1:module_common:java:java_common:services').projectDir = "$rootDir/module1/java/services" as File
.... and so on ....
This seems rather unmanageable. Is there a better way?
original 05/29/18
I have the following that works:
My build.gradle:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
ext {
dockerPluginVersion = '0.19.2'
}
dependencies {
classpath "gradle.plugin.com.palantir.gradle.docker:gradle-docker:${dockerPluginVersion}"
}
}
apply plugin: "com.palantir.docker"
apply plugin: "com.palantir.docker-run"
docker {
....
}
I want other people's build.gradles to be able to use the same configuration as in the above build.gradle without copy/paste, so I came up with the following:
Other Person's build.gradle:
apply from: ${rootDir}/common/docker/build.gradle
When I run the above, I get
* What went wrong:
A problem occurred evaluating script.
> Plugin with id 'com.palantir.docker' not found.
If I copy/paste only the buildscript part of My build.gradle to their build.gradle, then it works. So, must be some resolution / ordering issue.
I think the basic question is: how do I apply & configure a plugin from within a build.gradle that is itself being treated like a plugin via apply from ?
Typically, a multi project Gradle build would be setup something like this:
root
subproject1
subproject2
And in root's settings.gradle file, I'd have something like:
rootProject.name = 'root'
include 'subproject1'
include 'subproject2'
This kind of multi-project setup is straightforward. However, what if subproject1 or subproject2 aren't contained within root? For example, I might have an existing subproject on my machine that is stored somewhere far away from root. How do I configure the setting.gradle file to account for this "non-standard" structure? Is it just a matter of providing an absolute path to the subproject folder in the settings.gradle file?
This post covers it: https://discuss.gradle.org/t/how-to-include-sub-projects-in-settings-gradle-file/5096
Basically, you have to overwrite the project.projectDir attribute of your subproject so that it points to the location of the subproject on your machine.
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
I see a lot of examples of Gradle-built Java/Groovy projects that have the following structure:
some-app/
src/
main/
test/
docs/
README.md
build.gradle
gradlew
gradlew.bat
settings.gradle
gradle.properties
gradle/
*.gradle
I understand that build.gradle is the main buildscript and that gradle.properties is its properties file. But settings.gradle really throws me. Inside it I see:
rootProject.name = "someApp"
But this seems like it belongs in gradle.properties. I'm also wondering where the gradlew and gradlew.bat files come from, they seem to be generated.
Finally, I'm wondering why there are so many *.gradle files under the gradle/ dir: are these plugins, or extension scripts of some sort. They are all pulled in from the main build.gradle like so:
apply "gradle/fizz.gradle"
apply "gradle/buzz.gradle"
etc.
So:
What properties are supposed to go in settings.gradle that are not supposed to go in gradle.properties?
How are the gradlew/gradlew.bat files generated?
Why would someone have so many disparate *.gradle files? Why not just 1 big build.gradle buildscript?
1) gradle.properties is normal properties file, while settings.gradle is also a build script. You can add there some code that will be executed during build. Typically this file is needed when You have a multi-module project.
2) When You type gradle tasks in project build directory (empty build.gradle is enough to see it) You'll see wrapper task. This task is used to generate scripts You're asking about. More info.
3) The reason is that all these files have different responsibilities that are cleanly separated.
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'
}
}