Most people who start using Kotlin are already familiar with Java and its ecosystem. I, on the other hand, am not one of those people and have been looking for easier ways into the Java ecosystem. I found that Kotlin was probably that softer path back. (I wrote Java in college but never with anything other than pretty simple use-cases that never followed real Java development practices.)
I found getting started with Kotlin and Gradle to be painful (to say the least) since most of the tutorials assume some kind of prior knowledge. This is my attempt to provide what I couldn't find anywhere else.
Initial Setup
Since I'm running Fedora 29 for this project, I ran:
sudo dnf install -y gradle
With this I was able to successfully run:
gradle init
In my project directory. I started trying to follow documentation for setting the project up as a Kotlin application and ran into some issues. For one, the documentation told me to do:
gradle init --dsl kotlin \ --test-framework kotlintest \ --type kotlin-application
So when I cleared out my project directory and tried that I found that none of those option values were valid. I then checked the documented version I was working against and found that it needed Gradle 5.0.
After some scouring, I found that I could use an initialized project directory to get Gradle 5.0 with "The Wrapper". To do so, I ran:
gradle init --type java-application ./gradlew wrapper --gradle-version 5.0 ./gradlew help
Running ./gradlew help downloaded 5.0 for me which then allowed me to wipe my directory again (except for The Wrapper) and run:
./gradlew init --dsl kotlin \ --test-framework kotlintest \ --type kotlin-application
This generated a directory structure that looks like:
. ├── build │ ├── classes │ │ ├── java │ │ └── kotlin │ │ ├── main │ │ │ ├── myFirstKotlinApp │ │ │ │ ├── App.class │ │ │ │ └── AppKt.class │ │ │ └── META-INF │ │ │ └── myFirstKotlinApp.kotlin_module │ │ └── test │ │ ├── myFirstKotlinApp │ │ │ └── AppTest.class │ │ └── META-INF │ │ └── myFirstKotlinApp.kotlin_module │ ├── distributions │ │ ├── myFirstKotlinApp.tar │ │ └── myFirstKotlinApp.zip │ ├── kotlin │ │ ├── compileKotlin │ │ │ ├── build-history.bin │ │ │ ├── caches-jvm │ │ │ │ ├── inputs │ │ │ │ │ ├── source-to-output.tab │ │ │ │ │ ├── source-to-output.tab_i │ │ │ │ │ ├── source-to-output.tab_i.len │ │ │ │ │ ├── source-to-output.tab.keystream │ │ │ │ │ ├── source-to-output.tab.keystream.len │ │ │ │ │ ├── source-to-output.tab.len │ │ │ │ │ └── source-to-output.tab.values.at │ │ │ │ ├── jvm │ │ │ │ │ └── kotlin │ │ │ │ │ ├── class-fq-name-to-source.tab │ │ │ │ │ ├── class-fq-name-to-source.tab_i │ │ │ │ │ ├── class-fq-name-to-source.tab_i.len │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream.len │ │ │ │ │ ├── class-fq-name-to-source.tab.len │ │ │ │ │ ├── class-fq-name-to-source.tab.values.at │ │ │ │ │ ├── internal-name-to-source.tab │ │ │ │ │ ├── internal-name-to-source.tab_i │ │ │ │ │ ├── internal-name-to-source.tab_i.len │ │ │ │ │ ├── internal-name-to-source.tab.keystream │ │ │ │ │ ├── internal-name-to-source.tab.keystream.len │ │ │ │ │ ├── internal-name-to-source.tab.len │ │ │ │ │ ├── internal-name-to-source.tab.values.at │ │ │ │ │ ├── package-parts.tab │ │ │ │ │ ├── package-parts.tab_i │ │ │ │ │ ├── package-parts.tab_i.len │ │ │ │ │ ├── package-parts.tab.keystream │ │ │ │ │ ├── package-parts.tab.keystream.len │ │ │ │ │ ├── package-parts.tab.len │ │ │ │ │ ├── package-parts.tab.values.at │ │ │ │ │ ├── proto.tab │ │ │ │ │ ├── proto.tab_i │ │ │ │ │ ├── proto.tab_i.len │ │ │ │ │ ├── proto.tab.keystream │ │ │ │ │ ├── proto.tab.keystream.len │ │ │ │ │ ├── proto.tab.len │ │ │ │ │ ├── proto.tab.values.at │ │ │ │ │ ├── source-to-classes.tab │ │ │ │ │ ├── source-to-classes.tab_i │ │ │ │ │ ├── source-to-classes.tab_i.len │ │ │ │ │ ├── source-to-classes.tab.keystream │ │ │ │ │ ├── source-to-classes.tab.keystream.len │ │ │ │ │ ├── source-to-classes.tab.len │ │ │ │ │ └── source-to-classes.tab.values.at │ │ │ │ └── lookups │ │ │ │ ├── counters.tab │ │ │ │ ├── file-to-id.tab │ │ │ │ ├── file-to-id.tab_i │ │ │ │ ├── file-to-id.tab_i.len │ │ │ │ ├── file-to-id.tab.keystream │ │ │ │ ├── file-to-id.tab.keystream.len │ │ │ │ ├── file-to-id.tab.len │ │ │ │ ├── file-to-id.tab.values.at │ │ │ │ ├── id-to-file.tab │ │ │ │ ├── id-to-file.tab.keystream │ │ │ │ ├── id-to-file.tab.keystream.len │ │ │ │ ├── id-to-file.tab.len │ │ │ │ ├── id-to-file.tab.values.at │ │ │ │ ├── lookups.tab │ │ │ │ ├── lookups.tab_i │ │ │ │ ├── lookups.tab_i.len │ │ │ │ ├── lookups.tab.keystream │ │ │ │ ├── lookups.tab.keystream.len │ │ │ │ ├── lookups.tab.len │ │ │ │ └── lookups.tab.values.at │ │ │ ├── data-container-format-version.txt │ │ │ ├── format-version.txt │ │ │ ├── gradle-format-version.txt │ │ │ └── last-build.bin │ │ ├── compileTestKotlin │ │ │ ├── build-history.bin │ │ │ ├── caches-jvm │ │ │ │ ├── inputs │ │ │ │ │ ├── source-to-output.tab │ │ │ │ │ ├── source-to-output.tab_i │ │ │ │ │ ├── source-to-output.tab_i.len │ │ │ │ │ ├── source-to-output.tab.keystream │ │ │ │ │ ├── source-to-output.tab.keystream.len │ │ │ │ │ ├── source-to-output.tab.len │ │ │ │ │ └── source-to-output.tab.values.at │ │ │ │ ├── jvm │ │ │ │ │ └── kotlin │ │ │ │ │ ├── class-fq-name-to-source.tab │ │ │ │ │ ├── class-fq-name-to-source.tab_i │ │ │ │ │ ├── class-fq-name-to-source.tab_i.len │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream │ │ │ │ │ ├── class-fq-name-to-source.tab.keystream.len │ │ │ │ │ ├── class-fq-name-to-source.tab.len │ │ │ │ │ ├── class-fq-name-to-source.tab.values.at │ │ │ │ │ ├── internal-name-to-source.tab │ │ │ │ │ ├── internal-name-to-source.tab_i │ │ │ │ │ ├── internal-name-to-source.tab_i.len │ │ │ │ │ ├── internal-name-to-source.tab.keystream │ │ │ │ │ ├── internal-name-to-source.tab.keystream.len │ │ │ │ │ ├── internal-name-to-source.tab.len │ │ │ │ │ ├── internal-name-to-source.tab.values.at │ │ │ │ │ ├── proto.tab │ │ │ │ │ ├── proto.tab_i │ │ │ │ │ ├── proto.tab_i.len │ │ │ │ │ ├── proto.tab.keystream │ │ │ │ │ ├── proto.tab.keystream.len │ │ │ │ │ ├── proto.tab.len │ │ │ │ │ ├── proto.tab.values.at │ │ │ │ │ ├── source-to-classes.tab │ │ │ │ │ ├── source-to-classes.tab_i │ │ │ │ │ ├── source-to-classes.tab_i.len │ │ │ │ │ ├── source-to-classes.tab.keystream │ │ │ │ │ ├── source-to-classes.tab.keystream.len │ │ │ │ │ ├── source-to-classes.tab.len │ │ │ │ │ └── source-to-classes.tab.values.at │ │ │ │ └── lookups │ │ │ │ ├── counters.tab │ │ │ │ ├── file-to-id.tab │ │ │ │ ├── file-to-id.tab_i │ │ │ │ ├── file-to-id.tab_i.len │ │ │ │ ├── file-to-id.tab.keystream │ │ │ │ ├── file-to-id.tab.keystream.len │ │ │ │ ├── file-to-id.tab.len │ │ │ │ ├── file-to-id.tab.values.at │ │ │ │ ├── id-to-file.tab │ │ │ │ ├── id-to-file.tab.keystream │ │ │ │ ├── id-to-file.tab.keystream.len │ │ │ │ ├── id-to-file.tab.len │ │ │ │ ├── id-to-file.tab.values.at │ │ │ │ ├── lookups.tab │ │ │ │ ├── lookups.tab_i │ │ │ │ ├── lookups.tab_i.len │ │ │ │ ├── lookups.tab.keystream │ │ │ │ ├── lookups.tab.keystream.len │ │ │ │ ├── lookups.tab.len │ │ │ │ └── lookups.tab.values.at │ │ │ ├── data-container-format-version.txt │ │ │ ├── format-version.txt │ │ │ ├── gradle-format-version.txt │ │ │ └── last-build.bin │ │ ├── homesyncandbackupjar-classes.txt │ │ └── sessions │ ├── libs │ │ └── myFirstKotlinApp.jar │ ├── reports │ │ └── tests │ │ └── test │ │ ├── classes │ │ │ └── myFirstKotlinApp.AppTest.html │ │ ├── css │ │ │ ├── base-style.css │ │ │ └── style.css │ │ ├── index.html │ │ ├── js │ │ │ └── report.js │ │ └── packages │ │ └── myFirstKotlinApp.html │ ├── scripts │ │ ├── myFirstKotlinApp │ │ └── myFirstKotlinApp.bat │ ├── test-results │ │ └── test │ │ ├── binary │ │ │ ├── output.bin │ │ │ ├── output.bin.idx │ │ │ └── results.bin │ │ └── TEST-myFirstKotlinApp.AppTest.xml │ └── tmp │ ├── compileJava │ ├── compileTestJava │ └── jar │ └── MANIFEST.MF ├── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main │ ├── kotlin │ │ └── myFirstKotlinApp │ │ └── App.kt │ └── resources └── test ├── kotlin │ └── myFirstKotlinApp │ └── AppTest.kt └── resources 52 directories, 157 files
Hacking on the Rest
My editor of choice is vim but I'll definitely be playing with something a bit more intelligent. In this case, I've installed VSCode and will be playing around with it. I freely admit, however, that the vim bindings in VSCode are just close enough to real vim to be ultimately frustrating. I'll save that explanation for another blog post, though.