I have a project in which I have two different 'sub-projects', the main library and a compiler for a programming language in a directory structure like this:
- build.gradle
- src
- library
- compiler
- ...
My build.gradle looks like this:
sourceSets
{
library
{
java
{
srcDir 'src/library'
srcDir 'src/asm'
}
}
compiler
{
java
{
srcDir 'src/compiler'
}
}
//...
}
// ...
task buildLib(type: Jar, dependsOn: 'classes') {
from sourceSets.library.output
classifier = 'dyvil-library'
}
task buildCompiler(type: Jar, dependsOn: 'classes') {
from sourceSets.compiler.output
classifier = 'dyvil-compiler'
}
My problem is that whenever I try to run the buildCompiler task, the build fails and the Java Compiler gives me hundreds of errors in places where the compiler source code references classes in the library. I already tried to make the compiler dependant on the library classes like this:
task buildCompiler(type: Jar, dependsOn: [ 'classes', 'libraryClasses' ]) {
from sourceSets.compiler.output
classifier = 'dyvil-compiler'
}
But that did not help. I know that I could theoretically copy the srcDirs from sourceSets.library into sourceSets.compiler like this:
compiler
{
java
{
srcDir 'src/library'
srcDir 'src/asm'
srcDir 'src/compiler'
}
}
But that seems like a bad idea for obvious reasons.
Is there another way to include the source files from the library when compiling the compiler (duh)?
There's a similar question on SO here.
I replicated your situation locally and the fix was to add a line to the sourceSets for compiler as follows:
compiler
{
java {
srcDir 'src/compiler'
}
compileClasspath += library.output
}
Related
I need to create Gradle task which can replace .java files before Gradle build.
Gradle build package app in .war file. And I need to have replaced bytecode there after build.
I tried sourceSets Gradle task but it can only exclude files.
sourceSets {
main {
java {
exclude 'com/myapp/example/resource/impl/ResourceBundleImpl.java'
}
}
}
But I need to also include file in the same place. How I can do it with Gradle?
The directory to file that I need to exclude: com/myapp/example/resource/impl/ResourceBundleImpl.java
The directory to file that I need to include: src/main/webapp/WEB-INF/my/ResourceBundleImpl.java
To copy file content it is also posible solution.But How can I do it before compile time?
The below task didn't helped, becouse in build file have .java files instead of .classe file.
task prepareSources(type: Copy) {
from('src/main/webapp/WEB-INF/my')
into('build/classes/java/main/com/myapp/example/resource/impl')com/myapp/example
}
// Prepare sources, before compile
compileJava {
dependsOn prepareSources
}
The below task throws :
Task :cdx-war:compileJava FAILED
error: package com.myapp.example.util does not exist
sourceSets {
main {
java {
srcDir "$projectDir"
exclude 'com/medtronic/diabetes/carelink/rbps/resource/impl/ResourceBundleImpl.java'
include 'src/main/webapp/WEB-INF/my/ResourceBundleImpl.java'
}
}
I put classes that I need to replace together and add suffix to one of them and replace this suffix during compile time :
sourceSets {
main {
java {
srcDirs = ["$buildDir\\generated-src"]
}
}
}
task copySourceSet(type: Copy) {
println 'Change java class in patient-svc-war'
from "$rootDir\\patient-svc-war\\src\\main\\java"
into "$buildDir\\generated-src"
from file("$rootDir\\patient-svc-war\\**_suffix.java")
into "$buildDir\\generated-src"
rename { String fileName ->
fileName.replace("_suffix", "")
}
filter { line -> line.replaceAll('_suffix', '')}
}
compileJava.source "$buildDir\\generated-src"
compileJava {
dependsOn copySourceSet
}
compileJava.doLast {
delete "$buildDir\\generated-src"
}
I am unable to delombok my Java source code, apparently due to jar dependencies that the project has, and I don't understand why. There are two jar files that have to be committed to the repo to tag along, and they are added to the project in the dependencies node of the build.gradle file by adding the line compile files('myproj1.jar'). So, the relevant part of the build.gradle file looks like this:
dependencies {
compile files('myproj1.jar')
compile files('myproj2.jar')
.....
}
When I run the delombok task I get the following error:
Execution failed for task ':delombok'.
> taskdef class lombok.delombok.ant.Tasks$Delombok cannot be found
using the classloader AntClassLoader[/path/to/repo/myproj1.jar:/path/to/repo/myproj2.jar]
Why would delombok task be using the AntClassLoader from the jar files?
I have tried the delombok'ing code from this post
Here is the task from my build.gradle file
def srcJava = 'src/main/java'
def srcDelomboked = 'build/src-delomboked'
task delombok {
// delombok task may depend on other projects already being compiled
dependsOn configurations.compile.getTaskDependencyFromProjectDependency(true, "compileJava")
// Set up incremental build, must be made in the configuration phase (not doLast)
inputs.files file(srcJava)
outputs.dir file(srcDelomboked)
doLast {
FileCollection collection = files(configurations.compile)
FileCollection sumTree = collection + fileTree(dir: 'bin')
ant.taskdef(name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok', classpath: configurations.compile.asPath)
ant.delombok(from:srcJava, to:srcDelomboked, classpath: sumTree.asPath)
}
}
I expect to be able to delombok my Java source code as part of the build process so that when the project is compiled there are no dependencies on Lombok.
So after continued trial an error, I have a working implementation. To answer my own question, the problem has nothing to do with the additional Jar files. Rather, when gradle runs the delombok task, the classes in the lombok jar are not in the classpath of the org.gradle.api.AntBuilder (ie, the ant task), and so it does not have a reference to lombok.delombok.ant.Tasks$Delombok anywhere (which seems obvious at this point, but not at the time).
The solution thus far has been to add those references in from configurations.compile
Combining code snippits from this post and this post you can do it with something like this:
def srcDelomboked = 'build/src-delomboked'
task delombok {
description 'Delomboks the entire source code tree'
def srcJava = 'src/main/java'
inputs.files files( srcJava )
outputs.dir file( srcDelomboked )
doFirst {
ClassLoader antClassLoader = org.apache.tools.ant.Project.class.classLoader
def collection = files( configurations.compile + configurations.testCompile )
def sumTree = collection + fileTree( dir: 'bin' )
sumTree.forEach { File file ->
antClassLoader.addURL(file.toURI().toURL())
}
ant.taskdef( name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok',
classpath: configurations.compile.asPath + configurations.testCompile.asPath )
ant.delombok( from: srcJava, to: srcDelomboked, classpath: sumTree.asPath )
}
}
sourceSets {
main {
java { srcDirs = [ srcDelomboked ] } //blow away the old source sets so that we only use the delomboked source sets
}
test {
java { srcDirs += [ srcDelomboked ] } //but add those new source sets to the tests so that their references are available at test time
}
}
compileJava.dependsOn(delombok)
bootJar {
mainClassName = 'com.myproj.MyMainClass' // you will need this if its a Spring Boot project
}
Hope this helps whomever else needs to delombok their code.
Please find the script below to clean and build a project using gradle.
Everything was working fine until I added outputDir to the sourceSets in java block.
On commenting out the outputDir line, the gradle clean and build is successful, but the class files are generated in ''build/classes//main'' which I did not want. I wanted the class in directory ''build/classes//'' and not create the main folder and place the class files in that.
apply plugin: 'java'
//Declarations
def projectName="SomeProject"
def projectDir="build//classes"
sourceSets {
main {
java {
srcDir 'src'
outputDir = file("${projectDir}")
}
}
}
dependencies {
compile fileTree(include: ['*.jar'],dir: 'lib')
}
jar{
manifest {
attributes 'Main-Class': 'Foo'
attributes 'Class-Path': 'lib/com.jcraft.jsch_0.1.31.jar lib/commons-io-2.6.jar lib/commons-net-3.6.jar lib/javax.mail.jar lib/jsch-0.1.54.jar lib/json-simple-1.1.1.jar lib/jxl-2.6.jar lib/ojdbc8.jar'
}
baseName = '${projectName}'
}
Also tried with outputdir 'build//classes/ but the same error is occuring as
Could not find method main() for arguments [build_3eivce3rhjp4go01pnr2m3vvmq$_run_closure1_closure6#87800a] on root project 'SomeProject'.
Please suggest what am I missing. Seems some basic issue to me, but can't crack it.
See this documentation : https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSetOutput.html
outputDir is a property, not a method, so you are not using the correct syntax to set the property value : use the following instead:
sourceSets {
main {
java {
srcDir 'src'
outputDir = file("${projectName}")
}
}
}
Note: you are using simple quote ' in '${projectName}' instead of double
quote " : this cannot work as your variable projectName will not be evaluated.
I'm trying to work out how I get a Groovy test script to import a Java class during the testing phase ...
Specifically I want to use JavaFXThreadingRule: .java file from here (or rather here and so included in my Java test source path) and then import it in my Groovy test script to use as an annotation.
The Groovy test script path is src\test\ft\groovy\core\testscript.groovy.
The .java file is src\test\ft\java\core\JavaFXThreadingRule.java.
The package line I've used in both is "package core;"
My "sourceSets" clause in build.gradle looks like this:
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
}
test {
java {
srcDirs = ['src/test/ft/java' ]
}
groovy {
srcDirs = ['src/test/ft/groovy', 'src/test/ut/groovy']
}
}
}
Interestingly the build.gradle output shows that the compileTestJava task is run before the compileTestGroovy task ... and yet I get
unable to resolve class core.JavaFXThreadingRule # line 18, column 1.
import core.JavaFXThreadingRule ^
NB I also tried "import JavaFXThreadingRule" - same result.
In addition to just wanting to resolve the problem I'm also wondering how Gradle decides what order to do the tasks compileTestJava and compileTestGroovy... and whether I shouldn't perhaps make my compileTestGroovy explicitly dependent on compileTestJava...
Thanks to Tim Yates I found the "workaround" of putting this Java file in with the Groovy ones... but this answer gave me another clue, and I then changed my build.gradle to be like this:
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
}
test {
groovy {
srcDirs = ['src/test/ft/groovy', 'src/test/ut/groovy', 'src/test/ft/java' ]
}
}
}
... works ... no doubt obvious to old Gradle hands.
That question and answer referenced above were talking about the app code classes (and Gradle tasks). Unless some Gradle expert can say otherwise I'm assuming that the Groovy test compile task and Java test compile task are completely separate and can't "see" one another's classes...
The default scala plugin task flow compiles Java before Scala so importing Scala sources within Java causes "error: cannot find symbol".
Java and Scala can be combined in the scala sources to have them compiled jointly, e.g.
sourceSets {
main {
scala {
srcDirs = ['src/main/scala', 'src/main/java']
}
java {
srcDirs = []
}
}
If your java code use some external libraries like Lombok, using scala compiler to build java class will failed, as scala compiler don't know annotations.
My solution is to change the task dependencies, make compiling Scala before Java.
tasks.compileJava.dependsOn compileScala
tasks.compileScala.dependsOn.remove("compileJava")
Now the task compileScala runs before compileJava, that's it.
If your java code depends on scala code, you need to do two more steps,
Separate the output folder of scala and java,
sourceSets {
main {
scala {
outputDir = file("$buildDir/classes/scala/main")
}
java {
outputDir = file("$buildDir/classes/java/main")
}
}
Add the scala output as a dependency for compileJava,
dependencies {
compile files("$sourceSets.main.scala.outputDir")
}
for gradle kotlin dsl you can use this piece
sourceSets {
main {
withConvention(ScalaSourceSet::class) {
scala {
setSrcDirs(listOf("src/main/scala", "src/main/java"))
}
}
java {
setSrcDirs(emptyList<String>())
}
}
test {
withConvention(ScalaSourceSet::class) {
scala {
setSrcDirs(listOf("src/test/scala", "src/test/java"))
}
}
java {
setSrcDirs(emptyList<String>())
}
}
}
Posting this update from the future here, as it would saved me a day.
gradle 6 doesn't support task dependencies modification, but here is what you can do:
// to compile Java after Scala
tasks.compileScala.classpath = sourceSets.main.compileClasspath
tasks.compileJava.classpath += files(sourceSets.main.scala.classesDirectory)
And here is documentation.