Gradle provided dependencies with Intellij - java

I'm trying to build a Bukkit plugin. The plugin also uses exp4j. The final result needs to have the exp4j code included in the released jar but not have the Bukkit code included.
I followed the advice of this answer to copy the dependencies in and used this answer to declare Bukkit as provided. My build.gradle now looks like this:
apply plugin: 'java'
apply plugin: 'idea'
configurations {
provided
}
sourceSets {
main.compileClasspath += configurations.provided
test.compileClasspath += configurations.provided
test.runtimeClasspath += configurations.provided
}
dependencies {
provided "org.bukkit:bukkit:1.8.8-R0.1-SNAPSHOT"
compile "net.objecthunter:exp4j:0.4.5"
}
jar {
// copy the dependencies across
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
This works great and I can build and run the project happily using Gradle from the command line. The trouble is that Intellij (or maybe it's the Gradle idea plugin) doesn't recognise the provided dependencies, thus, importing anything from Bukkit causes it to incorrectly report an error.
How can I get the provided dependencies to play nicely with idea?
Other things I've tried:
I've also tried declaring the provided scope like this:
configurations {
provided
compile.extendsFrom provided
}
But this causes the provided dependencies to be copied into the final jar. I've also tried the plugins as recommended by this answer, but both cause Bukkit to be copied into the created jar. I've further tried declaring Bukkit to be runtime scope instead provided, but that simply caused lots of compile errors (but interestingly Intellij did have Bukkit listed as a dependency)
I have trouble believing that this has not been asked before, but I've searched and cannot find a full solution. I'm new to Gradle, so apologies if this is a super simple thing to do.

In Gradle 2.12 and later, there is a configuration called compileOnly that has the provided semantics you are looking for.
More about this configuration on the Gradle blog post on the subject.
Before 2.12, you can use the nebula.provided-base plugin to create a provided configuration with all the correct semantics.

See Gradle issue here.
There isn't a provided configuration in gradle, though there really should be one. The most reasonable workaround currently seems to be, to create your own configuration:
configurations {
provided
}
and then:
sourceSets {
main {
compileClasspath += configurations.provided
}
}
The problem with extendsFrom is that the provided dependency will end up being bundled in your distribution anyway unless if you add another explicit exclude, defeating the whole point of provided.
Edit: To tell idea to use the provided dependencies, you could apply the 'idea' plugin and then:
idea {
module {
scopes.PROVIDED.plus += [ configurations.provided ]
}
}
see more here.

I did find a very hacky solution. But it's so bad I feel bad posting it here :P
Declare the provided dependencies as runtime dependencies
Regenerate the idea files: gradle cleanIdea idea
Idea should now recognise the dependences
Change the provided dependencies back to provided
Go into the project settings and convert all the dependencies with runtime scope to Intellij's provided scope
Stuff works :) (Just don't ever regenerate the idea files)
Obvious problems, anyone using your project has to do the same hack. And every time you regenerate the idea files, the same thing will have to be repeated.

Related

Excluding testImplementation dependency for Kotlin stdlib in generated POM for Android artifact

I'm migrating an older Android library from jcenter to mavenCentral in light of their deprecation. All of the actual library modules are written in Java, but several of them have tests written in Kotlin.
Until upgrading (to AS 4.1.3, Gradle 6.8.3), the generated POM(s) never included a compilation dependency on the kotlin-stdlib. Now it appears that applying the kotlin-android plugin causes the generated POM to add it, even though the dependency is included as testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.31".
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.4.31</version>
<scope>compile</scope>
</dependency>
</dependencies>
The question: Was the previously-generated POM wrong in that it should have included kotlin as a compilation dependency, even though it's only used for testing?
Or, do I have to do something special to exclude that "dependency" in this specific case? If so, how could I exclude it if generating the POM when defining the publication like:
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
....
pom {
name = ...
licenses { }
developers { }
scm { }
}
}
}
}
}
Or, is that not really a good option and I should just rewrite the tests in Java?
It's worth noting that removing the kotlin-android plugin also removes it as a compilation dependency from the POM(s). It seems to have no effect on generating the AAR; it just prevents the tests from running.
Since Kotlin 1.4, the various Kotlin Gradle plugins automatically add a dependency on kotlin-std. See Dependency on the standard library.
You can disable it by adding to gradle.properties:
kotlin.stdlib.default.dependency=false
Going to "answer" my own questions and hope someone corrects me if anything is wrong.
Was the previously-generated POM wrong in that it should have included kotlin as a compilation dependency, even though it's only used for testing?
No, the kotlin-stdlib should not be included in the POM of a release publication for a java/android library that has no kotlin code or kotlin-based dependencies.
I'm making that assertion as I have compiled a java-only app against a version of this library where the kotlin-stdlib compilation condition was removed from the POM, and everything behaved as expected (in that it worked).
If so, how could I exclude it if generating the POM when defining the publication
After defining your POM, you can use pom.withXml from the MavenPom plugin to remove the dependency nodes you want to exclude from the generated POM. e.g.
pom {
licenses { ... }
developers { ... }
scm { ... }
}
pom.withXml {
asNode()
.dependencies
.dependency
.findAll { it.artifactId.text() == "kotlin-stdlib" }
.collect { it.parent().remove(it) }
}
Edit: This does not remove the dependencies from the generated module.json, so this now seems even more wrong than it felt before.
Instead, you can check the Gradle Start Parameters to determine which variant is being targeted by the current Task Requests, and only apply the plugin if the variant should actually include it:
def kolinEnabledVariant = getGradle()
.getStartParameter()
.getTaskRequests()
.toString()
.contains("Debug")
if (kolinEnabledVariant) {
apply plugin: 'kotlin-android'
}
Commenters on a related answer note that the variant name should start with a capital letter.
Or, is that not really a good option and I should just rewrite the tests in Java?
That's giving up.

Using your own Java library in another project?

So despite developing with Java for quite a while, I've only just decided to give making my own library a go. I've created it, and have a second project within Intellij Idea, but I don't know how to use said library in the second project. This is what I have so far:
In my library, build.gradle is:
apply plugin: 'java-library'
version = '0.0.1'
repositories {
jcenter()
}
jar {
manifest {
attributes(
'Implementation-Title': project.name,
'Implementation-Version': project.title
)
}
}
dependencies {
api 'org.apache.commons:commons-math3:3.6.1'
implementation 'com.google.guava:guava:23.0'
testImplementation 'junit:junit:4.12'
}
I guess what my question is, is how do I implement my library into the second project so that I can use it (and test it properly) in the second project? I've tried to search the net to find the answer to this and cannot seem to get an explanation that makes sense to me.
I have a multi-project in the same window of Intellij Idea.
I find the official guide very meaningful about how to build a custom library.You can follow that way : Building Java Libraries.
To look for the testing part with JUnit, you can follow that rules.

How to put local dependencies first when calling gradle idea?

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.

No option to run Java 9 project in IntelliJ IDEA

I'm not sure what I'm doing wrong here, I started working on a java 10 project (which of course uses java 9's new module system) so I suspect it has to do with that.
I have the following project directory structure:
However, the run button in IntelliJ is grayed out and it looks like intellij doesn't recognize this as being a properly structured Java project.
I've looked through the documentation, but a lot of it is circular explanations which cause more confusion than clarity.
I see that the .idea directory has a modules subdirectory which looks like it contains some relevant stuff - I'm probably doing something wrong here, but I don't know what.
Here's my build.gradle file, since I suspect this will be important here:
plugins {
id 'java'
id 'application'
id 'idea'
}
group 'com.chrismailloux'
version '0.1'
description = "Tutorial application developed to learn LWJGL and OpenGL."
sourceCompatibility = 1.8
task wrapper(type: Wrapper) {
gradleVersion = '4.9'
}
repositories {
mavenCentral()
}
dependencies {
// Dependencies left out for brevity
}
Can someone set me straight here? How can I fix this to be able to run the project?
The default folder structure in Gradle is src/main/java, but you use src/$project-name/main/java. I suspect that without further configuration, Gradle does not know where to find the sources and thus IntelliJ doesn't know either.
It looks like source sets (1, 2) are the answer:
sourceSets {
main {
java {
srcDirs 'src/$project-name/main/java'
}
}
}

Compile module with Gradle using Makefile

I have a Java/Gradle project with 3 different modules. One of these modules (let's call it orModule) has a dependency on or-tools, which unfortunately is not yet available through some public repository (like Maven) and therefore it has to be built from the source.
The team that's developing or-tools provides a Makefile for compiling/building the project and this is what I've been using so far. To be more specific I compile my orModule using the Makefile and then I run my main Java class, which belongs in the main module. Below are two screenshots of the Run Configurations I use for the two modules.
My goal is to move these configurations in the Gradle build system because I need the project to eventually be built with Gradle.
For the MainClass, I've added the following configuration in the root build.gradle:
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'com.some.package.MainClass'
applicationDefaultJvmArgs = ['-Djava.library.path=/orModule/lib']
And I think this is correct. However, I'm not sure how to "move" the Makefile configuration inside Gradle. The following is one of the alternatives that I've tried in the orModule/build.gradle but it fails.
task ndkBuild(type: Exec) {
commandLine 'make', ['EX=src/main/java/com/some/path/OrClass.java','PKG=com/some/package'], 'cjava', file('Makefile')
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
Any help on how to correctly write this configuration with Gradle, would be greatly appreciated.
I've solved this by getting rid of the Makefile altogether. Here's the original answer.
This was solved quite some time ago so if anyone has a simpler solution by now, please post it and I'll change the accepted answer.

Categories