From 2a3b632f9a02e5f7a6beb1487b9017ed26c0c140 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 11:49:15 +0100 Subject: [PATCH 01/17] Add some general notes about Spring --- .../ROOT/pages/javalib/spring-boot.adoc | 36 +++++++++++++++++++ .../pages/javalib/springboot-examples.adoc | 7 ---- 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 website/docs/modules/ROOT/pages/javalib/spring-boot.adoc delete mode 100644 website/docs/modules/ROOT/pages/javalib/springboot-examples.adoc diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc new file mode 100644 index 00000000000..fb333474860 --- /dev/null +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -0,0 +1,36 @@ += Spring Framework / Spring Boot Projects +:page-aliases: Spring_Boot_Java.adoc + +This page contains documentation and examples specific for Spring Framework and Spring Boot Projects. + +== General Information + +The Spring Framework and Spring Boot are JVM Runtime frameworks. +What this means is that they provide all their features at runtime. + +The IoC container as the central core of the Spring Framework inspects the runtime environment at application startup. +For that it uses mainly the classpath of the JVM. +It collects all configurations, instantiates services and injects dependencies. +Configurations can come from various places like Java Properties files, YAML files or Java `@Configuration` classes. + +The Spring Boot framework additionally scans the runtime classpath to auto-detect other frameworks and Spring Boot specific features. Based on it's finding it enables dedicated features. Additionally, you can configure Spring Boot programmatically and via configuration files. + +As a consequence, Spring projects typically don't need any special support from a build tool, which is nice, since you don't need to lean any new Mill-specific concepts when you want to use Mill for your Spring projects. +Spring Boot is also not tied to use of Java. It is easily possible to use or mix different JVM languages in your Spring projects. + +Being a runtime framework also means, that most issues you may encounter like configuration problems, missing dependencies, dependency conflicts or unexpected application behavior, can't be easily detected at build-time. These often require inspection of the running or starting application and dealing with error messages and JVM stack traces. The use of a debugger can be sometimes necessary. When developing Spring projects, you should therefore also maintain a functional test suite. + +// That being said, there are some nice features a build tool can provide for Spring Framework / Spring Boot based projects. +// TODO: list them here, once we have one + +Some tools provided by the Spring Boot project are also useful outside of Spring Boot projects, hence Mill makes them available even for non-Spring project. Those are linked below: + +* `RepackageModule` - Creates self-running application assemblies in a single-jar + + +== Spring Boot Web with Initializr + +// of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. + + +include::partial$example/springboot/java/1-web-initializr.adoc[] \ No newline at end of file diff --git a/website/docs/modules/ROOT/pages/javalib/springboot-examples.adoc b/website/docs/modules/ROOT/pages/javalib/springboot-examples.adoc deleted file mode 100644 index ecef426f6b3..00000000000 --- a/website/docs/modules/ROOT/pages/javalib/springboot-examples.adoc +++ /dev/null @@ -1,7 +0,0 @@ -= Spring Boot Project Examples - -This page contains examples of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. - -== Spring Boot Web with Initializr - -include::partial$example/springboot/java/1-web-initializr.adoc[] \ No newline at end of file From ac0e61cc56c157ad521cc80a4369a87edd13909a Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 11:55:00 +0100 Subject: [PATCH 02/17] Share Spring introduction with all Languages --- .../ROOT/pages/javalib/spring-boot.adoc | 27 +----------------- .../ROOT/pages/kotlinlib/spring-boot.adoc | 11 ++++++++ .../pages/kotlinlib/springboot-examples.adoc | 7 ----- .../ROOT/pages/scalalib/spring-boot.adoc | 5 ++++ .../modules/ROOT/partials/Spring_Header.adoc | 28 +++++++++++++++++++ 5 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc delete mode 100644 website/docs/modules/ROOT/pages/kotlinlib/springboot-examples.adoc create mode 100644 website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc create mode 100644 website/docs/modules/ROOT/partials/Spring_Header.adoc diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc index fb333474860..66f074ddb95 100644 --- a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -1,36 +1,11 @@ = Spring Framework / Spring Boot Projects :page-aliases: Spring_Boot_Java.adoc -This page contains documentation and examples specific for Spring Framework and Spring Boot Projects. - -== General Information - -The Spring Framework and Spring Boot are JVM Runtime frameworks. -What this means is that they provide all their features at runtime. - -The IoC container as the central core of the Spring Framework inspects the runtime environment at application startup. -For that it uses mainly the classpath of the JVM. -It collects all configurations, instantiates services and injects dependencies. -Configurations can come from various places like Java Properties files, YAML files or Java `@Configuration` classes. - -The Spring Boot framework additionally scans the runtime classpath to auto-detect other frameworks and Spring Boot specific features. Based on it's finding it enables dedicated features. Additionally, you can configure Spring Boot programmatically and via configuration files. - -As a consequence, Spring projects typically don't need any special support from a build tool, which is nice, since you don't need to lean any new Mill-specific concepts when you want to use Mill for your Spring projects. -Spring Boot is also not tied to use of Java. It is easily possible to use or mix different JVM languages in your Spring projects. - -Being a runtime framework also means, that most issues you may encounter like configuration problems, missing dependencies, dependency conflicts or unexpected application behavior, can't be easily detected at build-time. These often require inspection of the running or starting application and dealing with error messages and JVM stack traces. The use of a debugger can be sometimes necessary. When developing Spring projects, you should therefore also maintain a functional test suite. - -// That being said, there are some nice features a build tool can provide for Spring Framework / Spring Boot based projects. -// TODO: list them here, once we have one - -Some tools provided by the Spring Boot project are also useful outside of Spring Boot projects, hence Mill makes them available even for non-Spring project. Those are linked below: - -* `RepackageModule` - Creates self-running application assemblies in a single-jar +include::partial$Spring_Header.adoc[] == Spring Boot Web with Initializr // of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. - include::partial$example/springboot/java/1-web-initializr.adoc[] \ No newline at end of file diff --git a/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc new file mode 100644 index 00000000000..0b85784c763 --- /dev/null +++ b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc @@ -0,0 +1,11 @@ += Spring Framework / Spring Boot Projects +:page-aliases: Spring_Boot_Kotlin.adoc + +include::partial$Spring_Header.adoc[] + + +// This page contains examples of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. + +== Spring Boot Web with Initializr + +include::partial$example/springboot/kotlin/1-web-initializr.adoc[] \ No newline at end of file diff --git a/website/docs/modules/ROOT/pages/kotlinlib/springboot-examples.adoc b/website/docs/modules/ROOT/pages/kotlinlib/springboot-examples.adoc deleted file mode 100644 index 46b8e9813b9..00000000000 --- a/website/docs/modules/ROOT/pages/kotlinlib/springboot-examples.adoc +++ /dev/null @@ -1,7 +0,0 @@ -= Spring Boot Project Examples - -This page contains examples of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. - -== Spring Boot Web with Initializr - -include::partial$example/springboot/kotlin/1-web-initializr.adoc[] \ No newline at end of file diff --git a/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc new file mode 100644 index 00000000000..f01185406b4 --- /dev/null +++ b/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc @@ -0,0 +1,5 @@ += Spring Framework / Spring Boot Projects +:page-aliases: Spring_Boot_Scala.adoc + +include::partial$Spring_Header.adoc[] + diff --git a/website/docs/modules/ROOT/partials/Spring_Header.adoc b/website/docs/modules/ROOT/partials/Spring_Header.adoc new file mode 100644 index 00000000000..8f827753b09 --- /dev/null +++ b/website/docs/modules/ROOT/partials/Spring_Header.adoc @@ -0,0 +1,28 @@ +// Include this header in the entry page for the language-specific Spring Framework / Spring Boot page + +This page contains documentation and examples specific for Spring Framework and Spring Boot Projects. + +== General Information + +The Spring Framework and Spring Boot are JVM Runtime frameworks. +What this means is that they provide all their features at runtime. + +The IoC container as the central core of the Spring Framework inspects the runtime environment at application startup. +For that it uses mainly the classpath of the JVM. +It collects all configurations, instantiates services and injects dependencies. +Configurations can come from various places like Java Properties files, YAML files or Java `@Configuration` classes. + +The Spring Boot framework additionally scans the runtime classpath to auto-detect other frameworks and Spring Boot specific features. Based on it's finding it enables dedicated features. Additionally, you can configure Spring Boot programmatically and via configuration files. + +As a consequence, Spring projects typically don't need any special support from a build tool, which is nice, since you don't need to lean any new Mill-specific concepts when you want to use Mill for your Spring projects. +Spring Boot is also not tied to use of Java. It is easily possible to use or mix different JVM languages in your Spring projects. + +Being a runtime framework also means, that most issues you may encounter like configuration problems, missing dependencies, dependency conflicts or unexpected application behavior, can't be easily detected at build-time. These often require inspection of the running or starting application and dealing with error messages and JVM stack traces. The use of a debugger can be sometimes necessary. When developing Spring projects, you should therefore also maintain a functional test suite. + +// That being said, there are some nice features a build tool can provide for Spring Framework / Spring Boot based projects. +// TODO: list them here, once we have one + +Some tools provided by the Spring Boot project are also useful outside of Spring Boot projects, hence Mill makes them available even for non-Spring project. Those are linked below: + +* `RepackageModule` - Creates self-running application assemblies in a single-jar + From 32d0457284b9b9bb7bf3f88e53567719b8464d9d Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 12:01:20 +0100 Subject: [PATCH 03/17] Fix navigation bar --- website/docs/modules/ROOT/nav.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/modules/ROOT/nav.adoc b/website/docs/modules/ROOT/nav.adoc index 362cd6a3b9a..4c3321868b8 100644 --- a/website/docs/modules/ROOT/nav.adoc +++ b/website/docs/modules/ROOT/nav.adoc @@ -13,7 +13,7 @@ ** xref:javalib/publishing.adoc[] ** xref:javalib/build-examples.adoc[] ** xref:javalib/web-examples.adoc[] -** xref:javalib/springboot-examples.adoc[] +** xref:javalib/spring-boot.adoc[] * xref:scalalib/intro.adoc[] ** xref:scalalib/config.adoc[] ** xref:scalalib/script.adoc[] @@ -26,6 +26,7 @@ ** xref:scalalib/web-examples.adoc[] ** xref:scalalib/native-examples.adoc[] ** xref:scalalib/spark.adoc[] +** xref:scalalib/spring-boot.adoc[] * xref:kotlinlib/intro.adoc[] ** xref:kotlinlib/config.adoc[] ** xref:kotlinlib/script.adoc[] @@ -36,7 +37,7 @@ ** xref:kotlinlib/publishing.adoc[] // ** xref:kotlinlib/build-examples.adoc[] ** xref:kotlinlib/web-examples.adoc[] -** xref:kotlinlib/springboot-examples.adoc[] +** xref:kotlinlib/spring-boot.adoc[] [] * Experimental Platform Support ** Building Android Apps From f043197c83a5ffd46bed4e2a0ac6a8775953f689 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 12:10:32 +0100 Subject: [PATCH 04/17] Include spring examples in spring pages --- .../modules/ROOT/pages/javalib/spring-boot.adoc | 14 +++++++++++++- .../modules/ROOT/pages/javalib/web-examples.adoc | 4 +++- .../modules/ROOT/pages/kotlinlib/spring-boot.adoc | 11 ++++++++++- .../modules/ROOT/pages/kotlinlib/web-examples.adoc | 9 +++++---- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc index 66f074ddb95..46241eca367 100644 --- a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -8,4 +8,16 @@ include::partial$Spring_Header.adoc[] // of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. -include::partial$example/springboot/java/1-web-initializr.adoc[] \ No newline at end of file +include::partial$example/springboot/java/1-web-initializr.adoc[] + + +[#_spring_boot_hello_world_app] +== Spring Boot Hello World App + +include::partial$example/javalib/web/2-hello-spring-boot.adoc[] + + +== Spring Boot TodoMvc App + + +include::partial$example/javalib/web/3-todo-spring-boot.adoc[] diff --git a/website/docs/modules/ROOT/pages/javalib/web-examples.adoc b/website/docs/modules/ROOT/pages/javalib/web-examples.adoc index 3797ad0d594..982ce36ee71 100644 --- a/website/docs/modules/ROOT/pages/javalib/web-examples.adoc +++ b/website/docs/modules/ROOT/pages/javalib/web-examples.adoc @@ -14,11 +14,13 @@ include::partial$example/javalib/web/1-hello-jetty.adoc[] [#_spring_boot_hello_world_app] == Spring Boot Hello World App +This section was moved to the xref:javalib/spring-boot.adoc#_spring_boot_hello_world_app[Spring Boot page]. + include::partial$example/javalib/web/2-hello-spring-boot.adoc[] == Spring Boot TodoMvc App -include::partial$example/javalib/web/3-todo-spring-boot.adoc[] +This section was moved to the xref:javalib/spring-boot.adoc#_spring_boot_todomvc_app_[Spring Boot page]. [#_micronaut_hello_world_app] == Micronaut Hello World App diff --git a/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc index 0b85784c763..1a36b1c9ab1 100644 --- a/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc @@ -8,4 +8,13 @@ include::partial$Spring_Header.adoc[] == Spring Boot Web with Initializr -include::partial$example/springboot/kotlin/1-web-initializr.adoc[] \ No newline at end of file +include::partial$example/springboot/kotlin/1-web-initializr.adoc[] + +== Spring Boot Hello World App + +include::partial$example/kotlinlib/web/6-hello-spring-boot.adoc[] + +== Spring Boot TodoMvc App + +include::partial$example/kotlinlib/web/7-todo-spring-boot.adoc[] + diff --git a/website/docs/modules/ROOT/pages/kotlinlib/web-examples.adoc b/website/docs/modules/ROOT/pages/kotlinlib/web-examples.adoc index d82473b07ff..8459fd2ad2d 100644 --- a/website/docs/modules/ROOT/pages/kotlinlib/web-examples.adoc +++ b/website/docs/modules/ROOT/pages/kotlinlib/web-examples.adoc @@ -1,7 +1,5 @@ = Kotlin Web Project Examples - - This page contains examples of using Mill as a build tool for web-applications. It covers setting up a basic backend server with a variety of server frameworks @@ -27,8 +25,11 @@ include::partial$example/kotlinlib/web/5-webapp-kotlinjs-shared.adoc[] == Spring Boot Hello World App -include::partial$example/kotlinlib/web/6-hello-spring-boot.adoc[] +This section was moved to the xref:kotlinlib/spring-boot.adoc#_spring_boot_hello_world_app[Spring Boot page]. == Spring Boot TodoMvc App -include::partial$example/kotlinlib/web/7-todo-spring-boot.adoc[] \ No newline at end of file +This section was moved to the xref:kotlinlib/spring-boot.adoc#_spring_boot_todomvc_app[Spring Boot page]. + + + From c882c3325e6723b57b1d88cee455008c455ff635 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 12:56:52 +0100 Subject: [PATCH 05/17] Add kotlin version of repackage example --- .../9-repackage-config/bar/src/bar/Bar.kt | 15 ++++ .../bar/test/src/bar/BarTests.kt | 13 +++ .../publishing/9-repackage-config/build.mill | 80 +++++++++++++++++++ .../9-repackage-config/foo/src/foo/Foo.kt | 16 ++++ .../foo/test/src/foo/FooTests.kt | 13 +++ .../9-repackage-config/qux/src/qux/Qux.kt | 12 +++ .../ROOT/pages/kotlinlib/publishing.adoc | 5 ++ 7 files changed, 154 insertions(+) create mode 100644 example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt create mode 100644 example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt create mode 100644 example/kotlinlib/publishing/9-repackage-config/build.mill create mode 100644 example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt create mode 100644 example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt create mode 100644 example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt new file mode 100644 index 00000000000..378e30285a5 --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt @@ -0,0 +1,15 @@ +package bar + +import org.thymeleaf.TemplateEngine +import org.thymeleaf.context.Context + +class Bar { + companion object { + @JvmStatic + fun value() { + val context = Context() + context.setVariable("text", "world") + TemplateEngine().process("

", context) + } + } +} diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt new file mode 100644 index 00000000000..5353c12e559 --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt @@ -0,0 +1,13 @@ +package bar + +import org.junit.Assert.assertEquals + +import org.junit.Test + +public class BarTests { + + @Test + public void test() { + assertEquals(Bar.value(), "

world

") + +} diff --git a/example/kotlinlib/publishing/9-repackage-config/build.mill b/example/kotlinlib/publishing/9-repackage-config/build.mill new file mode 100644 index 00000000000..8d36c6e9f8b --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/build.mill @@ -0,0 +1,80 @@ +// An alternative way to produce self-executable assemblies is the `RepackageModule` which used the https://docs.spring.io/spring-boot/build-tool-plugin/index.html[Spring Boot Tools suite]. +// Instead of copying and merging dependencies classes and resources into a flat jar file, it embeds all dependencies as-is in the final jar. +// One of the pros of this approach is, that all dependency archives are kept unextracted, which makes later introspection for checksums, authorship and copyright questions easier. + +package build + +import mill.*, kotlinlib.*, publish.* +import mill.javalib.repackage.RepackageModule + +trait MyModule extends KotlinModule, PublishModule { + def kotlinVersion = "2.2.20" + + def publishVersion = "0.0.1" + + def pomSettings = PomSettings( + description = "Hello", + organization = "com.lihaoyi", + url = "https://github.com/lihaoyi/example", + licenses = Seq(License.MIT), + versionControl = VersionControl.github("lihaoyi", "example"), + developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi")) + ) + + def mvnDeps = Seq(mvn"org.thymeleaf:thymeleaf:3.1.1.RELEASE") + + object test extends JavaTests, TestModule.Junit4 +} + +object foo extends MyModule, RepackageModule { // <1> + def moduleDeps = Seq(bar, qux) +} + +object bar extends MyModule { + def moduleDeps = Seq(qux) +} + +object qux extends MyModule + +// <1> Add the `mill.javalib.repackage.RepackageModule` to the executable module. + +/** Usage + +> ./mill --version + +> ./mill foo.run +Foo.value:

hello

+Bar.value:

world

+Qux.value: 31337 + +> ./mill show foo.repackagedJar +".../out/foo/repackagedJar.dest/out.jar" + +> ./out/foo/repackagedJar.dest/out.jar +Foo.value:

hello

+Bar.value:

world

+Qux.value: 31337 + +> unzip -l ./out/foo/repackagedJar.dest/out.jar "BOOT-INF/lib*" +...BOOT-INF/lib/thymeleaf-3.1.1.RELEASE.jar +...BOOT-INF/lib/ognl-3.3.4.jar +...BOOT-INF/lib/attoparser-2.0.6.RELEASE.jar +...BOOT-INF/lib/unbescape-1.1.6.RELEASE.jar +...BOOT-INF/lib/slf4j-api-2.0.5.jar +...BOOT-INF/lib/javassist-3.29.0-GA.jar +...BOOT-INF/lib/qux-0.0.1.jar +...BOOT-INF/lib/bar-0.0.1.jar + +*/ + +// *Futher notes:* +// +// * a small wrapper application needs to be added, which is run as entry point and transparently manages loading the embedded jars and running your `main` method. +// This works for all Java (also Scala or Kotlin) applications. +// * It's not necessary to use the Spring Framework in the application. +// * The resulting jar is a self-executable application, but it might not suitable to be used on the classpath of other applications. + +// * Since the final jar produced with the `RepackageModule.repackagedJar` task often contains significantly less ZIP entries +// then the jar file produced with `.assembly`, it's possible to workaround +// an https://github.com/com-lihaoyi/mill/issues/2650[issue where `JavaModule.assembly` cannot produce executable assemblies] +// due to some JVM limitations in ZIP file handling of large files. diff --git a/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt b/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt new file mode 100644 index 00000000000..ee576d5e98e --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt @@ -0,0 +1,16 @@ +package foo + +public class Foo { + companion object { + val value = "

hello

" + + @JvmStatic + fun main(args: Array) { + println("Foo.value: " + Foo.value) + println("Bar.value: " + bar.Bar.value()) + println("Qux.value: " + qux.Qux.value) + } + } + +} + diff --git a/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt new file mode 100644 index 00000000000..85d49bbd352 --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt @@ -0,0 +1,13 @@ +package foo + +import org.junit.Assert.assertEquals + +import org.junit.Test + +public class FooTests { + + @Test + public void test() { + assertEquals(Foo.value, "

hello

") + } +} diff --git a/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt b/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt new file mode 100644 index 00000000000..aea73efaf90 --- /dev/null +++ b/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt @@ -0,0 +1,12 @@ +package qux + +public class Qux { + companion object { + val value = 31337 + + @JvmStatic + fun main(args: Array) { + println("Qux.value: " + Qux.value) + } + } +} diff --git a/website/docs/modules/ROOT/pages/kotlinlib/publishing.adoc b/website/docs/modules/ROOT/pages/kotlinlib/publishing.adoc index 8a65cbea62d..7a07dcfe732 100644 --- a/website/docs/modules/ROOT/pages/kotlinlib/publishing.adoc +++ b/website/docs/modules/ROOT/pages/kotlinlib/publishing.adoc @@ -10,6 +10,11 @@ This page will discuss common topics around packaging and publishing your Kotlin include::partial$example/kotlinlib/publishing/1-assembly-config.adoc[] +== Building `Repackage` Assemblies + +include::partial$example/kotlinlib/publishing/9-repackage-config.adoc[] + + == Building Native Image Binaries with Graal VM include::partial$example/kotlinlib/publishing/7-native-image.adoc[] From 11f95e910ac00f90f58a80d2f8dfb58b12e15211 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 14:50:18 +0100 Subject: [PATCH 06/17] Add repackage example for Scala --- .../9-repackage-config/bar/src/bar/Bar.scala | 12 +++ .../bar/test/src/bar/BarTests.java | 13 +++ .../publishing/9-repackage-config/build.mill | 79 +++++++++++++++++++ .../9-repackage-config/foo/src/foo/Foo.scala | 11 +++ .../foo/test/src/foo/FooTests.java | 13 +++ .../9-repackage-config/qux/src/qux/Qux.scala | 8 ++ .../ROOT/pages/scalalib/publishing.adoc | 4 + 7 files changed, 140 insertions(+) create mode 100644 example/scalalib/publishing/9-repackage-config/bar/src/bar/Bar.scala create mode 100644 example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java create mode 100644 example/scalalib/publishing/9-repackage-config/build.mill create mode 100644 example/scalalib/publishing/9-repackage-config/foo/src/foo/Foo.scala create mode 100644 example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java create mode 100644 example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala diff --git a/example/scalalib/publishing/9-repackage-config/bar/src/bar/Bar.scala b/example/scalalib/publishing/9-repackage-config/bar/src/bar/Bar.scala new file mode 100644 index 00000000000..dcc038d7035 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/bar/src/bar/Bar.scala @@ -0,0 +1,12 @@ +package bar + +import org.thymeleaf.TemplateEngine +import org.thymeleaf.context.Context + +object Bar { + def value: String = { + val context = Context() + context.setVariable("text", "world") + TemplateEngine().process("

", context) + } +} diff --git a/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java b/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java new file mode 100644 index 00000000000..74759a8b076 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java @@ -0,0 +1,13 @@ +package bar; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class BarTests { + + @Test + public void test() { + assertEquals(Bar.value(), "

world

"); + } +} diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill new file mode 100644 index 00000000000..0c858c11a77 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -0,0 +1,79 @@ +// An alternative way to produce self-executable assemblies is the `RepackageModule` which used the https://docs.spring.io/spring-boot/build-tool-plugin/index.html[Spring Boot Tools suite]. +// Instead of copying and merging dependencies classes and resources into a flat jar file, it embeds all dependencies as-is in the final jar. +// One of the pros of this approach is, that all dependency archives are kept unextracted, which makes later introspection for checksums, authorship and copyright questions easier. + +package build + +import mill.*, scalalib.*, publish.* +import mill.javalib.repackage.RepackageModule + +trait MyModule extends ScalaModule, PublishModule { + def scalaVersion = "3.7.3" + + def publishVersion = "0.0.1" + + def pomSettings = PomSettings( + description = "Hello", + organization = "com.lihaoyi", + url = "https://github.com/lihaoyi/example", + licenses = Seq(License.MIT), + versionControl = VersionControl.github("lihaoyi", "example"), + developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi")) + ) + + def mvnDeps = Seq(mvn"org.thymeleaf:thymeleaf:3.1.1.RELEASE") + + object test extends JavaTests, TestModule.Junit4 +} + +object foo extends MyModule, RepackageModule { // <1> + def moduleDeps = Seq(bar, qux) +} + +object bar extends MyModule { + def moduleDeps = Seq(qux) +} + +object qux extends MyModule + +// <1> Add the `mill.javalib.repackage.RepackageModule` to the executable module. + +/** Usage + +> ./mill foo.run +Foo.value:

hello

+Bar.value:

world

+Qux.value: 31337 + +> ./mill show foo.repackagedJar +".../out/foo/repackagedJar.dest/out.jar" + +> ./out/foo/repackagedJar.dest/out.jar +Foo.value:

hello

+Bar.value:

world

+Qux.value: 31337 + +> unzip -l ./out/foo/repackagedJar.dest/out.jar "BOOT-INF/lib*" +...BOOT-INF/lib/scala3-library_3-3.7.3.jar +...BOOT-INF/lib/thymeleaf-3.1.1.RELEASE.jar +...BOOT-INF/lib/ognl-3.3.4.jar +...BOOT-INF/lib/attoparser-2.0.6.RELEASE.jar +...BOOT-INF/lib/unbescape-1.1.6.RELEASE.jar +...BOOT-INF/lib/slf4j-api-2.0.5.jar +...BOOT-INF/lib/javassist-3.29.0-GA.jar +...BOOT-INF/lib/qux_3-0.0.1.jar +...BOOT-INF/lib/bar_3-0.0.1.jar + +*/ + +// *Futher notes:* +// +// * a small wrapper application needs to be added, which is run as entry point and transparently manages loading the embedded jars and running your `main` method. +// This works for all Java (also Scala or Kotlin) applications. +// * It's not necessary to use the Spring Framework in the application. +// * The resulting jar is a self-executable application, but it might not suitable to be used on the classpath of other applications. + +// * Since the final jar produced with the `RepackageModule.repackagedJar` task often contains significantly less ZIP entries +// then the jar file produced with `.assembly`, it's possible to workaround +// an https://github.com/com-lihaoyi/mill/issues/2650[issue where `JavaModule.assembly` cannot produce executable assemblies] +// due to some JVM limitations in ZIP file handling of large files. diff --git a/example/scalalib/publishing/9-repackage-config/foo/src/foo/Foo.scala b/example/scalalib/publishing/9-repackage-config/foo/src/foo/Foo.scala new file mode 100644 index 00000000000..aeb5f5b315a --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/foo/src/foo/Foo.scala @@ -0,0 +1,11 @@ +package foo + +object Foo { + val value = "

hello

" + + def main(args: Array[String]): Unit = { + println("Foo.value: " + Foo.value) + println("Bar.value: " + bar.Bar.value) + println("Qux.value: " + qux.Qux.value) + } +} diff --git a/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java b/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java new file mode 100644 index 00000000000..288d12e7cb9 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java @@ -0,0 +1,13 @@ +package foo; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class FooTests { + + @Test + public void test() { + assertEquals(Foo.value, "

hello

"); + } +} diff --git a/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala b/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala new file mode 100644 index 00000000000..184bfd41606 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala @@ -0,0 +1,8 @@ +package qux + +object Qux { + val value = 31337 + def main(args:Array[String]):Unit = { + println("Qux.value: "+Qux.value) + } +} diff --git a/website/docs/modules/ROOT/pages/scalalib/publishing.adoc b/website/docs/modules/ROOT/pages/scalalib/publishing.adoc index 210b8ba41b0..24585421f7d 100644 --- a/website/docs/modules/ROOT/pages/scalalib/publishing.adoc +++ b/website/docs/modules/ROOT/pages/scalalib/publishing.adoc @@ -9,6 +9,10 @@ This page will discuss common topics around packaging and publishing your Scala include::partial$example/scalalib/publishing/1-assembly-config.adoc[] +== Building `Repackage` Assemblies + +include::partial$example/scalalib/publishing/9-repackage-config.adoc[] + == Building Native Image Binaries with Graal VM From b3a9cf47bbb7cac5789fa9fd5688b8deebf78953 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 17:46:27 +0100 Subject: [PATCH 07/17] Fix snippet handling for languages specific examples / docs --- .../publishing/9-repackage-config/build.mill | 18 ++---------------- .../9-repackage-config/bar/src/bar/Bar.kt | 4 ++-- .../publishing/9-repackage-config/build.mill | 19 +++---------------- .../publishing/9-repackage-config/build.mill | 2 ++ .../ROOT/pages/javalib/spring-boot.adoc | 1 + .../ROOT/pages/kotlinlib/spring-boot.adoc | 1 + .../ROOT/pages/scalalib/spring-boot.adoc | 1 + .../modules/ROOT/partials/Spring_Header.adoc | 2 +- 8 files changed, 13 insertions(+), 35 deletions(-) diff --git a/example/javalib/publishing/9-repackage-config/build.mill b/example/javalib/publishing/9-repackage-config/build.mill index 70d599571de..7e4eb8899ba 100644 --- a/example/javalib/publishing/9-repackage-config/build.mill +++ b/example/javalib/publishing/9-repackage-config/build.mill @@ -1,7 +1,4 @@ -// An alternative way to produce self-executable assemblies is the `RepackageModule` which used the https://docs.spring.io/spring-boot/build-tool-plugin/index.html[Spring Boot Tools suite]. -// Instead of copying and merging dependencies classes and resources into a flat jar file, it embeds all dependencies as-is in the final jar. -// One of the pros of this approach is, that all dependency archives are kept unextracted, which makes later introspection for checksums, authorship and copyright questions easier. - +//// SNIPPET:BUILD package build import mill.*, javalib.*, publish.* @@ -62,15 +59,4 @@ Qux.value: 31337 ...BOOT-INF/lib/bar-0.0.1.jar */ - -// *Futher notes:* -// -// * a small wrapper application needs to be added, which is run as entry point and transparently manages loading the embedded jars and running your `main` method. -// This works for all Java (also Scala or Kotlin) applications. -// * It's not necessary to use the Spring Framework in the application. -// * The resulting jar is a self-executable application, but it might not suitable to be used on the classpath of other applications. - -// * Since the final jar produced with the `RepackageModule.repackagedJar` task often contains significantly less ZIP entries -// then the jar file produced with `.assembly`, it's possible to workaround -// an https://github.com/com-lihaoyi/mill/issues/2650[issue where `JavaModule.assembly` cannot produce executable assemblies] -// due to some JVM limitations in ZIP file handling of large files. +////SNIPPED:END diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt index 378e30285a5..de8252066ac 100644 --- a/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt +++ b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt @@ -6,10 +6,10 @@ import org.thymeleaf.context.Context class Bar { companion object { @JvmStatic - fun value() { + fun value(): String { val context = Context() context.setVariable("text", "world") - TemplateEngine().process("

", context) + return TemplateEngine().process("

", context) } } } diff --git a/example/kotlinlib/publishing/9-repackage-config/build.mill b/example/kotlinlib/publishing/9-repackage-config/build.mill index 8d36c6e9f8b..b32f9041d5f 100644 --- a/example/kotlinlib/publishing/9-repackage-config/build.mill +++ b/example/kotlinlib/publishing/9-repackage-config/build.mill @@ -1,7 +1,4 @@ -// An alternative way to produce self-executable assemblies is the `RepackageModule` which used the https://docs.spring.io/spring-boot/build-tool-plugin/index.html[Spring Boot Tools suite]. -// Instead of copying and merging dependencies classes and resources into a flat jar file, it embeds all dependencies as-is in the final jar. -// One of the pros of this approach is, that all dependency archives are kept unextracted, which makes later introspection for checksums, authorship and copyright questions easier. - +//// SNIPPET:BUILD package build import mill.*, kotlinlib.*, publish.* @@ -56,6 +53,7 @@ Bar.value:

world

Qux.value: 31337 > unzip -l ./out/foo/repackagedJar.dest/out.jar "BOOT-INF/lib*" +...BOOT-INF/lib/kotlin-stdlib-2.2.20.jar ...BOOT-INF/lib/thymeleaf-3.1.1.RELEASE.jar ...BOOT-INF/lib/ognl-3.3.4.jar ...BOOT-INF/lib/attoparser-2.0.6.RELEASE.jar @@ -66,15 +64,4 @@ Qux.value: 31337 ...BOOT-INF/lib/bar-0.0.1.jar */ - -// *Futher notes:* -// -// * a small wrapper application needs to be added, which is run as entry point and transparently manages loading the embedded jars and running your `main` method. -// This works for all Java (also Scala or Kotlin) applications. -// * It's not necessary to use the Spring Framework in the application. -// * The resulting jar is a self-executable application, but it might not suitable to be used on the classpath of other applications. - -// * Since the final jar produced with the `RepackageModule.repackagedJar` task often contains significantly less ZIP entries -// then the jar file produced with `.assembly`, it's possible to workaround -// an https://github.com/com-lihaoyi/mill/issues/2650[issue where `JavaModule.assembly` cannot produce executable assemblies] -// due to some JVM limitations in ZIP file handling of large files. +//// SNIPPET:END diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill index 0c858c11a77..d40feb21e45 100644 --- a/example/scalalib/publishing/9-repackage-config/build.mill +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -2,6 +2,7 @@ // Instead of copying and merging dependencies classes and resources into a flat jar file, it embeds all dependencies as-is in the final jar. // One of the pros of this approach is, that all dependency archives are kept unextracted, which makes later introspection for checksums, authorship and copyright questions easier. +//// SNIPPET:BUILD package build import mill.*, scalalib.*, publish.* @@ -65,6 +66,7 @@ Qux.value: 31337 ...BOOT-INF/lib/bar_3-0.0.1.jar */ +//// SNIPPET:END // *Futher notes:* // diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc index 46241eca367..e057740db16 100644 --- a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -1,5 +1,6 @@ = Spring Framework / Spring Boot Projects :page-aliases: Spring_Boot_Java.adoc +:language-small: java include::partial$Spring_Header.adoc[] diff --git a/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc index 1a36b1c9ab1..9f9ed15844c 100644 --- a/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/kotlinlib/spring-boot.adoc @@ -1,5 +1,6 @@ = Spring Framework / Spring Boot Projects :page-aliases: Spring_Boot_Kotlin.adoc +:language-small: kotlin include::partial$Spring_Header.adoc[] diff --git a/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc index f01185406b4..77b884d0748 100644 --- a/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/scalalib/spring-boot.adoc @@ -1,5 +1,6 @@ = Spring Framework / Spring Boot Projects :page-aliases: Spring_Boot_Scala.adoc +:language-small: scala include::partial$Spring_Header.adoc[] diff --git a/website/docs/modules/ROOT/partials/Spring_Header.adoc b/website/docs/modules/ROOT/partials/Spring_Header.adoc index 8f827753b09..c2f7179386c 100644 --- a/website/docs/modules/ROOT/partials/Spring_Header.adoc +++ b/website/docs/modules/ROOT/partials/Spring_Header.adoc @@ -24,5 +24,5 @@ Being a runtime framework also means, that most issues you may encounter like co Some tools provided by the Spring Boot project are also useful outside of Spring Boot projects, hence Mill makes them available even for non-Spring project. Those are linked below: -* `RepackageModule` - Creates self-running application assemblies in a single-jar +* xref:{language-small}lib/publishing.adoc#_building_repackage_assemblies[`RepackageModule`] - Creates self-running application assemblies in a single-jar From 5a63462795726cbb430ddd7f3a0ec26c21e7c0c1 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 20:25:00 +0100 Subject: [PATCH 08/17] Refined examples and tests --- .../publishing/9-repackage-config/build.mill | 4 ++++ .../bar/test/src/bar/BarTests.kt | 10 +++++----- .../publishing/9-repackage-config/build.mill | 8 +++++--- .../foo/test/src/foo/FooTests.kt | 10 +++++----- .../bar/test/src/bar/BarTests.java | 13 ------------- .../bar/test/src/bar/BarTests.scala | 14 ++++++++++++++ .../publishing/9-repackage-config/build.mill | 6 +++++- .../foo/test/src/foo/FooTests.java | 13 ------------- .../foo/test/src/foo/FooTests.scala | 13 +++++++++++++ 9 files changed, 51 insertions(+), 40 deletions(-) delete mode 100644 example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java create mode 100644 example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.scala delete mode 100644 example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java create mode 100644 example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.scala diff --git a/example/javalib/publishing/9-repackage-config/build.mill b/example/javalib/publishing/9-repackage-config/build.mill index 7e4eb8899ba..26d2b470631 100644 --- a/example/javalib/publishing/9-repackage-config/build.mill +++ b/example/javalib/publishing/9-repackage-config/build.mill @@ -40,6 +40,10 @@ Foo.value:

hello

Bar.value:

world

Qux.value: 31337 +> ./mill __.test +...Test run foo.FooTests finished: 0 failed, 0 ignored, 1 total, ...s +...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s + > ./mill show foo.repackagedJar ".../out/foo/repackagedJar.dest/out.jar" diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt index 5353c12e559..4b8c10c6eb2 100644 --- a/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt +++ b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt @@ -4,10 +4,10 @@ import org.junit.Assert.assertEquals import org.junit.Test -public class BarTests { - - @Test - public void test() { - assertEquals(Bar.value(), "

world

") +class BarTests { + @Test + fun test() { + assertEquals(Bar.value(), "

world

") + } } diff --git a/example/kotlinlib/publishing/9-repackage-config/build.mill b/example/kotlinlib/publishing/9-repackage-config/build.mill index b32f9041d5f..83a97c99ff1 100644 --- a/example/kotlinlib/publishing/9-repackage-config/build.mill +++ b/example/kotlinlib/publishing/9-repackage-config/build.mill @@ -20,7 +20,7 @@ trait MyModule extends KotlinModule, PublishModule { def mvnDeps = Seq(mvn"org.thymeleaf:thymeleaf:3.1.1.RELEASE") - object test extends JavaTests, TestModule.Junit4 + object test extends KotlinTests, TestModule.Junit4 } object foo extends MyModule, RepackageModule { // <1> @@ -37,13 +37,15 @@ object qux extends MyModule /** Usage -> ./mill --version - > ./mill foo.run Foo.value:

hello

Bar.value:

world

Qux.value: 31337 +> ./mill -j1 __.test +...Test run foo.FooTests finished: 0 failed, 0 ignored, 1 total, ...s +...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s + > ./mill show foo.repackagedJar ".../out/foo/repackagedJar.dest/out.jar" diff --git a/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt index 85d49bbd352..2101b9c213e 100644 --- a/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt +++ b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt @@ -4,10 +4,10 @@ import org.junit.Assert.assertEquals import org.junit.Test -public class FooTests { +class FooTests { - @Test - public void test() { - assertEquals(Foo.value, "

hello

") - } + @Test + fun test() { + assertEquals(Foo.value, "

hello

") + } } diff --git a/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java b/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java deleted file mode 100644 index 74759a8b076..00000000000 --- a/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package bar; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class BarTests { - - @Test - public void test() { - assertEquals(Bar.value(), "

world

"); - } -} diff --git a/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.scala b/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.scala new file mode 100644 index 00000000000..f6c02b2bea3 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/bar/test/src/bar/BarTests.scala @@ -0,0 +1,14 @@ +package bar + +import org.junit.Assert.assertEquals + +import org.junit.Test + +class BarTests { + + @Test + def test() = { + assertEquals(Bar.value, "

world

") + } + +} diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill index d40feb21e45..4fdb6e9aea2 100644 --- a/example/scalalib/publishing/9-repackage-config/build.mill +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -24,7 +24,7 @@ trait MyModule extends ScalaModule, PublishModule { def mvnDeps = Seq(mvn"org.thymeleaf:thymeleaf:3.1.1.RELEASE") - object test extends JavaTests, TestModule.Junit4 + object test extends ScalaTests, TestModule.Junit4 } object foo extends MyModule, RepackageModule { // <1> @@ -46,6 +46,10 @@ Foo.value:

hello

Bar.value:

world

Qux.value: 31337 + > ./mill -j1 __.test +...Test run foo.FooTests finished: 0 failed, 0 ignored, 1 total, ...s +...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s + > ./mill show foo.repackagedJar ".../out/foo/repackagedJar.dest/out.jar" diff --git a/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java b/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java deleted file mode 100644 index 288d12e7cb9..00000000000 --- a/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package foo; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class FooTests { - - @Test - public void test() { - assertEquals(Foo.value, "

hello

"); - } -} diff --git a/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.scala b/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.scala new file mode 100644 index 00000000000..8ff98af4167 --- /dev/null +++ b/example/scalalib/publishing/9-repackage-config/foo/test/src/foo/FooTests.scala @@ -0,0 +1,13 @@ +package foo + +import org.junit.Assert.assertEquals + +import org.junit.Test + +class FooTests { + + @Test + def test() = { + assertEquals(Foo.value, "

hello

") + } +} From 5c446a0df483e43b9cff5b83981f481c428e72f2 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:34:03 +0000 Subject: [PATCH 09/17] [autofix.ci] apply automated fixes --- .../9-repackage-config/bar/src/bar/Bar.kt | 14 +++++++------- .../bar/test/src/bar/BarTests.kt | 1 - .../9-repackage-config/foo/src/foo/Foo.kt | 2 -- .../foo/test/src/foo/FooTests.kt | 1 - .../9-repackage-config/qux/src/qux/Qux.kt | 2 +- .../9-repackage-config/qux/src/qux/Qux.scala | 4 ++-- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt index de8252066ac..0865a1b78bd 100644 --- a/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt +++ b/example/kotlinlib/publishing/9-repackage-config/bar/src/bar/Bar.kt @@ -4,12 +4,12 @@ import org.thymeleaf.TemplateEngine import org.thymeleaf.context.Context class Bar { - companion object { - @JvmStatic - fun value(): String { - val context = Context() - context.setVariable("text", "world") - return TemplateEngine().process("

", context) + companion object { + @JvmStatic + fun value(): String { + val context = Context() + context.setVariable("text", "world") + return TemplateEngine().process("

", context) + } } - } } diff --git a/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt index 4b8c10c6eb2..b5053f298db 100644 --- a/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt +++ b/example/kotlinlib/publishing/9-repackage-config/bar/test/src/bar/BarTests.kt @@ -1,7 +1,6 @@ package bar import org.junit.Assert.assertEquals - import org.junit.Test class BarTests { diff --git a/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt b/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt index ee576d5e98e..4158ab05e35 100644 --- a/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt +++ b/example/kotlinlib/publishing/9-repackage-config/foo/src/foo/Foo.kt @@ -11,6 +11,4 @@ public class Foo { println("Qux.value: " + qux.Qux.value) } } - } - diff --git a/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt index 2101b9c213e..1bd2ced9f69 100644 --- a/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt +++ b/example/kotlinlib/publishing/9-repackage-config/foo/test/src/foo/FooTests.kt @@ -1,7 +1,6 @@ package foo import org.junit.Assert.assertEquals - import org.junit.Test class FooTests { diff --git a/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt b/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt index aea73efaf90..02d55e224dc 100644 --- a/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt +++ b/example/kotlinlib/publishing/9-repackage-config/qux/src/qux/Qux.kt @@ -8,5 +8,5 @@ public class Qux { fun main(args: Array) { println("Qux.value: " + Qux.value) } - } + } } diff --git a/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala b/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala index 184bfd41606..5eaae1d06b5 100644 --- a/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala +++ b/example/scalalib/publishing/9-repackage-config/qux/src/qux/Qux.scala @@ -2,7 +2,7 @@ package qux object Qux { val value = 31337 - def main(args:Array[String]):Unit = { - println("Qux.value: "+Qux.value) + def main(args: Array[String]): Unit = { + println("Qux.value: " + Qux.value) } } From b719cc230e5787f533d3e44be8fdba5694caeece Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 23:01:42 +0100 Subject: [PATCH 10/17] . --- website/docs/modules/ROOT/pages/javalib/spring-boot.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc index e057740db16..e78e4edd0f7 100644 --- a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -4,10 +4,10 @@ include::partial$Spring_Header.adoc[] +// This page contains examples of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. -== Spring Boot Web with Initializr -// of providing Mill as a build tool for Spring Boot applications, adapting the official Spring Boot documentation in increasing complexity. +== Spring Boot Web with Initializr include::partial$example/springboot/java/1-web-initializr.adoc[] From cb637a7098f6d609ecd2b921fff2ce8bff1519e3 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 31 Oct 2025 23:07:15 +0100 Subject: [PATCH 11/17] Fix links --- website/docs/modules/ROOT/pages/javalib/spring-boot.adoc | 1 + website/docs/modules/ROOT/pages/javalib/web-examples.adoc | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc index e78e4edd0f7..c28b2a8245b 100644 --- a/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc +++ b/website/docs/modules/ROOT/pages/javalib/spring-boot.adoc @@ -18,6 +18,7 @@ include::partial$example/springboot/java/1-web-initializr.adoc[] include::partial$example/javalib/web/2-hello-spring-boot.adoc[] +[#_spring_boot_todomvc_app] == Spring Boot TodoMvc App diff --git a/website/docs/modules/ROOT/pages/javalib/web-examples.adoc b/website/docs/modules/ROOT/pages/javalib/web-examples.adoc index 982ce36ee71..d14cc25fc76 100644 --- a/website/docs/modules/ROOT/pages/javalib/web-examples.adoc +++ b/website/docs/modules/ROOT/pages/javalib/web-examples.adoc @@ -16,11 +16,10 @@ include::partial$example/javalib/web/1-hello-jetty.adoc[] This section was moved to the xref:javalib/spring-boot.adoc#_spring_boot_hello_world_app[Spring Boot page]. -include::partial$example/javalib/web/2-hello-spring-boot.adoc[] == Spring Boot TodoMvc App -This section was moved to the xref:javalib/spring-boot.adoc#_spring_boot_todomvc_app_[Spring Boot page]. +This section was moved to the xref:javalib/spring-boot.adoc#_spring_boot_todomvc_app[Spring Boot page]. [#_micronaut_hello_world_app] == Micronaut Hello World App From 49ee59c4b06cb38f5a47e3f0050530544b0440e3 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sat, 1 Nov 2025 10:45:37 +0100 Subject: [PATCH 12/17] fix --- example/scalalib/publishing/9-repackage-config/build.mill | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill index 4fdb6e9aea2..2c9c61bbea3 100644 --- a/example/scalalib/publishing/9-repackage-config/build.mill +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -46,7 +46,7 @@ Foo.value:

hello

Bar.value:

world

Qux.value: 31337 - > ./mill -j1 __.test +> ./mill -j1 __.test ...Test run foo.FooTests finished: 0 failed, 0 ignored, 1 total, ...s ...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s From 95518d46d8449762e92d714505de528bd97f388b Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sat, 1 Nov 2025 10:47:25 +0100 Subject: [PATCH 13/17] fix --- example/scalalib/publishing/9-repackage-config/build.mill | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill index 2c9c61bbea3..f49d3967fd8 100644 --- a/example/scalalib/publishing/9-repackage-config/build.mill +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -46,7 +46,7 @@ Foo.value:

hello

Bar.value:

world

Qux.value: 31337 -> ./mill -j1 __.test +> ./mill __.test ...Test run foo.FooTests finished: 0 failed, 0 ignored, 1 total, ...s ...Test run bar.BarTests finished: 0 failed, 0 ignored, 1 total, ...s From d0fa29db4030703afc0f1f52f6721218d92190cc Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sun, 2 Nov 2025 10:44:55 +0100 Subject: [PATCH 14/17] Make repackage example run under Java 11 --- .../scalalib/publishing/9-repackage-config/build.mill | 10 +++++++++- .../javalib/spring/boot/SpringBootToolsModule.scala | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/example/scalalib/publishing/9-repackage-config/build.mill b/example/scalalib/publishing/9-repackage-config/build.mill index f49d3967fd8..876fd8616a1 100644 --- a/example/scalalib/publishing/9-repackage-config/build.mill +++ b/example/scalalib/publishing/9-repackage-config/build.mill @@ -5,8 +5,9 @@ //// SNIPPET:BUILD package build -import mill.*, scalalib.*, publish.* +import mill.*, api.*, scalalib.*, publish.* import mill.javalib.repackage.RepackageModule +import mill.javalib.spring.boot.SpringBootToolsModule trait MyModule extends ScalaModule, PublishModule { def scalaVersion = "3.7.3" @@ -27,8 +28,15 @@ trait MyModule extends ScalaModule, PublishModule { object test extends ScalaTests, TestModule.Junit4 } +object SpringBootTools2 extends SpringBootToolsModule { + // Default version requires Java 17+, so downgrate to latest 2.x version + override def springBootToolsVersion = "2.7.18" + protected lazy val millDiscover = Discover[this.type] +} + object foo extends MyModule, RepackageModule { // <1> def moduleDeps = Seq(bar, qux) + override def springBootToolsModule = ModuleRef(SpringBootTools2) } object bar extends MyModule { diff --git a/libs/javalib/src/mill/javalib/spring/boot/SpringBootToolsModule.scala b/libs/javalib/src/mill/javalib/spring/boot/SpringBootToolsModule.scala index 803eb5cc287..83b7b2f8c96 100644 --- a/libs/javalib/src/mill/javalib/spring/boot/SpringBootToolsModule.scala +++ b/libs/javalib/src/mill/javalib/spring/boot/SpringBootToolsModule.scala @@ -12,6 +12,7 @@ trait SpringBootToolsModule extends CoursierModule { /** * The Spring-Boot tools version to use. * Defaults to the version which was used at built-time of this Mill release. + * Versions since `3.x` require at least Java 17. */ def springBootToolsVersion: T[String] = Task { Versions.springBuildToolsVersion From 02c9b5a4c6455d3aee5a3c658c3862fff03145c4 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sun, 2 Nov 2025 21:46:31 +0100 Subject: [PATCH 15/17] Fix RelocateModule using the wrong main class for older spring-boot-tools versions --- .../mill/javalib/repackage/RepackageModule.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala index 70fd1d6f13f..fbdf9c28dcb 100644 --- a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala +++ b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala @@ -33,9 +33,20 @@ trait RepackageModule extends mill.api.Module { */ def repackagePrependScript: T[Option[String]] = this match { case m: AssemblyModule => Task { + val mainClass = + if ( + mill.util.Version + .isAtLeast(springBootToolsModule().springBootToolsVersion(), "3.2.0-RC1") + (using mill.util.Version.MavenOrdering) + ) { + "org.springframework.boot.loader.launch.JarLauncher" + } else { + "org.springframework.boot.loader.JarLauncher" + } + Option( Jvm.launcherUniversalScript( - mainClass = "org.springframework.boot.loader.launch.JarLauncher", + mainClass = mainClass, shellClassPath = Seq("$0"), cmdClassPath = Seq("%~dpnx0"), shebang = false, From bc536f03e02a6ef09b99b528b3e03eb6f8837752 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sun, 2 Nov 2025 21:48:20 +0100 Subject: [PATCH 16/17] Add comment --- libs/javalib/src/mill/javalib/repackage/RepackageModule.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala index fbdf9c28dcb..23771f10c67 100644 --- a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala +++ b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala @@ -39,6 +39,7 @@ trait RepackageModule extends mill.api.Module { .isAtLeast(springBootToolsModule().springBootToolsVersion(), "3.2.0-RC1") (using mill.util.Version.MavenOrdering) ) { + // See issue: https://github.com/spring-projects/spring-boot/issues/37667 "org.springframework.boot.loader.launch.JarLauncher" } else { "org.springframework.boot.loader.JarLauncher" From f08731d0ff71de25f9c2e7ae71eb0672620ab343 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:57:13 +0000 Subject: [PATCH 17/17] [autofix.ci] apply automated fixes --- .../javalib/src/mill/javalib/repackage/RepackageModule.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala index 23771f10c67..dd8b775091b 100644 --- a/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala +++ b/libs/javalib/src/mill/javalib/repackage/RepackageModule.scala @@ -36,8 +36,9 @@ trait RepackageModule extends mill.api.Module { val mainClass = if ( mill.util.Version - .isAtLeast(springBootToolsModule().springBootToolsVersion(), "3.2.0-RC1") - (using mill.util.Version.MavenOrdering) + .isAtLeast(springBootToolsModule().springBootToolsVersion(), "3.2.0-RC1")(using + mill.util.Version.MavenOrdering + ) ) { // See issue: https://github.com/spring-projects/spring-boot/issues/37667 "org.springframework.boot.loader.launch.JarLauncher"