当前位置:  开发笔记 > 编程语言 > 正文

带Scala.js和Scala(jvm)的ScalaPB - 存在链接错误

如何解决《带Scala.js和Scala(jvm)的ScalaPB-存在链接错误》经验,为你挑选了1个好方法。

我的sbt中有多个子项目,一个是服务器(基于playframework),另一个是clientside(scala.js),第三个是protobuf(scalapb)形式的两者之间的通信.

现在,这是我的build.sbt:

lazy val generalSettings = Seq(
  organization := "tld.awesomeness",
  version := "0.0.1",
  scalaVersion := "2.12.1"
)

val CrossDependencies = new
  {
    val scalaTest = "org.scalatest" %% "scalatest" % "3.0.1" % "test"
    val scalactic = "org.scalactic" %% "scalactic" % "3.0.1"
    val scalaTags = "com.lihaoyi" %% "scalatags" % "0.6.2"
  }

lazy val proto = (project in file("modules/proto"))
  .settings(generalSettings: _*)
  .settings(
    PB.targets in Compile := Seq(
      scalapb.gen() -> (sourceManaged in Compile).value
    ),
    // If you need scalapb/scalapb.proto or anything from google/protobuf/*.proto
    libraryDependencies ++= Seq(
      "com.trueaccord.scalapb" %% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf",
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion,
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf"
    )
  )

lazy val play = (project in file("modules/play"))
  .enablePlugins(PlayScala)
  .settings(generalSettings: _*)
  .settings(
    name := "play",
    libraryDependencies ++= Seq(
      CrossDependencies.scalaTest,
      CrossDependencies.scalactic,
      CrossDependencies.scalaTags,
      "com.typesafe.play" %% "play-json" % "2.6.0-M1"),
    scalaJSProjects := Seq(client),
    pipelineStages in Assets := Seq(scalaJSPipeline),
    compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value
  )
  .aggregate(slick)
  .dependsOn(slick)
  .aggregate(flyway)
  .dependsOn(flyway)
  .aggregate(proto)
  .dependsOn(proto)

lazy val client = (project in file("modules/client"))
  .enablePlugins(ScalaJSPlugin, ScalaJSWeb)
  .settings(generalSettings: _*)
  .settings(
    name := "client",
    libraryDependencies += CrossDependencies.scalaTags,
    persistLauncher := true
  )
  .aggregate(proto)
  .dependsOn(proto)

// Loads the jvm project at sbt startup
onLoad in Global := (Command.process("project play", _: State)) compose (onLoad in Global).value

fork in run := true

这是plugins.sbt:

// Scala.JS
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.14")
addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.0.2")

// Play
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.0-SNAPSHOT")

// Proto
addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.3" exclude ("com.trueaccord.scalapb", "protoc-bridge_2.10"))
libraryDependencies += "com.trueaccord.scalapb" %% "compilerplugin-shaded" % "0.5.47"

这是一个proto文件:

syntax = "proto3";

package tld.awesomeness.proto;

message Test {
    int32 id = 1;
    string email = 2;
}

编译后我得到了 Test.class

现在在客户端我尝试:

private def doSend(ws: WebSocket): Unit =
{
    val msg = Test().withId(1337)
    val a: ArrayBuffer = new ArrayBuffer(msg.toByteArray.length)
    msg.toByteArray
    ws.send(a)
}

(当我通过它发送字符串时,websocket本身工作得很好!)

现在我得到了这个巨大的堆栈跟踪:

[info] Fast optimizing /home/sorona/awesomeness/modules/client/target/scala-2.12/client-fastopt.js
[error] Referring to non-existent class tld.awesomeness.proto.Test.Test$
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent class tld.awesomeness.proto.Test.Test
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent method tld.awesomeness.proto.Test.Test.toByteArray()[scala.Byte
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent method tld.awesomeness.proto.Test.Test.withId(scala.Int)tld.awesomeness.proto.Test.Test
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent method tld.awesomeness.proto.Test.Test$.apply$default$2()java.lang.String
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent method tld.awesomeness.proto.Test.Test$.apply$default$1()scala.Int
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
[error] Referring to non-existent method tld.awesomeness.proto.Test.Test.(scala.Int,java.lang.String)
[error]   called from tld.awesomeness.ScalaJSTest$.doSend(org.scalajs.dom.raw.WebSocket)scala.Unit
[error]   called from tld.awesomeness.ScalaJSTest$.tld$awesomeness$ScalaJSTest$$$anonfun$call$1(org.scalajs.dom.raw.Event,org.scalajs.dom.raw.WebSocket)org.scalajs.dom.raw.Event
[error]   called from tld.awesomeness.ScalaJSTest$.call()scala.Unit
[error]   called from tld.awesomeness.Main$.main()scala.Unit
[error]   called from scala.scalajs.js.JSApp.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.$$js$exported$meth$main()java.lang.Object
[error]   called from tld.awesomeness.Main$.main
[error]   exported to JavaScript with @JSExport
[error] involving instantiated classes:
[error]   tld.awesomeness.ScalaJSTest$
[error]   tld.awesomeness.Main$
java.lang.RuntimeException: There were linking errors
        at scala.sys.package$.error(package.scala:27)
        at org.scalajs.core.tools.linker.frontend.BaseLinker.linkInternal(BaseLinker.scala:133)
        at org.scalajs.core.tools.linker.frontend.BaseLinker.linkInternal(BaseLinker.scala:86)
        at org.scalajs.core.tools.linker.frontend.LinkerFrontend$$anonfun$4.apply(LinkerFrontend.scala:54)
        at org.scalajs.core.tools.linker.frontend.LinkerFrontend$$anonfun$4.apply(LinkerFrontend.scala:54)
        at org.scalajs.core.tools.logging.Logger$class.time(Logger.scala:28)
        at org.scalajs.sbtplugin.Loggers$SbtLoggerWrapper.time(Loggers.scala:7)
        at org.scalajs.core.tools.linker.frontend.LinkerFrontend.link(LinkerFrontend.scala:53)
        at org.scalajs.core.tools.linker.Linker$$anonfun$link$1.apply$mcV$sp(Linker.scala:50)
        at org.scalajs.core.tools.linker.Linker$$anonfun$link$1.apply(Linker.scala:49)
        at org.scalajs.core.tools.linker.Linker$$anonfun$link$1.apply(Linker.scala:49)
        at org.scalajs.core.tools.linker.Linker.guard(Linker.scala:67)
        at org.scalajs.core.tools.linker.Linker.link(Linker.scala:49)
        at org.scalajs.core.tools.linker.ClearableLinker$$anonfun$link$1.apply(ClearableLinker.scala:51)
        at org.scalajs.core.tools.linker.ClearableLinker$$anonfun$link$1.apply(ClearableLinker.scala:51)
        at org.scalajs.core.tools.linker.ClearableLinker.linkerOp(ClearableLinker.scala:62)
        at org.scalajs.core.tools.linker.ClearableLinker.link(ClearableLinker.scala:51)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$org$scalajs$sbtplugin$ScalaJSPluginInternal$$scalaJSStageSettings$4$$anonfun$apply$6$$anonfun$apply$7.apply(ScalaJSPluginInternal.scala:251)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$org$scalajs$sbtplugin$ScalaJSPluginInternal$$scalaJSStageSettings$4$$anonfun$apply$6$$anonfun$apply$7.apply(ScalaJSPluginInternal.scala:239)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:253)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:253)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:267)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:263)
        at sbt.Difference.apply(Tracked.scala:224)
        at sbt.Difference.apply(Tracked.scala:206)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:263)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:262)
        at sbt.Difference.apply(Tracked.scala:224)
        at sbt.Difference.apply(Tracked.scala:200)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:262)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:260)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$org$scalajs$sbtplugin$ScalaJSPluginInternal$$scalaJSStageSettings$4$$anonfun$apply$6.apply(ScalaJSPluginInternal.scala:256)
        at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$org$scalajs$sbtplugin$ScalaJSPluginInternal$$scalaJSStageSettings$4$$anonfun$apply$6.apply(ScalaJSPluginInternal.scala:237)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
        at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
        at sbt.std.Transform$$anon$4.work(System.scala:63)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
        at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
        at sbt.Execute.work(Execute.scala:237)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
        at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
        at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
[error] (client/compile:fastOptJS) There were linking errors

我的想法找到了一切,但显然我做错了什么.我已经看过https://github.com/thesamet/scalapbjs-test但无济于事.

该行出现问题 val msg = Test().withId(1337)

编辑:评论后我改变了build.sbt:

lazy val proto = (crossProject in file("modules/proto"))
  .settings(generalSettings: _*)
  .settings(
    PB.targets in Compile := Seq(
      scalapb.gen() -> (sourceManaged in Compile).value
    )).
  jvmSettings(
    libraryDependencies += "com.trueaccord.scalapb" %% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf",
    PB.targets in Compile := Seq(
      scalapb.gen() -> (sourceManaged in Compile).value
    )
  ).
  jsSettings(
    libraryDependencies ++= Seq(
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion,
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf"
    ),
    PB.targets in Compile := Seq(
      scalapb.gen() -> (sourceManaged in Compile).value
    )
  )

lazy val protoJs = proto.js
lazy val protoJVM = proto.jvm

lazy val play = (project in file("modules/play"))
  .enablePlugins(PlayScala)
  .settings(generalSettings: _*)
  .settings(
    name := "play",
    libraryDependencies ++= Seq(
      CrossDependencies.scalaTest,
      CrossDependencies.scalactic,
      CrossDependencies.scalaTags,
      "com.typesafe.play" %% "play-json" % "2.6.0-M1"),
    scalaJSProjects := Seq(client),
    pipelineStages in Assets := Seq(scalaJSPipeline),
    compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value
  )
  .aggregate(slick)
  .dependsOn(slick)
  .aggregate(flyway)
  .dependsOn(flyway)
  .aggregate(protoJVM)
  .dependsOn(protoJVM)

lazy val client = (project in file("modules/client"))
  .enablePlugins(ScalaJSPlugin, ScalaJSWeb)
  .settings(generalSettings: _*)
  .settings(
    name := "client",
    libraryDependencies += CrossDependencies.scalaTags,
    persistLauncher := true
  )
  .aggregate(protoJs)
  .dependsOn(protoJs)

现在既play不能client也不能解决原始类:(

(我也知道多余的PB.targets in Compile...,我只是认为共享可能不起作用,所以我再次将它添加到两个不同的设置中)



1> thesamet..:

使用纯CrossProject,您需要指定ScalaPB应查找原型文件的实际路径(它猜测的值是错误的).这是一个最小的例子:

lazy val proto = (crossProject.crossType(CrossType.Pure) in file("proto"))
  .settings(
    PB.targets in Compile := Seq(
      scalapb.gen() -> (sourceManaged in Compile).value
    ),
    // The trick is in this line:
    PB.protoSources in Compile := Seq(file("proto/src/main/protobuf")),
    libraryDependencies ++= Seq(
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion,
      "com.trueaccord.scalapb" %%% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf"
    )
  )

推荐阅读
周扒pi
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有