Scala 脚本无法在 Ubuntu 上运行

2024-03-15

我有一个以前可以工作的 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;DR

Ubuntu 的 Scala 包曾经与 Java 8 不兼容(这已在 2.11.12-4 中修复)。解决方案是卸载 Ubuntu 的 Scala 软件包并安装其中之一官方 Scala 包 https://www.scala-lang.org/download/all.html。这次你可能仍然想这样做,不是因为与 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)已更改为重写超类中返回 Buffer 的方法,以返回相应的子类型。

Bug: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-4774077 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-4774077
犯罪:https://github.com/AdoptOpenJDK/openjdk-jdk9/commit/d9d7e875470bf478110b849315b4fff55b4c35cf https://github.com/AdoptOpenJDK/openjdk-jdk9/commit/d9d7e875470bf478110b849315b4fff55b4c35cf

此更改不向后兼容二进制。如果在 Buffer 的子类之一中直接调用这些方法之一的某些 Java 代码是使用 JDK9+ 编译的,则生成的字节码将无法在 JRE8 中运行(即使根本不使用返回值)。发生这种情况是因为调用时方法的签名将被编译为java.nio.ByteBuffer.clear()Ljava/nio/ByteBufferJRE8 中不存在。但是,如果使用JDK8编译,编译成字节码的签名将是java/nio/ByteBuffer.clear:()Ljava/nio/Buffer它存在于 JRE8 和 JRE9+ 的 Buffer 类中。

Scala哪里出了问题? (或者做到了?)

Scala 编译器确实使用了一些受上述更改影响的方法。特别是,在源阅读器 https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/io/SourceReader.scala#L61OP问题中发生错误的类。

看着Scala 的兼容性矩阵 https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html,它说我们至少需要 Scala 2.11.12 才能使用 JDK11,但它并没有明确说明相反的兼容性方向。它确实说“Scala 2.12+ 绝对不能在 JDK 6 或 7 上工作”,所以我们可以预期 2.12+ 仍然与 JDK8 兼容,甚至 Scala 2.11 也兼容。

那么为什么他们会破坏兼容性呢?难道他们不能用旧的 JDK 版本编译 Scala 的源代码吗?他们没有,但他们可以,所以他们仍然这样做。

如果我们下载官方 Scala 包之一并检查清单文件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 源代码的 JDK 版本的上游选择,并一段时间以来一直在使用较新的 JDK 版本来编译 Ubuntu 的 Scala 包 https://launchpad.net/ubuntu/+source/scala/+changelog.

如果我们检查清单文件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)转换为其超类 Buffer 来实现的。实际上,这意味着将 Scala 的源代码从bytes.clear() to bytes.asInstanceOf[Buffer].clear().asInstanceOf[ByteBuffer](不知道为什么当结果来自时他们将其转换回 ByteBufferclear()好像根本没用过)。这是Ubuntu的补丁 http://launchpadlibrarian.net/398758355/scala_2.11.12-3_2.11.12-4.diff.gz.

Ubuntu 的方法似乎有点危险,因为其他不兼容的来源可能会被忽视,并且在某些非常特定的情况下仍然存在等待发生。此外,他们自己的设置与官方 Scala 版本不同,意味着整个 Scala 社区无法在实际场景中测试这些更改。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala 脚本无法在 Ubuntu 上运行 的相关文章

随机推荐

  • 美国天气雷达数据源或 API?

    美国是否有用于访问天气雷达数据的政府或私人 API NOAA 有一个 SOAP API http www nws noaa gov forecasts xml http www nws noaa gov forecasts xml 这里列出
  • Spring-MVC:什么是“上下文”和“命名空间”?

    From XmlWeb应用程序上下文 http javasourcecode org html open source spring spring 3 0 4 org springframework web context support
  • Python中字符串中重复字符的正则表达式

    我是正则表达式的新手 我有一个正则表达式 可以从字符串中删除重复字符 gt gt gt self repeat regexp re compile r w w 2 w gt gt gt self repl r 1 2 3 上面两行代码去掉了
  • 将代理附加到现有对象?

    我的计划是编写一个基于注释的缓存框架 用于缓存方法的返回值 当第一次使用特定参数调用方法时 缓存应该存储方法的返回值 当使用相同的参数第二次调用相同的方法时 该方法应该从缓存中返回先前计算的结果 并且不再执行其代码 我的注释如下所示 Cac
  • 如何在iPhone 6.0模拟器中添加视频? [复制]

    这个问题在这里已经有答案了 我在保存视频时遇到问题iPhone模拟器 我尝试过几种选择 例如去图书馆 但看起来不太酷 我想添加它们 以便以后可以在我的应用程序中选择它们 1 将视频文件拖放到模拟器中2 按选项卡栏上的操作按钮3 保存到相机胶
  • 在 OSX Ventura 上运行“__rvm_make -j8”RVM 安装时出错

    因为现在已经过去几个月了 我想我终于可以来这里问问了 我根本无法在 MacOS Ventura 上使用 RVM 安装任何版本的 Ruby 这似乎与其他人遇到的问题无关 我已经尝试了所有其他解决方案 但这似乎与 OpenSSL 或类似的东西无
  • 每个开发人员对 C# 类库的配置设置不同

    我们是一个小团队 致力于 ASP NET Web 项目以及服务 这两个项目都依赖于共享类库 我们希望每个开发人员的类库设置都不同 以及稍后的生产 设置中包含敏感信息 例如密码和主机名 我们应该如何分配这些设置 除非我错了 否则 web co
  • ReaderT静态环境

    宣言ReaderTmonad 转换器 为给定的 monad 添加静态环境 向给定的 monad 添加静态环境意味着什么 有人认为这是另一个问题的重复 我相信这个问题是独特的 因为我问的是静态环境意味着什么 而且我的问题与 ReaderT 有
  • 如何从内核读取性能计数器?

    我一直在用户空间使用Linux perf工具 我想编写代码 在每次执行上下文切换时读取线程的性能计数器 所需步骤是 1 获得一种读取性能计数器寄存器的机制 2 每次上下文切换后从调度程序调用步骤 1 我陷入了步骤 1 因为我无法弄清楚要调用
  • 仅在值之间使用分隔符打印

    我的代码输出有一个小问题 并且一直在搜索与此相同的主题 但我没有找到任何主题 while true System out print Enter a positive integer n sc nextInt System out prin
  • WinRT Chrome 应用程序如何在不触发操作系统的“打印”对话框的情况下进行打印?

    WinRT Chrome 应用程序如何在不触发操作系统的 打印 对话框的情况下进行打印 从我在其他地方看到的情况来看 每个 WinRT 应用程序都必须通过 Windows 打印对话框进行打印 但是 当您从 Chrome Metro 应用打印
  • Delphi 10.3 中使用 HTTPRIO 进行基本身份验证

    发生了变化HTTPRIO HTTPWebNode OnBeforePostDelphi 10 3 中的事件 在 Delphi 10 3 之前 事件处理程序是这样定义的 并且运行良好 procedure TForm1 HTTPRIO1HTTP
  • 检查 pandas 中任何列的任何行中是否存在一个值?

    是否有任何函数可以检查 pandas 中任何列的任何行中是否存在某个值 例如 columnA columnB columnC john 3 True mike 1 False bob 0 False 在上面的数据框中 我想知道是否有任何名为
  • Portlet 在 Plone 中如何呈现/排序/布局?

    我正在尝试更改 Plone 站点中 portlet 的全局排序 我搜索了 Plone 源代码 但找不到实际迭代并呈现 portlet 的代码 例如 plone rightcolumn 和 plone leftcolumn 内容提供程序 该代
  • 如何在 Linux 中查找所有以 .rb 结尾的文件?

    我所在的目录包含更多目录 我可以使用什么命令来获取所有以 rb 你可以尝试 find type f name rb
  • 在 Laravel 中将会话从文件传输到数据库

    我已经积累了超过6000万个会话 这导致了文件描述符耗尽的情况 通过将会话传输到数据库解决了该问题 在 Laravel 5 6 上测试 为了这 准备桌子 https laravel com docs 8 x session database
  • 使用子查询中的多个值进行更新

    假设我有一张桌子 items 有一个名为的整数列priority 我正在尝试从另一个表更新它 另一个表是一个临时表 我已经在其中预先计算了所有适当的priority要应用的值 UPDATE items SET priority SELECT
  • 将 DateTimePicker 值设置为 null

    我正在开发一个 WinForms UI 有两个DateTimePicker控制 最初 我想将控件的值设置为 null 直到用户选择日期 如果用户没有选择日期 则会将 null 值传递给数据库 默认情况下 它采用当前日期 您能否提出一些建议或
  • 检测实时应用程序中死锁的方法

    在实时多线程应用程序中检测死锁的方法有哪些 如果我们发现存在死锁 是否有任何方法可以解决它 而无需关闭 重新启动应用程序 有两种流行的方法来检测死锁 一种是让线程设置检查点 例如 如果您有一个具有工作循环的线程 您可以在开始工作时设置一个计
  • Scala 脚本无法在 Ubuntu 上运行

    我有一个以前可以工作的 Scala 脚本 当我尝试在新 PC 上运行它时 编译失败 所以我做了简单的脚本来测试 bin sh exec scala J Xmx2g 0 println test 尝试运行它我得到 test scala err