2017-01-25 27 views
1

我有我的sbt多個子項目,一個是服務器(基於playframework)另一個客戶端(scala.js)和第三個是兩者之間的交流以protobuf(scalapb)的形式出現。ScalaPB與Scala.js和斯卡拉(jvm) - 有鏈接錯誤

現在,這是我的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.<init>(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 

我的IDE找到了一切,但顯然我做錯了什麼。我已經看過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...,我只是認爲共享可能無法在那裏工作,所以我將它添加到二再次設置爲stinct)

+1

'proto'應該是'crossProject',因爲JVM項目'play'和JS項目'client'都依賴於它。 – sjrd

+0

@sjrd你能舉個例子嗎?我知道https://www.scala-js.org/doc/project/cross-build.html,但確實在這裏忘記了這一點。 Mea culpa。但現在我添加了它,我得到的類不能再被找到,所以我認爲protobuf項目需要設置不同,以便有一個src文件夾,但仍然是編譯爲js和java。 – Sorona

+0

如果您沒有任何平臺特定的源目錄,請使用'crossProject.crossType(CrossType.Pure)'。 – sjrd

回答

4

對於純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" 
    ) 
)