forked from sbt/sbt-native-packager
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support building Graal native images in docker
Closes sbt#1250 This provides support for building Graal native images in a docker container.
- Loading branch information
Showing
14 changed files
with
313 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 0 additions & 13 deletions
13
src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImageKeys.scala
This file was deleted.
Oops, something went wrong.
56 changes: 0 additions & 56 deletions
56
src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImagePlugin.scala
This file was deleted.
Oops, something went wrong.
17 changes: 17 additions & 0 deletions
17
src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.typesafe.sbt | ||
package packager | ||
package graalvmnativeimage | ||
|
||
import sbt._ | ||
|
||
/** | ||
* GraalVM settings | ||
*/ | ||
trait GraalVMNativeImageKeys { | ||
val graalVMNativeImageOptions = | ||
settingKey[Seq[String]]("GraalVM native-image options") | ||
|
||
val graalVMNativeImageGraalVersion = settingKey[Option[String]]( | ||
"Version of GraalVM to build with. Setting this has the effect of causing graalVMNativeImageBuilder to default to GeneratedDocker with the Oracle graalvm docker base image for this version." | ||
) | ||
} |
201 changes: 201 additions & 0 deletions
201
src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package com.typesafe.sbt.packager.graalvmnativeimage | ||
|
||
import sbt._ | ||
import sbt.Keys.{mainClass, name, _} | ||
import com.typesafe.sbt.packager.{MappingsHelper, Stager} | ||
import com.typesafe.sbt.packager.Keys._ | ||
import com.typesafe.sbt.packager.Compat._ | ||
import com.typesafe.sbt.packager.archetypes.JavaAppPackaging | ||
import com.typesafe.sbt.packager.docker.{Cmd, DockerPlugin, Dockerfile, ExecCmd} | ||
import com.typesafe.sbt.packager.universal.UniversalPlugin | ||
|
||
/** | ||
* Plugin to compile ahead-of-time native executables. | ||
* | ||
* @example Enable the plugin in the `build.sbt` | ||
* {{{ | ||
* enablePlugins(GraalVMNativeImagePlugin) | ||
* }}} | ||
*/ | ||
object GraalVMNativeImagePlugin extends AutoPlugin { | ||
|
||
object autoImport extends GraalVMNativeImageKeys { | ||
val GraalVMNativeImage: Configuration = config("graalvm-native-image") | ||
} | ||
|
||
import autoImport._ | ||
|
||
private val GraalVMBaseImage = "oracle/graalvm-ce" | ||
private val NativeImageCommand = "native-image" | ||
|
||
override def requires: Plugins = JavaAppPackaging | ||
|
||
override def projectConfigurations: Seq[Configuration] = Seq(GraalVMNativeImage) | ||
|
||
override lazy val projectSettings: Seq[Setting[_]] = Seq( | ||
target in GraalVMNativeImage := target.value / "graalvm-native-image", | ||
graalVMNativeImageOptions := Seq.empty, | ||
graalVMNativeImageGraalVersion := None, | ||
resourceDirectory in GraalVMNativeImage := sourceDirectory.value / "graal" | ||
) ++ inConfig(GraalVMNativeImage)(scopedSettings) | ||
|
||
private lazy val scopedSettings = Seq[Setting[_]]( | ||
resourceDirectories := Seq(resourceDirectory.value), | ||
includeFilter := "*", | ||
resources := resourceDirectories.value.descendantsExcept(includeFilter.value, excludeFilter.value).get, | ||
|
||
UniversalPlugin.autoImport.containerBuildImage := Def.taskDyn { | ||
graalVMNativeImageGraalVersion.value match { | ||
case Some(tag) => generateContainerBuildImage(s"$GraalVMBaseImage:$tag") | ||
case None => Def.task(None: Option[String]) | ||
} | ||
}.value, | ||
|
||
packageBin := { | ||
val targetDirectory = target.value | ||
val binaryName = name.value | ||
val className = mainClass.value.getOrElse(sys.error("Could not find a main class.")) | ||
val classpathJars = scriptClasspathOrdering.value | ||
val extraOptions = graalVMNativeImageOptions.value | ||
val streams = Keys.streams.value | ||
val dockerCommand = DockerPlugin.autoImport.dockerExecCommand.value | ||
val graalResourceDirectories = resourceDirectories.value | ||
val graalResources = resources.value | ||
|
||
UniversalPlugin.autoImport.containerBuildImage.value match { | ||
case None => | ||
buildLocal( | ||
targetDirectory, | ||
binaryName, | ||
className, | ||
classpathJars.map(_._1), | ||
extraOptions, | ||
streams.log | ||
) | ||
|
||
case Some(image) => | ||
|
||
val resourceMappings = MappingsHelper.relative(graalResources, graalResourceDirectories) | ||
|
||
buildInDockerContainer( | ||
targetDirectory, | ||
binaryName, | ||
className, | ||
classpathJars, | ||
extraOptions, | ||
dockerCommand, | ||
resourceMappings, | ||
image, | ||
streams | ||
) | ||
} | ||
} | ||
) | ||
|
||
private def buildLocal(targetDirectory: File, | ||
binaryName: String, | ||
className: String, | ||
classpathJars: Seq[File], | ||
extraOptions: Seq[String], | ||
log: ProcessLogger): File = { | ||
targetDirectory.mkdirs() | ||
val command = { | ||
val nativeImageArguments = { | ||
val classpath = classpathJars.mkString(":") | ||
Seq("--class-path", classpath, s"-H:Name=$binaryName") ++ extraOptions ++ Seq(className) | ||
} | ||
Seq(NativeImageCommand) ++ nativeImageArguments | ||
} | ||
sys.process.Process(command, targetDirectory) ! log match { | ||
case 0 => targetDirectory / binaryName | ||
case x => sys.error(s"Failed to run $command, exit status: " + x) | ||
} | ||
} | ||
|
||
private def buildInDockerContainer(targetDirectory: File, | ||
binaryName: String, | ||
className: String, | ||
classpathJars: Seq[(File, String)], | ||
extraOptions: Seq[String], | ||
dockerCommand: Seq[String], | ||
resources: Seq[(File, String)], | ||
image: String, | ||
streams: TaskStreams): File = { | ||
|
||
stage(targetDirectory, classpathJars, resources, streams) | ||
|
||
val command = dockerCommand ++ Seq( | ||
"run", | ||
"--rm", | ||
"-v", | ||
s"${targetDirectory.getAbsolutePath}:/opt/graalvm", | ||
image, | ||
"-cp", | ||
classpathJars.map(jar => "/opt/graalvm/stage/" + jar._2).mkString(":"), | ||
s"-H:Name=$binaryName" | ||
) ++ extraOptions ++ Seq(className) | ||
|
||
sys.process.Process(command) ! streams.log match { | ||
case 0 => targetDirectory / binaryName | ||
case x => sys.error(s"Failed to run $command, exit status: " + x) | ||
} | ||
} | ||
|
||
/** | ||
* This can be used to build a custom build image starting from a custom base image. Can be used like so: | ||
* | ||
* ``` | ||
* (containerBuildImage in GraalVMNativeImage) := generateContainerBuildImage("my-docker-hub-username/my-graalvm").value | ||
* ``` | ||
* | ||
* The passed in docker image must have GraalVM installed and on the PATH, including the gu utility. | ||
*/ | ||
def generateContainerBuildImage(baseImage: String): Def.Initialize[Task[Option[String]]] = Def.task { | ||
val dockerCommand = (DockerPlugin.autoImport.dockerExecCommand in GraalVMNativeImage).value | ||
val streams = Keys.streams.value | ||
val target = (Keys.target in GraalVMNativeImage).value | ||
|
||
val (baseName, tag) = baseImage.split(":", 2) match { | ||
case Array(n, t) => (n, t) | ||
case Array(n) => (n, "latest") | ||
} | ||
|
||
val imageName = s"${baseName.replace('/', '-')}-native-image:$tag" | ||
import sys.process._ | ||
if ((dockerCommand ++ Seq("image", "ls", imageName, "--quiet")).!!.trim.isEmpty) { | ||
streams.log.info(s"Generating new GraalVM native-image image based on $baseImage: $imageName") | ||
|
||
val dockerFile = target / "Dockerfile.graalvm-native-image" | ||
|
||
val dockerContent = Dockerfile( | ||
Cmd("FROM", baseImage), | ||
Cmd("WORKDIR", "/opt/graalvm"), | ||
ExecCmd("RUN", "gu", "install", "native-image"), | ||
ExecCmd("ENTRYPOINT", "native-image") | ||
).makeContent | ||
|
||
IO.write(dockerFile, dockerContent) | ||
|
||
DockerPlugin.publishLocalDocker( | ||
target, | ||
dockerCommand ++ Seq("build", "-f", dockerFile.getAbsolutePath, "-t", imageName, "."), | ||
streams.log | ||
) | ||
} else { | ||
streams.log.info(s"Using existing GraalVM native-image image: $imageName") | ||
} | ||
|
||
Some(imageName) | ||
} | ||
|
||
private def stage(targetDirectory: File, | ||
classpathJars: Seq[(File, String)], | ||
resources: Seq[(File, String)], | ||
streams: TaskStreams): File = { | ||
val stageDir = targetDirectory / "stage" | ||
val mappings = classpathJars ++ resources.map { | ||
case (resource, path) => resource -> s"resources/$path" | ||
} | ||
Stager.stage(GraalVMBaseImage)(streams, stageDir, mappings) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
src/sbt-test/graalvm-native-image/docker-native-image/build.sbt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
enablePlugins(GraalVMNativeImagePlugin) | ||
|
||
name := "docker-test" | ||
version := "0.1.0" | ||
graalVMNativeImageGraalVersion := Some("19.0.0") |
1 change: 1 addition & 0 deletions
1
src/sbt-test/graalvm-native-image/docker-native-image/project/plugins.sbt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) |
5 changes: 5 additions & 0 deletions
5
src/sbt-test/graalvm-native-image/docker-native-image/src/main/scala/Main.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
object Main { | ||
def main(args: Array[String]): Unit = { | ||
println("Hello Graal") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Generate the GraalVM native image | ||
> show graalvm-native-image:packageBin | ||
$ exec bash -c 'target/graalvm-native-image/docker-test | grep -q "Hello Graal"' |
Oops, something went wrong.