Introduction To Linux Development

This article will cover a basic introduction to Linux development using Kotlin via Kotlin Native. Not all native development on the Linux platform is system level, and there are plenty of opportunities to do development at a high level while still having some (albeit limited) low level access. Linux development opens many doors especially in the IoT development area.

It is assumed that you know the following in order to follow this article:

- How to use Linux
- Using the command line via a terminal
- Basic understanding of the Kotlin programming language
- Using IntelliJ
- Managing the build process using Gradle via the Gradle Kotlin DSL

Initial Setup

Before getting started the software development environment will need to be setup. The following software will need to be installed:

1. Linux (use a LTS version of a desktop Linux distribution, eg Linux Mint)
2. IntelliJ (in Linux)
3. SDK Man (in Linux)

Create Project

Before creating the project ensure that Linux is running.

1. Open a terminal
2. Install Gradle by running the following: sdk i gradle 6.2.2
3. Close the terminal
4. Open IntelliJ
5. Create a new Gradle project (uncheck all libraries and frameworks) called native-hello

Setup Build

1. Show the Terminal pane
2. Update the Gradle wrapper by running the following: gradle wrapper — gradle-version 6.2.2
3. Hide the Terminal pane
4. Do a Gradle refresh
5. Remove the build.gradle and settings.gradle files
6. Create the settings.gradle.kts file which contains the following:
```kotlin
rootProject.name = “native-hello”
```
7. Create the build.gradle.kts file which contains the following:
```kotlin
group = “org.example”
version = “0.1-SNAPSHOT”
```
8. Append the plugins block after version which will load the Kotlin Multiplatform plugin (supports Kotlin Native):
```kotlin
plugins {
kotlin(“multiplatform”) version “1.3.72”
}
```
9. Append the kotlin block which contains the target (linuxX64), and the binary setup:
```kotlin
kotlin {
linuxX64 {
binaries {
executable { entryPoint = “org.example.hello.main” }
}
}
}
```
10. Do a Gradle refresh

The build file should look like the following:
```kotlin
group = “org.example”
version = “0.1-SNAPSHOT”

plugins {
kotlin(“multiplatform”) version “1.3.72”
}

kotlin {
linuxX64 {
binaries {
executable { entryPoint = “org.example.hello.main” }
}
}
}
```

Note that the linuxX64 target has the simplest build process of all the Kotlin Native targets. This is one of the major advantages of the linuxX64 target. All Kotlin Native binaries must have a entry point to indicate how to start a program. In this case it will be the main.kt file which is in the org.example.hello package, where the entry point would be set to org.example.hello.main without the .kt file extension.

Develop CLI Program

Once the build is setup a CLI program will be developed which will run on a Linux AMD64 based PC via a terminal.

1. Select the Project pane
2. In native-hello directory press Alt+Insert
3. Select the Directory option and press Enter
4. Select the src/linuxX64Main/kotlin option and press Enter
5. In the src/linuxX64Main/kotlin directory press Alt+Insert
6. Select the Kotlin File/Class option and press Enter
7. In the new file enter in the following:
```kotlin
package org.example.hello

fun main() {
println(“Hello World! :)”)
}
```

Run CLI Program

With the Kotlin Native Linux targets both debug (contains debugging information), and release binaries can be created, which can be self contained. At a minimum the generated binary includes the Kotlin standard library (Kotlin Native version), and the Kotlin Native runtime. All generated binaries DO NOT rely on a virtual machine or any separate runtime.

1. Create a new Gradle run configuration with the Tasks field set to runDebugExecutableLinuxX64
2. Run the program by pressing Shift+F10

After a few seconds you should see a message being printed to the terminal via the stdout text stream. Inside the project directory the generated debug binary is located in build/bin/linuxX64/debugExecutable. Binary meta data can be obtained via the readelf command (https://linux.die.net/man/1/readelf). Below is a example which shows all dynamic libraries used in the binary (via the dynamic section), eg readelf -d native-hello.kexe:

```
Dynamic section at offset 0x7fba8 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x0000000000000001 (NEEDED) Shared library: [ld-linux-x86–64.so.2]
# …
```

There are other tools that can be used to obtain binary metadata like ldd (http://man7.org/linux/man-pages/man1/ldd.1.html) for example.

Binary Creation Process

The process of creating a binary with Kotlin (via Kotlin Native) is very similar to the process that the C language uses. Below is the Kotlin Native process used to create a binary with the Linux targets:

1. Software developer writes Kotlin source code (represented as .kt files)
2. If any native libraries (written in the C programming language) are used then the cinterop tool is run to generate the mapping files
3. The Kotlin Native compiler (a LLVM front-end compiler known as konan) takes the Kotlin source code/mapping files and generates LLVM bitcode (similar to Java bytecode) which is a intermediary format
4. The LLVM linker (lld) is used to create a binary (contains machine code) from the LLVM bitcode

Conclusion

By developing a native CLI program for Linux using Kotlin (via Kotlin Native) you have seen how the development process works, and have discovered that the build process is reasonably straightforward. Since real binaries are generated by Kotlin Native some of the C language tools can be used (readelf, valgrind, lldb etc).