gradle - Include JAR without filtering binary newlines - gradle

I have project A with SubProjects
A/B
A/C
(B and C subprojects). B and C are compiled into A into one Jar file. Using the below code.
gradle.taskGraph.whenReady {
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes("Main-Class": "brut.apktool.Main")
}
}
}
That works fine, but I have a prebuilt JAR in /a/b/src/main/resources/prebuilt.jar. This jar just encapsulates some random files I need during the program. There isn't any java or anything. I grab them from inputStream, but after building with Gradle it converts binary newline data and then messes up the archive.
I tried copying the jar using a CopyTask post built, but I never could get a Task to run prior to the gradle.TaskGraph.whenReady.
Back in Maven. I would just disable filtering for that file, but cannot find the same expression in Gradle.
EDIT: This is what I do currently, and it filters my changes into the properties files, but doesn't do my newline filtering.
processResources {
ext.fullrev = ''
ant.loadfile(srcFile: "../../.git/refs/heads/master", property: ext.fullrev)
filter(FixCrLfFilter)
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [version: apktoolversion, gitrev: ant.properties[ext.fullrev].substring(0,10)])
}

Well solved it. For future googlers.
processResources {
from('src/main/resources/properties') {
include '**/*.properties'
into 'properties'
ext.fullrev = ''
ant.loadfile(srcFile: "../../.git/refs/heads/master", property: ext.fullrev)
filter(ReplaceTokens, tokens: [version: apktoolversion, gitrev: ant.properties[ext.fullrev].substring(0,10)])
}
from('src/main/resources/') {
include '**/*.jar'
}
includeEmptyDirs = false
}
Pretty simple to explain. If the file falls into my *.properties include, then its filtered etc.
If it falls into *.jar, its just copied onward without filtering. Prevents graddle/plugin from filtering binary newlines in JAR files.

Related

How do I build a fat jar (bundle) with Bndtools in a Gradle project?

I am currently trying to create a bundle with bnd in a Gradle project.
The idea was to try getting the list of jar dependencies with Gradle and modify the jar task to add the libs inside the bundle.
Let's say, I would like to remove the includeresource.
-includeresource: lib/jsoup.jar=jsoup-1.14.3.jar
Bundle-ClassPath: ., lib/jsoup.jar
I tried some variations of:
jar {
duplicatesStrategy = 'EXCLUDE'
from {
configurations.runtimeClasspath.findAll {
it.name.endsWith('jar')
}.collect { println it.name; zipTree(it) }
}
}
I can see the name of the jar being printed, but not included inside the bundle, (I know zipTree would expand).
Suggestions from https://discuss.gradle.org/t/how-to-include-dependencies-in-jar/19571/16 do not seem to be valid for bnd, and I suspect bnd overrides the jar task somehow.
A suggestion as:
jar {
def libBuildDir = mkdir "${buildDir}/resources/main/lib"
copy {
from { configurations.jarLibs }
into { libBuildDir }
}
}
Would be perfect, but it does not seem to work for bundles.
Any ideas?

Fat Jar expands dependencies with Gradle Kotlin DSL

I am trying to build a fat jar using the following in my Kotlin based gradle file.
val fatJar = task("fatJar", type = Jar::class) {
baseName = "safescape-lib-${project.name}"
// manifest Main-Class attribute is optional.
// (Used only to provide default main class for executable jar)
from(configurations.runtimeClasspath.map({ if (it.isDirectory) it else zipTree(it) }))
with(tasks["jar"] as CopySpec)
}
tasks {
"build" {
dependsOn(fatJar)
}
}
However, the fat jar has all the dependencies expanded out. I would like to have the jars included as is in a /lib directory but I cannot work out how to achieve this.
Can anyone give any pointers as to how I can achieve this?
Thanks
Well you are using zipTree in that map part of the spec, and it behaves according to the documentation: it unzips the files that are not a directory.
If you want the jars in /lib, replace your from with:
from(configurations.runtimeClasspath) {
into("lib")
}
In case anyone is using kotlin-multiplatform plugin, the configuration is a bit different. Here's a fatJar task configuration assuming JVM application with embedded JS frontend from JS module:
afterEvaluate {
tasks {
create("jar", Jar::class).apply {
dependsOn("jvmMainClasses", "jsJar")
group = "jar"
manifest {
attributes(
mapOf(
"Implementation-Title" to rootProject.name,
"Implementation-Version" to rootProject.version,
"Timestamp" to System.currentTimeMillis(),
"Main-Class" to mainClassName
)
)
}
val dependencies = configurations["jvmRuntimeClasspath"].filter { it.name.endsWith(".jar") } +
project.tasks["jvmJar"].outputs.files +
project.tasks["jsJar"].outputs.files
dependencies.forEach { from(zipTree(it)) }
into("/lib")
}
}
}

Gradle - "apply from" a ZIP dependency

In my Gradle build script I want to import a ZIP dependency that contains static analysis configuration (CheckStyle, PMD etc.) and then "apply from" the files in that ZIP. When anyone runs the "check" task, my custom static analysis configuration should be used then.
I've tried the somewhat convoluted solution below, but I can't get it to work. The files are retrieved and unpacked into the "config" directory, but "apply from" does not work - Gradle complains it cannot find the files; I assume this is due to "apply from" being run during the build configuration phase.
Is there a simpler way to do this?
repositories {
maven { url MY_MAVEN_REPO }
}
configurations {
staticAnalysis {
description = "Static analysis configuration"
}
}
dependencies {
staticAnalysis group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
// Unzip static analysis conf files to "config" in root project dir.
// This is the Gradle default location.
task prepareStaticAnalysisConf(type: Copy) {
def confDir = new File(rootProject.projectDir, "config")
if (!confDir.exists()) {
confDir.mkdirs()
}
from {
configurations.staticAnalysis.collect { zipTree(it) }
}
into confDir
apply from: 'config/quality.gradle'
}
check.dependsOn('prepareStaticAnalysisConf')
You are perfectly right: Gradle runs apply during evaluation phase, but the prepareStaticAnalysisConf was not executed yet and the archive is not unpacked.
Instead of a task, just write some top-level code. It should do the trick. Also, you'd better use the buildscript level dependency, so that it is resolved before script is executed.
Here is the full script
buildScript {
repositories {
maven { url MY_MAVEN_REPO }
}
dependencies {
classpath group:'my-group', name:'gradle-static-analysis-conf', version:'+', ext:'zip'
}
}
def zipFile = buildscript.configurations.classpath.singleFile
copy {
from zipTree(it)
into 'config'
}
apply from: 'config/quality.gradle'

Gradle: 'clone' original jar task to create a new task for a jar including dependencies

I would like to create a new task in my project that creates a jar archive with the class files of my project and the class files of the dependencies (also called 'shaded jar' or 'fat jar').
The solution proposed by the Gradle cookbook modifies the standard jar task of the JavaPlugin:
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
However, I would like to keep the original jar taks as it is and have an additional task for the shaeded jar, i.e. a task that behaves exactly like the jar task, but includes the additional files according to
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
and has another classifier ('shaded').
I tried to take over the configuration of the jar task by copying properties like this:
task shadedJar(type: Jar, dependsOn: configurations.compile) {
dependencies = tasks.jar.taskDependencies
source = tasks.jar.source
manifest = tasks.jar.manifest
includes = tasks.jar.includes
classifier = 'shaded'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
But the resulting tasks does not take over the dependencies of 'jar' and the resulting jar does not include the project's class files. Additionally, this approach seems to cumbersome to be the recommended way of using an existing task as a template for a new one.
What would a recommendable approach to my specific need (the seperate shadedJar task) and for 'cloning' tasks to use them as templates for additional tasks in general?
(I am currently still on Gradle 1.3,but solutions for the current Gradle version are also welcome)
There is no built-in way to clone tasks. However, it's easy to configure the fatJar task to include the same files as the java plugin's jar task:
task fatJar(type: Jar) {
appendix = "fat"
from sourceSets.main.output // that's it
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
Task autowiring will automatically establish the necessary task dependencies.
If the build script goes on to customize the jar task, you can apply the customizations to both tasks simultaneously:
configure([jar, fatJar]) {
version = "2.0"
entryCompression = "STORED"
}
If, unlike in the jar task's case, you are defining the "template" yourself, you can "instantiate" it with a factory method:
def getMeAnotherOne(String name) {
task(name, type: Jar) {
version = "2.0"
entryCompression = "STORED"
}
}
getMeAnotherOne("jar1")
getMeAnotherOne("jar2")

Extract specific JARs from dependencies

I am new to gradle but learning quickly. I need to get some specific JARs from logback into a new directory in my release task. The dependencies are resolving OK, but I can't figure out how, in the release task, to extract just logback-core-1.0.6.jar and logback-access-1.0.6.jar into a directory called 'lib/ext'. Here are the relevant excerpts from my build.gradle.
dependencies {
...
compile 'org.slf4j:slf4j-api:1.6.4'
compile 'ch.qos.logback:logback-core:1.0.6'
compile 'ch.qos.logback:logback-classic:1.0.6'
runtime 'ch.qos.logback:logback-access:1.0.6'
...
}
...
task release(type: Tar, dependsOn: war) {
extension = "tar.gz"
classifier = project.classifier
compression = Compression.GZIP
into('lib') {
from configurations.release.files
from configurations.providedCompile.files
}
into('lib/ext') {
// TODO: Right here I want to extract just logback-core-1.0.6.jar and logback-access-1.0.6.jar
}
...
}
How do I iterated over the dependencies to locate those specific files and drop them in the lib/ext directory created by into('lib/ext')?
Configurations are just (lazy) collections. You can iterate over them, filter them, etc. Note that you typically only want to do this in the execution phase of the build, not in the configuration phase. The code below achieves this by using the lazy FileCollection.filter() method. Another approach would have been to pass a closure to the Tar.from() method.
task release(type: Tar, dependsOn: war) {
...
into('lib/ext') {
from findJar('logback-core')
from findJar('logback-access')
}
}
def findJar(prefix) {
configurations.runtime.filter { it.name.startsWith(prefix) }
}
It is worth nothing that the accepted answer filters the Configuration as a FileCollection so within the collection you can only access the attributes of a file. If you want to filter on the dependency itself (on group, name, or version) rather than its filename in the cache then you can use something like:
task copyToLib(type: Copy) {
from findJarsByGroup(configurations.compile, 'org.apache.avro')
into "$buildSrc/lib"
}
def findJarsByGroup(Configuration config, groupName) {
configurations.compile.files { it.group.equals(groupName) }
}
files takes a dependencySpecClosure which is just a filter function on a Dependency, see: https://gradle.org/docs/current/javadoc/org/gradle/api/artifacts/Dependency.html

Resources