I'm using Antlr in a simple Kotlin/Gradle project, and while my Gradle build is generating Antlr sources, they are not available for importing into the project.
As you can see (on the left), the classes (Lexer/Parser, etc.) are being generated. I have also configured this generated-src/antlr/main directory as a Source Root. Most questions I see list this as a solution, but I've already done it.
The issue persists after multiple rebuilds (both in IDEA and on the CLI), and following all the usual "Invalidate Cache and Restart" issues.
Further, the import issue is listed in the Gradle build on the CLI so it doesn't seem isolated to IDEA.
What am I missing here?
Here's the build.gradle file produced by IDEA when I was creating the project initially, and which IDEA is using for project/workspace synchronization.
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.50'
}
group 'com.craigotis'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
apply plugin: 'antlr'
dependencies {
antlr "org.antlr:antlr4:4.5"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.2.0'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Try adding this to your build.gradle:
sourceSets {
main.java.srcDirs += "${project.buildDir}/generated-src/antlr/main"
}
generateGrammarSource {
arguments += ["-visitor", "-package", "com.craigotis.sprint.core.antlr"]
outputDirectory = file("${project.buildDir}/generated-src/antlr/main/com/craigotis/sprint/core/antlr")
}
compileKotlin.dependsOn generateGrammarSource
Shouldn't it locate the compiled classes and not the sources? Do you see the antlr generated classes in the target directory?
Try this: first build the project without referencing or using any ANTLR generated classes, and only after the build is successful, then add the code that references them.
(In other words, what I think that happens, is that your ANTLR sources are compiled after the code that references them. They never have a chance to compile because build fails before)
Also if this is really the case, you can solve it also by splitting into two artifacts and make sure the ANTLR one is built before the one with the code that uses it
Try to add generated sources in idea module like this post from Daniel Dekany here:
apply plugin: "idea"
...
sourceSets.main.java.srcDir new File(buildDir, 'generated/javacc')
idea {
module {
// Marks the already(!) added srcDir as "generated"
generatedSourceDirs += file('build/generated/javacc')
}
}
Related
I'm trying to start using Kotlin DSL with gradle in the project with the following restrictions:
Project has different modules (moreover: sometimes these modules use different plugins, however if two projects uses the same plugin then version of the plugins are the same).
Project has internal corporate repositories only (e.g. we don't use jcenter directly, we use proxy for it).
What we have with Groovy:
Some common configurations items are excluded to the separate scripts. Please check the example below.
Gradle modules include these files.
As a result (just based on my example):
We don't need to add the same code lines into the each module.
The most of projects have difference just with dependency list.
I tried to reproduce the same with Gralde KTS and received the following difficulties:
I'm unable to apply plugin in the include file and use it in the module. In this case I receive compilation error (because plugin types are not added into the module script).
I'm unable to extract constants to the something common to use them in the each scripts (root build.gradle.kts inclusive). With Groovy I can just use variable like springBootVersion, however with Kotlin Script I have to create the same property in the each module file.
Precompiled script plugins does not work without public repositories access (e.g. I'm unable to configure common script file with idea "just use default embedded Kotlin Script version, download all dependencies from these urls: ...".
Include file sample:
apply plugin: 'kotlin'
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
Gradle module sample:
apply from: "${rootDir}/gradle/include/kotlin-common-include.gradle"
dependencies {
compile project(':my.project.libraries.common')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
}
Questions:
How can I put all common constants (such as dependency versions) to the separate file to include them just by using something like springBootVersion or Constants.springBootVersion with compile-time checks?
How can I extract plugin applying to the include scripts (to avoid Gradle module scripts overload)?
How can I use precompiled script plugins without public global repositories access?
Additional links:
Issue for Gradle KTS for plugin applying.
Issue for Gradle KTS for shared constants extracting.
There are limitations in Kotlin DSL currently (5.3) that prevents to have compile-time checks everywhere. In order for Kotlin DSL to work it has to add extensions on top of the Gradle API and it can't do it magically. First of all you need to go through Kotlin DSL Primer page to understand it.
How can I extract plugin applying to the include scripts (to avoid Gradle module scripts overload)?
The one way to do it is to use precompiled build scripts with Kotlin DSL Plugin. In order to do it you need to move your script into $rootDir/buildSrc project. Here how it might look like:
// $rootDir/buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
repositories {
maven {
url = uri("http://host:8082/artifactory/...")
}
}
Then put your common script like that:
// $rootDir/buildSrc/src/main/kotlin/common.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.3.21"
}
tasks.compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
tasks.compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Then you can apply this script as to a plugin like that:
// $rootDir/build.gradle.kts
subprojects {
apply(id = "common")
}
How can I use precompiled script plugins without public global repositories access?
Configuring custom repositories for pre-compiled scripts plugin is no different that your usual build script. Do it like that:
// $rootDir/buildSrc/settings.gradle.kts
pluginManagement {
repositories.maven {
url = uri("http://host:8082/artifactory/...")
}
}
The other way around that if you don't want to use precompiled plugins is to configure extensions explicitly. You can do it like that:
// $rootDir/gradle/common.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
And in main script:
// $rootDir/build.gradle.kts
subprojects {
apply {
plugin(KotlinPlatformJvmPlugin::class)
from("common.gradle.kts")
}
}
How can I put all common constants (such as dependency versions) to the separate file to include them just by using something like springBootVersion or Constants.springBootVersion with compile-time checks?
There is no good way to do it currently. You can use extra properties, but it won't guarantee compile time checks. Something like that:
// $rootDir/dependencies.gradle.kts
// this will try to take configuration from existing ones
val compile by configurations
val api by configurations
dependencies {
compile("commons-io:commons-io:1.2.3")
api("some.dep")
}
// This will put your version into extra extension
extra["springBootVersion"] = "1.2.3"
And you can use it like this:
// $rootDir/build.gradle.kts
subprojects {
apply {
plugin<JavaLibraryPlugin>()
from("$rootDir/dependencies.gradle.kts")
}
And in your module:
// $rootDir/module/build.gradle.kts
// This will take existing dependency from extra
val springBootVersion: String by extra
dependencies {
compile("org.spring:boot:$springBootVersion")
}
When calling gradle idea, external dependencies are ordered first in the class path relatively to local Jar inclusions. As such :
dependencies {
compile fileTree(dir: 'libs', include:['*.jar'])
compile group: 'foo', name:'bar', version:'1.0.0'
}
will include my local jars last. This is a problem in my project as these jars' purpose is to partially overwrite the external library.
The same behavior is observed when specifying the repository as a source of dependencies using flatDir and loading the jar without fileTree. It is put last in the classpath.
I have found several mentions of the problem when researching, such as https://discuss.gradle.org/t/gradle-messes-up-the-classpath-order-in-generated-projects-when-there-are-mixed-dependency-types/13130, but no workarounds.
I suppose these exist, gradle being very customisable, but being very new to it my attempts to make one fail. How to proceed?
I'm not using IntelliJ on a regular basis but tried it in the context of this question and my impression is that gradle's idea plugin and IntelliJ's gradle plugin don't go well together. That is you should either use the idea gradle plugin and import as plain Java project or import as gradle project using IntelliJ's gradle plugin. Main reason is that the idea plugin and the IntelliJ plugin are generating slightly different iml-files (those files are holding the project dependencies - amongst others) which leads to lot of confusion when using both plugins together. As you specifically asked for the gradle idea plugin, I used this plugin and imported into IntelliJ as plain java project.
But to answer your question I found no evidence that the order of libraries on the classpath differs from the order as declared in the dependencies section of the gradle file, when using a flatDir repo. When using compile fileTree(dir: 'libs', include:['*.jar']) the order was actually broken as described in your question. That is, you should stick to using a flatDir repo.
I'm using gradle 4.9 and IntelliJ 2018.2.
This is my gradle file
apply plugin: 'java'
apply plugin: 'idea'
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
dependencies {
compile 'zzz:zzz-0.0.0'
compile 'aaa:aaa-0.0.0'
compile 'com.google.guava:guava:24.0-jre'
compile group: 'javax.websocket', name: 'javax.websocket-api', version: '1.1'
}
task wrapper(type: Wrapper) {
gradleVersion = '4.9'
distributionUrl = "http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip"
}
In my libs folder there are two jars aaa-0.0.0.jar and zzz-0.0.0.jar both are copies of guava-24.0-jre.jar. That is all guava classes are present in both jars as well. As zzz:zzz-0.0.0 is the first dependency in the gradle file, the expectation would be that guava classes are being loaded from zzz-0.0.0.jar instead of guava-24.0-jre.jar or aaa-0.0.0.jar. I used the following main class to test this:
package test;
import com.google.common.math.LongMath;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(LongMath.class.getProtectionDomain().getCodeSource().getLocation().toURI());
}
}
And the output when running it from IntelliJ is
file:/C:/ws/gradle-idea-test/libs/zzz-0.0.0.jar
That is the com.google.common.math.LongMath class is indeed being loaded from the local libs/zzz-0.0.0.jar instead of the guava-24.0-jre.jar.
I noticed that the list of external dependencies in IntelliJ doesn't show the local libraries. And even more confusing the libraries are ordered alphabetically and don't reflect the actual order on the classpath which might be quite confusing:
To get the actual order of elements on the classpath you will have to look in the module dependencies section in the module settings ("Open Module Settings" > "Project" > "Modules" > "Dependencies Tab") which looks like this:
As you can see the dependencies are listed in correct order and include the local libraries as well. The order of libs in this dialog is basically the same as in the generated iml-file.
When using the IntelliJ gradle plugin instead of gradle's idea plugin, IntelliJ basically behaved the same way but the generated iml-file looked different and the external libraries were displayed in a different format. But there was no difference regarding the classpath order.
I am trying to move from Dagger 1.2.2 to Dagger 2.0.1 in AppEngine project (NOT Android one).
With Dagger 1.2.2 simple:
compile 'com.squareup.dagger:dagger-compiler:1.2.2'
compile 'com.squareup.dagger:dagger:1.2.2'
did the trick.
With Dagger 2.0.1:
compile 'com.google.dagger:dagger-compiler:2.0.1'
compile 'com.google.dagger:dagger:2.0.1'
does not work (source is generated but mixed up with *.class files in build/classes/main/..package../).
You can also do it without net.ltgt.apt plugin, (which by the way may conflict with lombok).
apply plugin: 'java'
apply plugin: 'idea'
def generatedMain = new File(buildDir, "generated/main")
compileJava {
doFirst {
generatedMain.mkdirs()
}
options.compilerArgs += ['-s', generatedMain]
}
idea.module.sourceDirs += generatedMain
dependencies {
compileOnly 'com.google.dagger:dagger-compiler:2.8'
compile 'com.google.dagger:dagger:2.8'
}
I've found a solution.
https://github.com/tbroyer/gradle-apt-plugin
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "net.ltgt.gradle:gradle-apt-plugin:0.3"
}
}
apply plugin: "net.ltgt.apt"
dependecies {
apt 'com.google.dagger:dagger-compiler:2.0.1'
compile 'com.google.dagger:dagger:2.0.1'
}
Additionally if you are using Intellij a following configuration is recommended:
When using the Gradle integration in IntelliJ IDEA however, rather
than the idea task, you'll have to manually enable annotation
processing: in Settings… → Build, Execution, Deployment → Compiler →
Annotation Processors, check Enable annotation processing and Obtain
processors from project classpath. To mimic the Gradle behavior and
generated files behavior, you can configure the production and test
sources directories to build/generated/source/apt/main and
build/generated/source/apt/test respectively and choose to Store
generated sources relative to: Module content root.
I've also had to remove Exclude from whole build directory and mark generated/source/apt/main directory as source.
I'm new to gradle and I'm trying to configure gradle with lwjgl3. Because I didn't found a repo where lwjgl3 is hosted i decided that everybody who use this project has to define the path to the lwjgl lib. I created a user.gradle file with contains the paths to the jar and to the natives.
My build.gradle looks like this at the moment.
apply plugin: 'java'
apply from: 'user.gradle'
apply plugin: 'application'
sourceCompatibility = 1.8
targetCompatibility = 1.8
mainClassName = "mp.Main"
println("LWJGL jar path is configured to: ${config.lwjgl3Jar}")
println("LWJGL natives path is configured to: ${config.lwjgl3Natives}")
repositories {
mavenCentral()
flatDir {
dir config.lwjgl3Jar
}
}
dependencies {
compile 'com.google.code.gson:gson:2.3.1'
compile 'net.java.dev.jna:jna:4.1.0'
testCompile 'junit:junit:4.+'
testCompile 'com.carrotsearch:junit-benchmarks:0.7.2'
compile name: 'lwjgl'
}
tasks.withType(Test) {
scanForTestClasses = false
include "**/*Test.class" // whatever Ant pattern matches your test class files
}
sourceSets{
main {
java {
srcDir 'src'
exclude 'mp/graphics/gl/scene/Mesh.java'
exclude 'test'
}
}
test{
java {
srcDir 'src/test'
exclude '**/UnsafeTest.java'
exclude '**/DispatchTests/*'
exclude '**/MemoryTest.java'
exclude '**/SuperFastListTest.java'
exclude '**/MatrixTest.java'
exclude '**/SimulationTest.java'
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
How to set the natives? I tried it different ways. Google didn't helped me out this time. All results are related to older versions of this lib and all are using repositories. Maybe I'm missing the forest for the trees in between. Any ideas?
Best regards!
PS: Not sure if it is important: We are using different IDE's like intelliJ and Eclipse on Windows, Linux, and Mac.
I have run into the same problem and wrote a plugin for handling the natives associated with Java jar files.
http://cjstehno.github.io/gradle-natives/
It will unpack them from the jar files so that you can use them and deploy them in your project.
I solved the problem for me. The problem for was that I didn't knew how to configure gradle to use the natives. Normally I set the the classpath in the run config. However:
The very simple solution how to set the classpath with gradle:
Apply the java plugin and use the function:
run {
systemProperty 'java.library.path', 'path to ur natives')
}
The simply run your application via gradle and it should work.
There were so many solutions by searching for "lwjgl gradle natives" that I didn't found the right one :-)
Hope the solution helps somebody.
I'm currently in the situation of editing to gradle projects( "database" & "masterdata"). Masterdata depends on the database-project. Database is published to a Nexus server from where it is loaded by masterdata as a dependency.
The build.gradle of masterdata
import org.gradle.plugins.ide.eclipse.model.SourceFolder
apply plugin: "java"
apply plugin: "eclipse"
sourceCompatibility = 1.7
version = '0.1-SNAPSHOT'
group = "net.example"
def nexusHost = "http://nexus:8081"
repositories {
logger.lifecycle("Configuration: Repositories")
maven {
url nexusHost + "/nexus/content/groups/public"
}
}
dependencies {
logger.lifecycle("Configuration: Dependencies")
compile 'net.example:database:0.1-SNAPSHOT' // project where the changes happen
compile 'com.google.guava:guava:14.0.1'
testCompile 'ch.qos.logback:logback-classic:1.0.13'
testCompile 'org.testng:testng:6.8.5'
testCompile 'org.dbunit:dbunit:2.4.9'
testCompile 'org.mockito:mockito-all:1.9.5'
testCompile 'org.easytesting:fest-assert-core:2.0M10'
testCompile 'org.hsqldb:hsqldb:2.2.9'
}
eclipse.classpath.file {
beforeMerged { classpath ->
classpath.entries.clear()
logger.lifecycle("Classpath entries cleared")
}
whenMerged { cp ->
cp.entries.findAll { it instanceof SourceFolder && it.path.startsWith("src/main/") }*.output = "bin/main"
cp.entries.findAll { it instanceof SourceFolder && it.path.startsWith("src/test/") }*.output = "bin/test"
cp.entries.removeAll { it.kind == "output" }
logger.lifecycle("Classpath entries modified")
}
}
When I change something in the database project it needs a complete build, publish, etc. till I see the changes in the masterdata project. In the company where I previously worked we had a similar setup using maven. There I saw changes in dependencies immediately with out publishing them first. Is this also possible with gradle? Maybe via Multi-Project Builds?
Basically a the following entry is missing from .classpath:
<classpathentry combineaccessrules="false" kind="src" path="/database"/>
Is there a way to automate the generation of it.
Update: As a workaround I add the entry manually to the .classpath
I did some additional searching and currently this is only possible with multi-project builds. Basically you need all your projects in one gigantic multi-project build. There you can reference them as you like and get the correct dependencies in eclipse as it seems.
There is a jira issue with a feature request to make that possible without a multi-project build. Custom logic for eclipse will only help with builds in eclipse, because in a gradle build it would use the dependency from the repository, where the changes are missing. You need to make sure that all changed dependencies are build and published before building the main project.
Eclipse workaround:
eclipse.classpath.file {
whenMerged { cp ->
// remove library dependencies
def toBeRemoved = cp.entries.findAll { it instanceof Library
&& ((Library) it).library.path.contains('someProject') }
//configure the project dependencies:
def toBeAdded = [ new ProjectDependency('/someProject', null)]
cp.entries -= toBeRemoved
cp.entries += toBeAdded
}
}
This would still fail when doing a manual gradle build, but if you using a CI-system with a good build order you should be fine.
Solution Multi-Project Build:
Creating a Multi-Project build is easier than I thought and it is also possible when the "subproject" is on the same level.
settings.gradle:
includeFlat 'database'
build.gradle:
dependencies {
...
compile project(':database')
...
}
This works well with gradle builds and with eclipse. The only disadvantage is that you always have to checkout the subproject everytime you checkout the project that depends on it. I'm sure someone can build some fancy groovy logic to fix that.