Several programs in one project
A project often holds more than one program: the app, a migration tool, a seed script, a few examples. Kargo has four ways to run them, all through kargo run:
| You want to | Use |
|---|---|
Run some other main once |
kargo run --main <class> |
| Give programs a name and keep them in the build | a [[bin]] table and kargo run --bin <name> |
| Ship small programs that show how to use your code | examples/<name>.kt and kargo run --example <name> |
| Run a one-off file kept anywhere in the project | kargo run <path>.kt |
Run another main
Section titled “Run another main”Say src/ holds two programs:
package com.example.app
fun main() = println("the app")package com.example.tools
fun main(args: Array<String>) = println("migrating ${args.joinToString()}")kargo run can’t pick one, so it lists both:
$ kargo run Compiling app v0.1.0 (jvm) Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.12serror: could not determine the main class: found multiple `main` functions; pick one with `kargo run --main <class>`, declare each as a [[bin]], or set `[target.jvm] main-class` in kargo.toml: com.example.app.MainKt (/home/me/app/src/Main.kt) com.example.tools.MigrateKt (/home/me/app/src/tools/Migrate.kt)Name the class with --main:
$ kargo run --main com.example.tools.MigrateKt -- up Running `java com.example.tools.MigrateKt up`migrating upThe class of a top-level fun main in Migrate.kt is MigrateKt; for object Cli { @JvmStatic fun main(...) } it is Cli.
On Kotlin/Native, --main takes the entry function instead, and --entry-point is an alias that reads better there:
kargo run -t native --entry-point com.example.tools.mainKargo links that executable separately, into target/<profile>/native/<platform>/main/.
To make one of them the default for kargo run, set [target.jvm] main-class (or [target.native] entry-point) in kargo.toml.
Named programs with [[bin]]
Section titled “Named programs with [[bin]]”A [[bin]] table gives a program a name, so you don’t have to remember its class:
[target.jvm]main-class = "com.example.app.MainKt" # what plain `kargo run` starts
[[bin]]name = "migrate"path = "src/tools/Migrate.kt" # the file with its `main`
[[bin]]name = "report"main = "com.example.report.Cli" # or name the JVM class directly$ kargo run --bin migrate -- down Running `java com.example.tools.MigrateKt down`migrating downEach [[bin]] needs a name and one way to find its entry point:
| Key | Meaning |
|---|---|
name |
What you pass to --bin. Letters, digits, - and _, starting with a letter |
path |
A .kt file, relative to the project root, that declares exactly one main. Works for every target |
main |
The JVM main class, e.g. com.example.tools.MigrateKt |
entry-point |
The Kotlin/Native entry function, e.g. com.example.tools.main |
Use path, or main and/or entry-point, not both. A bin with only main is JVM-only; one with only entry-point is Native-only.
The bin’s sources stay in src/, so they are compiled with the rest of the project. On the JVM that costs nothing extra: every bin runs from the same classes. With an executable Native target, kargo build also links one executable per bin with a native entry point, into target/<profile>/native/<platform>/bin/<name>. If the sources hold several main functions and entry-point is not set, the project’s own executable is skipped and only the bins are linked.
When kargo run can’t decide and the project has bins, the error lists them:
$ kargo runerror: could not determine the main class: found multiple `main` functions; ...run a [[bin]] with `kargo run --bin <name>`; available bins: migrate, reportA jar per bin
Section titled “A jar per bin”kargo package --bin <name> builds a fat jar that launches that bin, named after it:
$ kargo package --release --bin migrate Compiling app v0.1.0 (jvm) Packaging migrate v0.1.0 (/home/me/app/target/release/jvm/migrate-0.1.0-all.jar) Finished `release` profile [optimized] target(s) in 2.71s/home/me/app/target/release/jvm/migrate-0.1.0-all.jar$ java -jar target/release/jvm/migrate-0.1.0-all.jar upmigrating upWithout --bin, kargo package launches the project’s own main class as before.
Examples
Section titled “Examples”Put small programs that use your code in examples/, one file each:
import com.example.app.greeting
fun main() = println(greeting("examples"))$ kargo run --example greet Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s Compiling app v0.1.0 (jvm, example greet) Running `java GreetKt`Hello, examples!Examples work like Cargo’s:
- They are not part of
kargo buildor the packaged jar.kargo run --examplecompiles the one you ask for. - Each example is compiled on its own, against the project’s classes, its dependencies and its dev-dependencies. Several examples can therefore each declare
fun mainin the default package. - They see your public API only, not
internaldeclarations. - On the JVM, each one goes to
target/<profile>/jvm/examples/<name>/. On Kotlin/Native, the main sources become a library the example links against, intarget/<profile>/native/<platform>/examples/. There, an example’smainmust not be in a package where your main sources also declare amain; the default package (nopackageline) is the usual choice. kargo fmtandkargo lintcheckexamples/too.
Run a single file
Section titled “Run a single file”kargo run also takes the path of a .kt file with a main. The file can live anywhere under the project, such as a tools/ directory next to src/:
import com.example.app.Database
fun main(args: Array<String>) { Database.migrate(args.firstOrNull() ?: "up")}$ kargo run tools/migrate.kt down Compiling app v0.1.0 (jvm, tools/migrate.kt) Finished `dev` profile in 2.1s Running `java MigrateKt down`Like an example, the file is compiled on its own against the main classes, the dependencies and the dev-dependencies, into target/<profile>/jvm/files/<name>/. It is not part of the project’s build, so kargo build and kargo package leave it out, and an unchanged file is not compiled again. The project is the one the file belongs to, so kargo run ../app/tools/migrate.kt works from anywhere.
The first word after run is a file only when it ends in .kt or .kts and exists; every other word is an argument for the program. A .kts script, and a .kt file with a /* kargo ... */ front matter, run as kargo script runs them instead, with their own dependencies.
Targets
Section titled “Targets”| Target | --main |
--bin |
--example |
|---|---|---|---|
| JVM | main class | main, or the class of the main in path |
yes |
| Native | entry function (--entry-point) |
entry-point, or the top-level main in path |
yes |
| Wasm, Android | no | no | no |
Kotlin/Native only accepts a top-level fun main; a @JvmStatic fun main in an object is JVM-only.
See also: kargo run, [[bin]] and Project layout.