Gradle - how do I exclude a resource from the executable jar? - gradle

I have a "production" logback configuration file logback.xml under src/main/resources... but that directory also contains the "testing" logback configuration file logback-test.xml (which logback looks for first).
When creating an executable jar I want to delete the "testing" xml file.
I tried this
jar {
manifest {
// PS this is the correct line for Shadow Plugin...
attributes 'Class-Path': '/libs/a.jar'
attributes 'Main-Class': 'core.MyMainClass'
}
exclude("**/resources/*test*")
}
and I tried this
jar {
manifest {
attributes 'Class-Path': '/libs/a.jar'
attributes 'Main-Class': 'core.MyMainClass'
}
doLast {
exclude("**/resources/*test*")
}
}
... what am I doing wrong?
later
I find here that I was probably making life difficult for myself in putting these xmls under /src/main/resources ... so I created a new directory under src, /logback, and put the files in there instead. I added this to the classpath (as logback says that's where it looks for these files) by doing this:
test {
classpath += files( 'src/logback' )
}
Interestingly, as well as meaning that logging during testing happens OK, this is enough to get the resulting executable jar to use logback OK when run.
Unfortunately, configuring the "shadowJar" task like this
shadowJar {
baseName = 'DocumentIndexer'
classifier = null
version = project.version
exclude("logback/*test*")
}
or configuring "jar" task like this:
jar {
manifest {
attributes 'Class-Path': '/libs/a.jar'
attributes 'Main-Class': 'core.ConsoleIOHandler'
}
exclude("logback/*test*")
}
... just refuses to work: the file logback-test.xml is still there in the jar.

I got the answer from the forums at gradle.org.
The basic answer is that the "test" logback config file should go under src/test/resources and the "production" config file should go under src/main/resources. This way the former will be excluded from the jar.
The answerer also said the "resources" is one of the roots from which relative paths are specified.

configurations {
provided
compile.extendsFrom provided
}
dependencies {
provided 'WHATEVER' // Packages you don't need to add to jar
provided 'Other WHATEVER' // Packages you don't need to add to jar
shadow 'OTHER' // Packages you need to add to jar
shadow 'Another OTHER' // Packages you need to add to jar
}
shadowJar {
configurations = [project.configurations.shadow] // ***
}
as mentioned here
line *** is the way to tell shadow what dependencies to include in jar

Related

Gradle Ear plugin should not copy resources into .ear root

I have the following folder structure of my gradle module:
src
main
application
META-INF
application.xml
was.policy
was.webmodule
java
resources
image.bmp
logback.xml
... other files, properties
webapp
My goal is to build an ear archive, which will contain only Tclient.war and META-INF. However, gradle copies all resource files to the ear root.
Gradle documentation about Ear Plugin says that:
The default behavior of the Ear task is to copy the content of
src/main/application to the root of the archive. If your application
directory doesn’t contain a META-INF/application.xml deployment
descriptor then one will be generated for you.
So, It's not really clear why it puts resources into the root. Maybe, it work's like the org.gradle.api.tasks.bundling.Jar task and I should override this behaviour in some way?
Here is my partially build.gradle file:
jar {
description 'Creates tclient.jar'
archiveBaseName = 'tclient'
destinationDirectory = file('src/main/webapp/WEB-INF/lib')
from sourceSets.main.output
include '**/*'
include '**/*.properties'
include '**/*.cmd'
}
ear{
description = "Ear archive for WebSphere server"
archiveBaseName = 'Tclient'
appDirName('src/main/application')
// workaround to exclude classes from ear root
rootSpec.exclude('**/de/**')
rootSpec.exclude('**/org/**')
}
war {
archiveFileName = 'Tclient.war'
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
webInf {
from configurations.natives into 'lib/bin'
}
// do not put compiled classes inside WEB-INF/classes
rootSpec.exclude('**/de/**')
rootSpec.exclude('**/org/**')
rootSpec.exclude('urlrewrite*.dtd')
from(jar) {
into 'WEB-INF/lib'
}
}
dependencies {
// Place .war inside .ear root
deploy files(war)
....
}
The possible workaround is to exclude redundant resources from the rootspec:
ear{
description = "Ear archive for WebSphere server"
archiveBaseName = 'Tclient'
appDirName('src/main/application')
// workaround to exclude classes from ear root
rootSpec.exclude('**/de/**')
rootSpec.exclude('**/org/**')
// workaround
rootSpec.exclude('*.properties')
rootSpec.exclude('*.xml')
rootSpec.exclude('*.dtd')
rootSpec.exclude('*.bmp')
}
However, I'm finding this workaround a little bit ugly and it's a dirty hack.

Using gradle to programmatically add resource folder to test compile/runtime classpath

We have a master build script for 60+ components. The individual components do not have build.gradle files. What I'm trying to do is programmatically (in the master build.gradle) add a resource folder to certain projects. This resource folder contains a file which must be in the classpath when unit tests are ran. I'm trying to add this in the subprojects block like this:
subprojects { proj ->
...
// this is the folder I need in the test task classpath
def resdir = sprintf("%s\\resources", project(':Common').projectDir)
sourceSets {
test {
java {
srcDir 'test'
}
resources {
srcDirs = [resdir]
}
}
}
}
...
if(proj.name == "APROJECT"){
proj.tasks['test'].getClasspath().each {
logger.info "COMPILE CLASSPATH: {}", it
}
}
}
However, if I query the classpath of the test task (see above) I do not see the folder in the classpath. Additionally, of course, the test is failing because the folder is not in the classpath.
If I put the sourceSet update in a build.gradle in the component folder, it works as expected.
Am I doing something wrong? How can I get this folder into the classpath for testing?
I wasn't able to get this to work by dynamically updating the sourceSet, however, I was able to get it to work by adding the necessary resource path to the testCompile dependency. See this for adding a class folder to a dependency.
Update: It's still not an ideal solution since the "solution" only adds the class folder to the compile path, it doesn't treat it as a resource (e.g., copy it to the runtime class folder).
Update #2: It's actually working as expected. It turns out that different tests were referencing slightly different resource paths. Adding all resource paths dynamically as noted above works fine!

Gradle: create zip file containing classes and dependencies

I have to create a zip-file containing the classes (and the manifest etc.!) in a classes-folder and the dependency jar-files in an libs folder. My current attempt is:
task createZip(type: Jar) {
into('classes')
extension('zip')
with jar
}
but it does not add the dependencies (as anyone would have guessed)
ok, i solved it
task plugin(type: Zip) {
from (configurations.compile) {
into ('libs/')
}
from (sourceSets.main.output.classesDir) {
into ('classes/')
}
from (sourceSets.main.resources) {
into ('classes/')
}
from (new File(project.buildDir, 'tmp/jar/')) {
into ('classes/META-INF/')
}
}
plugin.dependsOn jar
this copies the dependencies into libs and all the compiled classes, resources and the manifest into libs. plugin.dependsOn jar is important because the manifest ist generated by the jar task.
Your question says you desire a zip file, though your attempt uses a Jar task. Regardless of whether you zip or jar (since the jar task extends zip), the below snippet will collect all compiled dependencies, and you can put them wherever you desire:
from (configurations.compile.collect { it.isDirectory() ? it : zipTree(it) })

How to expand property references in jar resources?

I'm using Gradle to build a jar containing an xml file in META-INF. This file has a row like
<property name="databasePlatform" value="${sqlDialect}" />
to allow for different SQL databases for different environments. I want to tell gradle to expand ${sqlDialect} from the project properties.
I tried this:
jar {
expand project.properties
}
but it fails with a GroovyRuntimeException that seems to me like the Jar task attempts to expand properties in .class files as well. So then I tried
jar {
from(sourceSets.main.resources) {
expand project.properties
}
}
which does not throw the above exception, but instead results in all resources being copied twice - once with property expansion and once without. I managed to work around this with
jar {
eachFile {
if(it.relativePath.segments[0] in ['META-INF']) {
expand project.properties
}
}
}
which does what I want, since in my use case I only need to expand properties of files in the META-INF directory. But this feels like a pretty ugly hack, is there a better way to do this?
I stumbled across this post in a thread about a different but closely related issue. Turns out you want to configure the processResources task, not the jar task:
processResources {
expand project.properties
}
For some reason, though, I did have to clean once before Gradle noticed the change.
In addition to #emil-lundberg 's excellent solution, I'd limit the resource processing to just the desired target file:
build.gradle
processResources {
filesMatching("**/applicationContext.xml") {
expand(project: project)
}
}
An additional note: if the ${...} parentheses are causing "Could not resolve placeholder" errors, you can alternatively use <%=...%>. N.B. tested with a *.properties file, not sure how this would work for an XML file.
I've had similar problems migrating from maven to gradle build. And so far the simplest/easiest solution was to simply do the filtering yourself such as:
processResources {
def buildProps = new Properties()
buildProps.load(file('build.properties').newReader())
filter { String line ->
line.findAll(/\$\{([a-z,A-Z,0-9,\.]+)\}/).each {
def key = it.replace("\${", "").replace("}", "")
if (buildProps[key] != null)
{
line = line.replace(it, buildProps[key])
}
}
line
}
}
This will load all the properties from the specified properties file and filter all the "${some.property.here}" type placeholders. Fully supports dot-separated properties in the *.properties file.
As an added bonus, it doesn't clash with $someVar type placeholders like expand() does. Also, if the placeholder could not be matched with a property, it's left untouched, thus reducing the possibility of property clashes from different sources.
here is what worked for me (Gradle 4.0.1) in a multi-module project:
in /webshared/build.gradle:
import org.apache.tools.ant.filters.*
afterEvaluate {
configure(allProcessResourcesTasks()) {
filter(ReplaceTokens,
tokens: [myAppVersion: MY_APP_VERSION])
}
}
def allProcessResourcesTasks() {
sourceSets*.processResourcesTaskName.collect {
tasks[it]
}
}
and my MY_APP_VERSION variable is defined in top-level build.gradle file:
ext {
// application release version.
// it is used in the ZIP file name and is shown in "About" dialog.
MY_APP_VERSION = "1.0.0-SNAPSHOT"
}
and my resource file is in /webshared/src/main/resources/version.properties :
# Do NOT set application version here, set it in "build.gradle" file
# This file is transformed/populated during the Gradle build.
version=#myAppVersion#
I took your first attempt and created a test project. I put a pom file from a jenkins plugin in ./src/main/resources/META-INF/. I assume it is a good enough xml example. I replaced the artifactId line to look like the following:
<artifactId>${artifactId}</artifactId>
My build.gradle:
apply plugin: 'java'
jar {
expand project.properties
}
When I ran gradle jar for the first time it exploded because I forgot to define a value for the property. My second attempt succeeded with the following commandline:
gradle jar -PartifactId=WhoCares
For testing purposes I just defined the property using -P. I'm not sure how you are trying to define your property, but perhaps that is the missing piece. Without seeing the stacktrace of your exception it's hard to know for sure, but the above example worked perfectly for me and seems to solve your problem.

Filter out resources from custom Test task in Gradle with goal of having one properties file for tests and a different one for production

So I have setup a way to just run integration tests using this configuration:
test {
exclude "**/*IntegrationTest*.class"
}
task integrationTest(type: Test, dependsOn: testClasses) {
include "**/*IntegrationTest*.class"
}
check.dependsOn integrationTest
Works great. But then for logging in my integration tests, I want to use a log4j.properties file from a specific directory instead of the one that is located in the src/main/resoures which is used for production.
I've tried this but didn't work:
integrationTest.classpath = files("$rootDir/test/src/main/resources/log4j.properties") + integrationTest.classpath
I also tried to just see if I could exclude the file, but could not find a way. Tried this:
processTestResources.exclude "**/*log4j.properties"
Any suggestions for including one properties file in production and another one for tests?
If you put the 'test' log4j.properties in the src/test/resources directory, it will actually come before anything in src/main/resources on the classpath of your tests/integration tests.
An alternative solution is to setup your bundling so that the log4j.properties for production is not in src/main/resources, but is added to the jar from a different directory...
jar {
from('production') {
include('log4j.properties')
}
}
If you want to keep the log4j.properties files in their current locations, you were almost there with what you tried. On the classpath you can have either jar files, or directories containing resources. So try doing this:
integrationTest.classpath = files("$rootDir/test/src/main/resources") + integrationTest.classpath
Seems like you could do something like this also so that the .jar task will include it later:
sourceSets {
main {
java {
srcDir 'src/main/java'
output.classesDir = 'build/classes/main'
}
resources {
srcDir 'src/main/resources'
include 'logback.xml'
output.resourcesDir = 'build/resources/main'
}
}
}

Resources