After 2 full days of trying to figure out how to make a project with multiple targets and have the java target use javafx, I think give up. I’m not the ranting type but I’ve decided to write about my journey and all the issues I’ve encountered which sometimes are many years old but are still untouched and goodluck finding a workaround.
A part of this has nothing to do with gradle kotlin but bear with me.
So, I want to create a multi-target Kotlin application using javafx for the Java target. I go to New project->Kotlin->Application. So far so good, right? Wrong. Already it doesn’t work, because it starts spitting a lot of errors on my screen. After some googling I find someone else who has the same issue: Gradle doesn’t work with JDK 17, I need JDK 15. Great, let’s go and install like the 3rd JDK today. I go to the java.net site, but it’s closed. Nice. So let’s google ‘JDK 15’ and click the first link. Ah, Oracle JDK archive, this is what I wanted. I click on ‘Download’ and Oracle politely asks me to create some account and provide full name, country, address, phone number, company, workplace info, credit card number, the 3 digits on the back and rights to my first-born child. Sounds good but let’s try an alternative. Let’s google ‘openjdk’. I go to their site and they redirect me to… java.net? But I thought it was closed? Whatever, I download jdk15, extract it and try to get on with my life. Create a new project (again) and it asks me to add a target. I’ve never used kotlin/gradle scripts before so here comes some intense research on how to use kotlin plugins. After some hours of trial and error, I think I understand how they work. So I create a new project (again), as the old one was full of said trial-and-error attempts over the build.gradle.kts files. And here starts the fun part.
I add a jvm target and create the ‘jvmMain’ and ‘jvmTest’ folders. I create a main function, add the application plugin and execute ‘gradle:run’. It works first try, I also create a native target and I configure the ‘entryPoint’, also works, Damn, I’m a hacker and everything’s so simple. So I now have some code in the common module used by the 2 target modules. Alright, let’s use javafx in my jvm target. Oh wait, stupid me thought Oracle would make it that simple. Spoiler: they didn’t. They thought it would be a good idea to remove javafx from java, although it still technically is part of openjdk. ? Whatever, I just add the javafx dependencies to my ‘jvmMain’ (I’m a hacker after all, I know how to do this) aaaand… it doesn’t work. Just like that, trolls me on the spot. Unresolved references everywhere. So some more intense googling and it seems like I have to use yet another plugin (yay!) - the javafx plugin, created specially to add javafx to java because they removed it from java. So I add the plugin and surprise surprise, it doesn’t work. I didn’t even expect it to work to be honest, I just wanted to start researching a different error, so this is what I do. And here starts the part where I’m mad at the kotlin plugins.
These kotlin plugins actually use ‘sourceSets’, where basically they create 10 different modules just because (jvmMain, commonMain etc.). And these new modules/sourceSets are not compatible with gradle plugins which try to add a dependency, or something. So I google how to use javafx and kotlin together and, apparently, ‘kotlin-multiplatform’ is compatible with pretty much nothing, as it uses completely different classes to represent internal objects such as sourceSets or projects, although they have the same name. Great, I can’t add javafx to my java sourceSets. So what’s the solution? None! Why would there be a solution, it’s only been a reported problem for at least 2 years. So let’s use a workaround instead: create a new module and use the ‘kotlin-jvm’ plugin, to which I can add dependencies like normal. Easier said than done, had to do some more ‘build.gradle.kts’ research. Anyways, it’s done - I have a new java module to where I moved all my code + the javafx plugin. No more Unresolved references! You’d think it’s over, but no.
No error in editor but attempting to run it throws me “Error: JavaFX runtime components are missing” …but I thought the plugin deals with that? What does that plugin even tho? Easy fix this time tho: adding the ‘application’ plugin and running it with ‘gradle:run’ works! Also, before I couldn’t add the ‘application’ plugin because, you’d never guess it, it’s not compatible with ‘kotlin-multiplatform’. Who cares tho, I finally have the module running. However, more Unresolved references now: this new project can’t acces my common code. Easy fix, right? Just add
implementation(rootProject) …it does nothing. Great, let’s see a sample project and see how they do it… oh, it’s
implementation(project(":")), silly me …it still does nothing. You’d think a 9 year old issue would be fixed. I wouldn’t, because it isn’t. Noone explains why or how it should (or not) work, the only reply is “it’s uncommon to do this”. Fantastic support right there. Anyway, time for yet another workaround! Create a new ‘core project’, move all the ‘kotlin-multiplatform’ there and leave the top project empty, only to contain the other 2. So I do this, and I try to build. At this point the screen is full of errors and more red (redder?) than my eyes, which is a good sign. Gradle is spitting
org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension_Decorated cannot be cast toorg.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension at me and I’m just crying. Alright, let’s run it from console with
--info, maybe I get more information. Ah, finally a documented error: I’m sent to [a nice webpage](https://org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension_Decorated cannot be cast to class org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension) which helps me fix the error. My gradle manages to sync! Amazing. Is it… over?
Nowhere near. I try to add
implementation(project(":core")) and… no gradle error this time! But it won’t build, and the Unresolved references are back. But… my ‘jvm’ project is depending on the ‘core’ project, and gradle isn’t complaining so… what’s wrong? I can’t seem to google the right terms, so I look at a sample project where a ‘jvm’ modules depends on a ‘core’ module, see how they do it. And… their ‘jvm’ module uses ‘kotlin-multiplatform’ with a jvm target instead of ‘kotlin-jvm’ as a workaround. Great, I just change it too, right? Except… I initially started with just a ‘kotlin-multiplatform’ project and swapped to ‘kotlin-jvm’ as another workaround… but now I need to switch back… or else I can’t use my common code… but if I switch back, I can’t use javafx, as it’s not compatible with ‘kotlin-multiplatform’, only with ‘kotlin-jvm’…
God just end me.
Basically, why is everything incompatible with everything in these kotlin gradle plugins?
BTW, this is a sample project which does not compile with the above setup:
Sample.zip (84.0 KB)