I have a multiproject build with multiple war modules that depends on one jar module.
Both war and jar modules have dependencies over libraries like Spring, Hibernate and so on, those dependencies are defined as providedCompile on the war modules and as compile on the jar.
The problem is that when JetGradle updates the dependencies all artifacts have errors, as the dependencies from the jar module are required on the artifacts.
I would like to use any of this solutions:
Include the libraries on the lib folder of the server and have Intellij treat them as provided.
Include the libraries as project wide libraries somehow, so intellij puts them on all artifacts even after the gradle dependencies are updated.
On the other hand my approach could be completely wrong from the beginning.
The dependencies in the war modules are defined as:
providedCompile 'org.slf4j:slf4j-log4j12:1.7.5'
providedCompile 'org.slf4j:jcl-over-slf4j:1.7.5'
...
compile(project(':jarModule')) {transitive = false}
...
The dependencies in the jar module are defined as:
...
compile 'org.slf4j:slf4j-log4j12:1.7.5'
compile 'org.slf4j:jcl-over-slf4j:1.7.5'
...
The best solution I found was to set the transitive "compile" dependencies from the jar module as provided using the following code in the Gradle configuration file:
apply plugin: 'idea'
configurations {
provided
provided.extendsFrom(compile)
}
idea {
module {
scopes.PROVIDED.plus += configurations.provided
}
}
For Gradle 2.0+ modify the last bit to be like this:
idea {
module {
scopes.PROVIDED.plus += [configurations.provided]
}
}
This solution works using the Intellij Gradle plugin and also the idea task in Gradle
I got this solution working based on the info on this urls:
https://github.com/Netflix/RxJava/pull/145
http://www.gradle.org/docs/current/dsl/org.gradle.plugins.ide.idea.model.IdeaModule.html
I hope this helps someone else
I tried the above solution but found a problem. In my scenario I had a sub-project that had the above configuration. The problem was that the transitive dependencies of the sub-project were not being exported in the IntelliJ configuration, which caused the base project to stop compiling.
I did some digging around and stumbled upon this little gem which fixed the problem.
https://github.com/gradle/gradle/blob/ccddc438ce09293d84030ebe31668d739c8a228a/gradle/providedConfiguration.gradle
/**
* Adds a configuration named 'provided'. 'Provided' dependencies
* are incoming compile dependencies that aren't outgoing
* dependencies. In other words, they have no effect on transitive
* dependency management.
*/
configurations {
provided
providedPlusCompile.extendsFrom(compile, provided)
testCompile.extendsFrom(providedPlusCompile)
}
sourceSets.main {
compileClasspath = configurations.providedPlusCompile
}
plugins.withType(IdeaPlugin) {
idea.module.scopes.PROVIDED.plus = [ configurations.provided ]
}
Adding to the answer from Adrijardi, for Gadle 2.0 + not only did I have to change
scopes.PROVIDED.plus += configurations.provided
to
scopes.PROVIDED.plus += [configurations.provided]
I also had to change
provided project(":module-name") {
transitive = false
}
to
provided (project(":module-name")) {
transitive = false
}
Note the extra set of brackets on the second code sample
Related
I have a Gradle project with two subprojects. The parent does not contain any code; all the Kotlin code is in the two subprojects. All Gradle build files are defined in the Kotlin DSL.
Upon building, Gradle generates two JAR files, one in the build subfolder of each subproject. I believe this is the intended default behavior of Gradle. But this is not what I want.
I want to publish the JAR file of the parent project as a Maven artifact. Therefore, I need both subprojects to be included in one JAR file. How can I achieve this?
Note: On this web page, the author seems to achieve pretty much what I would need in this code snippet:
apply plugin: "java"
subprojects.each { subproject -> evaluationDependsOn(subproject.path)}
task allJar(type: Jar, dependsOn: subprojects.jar) {
baseName = 'multiproject-test'
subprojects.each { subproject ->
from subproject.configurations.archives.allArtifacts.files.collect {
zipTree(it)
}
}
}
artifacts {
archives allJar
}
However, this is defined in Gradle's native Groovy DSL. And I find myself unable to translate it into the Kotlin DSL. I tried to put a Groovy build file (*.gradle) besides the Kotlin build file (*.gradle.kts), but this led to a strange build error. I'm not sure if mixed build file languages are supported. Besides, I would consider it bad practice too. Better only define all build files in just one language.
Also, the example above pertains to the Java programming language. But I do not expect this to be a big problem, as both Java and Kotlin produce JVM bytecode as compile output.
More clarification:
I am not talking about a "fat JAR". Dependencies and the Kotlin library are not supposed to be included in the JAR.
I do not care if the JAR files for the subprojects are still getting built or not. I'm only interested in the integrated JAR that contains both subprojects.
The main point is getting the combined JAR for the binaries. Combined JARs for the sources and JavaDoc would be a nice-to-have, but are not strictly required.
I would use the Gradle guide Creating "uber" or "fat" JARs from the Gradle documentation as a basis. What you want is essentially the same thing. It's also much better than the Groovy example you found, as it doesn't use the discouraged subprojects util, or 'simple sharing' that requires knowing how the other projects are configured.
Create a configuration for resolving other projects.
// build.gradle.kts
val mergedJar by configurations.creating<Configuration> {
// we're going to resolve this config here, in this project
isCanBeResolved = true
// this configuration will not be consumed by other projects
isCanBeConsumed = false
// don't make this visible to other projects
isVisible = false
}
Use the new configuration to add dependencies on the projects we want to add into our combined Jar
dependencies {
mergedJar(project(":my-subproject-alpha"))
mergedJar(project(":my-subproject-beta"))
}
Now copy the guide from the docs, except instead of using configurations.runtimeClasspath we can use the mergedJar configuration, which will only create the subprojects we specified.
However we need to make some modifications.
I've adjusted the example to edit the existing Jar task rather than creating a new 'fatJar' task.
for some reason, setting isTransitive = false causes Gradle to fail resolution. Instead I've added a filter (it.path.contains(rootDir.path)) to make sure the Jars we're consuming are inside the project.
tasks.jar {
dependsOn(mergedJar)
from({
mergedJar
.filter {
it.name.endsWith("jar") && it.path.contains(rootDir.path)
}
.map {
logger.lifecycle("depending on $it")
zipTree(it)
}
})
}
When creating a jar from my Kotlin code and running it, it says "No main manifest attribute".
When looking at the manifest.mf, it has this content:
Manifest-Version: 1.0
When looking at the file in the source, it has this content:
Manifest-Version: 1.0
Main-Class: MyMainClass
When manually copying the source manifest to the jar, it runs perfectly.
Screenshot of my artifact settings
I got this error with Gradle and Kotlin.
I had to add in my build.gradle.kts an explicit manifest attribute:
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = "com.example.MainKt"
}
}
From the gradle documentation, it's better to create a fatJar task to englobe all of the runtime dependencies in case you encounter java.lang.NoClassDefFoundError errors
If any of the dependent jars has a MANIFEST.MF file, it will override your custom one which defines the Main-Class.
In order to address this problem you should do the following:
Disable the alphabetical ordering
Change items ordering so that item which has META-INF/MANIFEST.MF file is the first in the list
Your custom MANIFEST.MF will be picked up by IntelliJ IDEA and displayed for the jar artifact.
See the related issue for more details.
You can also use Gradle or Maven to generate the fat jar instead.
1.Add the following task definition in the build script
tasks.jar {
manifest {
attributes["Main-Class"] = "MainKt"
}
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile))
}
}
Then the jar tasks (Tasks | build | jar) again from the right hand sidebar.
For Spring boot apps:
What worked for me (gradle kotlin) in build.gradle.kts
add spring boots plugin &. apply dependency management
plugins {
id("org.springframework.boot") version "2.6.7"
}
apply(plugin = "io.spring.dependency-management")
set your main class
springBoot {
mainClass.set("com.example.Application")
}
Found this all by reading up on spring-boot docs found here
I have a Kotlin project which is comprised of three modules:
Core < Service < Web
The structure is:
build.gradle
core/
build.gradle
service/
build.gradle
web/
build.gradle
The structure for the root build.gradle file is:
buildscript {
ext.kotlin_version = '1.1.60'
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
subprojects {
apply plugin: 'kotlin'
apply plugin: 'jacoco'
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
repositories {
mavenCentral()
jcenter()
}
}
The individual build files look like (for core):
dependencies {
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
...
}
And for service (note the only difference is the project dependency):
dependencies {
compile project (':core')
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
...
}
There are a few optimizations I'd like to make, but I'm still learning Gradle, and can't find the right way to reorganize things. The problems I have are:
I can't build service or web individually, as they complain about not being able to find their dependent sub-projects. (Via a gradle build in the service/ directory, for example.)
How can I define the Kotlin stdlib-jre8 dependency at the root level, so it's not duplicated in my three build files?
How can my subproject/buildscript tasks of the root buildfile use the same repository definitions, so that I don't have to define mavenCentral()/jcenter() twice?
Basically this build structure that I have was cobbled together through some experimentation/web resources, and happens to mostly work, but I'd like some guidance on doing it the right way, such that (1) it follows good Gradle practices, and (2) still works well via auto-import into IDEA.
If you want to run a build on a module you can do by running: gradle :web:build
In the same way that you are adding repositories in the subprojects clause, you can add the dependencies block in there. Make sure that only the shared dependencies are in there. For instance, compile project(':core') should only be in the relevant project. It's ok to have multiple dependency blocks. That said, it's usually more of a headache to use the subproject clause. Because, if dependencies change for one of the modules you are forced to update that for all of them
Regarding the repository definitions, they are very different. The one in the buildscript block is used by Gradle itself in order to find plugins and other 'pre-build' requirements. The one on the submodules is used to find the dependencies of the source code in the given module. As mentioned on the previous point, it's easier to manage when placed on the respective module build scripts.
Update:
If you want to keep the same version for all modules, the variables defined on ext in the buildscript should be able to be accessed from the submodules too: ext.kotlin_version = '1.1.60' and if you have multiple ones you can add them like:
ext {
kotlin_version = '1.1.60'
junit_version = '4.12'
}
Also, if you want to share code between modules, you can always extract it to gradle file and load it where needed using: apply file: "$rootDir/path/to/script.gradle"
Regarding #3, I'll give you a practical example.
Google has its own maven repository that contains all the dependencies for an android module. If you have a project that contains both a server-side module and android module you may not need the server-side one to look up dependencies on the Gradle artefact repository (artefact is the name of the jar dependency).
As for the buildscript repositories, in your case you are only loading one classpath (pre-build) dependency which is located on mavenCentral() so you may be ok removing jcenter() here.
I'm pretty new to Gradle and IntelliJ and I'm trying to understand how it works [cos that's what my project uses]. I created a simple java project in Intellij IDEA with a HelloWorld class and manually added the gradle.build file along with the necessary dependencies as follows:
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'application'
mainClassName = 'main.java.test.HelloWorld'
dependencies {
compile 'joda-time:joda-time:2.2'
}
repositories {
mavenLocal()
mavenCentral()
}
jar {
baseName = 'gradle-test'
manifest {
attributes 'Main-Class': 'main.java.test.HelloWorld'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.2'
}
While I specified the joda-time dependency, my IntelliJ project is unaware of this library and therefore I can't use of any of the autosuggest features of IntelliJ for the joda-time library.
Could someone give me information on how I can get IntelliJ to automatically start recognizing the downloaded jars and allow me to use the auto-suggest features?
In other words,
where are the dependency jars downloaded ?
How can I have these jars to automatically be included in the
class path of the project ? [Is it ok to do so ? If not, what
would be your recommendation ?]
Thanks !
I used Peter's suggestion to run gradle idea and imported the project into IntelliJ and I was able to start using the auto-suggestion features in IntelliJ.
Thanks
I am trying to get my existing Android Project working with gradle and IntelliJ 12.
Previously I had it working with maven but that didn't seem to be so flexible as gradle, and from what I think I got to know is that I need less subfolders.
My Android project is divided into a clean java library (:core) and the actual Application (:android). These two projects are both in my main project folder.
~-+MainProject
|--+core
| L--build.gradle
|--+android
| L--build.gradle
|--build.gradle
L--settings.gradle
I think the ideal solution to get gradle work with this is to treat the (:core) and (:android) project as nested projects, meaning I can simply be with my cmd in MainProject source folder to start the gradle tasks.
However I came up with different problems:
Gradle dependencies are only included in the core project
that means neither in the instrumentatetionTest nor in the main project files are any
directories correctly set as source / test directories
IntelliJ doesn't resolve any classes that I added as dependencies in gradle (AndroidSDK, junit, mockito, those from the :core project)
tried to use the plugin 'idea' but either I used it wrong or it didn't solve the problem
I get duplicate dependency Error for building the APK
must have something to do with junit:4.11 and mockito-core:1.9.5 that I added
what I tried:
deleting the dependencys -> build fails cause some classes of course could'nt be resolved
changing to junit:4.5+ as suggesed in some other thread -> no change at all
Here are the *.gradle configuartions
MainProject:
-- settings.gradle
include ':core', ':android'
-- build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
}
}
subprojects {
repositories {
mavenLocal()
maven { url "http://repo.maven.apache.org/maven2" }
}
}
:core
-- build.gradle
apply plugin: 'java'
dependencies {
testCompile 'junit:junit:4.11'
testCompile 'org.mockito:mockito-core:1.9.5'
}
:android
apply plugin: 'android'
repositories {
mavenCentral()
}
dependencies {
compile project(":core")
compile 'com.google.android:android:4.1.1.4'
instrumentTestCompile 'junit:junit:4.11'
instrumentTestCompile 'org.mockito:mockito-core:1.9.5'
instrumentTestCompile 'com.google.dexmaker:dexmaker:1.0'
instrumentTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'
instrumentTestCompile 'com.jayway.android.robotium:robotium-solo:3.6'
}
/* ... androidSettings
I hope someone could help me with that
MFG Dornathal
Alright, so you've got the right idea, but there are a few changes you still need.
Your root build.gradle file should be as follows:
subprojects {
repositories {
mavenCentral()
}
}
You only need to include mavenLocal() if you are using a locally installed repo. Most people don't, and nothing in your project indicates that you need one.
mavenCentral() can be used to replace to maven URL you were using.
We only need to modify the buildscript for the android project, so we should localize that to the android project's build.gradle.
Your settings.gradle and your build.gradle for the core project are good.
Your build.gradle for the android project however, needs some changes:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
}
}
apply plugin: 'android'
dependencies {
compile project(":core")
instrumentTestCompile 'com.jayway.android.robotium:robotium-solo:3.6'
}
By including the android plugin, we link directly to the installed SDK. This means we no longer need to include the android dependency: compile 'com.google.android:android:4.1.1.4'.
We don't need to include junit. It's provided by the SDK, so we'll just use that. One thing to note is that the SDK only includes JUnit 3.
We don't need to include mockito and dexmaker unless we actually use it for the android tests. If it's only being used for the tests on the java library, we don't need it here.
To answer your questions:
I'm not sure what you're asking here. It might help if you should your project structure including the directories in which you have files and such.
Your hunch is correct. Hamcrest, which provides matchers for tests, made a breaking change to their API between versions 1.1 and 1.3. JUnit 4.11 has a dependency on Hamcrest 1.3. Mockito 1.9.5 however, depends on Hamcrest 1.1. So when both are included, the 1.3 overrides the 1.1 & Mockito no longer works. Reverting to JUnit 4.5 is also a problem. Junit 4.5 includes the Hamcrest 1.1 jar as a file rather than as a dependency in the POM. So this will cause issues when we have 2 versions of the same file. JUnit 4.10 is the way you want to go here. This has the dependency on Hamcrest 1.1 and includes it in the POM instead of as a file. Win win! I've had issues with this too, and the best way for me to figure it out was to just look at the POM files on Maven Central and see what they tell me.
One final note. Intellij 12 cannot handle Gradle Multi-project builds. You need to switch to Android Studio or Intellij 13 for that.