启用Scala Native插件后Akka HTTP nativeLink报错求助
希望在后端服务中使用Scala Native运行C编写的代码,但在build.sbt中启用ScalaNativePlugin后,执行nativeLink时出现大量错误。Scala代码编译正常,但nativeLink返回类似如下的报错:
Unknown type akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers, referenced from:
...
(Compile / nativeLink) Unreachable symbols found after classloading run. It can happen when using dependencies not cross-compiled for Scala Native or not yet ported JDK definitions.
禁用Scala Native插件后无此问题,当前build.sbt配置如下:
lazy val akkaHttpVersion = "10.6.0" lazy val akkaVersion = "2.9.0" resolvers += "Akka library repository".at("https://repo.akka.io/maven") enablePlugins( JavaAppPackaging, DockerPlugin) fork := true dockerBaseImage := "adoptopenjdk:11-jre-hotspot" dockerExposedPorts ++= Seq(8080, 8080) lazy val root = (project in file(".")). settings( inThisBuild(List( organization := "fev.io", scalaVersion := "2.13.12" )), name := "miniuptane", libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpVersion, "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, "ch.qos.logback" % "logback-classic" % "1.2.11", "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % Test, "com.typesafe.akka" %% "akka-actor-testkit-typed" % akkaVersion % Test, "org.scalatest" %% "scalatest" % "3.2.12" % Test, "org.scala-lang" %% "toolkit" % "0.1.7", "com.typesafe.slick" %% "slick" % "3.4.1", "com.typesafe.slick" %% "slick-hikaricp" % "3.4.1", "mysql" % "mysql-connector-java" % "5.1.34" ) ) enablePlugins(ScalaNativePlugin) import scala.scalanative.build._ nativeConfig ~= { _.withLTO(LTO.thin) .withMode(Mode.releaseFast) .withGC(GC.commix) }
报错的核心原因是你引入的Akka系列库、Slick、MySQL JDBC驱动等均未官方支持Scala Native。Scala Native基于LLVM编译原生二进制,要求依赖库必须专门针对其做交叉编译适配,而这些JVM生态的主流库大多没有提供Scala Native兼容版本,导致链接阶段无法找到对应的原生符号。
针对这个问题,有几种可行的解决方向:
替换为Scala Native兼容的依赖库
选择有Scala Native支持的替代方案:- HTTP框架:用http4s替代Akka HTTP;
- 数据库访问:用
scala-native-query或原生C数据库驱动封装代替Slick和JDBC驱动; - 日志:用Scala Native支持的日志库代替Logback。
引入兼容库时,使用%%%代替%%来自动匹配Scala Native版本,例如:
"org.http4s" %%% "http4s-ember-server" % "1.0.0-M40"隔离C代码逻辑
不需要把整个服务都用Scala Native编译,仅将需要调用C代码的部分单独做成一个Scala Native模块,编译成独立的原生可执行文件。主服务继续在JVM上运行,通过进程间通信(如Socket、管道)或JNI/NAJNI调用这个原生程序,实现C代码的调用需求。手动移植依赖(不推荐)
如果必须使用Akka、Slick等库,需要手动移植这些库的核心类到Scala Native,补全缺失的JDK API实现。但这个过程工作量极大,需要深入理解Scala Native的编译模型和目标库的内部逻辑,且后续维护成本很高,仅适合特殊场景。
内容的提问来源于stack exchange,提问作者user25511590

