Skip to content

Commit

Permalink
Add basic examples for Kotlin integration
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnm committed Sep 10, 2024
1 parent f14502b commit a983d13
Show file tree
Hide file tree
Showing 17 changed files with 411 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ jobs:
millargs: "'example.javalib.__.local.testCached'"
- java-version: 17
millargs: "'example.scalalib.__.local.testCached'"
- java-version: 17
millargs: "'example.kotlinlib.__.local.testCached'"
- java-version: '11'
millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.testCached'"
- java-version: 17
Expand Down
101 changes: 101 additions & 0 deletions example/kotlinlib/basic/1-simple/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//// SNIPPET:BUILD
package build
import mill._, kotlinlib._, scalalib._

object `package` extends RootModule with KotlinModule {

def kotlinVersion = "1.9.24"

def mainClass = Some("foo.FooKt")

def ivyDeps = super.ivyDeps() ++ Agg(
ivy"net.sourceforge.argparse4j:argparse4j:0.9.0",
ivy"org.apache.commons:commons-text:1.12.0"
)

object test extends KotlinModuleTests with TestModule.Junit4{
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.google.guava:guava:33.3.0-jre"
)
}
}

// This is a basic Mill build for a single `KotlinModule`, with two
// third-party dependencies and a test suite using the JUnit framework. As a
// single-module project, it `extends RootModule` to mark `object foo` as the
// top-level module in the build. This lets us directly perform operations
// `./mill compile` or `./mill run` without needing to prefix it as
// `foo.compile` or `foo.run`.
//
//// SNIPPET:DEPENDENCIES
//
// This example project uses two third-party dependencies - ArgParse4J for CLI
// argument parsing, Apache Commons Text for HTML escaping - and uses them to wrap a
// given input string in HTML templates with proper escaping.
//
// You can run `assembly` to generate a standalone executable jar, which then
// can be run from the command line or deployed to be run elsewhere.

/** Usage

> ./mill resolve _ # List what tasks are available to run
assembly
...
clean
...
compile
...
run
...
show
...
inspect
...

> ./mill inspect compile # Show documentation and inputs of a task
compile(KotlinModule...)
Compiles all the sources to JVM class files.
Compiles the current module to generate compiled classfiles/bytecode.
When you override this, you probably also want/need to override [[bspCompileClassesPath]],
as that needs to point to the same compilation output path.
Keep in sync with [[bspCompileClassesPath]]
Inputs:
allJavaSourceFiles
allKotlinSourceFiles
compileClasspath
upstreamCompileOutput
javacOptions
zincReportCachedProblems
kotlincOptions
kotlinCompilerClasspath
...

> ./mill compile # compile sources into classfiles
...
Compiling 1 Kotlin sources to...

> ./mill run # run the main method, if any
error: argument -t/--text is required
...

> ./mill run --text hello
<h1>hello</h1>

> ./mill test
...
Test foo.FooTest.testEscaping finished, ...
Test foo.FooTest.testSimple finished, ...
Test run foo.FooTest finished: 0 failed, 0 ignored, 2 total, ...

> ./mill assembly # bundle classfiles and libraries into a jar for deployment

> ./mill show assembly # show the output of the assembly task
".../out/assembly.dest/out.jar"

> java -jar ./out/assembly.dest/out.jar --text hello
<h1>hello</h1>

> ./out/assembly.dest/out.jar --text hello # mac/linux
<h1>hello</h1>

*/
30 changes: 30 additions & 0 deletions example/kotlinlib/basic/1-simple/src/foo/Foo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package foo

import org.apache.commons.text.StringEscapeUtils
import net.sourceforge.argparse4j.ArgumentParsers
import net.sourceforge.argparse4j.inf.ArgumentParser
import net.sourceforge.argparse4j.inf.ArgumentParserException
import net.sourceforge.argparse4j.inf.Namespace

fun generateHtml(text: String): String {
return "<h1>" + StringEscapeUtils.escapeHtml4(text) + "</h1>"
}

fun main(args: Array<String>) {
val parser = ArgumentParsers.newFor("template").build()
.defaultHelp(true)
.description("Inserts text into a HTML template")

parser.addArgument("-t", "--text")
.required(true)
.help("text to insert")

try {
parser.parseArgs(args).let {
println(generateHtml(it.getString("text")))
}
} catch (e: Exception) {
println(e.message)
System.exit(1)
}
}
18 changes: 18 additions & 0 deletions example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package foo

import com.google.common.html.HtmlEscapers.htmlEscaper
import foo.generateHtml
import org.junit.Assert.assertEquals
import org.junit.Test

class FooTest {
@Test
fun testSimple() {
assertEquals(generateHtml("hello"), "<h1>hello</h1>")
}

@Test
fun testEscaping() {
assertEquals(generateHtml("<hello>"), "<h1>" + htmlEscaper().escape("<hello>") + "</h1>")
}
}
23 changes: 23 additions & 0 deletions example/kotlinlib/basic/2-custom-build-logic/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//// SNIPPET:BUILD
package build
import mill._, kotlinlib._, scalalib._

object `package` extends RootModule with KotlinModule {

def kotlinVersion = "1.9.24"

def mainClass = Some("foo.FooKt")

/** Total number of lines in module's source files */
def lineCount = T{
allSourceFiles().map(f => os.read.lines(f.path).size).sum
}

/** Generate resources using lineCount of sources */
override def resources = T{
os.write(T.dest / "line-count.txt", "" + lineCount())
Seq(PathRef(T.dest))
}

object test extends KotlinModuleTests with TestModule.Junit4
}
17 changes: 17 additions & 0 deletions example/kotlinlib/basic/2-custom-build-logic/src/foo/Foo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package foo

import java.io.IOException

fun getLineCount(): String? {
return try {
String(
::main.javaClass.classLoader.getResourceAsStream("line-count.txt").readAllBytes()
)
} catch (e: IOException) {
null
}
}

fun main() {
println("Line Count: " + getLineCount())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package foo

import org.junit.Assert.assertEquals
import org.junit.Test
import foo.getLineCount

class FooTests {

@Test
fun testSimple() {
val expectedLineCount = 12
val actualLineCount = getLineCount()?.trim().let { Integer.parseInt(it) }
assertEquals(expectedLineCount, actualLineCount)
}
}
14 changes: 14 additions & 0 deletions example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package bar

import org.apache.commons.text.StringEscapeUtils
import net.sourceforge.argparse4j.ArgumentParsers
import net.sourceforge.argparse4j.inf.ArgumentParser
import net.sourceforge.argparse4j.inf.Namespace

fun generateHtml(text: String): String {
return "<h1>" + StringEscapeUtils.escapeHtml4(text) + "</h1>"
}

fun main(args: Array<String>) {
println("Bar.value: " + generateHtml(args[0]))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package bar

import bar.generateHtml
import org.junit.Assert.assertEquals
import org.junit.Test

class BarTests {

@Test
fun simple() {
val result = generateHtml("hello")
assertEquals("<h1>hello</h1>", result)
}

@Test
fun escaping() {
val result = generateHtml("<hello>")
assertEquals("<h1>&lt;hello&gt;</h1>", result)
}
}
54 changes: 54 additions & 0 deletions example/kotlinlib/basic/3-multi-module/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//// SNIPPET:BUILD
package build
import mill._, kotlinlib._, scalalib._

trait MyModule extends KotlinModule {

def kotlinVersion = "1.9.24"

}

object foo extends MyModule {
def mainClass = Some("foo.FooKt")
def moduleDeps = Seq(bar)
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"net.sourceforge.argparse4j:argparse4j:0.9.0"
)
}

object bar extends MyModule {
def mainClass = Some("bar.BarKt")
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"net.sourceforge.argparse4j:argparse4j:0.9.0",
ivy"org.apache.commons:commons-text:1.12.0"
)

object test extends KotlinModuleTests with TestModule.Junit4
}

//// SNIPPET:TREE
// ----
// build.mill
// foo/
// src/
// foo/
// Foo.java
// resources/
// ...
// bar/
// src/
// bar/
// Bar.java
// resources/
// ...
// out/
// foo/
// compile.json
// compile.dest/
// ...
// bar/
// compile.json
// compile.dest/
// ...
// ----
//
25 changes: 25 additions & 0 deletions example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package foo

import net.sourceforge.argparse4j.ArgumentParsers
import net.sourceforge.argparse4j.inf.ArgumentParser
import net.sourceforge.argparse4j.inf.Namespace

const val VALUE: String = "hello"

fun mainFunction(fooText: String, barText: String) {
println("Foo.value: " + VALUE)
println("Bar.value: " + bar.generateHtml(barText))
}

fun main(args: Array<String>) {
val parser = ArgumentParsers.newFor("Foo").build()
parser.addArgument("--foo-text").required(true)
parser.addArgument("--bar-text").required(true)

val res = parser.parseArgs(args)

val fooText = res.getString("foo_text")
val barText = res.getString("bar_text")

mainFunction(fooText, barText)
}
14 changes: 14 additions & 0 deletions example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package bar

import org.apache.commons.text.StringEscapeUtils
import net.sourceforge.argparse4j.ArgumentParsers
import net.sourceforge.argparse4j.inf.ArgumentParser
import net.sourceforge.argparse4j.inf.Namespace

fun generateHtml(text: String): String {
return "<h1>" + StringEscapeUtils.escapeHtml4("world") + "</h1>"
}

fun main(args: Array<String>) {
println("Bar.value: " + generateHtml(args[0]))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package bar

import bar.generateHtml
import org.junit.Assert.assertEquals
import org.junit.Test

public class BarTests {

@Test
fun testSimple() {
val result = generateHtml("hello")
assertEquals("<h1>hello</h1>", result)
}

@Test
fun testEscaping() {
val result = generateHtml("<hello>")
assertEquals("<h1>&lt;hello&gt;</h1>", result)
}
}
Loading

0 comments on commit a983d13

Please sign in to comment.