Skip to content

How to use this repository for learning about Kotlin Multiplatform

Dmitry Savvinov edited this page Nov 8, 2022 · 1 revision

The main purpose of this repository is to provide good educational material on how Kotlin Multiplatform is compiled for tech-savvy and curious people.

The recommended way of reading this repository:

  • Read the current page to understand the context and limitations
  • Read the "Kotlin Compilation Model" section in order. It will reference the relevant code pieces for each part.

Of course, you can jump straight to the code, if you prefer it this way :)

Use-case description

There are two Kotlin Multiplatform libraries in sources/-folder: DirectDependency and TransitiveDependency, as well as Android-only app and iOS-app.

Both apps depend on DirectDependency directly. DirectDependency, in turn, depends on TranstiviveDependency, so apps see TransitiveDependency ... transitively (you probably see the pattern in naming at this point :) ) as well.

Both libraries have the same set of targets: jvm (read why not android() in "Limitations" section), iosArm64, iosX64 and iosSimulatorArm64. Both libraries have a simple hierarchy of Kotlin Source Sets, with iosMain/iosTest` source set shared among all iOS-related targets.

Limitations, restrictions

Supporting the whole Kotlin Multiplatform build in its full glory is more or less equivalent to re-implementing not only the Kotlin Gradle plugin, but a good chunk of the Gradle itself. Here we will explicitly list all limitations/restrictions applied:

  • No consuming dependencies from Maven-like repositories. It requires fiddling with Maven format of publication, network requests, as well as some intrinsic knowledge of Kotlin Multiplatform. However, as long as the dependencies are somehow downloaded to the local machine, it should be fairly trivial to pass them to the Kotlin compiler, using the code in this repo as an example

  • No publishing libraries to Maven-like repositories. Additionally to everything mentioned above, currently, clients of Kotlin Multiplatform make heavy use of Gradle Variant-aware resolution. If one wants to publish a Kotlin Multiplatform library transparently to those clients, they will have to be able to publish Gradle Module Metadata

  • Integration with Xcode has not the best UX, see the respective section for details on that

  • JVM target is used instead of Android. This precludes using Android SDK in shared sources (you can still use it perfectly fine in Android-only App). Our experience shows that this is a realistic enough case, and figuring out the intricacies of compiling against Android SDK is out of the scope of this project.

  • There are no test sources yet

  • Compilation of intermediate iosMain source set to Kotlin Metadata is disabled. Read details in "Kotlin Metadata" section.

Repository structure

sources folder contains several connected simple projects (two Kotlin Multiplatform libraries, and Android/iOS apps) that will be used to demonstrate how Kotlin Multiplatform is compiled, packaged, distributed and consumed in Kotlin Multiplatform-agnostic clients.

buildInfra contains stuff that is usually of a build system concern.

  • buildInfra/tools contains various "external" tools like compilers or libraries, necessary for compiling projects in subject.
  • buildInfra/logic contains a logic describing the whole process of compilation and distribution. This logic is written in form of a Kotlin program - reasons for that are purely subjective, and it should be trivial to convert this logic to any scripting language (like Python), plain shell (like bash), or even into a plugin for a different build system.
  • buildInfra/output will contain the binary output of running Kotlin Compiler and related tools

Source code structure of the logic-project

There are a few simple abstractions there:

  • tools-package contains programmatic wrappers around various tools used during Kotlin Compilation (mostly kotlinc and its platform-specific wrappers like kotlinc-jvm)

  • hardcode package contains a lot of hardcoded information, which is necessary for compiling Kotlin sources:

    • HardcodedDependencies is self-explanatory, and there are a lot of comments there, explaining which data is usually provided by users, and which is the convention/default of Kotlin Gradle Plugin.
    • HardcodedPaths is a bit more interesting. Basically, one instance describes the layout of sources and expected output paths of one "Kotlin Library". There are two instances in play in this example: one for DirectDependency and one for TransitiveDependency. The code intentionally uses very low-level word "Paths" rather than "Library" or "Module", read the KDoc in code for some details.
  • Finally, tasks provides multiple objects that perform one step of building Kotlin Multiplatform code. Basically, each task accepts HardcodedPaths it will operate onto, forms the CLI argument list (with the help of HardcodedDependencies) and passes that list to one of the tools from tools-package