Scala Maven Plugin 编译报错:Cannot run program ... jre\bin\javac


问题背景

之前在编译一个老的 Maven + Scala 项目时,以为只是一次很普通的 compile,结果直接翻车。

控制台里报错是日志如下:

D:\ProgramFiles\Java\jdk1.8.0_341\bin\java.exe -Dmaven.multiModuleProjectDirectory=D:\Documents\work\codes\dgzwb-archival -Djansi.passthrough=true -Dmaven.home=D:\ProgramFiles\apache-maven-3.6.1 -Dclassworlds.conf=D:\ProgramFiles\apache-maven-3.6.1\bin\m2.conf "-Dmaven.ext.class.path=D:\ProgramFiles\JetBrains\IntelliJ IDEA 2022.3.3\plugins\maven\lib\maven-event-listener.jar" "-javaagent:D:\ProgramFiles\JetBrains\IntelliJ IDEA 2022.3.3\lib\idea_rt.jar=60685:D:\ProgramFiles\JetBrains\IntelliJ IDEA 2022.3.3\bin" -Dfile.encoding=UTF-8 -classpath D:\ProgramFiles\apache-maven-3.6.1\boot\plexus-classworlds-2.6.0.jar org.codehaus.classworlds.Launcher -Didea.version=2022.3.3 -s D:/ProgramFiles/apache-maven-3.6.1/conf/settings.xml -Dmaven.repo.local=D:/ProgramFiles/apache-maven-3.6.1/repository compile
[WARNING] 
[WARNING] Some problems were encountered while building the effective settings
[WARNING] Unrecognised tag: 'blocked' (position: START_TAG seen ...</url>\n      <blocked>... @216:16)  @ D:\ProgramFiles\apache-maven-3.6.1\conf\settings.xml, line 216, column 16
[WARNING] Unrecognised tag: 'blocked' (position: START_TAG seen ...</url>\n      <blocked>... @216:16)  @ D:\ProgramFiles\apache-maven-3.6.1\conf\settings.xml, line 216, column 16
[WARNING] 
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.surfilter:wangba_archival >--------------------
[INFO] Building wangba_archival 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:copy-resources (copy-resources) @ wangba_archival ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 9 resources
[INFO] The encoding used to copy filtered properties files have not been set. This means that the same encoding will be used to copy filtered properties files as when copying other filtered resources. This might not be what you want! Run your build with --debug to see which files might be affected. Read more at https://maven.apache.org/plugins/maven-resources-plugin/examples/filtering-properties-files.html
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ wangba_archival ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 9 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ wangba_archival ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\Documents\work\codes\dgzwb-archival\target\classes
[INFO] 
[INFO] --- scala-maven-plugin:4.5.0:compile (default) @ wangba_archival ---
[WARNING]  Expected all dependencies to require Scala version: 2.11.8
[WARNING]  com.surfilter:wangba_archival:1.0-SNAPSHOT requires scala version: 2.11.8
[WARNING]  org.json4s:json4s-core_2.11:3.2.11 requires scala version: 2.11.0
[WARNING] Multiple versions of scala libraries detected!
[INFO] Using incremental compilation using Mixed compile order
[INFO] Compiler bridge file: C:\Users\R5117\.sbt\1.0\zinc\org.scala-sbt\org.scala-sbt-compiler-bridge_2.11-1.5.1-bin_2.11.8__52.0-1.5.1_20210425T221145.jar
[INFO] compiling 32 Scala sources and 2 Java sources to D:\Documents\work\codes\dgzwb-archival\target\classes ...
[WARNING] D:/Documents/work/codes/dgzwb-archival/src/main/scala/com/surfilter/task/x1/X1Data.scala:38: a pure expression does nothing in statement position; you may be omitting necessary parentheses
[WARNING] D:/Documents/work/codes/dgzwb-archival/src/main/scala/com/surfilter/task/x1/X1Data2.scala:43: a pure expression does nothing in statement position; you may be omitting necessary parentheses
[WARNING] two warnings found
[WARNING] javac exited with exit code -1
[ERROR] ## Exception when compiling 34 sources to D:\Documents\work\codes\dgzwb-archival\target\classes
java.io.IOException: Cannot run program "D:\ProgramFiles\Java\jdk1.8.0_341\jre\bin\javac" (in directory "D:\Documents\work\codes\dgzwb-archival"): CreateProcess error=2, 系统找不到指定的文件。
java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
scala.sys.process.ProcessBuilderImpl$Simple.run(ProcessBuilderImpl.scala:85)
scala.sys.process.ProcessBuilderImpl$AbstractBuilder.run(ProcessBuilderImpl.scala:119)
scala.sys.process.ProcessBuilderImpl$AbstractBuilder.$anonfun$runBuffered$1(ProcessBuilderImpl.scala:205)
scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.scala:17)
sbt.internal.inc.javac.JavacLogger.buffer(JavacProcessLogger.scala:49)
scala.sys.process.ProcessBuilderImpl$AbstractBuilder.runBuffered(ProcessBuilderImpl.scala:205)
scala.sys.process.ProcessBuilderImpl$AbstractBuilder.$bang(ProcessBuilderImpl.scala:146)
sbt.internal.inc.javac.ForkedJava$.$anonfun$launch$3(ForkedJava.scala:55)
sbt.internal.inc.javac.ForkedJava$.$anonfun$launch$3$adapted(ForkedJava.scala:48)
sbt.internal.inc.javac.ForkedJava$.$anonfun$withArgumentFile$1(ForkedJava.scala:77)
sbt.io.IO$.withTemporaryDirectory(IO.scala:490)
sbt.io.IO$.withTemporaryDirectory(IO.scala:500)
sbt.internal.inc.javac.ForkedJava$.withArgumentFile(ForkedJava.scala:74)
sbt.internal.inc.javac.ForkedJava$.launch(ForkedJava.scala:48)
sbt.internal.inc.javac.ForkedJavaCompiler.run(ForkedJava.scala:103)
sbt.internal.inc.javac.AnalyzingJavaCompiler.$anonfun$compile$12(AnalyzingJavaCompiler.scala:167)
scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
sbt.internal.inc.javac.AnalyzingJavaCompiler.timed(AnalyzingJavaCompiler.scala:251)
sbt.internal.inc.javac.AnalyzingJavaCompiler.compile(AnalyzingJavaCompiler.scala:156)
sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compileJava$1(MixedAnalyzingCompiler.scala:98)
scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:241)
sbt.internal.inc.MixedAnalyzingCompiler.compileJava(MixedAnalyzingCompiler.scala:61)
sbt.internal.inc.MixedAnalyzingCompiler.compileJava0$1(MixedAnalyzingCompiler.scala:191)
sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:204)
sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:528)
sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:528)
sbt.internal.inc.Incremental$.$anonfun$apply$5(Incremental.scala:174)
sbt.internal.inc.Incremental$.$anonfun$apply$5$adapted(Incremental.scala:172)
sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:457)
sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:116)
sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:261)
sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:412)
sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:499)
sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:399)
sbt.internal.inc.Incremental$.apply(Incremental.scala:166)
sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:528)
sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:482)
sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:420)
sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
sbt_inc.SbtIncrementalCompiler.compile(SbtIncrementalCompiler.java:179)
scala_maven.ScalaCompilerSupport.incrementalCompile(ScalaCompilerSupport.java:356)
scala_maven.ScalaCompilerSupport.compile(ScalaCompilerSupport.java:113)
scala_maven.ScalaCompilerSupport.doExecute(ScalaCompilerSupport.java:88)
scala_maven.ScalaMojoSupport.execute(ScalaMojoSupport.java:308)
org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
org.apache.maven.cli.MavenCli.execute(MavenCli.java:956)
org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
org.apache.maven.cli.MavenCli.main(MavenCli.java:192)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
org.codehaus.classworlds.Launcher.main(Launcher.java:47)
           
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.119 s
[INFO] Finished at: 2023-10-07T10:29:50+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal net.alchim31.maven:scala-maven-plugin:4.5.0:compile (default) on project wangba_archival: wrap: java.io.IOException: Cannot run program "D:\ProgramFiles\Java\jdk1.8.0_341\jre\bin\javac" (in directory "D:\Documents\work\codes\dgzwb-archival"): CreateProcess error=2, 系统找不到指定的文件。 -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

Process finished with exit code 1

第一眼看上去,我当时还以为是Scala 插件坏了或者代码里有语法问题,依赖冲突把编译搞挂了. 结果往下看才发现,这次问题其实很典型:不是代码问题,而是环境里的 javac 路径找错了。


一开始最容易被带偏的地方

这份日志其实挺会“误导人”的。

因为在真正报错前,前面已经出现了不少 warning,比如:

[WARNING] Unrecognised tag: 'blocked'

还有:

Expected all dependencies to require Scala version: 2.11.8
org.json4s:json4s-core_2.11:3.2.11 requires scala version: 2.11.0

以及:

a pure expression does nothing in statement position

如果排查经验不够,很容易顺着这些 warning 一路查下去,最后越查越远。

我一开始也差点被这些信息带偏,尤其是看到 scala-maven-plugin 报错的时候,第一反应就是:是不是 Scala 版本冲突了,或者某个 Scala 文件写挂了。

但这类问题最怕的就是——真正导致构建失败的,往往不是前面最花哨的 warning,而是最后那条最朴素的报错。


真正要盯住的是哪一行

这次最关键的一行其实是:

Cannot run program "D:\ProgramFiles\Java\jdk1.8.0_341\jre\bin\javac"

看到这里,问题基本就已经很明确了。

因为正常情况下,javac 应该在这里:

D:\ProgramFiles\Java\jdk1.8.0_341\bin\javac.exe

而不是这里:

D:\ProgramFiles\Java\jdk1.8.0_341\jre\bin\javac

也就是说,编译过程去找了一个 根本不存在的路径

Windows 返回的 CreateProcess error=2 其实也已经把答案说得很直白了:

系统找不到指定的文件。

这时候就应该立刻把注意力从“代码有没有问题”切回到“环境是不是配错了”。


为什么 Scala 编译会去找 javac

这里还有一个很容易让人疑惑的点:

不是 Scala 项目吗,为什么会去调 javac

看日志就明白了,这个项目不是纯 Scala:

  • 32 个 Scala 源文件
  • 2 个 Java 源文件

也就是说,这是一个 Scala + Java 混编项目

scala-maven-plugin 在做增量编译的时候,会一起处理 Scala 和 Java 源码,所以它去调用 javac 是正常行为,不是异常行为。

换句话说,这次不是“插件莫名其妙找 javac”,而是:

它本来就该找,只不过找错地方了。


我最后是怎么定位到根因的

我后面重新顺着日志看了一遍,基本把问题归到这一类:

1. JAVA_HOME 很可能配错了

最常见的错误写法是:

JAVA_HOME=D:\ProgramFiles\Java\jdk1.8.0_341\jre

正确应该是:

JAVA_HOME=D:\ProgramFiles\Java\jdk1.8.0_341

只要把 JAVA_HOME 指到了 jre,很多构建工具在推断编译器路径的时候就可能出问题。


2. IDEA 内部跑 Maven 时,可能偷偷用了另一套 Java

这个也很常见。

有时候你命令行里的 javajavac 都没问题,但 IDEA 里一点 Maven Compile 还是挂。

原因是 IDEA 自己还有一套配置,比如:

  • Project SDK
  • Maven Runner JDK
  • Maven Importer JDK

只要其中某个地方配成了 JRE,就可能出现这种“命令行没事,IDEA 里死活不行”的情况。

这类问题最烦的地方就在这里:表面看像项目问题,实际上是 IDE 运行时环境和你想象的不一致。


3. 也不排除 JDK 安装本身就有问题

还有一种更直接的可能,就是这个文件本身就不存在:

D:\ProgramFiles\Java\jdk1.8.0_341\bin\javac.exe

如果连这个文件都没有,那就不是单纯的配置问题了,而是 JDK 安装不完整,或者装错了版本。


那前面的 warning 到底要不要管

要管,但要分优先级。

blocked 标签 warning

[WARNING] Unrecognised tag: 'blocked'

这个大概率是因为当前 Maven 版本太老,settings.xml 里却用了新配置项。

你这里用的是:

apache-maven-3.6.1

所以它认不出来这个标签,很正常。

但这个 warning 不会直接导致 javac 找不到。


Scala 版本不一致 warning

Expected all dependencies to require Scala version: 2.11.8
org.json4s:json4s-core_2.11:3.2.11 requires scala version: 2.11.0

这个说明依赖版本没有完全对齐,后面最好收敛一下,不然以后可能埋坑。

但就这次构建失败来说,它也不是主因。


a pure expression does nothing in statement position

这个属于代码 warning,通常表示某一行表达式写出来了,但实际上没有效果。

这种问题该修还是要修,不过它不会导致这次这种“连编译器进程都起不来”的失败。


我会怎么处理这个问题

如果下次我自己再碰到类似问题,我会直接按这个顺序查。

第一步:先确认 javac.exe 到底在不在

dir D:\ProgramFiles\Java\jdk1.8.0_341\bin\javac.exe

如果这个文件都不存在,那别折腾了,先重装 JDK。


第二步:检查环境变量

echo %JAVA_HOME%
where java
where javac
java -version
javac -version

重点看两件事:

  • JAVA_HOME 有没有指到 jre
  • where javac 找到的是不是你预期的那份 JDK

推荐配置应该是:

set JAVA_HOME=D:\ProgramFiles\Java\jdk1.8.0_341
set PATH=%JAVA_HOME%\bin;%PATH%

第三步:去 IDEA 里查 Maven 用的到底是谁

重点看:

  1. Project Structure -> Project SDK
  2. Settings -> Build Tools -> Maven -> Runner
  3. Settings -> Build Tools -> Maven -> Importing

全部都应该指向:

D:\ProgramFiles\Java\jdk1.8.0_341

不要指向:

D:\ProgramFiles\Java\jdk1.8.0_341\jre

第四步:再回过头清理 warning

比如:

  • 升级 Maven
  • 统一 Scala 依赖版本
  • 修掉无意义 expression warning

这些都值得做,但优先级一定排在“先让编译跑起来”之后。


这次踩坑最大的感受

这类报错特别像“假复杂问题”。

看起来日志一大堆:

  • Maven settings warning
  • Scala 版本 warning
  • Scala 代码 warning
  • 插件报错

很容易让人以为是个很深的依赖兼容问题。

但其实真正的根因很朴素:

构建环境把 javac 找到了 jre\bin 下面,而这个路径根本不对。

也就是说,这次不是业务代码翻车,不是 Scala 语法翻车,也不是依赖解析翻车,本质上还是 JDK / JRE 配置问题


最终结论

这次编译失败的核心原因可以总结成一句话:

scala-maven-plugin 在执行 Scala/Java 混合增量编译时,需要调用 javac,但运行环境把 javac 错误定位到了 JDK\jre\bin 目录下,最终触发 CreateProcess error=2

真正的修复重点也很明确:

  • 使用完整 JDK,而不是 JRE
  • JAVA_HOME 指向 JDK 根目录
  • PATH 中能找到正确的 javac
  • IDEA 里的 Maven Runner / Importer / Project SDK 都要确认一遍

这次排查时用到的命令

echo %JAVA_HOME%
where java
where javac
java -version
javac -version
dir D:\ProgramFiles\Java\jdk1.8.0_341\bin\javac.exe

总结

以后再看到这类错误,我会先提醒自己两件事:

  1. 不要先被一堆 warning 带偏
  2. 先看最后那条真正导致进程启动失败的报错

只要看到这种路径:

D:\ProgramFiles\Java\jdk1.8.0_341\jre\bin\javac

基本就该立刻想到:

不是 Scala 写挂了,而是 Java 环境配歪了。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录