Kotlin 1.3-M2: new multiplatform projects model

The project is here. And the build fails in MetaFormat class.The package name is not the same. In the library module it is hep.dataforge.meta and in io module it is hep.dataforge.meta.io. I will open a ticket later.

is it possible to use plugins in presets or ir it can be done some other way? for example shadow(fat jar) for jvm or kotlin-frontend for js? does anyone have working example?

I’m working on a multi-platform project, currently Android and JS (client), and am trying to copy additional files as part of the Gradle build.


kotlin {
  targets {
    fromPreset(presets.android, 'android') {
      compilations.all {
        tasks[compileKotlinTaskName].dependsOn syncAndroidAssets
      }
    }

    fromPreset(presets.js, 'js') {
      compilations.all {
        tasks[compileKotlinTaskName].dependsOn syncJsAssets, syncJsHtml
      }
    }
  }

  sourceSets {
    // ....
  }

Where “sync[Name]” are my custom tasks.The JS target works great, but the Android target fails with

Task with name ‘compileDebugKotlinAndroid’ not found in project

Is this just not working, or should be done in a different way?

tasks.compileKotlinNative.dependsOn compileWindowsResources

works for Kotlin/Native.

Maybe for you case something like this will work:

tasks.compileKotlinAndroid.dependsOn syncAndroidAssets

I believe there are some problems with Android configuration which aren’t actually related to MPP. To find out the root issue, try some additional logging, described here — stack traces will help you to get the idea what’s actually wrong with that. So far, it’s hard to diagnose what is the problem, not enough information for that.

As to shadow plugin (com.github.johnrengelman.shadow), the situation is as follows.

  • in case of using jvmWithJava target, it should work as is, just apply it from the top level of the build.gradle script. Note, hovewer, that the target is going to be deprecated as it’s already said above.

  • when using with jvm target, an additional task definition is needed, like the following:

      task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
              def target = kotlin.targets.jvm6
              from target.compilations.main.output
              def runtimeClasspath = target.compilations.main.runtimeDependencyFiles
              configurations = [runtimeClasspath]
      }
    

    — where jvm6 is the JVM target’s name.

    So overall build.gradle will look like:

      group 'com.example'
      version '1.0'
    
      buildscript {
          repositories {
              jcenter()
          }
          dependencies {
              classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.10'
              classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2'
          }
      }
    
      apply plugin: 'kotlin-multiplatform'
      apply plugin: 'com.github.johnrengelman.shadow'
    
      repositories {
          jcenter()
      }
    
      kotlin {
          targets {
              fromPreset(presets.jvm, 'jvm6')
          }
          sourceSets {
              commonMain {
                  dependencies {
                      api 'org.jetbrains.kotlin:kotlin-stdlib-common'
                  }
              }
              jvm6Main {
                  dependencies {
                      api 'org.jetbrains.kotlin:kotlin-stdlib'
                  }
              }
          }
      }
    
      task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
      	from kotlin.targets.jvm6.compilations.main.output
      	def runtimeClasspath = kotlin.targets.jvm6.compilations.main.runtimeDependencyFiles
      	configurations = [runtimeClasspath]
    

The support of kotlin-frontend plugin for JS is in progress right now, you may track the progress via this issue.

1 Like

Generated a stack trace, saved to
https://scans.gradle.com/s/kckuuohn2u23q/console-log
For now, got it working by hooking my task onto compileDebugSources

That specific task did not work, but with your idea of using a specific task found compileDebugSources which does work

thank you for reply, i’m tried shadow plugin in this way:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
    }
}

plugins {
    id 'kotlin-multiplatform' version '1.3.10'
    id 'kotlinx-serialization' version '1.3.10'
}

apply plugin: 'com.github.johnrengelman.shadow'

repositories {
    jcenter()
    mavenCentral()
    maven { url "https://dl.bintray.com/kotlin/ktor" }
    maven { url "https://kotlin.bintray.com/kotlinx" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-eap" }
    maven { url "http://dl.bintray.com/kotlin/kotlin-js-wrappers" }
}

kotlin {
    ext.ktor_version = '1.0.0-beta-4'
    ext.serialization_version = '0.9.0'

    targets {
        fromPreset(presets.jvm, 'jvm')
        fromPreset(presets.js, 'js')
    }
    sourceSets {
        commonMain {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
            }
        }
        commonTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-common'
                implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
            }
        }
        jvmMain {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
                implementation 'org.jetbrains.exposed:exposed:0.9.1'
                implementation "io.ktor:ktor-server-netty:$ktor_version"
                implementation "io.ktor:ktor-gson:$ktor_version"
                implementation "io.ktor:ktor-client-core:$ktor_version"
                implementation "io.ktor:ktor-client-cio:$ktor_version"
                implementation "io.ktor:ktor-locations:$ktor_version"
                implementation "io.ktor:ktor-html-builder:$ktor_version"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
                implementation "mysql:mysql-connector-java:8.0.7-dmr"
            }
        }
        jvmTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test'
                implementation 'org.jetbrains.kotlin:kotlin-test-junit'
            }
        }
        jsMain {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
                implementation 'org.jetbrains:kotlin-react:16.6.0-pre.59-kotlin-1.3.0'
                implementation 'org.jetbrains:kotlin-react-dom:16.6.0-pre.59-kotlin-1.3.0'
            }
        }
        jsTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-js'
            }
        }
    }
}

task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
    def target = kotlin.targets.jvm
    from target.compilations.main.output
    def runtimeClasspath = target.compilations.main.runtimeDependencyFiles
    configurations = [runtimeClasspath]
    println "task shadow jar"
}

it builds but jar is not fat…

@leonid
I tried your build script, and by running ./gradlew shadowJar I got a fat JAR under build/libs/test-project.jar which contains the dependencies:

$ unzip build/libs/test-project.jar
$ tree build/libs/test-project -L 2 -d
.
├── com
│   ├── google
│   ├── mysql
│   └── typesafe
├── io
│   ├── ktor
│   └── netty
├── junit
│   ├── extensions
│   ├── framework
│   ├── runner
│   └── textui
├── kotlin
│   ├── annotation
│   ├── collections
│   ├── comparisons
│   ├── concurrent
│   ├── contracts
│   ├── coroutines
│   ├── experimental
│   ├── internal
│   ├── io
│   ├── jdk7
│   ├── js
│   ├── jvm
│   ├── math
│   ├── properties
│   ├── random
│   ├── ranges
│   ├── reflect
│   ├── sequences
│   ├── streams
│   ├── system
│   └── text
├── kotlinx
│   ├── atomicfu
│   ├── coroutines
│   ├── html
│   ├── io
│   └── serialization
├── META-INF
│   ├── maven
│   ├── proguard
│   └── services
└── org
    ├── eclipse
    ├── hamcrest
    ├── intellij
    ├── jetbrains
    ├── joda
    ├── json
    ├── junit
    └── slf4j

Is your result different? What do you mean by ‘jar is not fat’?

1 Like

@leonid Note that the shadow JAR is created alongside with the normal JAR for the JVM target. The normal JAR is suffixed with -jvm. It is up to you to replace the normal JVM JAR with the fat one whenever it is used or published.

1 Like

yes, it works, thank you

You guys should really add this to documentation.

1 Like

@wild_lynx Hi, I’ve tried creating a project from template (Kotlin - Mobile Android and iOS) in Idea 2018.3.1, kotlin plugin 1.3.11-release-IJ2018.3-1, gradle 4.7. I’ve tried adding jvm target for ktor server I planned and building that target throws this error Error:Kotlin: Unsupported plugin option: org.jetbrains.kotlin.android:enabled=true.
Looking at module setting, it appears that all targets/submodules/sourcesets have android plugin enabled in “Compiler Plugins” tab. I’ve tried removing that in .idea project files but it just reappears after IDE restart. Any suggestions? build.gradle.xml (3.1 KB)

It would be good to have, or may it already exist, an example for new mpp model, like kotlin-fullstack-example ktor 0.9.0 · Issue #27 · Kotlin/kotlin-fullstack-sample · GitHub

Please make sure in IDE that Preferences | Build, Execution, Deployment | Build Tools | Gradle | Runner > Delegate IDE build/run actions to gradle is checked. For the IDEA’s built-in builder (JPS), there is an issue. Does it help?

Also please note that in general, Gradle is a preferred way to build the multiplatform projects; JPS has certain limitations (in particular, it wouldn’t build any of Native targets, such as iOS in your project).

A small comment on the build.gradle you’ve shared: for the source sets generated from the presets (jvm, js, etc.), it isn’t necessary to specify explicitly the dependencies of the corresponding *Main and *Test compilations on commonMain and commonTest explicitly, those are specified already by default, as it said at the documentation. So the following line of your configuration for serverMain may be simply removed: dependsOn commonMain

Indeed, there are some already. You may try various samples generated by IDEA via New Project Wizard: open Kotlin section, there are four of them (Multiplatform).

Also, please have a look:
GitHub - orangy/multiplatform-lib: Sample multiplatform library in Kotlin with tests and publishing
GitHub - h0tk3y/better-parse: A nice parser combinator library for Kotlin
GitHub - Kotlin/kotlinx.atomicfu: The idiomatic way to use atomic operations in Kotlin
GitHub - Kotlin/kmm-basic-sample: Example of Kotlin multiplatform project
GitHub - JetBrains/kotlinconf-app: KotlinConf Schedule Application
GitHub - jonnyzzz/kotlin-fractals
GitHub - jonnyzzz/kotlin-mpp-mobile
GitHub - RubyLichtenstein/Kotlin-Multiplatform-Firebase: Kotlin Multiplatform - Android/iOS/Web/Node.Js(FIrebase)

Thanks, offloading build to gradle fixed the build issues. Btw, should jvm server be runnable from IDEA now? Cause I’m having troubles just running hello world server, I get this error: Error: Could not find or load main class rs.mmitic.MainKt

It’s possible that I’m just a dummy, I haven’t coded in Kotlin that much but I’ve tried lots of stuff:

package rs.mmitic
import sample.hello

object Main {
    @JvmStatic
    fun main(args: Array<String>) {
        print(hello())
    }
}

class Main2 {
    companion object {
        @JvmStatic
        fun main(vararg args: String) {}
    }
}

fun main(args: Array<String>) {
    print(hello())
}

There is an issue with main run in IDEA for the multiplatform projects having Android target among others.

As a workaround, a Gradle task for the main run may be specified:

task run(type: JavaExec) {
    main = 'sample.SampleJKt'
    def target = kotlin.targets.jvm
    def compilation = target.compilations.main

    def classes = files(
            compilation.runtimeDependencyFiles,
            compilation.output.allOutputs
    )
    classpath = classes
}

— where main is a path to the class with main() function defined as follows:

  • if main is declared as a top-level function, then packageName + “.” + fileName.capitalized().removeSuffix(“.kt”) + “Kt”
  • if main is declared inside of an object, then packageName + “.” + objectName

For a file without package, for example, ActualB.kt: main = 'ActualBKt'

Thanks a lot.