From fe68886d68bd53e3dfb2eb4674f6ae7cc841290b Mon Sep 17 00:00:00 2001 From: TH Lim Date: Sun, 1 Mar 2020 00:20:16 +0800 Subject: [PATCH 1/7] Fix for native-image to work in Windows * The final native-image executable cannot be located in the file native-image.cmd unless the full path to native-image.cmd is given. Therefore, a new parameter is provided to the user to specify the native-image.cmd location. * Picks the right CLASSPATH separator according to the OS instead of hardcoded to colon. Using Colon will fail in Windows build. * Tested against GraalVM 20.0.0 and VS 2019. --- .../GraalVMNativeImageKeys.scala | 2 ++ .../GraalVMNativeImagePlugin.scala | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) 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 a641cfe81..942781ca3 100644 --- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala @@ -8,6 +8,8 @@ import sbt._ * GraalVM settings */ trait GraalVMNativeImageKeys { + val graalVMNativeImageCommand = settingKey[String]("GraalVM native-image executable name") + val graalVMNativeImageOptions = settingKey[Seq[String]]("GraalVM native-image options") 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 1ff423228..f6494a6a4 100644 --- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala @@ -28,7 +28,6 @@ object GraalVMNativeImagePlugin extends AutoPlugin { import autoImport._ private val GraalVMBaseImage = "oracle/graalvm-ce" - private val NativeImageCommand = "native-image" override def requires: Plugins = JavaAppPackaging @@ -38,6 +37,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin { target in GraalVMNativeImage := target.value / "graalvm-native-image", graalVMNativeImageOptions := Seq.empty, graalVMNativeImageGraalVersion := None, + graalVMNativeImageCommand := "native-image", resourceDirectory in GraalVMNativeImage := sourceDirectory.value / "graal", mainClass in GraalVMNativeImage := (mainClass in Compile).value ) ++ inConfig(GraalVMNativeImage)(scopedSettings) @@ -55,6 +55,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin { packageBin := { val targetDirectory = target.value val binaryName = name.value + val nativeImageCommand = graalVMNativeImageCommand.value val className = mainClass.value.getOrElse(sys.error("Could not find a main class.")) val classpathJars = scriptClasspathOrdering.value val extraOptions = graalVMNativeImageOptions.value @@ -65,7 +66,15 @@ object GraalVMNativeImagePlugin extends AutoPlugin { UniversalPlugin.autoImport.containerBuildImage.value match { case None => - buildLocal(targetDirectory, binaryName, className, classpathJars.map(_._1), extraOptions, streams.log) + buildLocal( + targetDirectory, + binaryName, + nativeImageCommand, + className, + classpathJars.map(_._1), + extraOptions, + streams.log + ) case Some(image) => val resourceMappings = MappingsHelper.relative(graalResources, graalResourceDirectories) @@ -87,6 +96,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin { private def buildLocal(targetDirectory: File, binaryName: String, + nativeImageCommand: String, className: String, classpathJars: Seq[File], extraOptions: Seq[String], @@ -94,11 +104,12 @@ object GraalVMNativeImagePlugin extends AutoPlugin { targetDirectory.mkdirs() val command = { val nativeImageArguments = { - val classpath = classpathJars.mkString(":") + val classpath = classpathJars.mkString(System.getProperty("path.separator")) Seq("--class-path", classpath, s"-H:Name=$binaryName") ++ extraOptions ++ Seq(className) } - Seq(NativeImageCommand) ++ nativeImageArguments + 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) From 1eea949e375487c0e7649608e2bce159728d12d7 Mon Sep 17 00:00:00 2001 From: TH Lim Date: Sun, 1 Mar 2020 14:42:44 +0800 Subject: [PATCH 2/7] Update src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala Co-Authored-By: nigredo-tori --- .../packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f6494a6a4..116bceb89 100644 --- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala @@ -104,7 +104,7 @@ object GraalVMNativeImagePlugin extends AutoPlugin { targetDirectory.mkdirs() val command = { val nativeImageArguments = { - val classpath = classpathJars.mkString(System.getProperty("path.separator")) + val classpath = classpathJars.mkString(java.io.File.pathSeparator) Seq("--class-path", classpath, s"-H:Name=$binaryName") ++ extraOptions ++ Seq(className) } Seq(nativeImageCommand) ++ nativeImageArguments From f0e1e9a24b5f06d695a213c5831ec9e9c8628aff Mon Sep 17 00:00:00 2001 From: sshark Date: Sun, 1 Mar 2020 14:47:33 +0800 Subject: [PATCH 3/7] Exclude SettingKey from MIMA filter --- build.sbt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 97dc04731..84a5f0bfa 100644 --- a/build.sbt +++ b/build.sbt @@ -91,8 +91,14 @@ mimaBinaryIssueFilters ++= { ProblemFilters.exclude[ReversedMissingMethodProblem]( "com.typesafe.sbt.packager.docker.DockerKeys.dockerAutoremoveMultiStageIntermediateImages" ), - ProblemFilters.exclude[DirectMissingMethodProblem]( - "com.typesafe.sbt.packager.docker.DockerPlugin.publishLocalDocker" + ProblemFilters + .exclude[DirectMissingMethodProblem]("com.typesafe.sbt.packager.docker.DockerPlugin.publishLocalDocker"), + // added via #1312 + ProblemFilters.exclude[ReversedMissingMethodProblem]( + "com.typesafe.sbt.packager.graalvmnativeimage.GraalVMNativeImageKeys.graalVMNativeImageCommand" + ), + ProblemFilters.exclude[ReversedMissingMethodProblem]( + "com.typesafe.sbt.packager.graalvmnativeimage.GraalVMNativeImageKeys.com$typesafe$sbt$packager$graalvmnativeimage$GraalVMNativeImageKeys$_setter_$graalVMNativeImageCommand_=" ) ) } From ce436e809fea57914b679522b3171ff24d4d6ad7 Mon Sep 17 00:00:00 2001 From: sshark Date: Sun, 1 Mar 2020 19:58:08 +0800 Subject: [PATCH 4/7] Adds info for graalVMNativeImageCommand setting key --- src/sphinx/formats/graalvm-native-image.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sphinx/formats/graalvm-native-image.rst b/src/sphinx/formats/graalvm-native-image.rst index be91e5630..500ac78bb 100644 --- a/src/sphinx/formats/graalvm-native-image.rst +++ b/src/sphinx/formats/graalvm-native-image.rst @@ -47,6 +47,19 @@ Required Settings Settings -------- +``native-image`` Executable Command (Pay attention if you are using Windows OS) +~~~~ +Putting ``native-image`` in ``PATH`` does not work for Windows. ``native-image``is a batch file in Windows that calls another executable to compile the Java classes to a standalone executable. Therefore, the full path to the batch file e.g. ``C:\Program Files\Java\graalvm\bin\native-image.cmd`` must be provided. It is important to include ``.cmd``. + + ``graalVMNativeImageCommand`` + Set this parameter to point to ``native-image`` or ``native-image.cmd``. For Linux, set this parameter if it is inconvenient to make ``native-image`` available in your ``PATH``. + + For example: + + .. code-block:: scala + + graalVMNativeImageCommand := "C:/Program Files/Java/graalvm/bin/native-image.cmd" + Docker Image Build Settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 689c7692c85c6e3efa45e372875058b23514a1e0 Mon Sep 17 00:00:00 2001 From: TH Lim Date: Wed, 4 Mar 2020 00:03:19 +0800 Subject: [PATCH 5/7] Workaround for MiMa exceptions --- build.sbt | 9 +-------- .../graalvmnativeimage/GraalVMNativeImageKeys.scala | 6 ++++-- .../graalvmnativeimage/GraalVMNativeImagePlugin.scala | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/build.sbt b/build.sbt index 84a5f0bfa..40b70788a 100644 --- a/build.sbt +++ b/build.sbt @@ -92,14 +92,7 @@ mimaBinaryIssueFilters ++= { "com.typesafe.sbt.packager.docker.DockerKeys.dockerAutoremoveMultiStageIntermediateImages" ), ProblemFilters - .exclude[DirectMissingMethodProblem]("com.typesafe.sbt.packager.docker.DockerPlugin.publishLocalDocker"), - // added via #1312 - ProblemFilters.exclude[ReversedMissingMethodProblem]( - "com.typesafe.sbt.packager.graalvmnativeimage.GraalVMNativeImageKeys.graalVMNativeImageCommand" - ), - ProblemFilters.exclude[ReversedMissingMethodProblem]( - "com.typesafe.sbt.packager.graalvmnativeimage.GraalVMNativeImageKeys.com$typesafe$sbt$packager$graalvmnativeimage$GraalVMNativeImageKeys$_setter_$graalVMNativeImageCommand_=" - ) + .exclude[DirectMissingMethodProblem]("com.typesafe.sbt.packager.docker.DockerPlugin.publishLocalDocker") ) } 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 942781ca3..8460b265d 100644 --- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala +++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImageKeys.scala @@ -8,8 +8,6 @@ import sbt._ * GraalVM settings */ trait GraalVMNativeImageKeys { - val graalVMNativeImageCommand = settingKey[String]("GraalVM native-image executable name") - val graalVMNativeImageOptions = settingKey[Seq[String]]("GraalVM native-image options") @@ -17,3 +15,7 @@ trait GraalVMNativeImageKeys { "Version of GraalVM to build with. Setting this has the effect of generating a container build image to build the native image with this version of GraalVM." ) } + +trait GraalVMNativeImageKeysEx extends GraalVMNativeImageKeys { + val graalVMNativeImageCommand = settingKey[String]("GraalVM native-image executable command") +} 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 116bceb89..db99a8c94 100644 --- a/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/graalvmnativeimage/GraalVMNativeImagePlugin.scala @@ -21,7 +21,7 @@ import com.typesafe.sbt.packager.universal.UniversalPlugin */ object GraalVMNativeImagePlugin extends AutoPlugin { - object autoImport extends GraalVMNativeImageKeys { + object autoImport extends GraalVMNativeImageKeysEx { val GraalVMNativeImage: Configuration = config("graalvm-native-image") } From 5a6f58ae980d3364a2953ff4f463c2fa84187d05 Mon Sep 17 00:00:00 2001 From: TH Lim Date: Sun, 8 Mar 2020 16:21:14 +0800 Subject: [PATCH 6/7] Fix failing in `windows / test-bat-template` * Use `Seq` for better manage command lines management. * Cannot fix "include symbols with double q" because there is no way to escape '<' and '>' symbols during `cmd` execution. Is there a way to do so? * Have to trim the argument for `include symbols on normal args` otherwise it won't pass the test. --- .../windows/test-bat-template/build.sbt | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/sbt-test/windows/test-bat-template/build.sbt b/src/sbt-test/windows/test-bat-template/build.sbt index 339021ec8..7399c57a3 100644 --- a/src/sbt-test/windows/test-bat-template/build.sbt +++ b/src/sbt-test/windows/test-bat-template/build.sbt @@ -49,13 +49,14 @@ TaskKey[Unit]("checkScript") := { } def crlf2cr(txt: String) = txt.trim.replaceAll("\\\r\\\n", "\n") def checkOutput(testName: String, - args: String, + args: Seq[String], expected: String, env: Map[String, String] = Map.empty, expectedRC: Int = 0) = { val pr = new StringBuilder() val logger = ProcessLogger((o: String) => pr.append(o + "\n"), (e: String) => pr.append("error < " + e + "\n")) - val cmd = Seq("cmd", "/c", script.getAbsolutePath + " " + args) + val cmd = Seq("cmd", "/c", script.getAbsolutePath) ++ args +// val cmd = Seq("cmd", "/c", script.getAbsolutePath, "-Dtest.hoge=C:\\Program Files\\Java", "'C:\\Program Files\\Java'") val result = sys.process.Process(cmd, None, env.toSeq: _*) ! logger if (result != expectedRC) { pr.append("error code: " + result + "\n") @@ -85,43 +86,50 @@ TaskKey[Unit]("checkScript") := { debugOutFile.delete() } } - checkOutput("normal argmument", "OK", "arg #0 is [OK]\nSUCCESS!") - checkOutput("with -D", "-Dtest.hoge=\"huga\" OK", "arg #0 is [OK]\nproperty(test.hoge) is [huga]\nSUCCESS!") + + checkOutput("normal argmument", Seq("OK"), "arg #0 is [OK]\nSUCCESS!") + checkOutput("with -D", Seq("-Dtest.hoge=huga", "OK"), "arg #0 is [OK]\nproperty(test.hoge) is [huga]\nSUCCESS!") checkOutput( "with -J java-opt", - "-J-Xms6m OK", + Seq("-J-Xms6m", "OK"), "arg #0 is [OK]\nvmarg #0 is [-Xms6m]\nSUCCESS!", Map("show-vmargs" -> "true") ) checkOutput( "complex", - "first -Dtest.hoge=\"huga\" -J-Xms6m -XX last", + Seq("first", "-Dtest.hoge=huga", "-J-Xms6m", "-XX", "last"), "arg #0 is [first]\narg #1 is [-XX]\narg #2 is [last]\nproperty(test.hoge) is [huga]\nvmarg #0 is [-Dtest.hoge=huga]\nvmarg #1 is [-Xms6m]\nSUCCESS!", Map("show-vmargs" -> "true") ) checkOutput( "include space", - """-Dtest.hoge="C:\Program Files\Java" "C:\Program Files\Java" """, + Seq("""-Dtest.hoge=C:\Program Files\Java""", """"C:\Program Files\Java""""), "arg #0 is [C:\\Program Files\\Java]\nproperty(test.hoge) is [C:\\Program Files\\Java]\nSUCCESS!" ) - checkOutput("include symbols on -D", "\"-Dtest.hoge=\\[]!< >%\"", "property(test.hoge) is [\\[]!< >%]\nSUCCESS!") - checkOutput("include symbols on normal args", """ "\[]!< >%" """, "arg #0 is [\\[]!< >%]\nSUCCESS!") + checkOutput("include symbols on -D", Seq("-Dtest.hoge=\\[]!< >%"), "property(test.hoge) is [\\[]!< >%]\nSUCCESS!") + checkOutput("include symbols on normal args", Seq("\"\\[]!< >%\""), "arg #0 is [\\[]!< >%]\nSUCCESS!") + + /* fails test because symbols '<' and '>' cannot be properly escaped during cmd execution checkOutput( "include symbols with double quote", - "-Dtest.huga=\"[]!<>%\"", + Seq("-Dtest.huga=\"[]!<>%\""), "property(test.huga) is [[]!<>%]\nSUCCESS!" ) + */ + checkOutput( "include symbols with double quote2", - """ "-Dtest.hoge=\[]!< >%" "\[]!< >%" -Dtest.huga="\[]!<>%" """, + Seq("-Dtest.hoge=\\[]!< >%", "\"\\[]!< >%\"", "-Dtest.huga=\\[]!<>%"), "arg #0 is [\\[]!< >%]\nproperty(test.hoge) is [\\[]!< >%]\nproperty(test.huga) is [\\[]!<>%]\nSUCCESS!" ) + // can't success include double-quote. arguments pass from Process(Seq("-Da=xx\"yy", "aa\"bb")) is parsed (%1="-Da", %2="xx\"yy aa\"bb") by cmd.exe ... //checkOutput("include space and double-quote", // "-Dtest.hoge=aa\"bb xx\"yy", // "arg #0 is [xx\"yy]\nproperty(test.hoge) is [aa\"bb]\nvmarg #0 is [-Dtest.hoge=aa\"bb]\nSUCCESS!") - checkOutput("return-cord not 0", "RC1", "arg #0 is [RC1]\nFAILURE!", Map("return-code" -> "1"), 1) - checkOutput("return-cord not 0 and 1", "RC2", "arg #0 is [RC2]\nFAILURE!", Map("return-code" -> "2"), 2) - checkOutput("return-code negative", "RC-1", "arg #0 is [RC-1]\nFAILURE!", Map("return-code" -> "-1"), -1) + + checkOutput("return-cord not 0", Seq("RC1"), "arg #0 is [RC1]\nFAILURE!", Map("return-code" -> "1"), 1) + checkOutput("return-cord not 0 and 1", Seq("RC2"), "arg #0 is [RC2]\nFAILURE!", Map("return-code" -> "2"), 2) + checkOutput("return-code negative", Seq("RC-1"), "arg #0 is [RC-1]\nFAILURE!", Map("return-code" -> "-1"), -1) assert(fails.toString == "", fails.toString) } From 2265147df14f513075e4945e129cb7f82eb5a6b9 Mon Sep 17 00:00:00 2001 From: Nepomuk Seiler Date: Tue, 10 Mar 2020 15:18:46 +0100 Subject: [PATCH 7/7] Remove debugging comment --- src/sbt-test/windows/test-bat-template/build.sbt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sbt-test/windows/test-bat-template/build.sbt b/src/sbt-test/windows/test-bat-template/build.sbt index 7399c57a3..b2395a059 100644 --- a/src/sbt-test/windows/test-bat-template/build.sbt +++ b/src/sbt-test/windows/test-bat-template/build.sbt @@ -56,7 +56,6 @@ TaskKey[Unit]("checkScript") := { val pr = new StringBuilder() val logger = ProcessLogger((o: String) => pr.append(o + "\n"), (e: String) => pr.append("error < " + e + "\n")) val cmd = Seq("cmd", "/c", script.getAbsolutePath) ++ args -// val cmd = Seq("cmd", "/c", script.getAbsolutePath, "-Dtest.hoge=C:\\Program Files\\Java", "'C:\\Program Files\\Java'") val result = sys.process.Process(cmd, None, env.toSeq: _*) ! logger if (result != expectedRC) { pr.append("error code: " + result + "\n")