diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml
index e2eddd119..177e8380b 100644
--- a/.github/workflows/validate-pr.yml
+++ b/.github/workflows/validate-pr.yml
@@ -252,11 +252,15 @@ jobs:
restore-keys: |
${{ runner.os }}-ivy-
- name: Setup GraalVM environment
- uses: olafurpg/setup-scala@v10
+ uses: graalvm/setup-graalvm@v1
with:
- java-version: graalvm@22.0.0=tgz+https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.0.0.2/graalvm-ce-java11-linux-amd64-22.0.0.2.tar.gz
- - name: Install native-image
- run: gu install native-image
+ java-version: 17.0.8
+ distribution: 'graalvm'
+ cache: 'sbt'
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Docker Setup Buildx
+ uses: docker/setup-buildx-action@v2.9.1
- name: Validate
run: sbt "^validateGraalVMNativeImage"
diff --git a/.gitignore b/.gitignore
index 96a52da37..a0d11fa9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,5 @@ target/
.ensime*
.bloop/*
.metals/*
+
+.bsp/
diff --git a/build.sbt b/build.sbt
index 489c4149b..8556db2cd 100644
--- a/build.sbt
+++ b/build.sbt
@@ -3,10 +3,10 @@ organization := "com.github.sbt"
homepage := Some(url("https://github.com/sbt/sbt-native-packager"))
Global / onChangedBuildSource := ReloadOnSourceChanges
-Global / scalaVersion := "2.12.12"
+Global / scalaVersion := "2.12.13"
// crossBuildingSettings
-crossSbtVersions := Vector("1.1.6")
+crossSbtVersions := Vector("1.9.3")
Compile / scalacOptions ++= Seq("-deprecation")
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
diff --git a/project/build.properties b/project/build.properties
index 9edb75b77..52413ab79 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=1.5.4
+sbt.version=1.9.3
diff --git a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala
index 8460b265d..01264be6b 100644
--- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala
+++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala
@@ -18,4 +18,8 @@ trait GraalVMNativeImageKeys {
trait GraalVMNativeImageKeysEx extends GraalVMNativeImageKeys {
val graalVMNativeImageCommand = settingKey[String]("GraalVM native-image executable command")
+
+ val graalVMNativeImagePlatformArch = settingKey[Option[String]](
+ "Platform architecture of GraalVM to build with. This only works when building the native image with a container."
+ )
}
diff --git a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala
index c3ba42ab3..016b4e6ff 100644
--- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala
+++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala
@@ -27,7 +27,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
import autoImport._
- private val GraalVMBaseImage = "ghcr.io/graalvm/graalvm-ce"
+ private val GraalVMBaseImagePath = "ghcr.io/graalvm/"
override def requires: Plugins = JavaAppPackaging
@@ -37,6 +37,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
target in GraalVMNativeImage := target.value / "graalvm-native-image",
graalVMNativeImageOptions := Seq.empty,
graalVMNativeImageGraalVersion := None,
+ graalVMNativeImagePlatformArch := None,
graalVMNativeImageCommand := (if (scala.util.Properties.isWin) "native-image.cmd" else "native-image"),
resourceDirectory in GraalVMNativeImage := sourceDirectory.value / "graal",
mainClass in GraalVMNativeImage := (mainClass in Compile).value
@@ -47,9 +48,22 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
includeFilter := "*",
resources := resourceDirectories.value.descendantsExcept(includeFilter.value, excludeFilter.value).get,
UniversalPlugin.autoImport.containerBuildImage := Def.taskDyn {
+ val splitPackageVersion = "(.*):(.*)".r
graalVMNativeImageGraalVersion.value match {
- case Some(tag) => generateContainerBuildImage(s"$GraalVMBaseImage:$tag")
- case None => Def.task(None: Option[String])
+ case Some(splitPackageVersion(packageName, tag)) =>
+ packageName match {
+ case "native-image-community" | "native-image" =>
+ Def.task(Some(s"$GraalVMBaseImagePath$packageName:$tag"): Option[String])
+ case "graalvm-community" | "graalvm-ce" =>
+ generateContainerBuildImage(
+ s"${GraalVMBaseImagePath}$packageName:$tag",
+ graalVMNativeImagePlatformArch.value
+ )
+ case _ => sys.error("Other ghcr.io/graalvm images are unsupported")
+ }
+ case Some(tag) =>
+ generateContainerBuildImage(s"${GraalVMBaseImagePath}graalvm-ce:$tag", graalVMNativeImagePlatformArch.value)
+ case None => Def.task(None: Option[String])
}
}.value,
packageBin := {
@@ -59,6 +73,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
val className = mainClass.value.getOrElse(sys.error("Could not find a main class."))
val classpathJars = scriptClasspathOrdering.value
val extraOptions = graalVMNativeImageOptions.value
+ val platformArch = graalVMNativeImagePlatformArch.value
val streams = Keys.streams.value
val dockerCommand = DockerPlugin.autoImport.dockerExecCommand.value
val graalResourceDirectories = resourceDirectories.value
@@ -85,6 +100,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
className,
classpathJars,
extraOptions,
+ platformArch,
dockerCommand,
resourceMappings,
image,
@@ -131,23 +147,29 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
className: String,
classpathJars: Seq[(File, String)],
extraOptions: Seq[String],
+ platformArch: Option[String],
dockerCommand: Seq[String],
resources: Seq[(File, String)],
image: String,
streams: TaskStreams
): File = {
-
+ import sys.process._
stage(targetDirectory, classpathJars, resources, streams)
val graalDestDir = "/opt/graalvm"
val stageDestDir = s"$graalDestDir/stage"
val resourcesDestDir = s"$stageDestDir/resources"
+ val hostPlatform =
+ (dockerCommand ++ Seq("system", "info", "--format", "{{.OSType}}/{{.Architecture}}")).!!.trim
+ .replace("x86_64", "amd64")
val command = dockerCommand ++ Seq(
"run",
"--workdir",
"/opt/graalvm",
"--rm",
+ "--platform",
+ platformArch.getOrElse(hostPlatform),
"-v",
s"${targetDirectory.getAbsolutePath}:$graalDestDir",
image,
@@ -166,15 +188,24 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
* 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
+ * (containerBuildImage in GraalVMNativeImage) := generateContainerBuildImage("my-docker-hub-username/my-graalvm", Some("arm64")).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 generateContainerBuildImage(
+ baseImage: String,
+ platformArch: Option[String] = None
+ ): Def.Initialize[Task[Option[String]]] =
Def.task {
+ import sys.process._
+
val dockerCommand = (DockerPlugin.autoImport.dockerExecCommand in GraalVMNativeImage).value
val streams = Keys.streams.value
+ val hostPlatform =
+ (dockerCommand ++ Seq("system", "info", "--format", "{{.OSType}}/{{.Architecture}}")).!!.trim
+ .replace("x86_64", "amd64")
+ val platformValue = platformArch.getOrElse(hostPlatform)
val (baseName, tag) = baseImage.split(":", 2) match {
case Array(n, t) => (n, t)
@@ -182,8 +213,17 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
}
val imageName = s"${baseName.replace('/', '-')}-native-image:$tag"
+
import sys.process._
- if ((dockerCommand ++ Seq("image", "ls", imageName, "--quiet")).!!.trim.isEmpty) {
+ val buildContainerExists = (dockerCommand ++ Seq(
+ "image",
+ "ls",
+ "--filter",
+ s"label=com.typesafe.sbt.packager.graalvmnativeimage.platform=$platformValue",
+ "--quiet",
+ imageName
+ )).!!.trim.nonEmpty
+ if (!buildContainerExists) {
streams.log.info(s"Generating new GraalVM native-image image based on $baseImage: $imageName")
val dockerContent = Dockerfile(
@@ -194,9 +234,29 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
ExecCmd("ENTRYPOINT", "native-image")
).makeContent
- val command = dockerCommand ++ Seq("build", "-t", imageName, "-")
-
- val ret = sys.process.Process(command) #<
+ val buildCommand = dockerCommand ++ Seq(
+ "build",
+ "--label",
+ s"com.typesafe.sbt.packager.graalvmnativeimage.platform=$platformValue",
+ "-t",
+ imageName,
+ "-"
+ )
+
+ val buildxCommand = dockerCommand ++ Seq(
+ "buildx",
+ "build",
+ "--platform",
+ platformValue,
+ "--load",
+ "--label",
+ s"com.typesafe.sbt.packager.graalvmnativeimage.platform=$platformValue",
+ "-t",
+ imageName,
+ "-"
+ )
+
+ val ret = sys.process.Process(platformArch.map(_ => buildxCommand).getOrElse(buildCommand)) #<
new ByteArrayInputStream(dockerContent.getBytes()) !
DockerPlugin.publishLocalLogger(streams.log)
@@ -219,7 +279,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin {
val mappings = classpathJars ++ resources.map {
case (resource, path) => resource -> s"resources/$path"
}
- Stager.stage(GraalVMBaseImage)(streams, stageDir, mappings)
+ Stager.stage(GraalVMBaseImagePath)(streams, stageDir, mappings)
}
}
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-arm64/build.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/build.sbt
new file mode 100644
index 000000000..bd23343a2
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/build.sbt
@@ -0,0 +1,7 @@
+enablePlugins(GraalVMNativeImagePlugin)
+
+name := "docker-test"
+version := "0.1.0"
+graalVMNativeImageOptions := Seq("--no-fallback")
+graalVMNativeImageGraalVersion := Some("native-image-community:17.0.8")
+graalVMNativeImagePlatformArch := Some("arm64")
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-arm64/project/plugins.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/project/plugins.sbt
new file mode 100644
index 000000000..218f1a27d
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.github.sbt" % "sbt-native-packager" % sys.props("project.version"))
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-arm64/src/main/scala/Main.scala b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/src/main/scala/Main.scala
new file mode 100644
index 000000000..43c2459f3
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/src/main/scala/Main.scala
@@ -0,0 +1,5 @@
+object Main {
+ def main(args: Array[String]): Unit = {
+ println("Hello Graal")
+ }
+}
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-arm64/test b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/test
new file mode 100644
index 000000000..f4e58abe3
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-arm64/test
@@ -0,0 +1,3 @@
+# Generate the GraalVM native image
+> show graalvm-native-image:packageBin
+$ exec bash -c 'docker run --rm --platform arm64 -v .:/test -w /test ubuntu ./target/graalvm-native-image/docker-test | grep -q "Hello Graal"'
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/build.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/build.sbt
new file mode 100644
index 000000000..38b1dd18c
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/build.sbt
@@ -0,0 +1,6 @@
+enablePlugins(GraalVMNativeImagePlugin)
+
+name := "docker-test"
+version := "0.1.0"
+graalVMNativeImageOptions := Seq("--no-fallback")
+graalVMNativeImageGraalVersion := Some("native-image:22.3.3")
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/project/plugins.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/project/plugins.sbt
new file mode 100644
index 000000000..218f1a27d
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.github.sbt" % "sbt-native-packager" % sys.props("project.version"))
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/src/main/scala/Main.scala b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/src/main/scala/Main.scala
new file mode 100644
index 000000000..43c2459f3
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/src/main/scala/Main.scala
@@ -0,0 +1,5 @@
+object Main {
+ def main(args: Array[String]): Unit = {
+ println("Hello Graal")
+ }
+}
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/test b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/test
new file mode 100644
index 000000000..3d6230bcd
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy-specify-package/test
@@ -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"'
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy/build.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/build.sbt
new file mode 100644
index 000000000..c82125a5b
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/build.sbt
@@ -0,0 +1,6 @@
+enablePlugins(GraalVMNativeImagePlugin)
+
+name := "docker-test"
+version := "0.1.0"
+graalVMNativeImageOptions := Seq("--no-fallback")
+graalVMNativeImageGraalVersion := Some("22.3.3")
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy/project/plugins.sbt b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/project/plugins.sbt
new file mode 100644
index 000000000..218f1a27d
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.github.sbt" % "sbt-native-packager" % sys.props("project.version"))
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy/src/main/scala/Main.scala b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/src/main/scala/Main.scala
new file mode 100644
index 000000000..43c2459f3
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/src/main/scala/Main.scala
@@ -0,0 +1,5 @@
+object Main {
+ def main(args: Array[String]): Unit = {
+ println("Hello Graal")
+ }
+}
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image-legacy/test b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/test
new file mode 100644
index 000000000..3d6230bcd
--- /dev/null
+++ b/src/sbt-test/graalvm-native-image/docker-native-image-legacy/test
@@ -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"'
diff --git a/src/sbt-test/graalvm-native-image/docker-native-image/build.sbt b/src/sbt-test/graalvm-native-image/docker-native-image/build.sbt
index 899d15a56..9663b1fa2 100644
--- a/src/sbt-test/graalvm-native-image/docker-native-image/build.sbt
+++ b/src/sbt-test/graalvm-native-image/docker-native-image/build.sbt
@@ -2,4 +2,5 @@ enablePlugins(GraalVMNativeImagePlugin)
name := "docker-test"
version := "0.1.0"
-graalVMNativeImageGraalVersion := Some("22.0.0.2")
\ No newline at end of file
+graalVMNativeImageOptions := Seq("--no-fallback")
+graalVMNativeImageGraalVersion := Some("graalvm-community:17.0.8")
diff --git a/src/sbt-test/graalvm-native-image/simple-native-image/build.sbt b/src/sbt-test/graalvm-native-image/simple-native-image/build.sbt
index 27042d757..b3eb05a63 100644
--- a/src/sbt-test/graalvm-native-image/simple-native-image/build.sbt
+++ b/src/sbt-test/graalvm-native-image/simple-native-image/build.sbt
@@ -1,5 +1,5 @@
enablePlugins(GraalVMNativeImagePlugin)
name := "simple-test"
-
version := "0.1.0"
+graalVMNativeImageOptions := Seq("--no-fallback")
diff --git a/src/sphinx/formats/graalvm-native-image.rst b/src/sphinx/formats/graalvm-native-image.rst
index bd110785d..386943dd7 100644
--- a/src/sphinx/formats/graalvm-native-image.rst
+++ b/src/sphinx/formats/graalvm-native-image.rst
@@ -5,17 +5,20 @@ GraalVM Native Image Plugin
GraalVM's ``native-image`` compiles Java programs AOT (ahead-of-time) into native binaries.
- https://www.graalvm.org/22.1/reference-manual/native-image/ documents the AOT compilation of GraalVM.
+ https://www.graalvm.org/latest/reference-manual/native-image/ documents the AOT compilation of GraalVM.
The plugin supports both using a local installation of the GraalVM ``native-image`` utility, or building inside a
Docker container. If you intend to run the native image on Linux, then building inside a Docker container is
recommended since GraalVM native images can only be built for the platform they are built on. By building in a Docker
-container, you can build Linux native images not just on Linux but also on Windows and macOS.
+container, you can build Linux native images not only on Linux but also on Windows and macOS and for different architectures
+like amd64 or arm64.
Requirements
------------
To build using a local installation of GraalVM, you must have the ``native-image`` utility of GraalVM in your ``PATH``.
+To build using a docker container, you must have a working installation of docker.
+To build for a different architecture, you must have docker with the buildx plugin and QEMU set up for the target architecture.
``native-image`` quick installation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -65,16 +68,41 @@ By default, a local build will be done, expecting the ``native-image`` command t
customized using the following settings.
``graalVMNativeImageGraalVersion``
- Setting this enables generating a Docker container to build the native image, and then building it in that container.
- It must correspond to a valid version of the
- `Oracle GraalVM Community Edition Docker image `_. This setting has no
- effect if ``containerBuildImage`` is explicitly set.
+ Setting this enables using a Docker container to build the native image.
+ It should be in the format ``:``. `Supported packages `_ include:
+ * ``graalvm-ce`` - Versions prior to and including 22.3.3. An intermediate image will be created.
+ * ``native-image`` - Versions prior to and including 22.3.3. The docker image will be used directly.
+ * ``graalvm-community`` - Versions after and including 17.0.7. An intermediate image will be created.
+ * ``native-image-community`` - Versions after and including 17.0.7. The docker image will be used directly.
+
+ The legacy format of specifying the version number is supported up to 22.3.3
+
+ This setting has no effect if ``containerBuildImage`` is explicitly set.
+
+ For example:
+
+ .. code-block:: scala
+
+ graalVMNativeImageGraalVersion := Some("19.1.1") // Legacy GraalVM versions supported up to 22.3.3
+ graalVMNativeImageGraalVersion := Some("graalvm-ce:19.1.1") // Legacy GraalVM versions supported up to 22.3.3
+ graalVMNativeImageGraalVersion := Some("native-image:19.1.1") // Uses the legacy native-image image from GraalVM directly
+ graalVMNativeImageGraalVersion := Some("graalvm-community:17.0.8") // New GraalVM version scheme
+ graalVMNativeImageGraalVersion := Some("native-image-community:17.0.8") // Uses the native-image image from GraalVM directly
+
+ ``graalVMNativeImagePlatformArch``
+ Setting this enables building the native image for a different platform architecture. Requires ``graalVMNativeImageGraalVersion``
+ or ``containerBuildImage`` to be set. Multiplatform builds are currently not supported. Defaults to the platform of the host.
+ If ``containerBuildImage`` is specified, ensure that your specified image has the same platform that you are targeting.
+
+ Requires Docker buildx plugin with a valid builder and QEMU set up for the target platform architecture.
+ `See here for more information `_.
For example:
.. code-block:: scala
- graalVMNativeImageGraalVersion := Some("19.1.1")
+ graalVMNativeImagePlatformArch := Some("arm64")
+ graalVMNativeImagePlatformArch := Some("linux/amd64")
``containerBuildImage``