diff --git a/.github/workflows/aot-test.yml b/.github/workflows/aot-test.yml index f8874e5..1e80b0f 100644 --- a/.github/workflows/aot-test.yml +++ b/.github/workflows/aot-test.yml @@ -35,7 +35,10 @@ jobs: target/graalvm-native-image/jelly-cli version && \ echo '_:b _:b .' | target/graalvm-native-image/jelly-cli \ rdf to-jelly --in-format=nt > out.jelly && \ - [ -s out.jelly ] + [ -s out.jelly ] && + target/graalvm-native-image/jelly-cli \ + rdf from-jelly --out-format=jelly-text out.jelly > out.txt && \ + [ -s out.txt ] - name: Upload binary uses: actions/upload-artifact@v4 diff --git a/build.sbt b/build.sbt index 1eab235..c9e8c80 100644 --- a/build.sbt +++ b/build.sbt @@ -7,6 +7,7 @@ resolvers += lazy val jenaV = "5.3.0" lazy val jellyV = "3.1.0" +lazy val graalvmV = "24.1.1" addCommandAlias("fixAll", "scalafixAll; scalafmtAll") @@ -22,7 +23,9 @@ lazy val graalOptions = Seq( // Do a fast build if it's a dev build // For the release build, optimize for size and make a build report if (isDevBuild) Seq("-Ob") else Seq("-Os", "--emit build-report"), -).flatten +).flatten ++ Seq( + "--features=eu.neverblink.jelly.cli.graal.ProtobufFeature", +) lazy val root = (project in file(".")) .enablePlugins( @@ -40,6 +43,9 @@ lazy val root = (project in file(".")) "com.github.alexarchambault" %% "case-app" % "2.1.0-M30", "org.scalatest" %% "scalatest" % "3.2.19" % Test, "org.yaml" % "snakeyaml" % "2.4" % Test, + // For native-image reflection compatibility + "org.graalvm.sdk" % "graal-sdk" % graalvmV % "provided", + "org.reflections" % "reflections" % "0.10.2", ), scalacOptions ++= Seq( "-Wunused:imports", diff --git a/src/main/scala/eu/neverblink/jelly/cli/graal/ProtobufFeature.scala b/src/main/scala/eu/neverblink/jelly/cli/graal/ProtobufFeature.scala new file mode 100644 index 0000000..29011d1 --- /dev/null +++ b/src/main/scala/eu/neverblink/jelly/cli/graal/ProtobufFeature.scala @@ -0,0 +1,25 @@ +package eu.neverblink.jelly.cli.graal + +import org.graalvm.nativeimage.hosted.{Feature, RuntimeReflection} +import org.reflections.Reflections +import org.reflections.scanners.Scanners + +import scala.jdk.CollectionConverters.* + +class ProtobufFeature extends Feature: + import Feature.* + + override def getDescription: String = + "Registers Google-style Protobuf classes for reflection. Needed for jelly-text support." + + override def beforeAnalysis(access: BeforeAnalysisAccess): Unit = + val reflections = Reflections("eu.neverblink.jelly.core.proto.google.v1", Scanners.SubTypes) + val classes = reflections.getSubTypesOf( + classOf[com.google.protobuf.GeneratedMessage], + ).asScala ++ + reflections.getSubTypesOf(classOf[com.google.protobuf.GeneratedMessage.Builder[?]]).asScala ++ + reflections.getSubTypesOf(classOf[com.google.protobuf.ProtocolMessageEnum]).asScala + classes.foreach(clazz => { + RuntimeReflection.register(clazz) + RuntimeReflection.register(clazz.getDeclaredMethods*) + })