Scala 脚本不能在 Ubuntu 上运行

标签 scala ubuntu scala-script

我有一个以前工作的 Scala 脚本,当我尝试在新 PC 上运行它时,编译失败。

所以我做了简单的脚本来测试:

#!/bin/sh
exec scala -J-Xmx2g "$0" "$@"
!#

println("test")

并尝试运行它,我得到:
test.scala 
error: Compile server encountered fatal condition: java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer;
java.lang.NoSuchMethodError: java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer;
at scala.tools.nsc.io.SourceReader.read(SourceReader.scala:61)
at scala.tools.nsc.io.SourceReader.read(SourceReader.scala:40)
at scala.tools.nsc.io.SourceReader.read(SourceReader.scala:49)
at scala.tools.nsc.Global.getSourceFile(Global.scala:395)
at scala.tools.nsc.Global.getSourceFile(Global.scala:401)
at scala.tools.nsc.Global$Run$$anonfun$30.apply(Global.scala:1607)
at scala.tools.nsc.Global$Run$$anonfun$30.apply(Global.scala:1607)
at scala.collection.immutable.List.map(List.scala:284)
at scala.tools.nsc.Global$Run.compile(Global.scala:1607)
at scala.tools.nsc.StandardCompileServer.session(CompileServer.scala:151)
at scala.tools.util.SocketServer$$anonfun$doSession$1$$anonfun$apply$1.apply(SocketServer.scala:74)
at scala.tools.util.SocketServer$$anonfun$doSession$1$$anonfun$apply$1.apply(SocketServer.scala:74)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:65)
at scala.tools.util.SocketServer$$anonfun$doSession$1.apply(SocketServer.scala:74)
at scala.tools.util.SocketServer$$anonfun$doSession$1.apply(SocketServer.scala:69)
at scala.tools.nsc.io.Socket.applyReaderAndWriter(Socket.scala:49)
at scala.tools.util.SocketServer.doSession(SocketServer.scala:69)
at scala.tools.util.SocketServer.loop$1(SocketServer.scala:85)
at scala.tools.util.SocketServer.run(SocketServer.scala:97)
at scala.tools.nsc.CompileServer$$anonfun$execute$2$$anonfun$apply$mcZ$sp$1.apply$mcZ$sp(CompileServer.scala:218)
at scala.tools.nsc.CompileServer$$anonfun$execute$2$$anonfun$apply$mcZ$sp$1.apply(CompileServer.scala:213)
at scala.tools.nsc.CompileServer$$anonfun$execute$2$$anonfun$apply$mcZ$sp$1.apply(CompileServer.scala:213)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:53)
at scala.tools.nsc.CompileServer$$anonfun$execute$2.apply$mcZ$sp(CompileServer.scala:213)
at scala.tools.nsc.CompileServer$$anonfun$execute$2.apply(CompileServer.scala:213)
at scala.tools.nsc.CompileServer$$anonfun$execute$2.apply(CompileServer.scala:213)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withErr(Console.scala:80)
at scala.tools.nsc.CompileServer$.execute(CompileServer.scala:212)
at scala.tools.nsc.CompileServer$.main(CompileServer.scala:180)
at scala.tools.nsc.CompileServer.main(CompileServer.scala)

似乎 Scala 正在我的脚本附近编译一些东西,但我不太清楚如何调试和修复它。

最佳答案

TL;博士
Ubuntu 的 Scala 包曾经与 Java 8 不兼容(这已在 2.11.12-4 中修复)。解决方案是卸载 Ubuntu 的 Scala 软件包并安装 official Scala packages 之一。 .这一次你可能仍然想这样做,不是因为与 Java 不兼容,而是因为 Ubuntu 最新打包的 Scala 版本仍然是 2.11,而 Scala 目前的最新版本是 2.13。

sudo apt remove scala-library scala
wget https://downloads.lightbend.com/scala/2.13.4/scala-2.13.4.deb
sudo dpkg -i scala-2.13.4.deb
由于很多人都在问这个问题背后的原因,我也很好奇是什么原因造成的,所以我做了一些挖掘......
问题的根源
在 Java 9 中,Buffer 子类(包括 ByteBuffer)被更改为覆盖父类(super class)中返回 Buffer 的方法以返回相应的子类型。
错误:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-4774077
提交:https://github.com/AdoptOpenJDK/openjdk-jdk9/commit/d9d7e875470bf478110b849315b4fff55b4c35cf
此更改不是二进制向后兼容的。如果一些在 Buffer 的子类之一中直接调用这些方法的 Java 代码使用 JDK9+ 编译,则生成的字节码将不会在 JRE8 中运行(即使根本不使用返回值)。这是因为调用时方法的签名将被编译为 java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer这在 JRE8 中不存在。但是,如果使用 JDK8 编译,编译成字节码的签名将是 java/nio/ByteBuffer.clear:()Ljava/nio/Buffer它存在于 JRE8 和 JRE9+ 的 Buffer 类中。
Scala 哪里出错了? (或者做到了?)
Scala 编译器确实使用了一些受上述更改影响的方法。特别是在 SourceReader OP问题中发生错误的类。
Scala's compatibility matrix ,它说我们至少需要 Scala 2.11.12 才能使用 JDK11,但它并没有明确说明兼容性的相反方向。虽然它确实说“Scala 2.12+ 绝对不能在 JDK 6 或 7 上运行”,所以我们可以预期 2.12+ 仍然与 JDK8 兼容,甚至 Scala 2.11 更是如此。
为什么他们会破坏兼容性呢?他们不能只用旧的 JDK 版本编译 Scala 的源代码吗?他们没有,他们可以做到,以至于他们仍然这样做。
如果我们下载官方 Scala 软件包之一并检查 list 文件中的 scala-compiler.jar ,这就是我们发现的:
斯卡拉 2.11.12:
Bundle-Name: Scala Compiler
Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7
Bundle-SymbolicName: org.scala-lang.scala-compiler
Bundle-Version: 2.11.12.v20171031-225310-b8155a5502
Created-By: 1.6.0_45 (Sun Microsystems Inc.)
斯卡拉 2.13.4:
Bundle-Name: Scala Compiler
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-SymbolicName: org.scala-lang.scala-compiler
Bundle-Version: 2.13.4.v20201117-181115-VFINAL-39148e4
Created-By: 1.8.0_275 (AdoptOpenJDK)
因此,似乎 Scala 2.11 仍在使用 JDK6 进行编译,而 Scala 2.13 仍在使用 JDK8 进行编译。这不应该意味着它们都与 JRE8 兼容吗?是的,确实如此。那么错误来自哪里?
Ubuntu 哪里出错了?
与大多数其他 Linux 发行版一样,Ubuntu 喜欢构建自己的包,这些包可以通过其包管理器提供。这样做是为了确保操作系统生态系统中的一切正常运行,这通常意味着修补上游项目的源代码。
特别是关于 Scala 包,Ubuntu 决定放弃用于编译 Scala 源代码和 has been using newer JDK versions to compile Ubuntu's Scala package for a while 的上游 JDK 版本选择。 .
如果我们检查 list 文件中的 scala-compiler.jar在 Ubuntu 的 Scala 2.11.12-4 中,我们可以看到它是用 JDK11 编译的:
Created-By: 11.0.2+9-Ubuntu-3ubuntu1 (Oracle Corporation)
Bundle-Name: Scala Distribution
Bundle-SymbolicName: org.scala-ide.scala.compiler;singleton:=true
Bundle-Version: 2.11.12
你不是说问题在 2.11.12-4 中解决了吗?是的,我做到了。
Ubuntu 针对这个问题的解决方案不是用 JDK8 编译 Scala,而是对 Scala 的源代码打补丁,避免在子类中直接调用有问题的方法。这是通过在调用这些方法之前将 ByteBuffer(和 CharBuffer)转换为其父类(super class) Buffer 来实现的。实际上,这意味着将 Scala 的源代码从 bytes.clear() 更改为至bytes.asInstanceOf[Buffer].clear().asInstanceOf[ByteBuffer] (不知道为什么当 clear() 的结果似乎根本没有被使用时,他们将其转换回 ByteBuffer)。这里是 Ubuntu's patch .
Ubuntu 的方法似乎有点危险,因为其他不兼容的来源可能会被忽视,并且在某些非常特定的情况下仍然存在等待发生。此外,拥有与官方 Scala 版本不同的设置意味着没有整个 Scala 社区在真实案例中测试这些更改。

关于Scala 脚本不能在 Ubuntu 上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50876388/

相关文章:

scala - play 框架 2.0 演进,如何在 PROD 中将不一致的状态标记为已解决

scala - Elastic BeanStalk Docker应用程序,写入本地文件

ubuntu - SSM 始终以 Ubuntu 用户身份登录?

linux - 您将如何在 WSL2 上运行的 ubuntu 中正确安装 MATLAB?

php - 如何编辑/etc/phps/apache2/php.ini

scala - Intellij-idea 中的 scala 类、脚本和工作表有什么区别?

scala - 将多个函数与回调参数组合起来

scala - 如何为scala中的类创建别名