Created
May 17, 2010 21:49
-
-
Save harrah/404272 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Proposal: Default compiler/interpreter classpath in a managed environment | |
The compiler uses a default classpath of 'java.class.path'. Because many | |
applications do not use custom class loaders, this default is often sufficient | |
and no classpath configuration is required. This is because all jars- the | |
application and its dependencies, including the Scala compiler and library- are | |
in 'java.class.path' and the class loader for the application is the same as | |
that for Scala. | |
However, it does not work in more complicated setups, including various managed | |
environments. People running in web containers, for example, need to explicitly | |
set the classpath for the jars they want to compile/interpret against. | |
Another case is sbt. sbt runs, tests, and drops to the REPL in the same jvm as | |
sbt itself. This means "java.class.path" is just sbt-launcher.jar and the | |
default compiler classpath does not work. Additionally, the Scala class loader | |
is an ancestor of the application class loader. The Interpreter.breakIf method | |
won't work at all without duplicating the entire method, since it only uses the | |
default classpath and the class loader used is the one used to load the | |
Interpreter class. This class loader does not have access to the application | |
classes. | |
This proposal aims to make it possible to provide a default classpath to the | |
compiler with more granularity than a system property and to be able to provide | |
a different class loader to 'breakIf' and the REPL without much trouble. | |
The proposed approach is for the managed environment to put two resources on the | |
classpath: 'app.class.path' and 'boot.class.path'. (These names could be | |
modified to reduce possible collisions with application resources.) | |
The compiler/interpreter reads in these resources and uses these for the | |
classpath and boot classpath when they are not explicitly specified. These | |
would take precedence over java.class.path. | |
One way to implement this would be to add a method 'embeddedDefaults' to | |
settings. It would take a type parameter T with an implicit Manifest[T]. This | |
would be used to grab a ClassLoader. This loader would be searched for the | |
resources 'app.class.path' and 'boot.class.path'. In this way, the default | |
values can be specified per ClassLoader. Usage by the user looks like: | |
val settings = new Settings() | |
settings.embeddedDefaults[MyType] | |
Settings: | |
def embeddedDefaults[T: Manifest] { | |
val loader = implicitly[Manifest[T]].erasure.getClassLoader | |
explicitParentLoader = Some(loader) // for the Interpreter parentClassLoader | |
getClasspath("app", loader) foreach { classpath.value = _ } | |
getClasspath("boot", loader) foreach { | |
bootclasspath.value = settings.bootclasspath.value + File.separator + _ | |
} | |
} | |
private def getClasspath(id: String, loader: ClassLoader) = | |
Option(loader.getResource(id + ".class.path")).map { cp => | |
Source.fromURL(cp).mkString | |
} | |
Interpreter | |
protected def parentClassLoader = settings.explicitParentLoader.getOrElse( | |
this.getClass.getClassLoader ) | |
breakIf needs to be able to take a class loader for it to work in an embedded | |
environment: | |
def breakIf[T: Manifest](assertion: => Boolean, args: DebugParam[_]*) = ... | |
and then use settings.embeddedDefaults[T] in the implementation. | |
For normal interpreter usage, something similar could be done or the user could | |
be required to do it manually. |
This works for me even without overriding the parentClassLoader and without setting the context class loader, although the latter might be for something else you are doing.
It would be nice if you could set usejavacp and embeddedDefaults and have embeddedDefaults take precedence when 'app.class.path' exists, but that would be too invasive. So, I'm happy with the changeset.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ok. the change is working. I tested with 2.8.1-SNAPSHOT and the following creation sequence:
// make a custom parser
def mkParser (errLogger: String => Unit) = {
if (SS.getClass.getClassLoader.getResource("app.class.path") != null) {
val settings = new Settings(errLogger)
settings embeddedDefaults getClass.getClassLoader
println (">>>>>>>>>>>>>>" + settings.classpath.value)
println (">>>>>>>>>>>>>>" + settings.bootclasspath.value)
}