All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mill.scalalib.JlinkModule.scala Maven / Gradle / Ivy

The newest version!
package mill
package scalalib

import mill._
import mill.util.Jvm

/**
 * Support building modular runtime images with the `jlink` tool, which is included in JDK 9 and later.
 *
 * The official `jlink` docs: https://docs.oracle.com/en/java/javase/23/docs/specs/man/jlink.html
 */
trait JlinkModule extends JavaModule {

  /** The base name for the runtime image */
  def jlinkImageName: T[String] = Task { "jlink" }

  /** Name of the main module to be included in the runtime image */
  def jlinkModuleName: T[String] = Task { "" }

  /** The main module's version number. */
  def jlinkModuleVersion: T[Option[String]] = Task { None }

  /** The main class to use as the runtime entry point. */
  def jlinkMainClass: T[String] = Task { finalMainClass() }

  /**
   * Compress level for the runtime image.
   * On newer versions of OpenJDK, valid values range between:
   *  "zip-0" (no compression) and "zip-9" (best compression).
   *
   * On all versions of Oracle's JDK, valid values range between:
   *  0 (no compression), 1 (constant string sharing) and 2 (ZIP).
   *
   * Assumes you are on a recent OpenJDK version thus defaults to "zip-6".
   */
  def jlinkCompressLevel: T[String] = Task { "zip-6" }

  /**
   * Creates a Java module file (.jmod) from compiled classes
   */
  def jmodPackage: T[PathRef] = Task {

    val mainClass: String = finalMainClass()
    val outputPath = Task.dest / "jlink.jmod"

    val libs = Task.dest / "libs"
    val cp = runClasspath().map(_.path)
    val jars = cp.filter(os.exists).zipWithIndex.map { case (p, idx) =>
      val dest = libs / s"${p.last}"
      os.copy(p, dest, createFolders = true)
      dest
    }

    val classPath = jars.map(_.toString).mkString(sys.props("path.separator"))
    val args = {
      val baseArgs = Seq(
        Jvm.jdkTool("jmod", this.zincWorker().javaHome().map(_.path)),
        "create",
        "--class-path",
        classPath.toString,
        "--main-class",
        mainClass,
        "--module-path",
        classPath.toString,
        outputPath.toString
      )

      val versionArgs = jlinkModuleVersion().toSeq.flatMap { version =>
        Seq("--module-version", version)
      }

      baseArgs ++ versionArgs
    }
    os.proc(args).call()

    PathRef(outputPath)
  }

  /** Builds a custom runtime image using jlink */
  def jlinkAppImage: T[PathRef] = Task {
    val modulePath = jmodPackage().path.toString
    val outputPath = Task.dest / "jlink-runtime"

    val args = Seq(
      Jvm.jdkTool("jlink", this.zincWorker().javaHome().map(_.path)),
      "--launcher",
      s"${jlinkImageName()}=${jlinkModuleName()}/${jlinkMainClass()}",
      "--module-path",
      modulePath,
      "--add-modules",
      jlinkModuleName(),
      "--output",
      outputPath.toString,
      "--compress",
      jlinkCompressLevel().toString,
      "--no-header-files",
      "--no-man-pages"
    )
    os.proc(args).call()

    PathRef(outputPath)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy