I am attempting to move some commonly used modules into their own project so they can be uploaded to a private repository and used in applications. Currently, they are all in one multi-project configuration and everything works as expected. I moved them out and it all seems to build fine. When I use them as dependencies in my applications, I get errors that say java.lang.module.FindException: Module X not found, required by COMMON-MODULE-Y.
I was under the impression that the java-library plugin allowed my application to import those secondary dependencies that were needed by the dependencies declared, and this is not what is happening.
How can I package my commonly used modules to allow this OR how can i configure my project to allow for this? I am using Java 11 for this.
EDIT
Basically I want my libraries to be self contained and the applications that use them to not include any additional libraries.
Here is my build.gradle for the module I am getting the above error on.
plugins {
id 'java-library'
id 'idea'
}
if(project.hasProperty('javaVer') && javaVer == '8') {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
idea {
module {
inheritOutputDirs = true
}
}
def currentOS = org.gradle.internal.os.OperatingSystem.current()
def depPlatform
if (!project.hasProperty(('platform'))) {
if (currentOS.isWindows()) {
depPlatform = 'win'
} else if (currentOS.isLinux()) {
depPlatform = 'linux'
} else if (currentOS.isMacOsX()) {
depPlatform = 'mac'cd
}
}else {
depPlatform = project.getProperties().get("platform");
}
sourceSets.main {
java {
srcDir 'src/main/java' //assume that your source codes are inside this path
}
resources {
srcDirs = ['src/main/java', 'src/main/resources']
exclude "**/*.java"
}
}
ext.moduleName = 'license'
dependencies {
implementation project(':common')
implementation "org.openjfx:javafx-base:11.0.2:${depPlatform}"
implementation 'commons-io:commons-io:2.6'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.github.purejavacomm:purejavacomm:1.0.1.RELEASE'
}
Here is the build.gradle for the multi-project the above module is in.
subprojects {
afterEvaluate {
repositories {
mavenCentral()
jcenter()
}
compileJava {
if (project.hasProperty(('javaVer')) && javaVer == '8') {
excludes = ['**/module-info.java']
} else {
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
]
}
}
}
jar {
archiveFileName = project.name + '-' + (project.hasProperty(('releaseSpec')) ? project.releaseSpec : 'SNAPSHOT') + '.jar'
}
}
}
解决方案
Short answer: you want to expose runtime dependencies. E.g. they are needed for application execution, however they shouldn't be available for compilation.
Please use the following workable code:
settings.gradle.kts:
include(":application")
include(":libraryA")
include(":libraryB")
application/gradle.build.kts:
plugins {
application
kotlin("jvm") version "1.3.61"
}
dependencies {
api(project(":libraryA")) // we reference only library, we know nothing about the dependencies.
}
libraryA/gradle.build.kts:
plugins {
kotlin("jvm") version "1.3.61"
}
dependencies {
api(project(":libraryB")) // again, we know nothing about the dependencies
}
libraryB/gradle.build.kts:
plugins {
kotlin("jvm") version "1.3.61"
}
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61")
// api("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.3.3") - uncomment this line to expose api. You will see kotlinx-coroutines-reactor members in intellisence
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.3.3") // mark, that this dependency is needed for compilation. However it will not be exposed to other project.
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.3.3") // mark, that this dependency is required for runtime. It means, that it will be exposed as runtime dependency only
}
libraryB\src\Something.kt
import kotlinx.coroutines.runBlocking
fun doSomething() {
runBlocking { // use kotlinx-coroutines here.
println("Yess !!!")
}
}
application\src\Application.kt
package gradle.multi.application
import doSomething
fun main() {
"12314".toInt() // check, that api layer is exposed here
/* runBlocking { // this couldn't be compiled, because kotlinx-coroutines aren't exposed here
}*/
doSomething()
}
So, what we did:
Our dependencies: application --> libraryA --> libraryB
application is runnable project, all other are just libraries.
libraryA doesn't have any words to re-expose dependency. It just references libraryB
application just references libraryA. Developer doesn't know about any implicit dependencies (compile or runtime).
libraryB uses kotlinx-coroutines-reactor and exposes it as runtime dependency. And this information is published through libraryA to the runnable project.