Kotlin-native: Multiplatform plugin doesn't include all classes in Framework output

Created on 22 Oct 2018  路  10Comments  路  Source: JetBrains/kotlin-native

I have two projects using the kotlin-multiplatform plugin, each with common, Android, and iOS source sets. The app1 project depends on common.

Kotlin version = 1.3.0-rc-146, Kotlin/Native version = 0.9.3.

common (project)
|-- commonMain
|-- androidMain (target: Android library)
|-- iosMain (target: iOS framework)

app1
|-- commonMain
|-- androidMain (target: Android app)
|-- iosMain (target: iOS framework)

The iOS target is like so in both projects:

def buildForDevice = project.findProperty("device")?.toBoolean() ?: false
def iosPreset = (buildForDevice) ? presets.iosArm64 : presets.iosX64

fromPreset(iosPreset, 'ios') {
    compilations.main.outputKinds('FRAMEWORK')
}

Now, when I build the app1 iOS target (linkDebugFrameworkIos), common.framework and app1.framework outputs are generated, and app1.framework appears to include all (most) of common.framework, making common.framework redundant.

My problem is where is say _most_. Many classes and interfaces from common.framework headers are present in the app1.framework headers (with "App1..." prepended to the names). But some of the public interfaces from common.framework are not present in the app1.framework headers.

I can't determine a pattern between those interfaces that are included and those that aren't... It doesn't seem to be related to whether or not those interfaces are referenced inside app1.

Is this a bug? Or am I doing something wrong?

Btw, I would have thought it might have been nice if Kotlin/Native just linked app1.framework against common.framework rather than embedding common.framework inside...?

Most helpful comment

I honestly do not get it. In what term would the 'API dependency approach' be different to the current model we are used? If I declare a module or library to be an 'API' dependency, I expect its public API to be accessible from all consumers (including swift)?
Also as @brettwillis explained: I also have never seen the stdlib being declared as 'API' dependency!
What if I have one module that acts as some kind of glue for many other modules. Would I have to create stubs for every public API of my child modules?!

This really makes modularization painful IMHO =(

All 10 comments

I can't determine a pattern between those interfaces that are included and those that aren't... It doesn't seem to be related to whether or not those interfaces are referenced inside app1.

All common types referenced from app1 public API are included into app1.framework.
(All types referenced from API of these types are included as well, and so on).

See #2030 and #2201.

Btw, I would have thought it might have been nice if Kotlin/Native just linked app1.framework against common.framework rather than embedding common.framework inside...?

This approach would be inconsistent with Kotlin/Native closed-world compilation model, which requires all Kotlin code to be compiled into single native binary.

Only one Kotlin/Native framework could be used in single app, so we couldn't link multiple frameworks.

Ok, it seems like those issues mentioned above fully encapsulate my problem, so I might as well close this issue.

However the questions there remain unanswered: Is there any way to include all public classes from the dependency (common), not just those referenced by the final project (app1)? The suggestion of api dependency seems like a logical idea.

Is there any way to include all public classes from the dependency (common), not just those referenced by the final project (app1)?

Not yet.

The suggestion of api dependency seems like a logical idea.

For example, Kotlin stdlib is technically an api dependency of every Kotlin module. Does this mean that every compiled framework should contain the entire Kotlin stdlib API? I don't think so.
And it is not enough to handle stdlib specially, because there may be a library similar to stdlib in that sense.

However the questions there remain unanswered: Is there any way to include all public classes from the dependency (common), not just those referenced by the final project (app1)?

I write in final project simple public vals with needed types from dependency. For now it works...

For example, Kotlin stdlib is technically an api dependency of every Kotlin module.

Perhaps it is techincally an api dependency under the hood (?), but I've never seen it declared as api in build.gradle. It is always declared as implementation. Could you determine this based on only what is _explicity_ declared as api in build.gradle?

Could you determine this based on only what is explicity declared as api in build.gradle?

This doesn't seem to be ultimately correct solution, because

And it is not enough to handle stdlib specially, because there may be a library similar to stdlib in that sense.

Consider another huge library. With this approach user would have only two options: either totally avoid using types from this library in public API of his module or include the library API as a whole. These options don't seem to cover all possible cases, so I suppose the control should be more fine-grained.

I honestly do not get it. In what term would the 'API dependency approach' be different to the current model we are used? If I declare a module or library to be an 'API' dependency, I expect its public API to be accessible from all consumers (including swift)?
Also as @brettwillis explained: I also have never seen the stdlib being declared as 'API' dependency!
What if I have one module that acts as some kind of glue for many other modules. Would I have to create stubs for every public API of my child modules?!

This really makes modularization painful IMHO =(

The upcoming Kotlin 1.3.20 release will include basic support for including dependencies APIs to frameworks. We will provide more details later.

If I declare a module or library to be an 'API' dependency, I expect its public API to be accessible from all consumers (including swift)?

This would make binaries bigger without explicit intention of a developer.
Moving a dependency from implementation to api does not have any cost on JVM in terms of application size, but with Kotlin/Native this wouldn't be true if 'API dependency approach' was used.

In 1.3.20-eap-52 a support for such an export was added in the kotlin-multiplatform plugin. You can find an example here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nvlizlo picture nvlizlo  路  4Comments

alastaircoote picture alastaircoote  路  3Comments

Cortlandd picture Cortlandd  路  4Comments

msink picture msink  路  4Comments

benasher44 picture benasher44  路  4Comments