Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native image binary doesn't run using plugin. Works with scala-cli. #19

Closed
carlosedp opened this issue Dec 5, 2022 · 6 comments
Closed

Comments

@carlosedp
Copy link
Collaborator

I have a simple ZIO-http app and found-out that using scala-cli it can package the app with native image and it works fine.

Using the mill-native-image plugin, the binary fails with some LazyVal error... Dunno exactly what is different between both as the native-image options are the same.

The project sources are at: https://github.com/carlosedp/ziohttp

The mill project used scala-cli export function and I just added the plugin native image parameters.

To test it out, just clone the repo and run nativepackage.sh to use scala-cli (which worksfine). Using the plugin, run cd ziohttpproj && ./mill show ziohttp.nativeImage, then run ./out/ziohttp/nativeImage.dest/ziohttp.bin. It fails with:

❯ ./out/ziohttp/nativeImage.dest/ziohttp.bin
Exception in thread "main" scala.runtime.LazyVals$$anon$1
	at scala.runtime.LazyVals$.$init$$$anonfun$3(LazyVals.scala:21)
	at scala.Option.getOrElse(Option.scala:201)
	at scala.runtime.LazyVals$.<clinit>(LazyVals.scala:22)
	at zio.ZLayer.<clinit>(ZLayer.scala:42)
	at [email protected]/java.lang.Class.ensureInitialized(DynamicHub.java:528)
	at zio.ZLayer$Suspend$.apply(ZLayer.scala:439)
	at zio.ZLayer$.suspend(ZLayer.scala:675)
	at zio.ZLayer$.fromZIOEnvironment(ZLayer.scala:621)
	at zio.ZLayer$.<clinit>(ZLayer.scala:566)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:43)
	at ZioHttpApp$.<clinit>(ziohttp.scala:7)
	at ZioHttpApp.main(ziohttp.scala)

Similar error Ref. scala/scala3#13985

@carlosedp
Copy link
Collaborator Author

I believe it has something to do with creating the native-config file like VirtusLab/scala-cli#830.

Tried doing something like:

diff --git a/plugin/src/io/github/alexarchambault/millnativeimage/NativeImage.scala b/plugin/src/io/github/alexarchambault/millnativeimage/NativeImage.scala
index 0ff85f3..41f06cb 100644
--- a/plugin/src/io/github/alexarchambault/millnativeimage/NativeImage.scala
+++ b/plugin/src/io/github/alexarchambault/millnativeimage/NativeImage.scala
@@ -356,12 +356,27 @@ object NativeImage {
 
     def command(nativeImage: String, extraNativeImageArgs: Seq[String], destDir: Option[String], destName: String, classPath: String) = {
       val destDirOptions = destDir.toList.map(d => s"-H:Path=$d")
+      val nativeConfigFile = os.temp(suffix = ".json")
+      os.write.over(
+        nativeConfigFile,
+        """[
+          |  {
+          |    "name": "sun.misc.Unsafe",
+          |    "allDeclaredConstructors": true,
+          |    "allPublicConstructors": true,
+          |    "allDeclaredMethods": true,
+          |    "allDeclaredFields": true
+          |  }
+          |]
+          |""".stripMargin
+      )
       Seq(nativeImage) ++
       extraNativeImageArgs ++
       nativeImageOptions ++
       destDirOptions ++
       Seq(
         s"-H:Name=$destName",
+        s"-H:ReflectionConfigurationFiles=$nativeConfigFile",
         "-cp",
         classPath,
         mainClass

But something is still missing cause I'm getting:

❯ /Users/cdepaula/repos/scala-playground/ziohttp/ziohttpproj/out/ziohttp/nativeImage.dest/ziohttp.bin
Exception in thread "main" java.lang.ExceptionInInitializerError
	at [email protected]/java.lang.Class.ensureInitialized(DynamicHub.java:528)
	at izumi.reflect.macrortti.LightTypeTag$.parse(LightTypeTag.scala:271)
	at zio.ZIOAppDefault.$init$(ZIOAppDefault.scala:45)
	at ZioHttpApp$.<clinit>(ziohttp.scala:7)
	at ZioHttpApp.main(ziohttp.scala)
Caused by: java.lang.NoSuchFieldException: 0bitmap$1
	at [email protected]/java.lang.Class.getDeclaredField(DynamicHub.java:961)
	at scala.runtime.LazyVals$.getOffset(LazyVals.scala:107)
	at izumi.reflect.macrortti.LightTypeTag.<clinit>(LightTypeTag.scala:49)
	... 5 more

Maybe @romanowski can give some tips :)

@carlosedp
Copy link
Collaborator Author

Apparently it's not as trivial as I thought... from what I understood, scala-cli manipulates the bytecode to allow the unsafe declarations thru BytecodeProcessor and all...

Maybe the plugin could import scala-cli and it would make things easier... dunno :)

@romanowski
Copy link

Yes, Scala CLI changes a bit implementation of lazy vals in Scala 3 so people does not need to apply config for each lazy val in their code (and all its dependencies). Lazy vals were reworked so it should be better in the future.

As for the rewrites, we have an application in Scala CLI that changes the classpath that goes into graal native image generation. We publish it so sonatype so it should be possible to use it your build.

@carlosedp
Copy link
Collaborator Author

carlosedp commented Dec 6, 2022

Thanks a lot @romanowski ... I think the bigger issue here is that the mill-native-image plugin is Scala 2.13 code and the scala3-graal lib is Scala 3.

I tried adding the lib as ivy"org.virtuslab.scala-cli:scala3-graal_3:0.1.18" and using -Ytasty-reader but in the sources it complained due to conflicts but I don't know much about using cross-version Scala libs:

Maybe @alexarchambault have some pointer.

@carlosedp
Copy link
Collaborator Author

Implemented in #20.

@carlosedp
Copy link
Collaborator Author

This is solved by using Scala 3.3 which implements LazyVals correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants