I have a Kotlin Multiplatform project that targets both Jvm and Android.
Creating separate javafx and android sample projects, but for my javafx module when I add the dependency for my multiplatform module, it is importing the -android variant.
// sample-javafx build.gradle.kts
plugins {
kotlin("jvm")
// javafx plugins
id("application")
}
// gradle stuff...
dependencies {
// this imports the -android variant
implementation(project("my-multiplatform-module"))
}
For example, when importing a published multiplatform library you can specify the -jvm variant, like:
// build.gradle.kts
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.0")
}
How can I configure the dependency for my javafx sample to import the -jvm variant of my kmp library project module?
My wild guess is that in this case the multiplatform library is not exposing a jvm artifact explicitly, this could be a potential feature request
Alternatively you could try publishToMavenLocal() and consume it from local maven
Related
In our team we have lot of projects built with Gradle. Some parts in the Gradle files are all the same. For example, we use Java 11 in all our projects. So my idea was that I could split up my build.gradle files into a common part, that is then synced from a central repository into every Gradle project while the project specific parts remain in build.gradle.
build.gradle:
plugins {
id 'java'
//...
}
apply from: "common.gradle.kts"
dependencies {
// ...
}
common.gradle.kts
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
test {
useJUnitPlatform()
}
Now I get the error message by Gradle
* Where:
Script '/Users/.../common.gradle.kts' line: 4
* What went wrong:
Script compilation errors:
Line 04: java {
^ Expression 'java' cannot be invoked as a function. The function 'invoke()' is not found
Line 04: java {
^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val PluginDependenciesSpec.java: PluginDependencySpec defined in org.gradle.kotlin.dsl
Line 05: toolchain {
^ Unresolved reference: toolchain
Line 06: languageVersion = JavaLanguageVersion.of(11)
^ Unresolved reference: languageVersion
Line 09: test {
^ Unresolved reference: test
Line 10: useJUnitPlatform()
^ Unresolved reference: useJUnitPlatform
6 errors
For some configurations I found an alternative using a more generic API that works, though it is a lot of effort to find the corresponding alternatives and in the end no one can guarantee that they do exactly the same thing:
tasks.withType<JavaCompile> {
options.release.set(11)
}
So the question remains: why can't I use the DSL functions java or test in my externalized common.gradle.kts?
It seems it has to do something with the use of Kotlin script, at least if I use Groovy too for my externalized script, it works.
In your common.gradle.kts, java { } is generated helper Kotlin DSL function. Gradle doesn't know about the Kotlin DSL helpers unless
it's part of a build (not using apply(from = "...")
the java plugin is applied
Understanding when type-safe model accessors are available
Only the main project build scripts and precompiled project script plugins have type-safe model accessors. Initialization scripts, settings scripts, script plugins do not. These limitations will be removed in a future Gradle release.
Reacting to plugins
https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#reacting_to_plugins
It's still possible to have your common.gradle.kts - but it needs to configure the Java Plugin without the Kotlin DSLs
// common.gradle.kts
plugins.withType(JavaBasePlugin::class).configureEach {
// the project has the Java plugin
project.extensions.getByType<JavaPluginExtension>().apply {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
}
This is a little more clunky because the Kotlin DSL helpers aren't available.
buildSrc convention plugins
If you want to create conventions for a single project, then the standard way is to create buildSrc convention plugins.
https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources
This is best for projects that have lots of subprojects.
// $projectRoot/buildSrc/src/main/kotlin/java-convention.gradle.kts
plugins {
java
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
test {
useJUnitPlatform()
}
See the answer here for more detail: https://stackoverflow.com/a/71892685/4161471
Sharing plugins between projects
https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html
It's possible to share convention plugins between projects, so long as you have a Maven repo to deploy your plugins.
It's even possible to create your own Gradle distribution, so the plugins are included along with the Gradle wrapper! https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:custom_gradle_distribution
However I'd advise against these approaches. Generally the time invested in creating shared plugins will never be faster than just copy and pasting buildSrc convention plugins. And more importantly, it's best to keep projects independent. While sharing build conventions seems like a good idea, it introduces dependencies that make it hard to track problems, and makes updating the shared plugins hard as you're not sure what the consequences might be. This article explains more https://phauer.com/2016/dont-share-libraries-among-microservices/
I'm creating a sample demo application with JavaFX in IntelliJ, but I need to use a library called the JavaFaker library. I'm using Gradle as the build system, but every time I try to add the library, either as the implementation in the build.gradle file, or via IntelliJ project structure options, the module.java file says error: module not found. I've already tried adding it to modules but nothing changes.
module-info.java
module com.example.demo1 {
requires javafx.controls;
requires javafx.fxml;
requires javafaker;
opens com.example.demo1 to javafx.fxml;
exports com.example.demo1;
}
build.gradle
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.10'
id 'org.beryx.jlink' version '2.24.1'
}
group 'com.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
ext {
junitVersion = '5.8.2'
javaFakerVersion = '1.0.2'
}
sourceCompatibility = '17'
targetCompatibility = '17'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
application {
mainModule = 'com.example.demo1'
mainClass = 'com.example.demo1.HelloApplication'
}
javafx {
version = '17.0.1'
modules = ['javafx.controls', 'javafx.fxml']
}
dependencies {
implementation("com.github.javafaker:javafaker:${javaFakerVersion}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}
test {
useJUnitPlatform()
}
jlink {
imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") as RegularFile
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'app'
}
}
jlinkZip {
group = 'distribution'
}
error message
> Task :HelloApplication.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
I tried for a while to get this to work with Gradle but was unable to. I don't know Gradle well, but unless you do, I don't advise trying it.
Alternate option: use a static import
I didn't try this, but this is suggested in another answer.
Before you try this, see:
What's the difference between requires and requires static in module declaration
It is IMO, a bit of a hack in this usage case. This makes the module optional at runtime. But, if the module is on the classpath instead of the module path its code can still be used. More information quoted from the linked answer:
A requires static clause expresses a dependency that is optional at
run time. That means at compile time the module system behaves exactly
as described above.
At run time, on the other hand, it mostly ignores requires static
clauses. If it encounters one, it does not resolve it. That means, if
an observable module is only referenced with requires static, it does
not make it into the module graph!
Alternate option: Non-modular project
You can fix this issue by making your project non-modular:
Delete your module-info.java file.
Run your application with JavaFX modules on the module-path.
The org.openjfx.javafxplugin you are already doing will help achieve this by specifying the modules to be used.
To execute the application directly in the IDE rather than through Gradle, you will need to specify the module options to the VM for the IDE execution configuration (information on that is in the getting started documentation at openjfx.io).
For packaging, switch to using the badass-runtime-plugin rather than the badass-jlink-plugin. This will package the application via jpackage rather than jlink (which cannot package non-modular applications or applications with automatic modules).
In the application block of your build file, you no longer need to specify the module for your application as you no longer have one.
While that means that your application is no longer modular, in this case, in my opinion, this is not such a big loss. The dependencies you are using are not well-defined modules, so you can't use jlink to create a package for your application, and you don't have the level of modular encapsulation and definition you would normally receive for fully modular projects.
For more information, see the Getting started instructions at:
https://openjfx.io/openjfx-docs/
Under the sections "Non-Modular with Gradle" for your selected IDE.
Alternate option: Using Maven
It is easy to get this to work with Maven.
Create a new JavaFX project
Choose Maven as your build system instead of Gradle.
Add the javafaker dependency to your pom.xml.
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
Press the refresh icon in the Maven window to reimport the Maven project into the IDE.
Add the requires clause for the javafaker module into your module-info.java
requires javafaker;
Add the code to use javafaker to your app.
I don't have code to use javafaker, so I could not verify that the last step would work, but, give it a try . . .
Why you can receive this issue when using Gradle, but not Maven
Looking at the Gradle Documentation section "Using libraries that are not modules":
A third case are traditional libraries that provide no module information at all — for example commons-cli:commons-cli:1.4. Gradle puts such libraries on the classpath instead of the module path. The classpath is then treated as one module (the so called unnamed module) by Java.
This is the case with the javafaker dependency that you are using. It has no module-info.java and does not define the property Automatic-Module-Name in its manifest file (which are the other two cases in the section). Both the other cases result in Gradle putting the library on the module path, but the case you have means that it is on the class path.
This is a problem when you want to access the code from a named module that you define, which you have because you created a module-info.java.
Your module can only find code and resources of modules it requires (which need to be on the module path), so you add requires javafaker to the module-info.java, and get the following when you try to run through the IDE:
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
So you remove the requires javafaker from the module-info.java as advised by the Gradle documentation I linked and you get the following when you try to compile:
Package 'com.github.javafaker' is declared in module 'javafaker', but module 'com.example.demo1' does not read it
So you must place the library in the module-info to use it, but you can't place the library in module-info because Gradle puts in on the classpath -> catch-22.
There are workarounds to this such as providing VM arguments to allow access to the unnamed module (which is the classpath), or maybe modifying the module path handling of the Gradle build and/or IDE somehow (I don't know how), but they are pretty ugly.
On the other hand, for this case, Maven acts differently from Gradle, it places the dependent library on the module path, even if it does not have a module-info.java or Automatic-Module-Name defined. This means that it was (for me) much easier to set up and use.
Incidental advice on module naming
This is not an error, but note: Although module names with numbers in them are now allowed due to a change in the module system specification, it is probably best not to put numbers in module names to prevent the module name and version info being confused.
I've had a similar issue recently. Adding static to the requires statement helped however. Maybe this will fix your issue without having to switch to maven.
So you'd need to add: requires static javafaker;
Please forgive me in advance as I've been using Java since the early 2000s and have been slow to transition new projects toward being compliant with Project Jigsaw and modules (introduced in Java 9.) I'm stuck and hoping someone can help me out. I've tried to create as minimal project as possible to help me focus on the problem. I'm using:
JavaFX - I followed the instructions on https://openjfx.io/openjfx-docs/ using their guidance for Modular Gradle with IntelliJ, though I'm not interested in building an image yet, so I'm leaving jlink out of it. This worked just fine.
Tablesaw for some pandas-like data crunching
JUnit 5.8.2
I have only one class file, HelloFX down the package org.hello.
Executing..
$ .\gradlew run
I get a ResolutionException error from Gradle while trying to run the project:
Error occurred during initialization of boot layer java.lang.module.ResolutionException: Modules shims and RoaringBitmap export package org.roaringbitmap to module listenablefuture
My project tree (all located in a root folder called TestProject):
./gradle
./gradlew
./build.gradle
./.gradle
./gradlew.bat
./settings.gradle
./.idea
./src
./src/test
./src/test/resources
./src/test/java
./src/main
./src/main/resources
./src/main/java
./src/main/java/module-info.java
./src/main/java/org
./src/main/java/org/hello
./src/main/java/org/hello/HelloTS.java
Here are the pertinent files:
settings.gradle
rootProject.name = 'TestProject'
build.gradle
plugins {
id 'application'
id 'java'
id 'idea'
id 'org.openjfx.javafxplugin' version '0.0.12'
id 'org.javamodularity.moduleplugin' version '1.8.10'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
javafx {
version = "17.0.2"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
test {
useJUnitPlatform()
}
dependencies {
implementation 'tech.tablesaw:tablesaw-core:0.42.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
}
application {
mainModule = "$moduleName"
mainClassName = "org.hello.HelloFX"
}
module-info.java
module TestProject {
requires javafx.graphics;
requires javafx.controls;
requires tablesaw.core;
exports org.hello;
}
What I've discovered so far:
Eliminate Tablesaw - Comment out requires tablesaw.core; from module-info.java and implementation 'tech.tablesaw:tablesaw-core:0.42.0' from build.gradle and my little JavaFX app works just fine with modules, but then I lose Tablesaw.
Eliminate modules - Remove module-info.java, then comment out the mainModule line in build.gradle. Then, I can run both a sample JavaFX program and a sample Tablesaw program by simply changing mainClassName to the program I want to run. I can even add some Tablesaw code in my sample JavaFX app, and it works. This is my backup plan, since it gives me what I want, albiet without modularization.
So, I'm really stumped here. This post didn't help, nor did any other that tried to address this weird ResolutionException error from Gradle. My guess is that Tablesaw is not module compliant? Or I need some sort of exclusion clause in my dependencies for Tablesaw? I tried to use the java-library plugin and use the api clause in build.gradle for Tablesaw as it seemed like that plugin is for non-modular libraries, but that didn't work.
There must be a way to do this, but admittedly I am about ready to throw in the towel and, yet again, just go back to non-modular development for my latest project. I have been a huge fan of Java since its inception, (even fully certified back in the Sun Microsystems days! That'll date me!) I understand why modularization has been introduced. Makes complete sense! But frankly, I'm finding its implementation to be quite challenging to embrace.
Any help would be greatly appreciated. Thank you kindly!
Tablesaw 0.42.0 isn’t built to support the Java module system.
It has no module-info.
It uses shading for its dependencies
It uses dependencies like RoaringBitmap that have issues if you try to use them with the module system.
I suggest you log an issue with Tablesaw requesting that they modularize the library.
In the meantime, JavaFX should be run from the module path as it is only supported that way, but it will probably be better to run Tablesaw from the class path.
You can put JavaFX on the module path and add the JavaFX modules via command line switches.
Put Tablesaw on the class path, don’t add it as a module.
Don’t define a module-info for your app, create a non-modular app that adds the JavaFX modules via switches. This means that your app code is also on the class path so it can access Tablesaw and it can also access JavaFX modules through virtue of the command line switches.
I don’t use Gradle, so I can’t provide you the exact build script you need for this.
For more info see:
openjfx.Io getting started documentation on non-modular with gradle for your IDE
You will probably be able to package your app using the:
badass runtime plugin.
We have a very productive and robust SPL (software production line) for .NET platform to create web applications and HTTP services.
Now we want to import that knowledge into Android.
This is the scenario:
Developer A gets our android framework, and project A into the following paths:
D:\Android\Framework
D:\Android\ProjectA
And of course, project A should reuse code and stuff in Framework (layouts, java utilities, etc.)
Developer B's setup is:
C:\Users\Jack\AndroidStudioProjects\Framework
C:\Users\Jack\AndroidStudioProjects\ProjectB
Again, project B reuses Framework libraries.
Both developer A and developer B have AndroidProjectsRoot environment variables defined in their systems. For developer A it refers to D:\Android and for developer B the path is C:\Users\Jack\AndroidStudioProjects.
It's a team-convention that we all have one root folder and get each project in a direct child directory of that root folder.
We use Android Studio and gradle, and here's the place where we're stuck. We can't make project A or B build using Framework libraries. Framework has these libraries:
--jzp.framework
--validation
--http
-- more libraries here
Inside project A/B's settings.gradle we have:
include ':app', ':http', ':validation' //, more includes here
project(':http').projectDir = new File('$System.getenv("")/Framework/http/libs')
project(':validation').projectDir = new File('$System.getenv("")/Framework/validation/libs')
// more directory configurations here
Then in the app's gradle we have:
dependencies {
// other dependencies here
compile project(':http')
compile project(':validation')
// more compile statements here
}
However, project A/B won't build and we can't see our libraries packages inside our projects A/B etc.
What might be wrong? How to add dependencies to other modules on the hard drive, via environment variables?
I'm trying to pull together an Android project, it includes several library packages, and a demo app that utilize the libraries.
I'm using Android Studio and Gradle. (I used to work with Eclipse, without gradle.)
So here in Android Studio, I have a project including several packages(or modules? what's the difference in concept?)
I have a core library project. And another library, say Flavor, that utilizes core.
In the Flavor module,
In build.gradle file, I have
dependencies {
....
compile project(':**core-project**')
....
}
I understand it as that Gradle add the core project build path to my Flavor project. (Same way that you can do it manually in Eclipse. See picture)
In java file,
import org.project.**core**; //this shows Unused
public MyFlavorClass extends CoreClass {
...
}
I deleted the import statement and it seems fine.
So why the import statement is not needed here?