update(docs): replace doc content with links to kotlinlang.ord pages

This commit is contained in:
Pavel Semyonov
2021-02-15 15:49:43 +07:00
committed by Vasily Levchenko
parent 41a1e462f0
commit c2b1c7df53
11 changed files with 11 additions and 2984 deletions
+1 -749
View File
@@ -1,751 +1,3 @@
# CocoaPods integration
Kotlin/Native provides integration with the [CocoaPods dependency manager](https://cocoapods.org/).
You can add dependencies on Pod libraries as well as use a multiplatform project with
native targets as a CocoaPods dependency (Kotlin Pod).
You can manage Pod dependencies directly in IntelliJ IDEA and enjoy all the additional features such as code highlighting
and completion. You can build the whole Kotlin project with Gradle and not ever have to switch to Xcode.
Use Xcode only when you need to write Swift/Objective-C code or run your application on a simulator or device.
To work correctly with Xcode, you should [update your Podfile](#update-podfile-for-xcode).
Depending on your project and purposes, you can add dependencies between [a Kotlin project and a Pod library](#add-dependencies-on-pod-libraries) as well as [a Kotlin Pod and an Xcode project](#use-a-kotlin-gradle-project-as-a-cocoapods-dependency).
>You can also add dependencies between a Kotlin Pod and multiple Xcode projects. However, in this case you need to add a
>dependency by calling `pod install` manually for each Xcode project. In other cases, it's done automatically.
{:.note}
## Install the CocoaPods dependency manager and plugin
1. Install the [CocoaPods dependency manager](https://cocoapods.org/).
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
```ruby
$ sudo gem install cocoapods
```
</div>
2. Install the [`cocoapods-generate`](https://github.com/square/cocoapods-generate) plugin.
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
```ruby
$ sudo gem install cocoapods-generate
```
</div>
3. In `build.gradle.kts` (or `build.gradle`) of your IDEA project, apply the CocoaPods plugin as well as the Kotlin
Multiplatform plugin.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
plugins {
kotlin("multiplatform") version "{{ site.data.releases.latest.version }}"
kotlin("native.cocoapods") version "{{ site.data.releases.latest.version }}"
}
```
</div>
4. Configure `summary`, `homepage`, and `frameworkName`of the `Podspec` file in the `cocoapods` block.
`version` is a version of the Gradle project.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
plugins {
kotlin("multiplatform") version "{{ site.data.releases.latest.version }}"
kotlin("native.cocoapods") version "{{ site.data.releases.latest.version }}"
}
// CocoaPods requires the podspec to have a version.
version = "1.0"
kotlin {
cocoapods {
// Configure fields required by CocoaPods.
summary = "Some description for a Kotlin/Native module"
homepage = "Link to a Kotlin/Native module homepage"
// You can change the name of the produced framework.
// By default, it is the name of the Gradle project.
frameworkName = "my_framework"
}
}
```
</div>
5. Re-import the project.
6. Generate the [Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) to avoid compatibility issues during an Xcode build.
When applied, the CocoaPods plugin does the following:
* Adds both `debug` and `release` frameworks as output binaries for all macOS, iOS, tvOS, and watchOS targets.
* Creates a `podspec` task which generates a [Podspec](https://guides.cocoapods.org/syntax/podspec.html)
file for the project.
The `Podspec` file includes a path to an output framework and script phases that automate building this framework during
the build process of an Xcode project.
## Add dependencies on Pod libraries
To add dependencies between a Kotlin project and a Pod library, you should [complete the initial configuration](#install-the-cocoapods-dependency-manager-and-plugin).
This allows you to add dependencies on the following types of Pod libraries:
* [A Pod library from the CocoaPods repository](#add-a-dependency-on-a-pod-library-from-the-cocoapods-repository)
* [A Pod library stored locally](#add-a-dependency-on-a-pod-library-stored-locally)
* [A Pod library from a Git repository](#add-a-dependency-on-a-pod-library-from-the-git-repository)
* [A Pod library from an archive](#add-a-dependency-on-a-pod-library-from-an-archive)
* [A Pod library from a custom Podspec repository](#add-a-dependency-on-a-pod-library-from-a-custom-podspec-repository)
* [A Pod library with custom cinterop options](#add-a-dependency-on-a-pod-library-with-custom-cinterop-options)
* [A static Pod library](#add-a-dependency-on-a-static-pod-library)
A Kotlin project requires the `pod()` function call in `build.gradle.kts` (`build.gradle`) for adding a Pod dependency. Each dependency requires its own separate function call.
You can specify the parameters for the dependency in the configuration block of the function.
When you add a new dependency and re-import the project in IntelliJ IDEA, the new dependency will be added automatically.
No additional steps are required.
To use your Kotlin project with Xcode, you should [make changes in your project Podfile](#update-podfile-for-xcode).
### Add a dependency on a Pod library from the CocoaPods repository
You can add dependencies on a Pod library from the CocoaPods repository with `pod()` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the name of a Pod library in the `pod()` function. In the configuration block you can specify the version of the library using the `version` parameter. To use the latest version of the library, you can just omit this parameter all-together.
> You can add dependencies on subspecs.
{:.note}
2. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
ios.deploymentTarget = "13.5"
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
pod("AFNetworking") {
version = "~> 4.0.1"
}
}
}
```
</div>
3. Re-import the project.
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.AFNetworking.*
```
</div>
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
### Add a dependency on a Pod library stored locally
You can add a dependency on a Pod library stored locally with `pod()` to `build.gradle.kts` (`build.gradle`) of your project:
1. Specify the name of a Pod library in the `pod()` function. In the configuration block specify the path to the local Pod library: use the `path()` function in the `source` parameter value.
> You can add local dependencies on subspecs as well.
> The `cocoapods` block can include dependencies to Pods stored locally and Pods from the CocoaPods repository at
> the same time.
{:.note}
2. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("pod_dependency") {
version = "1.0"
source = path(project.file("../pod_dependency/pod_dependency.podspec"))
}
pod("subspec_dependency/Core") {
version = "1.0"
source = path(project.file("../subspec_dependency/subspec_dependency.podspec"))
}
pod("AFNetworking") {
version = "~> 4.0.1"
}
}
}
```
</div>
> You can also specify the version of the library using `version` parameter in the configuration block.
> To use the latest version of the library, omit the parameter.
{:.note}
3. Re-import the project.
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.pod_dependency.*
import cocoapods.subspec_dependency.*
import cocoapods.AFNetworking.*
```
</div>
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
### Add a dependency on a Pod library from the Git repository
You can add dependencies on a Pod library from a custom Git repository with `pod()` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the name of a Pod library in the `pod()` function.
In the configuration block specify the path to the git repository: use the `git()` function in the `source` parameter value.
Additionally, you can specify the following parameters in the block after `git()`:
* `commit` to use a specific commit from the repository
* `tag` to use a specific tag from the repository
* `branch` to use a specific branch from the repository
The `git()` function prioritizes passed parameters in the following order: `commit`, `tag`, `branch`.
If you don't specify a parameter, the Kotlin plugin uses `HEAD` from the `master` branch.
> You can combine `branch`, `commit`, and `tag` parameters to get the specific version of a Pod.
{:.note}
2. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("AFNetworking") {
source = git("https://github.com/AFNetworking/AFNetworking") {
tag = "4.0.0"
}
}
pod("JSONModel") {
source = git("https://github.com/jsonmodel/jsonmodel.git") {
branch = "key-mapper-class"
}
}
pod("CocoaLumberjack") {
source = git("https://github.com/CocoaLumberjack/CocoaLumberjack.git") {
commit = "3e7f595e3a459c39b917aacf9856cd2a48c4dbf3"
}
}
}
}
```
</div>
3. Re-import the project.
> To work correctly with Xcode, you should specify the path to the Podspec in your Podfile.
> For example:
>
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
>
> ```ruby
> target 'ios-app' do
> # ... other pod depedencies ...
> pod 'JSONModel', :path => '../cocoapods/kotlin-with-cocoapods-sample/kotlin-library/build/cocoapods/externalSources/git/JSONModel'
> end
> ```
>
> </div>
>
{:.note}
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.AFNetworking.*
import cocoapods.JSONModel.*
import cocoapods.CocoaLumberjack.*
```
</div>
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
### Add a dependency on a Pod library from an archive
You can add dependencies on a Pod library from `zip`, `tar`, or `jar` archive with `pod()` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the name of a Pod library in the `pod()` function.
In the configuration block specify the path to the archive: use the `url()` function with an arbitrary HTTP address in the `source` parameter value.
Additionally, you can specify the boolean `flatten` parameter as a second argument for the `url()` function.
This parameter indicates that all the Pod files are located in the root directory of the archive.
2. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("pod_dependency") {
source = url("https://github.com/Kotlin/kotlin-with-cocoapods-sample/raw/cocoapods-zip/cocoapodSourcesZip.zip", flatten = true)
}
}
}
```
</div>
3. Re-import the project.
> To work correctly with Xcode, you should specify the path to the Podspec in your Podfile.
> For example:
>
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
>
> ```ruby
> target 'ios-app' do
> # ... other pod depedencies ...
> pod 'podspecWithFilesExample', :path => '../cocoapods/kotlin-with-cocoapods-sample/pod_dependency'
> end
> ```
>
> </div>
>
{:.note}
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.pod_dependency.*
```
</div>
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
### Add a dependency on a Pod library from a custom Podspec repository
You can add dependencies on a Pod library from a custom Podspec repository with `pod()` and `specRepos` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the HTTP address to the custom Podspec repository using the `url()` inside the `specRepos` block.
2. Specify the name of a Pod library in the `pod()` function.
3. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
specRepos {
url("https://github.com/Kotlin/kotlin-cocoapods-spec.git")
}
pod("example")
}
}
```
</div>
4. Re-import the project.
> To work correctly with Xcode, you should specify the location of specs at the beginning of your Podfile.
> For example:
>
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
>
> ```ruby
> source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
> ```
>
> </div>
>
> You should also specify the path to the Podspec in your Podfile.
> For example:
>
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
>
> ```ruby
> target 'ios-app' do
> # ... other pod depedencies ...
> pod 'podspecWithFilesExample', :path => '../cocoapods/kotlin-with-cocoapods-sample/pod_dependency'
> end
> ```
>
> </div>
>
{:.note}
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.example.*
```
</div>
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
### Add a dependency on a Pod library with custom cinterop options
You can add dependencies on a Pod library with custom cinterop options with `pod()` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the name of a Pod library in the `pod()` function.
In the configuration block specify the cinterop options:
* `extraOpts` to specify the list of options for a Pod library. For example, specific flags: `extraOpts = listOf("-compiler-option")`
* `packageName` to specify the package name. If you specify this, you can import the library using the package name: `import <packageName>`.
2. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
useLibraries()
pod("YandexMapKit") {
packageName = "YandexMK"
}
}
}
```
</div>
3. Re-import the project.
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.YandexMapKit.*
```
</div>
If you use the `packageName` parameter, you can import the library using the package name: `import <packageName>`:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import YandexMK.YMKPoint
import YandexMK.YMKDistance
```
</div>
### Add a dependency on a static Pod library
You can add dependencies on a static Pod library with `pod()` and `useLibraries()` to `build.gradle.kts`
(`build.gradle`) of your project:
1. Specify the name of the library using the `pod()` function.
2. Call the `useLibraries()` function: it enables a special flag for static libraries.
3. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("YandexMapKit") {
version = "~> 3.2"
}
useLibraries()
}
}
```
</div>
4. Re-import the project.
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import cocoapods.YandexMapKit.*
```
</div>
### Update Podfile for Xcode
If you want to import your Kotlin project in an Xcode project, youll need to make some changes to your Podfile for it to work correctly:
* If your project has any Git, HTTP, or custom Podspec repository dependencies, you should also specify the path to the Podspec in the Podfile.
For example, if you add a dependency on `podspecWithFilesExample`, declare the path to the Podspec in the Podfile:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```ruby
target 'ios-app' do
# ... other depedencies ...
pod 'podspecWithFilesExample', :path => 'cocoapods/externalSources/url/podspecWithFilesExample'
end
```
</div>
The `:path` should contain the filepath to the Pod.
* When you add a library from the custom Podspec repository, you should also specify the [location](https://guides.cocoapods.org/syntax/podfile.html#source) of specs at the beginning of your Podfile:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```ruby
source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
target 'kotlin-cocoapods-xcproj' do
# ... other depedencies ...
pod 'example'
end
```
</div>
> Re-import the project after making changes in Podfile.
{:.note}
If you don't make these changes to the Podfile, the `podInstall` task will fail and the CocoaPods plugin will show an error message in the log.
Check out the `withXcproject` branch of the [sample project](https://github.com/Kotlin/kotlin-with-cocoapods-sample), which contains an example of Xcode integration with an existing Xcode project named `kotlin-cocoapods-xcproj`.
## Use a Kotlin Gradle project as a CocoaPods dependency
You can use a Kotlin Multiplatform project with native targets as a CocoaPods dependency (Kotlin Pod). You can include such a dependency
in the Podfile of the Xcode project by its name and path to the project directory containing the generated Podspec.
This dependency will be automatically built (and rebuilt) along with this project.
Such an approach simplifies importing to Xcode by removing a need to write the corresponding Gradle tasks and Xcode build steps manually.
You can add dependencies between:
* [A Kotlin Pod and an Xcode project with one target](#add-a-dependency-between-a-kotlin-pod-and-xcode-project-with-one-target)
* [A Kotlin Pod and an Xcode project with several targets](#add-a-dependency-between-a-kotlin-pod-with-an-xcode-project-with-several-targets)
> To correctly import the dependencies into the Kotlin/Native module, the
`Podfile` must contain either [`use_modular_headers!`](https://guides.cocoapods.org/syntax/podfile.html#use_modular_headers_bang)
or [`use_frameworks!`](https://guides.cocoapods.org/syntax/podfile.html#use_frameworks_bang)
directive.
{:.note}
### Add a dependency between a Kotlin Pod and Xcode project with one target
1. Create an Xcode project with a `Podfile` if you havent done so yet.
2. Add the path to your Xcode project `Podfile` with `podfile = project.file(..)` to `build.gradle.kts` (`build.gradle`)
of your Kotlin project.
This step helps synchronize your Xcode project with Kotlin Pod dependencies by calling `pod install` for your `Podfile`.
3. Specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("AFNetworking") {
version = "~> 4.0.0"
}
podfile = project.file("../ios-app/Podfile")
}
}
```
</div>
4. Add the name and path of the Kotlin Pod you want to include in the Xcode project to `Podfile`.
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
```ruby
use_frameworks!
platform :ios, '13.5'
target 'ios-app' do
pod 'kotlin_library', :path => '../kotlin-library'
end
```
</div>
5. Re-import the project.
### Add a dependency between a Kotlin Pod with an Xcode project with several targets
1. Create an Xcode project with a `Podfile` if you havent done so yet.
2. Add the path to your Xcode project `Podfile` with `podfile = project.file(..)` to `build.gradle.kts` (`build.gradle`) of
your Kotlin project.
This step helps synchronize your Xcode project with Kotlin Pod dependencies by calling `pod install` for your `Podfile`.
3. Add dependencies to the Pod libraries that you want to use in your project with `pod()`.
4. For each target, specify the minimum deployment target version for the Pod library.
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
{:.note}
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
kotlin {
ios()
tvos()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
tvos.deploymentTarget = "13.4"
pod("AFNetworking") {
version = "~> 4.0.0"
}
podfile = project.file("../severalTargetsXcodeProject/Podfile") // specify the path to Podfile
}
}
```
</div>
5. Add the name and path of the Kotlin Pod you want to include in the Xcode project to the `Podfile`.
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
```ruby
target 'iosApp' do
use_frameworks!
platform :ios, '13.5'
# Pods for iosApp
pod 'kotlin_library', :path => '../kotlin-library'
end
target 'TVosApp' do
use_frameworks!
platform :tvos, '13.4'
# Pods for TVosApp
pod 'kotlin_library', :path => '../kotlin-library'
end
```
</div>
6. Re-import the project.
You can find a sample project [here](https://github.com/Kotlin/multitarget-xcode-with-kotlin-cocoapods-sample).
The content of this page is moved to https://kotlinlang.org/docs/native-cocoapods.html
+1 -220
View File
@@ -1,222 +1,3 @@
## Concurrency in Kotlin/Native
Kotlin/Native runtime doesn't encourage a classical thread-oriented concurrency
model with mutually exclusive code blocks and conditional variables, as this model is
known to be error-prone and unreliable. Instead, we suggest a collection of
alternative approaches, allowing you to use hardware concurrency and implement blocking IO.
Those approaches are as follows, and they will be elaborated on in further sections:
* Workers with message passing
* Object subgraph ownership transfer
* Object subgraph freezing
* Object subgraph detachment
* Raw shared memory using C globals
* Atomic primitives and references
* Coroutines for blocking operations (not covered in this document)
### Workers
Instead of threads Kotlin/Native runtime offers the concept of workers: concurrently executed
control flow streams with an associated request queue. Workers are very similar to the actors
in the Actor Model. A worker can exchange Kotlin objects with another worker, so that at any moment
each mutable object is owned by a single worker, but ownership can be transferred.
See section [Object transfer and freezing](#transfer).
Once a worker is started with the `Worker.start` function call, it can be addressed with its own unique integer
worker id. Other workers, or non-worker concurrency primitives, such as OS threads, can send a message
to the worker with the `execute` call.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val future = execute(TransferMode.SAFE, { SomeDataForWorker() }) {
// data returned by the second function argument comes to the
// worker routine as 'input' parameter.
input ->
// Here we create an instance to be returned when someone consumes result future.
WorkerResult(input.stringParam + " result")
}
future.consume {
// Here we see result returned from routine above. Note that future object or
// id could be transferred to another worker, so we don't have to consume future
// in same execution context it was obtained.
result -> println("result is $result")
}
```
</div>
The call to `execute` uses a function passed as its second parameter to produce an object subgraph
(i.e. set of mutually referring objects) which is then passed as a whole to that worker, it is then no longer
available to the thread that initiated the request. This property is checked if the first parameter
is `TransferMode.SAFE` by graph traversal and is just assumed to be true, if it is `TransferMode.UNSAFE`.
The last parameter to `execute` is a special Kotlin lambda, which is not allowed to capture any state,
and is actually invoked in the target worker's context. Once processed, the result is transferred to whatever consumes
it in the future, and it is attached to the object graph of that worker/thread.
If an object is transferred in `UNSAFE` mode and is still accessible from multiple concurrent executors,
program will likely crash unexpectedly, so consider that last resort in optimizing, not a general purpose
mechanism.
For a more complete example please refer to the [workers example](https://github.com/JetBrains/kotlin-native/tree/master/samples/workers)
in the Kotlin/Native repository.
<a name="transfer"></a>
### Object transfer and freezing
An important invariant that Kotlin/Native runtime maintains is that the object is either owned by a single
thread/worker, or it is immutable (_shared XOR mutable_). This ensures that the same data has a single mutator,
and so there is no need for locking to exist. To achieve such an invariant, we use the concept of not externally
referred object subgraphs.
This is a subgraph which has no external references from outside of the subgraph, which could be checked
algorithmically with O(N) complexity (in ARC systems), where N is the number of elements in such a subgraph.
Such subgraphs are usually produced as a result of a lambda expression, for example some builder, and may not
contain objects, referred to externally.
Freezing is a runtime operation making a given object subgraph immutable, by modifying the object header
so that future mutation attempts throw an `InvalidMutabilityException`. It is deep, so
if an object has a pointer to other objects - transitive closure of such objects will be frozen.
Freezing is a one way transformation, frozen objects cannot be unfrozen. Frozen objects have a nice
property that due to their immutability, they can be freely shared between multiple workers/threads
without breaking the "mutable XOR shared" invariant.
If an object is frozen it can be checked with an extension property `isFrozen`, and if it is, object sharing
is allowed. Currently, Kotlin/Native runtime only freezes the enum objects after creation, although additional
autofreezing of certain provably immutable objects could be implemented in the future.
<a name="detach"></a>
### Object subgraph detachment
An object subgraph without external references can be disconnected using `DetachedObjectGraph<T>` to
a `COpaquePointer` value, which could be stored in `void*` data, so the disconnected object subgraphs
can be stored in a C data structure, and later attached back with `DetachedObjectGraph<T>.attach()` in an arbitrary thread
or a worker. Combining it with [raw memory sharing](#shared) it allows side channel object transfer between
concurrent threads, if the worker mechanisms are insufficient for a particular task. Note, that object detachment
may require explicit leaving function holding object references and then performing cyclic garbage collection.
For example, code like:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val graph = DetachedObjectGraph {
val map = mutableMapOf<String, String>()
for (entry in map.entries) {
// ...
}
map
}
```
</div>
will not work as expected and will throw runtime exception, as there are uncollected cycles in the detached graph, while:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val graph = DetachedObjectGraph {
{
val map = mutableMapOf<String, String>()
for (entry in map.entries) {
// ...
}
map
}().also {
kotlin.native.internal.GC.collect()
}
}
```
</div>
will work properly, as holding references will be released, and then cyclic garbage affecting reference counter is
collected.
<a name="shared"></a>
### Raw shared memory
Considering the strong ties between Kotlin/Native and C via interoperability, in conjunction with the other mechanisms
mentioned above it is possible to build popular data structures, like concurrent hashmap or shared cache with
Kotlin/Native. It is possible to rely upon shared C data, and store in it references to detached object subgraphs.
Consider the following .def file:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
package = global
---
typedef struct {
int version;
void* kotlinObject;
} SharedData;
SharedData sharedData;
```
</div>
After running the cinterop tool it can share Kotlin data in a versionized global structure,
and interact with it from Kotlin transparently via autogenerated Kotlin like this:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
class SharedData(rawPtr: NativePtr) : CStructVar(rawPtr) {
var version: Int
var kotlinObject: COpaquePointer?
}
```
</div>
So in combination with the top level variable declared above, it can allow looking at the same memory from different
threads and building traditional concurrent structures with platform-specific synchronization primitives.
<a name="top_level"></a>
### Global variables and singletons
Frequently, global variables are a source of unintended concurrency issues, so _Kotlin/Native_ implements
the following mechanisms to prevent the unintended sharing of state via global objects:
* global variables, unless specially marked, can be only accessed from the main thread (that is, the thread
_Kotlin/Native_ runtime was first initialized), if other thread access such a global, `IncorrectDereferenceException` is thrown
* for global variables marked with the `@kotlin.native.ThreadLocal` annotation each threads keeps thread-local copy,
so changes are not visible between threads
* for global variables marked with the `@kotlin.native.SharedImmutable` annotation value is shared, but frozen
before publishing, so each threads sees the same value
* singleton objects unless marked with `@kotlin.native.ThreadLocal` are frozen and shared, lazy values allowed,
unless cyclic frozen structures were attempted to be created
* enums are always frozen
Combined, these mechanisms allow natural race-free programming with code reuse across platforms in MPP projects.
<a name="atomic_references"></a>
### Atomic primitives and references
Kotlin/Native standard library provides primitives for safe working with concurrently mutable data, namely
`AtomicInt`, `AtomicLong`, `AtomicNativePtr`, `AtomicReference` and `FreezableAtomicReference` in the package
`kotlin.native.concurrent`.
Atomic primitives allows concurrency-safe update operations, such as increment, decrement and compare-and-swap,
along with value setters and getters. Atomic primitives are considered always frozen by the runtime, and
while their fields can be updated with the regular `field.value += 1`, it is not concurrency safe.
Value must be be changed using dedicated operations, so it is possible to perform concurrent-safe
global counters and similar data structures.
Some algorithms require shared mutable references across the multiple workers, for example global mutable
configuration could be implemented as an immutable instance of properties list atomically replaced with the
new version on configuration update as the whole in a single transaction. This way no inconsistent configuration
could be seen, and at the same time configuration could be updated as needed.
To achieve such functionality Kotlin/Native runtime provides two related classes:
`kotlin.native.concurrent.AtomicReference` and `kotlin.native.concurrent.FreezableAtomicReference`.
Atomic reference holds reference to a frozen or immutable object, and its value could be updated by set
or compare-and-swap operation. Thus, dedicated set of objects could be used to create mutable shared object graphs
(of immutable objects). Cycles in the shared memory could be created using atomic references.
Kotlin/Native runtime doesn't support garbage collecting cyclic data when reference cycle goes through
`AtomicReference` or frozen `FreezableAtomicReference`. So to avoid memory leaks atomic references
that are potentially parts of shared cyclic data should be zeroed out once no longer needed.
If atomic reference value is attempted to be set to non-frozen value runtime exception is thrown.
Freezable atomic reference is similar to the regular atomic reference, but until frozen behaves like regular box
for a reference. After freezing it behaves like an atomic reference, and can only hold a reference to a frozen object.
The content of this page is moved to https://kotlinlang.org/docs/native-concurrency.html
+1 -261
View File
@@ -1,263 +1,3 @@
## Debugging
Currently the Kotlin/Native compiler produces debug info compatible with the DWARF 2 specification, so modern debugger tools can
perform the following operations:
- breakpoints
- stepping
- inspection of type information
- variable inspection
### Producing binaries with debug info with Kotlin/Native compiler
To produce binaries with the Kotlin/Native compiler it's sufficient to use the ``-g`` option on the command line.<br/>
_Example:_
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
0:b-debugger-fixes:minamoto@unit-703(0)# cat - > hello.kt
fun main(args: Array<String>) {
println("Hello world")
println("I need your clothes, your boots and your motocycle")
}
0:b-debugger-fixes:minamoto@unit-703(0)# dist/bin/konanc -g hello.kt -o terminator
KtFile: hello.kt
0:b-debugger-fixes:minamoto@unit-703(0)# lldb terminator.kexe
(lldb) target create "terminator.kexe"
Current executable set to 'terminator.kexe' (x86_64).
(lldb) b kfun:main(kotlin.Array<kotlin.String>)
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
(lldb) r
Process 28473 launched: '/Users/minamoto/ws/.git-trees/debugger-fixes/terminator.kexe' (x86_64)
Process 28473 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x00000001000012e4 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:2
1 fun main(args: Array<String>) {
-> 2 println("Hello world")
3 println("I need your clothes, your boots and your motocycle")
4 }
(lldb) n
Hello world
Process 28473 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x00000001000012f0 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:3
1 fun main(args: Array<String>) {
2 println("Hello world")
-> 3 println("I need your clothes, your boots and your motocycle")
4 }
(lldb)
```
</div>
### Breakpoints
Modern debuggers provide several ways to set a breakpoint, see below for a tool-by-tool breakdown:
#### lldb
- by name
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(lldb) b -n kfun:main(kotlin.Array<kotlin.String>)
Breakpoint 4: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
```
</div>
_``-n`` is optional, this flag is applied by default_
- by location (filename, line number)
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(lldb) b -f hello.kt -l 1
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
```
</div>
- by address
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(lldb) b -a 0x00000001000012e4
Breakpoint 2: address = 0x00000001000012e4
```
</div>
- by regex, you might find it useful for debugging generated artifacts, like lambda etc. (where used ``#`` symbol in name).
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
3: regex = 'main\(', locations = 1
3.1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = terminator.kexe[0x00000001000012e4], unresolved, hit count = 0
```
</div>
#### gdb
- by regex
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(gdb) rbreak main(
Breakpoint 1 at 0x1000109b4
struct ktype:kotlin.Unit &kfun:main(kotlin.Array<kotlin.String>);
```
</div>
- by name __unusable__, because ``:`` is a separator for the breakpoint by location
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(gdb) b kfun:main(kotlin.Array<kotlin.String>)
No source file named kfun.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (kfun:main(kotlin.Array<kotlin.String>)) pending
```
</div>
- by location
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(gdb) b hello.kt:1
Breakpoint 2 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 1.
```
</div>
- by address
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
(gdb) b *0x100001704
Note: breakpoint 2 also set at pc 0x100001704.
Breakpoint 3 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 2.
```
</div>
### Stepping
Stepping functions works mostly the same way as for C/C++ programs
### Variable inspection
Variable inspections for var variables works out of the box for primitive types.
For non-primitive types there are custom pretty printers for lldb in
`konan_lldb.py`:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
λ cat main.kt | nl
1 fun main(args: Array<String>) {
2 var x = 1
3 var y = 2
4 var p = Point(x, y)
5 println("p = $p")
6 }
7 data class Point(val x: Int, val y: Int)
λ lldb ./program.kexe -o 'b main.kt:5' -o
(lldb) target create "./program.kexe"
Current executable set to './program.kexe' (x86_64).
(lldb) b main.kt:5
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 289 at main.kt:5, address = 0x000000000040af11
(lldb) r
Process 4985 stopped
* thread #1, name = 'program.kexe', stop reason = breakpoint 1.1
frame #0: program.kexe`kfun:main(kotlin.Array<kotlin.String>) at main.kt:5
2 var x = 1
3 var y = 2
4 var p = Point(x, y)
-> 5 println("p = $p")
6 }
7
8 data class Point(val x: Int, val y: Int)
Process 4985 launched: './program.kexe' (x86_64)
(lldb) fr var
(int) x = 1
(int) y = 2
(ObjHeader *) p = 0x00000000007643d8
(lldb) command script import dist/tools/konan_lldb.py
(lldb) fr var
(int) x = 1
(int) y = 2
(ObjHeader *) p = [x: ..., y: ...]
(lldb) p p
(ObjHeader *) $2 = [x: ..., y: ...]
(lldb) script lldb.frame.FindVariable("p").GetChildMemberWithName("x").Dereference().GetValue()
'1'
(lldb)
```
</div>
Getting representation of the object variable (var) could also be done using the
built-in runtime function `Konan_DebugPrint` (this approach also works for gdb,
using a module of command syntax):
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
0:b-debugger-fixes:minamoto@unit-703(0)# cat ../debugger-plugin/1.kt | nl -p
1 fun foo(a:String, b:Int) = a + b
2 fun one() = 1
3 fun main(arg:Array<String>) {
4 var a_variable = foo("(a_variable) one is ", 1)
5 var b_variable = foo("(b_variable) two is ", 2)
6 var c_variable = foo("(c_variable) two is ", 3)
7 var d_variable = foo("(d_variable) two is ", 4)
8 println(a_variable)
9 println(b_variable)
10 println(c_variable)
11 println(d_variable)
12 }
0:b-debugger-fixes:minamoto@unit-703(0)# lldb ./program.kexe -o 'b -f 1.kt -l 9' -o r
(lldb) target create "./program.kexe"
Current executable set to './program.kexe' (x86_64).
(lldb) b -f 1.kt -l 9
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 463 at 1.kt:9, address = 0x0000000100000dbf
(lldb) r
(a_variable) one is 1
Process 80496 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000dbf program.kexe`kfun:main(kotlin.Array<kotlin.String>) at 1.kt:9
6 var c_variable = foo("(c_variable) two is ", 3)
7 var d_variable = foo("(d_variable) two is ", 4)
8 println(a_variable)
-> 9 println(b_variable)
10 println(c_variable)
11 println(d_variable)
12 }
Process 80496 launched: './program.kexe' (x86_64)
(lldb) expression -- (int32_t)Konan_DebugPrint(a_variable)
(a_variable) one is 1(int32_t) $0 = 0
(lldb)
```
</div>
### Known issues
- performance of Python bindings.
_Note:_ Supporting the DWARF 2 specification means that the debugger tool recognizes Kotlin as C89, because before the DWARF 5 specification, there is no identifier for the Kotlin language type in specification.
The content of this page is moved to https://kotlinlang.org/docs/native-debugging.html
+1 -206
View File
@@ -1,206 +1 @@
### Q: How do I run my program?
A: Define a top level function `fun main(args: Array<String>)` or just `fun main()` if you are not interested
in passed arguments, please ensure it's not in a package.
Also compiler switch `-entry` could be used to make any function taking `Array<String>` or no arguments
and return `Unit` as an entry point.
### Q: What is Kotlin/Native memory management model?
A: Kotlin/Native provides an automated memory management scheme, similar to what Java or Swift provides.
The current implementation includes an automated reference counter with a cycle collector to collect cyclical
garbage.
### Q: How do I create a shared library?
A: Use the `-produce dynamic` compiler switch, or `binaries.sharedLib()` in Gradle, i.e.
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
```kotlin
kotlin {
iosArm64("mylib") {
binaries.sharedLib()
}
}
```
</div>
It will produce a platform-specific shared object (.so on Linux, .dylib on macOS, and .dll on Windows targets) and a
C language header, allowing the use of all public APIs available in your Kotlin/Native program from C/C++ code.
See `samples/python_extension` for an example of using such a shared object to provide a bridge between Python and
Kotlin/Native.
### Q: How do I create a static library or an object file?
A: Use the `-produce static` compiler switch, or `binaries.staticLib()` in Gradle, i.e.
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
```kotlin
kotlin {
iosArm64("mylib") {
binaries.staticLib()
}
}
```
</div>
It will produce a platform-specific static object (.a library format) and a C language header, allowing you to
use all the public APIs available in your Kotlin/Native program from C/C++ code.
### Q: How do I run Kotlin/Native behind a corporate proxy?
A: As Kotlin/Native needs to download a platform specific toolchain, you need to specify
`-Dhttp.proxyHost=xxx -Dhttp.proxyPort=xxx` as the compiler's or `gradlew` arguments,
or set it via the `JAVA_OPTS` environment variable.
### Q: How do I specify a custom Objective-C prefix/name for my Kotlin framework?
A: Use the `-module-name` compiler option or matching Gradle DSL statement, i.e.
<div class="multi-language-sample" data-lang="kotlin">
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
```kotlin
kotlin {
iosArm64("myapp") {
binaries.framework {
freeCompilerArgs += listOf("-module-name", "TheName")
}
}
}
```
</div>
</div>
<div class="multi-language-sample" data-lang="groovy">
<div class="sample" markdown="1" theme="idea" mode="groovy">
```groovy
kotlin {
iosArm64("myapp") {
binaries.framework {
freeCompilerArgs += ["-module-name", "TheName"]
}
}
}
```
</div>
</div>
### Q: How do I rename the iOS framework? (default name is _\<project name\>_.framework)
A: Use the `baseName` option. This will also set the module name.
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
```kotlin
kotlin {
iosArm64("myapp") {
binaries {
framework {
baseName = "TheName"
}
}
}
}
```
</div>
### Q: How do I enable bitcode for my Kotlin framework?
A: By default gradle plugin adds it on iOS target.
* For debug build it embeds placeholder LLVM IR data as a marker.
* For release build it embeds bitcode as data.
Or commandline arguments: `-Xembed-bitcode` (for release) and `-Xembed-bitcode-marker` (debug)
Setting this in a Gradle DSL:
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
```kotlin
kotlin {
iosArm64("myapp") {
binaries {
framework {
// Use "marker" to embed the bitcode marker (for debug builds).
// Use "disable" to disable embedding.
embedBitcode("bitcode") // for release binaries.
}
}
}
}
```
</div>
These options have nearly the same effect as clang's `-fembed-bitcode`/`-fembed-bitcode-marker`
and swiftc's `-embed-bitcode`/`-embed-bitcode-marker`.
### Q: Why do I see `InvalidMutabilityException`?
A: It likely happens, because you are trying to mutate a frozen object. An object can transfer to the
frozen state either explicitly, as objects reachable from objects on which the `kotlin.native.concurrent.freeze` is called,
or implicitly (i.e. reachable from `enum` or global singleton object - see the next question).
### Q: How do I make a singleton object mutable?
A: Currently, singleton objects are immutable (i.e. frozen after creation), and it's generally considered
good practise to have the global state immutable. If for some reason you need a mutable state inside such an
object, use the `@konan.ThreadLocal` annotation on the object. Also the `kotlin.native.concurrent.AtomicReference` class could be
used to store different pointers to frozen objects in a frozen object and automatically update them.
### Q: How can I compile my project against the Kotlin/Native master?
A: One of the following should be done:
<details>
<summary>For the CLI, you can compile using gradle as stated in the README (and if you get errors, you can try to do a <code>./gradlew clean</code>):</summary>
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
./gradlew dependencies:update
./gradlew dist distPlatformLibs
```
</div>
You can then set the `KONAN_HOME` env variable to the generated `dist` folder in the git repository.
</details>
<details>
<summary>For Gradle, you can use <a href="https://docs.gradle.org/current/userguide/composite_builds.html">Gradle composite builds</a> like this:</summary>
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
# Set with the path of your kotlin-native clone
export KONAN_REPO=$PWD/../kotlin-native
# Run this once since it is costly, you can remove the `clean` task if not big changes were made from the last time you did this
pushd $KONAN_REPO && git pull && ./gradlew clean dependencies:update dist distPlatformLibs && popd
# In your project, you set have to the org.jetbrains.kotlin.native.home property, and include as composite the shared and gradle-plugin builds
./gradlew check -Porg.jetbrains.kotlin.native.home=$KONAN_REPO/dist --include-build $KONAN_REPO/shared --include-build $KONAN_REPO/tools/kotlin-native-gradle-plugin
```
</div>
</details>
The content of this page is moved to https://kotlinlang.org/docs/native-faq.html
+1 -1
View File
@@ -3,7 +3,7 @@
Since 1.3.40, a separate Gradle plugin for Kotlin/Native is deprecated in favor of the `kotlin-multiplatform` plugin.
This plugin provides an IDE support along with support of the new multiplatform project model introduced in Kotlin 1.3.0.
Below you can find a short list of differences between `kotlin-platform-native` and `kotlin-muliplatform` plugins.
For more information see the `kotlin-muliplatform` [documentation page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html).
For more information see the `kotlin-muliplatform` [documentation page](https://kotlinlang.org/docs/mpp-discover-project.html).
For `kotlin-platform-native` reference see the [corresponding section](#kotlin-platform-native-reference).
### Applying the multiplatform plugin
+1 -29
View File
@@ -1,31 +1,3 @@
# Immutability in Kotlin/Native
Kotlin/Native implements strict mutability checks, ensuring
the important invariant that the object is either immutable or
accessible from the single thread at that moment in time (`mutable XOR global`).
Immutability is a runtime property in Kotlin/Native, and can be applied
to an arbitrary object subgraph using the `kotlin.native.concurrent.freeze` function.
It makes all the objects reachable from the given one immutable,
such a transition is a one-way operation (i.e., objects cannot be unfrozen later).
Some naturally immutable objects such as `kotlin.String`, `kotlin.Int`, and
other primitive types, along with `AtomicInt` and `AtomicReference` are frozen
by default. If a mutating operation is applied to a frozen object,
an `InvalidMutabilityException` is thrown.
To achieve `mutable XOR global` invariant, all globally visible state (currently,
`object` singletons and enums) are automatically frozen. If object freezing
is not desired, a `kotlin.native.ThreadLocal` annotation can be used, which will make
the object state thread local, and so, mutable (but the changed state is not visible to
other threads).
Top level/global variables of non-primitive types are by default accessible in the
main thread (i.e., the thread which initialized _Kotlin/Native_ runtime first) only.
Access from another thread will lead to an `IncorrectDereferenceException` being thrown.
To make such variables accessible in other threads, you can use either the `@ThreadLocal` annotation,
and mark the value thread local or `@SharedImmutable`, which will make the value frozen and accessible
from other threads.
Class `AtomicReference` can be used to publish the changed frozen state to
other threads, and so build patterns like shared caches.
The content of this page is moved to https://kotlinlang.org/docs/native-immutability.html
+1 -720
View File
@@ -1,722 +1,3 @@
# _Kotlin/Native_ interoperability #
## Introduction ##
_Kotlin/Native_ follows the general tradition of Kotlin to provide excellent
existing platform software interoperability. In the case of a native platform,
the most important interoperability target is a C library. So _Kotlin/Native_
comes with a `cinterop` tool, which can be used to quickly generate
everything needed to interact with an external library.
The following workflow is expected when interacting with the native library.
* create a `.def` file describing what to include into bindings
* use the `cinterop` tool to produce Kotlin bindings
* run _Kotlin/Native_ compiler on an application to produce the final executable
The interoperability tool analyses C headers and produces a "natural" mapping of
the types, functions, and constants into the Kotlin world. The generated stubs can be
imported into an IDE for the purpose of code completion and navigation.
Interoperability with Swift/Objective-C is provided too and covered in a
separate document [OBJC_INTEROP.md](OBJC_INTEROP.md).
## Platform libraries ##
Note that in many cases there's no need to use custom interoperability library creation mechanisms described below,
as for APIs available on the platform standardized bindings called [platform libraries](PLATFORM_LIBS.md)
could be used. For example, POSIX on Linux/macOS platforms, Win32 on Windows platform, or Apple frameworks
on macOS/iOS are available this way.
## Simple example ##
Install libgit2 and prepare stubs for the git library:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
cd samples/gitchurn
../../dist/bin/cinterop -def src/nativeInterop/cinterop/libgit2.def \
-compiler-option -I/usr/local/include -o libgit2
```
</div>
Compile the client:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
../../dist/bin/kotlinc src/gitChurnMain/kotlin \
-library libgit2 -o GitChurn
```
</div>
Run the client:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
./GitChurn.kexe ../..
```
</div>
## Creating bindings for a new library ##
To create bindings for a new library, start by creating a `.def` file.
Structurally it's a simple property file, which looks like this:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = png.h
headerFilter = png.h
package = png
```
</div>
Then run the `cinterop` tool with something like this (note that for host libraries that are not included
in the sysroot search paths, headers may be needed):
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
cinterop -def png.def -compiler-option -I/usr/local/include -o png
```
</div>
This command will produce a `png.klib` compiled library and
`png-build/kotlin` directory containing Kotlin source code for the library.
If the behavior for a certain platform needs to be modified, you can use a format like
`compilerOpts.osx` or `compilerOpts.linux` to provide platform-specific values
to the options.
Note, that the generated bindings are generally platform-specific, so if you are developing for
multiple targets, the bindings need to be regenerated.
After the generation of bindings, they can be used by the IDE as a proxy view of the
native library.
For a typical Unix library with a config script, the `compilerOpts` will likely contain
the output of a config script with the `--cflags` flag (maybe without exact paths).
The output of a config script with `--libs` will be passed as a `-linkedArgs` `kotlinc`
flag value (quoted) when compiling.
### Selecting library headers
When library headers are imported to a C program with the `#include` directive,
all of the headers included by these headers are also included in the program.
So all header dependencies are included in generated stubs as well.
This behavior is correct but it can be very inconvenient for some libraries. So
it is possible to specify in the `.def` file which of the included headers are to
be imported. The separate declarations from other headers can also be imported
in case of direct dependencies.
#### Filtering headers by globs
It is possible to filter headers by globs. The `headerFilter` property value
from the `.def` file is treated as a space-separated list of globs. If the
included header matches any of the globs, then the declarations from this header
are included into the bindings.
The globs are applied to the header paths relative to the appropriate include
path elements, e.g. `time.h` or `curl/curl.h`. So if the library is usually
included with `#include <SomeLibrary/Header.h>`, then it would probably be
correct to filter headers with
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headerFilter = SomeLibrary/**
```
</div>
If a `headerFilter` is not specified, then all headers are included.
#### Filtering by module maps
Some libraries have proper `module.modulemap` or `module.map` files in its
headers. For example, macOS and iOS system libraries and frameworks do.
The [module map file](https://clang.llvm.org/docs/Modules.html#module-map-language)
describes the correspondence between header files and modules. When the module
maps are available, the headers from the modules that are not included directly
can be filtered out using the experimental `excludeDependentModules` option of the
`.def` file:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = OpenGL/gl.h OpenGL/glu.h GLUT/glut.h
compilerOpts = -framework OpenGL -framework GLUT
excludeDependentModules = true
```
</div>
When both `excludeDependentModules` and `headerFilter` are used, they are
applied as an intersection.
### C compiler and linker options ###
Options passed to the C compiler (used to analyze headers, such as preprocessor definitions) and the linker
(used to link final executables) can be passed in the definition file as `compilerOpts` and `linkerOpts`
respectively. For example
<div class="sample" markdown="1" theme="idea" mode="c">
```c
compilerOpts = -DFOO=bar
linkerOpts = -lpng
```
</div>
Target-specific options, only applicable to the certain target can be specified as well, such as
<div class="sample" markdown="1" theme="idea" mode="c">
```c
compilerOpts = -DBAR=bar
compilerOpts.linux_x64 = -DFOO=foo1
compilerOpts.mac_x64 = -DFOO=foo2
```
</div>
and so, C headers on Linux will be analyzed with `-DBAR=bar -DFOO=foo1` and on macOS with `-DBAR=bar -DFOO=foo2`.
Note that any definition file option can have both common and the platform-specific part.
### Adding custom declarations ###
Sometimes it is required to add custom C declarations to the library before
generating bindings (e.g., for [macros](#macros)). Instead of creating an
additional header file with these declarations, you can include them directly
to the end of the `.def` file, after a separating line, containing only the
separator sequence `---`:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = errno.h
---
static inline int getErrno() {
return errno;
}
```
</div>
Note that this part of the `.def` file is treated as part of the header file, so
functions with the body should be declared as `static`.
The declarations are parsed after including the files from the `headers` list.
### Including static library in your klib
Sometimes it is more convenient to ship a static library with your product,
rather than assume it is available within the user's environment.
To include a static library into `.klib` use `staticLibrary` and `libraryPaths`
clauses. For example:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = foo.h
staticLibraries = libfoo.a
libraryPaths = /opt/local/lib /usr/local/opt/curl/lib
```
</div>
When given the above snippet the `cinterop` tool will search `libfoo.a` in
`/opt/local/lib` and `/usr/local/opt/curl/lib`, and if it is found include the
library binary into `klib`.
When using such `klib` in your program, the library is linked automatically.
## Using bindings ##
### Basic interop types ###
All the supported C types have corresponding representations in Kotlin:
* Signed, unsigned integral, and floating point types are mapped to their
Kotlin counterpart with the same width.
* Pointers and arrays are mapped to `CPointer<T>?`.
* Enums can be mapped to either Kotlin enum or integral values, depending on
heuristics and the [definition file hints](#definition-file-hints).
* Structs / unions are mapped to types having fields available via the dot notation,
i.e. `someStructInstance.field1`.
* `typedef` are represented as `typealias`.
Also, any C type has the Kotlin type representing the lvalue of this type,
i.e., the value located in memory rather than a simple immutable self-contained
value. Think C++ references, as a similar concept.
For structs (and `typedef`s to structs) this representation is the main one
and has the same name as the struct itself, for Kotlin enums it is named
`${type}Var`, for `CPointer<T>` it is `CPointerVar<T>`, and for most other
types it is `${type}Var`.
For types that have both representations, the one with a "lvalue" has a mutable
`.value` property for accessing the value.
#### Pointer types ####
The type argument `T` of `CPointer<T>` must be one of the "lvalue" types
described above, e.g., the C type `struct S*` is mapped to `CPointer<S>`,
`int8_t*` is mapped to `CPointer<int_8tVar>`, and `char**` is mapped to
`CPointer<CPointerVar<ByteVar>>`.
C null pointer is represented as Kotlin's `null`, and the pointer type
`CPointer<T>` is not nullable, but the `CPointer<T>?` is. The values of this
type support all the Kotlin operations related to handling `null`, e.g. `?:`, `?.`,
`!!` etc.:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val path = getenv("PATH")?.toKString() ?: ""
```
</div>
Since the arrays are also mapped to `CPointer<T>`, it supports the `[]` operator
for accessing values by index:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun shift(ptr: CPointer<BytePtr>, length: Int) {
for (index in 0 .. length - 2) {
ptr[index] = ptr[index + 1]
}
}
```
</div>
The `.pointed` property for `CPointer<T>` returns the lvalue of type `T`,
pointed by this pointer. The reverse operation is `.ptr`: it takes the lvalue
and returns the pointer to it.
`void*` is mapped to `COpaquePointer` the special pointer type which is the
supertype for any other pointer type. So if the C function takes `void*`, then
the Kotlin binding accepts any `CPointer`.
Casting a pointer (including `COpaquePointer`) can be done with
`.reinterpret<T>`, e.g.:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val intPtr = bytePtr.reinterpret<IntVar>()
```
</div>
or
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val intPtr: CPointer<IntVar> = bytePtr.reinterpret()
```
</div>
As is with C, these reinterpret casts are unsafe and can potentially lead to
subtle memory problems in the application.
Also there are unsafe casts between `CPointer<T>?` and `Long` available,
provided by the `.toLong()` and `.toCPointer<T>()` extension methods:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val longValue = ptr.toLong()
val originalPtr = longValue.toCPointer<T>()
```
</div>
Note that if the type of the result is known from the context, the type argument
can be omitted as usual due to the type inference.
### Memory allocation ###
The native memory can be allocated using the `NativePlacement` interface, e.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val byteVar = placement.alloc<ByteVar>()
```
</div>
or
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val bytePtr = placement.allocArray<ByteVar>(5)
```
</div>
The most "natural" placement is in the object `nativeHeap`.
It corresponds to allocating native memory with `malloc` and provides an additional
`.free()` operation to free allocated memory:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val buffer = nativeHeap.allocArray<ByteVar>(size)
<use buffer>
nativeHeap.free(buffer)
```
</div>
However, the lifetime of allocated memory is often bound to the lexical scope.
It is possible to define such scope with `memScoped { ... }`.
Inside the braces, the temporary placement is available as an implicit receiver,
so it is possible to allocate native memory with `alloc` and `allocArray`,
and the allocated memory will be automatically freed after leaving the scope.
For example, the C function returning values through pointer parameters can be
used like
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val fileSize = memScoped {
val statBuf = alloc<stat>()
val error = stat("/", statBuf.ptr)
statBuf.st_size
}
```
</div>
### Passing pointers to bindings ###
Although C pointers are mapped to the `CPointer<T>` type, the C function
pointer-typed parameters are mapped to `CValuesRef<T>`. When passing
`CPointer<T>` as the value of such a parameter, it is passed to the C function as is.
However, the sequence of values can be passed instead of a pointer. In this case
the sequence is passed "by value", i.e., the C function receives the pointer to
the temporary copy of that sequence, which is valid only until the function returns.
The `CValuesRef<T>` representation of pointer parameters is designed to support
C array literals without explicit native memory allocation.
To construct the immutable self-contained sequence of C values, the following
methods are provided:
* `${type}Array.toCValues()`, where `type` is the Kotlin primitive type
* `Array<CPointer<T>?>.toCValues()`, `List<CPointer<T>?>.toCValues()`
* `cValuesOf(vararg elements: ${type})`, where `type` is a primitive or pointer
For example:
C:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
void foo(int* elements, int count);
...
int elements[] = {1, 2, 3};
foo(elements, 3);
```
</div>
Kotlin:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
foo(cValuesOf(1, 2, 3), 3)
```
</div>
### Working with the strings ###
Unlike other pointers, the parameters of type `const char*` are represented as
a Kotlin `String`. So it is possible to pass any Kotlin string to a binding
expecting a C string.
There are also some tools available to convert between Kotlin and C strings
manually:
* `fun CPointer<ByteVar>.toKString(): String`
* `val String.cstr: CValuesRef<ByteVar>`.
To get the pointer, `.cstr` should be allocated in native memory, e.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```
val cString = kotlinString.cstr.getPointer(nativeHeap)
```
</div>
In all cases, the C string is supposed to be encoded as UTF-8.
To skip automatic conversion and ensure raw pointers are used in the bindings, a `noStringConversion`
statement in the `.def` file could be used, i.e.
<div class="sample" markdown="1" theme="idea" mode="c">
```c
noStringConversion = LoadCursorA LoadCursorW
```
</div>
This way any value of type `CPointer<ByteVar>` can be passed as an argument of `const char*` type.
If a Kotlin string should be passed, code like this could be used:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
memScoped {
LoadCursorA(null, "cursor.bmp".cstr.ptr) // for ASCII version
LoadCursorW(null, "cursor.bmp".wcstr.ptr) // for Unicode version
}
```
</div>
### Scope-local pointers ###
It is possible to create a scope-stable pointer of C representation of `CValues<T>`
instance using the `CValues<T>.ptr` extension property, available under `memScoped { ... }`.
It allows using the APIs which require C pointers with a lifetime bound to a certain `MemScope`. For example:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
memScoped {
items = arrayOfNulls<CPointer<ITEM>?>(6)
arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr }
menu = new_menu("Menu".cstr.ptr, items.toCValues().ptr)
...
}
```
</div>
In this example, all values passed to the C API `new_menu()` have a lifetime of the innermost `memScope`
it belongs to. Once the control flow leaves the `memScoped` scope the C pointers become invalid.
### Passing and receiving structs by value ###
When a C function takes or returns a struct / union `T` by value, the corresponding
argument type or return type is represented as `CValue<T>`.
`CValue<T>` is an opaque type, so the structure fields cannot be accessed with
the appropriate Kotlin properties. It should be possible, if an API uses structures
as handles, but if field access is required, there are the following conversion
methods available:
* `fun T.readValue(): CValue<T>`. Converts (the lvalue) `T` to a `CValue<T>`.
So to construct the `CValue<T>`, `T` can be allocated, filled, and then
converted to `CValue<T>`.
* `CValue<T>.useContents(block: T.() -> R): R`. Temporarily places the
`CValue<T>` to memory, and then runs the passed lambda with this placed
value `T` as receiver. So to read a single field, the following code can be
used:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val fieldValue = structValue.useContents { field }
```
</div>
### Callbacks ###
To convert a Kotlin function to a pointer to a C function,
`staticCFunction(::kotlinFunction)` can be used. It is also able to provide
the lambda instead of a function reference. The function or lambda must not
capture any values.
If the callback doesn't run in the main thread, it is mandatory to init the _Kotlin/Native_
runtime by calling `kotlin.native.initRuntimeIfNeeded()`.
#### Passing user data to callbacks ####
Often C APIs allow passing some user data to callbacks. Such data is usually
provided by the user when configuring the callback. It is passed to some C function
(or written to the struct) as e.g. `void*`.
However, references to Kotlin objects can't be directly passed to C.
So they require wrapping before configuring the callback and then unwrapping in
the callback itself, to safely swim from Kotlin to Kotlin through the C world.
Such wrapping is possible with `StableRef` class.
To wrap the reference:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val stableRef = StableRef.create(kotlinReference)
val voidPtr = stableRef.asCPointer()
```
</div>
where the `voidPtr` is a `COpaquePointer` and can be passed to the C function.
To unwrap the reference:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val stableRef = voidPtr.asStableRef<KotlinClass>()
val kotlinReference = stableRef.get()
```
</div>
where `kotlinReference` is the original wrapped reference.
The created `StableRef` should eventually be manually disposed using
the `.dispose()` method to prevent memory leaks:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
stableRef.dispose()
```
</div>
After that it becomes invalid, so `voidPtr` can't be unwrapped anymore.
See the `samples/libcurl` for more details.
### Macros ###
Every C macro that expands to a constant is represented as a Kotlin property.
Other macros are not supported. However, they can be exposed manually by
wrapping them with supported declarations. E.g. function-like macro `FOO` can be
exposed as function `foo` by
[adding the custom declaration](#adding-custom-declarations) to the library:
<div class="sample" markdown="1" theme="idea" mode="c">
```c
headers = library/base.h
---
static inline int foo(int arg) {
return FOO(arg);
}
```
</div>
### Definition file hints ###
The `.def` file supports several options for adjusting the generated bindings.
* `excludedFunctions` property value specifies a space-separated list of the names
of functions that should be ignored. This may be required because a function
declared in the C header is not generally guaranteed to be really callable, and
it is often hard or impossible to figure this out automatically. This option
can also be used to workaround a bug in the interop itself.
* `strictEnums` and `nonStrictEnums` properties values are space-separated
lists of the enums that should be generated as a Kotlin enum or as integral
values correspondingly. If the enum is not included into any of these lists,
then it is generated according to the heuristics.
* `noStringConversion` property value is space-separated lists of the functions whose
`const char*` parameters shall not be autoconverted as Kotlin string
### Portability ###
Sometimes the C libraries have function parameters or struct fields of a
platform-dependent type, e.g. `long` or `size_t`. Kotlin itself doesn't provide
neither implicit integer casts nor C-style integer casts (e.g.
`(size_t) intValue`), so to make writing portable code in such cases easier,
the `convert` method is provided:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun ${type1}.convert<${type2}>(): ${type2}
```
</div>
where each of `type1` and `type2` must be an integral type, either signed or unsigned.
`.convert<${type}>` has the same semantics as one of the
`.toByte`, `.toShort`, `.toInt`, `.toLong`,
`.toUByte`, `.toUShort`, `.toUInt` or `.toULong`
methods, depending on `type`.
The example of using `convert`:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun zeroMemory(buffer: COpaquePointer, size: Int) {
memset(buffer, 0, size.convert<size_t>())
}
```
</div>
Also, the type parameter can be inferred automatically and so may be omitted
in some cases.
### Object pinning ###
Kotlin objects could be pinned, i.e. their position in memory is guaranteed to be stable
until unpinned, and pointers to such objects inner data could be passed to the C functions. For example
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun readData(fd: Int): String {
val buffer = ByteArray(1024)
buffer.usePinned { pinned ->
while (true) {
val length = recv(fd, pinned.addressOf(0), buffer.size.convert(), 0).toInt()
if (length <= 0) {
break
}
// Now `buffer` has raw data obtained from the `recv()` call.
}
}
}
```
</div>
Here we use service function `usePinned`, which pins an object, executes block and unpins it on normal and
exception paths.
The content of this page is moved to https://kotlinlang.org/docs/native-c-interop.html
+1 -72
View File
@@ -1,74 +1,3 @@
# Symbolicating iOS crash reports
Debugging an iOS application crash sometimes involves analyzing crash reports.
More info about crash reports can be found
[in the official documentation](https://developer.apple.com/library/archive/technotes/tn2151/_index.html).
Crash reports generally require symbolication to become properly readable:
symbolication turns machine code addresses into human-readable source locations.
The document below describes some specific details of symbolicating crash reports
from iOS applications using Kotlin.
## Producing .dSYM for release Kotlin binaries
To symbolicate addresses in Kotlin code (e.g. for stack trace elements
corresponding to Kotlin code) `.dSYM` bundle for Kotlin code is required.
By default Kotlin/Native compiler produces `.dSYM` for release
(i.e. optimized) binaries on Darwin platforms. This can be disabled with `-Xadd-light-debug=disable`
compiler flag. At the same time this option is disabled by default for other platforms, to enable it use `-Xadd-light-debug=enable`.
To control option in Gradle, use
```kotlin
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
}
}
}
```
(in Kotlin DSL).
In projects created from IntelliJ IDEA or AppCode templates these `.dSYM` bundles
are then discovered by Xcode automatically.
## Make frameworks static when using rebuild from bitcode
Rebuilding Kotlin-produced framework from bitcode invalidates the original `.dSYM`.
If it is performed locally, make sure the updated `.dSYM` is used when symbolicating
crash reports.
If rebuilding is performed on App Store side, then `.dSYM` of rebuilt *dynamic* framework
seems discarded and not downloadable from App Store Connect.
So in this case it may be required to make the framework static, e.g. with
```kotlin
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework> {
isStatic = true
}
}
}
```
(in Kotlin DSL).
## Decode inlined stack frames
Xcode doesn't seem to properly decode stack trace elements of inlined function
calls (these aren't only Kotlin `inline` functions but also functions that are
inlined when optimizing machine code). So some stack trace elements may be
missing. If this is the case, consider using `lldb` to process crash report
that is already symbolicated by Xcode, for example:
```bash
$ lldb -b -o "script import lldb.macosx" -o "crashlog file.crash"
```
This command should output crash report that is additionally processed and
includes inlined stack trace elements.
More details can be found in [LLDB documentation](https://lldb.llvm.org/use/symbolication.html).
The content of this page is moved to https://kotlinlang.org/docs/native-ios-symbolication.html
+1 -243
View File
@@ -1,245 +1,3 @@
# Kotlin/Native libraries
## Kotlin compiler specifics
To produce a library with the Kotlin/Native compiler use the `-produce library` or `-p library` flag. For example:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ kotlinc foo.kt -p library -o bar
```
</div>
the above command will produce a `bar.klib` with the compiled contents of `foo.kt`.
To link to a library use the `-library <name>` or `-l <name>` flag. For example:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ kotlinc qux.kt -l bar
```
</div>
the above command will produce a `program.kexe` out of `qux.kt` and `bar.klib`
## cinterop tool specifics
The **cinterop** tool produces `.klib` wrappers for native libraries as its main output.
For example, using the simple `libgit2.def` native library definition file provided in your Kotlin/Native distribution
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ cinterop -def samples/gitchurn/src/nativeInterop/cinterop/libgit2.def -compiler-option -I/usr/local/include -o libgit2
```
</div>
we will obtain `libgit2.klib`.
See more details in [INTEROP.md](INTEROP.md)
## klib utility
The **klib** library management utility allows you to inspect and install the libraries.
The following commands are available.
To list library contents:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib contents <name>
```
</div>
To inspect the bookkeeping details of the library
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib info <name>
```
</div>
To install the library to the default location use
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib install <name>
```
</div>
To remove the library from the default repository use
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib remove <name>
```
</div>
All of the above commands accept an additional `-repository <directory>` argument for specifying a repository different to the default one.
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib <command> <name> -repository <directory>
```
</div>
## Several examples
First let's create a library.
Place the tiny library source code into `kotlinizer.kt`:
<div class="sample" markdown="1" theme="idea" mode="shell">
```kotlin
package kotlinizer
val String.kotlinized
get() = "Kotlin $this"
```
```bash
$ kotlinc kotlinizer.kt -p library -o kotlinizer
```
</div>
The library has been created in the current directory:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ ls kotlinizer.klib
kotlinizer.klib
```
</div>
Now let's check out the contents of the library:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib contents kotlinizer
```
</div>
We can install `kotlinizer` to the default repository:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ klib install kotlinizer
```
</div>
Remove any traces of it from the current directory:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ rm kotlinizer.klib
```
</div>
Create a very short program and place it into a `use.kt` :
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import kotlinizer.*
fun main(args: Array<String>) {
println("Hello, ${"world".kotlinized}!")
}
```
</div>
Now compile the program linking with the library we have just created:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ kotlinc use.kt -l kotlinizer -o kohello
```
</div>
And run the program:
<div class="sample" markdown="1" theme="idea" mode="shell">
```bash
$ ./kohello.kexe
Hello, Kotlin world!
```
</div>
Have fun!
# Advanced topics
## Library search sequence
When given a `-library foo` flag, the compiler searches the `foo` library in the following order:
* Current compilation directory or an absolute path.
* All repositories specified with `-repo` flag.
* Libraries installed in the default repository (For now the default is `~/.konan`, however it could be changed by setting **KONAN_DATA_DIR** environment variable).
* Libraries installed in `$installation/klib` directory.
## The library format
Kotlin/Native libraries are zip files containing a predefined
directory structure, with the following layout:
**foo.klib** when unpacked as **foo/** gives us:
```yaml
- foo/
- $component_name/
- ir/
- Serialized Kotlin IR.
- targets/
- $platform/
- kotlin/
- Kotlin compiled to LLVM bitcode.
- native/
- Bitcode files of additional native objects.
- $another_platform/
- There can be several platform specific kotlin and native pairs.
- linkdata/
- A set of ProtoBuf files with serialized linkage metadata.
- resources/
- General resources such as images. (Not used yet).
- manifest - A file in *java property* format describing the library.
```
An example layout can be found in `klib/stdlib` directory of your installation.
The content of this page is moved to https://kotlinlang.org/docs/native-libraries.html
+1 -424
View File
@@ -1,426 +1,3 @@
# _Kotlin/Native_ interoperability with Swift/Objective-C
This document covers some details of Kotlin/Native interoperability with
Swift/Objective-C.
## Usage
Kotlin/Native provides bidirectional interoperability with Objective-C.
Objective-C frameworks and libraries can be used in Kotlin code if
properly imported to the build (system frameworks are imported by default).
See e.g. "Using cinterop" in
[Gradle plugin documentation](GRADLE_PLUGIN.md#using-cinterop).
A Swift library can be used in Kotlin code if its API is exported to Objective-C
with `@objc`. Pure Swift modules are not yet supported.
Kotlin modules can be used in Swift/Objective-C code if compiled into a
framework (see "Targets and output kinds" section in [Gradle plugin documentation](GRADLE_PLUGIN.md#targets-and-output-kinds)).
See [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator) for an example.
## Mappings
The table below shows how Kotlin concepts are mapped to Swift/Objective-C and vice versa.
"->" and "<-" indicate that mapping only goes one way.
| Kotlin | Swift | Objective-C | Notes |
| ------ | ----- |------------ | ----- |
| `class` | `class` | `@interface` | [note](#name-translation) |
| `interface` | `protocol` | `@protocol` | |
| `constructor`/`create` | Initializer | Initializer | [note](#initializers) |
| Property | Property | Property | [note](#top-level-functions-and-properties) [note](#setters)|
| Method | Method | Method | [note](#top-level-functions-and-properties) [note](#method-names-translation) |
| `suspend` -> | `completionHandler:` | | [note](#errors-and-exceptions) |
| `@Throws fun` | `throws` | `error:(NSError**)error` | [note](#errors-and-exceptions) |
| Extension | Extension | Category member | [note](#extensions-and-category-members) |
| `companion` member <- | Class method or property | Class method or property | |
| `null` | `nil` | `nil` | |
| `Singleton` | `Singleton()` | `[Singleton singleton]` | [note](#kotlin-singletons) |
| Primitive type | Primitive type / `NSNumber` | | [note](#nsnumber) |
| `Unit` return type | `Void` | `void` | |
| `String` | `String` | `NSString` | |
| `String` | `NSMutableString` | `NSMutableString` | [note](#nsmutablestring) |
| `List` | `Array` | `NSArray` | |
| `MutableList` | `NSMutableArray` | `NSMutableArray` | |
| `Set` | `Set` | `NSSet` | |
| `MutableSet` | `NSMutableSet` | `NSMutableSet` | [note](#collections) |
| `Map` | `Dictionary` | `NSDictionary` | |
| `MutableMap` | `NSMutableDictionary` | `NSMutableDictionary` | [note](#collections) |
| Function type | Function type | Block pointer type | [note](#function-types) |
| Inline classes | Unsupported| Unsupported| [note](#unsupported) |
### Name translation
Objective-C classes are imported into Kotlin with their original names.
Protocols are imported as interfaces with `Protocol` name suffix,
i.e. `@protocol Foo` -> `interface FooProtocol`.
These classes and interfaces are placed into a package [specified in build configuration](#usage)
(`platform.*` packages for preconfigured system frameworks).
The names of Kotlin classes and interfaces are prefixed when imported to Objective-C.
The prefix is derived from the framework name.
### Initializers
Swift/Objective-C initializers are imported to Kotlin as constructors and factory methods
named `create`. The latter happens with initializers declared in the Objective-C category or
as a Swift extension, because Kotlin has no concept of extension constructors.
Kotlin constructors are imported as initializers to Swift/Objective-C.
### Setters
Writeable Objective-C properties overriding read-only properties of the superclass are represented as `setFoo()` method for the property `foo`. Same goes for a protocol's read-only properties that are implemented as mutable.
### Top-level functions and properties
Top-level Kotlin functions and properties are accessible as members of special classes.
Each Kotlin file is translated into such a class. E.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
// MyLibraryUtils.kt
package my.library
fun foo() {}
```
</div>
can be called from Swift like
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
MyLibraryUtilsKt.foo()
```
</div>
### Method names translation
Generally Swift argument labels and Objective-C selector pieces are mapped to Kotlin
parameter names. Anyway these two concepts have different semantics, so sometimes
Swift/Objective-C methods can be imported with a clashing Kotlin signature. In this case
the clashing methods can be called from Kotlin using named arguments, e.g.:
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
[player moveTo:LEFT byMeters:17]
[player moveTo:UP byInches:42]
```
</div>
in Kotlin it would be:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
player.moveTo(LEFT, byMeters = 17)
player.moveTo(UP, byInches = 42)
```
</div>
### Errors and exceptions
Kotlin has no concept of checked exceptions, all Kotlin exceptions are unchecked.
Swift has only checked errors. So if Swift or Objective-C code calls a Kotlin method
which throws an exception to be handled, then the Kotlin method should be marked
with a `@Throws` annotation specifying a list of "expected" exception classes.
When compiling to Objective-C/Swift framework, non-`suspend` functions having or inheriting
`@Throws` annotation are represented as `NSError*`-producing methods in Objective-C
and as `throws` methods in Swift. Representations for `suspend` functions always have
`NSError*`/`Error` parameter in completion handler.
When Kotlin function called from Swift/Objective-C code throws an exception
which is an instance of one of the `@Throws`-specified classes or their subclasses,
it is propagated as `NSError`. Other Kotlin exceptions reaching Swift/Objective-C
are considered unhandled and cause program termination.
`suspend` functions without `@Throws` propagate only
`CancellationException` as `NSError`. Non-`suspend` functions without `@Throws`
don't propagate Kotlin exceptions at all.
Note that the opposite reversed translation is not implemented yet:
Swift/Objective-C error-throwing methods aren't imported to Kotlin as
exception-throwing.
### Extensions and category members
Members of Objective-C categories and Swift extensions are imported to Kotlin
as extensions. That's why these declarations can't be overridden in Kotlin.
And the extension initializers aren't available as Kotlin constructors.
Kotlin extensions to "regular" Kotlin classes are imported to Swift and Objective-C as extensions and category members respectively.
Kotlin extensions to other types are treated as [top-level declarations](#top-level-functions-and-properties)
with an additional receiver parameter. These types include:
* Kotlin `String` type
* Kotlin collection types and subtypes
* Kotlin `interface` types
* Kotlin primitive types
* Kotlin `inline` classes
* Kotlin `Any` type
* Kotlin function types and subtypes
* Objective-C classes and protocols
### Kotlin singletons
Kotlin singleton (made with an `object` declaration, including `companion object`)
is imported to Swift/Objective-C as a class with a single instance.
The instance is available through the factory method, i.e. as
`[MySingleton mySingleton]` in Objective-C and `MySingleton()` in Swift.
### NSNumber
Kotlin primitive type boxes are mapped to special Swift/Objective-C classes.
For example, `kotlin.Int` box is represented as `KotlinInt` class instance in Swift
(or `${prefix}Int` instance in Objective-C, where `prefix` is the framework names prefix).
These classes are derived from `NSNumber`, so the instances are proper `NSNumber`s
supporting all corresponding operations.
`NSNumber` type is not automatically translated to Kotlin primitive types
when used as a Swift/Objective-C parameter type or return value.
The reason is that `NSNumber` type doesn't provide enough information
about a wrapped primitive value type, i.e. `NSNumber` is statically not known
to be a e.g. `Byte`, `Boolean`, or `Double`. So Kotlin primitive values
should be cast to/from `NSNumber` manually (see [below](#casting-between-mapped-types)).
### NSMutableString
`NSMutableString` Objective-C class is not available from Kotlin.
All instances of `NSMutableString` are copied when passed to Kotlin.
### Collections
Kotlin collections are converted to Swift/Objective-C collections as described
in the table above. Swift/Objective-C collections are mapped to Kotlin in the same way,
except for `NSMutableSet` and `NSMutableDictionary`. `NSMutableSet` isn't converted to
a Kotlin `MutableSet`. To pass an object for Kotlin `MutableSet`,
you can create this kind of Kotlin collection explicitly by either creating it
in Kotlin with e.g. `mutableSetOf()`, or using the `KotlinMutableSet` class in Swift
(or `${prefix}MutableSet` in Objective-C, where `prefix` is the framework names prefix).
The same holds for `MutableMap`.
### Function types
Kotlin function-typed objects (e.g. lambdas) are converted to
Swift functions / Objective-C blocks. However there is a difference in how
types of parameters and return values are mapped when translating a function
and a function type. In the latter case primitive types are mapped to their
boxed representation. Kotlin `Unit` return value is represented
as a corresponding `Unit` singleton in Swift/Objective-C. The value of this singleton
can be retrieved in the same way as it is for any other Kotlin `object`
(see singletons in the table above).
To sum the things up:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
fun foo(block: (Int) -> Unit) { ... }
```
</div>
would be represented in Swift as
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
func foo(block: (KotlinInt) -> KotlinUnit)
```
</div>
and can be called like
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
foo {
bar($0 as! Int32)
return KotlinUnit()
}
```
</div>
### Generics
Objective-C supports "lightweight generics" defined on classes, with a relatively limited feature set. Swift can import
generics defined on classes to help provide additional type information to the compiler.
Generic feature support for Objective-C and Swift differ from Kotlin, so the translation will inevitably lose some information,
but the features supported retain meaningful information.
#### Limitations
Objective-C generics do not support all features of either Kotlin or Swift, so there will be some information lost
in the translation.
Generics can only be defined on classes, not on interfaces (protocols in Objective-C and Swift) or functions.
#### Nullability
Kotlin and Swift both define nullability as part of the type specification, while Objective-C defines nullability on methods
and properties of a type. As such, the following:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
class Sample<T>() {
fun myVal(): T
}
```
</div>
will (logically) look like this:
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
class Sample<T>() {
fun myVal(): T?
}
```
</div>
In order to support a potentially nullable type, the Objective-C header needs to define `myVal` with a nullable return value.
To mitigate this, when defining your generic classes, if the generic type should *never* be null, provide a non-null
type constraint:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
class Sample<T : Any>() {
fun myVal(): T
}
```
</div>
That will force the Objective-C header to mark `myVal` as non-null.
#### Variance
Objective-C allows generics to be declared covariant or contravariant. Swift has no support for variance. Generic classes coming
from Objective-C can be force-cast as needed.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
data class SomeData(val num: Int = 42) : BaseData()
class GenVarOut<out T : Any>(val arg: T)
```
</div>
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
let variOut = GenVarOut<SomeData>(arg: sd)
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>
```
</div>
#### Constraints
In Kotlin you can provide upper bounds for a generic type. Objective-C also supports this, but that support is unavailable
in more complex cases, and is currently not supported in the Kotlin - Objective-C interop. The exception here being a non-null
upper bound will make Objective-C methods/properties non-null.
### To disable
To have the framework header written without generics, add the flag to the compiler config:
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
binaries.framework {
freeCompilerArgs += "-Xno-objc-generics"
}
```
</div>
## Casting between mapped types
When writing Kotlin code, an object may need to be converted from a Kotlin type
to the equivalent Swift/Objective-C type (or vice versa). In this case a plain old
Kotlin cast can be used, e.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
val nsArray = listOf(1, 2, 3) as NSArray
val string = nsString as String
val nsNumber = 42 as NSNumber
```
</div>
## Subclassing
### Subclassing Kotlin classes and interfaces from Swift/Objective-C
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
and protocols.
### Subclassing Swift/Objective-C classes and protocols from Kotlin
Swift/Objective-C classes and protocols can be subclassed with a Kotlin `final` class.
Non-`final` Kotlin classes inheriting Swift/Objective-C types aren't supported yet, so it is
not possible to declare a complex class hierarchy inheriting Swift/Objective-C types.
Normal methods can be overridden using the `override` Kotlin keyword. In this case
the overriding method must have the same parameter names as the overridden one.
Sometimes it is required to override initializers, e.g. when subclassing `UIViewController`.
Initializers imported as Kotlin constructors can be overridden by Kotlin constructors
marked with the `@OverrideInit` annotation:
<div class="sample" markdown="1" theme="idea" mode="swift">
```swift
class ViewController : UIViewController {
@OverrideInit constructor(coder: NSCoder) : super(coder)
...
}
```
</div>
The overriding constructor must have the same parameter names and types as the overridden one.
To override different methods with clashing Kotlin signatures, you can add a
`@Suppress("CONFLICTING_OVERLOADS")` annotation to the class.
By default the Kotlin/Native compiler doesn't allow calling a non-designated
Objective-C initializer as a `super(...)` constructor. This behaviour can be
inconvenient if the designated initializers aren't marked properly in the Objective-C
library. Adding a `disableDesignatedInitializerChecks = true` to the `.def` file for
this library would disable these compiler checks.
## C features
See [INTEROP.md](INTEROP.md) for an example case where the library uses some plain C features
(e.g. unsafe pointers, structs etc.).
## Unsupported
Some features of Kotlin programming language are not yet mapped into respective features of Objective-C or Swift.
Currently, following features are not properly exposed in generated framework headers:
* inline classes (arguments are mapped as either underlying primitive type or `id`)
* custom classes implementing standard Kotlin collection interfaces (`List`, `Map`, `Set`) and other special classes
* Kotlin subclasses of Objective-C classes
The content of this page is moved to https://kotlinlang.org/docs/native-objc-interop.html
+1 -59
View File
@@ -1,61 +1,3 @@
# Platform libraries
## Overview
To provide access to user's native operating system services,
`Kotlin/Native` distribution includes a set of prebuilt libraries specific to
each target. We call them **Platform Libraries**.
### POSIX bindings
For all `Unix` or `Windows` based targets (including `Android` and
`iOS`) we provide the `posix` platform lib. It contains bindings
to platform's implementation of `POSIX` standard.
To use the library just
<div class="sample" markdown="1" theme="idea" data-highlight-only>
```kotlin
import platform.posix.*
```
</div>
The only target for which it is not available is [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly).
Note that the content of `platform.posix` is NOT identical on
different platforms, in the same way as different `POSIX` implementations
are a little different.
### Popular native libraries
There are many more platform libraries available for host and
cross-compilation targets. `Kotlin/Native` distribution provides access to
`OpenGL`, `zlib` and other popular native libraries on
applicable platforms.
On Apple platforms `objc` library is provided for interoperability with [Objective-C](https://en.wikipedia.org/wiki/Objective-C).
Inspect the contents of `dist/klib/platform/$target` of the distribution for the details.
## Availability by default
The packages from platform libraries are available by default. No
special link flags need to be specified to use them. `Kotlin/Native`
compiler automatically detects which of the platform libraries have
been accessed and automatically links the needed libraries.
On the other hand, the platform libs in the distribution are merely
just wrappers and bindings to the native libraries. That means the
native libraries themselves (`.so`, `.a`, `.dylib`, `.dll` etc)
should be installed on the machine.
## Examples
`Kotlin/Native` installation provides a wide spectrum of examples
demonstrating the use of platform libraries.
See [samples](https://github.com/JetBrains/kotlin-native/tree/master/samples) for details.
The content of this page is moved to https://kotlinlang.org/docs/native-platform-libs.html