Skip to content

Commit 0864a52

Browse files
committed
Add support for alternative system images to the compiler
This PR adds a new `-system` / `--system` setting to scalac which mimics its counterpart in javac. This fixes the biggest problem (at least for us) of scala/bug#13015. It is now possible to compile code against an older system image without enforcing strict module access. scalac generally does not enforce modules (scala/scala-dev#529) but it does when using `-release` (with class lookup based on `ct.sym`) and there was no way to opt out of these restrictions. The usual opt-out in javac is `--add-exports` but it is not supported for system modules in combination with `--release` (https://bugs.openjdk.org/browse/JDK-8178152) so there is no expectation that scalac could support it. Instead the solution for javac is to replace `--release` with a combination of `-source`, `-target` and a system image for the target version via `--system`. This combination, unlike `--release`, can be used with `--add-exports`. If scalac adds full module support at a later time (with access restrictions enabled by default) it will also need to support `--add-exports` and allow its use in combination with `--system`. I am also un-deprecating `-target` (which was deprecated in favor of `-release`) because it now has a legitimate use in combination with an alternative system image (just like in javac where it serves the same purpose and is not deprecated, either). # Conflicts: # src/compiler/scala/tools/nsc/Global.scala # src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala # src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
1 parent 4d41da9 commit 0864a52

File tree

4 files changed

+21
-10
lines changed

4 files changed

+21
-10
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
138138
def optimizerClassPath(base: ClassPath): ClassPath =
139139
base match {
140140
case AggregateClassPath(entries) if entries.head.isInstanceOf[CtSymClassPath] =>
141-
JrtClassPath(release = None, unsafe = None, closeableRegistry) match {
141+
JrtClassPath(release = None, settings.systemPathValue, unsafe = None, closeableRegistry) match {
142142
case jrt :: _ => AggregateClassPath(jrt +: entries.drop(1))
143143
case _ => base
144144
}

src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package scala.tools.nsc.classpath
1515
import java.io.{Closeable, File}
1616
import java.net.{URI, URL}
1717
import java.nio.file._
18+
import java.util.Collections
1819

1920
import scala.collection.JavaConverters._
2021
import scala.reflect.internal.JDK9Reflectors
@@ -130,9 +131,9 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
130131
}
131132

132133
object JrtClassPath {
133-
private val jrtClassPathCache = new FileBasedCache[Unit, JrtClassPath]()
134+
private val jrtClassPathCache = new FileBasedCache[Option[String], JrtClassPath]()
134135
private val ctSymClassPathCache = new FileBasedCache[String, CtSymClassPath]()
135-
def apply(release: Option[String], unsafe: Option[List[String]], closeableRegistry: CloseableRegistry): List[ClassPath] =
136+
def apply(release: Option[String], systemPath: Option[String], unsafe: Option[List[String]], closeableRegistry: CloseableRegistry): List[ClassPath] =
136137
if (!isJavaAtLeast("9")) Nil
137138
else {
138139
// TODO escalate errors once we're sure they are fatal
@@ -148,14 +149,14 @@ object JrtClassPath {
148149
val ct = createCt(version, closeableRegistry)
149150
unsafe match {
150151
case Some(pkgs) if pkgs.nonEmpty =>
151-
createJrt(closeableRegistry) match {
152+
createJrt(systemPath, closeableRegistry) match {
152153
case Nil => ct
153154
case jrt :: _ => ct :+ new FilteringJrtClassPath(jrt, pkgs: _*)
154155
}
155156
case _ => ct
156157
}
157158
case _ =>
158-
createJrt(closeableRegistry)
159+
createJrt(systemPath, closeableRegistry)
159160
}
160161
}
161162
private def createCt(v: String, closeableRegistry: CloseableRegistry): List[ClassPath] =
@@ -168,10 +169,15 @@ object JrtClassPath {
168169
} catch {
169170
case NonFatal(_) => Nil
170171
}
171-
private def createJrt(closeableRegistry: CloseableRegistry): List[JrtClassPath] =
172+
private def createJrt(systemPath: Option[String], closeableRegistry: CloseableRegistry): List[JrtClassPath] =
172173
try {
173-
val fs = FileSystems.getFileSystem(URI.create("jrt:/"))
174-
val classPath = jrtClassPathCache.getOrCreate((), Nil, () => new JrtClassPath(fs), closeableRegistry, checkStamps = false)
174+
val classPath = jrtClassPathCache.getOrCreate(systemPath, Nil, () => {
175+
val fs = systemPath match {
176+
case Some(javaHome) => FileSystems.newFileSystem(URI.create("jrt:/"), Collections.singletonMap("java.home", javaHome))
177+
case None => FileSystems.getFileSystem(URI.create("jrt:/"))
178+
}
179+
new JrtClassPath(fs, systemPath.isDefined)
180+
}, closeableRegistry, checkStamps = false)
175181
List(classPath)
176182
} catch {
177183
case _: ProviderNotFoundException | _: FileSystemNotFoundException => Nil
@@ -199,7 +205,7 @@ final class FilteringJrtClassPath(delegate: JrtClassPath, allowed: String*) exte
199205
*
200206
* The implementation assumes that no classes exist in the empty package.
201207
*/
202-
final class JrtClassPath(fs: FileSystem) extends ClassPath with NoSourcePaths {
208+
final class JrtClassPath(fs: FileSystem, closeFS: Boolean) extends ClassPath with NoSourcePaths with Closeable {
203209
type F = Path
204210
private val dir: Path = fs.getPath("/packages")
205211

@@ -245,6 +251,9 @@ final class JrtClassPath(fs: FileSystem) extends ClassPath with NoSourcePaths {
245251
}.take(1).toList.headOption
246252
}
247253
}
254+
255+
def close(): Unit =
256+
if (closeFS) fs.close()
248257
}
249258

250259
/**

src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ trait StandardScalaSettings { _: MutableSettings =>
3737
val javaextdirs = PathSetting ("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs)
3838
val sourcepath = PathSetting ("-sourcepath", "Specify location(s) of source files.", "") // Defaults.scalaSourcePath
3939
val rootdir = PathSetting ("-rootdir", "The absolute path of the project root directory, usually the git/scm checkout. Used by -Wconf.", "") withAbbreviation "--root-directory"
40+
val systemPath = PathSetting ("-system", "Override location of Java system modules", "") withAbbreviation "--system"
4041

4142
/** Other settings.
4243
*/
@@ -66,6 +67,7 @@ trait StandardScalaSettings { _: MutableSettings =>
6667
.withAbbreviation("--release")
6768
.withAbbreviation("-java-output-version")
6869
def releaseValue: Option[String] = release.valueSetByUser
70+
def systemPathValue: Option[String] = systemPath.valueSetByUser
6971
val target =
7072
ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 - 1.7 targets are deprecated.", AllTargetVersions, DefaultTargetVersion)
7173
.withPreSetHook(normalizeTarget)

src/compiler/scala/tools/util/PathResolver.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr
273273
sourcesInPath(sourcePath) // 7. The Scala source path.
274274
)
275275

276-
private def jrt: List[ClassPath] = JrtClassPath.apply(settings.releaseValue, settings.unsafe.valueSetByUser, closeableRegistry)
276+
private def jrt: List[ClassPath] = JrtClassPath.apply(settings.releaseValue, settings.systemPathValue, settings.unsafe.valueSetByUser, closeableRegistry)
277277

278278
lazy val containers = basis.flatten.distinct
279279

0 commit comments

Comments
 (0)