Kotlin 1.3-M2: new multiplatform projects model

@h0tk3y I saw you updated to 1.3.0-dev-496 in your samples

none of your examples use coroutines and trying to use the latest coroutines (0.26.0-eap13) results in “Class ‘kotlinx.coroutines.Job’ is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler”

Does anyone know how solve this?

(also coroutines lib doesn’t work with Gradle over 4.7 but I don’t think that causes an issue)

I see two questions there :slight_smile:
First,

Does this now also mean that I can (easily, on class by class basis) have some classes have different code for android, js and jvm and some share code between android and jvm (but not js)

— yes, you can. See split-actuals example to find out how it may be done.

Second,

Is that also possible if the android variant is in reality a java library that is just designed for use on Android

— do you mean a case when the Android IDE module is actually just a module with pure Java source code without any multiplatform (actual) declarations, but with a dependency on some platform-agnostic module which is, in turn, has some actual’s and is shared between several modules? :slight_smile:
Or maybe can give a real example of the structure you meant?

Actually what I have (currently) is two multiplatform-jvm modules for the same conmon module one that happens to be called Android and one that happens to be called jvm. They have some different implementations and dependencies but are both regular Jar files. (btw. this is due to Android not having all classes of the jdk as well as replacement libraries (which can be provided as compileOnly)

@nbransby Looks like the latest EAP version of kotlinx.coroutines is built with an earlier pre-release (1.3-M2) version of the compiler, and the 1.3.0-dev-* builds reject them. The easiest solution is to use a stable version of kotlinx.coroutines, though you can force the compiler to skip this check as well by passing -Xskip-metadata-version-check into the compiler args.

Also, the situation with kotlinx.coroutines and Gradle 4.8+ incompatibility is caused by the fact that the library’s Native parts have been published with Gradle metadata enabled, which is an experimental Gradle feature that is deliberately non-backward-compatible, according to the spec.

The same may happen with libraries built with kotlin-multiplatform and published with experimental Gradle metadata enabled: they may fail to get resolved with newer Gradle versions. Unfortunately, Gradle metadata is exactly what’s required for publishing a multiplatform library as a single Maven module, so, if a library is published with no metadata, one has to specify dependencies on its platform-specific parts (e.g. my-lib-jvm and my-lib-js) rather than a single dependency (my-lib) in case a library has Gradle metadata.

Overall, this publishing mode should be considered experimental until the Gradle metadata feature becomes stable.

Thanks for that, now im having a new problem which I suspect is due to using a dev version of the plugin

e: java.lang.IllegalStateException: Backend Internal error: Exception during code generation
Cause: Back-end (JVM) Internal error: Error type encountered: [ERROR : For Result] (ErrorType).
Cause: Error type encountered: [ERROR : For Result] (ErrorType).
The root cause was thrown at: KotlinTypeMapper.java:113

I can add on you track if you it would be useful

I’m trying to use kotlin-android-extensions in a MPP. There is no error but looks like it’s not available as I’m trying to use it but it can’t resolve kotlinx.android.synthetic. Do you know what could be happening?

I see, thank you. Well, as far as I understood the configuration, if you specify the source set (see the definition here) targeting to Android, it requires one of the Android Gradle plugins to be applied, accompanied with all the corresponding Android-specific configurations. Also, please note that multiplatform projects, which have Android target among others, have to have a bit specific structure, with at least two build.gradle files and all the source sets placed inside of app module. To get the point, please take a look at the following examples: simple-app-with-android, android-lib-and-app-with-mpp and common-lib-and-android-app.

Can you share an example project, please? Or at least its configuration. You may also do it privately if needed

Do you mean that the project builds successfully, but the code in the editor is highlighted in red? Is it highlighted so even after Gradle build task being successfully executed or only after the project import and/or Gradle clean?
Does this issue look like what you experience?

1 Like

Sorry if this has been asked or reported before, but is it going to be possible to publish with the new plugin to maven and in turn use those artifacts from the old plugin? (In particular using from non-multiplatform projects)

For the most part this seems to work as-is, but project dependencies are not declared properly in the pom files. Specifically if I publish lib1, lib1-jvm, lib2 and lib2-jvm with lib2 depending on lib1 then lib2-jvm only depends on lib1 instead of lib1-jvm1 in the pom file. (The same is true for *-metadata and *-js artifacts as well)

In case this is just a bug I can put this on youtrack.

Yes its here: https://github.com/TeamHubApp/pusher-chatkit-multiplatform

looks like it’s the same issue. Thanks for the link!

1 Like

Thank you!

I found out the reason. Indeed, there was something about pre-release versions. To fix it, you should do the following:

  1. Update Kotlin Gradle plugin version to 1.3.0-rc-26. Note that it’s not the Kotlin 1.3-RC itself and should be considered as yet another dev version, as well as the one you’ve set in your project. It’s just more up-to-date now.
  2. Update coroutines version to 0.26.0-rc13, so it’s compatible with the plugin version.
  3. Perform the migration to the new language version: you can either agree with the migration in the dialog which appears when you’ve set the newer plugin version or do it manually, applying a quick fix on all the kotlinx.coroutines import. The thing is, as you may know, coroutines become stable in Kotlin 1.3. Therefore, those are moved to another package and that’s why you need the migration.

When all is done, build passes successfully. Please let me know if you experience any problems with that.

@GitOut

The dependencies you observe in the POM are fit for the new publishing mechanism where we also publish Gradle metadata, which serves as a hint for consumers who have a lib1 dependency – it then gets resolved to an appropriate variant for each of the consumer’s targets (e.g. the lib2 JVM target gets the lib1-jvm artifacts, even though it has just the lib1 dependency).

However, when a library is published with no metadata, or the consumer did not enableFeaturePreview("GRADLE_METADATA"), this kind of dependency becomes invalid. We are going to fix that in upcoming updates.

2 Likes

That’s great thanks, its all working now, I have MPP libraries that now successfully depend on other MPP libraries.

Only issue I have now is consuming an MPP library in an ordinary Kotlin JVM project. Well in fact it’s an intellij plugin project and when I depend on my MPP lib via

compile 'com.example:mpp-lib:1.0.0'

I get the error:

Cannot choose between the following configurations of com.example:mpp-lib:1.0.0:
  - js-runtime
  - jvm-runtime
All of them match the consumer attributes:
  - Configuration 'js-runtime':
      - Found artifactType 'jar' but wasn't required.
      - Found org.gradle.status 'release' but wasn't required.
      - Found org.gradle.usage 'java-runtime-jars' but wasn't required.
      - Found org.jetbrains.kotlin.platform.type 'js' but wasn't required.
  - Configuration 'jvm-runtime':
      - Found artifactType 'jar' but wasn't required.
      - Found org.gradle.status 'release' but wasn't required.
      - Found org.gradle.usage 'java-runtime-jars' but wasn't required.

And if I change to

implementation 'com.example:mpp-lib:1.0.0'

It compiles but the intellij gradle plugin does not include the lib in the plugin jar dist and so I get:

java.lang.NoClassDefFoundError: com/example/Context

at runtime.

Any ideas?

If you have an ordinary JVM project, then you need only JVM part of the MPP library. Therefore, on the assumption of library names, given in the error message, I suppose you need to clarify the dependency a bit: compile 'com.example:mpp-lib-jvm-runtime:1.0.0'

In general, if you’ll navigate to the local repo (I suppose you’ve published the library there), and open com.example directory, you’ll see several flavors of the library: the multiplatform one and the platform ones, by one for each target you’ve had for the library.

Hey thanks, that works but the library in question itself depends on another mpp lib and now I get the same error but for that lib. How can I tell it to use the jvm version of a non-direct dependency?

I’ll take a closer look at this a bit later, but a quick guess for now: do you have Gradle metadata enabled in the end-point (JVM?) project? Library consumers have to have it enabled as well as library publishers. To enable it, add the following line into your settings.gradle file: enableFeaturePreview('GRADLE_METADATA)`

Yes I have that in every settings.gradle

Hey, I failed to reproduce the problem. Let’s compare our configurations to find out the difference.
So I use Gradle 4.7 and enableFeaturePreview('GRADLE_METADATA') everywhere, my Kotlin Gradle plugin is 1.3.0-rc-57 with EAP repo maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } specifid.

I investigate the following construction: app > lib1 > lib2, where lib1 and lib2 are MPP libraries, built with the new multiplatform plugin, and app is a simple JVM project.

I’ve tried the following dependencies variants and all the possible combinations of them:

  • lib1 depends on JVM part of lib2
  • lib1 depends on the whole lib2 (via the dependencies of the common module)
  • app depends on lib1, either the whole library or its jvm part.

Do my cases include your configuration or did I miss something? I’m confused because with such a configuration everything works as expected, all builds pass successfully.