My project is split up into multiple packages that...
# getting-started
r
My project is split up into multiple packages that I import into main. Pressing play in intellij runs my project, but when I manually do
kotlinc Main.kt -include-runtime -d out.jar && java -jar out.jar
, I get unresolved errors about my package imports.
h
Well, it depends on the error message you didn’t mention. If you get ClassNotFoundExceptions during runtime it’s because you didn’t add the classpath to the java call.
r
error: unresolved reference: mysubmodule import mysubmodule
h
And IMHO you should not fight against the main conventions of the ecosystem. This makes everything harder.
r
that is hard when the conventions extend to being forced into a specific IDE that I dislike
h
Then you might need to add the jars/files to kotlinc.
r
and currently, all we're trying to do is run code, from the command line
is the convention in JVM/Kotlin to be so dependent on GUI tooling that running code without clicking big shiny icons is tricky to get working?
h
Gradle isn’t forced to a specific IDE, build servers don’t need an IDE ;)
r
But just having a normal development workflow, where I write code, execute it, test it, repeat, etc.
most of the guides I find online say "click this button in intellij"
h
I never use IntelliJs buttons but Gradle 🤷🏻‍♂️ and properly almost many users here too.
r
Then you might need to add the jars/files to kotlinc. (edited)
could you clarify what you mean by this. I have this structure src/ main.kt foo/ A.kt B.kt
in main, i do
Copy code
import foo.A

fun main() {
  A()
}
(omitting the
package
declarations)
r
what would I put there
I obviously cannot explicitly enumerate my packages
my codebase is a pyramid of dozens of packages
i assume it can just recursively resolve imports
like all other languages
Copy code
sudo kotlinc src -include-runtime -d out.jar && java -jar outjar
Error: Unable to access jarfile outjar
this is my problem
both answers look equally useless though
one of the answers just says to do
-cp main.jar:package_a
...but that is for a single package.
why does the big play button in intellij not require this
it intelligently resolves imports recursively.
j
> I obviously cannot explicitly enumerate my packages In the JVM world you pass all your sources to the compiler, yes. Imports are not used for dependency resolution, they are used as a convenient syntax to avoid using qualified names
r
but the play button doesn't do that..
j
but the play button doesn't do that..
What do you mean?
h
It does under the hood
r
so why isn't there a command line utility that does that under the hood
h
You just invented a build tool like Maven or Gradle.
j
IntelliJ calls the Kotlin compiler with all your source files depending on how you configured it. If you use Gradle, then IntelliJ will know the source directories from the Gradle source sets configuration
r
i am using gradle right now
is there a gradle command i need to do
h
./gradlew :compileKotlin
or just
./gradlew assemble
👍 1
r
after doing ./gradlew assemble, where does the jar go
trying to run it now
j
i assume it can just recursively resolve imports like all other languages
That's not how most JVM languages work, btw
1
h
build/libs/name.jar
r
just, in software, you modularize your code into sub folders with sub folders etc. that are all eventually imported back up into main where you run your code
i go python main.py, and it triggers that cascade of imports
or npm run dev
or rust run (i forget)
h
npm is also a build tool.
😆 1
1
r
all i'm conveying is that when you execute a top-level file like main.whatever, and main.whatever references other files, some mechanism resolves those
npm run dev just triggers node runtime typically
image.png
Copy code
java -jar build/libs/kts.jar                                                                                    
no main manifest attribute, in build/libs/kts.jar
(sorry for naming the project kts. it is not a .kts project)
j
all i'm conveying is that when you execute a top-level file like main.whatever, and main.whatever references other files, some mechanism resolves those
You don't execute a source file. You compile a set of source files, then you execute compiled files/classes/jars. In the JVM world, you can compile multiple parts independently. So you need to tell the compiler which files you're compiling together, and which dependencies need to be available to them. Your examples are about running the app, which is different. And actually Python and JS are not compiled, so you can't really compare that to a compilation. Rust uses cargo as a build tool, so if you're comparing with this you should compare with Gradle commands, and you get pretty much the same convenience
r
im fine using a gradle command to compile and run the code
👌 1
h
Yes, you need to specify the entry point. When running with the IntelliJ button, IntelliJ
sets
it for you.
r
so then something like
Copy code
manifest {
    attributes["Main-Class"] = "MainKt"
}
?
j
To do that, use the
application
plugin in Gradle, so it knows it's not a library but an application you can run. You don't need to manually set manifest entries, Gradle will do it for you. https://kotlinlang.org/docs/get-started-with-jvm-gradle-project.html#explore-the-build-script
Copy code
plugins {
    kotlin("jvm") version "1.9.23"
    application
}

application {
    mainClass.set("MainKt") // The main class of the application
}
👍 1
h
application.mainClass.set("MainKt")
j
I agree that it's annoying that the tutorial goes through IntelliJ, it gives a false sense that IntelliJ is necessary. I think it was made in an attempt to simplify the project initialization for beginners, but for users like you it seems counter productive
h
and if you want to run the jar without calling java, use
./gradlew run
and for tests
./gradlew test
j
Yep, all you really need for a JVM CLI application is a
build.gradle.kts
with:
Copy code
plugins {
    kotlin("jvm") version "1.9.23"
    application
}

repositories {
    mavenCentral()
}

application {
    mainClass.set("MainKt")
}
Then place your source files in
src/main/kotlin
, with one of them called
Main.kt
with the main function. Then you can compile and run your app using
./gradlew run
. If you just want compilation or test you can run other Gradle tasks.
r
How can I change the folder structure target. I have
src/main.kt
, not
src/main/kotllin/Main.kt
Copy code
plugins {
    kotlin("jvm") version "1.9.23"
    application
}
repositories {
    mavenCentral()
}
application {
    mainClass.set("MainKt")
}
dependencies {
    testImplementation(kotlin("test"))
}
sourceSets {
    main {
        kotlin.srcDirs("src/")
    }
    test {
        kotlin.srcDirs("test/")
    }
}
j
Yeah if you want to stray away from conventions, you're going to have to add more config in your Gradle build script. And I think you got it right, here
r
Copy code
/gradlew run                                                                                                    3.66s
> Task :run FAILED
Error: Could not find or load main class MainKt
Caused by: java.lang.ClassNotFoundException: MainKt

FAILURE: Build failed with an exception.
main.kt
Copy code
package <http://org.me|org.me>

import mod.Foo

fun main() {
    Foo()
}
j
You have a
package <http://org.me|org.me>
at the top, so your main class is not just
MainKt
but
org.me.MainKt
r
great, all working now. thanks a lot
🙏 1
is it good practice to do package org.me at the top of main.kt?
j
Yeah it's often the case to have all your sources under at least some top level package name (usually a reverse domain name that you own). But it's not mandatory.
r
gotcha. I apologies for my posts recently coming off abrasive. just not used to this setup / run process all being so difficult coming from primarily scripting languages, and have been struggling with actually getting to do anything with this language for days now so feeling exhausted. thanks for helping
👍 1
h
You are welcome. BTW do you need a trailing slash in
srcDirs
?
r
I assumed it was necessary to indicate a folder vs file
j
Thanks for saying this, really appreciated. I'm happy to help. Note that you can avoid a part of this frustration if you try to follow more the conventions of the tools rather than tweak them to match the conventions you're used to from other ecosystems.
That said, I have to admit that Gradle can be intimidating when you're starting. We're trying to address that with Amper.