diff --git a/.travis.yml b/.travis.yml index 9b35354e6..455fe7f22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ cache: - $HOME/.sbt - $HOME/.m2 - $HOME/.ivy2 + - $HOME/graalvm env: global: @@ -28,6 +29,14 @@ env: before_install: # travis sets the java options which breaks tests that check the applied memory settings - unset _JAVA_OPTIONS + - if [[ ! -d graalvm ]]; then + # Make `native-image` available and nothing else + wget https://github.com/oracle/graal/releases/download/vm-1.0.0-rc7/graalvm-ce-1.0.0-rc7-linux-amd64.tar.gz; + tar zxf graalvm-ce-1.0.0-rc7-linux-amd64.tar.gz; + mkdir -p ~/.bin; + ln -s ~/graalvm-ee-1.0.0-rc7/bin/native-image ~/.bin/native-image; + export PATH=~/.bin:$PATH; + fi - if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew update; brew install xz; @@ -58,6 +67,9 @@ jobs: - script: sbt "^validateAsh" name: "scripted ash tests" if: type = pull_request OR (type = push AND branch = master) + - script: sbt "^validateGraalVMNativeImage" + name: "scripted GraalVM native-image tests" + if: type = pull_request OR (type = push AND branch = master) - script: sbt "^validateRpm" name: "scripted rpm tests" if: type = pull_request OR (type = push AND branch = master) diff --git a/build.sbt b/build.sbt index e120b3bc5..84e111fc3 100644 --- a/build.sbt +++ b/build.sbt @@ -96,6 +96,7 @@ addCommandAlias("validateUniversal", "scripted universal/*") addCommandAlias("validateJar", "scripted jar/*") addCommandAlias("validateBash", "scripted bash/*") addCommandAlias("validateAsh", "scripted ash/*") +addCommandAlias("validateGraalVMNativeImage", "scripted graalvm-native-image/*") addCommandAlias("validateRpm", "scripted rpm/*") addCommandAlias("validateDebian", "scripted debian/*") addCommandAlias("validateDocker", "scripted docker/*") diff --git a/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImageKeys.scala b/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImageKeys.scala new file mode 100644 index 000000000..64bf48346 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImageKeys.scala @@ -0,0 +1,13 @@ +package com.typesafe.sbt +package packager +package graalvmnativeimage + +import sbt._ + +/** + * GraalVM settings + */ +trait GraalVMNativeImageKeys { + val graalVMNativeImageOptions = + SettingKey[Seq[String]]("graalvm-native-image-options", "GraalVM native-image options") +} diff --git a/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImagePlugin.scala b/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImagePlugin.scala new file mode 100644 index 000000000..d8fc06c41 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/packager/graalvm-native-image/GraalVMNativeImagePlugin.scala @@ -0,0 +1,56 @@ +package com.typesafe.sbt.packager.graalvmnativeimage + +import sbt._ +import sbt.Keys._ +import java.nio.charset.Charset + +import com.typesafe.sbt.packager.SettingsHelper +import com.typesafe.sbt.packager.Keys._ +import com.typesafe.sbt.packager.linux._ +import com.typesafe.sbt.packager.Compat._ +import com.typesafe.sbt.packager.validation._ + +/** + * 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") + } + + private val GraalVMNativeImageCommand = "native-image" + + import autoImport._ + + override def projectConfigurations: Seq[Configuration] = Seq(GraalVMNativeImage) + + override lazy val projectSettings = Seq( + target in GraalVMNativeImage := target.value / "graalvm-native-image", + graalVMNativeImageOptions := Seq.empty, + packageBin in GraalVMNativeImage := { + val targetDirectory = (target in GraalVMNativeImage).value + targetDirectory.mkdirs() + val binaryName = name.value + val command = { + val nativeImageArguments = { + val className = (mainClass in Compile).value.getOrElse(sys.error("Could not find a main class.")) + val classpathJars = Seq((packageBin in Compile).value) ++ (dependencyClasspath in Compile).value.map(_.data) + val classpath = classpathJars.mkString(":") + val extraOptions = graalVMNativeImageOptions.value + Seq("--class-path", classpath, s"-H:Name=$binaryName") ++ extraOptions ++ Seq(className) + } + Seq(GraalVMNativeImageCommand) ++ nativeImageArguments + } + sys.process.Process(command, targetDirectory) ! streams.value.log match { + case 0 => targetDirectory / binaryName + case x => sys.error(s"Failed to run $GraalVMNativeImageCommand, exit status: " + x) + } + } + ) +} 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 new file mode 100644 index 000000000..27042d757 --- /dev/null +++ b/src/sbt-test/graalvm-native-image/simple-native-image/build.sbt @@ -0,0 +1,5 @@ +enablePlugins(GraalVMNativeImagePlugin) + +name := "simple-test" + +version := "0.1.0" diff --git a/src/sbt-test/graalvm-native-image/simple-native-image/project/plugins.sbt b/src/sbt-test/graalvm-native-image/simple-native-image/project/plugins.sbt new file mode 100644 index 000000000..b53de154c --- /dev/null +++ b/src/sbt-test/graalvm-native-image/simple-native-image/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version")) diff --git a/src/sbt-test/graalvm-native-image/simple-native-image/src/main/scala/Main.scala b/src/sbt-test/graalvm-native-image/simple-native-image/src/main/scala/Main.scala new file mode 100644 index 000000000..4ff1307d0 --- /dev/null +++ b/src/sbt-test/graalvm-native-image/simple-native-image/src/main/scala/Main.scala @@ -0,0 +1,5 @@ +object Main { + def main(args: Array[String]): Unit = { + println("Hello world") + } +} diff --git a/src/sbt-test/graalvm-native-image/simple-native-image/test b/src/sbt-test/graalvm-native-image/simple-native-image/test new file mode 100644 index 000000000..962eafe69 --- /dev/null +++ b/src/sbt-test/graalvm-native-image/simple-native-image/test @@ -0,0 +1,3 @@ +# Generate the GraalVM native image +> show graalvm-native-image:packageBin +$ exists target/graalvm-native-image/simple-testx diff --git a/src/sphinx/formats/graalvm-native-image.rst b/src/sphinx/formats/graalvm-native-image.rst new file mode 100644 index 000000000..4defd9e3a --- /dev/null +++ b/src/sphinx/formats/graalvm-native-image.rst @@ -0,0 +1,45 @@ +.. _graalvm-native-image-plugin: + +GraalVM Native Image Plugin +============= + +GraalVM's ``native-image`` compiles Java programs AOT (ahead-of-time) into native binaries. + + https://www.graalvm.org/docs/reference-manual/aot-compilation/ documents the AOT compilation of GraalVM. + +Requirements +------------ + +You must have ``native-image`` of GraalVM in your ``PATH``. + +Build +----- + +.. code-block:: bash + + sbt 'show graalvm-native-image:packageBin' + + +Required Settings +~~~~~~~~~~~~~~~~~ + +.. code-block:: scala + + enablePlugins(GraalVMNativeImagePlugin) + + +Settings +-------- + +Publishing Settings +~~~~~~~~~~~~~~~~~~~ + + ``graalVMNativeImageOptions`` + Extra options that will be passed to the ``native-image`` command. By default, this includes the name of the main class. + +Tasks +----- +The GraalVM Native Image plugin provides the following commands: + + ``graalvm-native-image:packageBin`` + Generates a native image using GraalVM.