Skip to content

Commit

Permalink
Allow for a non-local Java Compiler to be used (#1988)
Browse files Browse the repository at this point in the history
Currently if you want to pass in some `javacOptions` that start with
`-J` the Java compiler will reject them since the local java compiler
won't accept `-J` flags. You can see this in an example on JDK 17 where
you need some `-J` flags to get the java semanticdb compiler plugin to
work:

```scala
object javaModule extends JavaModule {

  def ivyDeps = Agg(ivy"com.sourcegraph:semanticdb-javac:0.8.2")

  def javacOptions = Seq(
    "-J--add-exports",
    "-Jjdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
    "-J--add-exports",
    "-Jjdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
    "-J--add-exports",
    "-Jjdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
    "-J--add-exports",
    "-Jjdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
    "-J--add-exports",
    "-Jjdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
    s"-Xplugin:semanticdb -sourceroot:${T.workspace} -targetroot:${T.dest}"
  )

}
```

Which will result in:

```
❯ mill javaModule.compile
[19/19] javaModule.compile
[info] compiling 1 Java source to /Users/ckipp/Documents/scala-workspace/minimal/out/javaModule/compile.dest/classes ...
[warn] Javac is running in 'local' mode. These flags have been removed:
[warn]  -J--add-exports, -Jjdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED, -J--add-exports, -Jjdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED, -J--add-exports, -Jjdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED, -J--add-exports, -Jjdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED, -J--add-exports, -Jjdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
[error] An exception has occurred in the compiler (17.0.4). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
[error] java.lang.IllegalAccessError: class com.sourcegraph.semanticdb_javac.SemanticdbPlugin (in unnamed module @0x4e59433c) cannot access class com.sun.tools.javac.api.BasicJavacTask (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @0x4e59433c
...
```

This change looks at the `javacOptions` when creating the java compiler
and if detected makes sure you get a non-local compiler. If none are
detected, then the behavior basically is the same as it was before where
a local instance is used.

We also cache this to ensure it's not created over and over. However if
your `javacOptions` change, then we do give a new instance.

Fixes #1983

Pull request: #1988
  • Loading branch information
ckipp01 authored Aug 12, 2022
1 parent 8df8933 commit 080d704
Showing 1 changed file with 55 additions and 35 deletions.
90 changes: 55 additions & 35 deletions scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import java.io.File
import java.util.Optional
import java.util.concurrent.ConcurrentHashMap

import scala.ref.SoftReference
import scala.util.Properties.isWin
import mill.api.Loose.Agg
import mill.api.{CompileProblemReporter, KeyedLockedCache, PathRef, internal}
import mill.scalalib.api.{CompilationResult, ZincWorkerApi, ZincWorkerUtil => Util}
Expand All @@ -17,6 +15,9 @@ import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance
import xsbti.{PathBasedFile, VirtualFile}

import scala.annotation.nowarn
import scala.collection.mutable
import scala.ref.SoftReference
import scala.util.Properties.isWin

@internal
class ZincWorkerImpl(
Expand All @@ -32,40 +33,59 @@ class ZincWorkerImpl(
) extends ZincWorkerApi with AutoCloseable {
private val zincLogLevel = if (zincLogDebug) sbt.util.Level.Debug else sbt.util.Level.Info
private[this] val ic = new sbt.internal.inc.IncrementalCompilerImpl()
lazy val javaOnlyCompilers = {
// Keep the classpath as written by the user
val classpathOptions = ClasspathOptions.of(
/*bootLibrary*/ false,
/*compiler*/ false,
/*extra*/ false,
/*autoBoot*/ false,
/*filterLibrary*/ false
)
private val javaOnlyCompilersCache = mutable.Map.empty[Seq[String], SoftReference[Compilers]]

def javaOnlyCompilers(javacOptions: Seq[String]) = {
javaOnlyCompilersCache.get(javacOptions) match {
case Some(SoftReference(compilers)) => compilers
case _ =>
// Keep the classpath as written by the user
val classpathOptions = ClasspathOptions.of(
/*bootLibrary*/ false,
/*compiler*/ false,
/*extra*/ false,
/*autoBoot*/ false,
/*filterLibrary*/ false
)

val dummyFile = new java.io.File("")
// Zinc does not have an entry point for Java-only compilation, so we need
// to make up a dummy ScalaCompiler instance.
val scalac = ZincUtil.scalaCompiler(
new ScalaInstance(
version = "",
loader = null,
loaderCompilerOnly = null,
loaderLibraryOnly = null,
libraryJars = Array(dummyFile),
compilerJars = Array(dummyFile),
allJars = new Array(0),
explicitActual = Some("")
),
dummyFile,
classpathOptions // this is used for javac too
)
val dummyFile = new java.io.File("")
// Zinc does not have an entry point for Java-only compilation, so we need
// to make up a dummy ScalaCompiler instance.
val scalac = ZincUtil.scalaCompiler(
new ScalaInstance(
version = "",
loader = null,
loaderCompilerOnly = null,
loaderLibraryOnly = null,
libraryJars = Array(dummyFile),
compilerJars = Array(dummyFile),
allJars = new Array(0),
explicitActual = Some("")
),
dummyFile,
classpathOptions // this is used for javac too
)

ic.compilers(
instance = null,
classpathOptions,
None,
scalac
)
val javaTools = {
val (javaCompiler, javaDoc) =
// Local java compilers don't accept -J flags so when we put this together if we detect
// any javacOptions starting with -J we ensure we have a non-local Java compiler which
// can handle them.
if (javacOptions.exists(_.startsWith("-J"))) {
(javac.JavaCompiler.fork(None), javac.Javadoc.fork(None))

} else {
val compiler = javac.JavaCompiler.local.getOrElse(javac.JavaCompiler.fork(None))
val docs = javac.Javadoc.local.getOrElse(javac.Javadoc.fork())
(compiler, docs)
}
javac.JavaTools(javaCompiler, javaDoc)
}

val compilers = ic.compilers(javaTools, scalac)
javaOnlyCompilersCache.update(javacOptions, SoftReference(compilers))
compilers
}
}

val compilerBridgeLocks = collection.mutable.Map.empty[String, Object]
Expand Down Expand Up @@ -265,7 +285,7 @@ class ZincWorkerImpl(
compileClasspath,
javacOptions,
scalacOptions = Nil,
javaOnlyCompilers,
javaOnlyCompilers(javacOptions),
reporter
)
}
Expand Down

0 comments on commit 080d704

Please sign in to comment.