gradle processResources with different replacement and renaming rules - java

I'd love to filter specific java resources in my gradle project. Where some files should have replaced contents only, some should be also renamed (and have different content replaced).
My gradle java project setup is:
> cat build.gradle
apply plugin: 'java'
sourceSets {
main {
java {
resources {
srcDirs = [ "foo" ]
include '**/**'
}
}
}
}
processResources {
include '**/file_a.txt'
filter { String line ->
line
.replace("foo", "fool" )
}
}
processResources {
include '**/file_b.txt'
rename { "file_c.txt" }
filter { String line ->
line
.replace("ipsum", "zzz" )
}
}
> cat foo/file_a.txt
my name is foo
test ipsum
> cat foo/file_b.txt
lorem ipsum ...
Once running:
gradle build
I get:
> ls build/resources/main
file_c.txt
> cat build/resources/main/file_c.txt
my name is fool
test zzz
However I'd like to get both files, where only file_b.txt would be renamed and both would be replaced by the specific rules. What is the proper way to achieve that?

OK, found the solution myself, following seems to be working as expected:
apply plugin: 'java'
sourceSets {
main {
java {
resources {
srcDirs = [ "foo" ]
include '**/**'
exclude '**/*.txt'
}
}
}
}
processResources {
with copySpec {
from 'foo/file_a.txt'
filter { String line ->
line
.replace("foo", "fool" )
}
}
with copySpec {
from 'foo/file_b.txt'
rename { "file_c.txt" }
filter { String line ->
line
.replace("ipsum", "zzz" )
}
}
}

I, personally, think that resources and filtered resources should be kept separate. Ie src/main/resources and src/main/filteredResources. I also thing you should avoid excludes (eg exclude '**/*.txt') resources and filteredResources directories should contain ONLY what will end up in the jar... NOTHING ELSE
import org.apache.tools.ant.filters.ReplaceTokens
processResources {
with copySpec {
from 'src/main/filteredResources'
filter(ReplaceTokens, tokens: [foo: 'fool', ipsum: 'zzz'])
}
}
The above snippet will replace #foo# and #ipsum# in ALL files in the src/main/filteredResources folder

Related

Duplicate handling strategy error with gradle while using protobuf for java

I am using the below configuration build.gradle
plugins {
id "com.google.protobuf" version "0.8.17"
id "java"
}
group "de.prerna.aws.tests"
version "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
ext {
protobufVersion = "3.18.1"
}
dependencies {
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
sourceSets {
main {
proto {
srcDir 'src/main/proto'
}
java {
// include self written and generated code
srcDirs 'src/main/java'
}
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:4.0.0-rc-2'
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:1.39.0"
}
}
generateProtoTasks.generatedFilesBaseDir = 'generated-sources'
generateProtoTasks {
all().each { task ->
task.plugins { grpc{} }
}
ofSourceSet('main')
}
}
Error
* What went wrong:
Execution failed for task ':processResources'.
> Entry Person.proto is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.2/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.
A variant of BParolini for build.gradle (Groovy DSL)
tasks.withType(Copy) {
filesMatching("**/*.proto") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
I could fix this problem by adding the following code to my build.gradle.kts:
tasks {
withType<Copy> {
filesMatching("**/*.proto") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
}
Extra info: I'm using Gradle 7.3-rc-3 and Java 17.
Unfortunately nobody explains reasons for this problem, so here is some of my explorations and guesses. Please correct me if you know more.
If found that following build script code causes this error:
proto { srcDir 'src/main/proto' }
If look inside "build/extracted-include-protos" directory, there are original .proto files copied into "build/extracted-include-protos/test" (but not into main).
My guess is that those auto-copied .proto files are originally uses as the only sources, but when adding "src/main/proto" source set we give some compiler tool second set of same files.
Removing this srcDir is not a good idea, because it required for IDEA to correctly open included .proto on Ctrl+click (otherwise it is opened extracted copies which is useless).

How to combine multiple Javadoc into one using Gradle

This question was answered before but the chosen answer doesn't explain a lot for me on how this is doable on Gradle.
That and the fact that I can't comment on the solution to ask for more info forced me to make this question.
I have a Gradle project that has several modules available and I now want to set up the Javadoc task to combine the Javadoc comments of all the modules into a single location where I could browse it.
How would I now be able to do this using Gradle? I run Gradle 5.5 if the version is of any importance and I have the following things set in the build.gradle file:
allprojects {
ext {
// Convenience method to configure Javadoc
configureJavadoc = { Object jDocConfig ->
jDocConfig.options {
it.author()
it.encoding = 'UTF-8'
it.memberLevel = JavadocMemberLevel.PROTECTED
if (it instanceof StandardJavadocDocletOptions) {
def opt = it as StandardJavadocDocletOptions
opt.links(
"https://docs.example.com/java/"
)
if (JavaVersion.current().isJava9Compatible()) {
opt.addBooleanOption("html5", true)
opt.addStringOption("-release", "8")
}
if (JavaVersion.current().isJava11Compatible()) {
opt.addBooleanOption("-no-module-directories", true)
}
}
}
}
}
}
subprojects {
javadoc {
destinationDir = file("$rootDir/docs/")
configureJavadoc(it)
}
}
I was able to do it with:
def exportedProjects = [
":",
":module-a",
":module-b",
":module-c"
]
task allJavadoc(type: Javadoc) {
source exportedProjects.collect { project(it).sourceSets.main.allJava }
classpath = files(exportedProjects.collect { project(it).sourceSets.main.compileClasspath })
destinationDir = file("${buildDir}/docs/javadoc-all")
}

How to use variables in a multi-project gradle build system?

I have a multi-project structure managed with gradle. All the java sub-projects have this jar block:
def main_class = "foo.Main"
jar {
manifest {
attributes 'Main-Class': main_class
}
}
So I decided that I'm going to move the jar block to the root project and have only the def main_class in the sub-projects, but no matter how I defined the main_class variable, it didn't work. I tried project.ext and properties {} as well.
I had a task, called dist which worked with project.ext:
root project:
subprojects {
task dist(dependsOn: jar) << {
copy {
from('build/libs') {
include (build_jar)
}
into('.')
rename(build_jar, app_jar)
}
}
}
a sub-project:
project.ext {
build_jar = "..."
app_jar = "..."
}
How can I define a variable that works with jar like with the dist task?
You can just do:
ext {
main_class = 'com.Main'
}
subprojects {
apply plugin: 'java'
jar {
manifest {
attributes 'Main-Class': main_class
}
}
}
in PROJECT_ROOT/build.gradle file.
To override extra properties in subproject's build.gradle you must put your desired task (in this case the jar task) inside the afterEvaluate block.
root project build.gradle:
subprojects {
apply plugin: 'java'
apply plugin: 'application'
ext {
// the property that should be overridden in suproject's build.gradle
main_class = ''
}
afterEvaluate {
jar {
manifest {
attributes 'Main-Class': main_class
}
}
}
}
sub project build.gradle:
mainClassName = 'project.specific.Main'
ext.set("main_class", mainClassName)
// you could also use this notation, but I prefer the first one
// since it is more clear on what it is doing
//main_class = mainClassName

Versioning Gradle native plugin builds

Here's my first attempt at a C program built with Gradle's C plugin:
apply plugin: 'c'
model {
components {
derpus(NativeExecutableSpec) {
sources {
c(CSourceSet) {
source {
srcDir "src/derpus/c"
include "**/*.c"
}
exportedHeaders {
srcDir "src/derpus/headers"
}
}
}
}
}
}
This produces an executable called derpus.exe. I would like, if at all possible, to version these executables (derpus-1.0.0.exe, derpus-1.0.1.exe, etc.). When I change the derpus closure to derpus-1.0.0 like so:
derpus-1.0.0(NativeExecutableSpec) {
And run gradle clean build I get:
D:\workspace\derp\20150505\derpus>gradlew clean build
FAILURE: Build failed with an exception.
* Where:
Build file 'D:\derpus\build.gradle' line: 6
* What went wrong:
Could not compile build file 'D:\derpus\build.gradle'.
> startup failed:
build file 'D:\derpus\build.gradle': 6: unexpected tok
en: 0 # line 6, column 20.
derpus-1.0.0(NativeExecutableSpec) {
^
1 error
Does anybody know of a way to version these executables?
Update
Now this is really weird! Taking Amnon's advice, I added a gradle.properties file that defined version=1.0.0. I then modified my model closure to:
model {
components {
derpus(NativeExecutableSpec) {
sources {
c(CSourceSet) {
source {
srcDir "src/derpus/c"
include "**/*.c"
}
exportedHeaders {
srcDir "src/derpus/headers"
}
}
}
baseName = "derpus-${version}"
}
}
}
This produces an executable named derpus-1 (what?!?!)!
So then I modified model again:
version = "3.4"
model {
components {
derpus(NativeExecutableSpec) {
sources {
c(CSourceSet) {
source {
srcDir "src/derpus/c"
include "**/*.c"
}
exportedHeaders {
srcDir "src/derpus/headers"
}
}
}
baseName = "derpus-${version}"
}
}
}
As you can see, this should overrdide the version set in gradle.properties, however after running gradle clean build, it produces derpus-3!
So I modified model yet again:
model {
components {
derpus(NativeExecutableSpec) {
sources {
c(CSourceSet) {
source {
srcDir "src/derpus/c"
include "**/*.c"
}
exportedHeaders {
srcDir "src/derpus/headers"
}
}
}
baseName = "derpus-3.4.5"
}
}
}
This produces derpus-3.4!!! What is going on here?!? Does the C plugin have a bug in it that doesn't honor the full version variable?
In your example above the problem with derpus-1.0.0 is the gradle things that the dash character is a minus which is unexpected in a component spec name, thus the failure. You can overcome this by wrapping derpus-1.0.0 with inverted commas. A better approach, however, would be to apply the version to the baseName property of the component spec, i.e. add the following line under derpus component definition:
baseName = "derpus-1.0.0"
or
baseName = "derpus-$version"
Where in the second case the version property $version is taken from the project object.
Update
Per smeeb comments below another workaround that can be applied is to directly rename the target binaries:
afterEvaluate {
RenameNativeBinaries()
}
def RenameNativeBinaries() {
binaries.all { b ->
if (b instanceof SharedLibraryBinarySpec) {
b.sharedLibraryFile = ReconstructFileName(b.sharedLibraryFile)
} else if (b instanceof StaticLibraryBinarySpec) {
b.staticLibraryFile = ReconstructFileName(b.staticLibraryFile)
}
}
}
def ReconstructFileName(File originalFile) {
def originalFileName = originalFile.absolutePath
def filePath = FilenameUtils.getFullPath(originalFileName)
def baseName = FilenameUtils.getBaseName(originalFileName)
def extension = FilenameUtils.getExtension(originalFileName)
def newName = "$baseName-$version.$extension"
def newFile = new File(filePath, newName)
newFile
}
Where FilenameUtils is taken from commons-io:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-io', name: 'commons-io', version: '2.4'
}
}

Getting location of cross-project resources

I have read many similar questions where the reply is that the project structure is not ideal so my questions based on the following:
I have a main project (ProjA) which needs to include a second project (ProjB) which is not a child project. ProjB has various resource files which need to be copied in the distribution of ProjA.
build.gradle of ProjA
dependencies {
compile project(":ProjB")
}
distributions {
main {
baseName = "Something"
contents {
into('bin') { from jar.archivePath }
into('lib') { from configurations.runtime }
into('etc') {
from ('../../projb/src/main/webapp') // Fix me!
}
}
}
}
1.) Ideally ProjB should expose the location of the resource files through a property used by ProjA, how can this be done?
2.) Is this the correct way to do it as I have read alot about cross-project properties not being ideal - or should I be doing something completely different?
Don't know if it helps but it seems that the best way is to do it in the following way:
distributions {
main {
baseName = "Something"
contents {
into('bin') { from jar.archivePath }
into('lib') { from configurations.runtime }
into('etc') {
from project(':projB').file('src/main/webapp')
}
}
}
}
The path must be hardcoded in that case.
Second option might be specifying a project property - in general not a very good idea - and use in another project - there must be also evaluation order defined.
In projB
ext.resourcesDir = project.file('src/main/webapp2')
and in projA
evaluationDependsOn(':projB')
and:
distributions {
main {
baseName = "Something"
contents {
into('bin') { from jar.archivePath }
into('lib') { from configurations.runtime }
into('etc') {
from project(':projB').file('src/main/webapp')
from project(':projB').resourcesDir
}
}
}
}
Here's complete example.

Categories

Resources