Skip to content

Commit 03faeba

Browse files
authored
Fix jelly-text support in AOT builds (#131)
* Fix jelly-text support in AOT builds Closes #128 Turns out that the Google proto classes require reflection for text serialization -- this was something that scalapb did not require. To fix this, we could hardcode the methods and classes that need to have reflective access enabled, but it would be a mess to maintain. Instead, we have a custom Graal feature that discovers the needed classes and registers them for reflective access. * fix typo
1 parent 9b4dbc7 commit 03faeba

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

.github/workflows/aot-test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ jobs:
3535
target/graalvm-native-image/jelly-cli version && \
3636
echo '_:b <http://t.org/> _:b .' | target/graalvm-native-image/jelly-cli \
3737
rdf to-jelly --in-format=nt > out.jelly && \
38-
[ -s out.jelly ]
38+
[ -s out.jelly ] &&
39+
target/graalvm-native-image/jelly-cli \
40+
rdf from-jelly --out-format=jelly-text out.jelly > out.txt && \
41+
[ -s out.txt ]
3942
4043
- name: Upload binary
4144
uses: actions/upload-artifact@v4

build.sbt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ resolvers +=
77

88
lazy val jenaV = "5.3.0"
99
lazy val jellyV = "3.1.0"
10+
lazy val graalvmV = "24.1.1"
1011

1112
addCommandAlias("fixAll", "scalafixAll; scalafmtAll")
1213

@@ -22,7 +23,9 @@ lazy val graalOptions = Seq(
2223
// Do a fast build if it's a dev build
2324
// For the release build, optimize for size and make a build report
2425
if (isDevBuild) Seq("-Ob") else Seq("-Os", "--emit build-report"),
25-
).flatten
26+
).flatten ++ Seq(
27+
"--features=eu.neverblink.jelly.cli.graal.ProtobufFeature",
28+
)
2629

2730
lazy val root = (project in file("."))
2831
.enablePlugins(
@@ -40,6 +43,9 @@ lazy val root = (project in file("."))
4043
"com.github.alexarchambault" %% "case-app" % "2.1.0-M30",
4144
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
4245
"org.yaml" % "snakeyaml" % "2.4" % Test,
46+
// For native-image reflection compatibility
47+
"org.graalvm.sdk" % "graal-sdk" % graalvmV % "provided",
48+
"org.reflections" % "reflections" % "0.10.2",
4349
),
4450
scalacOptions ++= Seq(
4551
"-Wunused:imports",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package eu.neverblink.jelly.cli.graal
2+
3+
import org.graalvm.nativeimage.hosted.{Feature, RuntimeReflection}
4+
import org.reflections.Reflections
5+
import org.reflections.scanners.Scanners
6+
7+
import scala.jdk.CollectionConverters.*
8+
9+
class ProtobufFeature extends Feature:
10+
import Feature.*
11+
12+
override def getDescription: String =
13+
"Registers Google-style Protobuf classes for reflection. Needed for jelly-text support."
14+
15+
override def beforeAnalysis(access: BeforeAnalysisAccess): Unit =
16+
val reflections = Reflections("eu.neverblink.jelly.core.proto.google.v1", Scanners.SubTypes)
17+
val classes = reflections.getSubTypesOf(
18+
classOf[com.google.protobuf.GeneratedMessage],
19+
).asScala ++
20+
reflections.getSubTypesOf(classOf[com.google.protobuf.GeneratedMessage.Builder[?]]).asScala ++
21+
reflections.getSubTypesOf(classOf[com.google.protobuf.ProtocolMessageEnum]).asScala
22+
classes.foreach(clazz => {
23+
RuntimeReflection.register(clazz)
24+
RuntimeReflection.register(clazz.getDeclaredMethods*)
25+
})

0 commit comments

Comments
 (0)