From 118ce34edd0dbe863030afea133af60dd0399537 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Thu, 6 Jun 2024 23:37:19 +0200 Subject: [PATCH 1/4] Experiment with jpackage to create executable binaries --- .../scalamodule/10-assembly-config/build.sc | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/example/scalamodule/10-assembly-config/build.sc b/example/scalamodule/10-assembly-config/build.sc index 2ac91b0f4a5..4ed13fbbce8 100644 --- a/example/scalamodule/10-assembly-config/build.sc +++ b/example/scalamodule/10-assembly-config/build.sc @@ -1,7 +1,27 @@ import mill._, scalalib._ import mill.scalalib.Assembly._ -object foo extends ScalaModule { +trait Jpackage extends JavaModule { + def jpackageName: T[String] = artifactName + def jpackage: T[PathRef] = T { + val localCp = localClasspath().map(_.path) + val runCp = runClasspath().map(_.path) + val mainJar = jar().path + val cp = runCp.filterNot(localCp.contains) ++ Seq(mainJar) + + val libs = T.dest / "lib" + cp.filter(os.exists).foreach { p => + os.copy.into(p, libs, createFolders = true) + } + val appName = jpackageName() + val outDest = T.dest / "out" + os.makeDir.all(outDest) + os.proc("jpackage", "--type", "app-image", "--name", appName, "--input", libs, "--main-jar", mainJar.last).call(cwd = outDest) + PathRef(outDest / appName / "bin" / appName) + } +} + +object foo extends ScalaModule with Jpackage { def moduleDeps = Seq(bar) def scalaVersion = "2.13.8" def ivyDeps = Agg(ivy"com.lihaoyi::os-lib:0.9.1") @@ -43,4 +63,10 @@ Loaded application.conf from resources:... ...Foo Application Conf ...Bar Application Conf +> mill show foo.jpackage +".../out/foo/jpackage.dest/out/foo/bin/foo" + +> ./out/foo/jpackage.dest/out/foo/bin/foo +Loaded application.conf from resources: Foo Application Conf + */ \ No newline at end of file From 9101e2f1e14ab093373cee2e176bdc37587112cd Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Fri, 7 Jun 2024 10:02:05 +0200 Subject: [PATCH 2/4] . --- .../scalamodule/10-assembly-config/build.sc | 20 +----- .../10-assembly-config/mill-build/build.sc | 1 + .../mill-build/src/Jpackage.scala | 72 +++++++++++++++++++ 3 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 example/scalamodule/10-assembly-config/mill-build/build.sc create mode 100644 example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala diff --git a/example/scalamodule/10-assembly-config/build.sc b/example/scalamodule/10-assembly-config/build.sc index 4ed13fbbce8..70101aab33e 100644 --- a/example/scalamodule/10-assembly-config/build.sc +++ b/example/scalamodule/10-assembly-config/build.sc @@ -1,25 +1,9 @@ import mill._, scalalib._ import mill.scalalib.Assembly._ -trait Jpackage extends JavaModule { - def jpackageName: T[String] = artifactName - def jpackage: T[PathRef] = T { - val localCp = localClasspath().map(_.path) - val runCp = runClasspath().map(_.path) - val mainJar = jar().path - val cp = runCp.filterNot(localCp.contains) ++ Seq(mainJar) +import $meta.`mill-build` - val libs = T.dest / "lib" - cp.filter(os.exists).foreach { p => - os.copy.into(p, libs, createFolders = true) - } - val appName = jpackageName() - val outDest = T.dest / "out" - os.makeDir.all(outDest) - os.proc("jpackage", "--type", "app-image", "--name", appName, "--input", libs, "--main-jar", mainJar.last).call(cwd = outDest) - PathRef(outDest / appName / "bin" / appName) - } -} +import jpackage.Jpackage object foo extends ScalaModule with Jpackage { def moduleDeps = Seq(bar) diff --git a/example/scalamodule/10-assembly-config/mill-build/build.sc b/example/scalamodule/10-assembly-config/mill-build/build.sc new file mode 100644 index 00000000000..84b724a2aaf --- /dev/null +++ b/example/scalamodule/10-assembly-config/mill-build/build.sc @@ -0,0 +1 @@ +object root extends mill.runner.MillBuildRootModule diff --git a/example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala b/example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala new file mode 100644 index 00000000000..b7d8530d987 --- /dev/null +++ b/example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala @@ -0,0 +1,72 @@ +package jpackage + +import mill._ +import mill.scalalib.JavaModule + +trait Jpackage extends JavaModule { + + /** The application name */ + def jpackageName: T[String] = T { artifactName() } + + def jpackageMainClass: T[String] = T { finalMainClass() } + + def transitiveJars: T[Seq[PathRef]] = T { + T.traverse(transitiveModuleCompileModuleDeps)(_.jar)() + } + + /** + * The classpath used for the jpackage tool. The first entry is the main jar. + * In difference to [[runClasspath]], it contains the built jars of all dependent modules. + */ + def jpackageRunClasspath: T[Seq[PathRef]] = T { + val recLocalClasspath = (localClasspath() ++ transitiveLocalClasspath()).map(_.path) + + val runCp = runClasspath().filterNot(pr => recLocalClasspath.contains(pr.path)) + + val mainJar = jar() + val recJars = transitiveJars() + + mainJar +: (recJars ++ runCp) + } + + /** Builds a native binary from the main application. */ + def jpackage: T[PathRef] = T { + // materialize all jars into a "lib" dir + val libs = T.dest / "lib" + val cp = jpackageRunClasspath().map(_.path) + var counter = 1 + val jars = cp.filter(os.exists).map { p => + val dest = libs / s"${counter}-${p.last}" + os.copy(p, dest, createFolders = true) + counter += 1 + dest + } + + val appName = jpackageName() + val mainClass = jpackageMainClass() + val mainJarName = jars.head.last + + // TODO: runtime java options, e.g. env and stuff + + val args: Seq[String] = Seq( + "jpackage", + "--type", + "app-image", + "--name", + appName, + "--input", + libs.toString(), + "--main-jar", + mainJarName, + "--main-class", + mainClass + ) + + // run jpackage tool + val outDest = T.dest / "out" + os.makeDir.all(outDest) + os.proc(args).call(cwd = outDest) + PathRef(outDest / appName / "bin" / appName) + } + +} From cffc3de430b8b548c7b71712f952526b79087724 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Mon, 10 Jun 2024 11:46:18 +0200 Subject: [PATCH 3/4] Drafted a `JackageModule` in `scalalib`. --- .../scalamodule/10-assembly-config/build.sc | 10 +++----- .../10-assembly-config/mill-build/build.sc | 1 - scalalib/src/mill/scalalib/JavaModule.scala | 9 +++++++- .../src/mill/scalalib/JpackageModule.scala | 23 +++++++++---------- 4 files changed, 22 insertions(+), 21 deletions(-) delete mode 100644 example/scalamodule/10-assembly-config/mill-build/build.sc rename example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala => scalalib/src/mill/scalalib/JpackageModule.scala (73%) diff --git a/example/scalamodule/10-assembly-config/build.sc b/example/scalamodule/10-assembly-config/build.sc index 70101aab33e..887187c8888 100644 --- a/example/scalamodule/10-assembly-config/build.sc +++ b/example/scalamodule/10-assembly-config/build.sc @@ -1,11 +1,7 @@ import mill._, scalalib._ import mill.scalalib.Assembly._ -import $meta.`mill-build` - -import jpackage.Jpackage - -object foo extends ScalaModule with Jpackage { +object foo extends ScalaModule with JpackageModule { def moduleDeps = Seq(bar) def scalaVersion = "2.13.8" def ivyDeps = Agg(ivy"com.lihaoyi::os-lib:0.9.1") @@ -48,9 +44,9 @@ Loaded application.conf from resources:... ...Bar Application Conf > mill show foo.jpackage -".../out/foo/jpackage.dest/out/foo/bin/foo" +".../out/foo/jpackage.dest/image" -> ./out/foo/jpackage.dest/out/foo/bin/foo +> ./out/foo/jpackage.dest/image/foo/bin/foo Loaded application.conf from resources: Foo Application Conf */ \ No newline at end of file diff --git a/example/scalamodule/10-assembly-config/mill-build/build.sc b/example/scalamodule/10-assembly-config/mill-build/build.sc deleted file mode 100644 index 84b724a2aaf..00000000000 --- a/example/scalamodule/10-assembly-config/mill-build/build.sc +++ /dev/null @@ -1 +0,0 @@ -object root extends mill.runner.MillBuildRootModule diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index 2bf618462d9..e515f910f24 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -279,12 +279,19 @@ trait JavaModule } /** - * The transitive version of `localClasspath` + * The transitive version of [[localClasspath]]. */ def transitiveLocalClasspath: T[Agg[PathRef]] = T { T.traverse(transitiveModuleCompileModuleDeps)(_.localClasspath)().flatten } + /** + * Almost the same as [[transitiveLocalClasspath]], but using the [[jar]]s instead of [[localClasspath]]. + */ + def transitiveJars: T[Seq[PathRef]] = T { + T.traverse(transitiveModuleCompileModuleDeps)(_.jar)() + } + /** * Same as [[transitiveLocalClasspath]], but with all dependencies on [[compile]] * replaced by their non-compiling [[bspCompileClassesPath]] variants. diff --git a/example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala b/scalalib/src/mill/scalalib/JpackageModule.scala similarity index 73% rename from example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala rename to scalalib/src/mill/scalalib/JpackageModule.scala index b7d8530d987..ab3b6720f03 100644 --- a/example/scalamodule/10-assembly-config/mill-build/src/Jpackage.scala +++ b/scalalib/src/mill/scalalib/JpackageModule.scala @@ -1,21 +1,20 @@ -package jpackage +package mill.scalalib import mill._ -import mill.scalalib.JavaModule -trait Jpackage extends JavaModule { +/** + * Support building native binaries with ` jpackage` tools, which chomes bundles with Java SDK since version 16. + */ +trait JpackageModule extends JavaModule { /** The application name */ def jpackageName: T[String] = T { artifactName() } + /** The main class to use as the native binary entry point. */ def jpackageMainClass: T[String] = T { finalMainClass() } - def transitiveJars: T[Seq[PathRef]] = T { - T.traverse(transitiveModuleCompileModuleDeps)(_.jar)() - } - /** - * The classpath used for the jpackage tool. The first entry is the main jar. + * The classpath used for the `jpackage`` tool. The first entry needs to be the main jar. * In difference to [[runClasspath]], it contains the built jars of all dependent modules. */ def jpackageRunClasspath: T[Seq[PathRef]] = T { @@ -29,8 +28,8 @@ trait Jpackage extends JavaModule { mainJar +: (recJars ++ runCp) } - /** Builds a native binary from the main application. */ - def jpackage: T[PathRef] = T { + /** Builds a native binary image of the main application. */ + def jpackageAppImage: T[PathRef] = T { // materialize all jars into a "lib" dir val libs = T.dest / "lib" val cp = jpackageRunClasspath().map(_.path) @@ -63,10 +62,10 @@ trait Jpackage extends JavaModule { ) // run jpackage tool - val outDest = T.dest / "out" + val outDest = T.dest / "image" os.makeDir.all(outDest) os.proc(args).call(cwd = outDest) - PathRef(outDest / appName / "bin" / appName) + PathRef(outDest) } } From fcad97b3d51be083fe4791dcc9029166ab0f0ec7 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Tue, 11 Jun 2024 03:44:41 +0200 Subject: [PATCH 4/4] . --- example/scalamodule/10-assembly-config/build.sc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/scalamodule/10-assembly-config/build.sc b/example/scalamodule/10-assembly-config/build.sc index 887187c8888..086681f3ce8 100644 --- a/example/scalamodule/10-assembly-config/build.sc +++ b/example/scalamodule/10-assembly-config/build.sc @@ -43,10 +43,10 @@ Loaded application.conf from resources:... ...Foo Application Conf ...Bar Application Conf -> mill show foo.jpackage -".../out/foo/jpackage.dest/image" +> mill show foo.jpackageAppImage +".../out/foo/jpackageAppImage.dest/image" -> ./out/foo/jpackage.dest/image/foo/bin/foo +> ./out/foo/jpackageAppImage.dest/image/foo/bin/foo Loaded application.conf from resources: Foo Application Conf */ \ No newline at end of file