我的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...
,我只是认为共享可能不起作用,所以我再次将它添加到两个不同的设置中)
使用纯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" ) )