Skip to content

Instantly share code, notes, and snippets.

@harrah
Created May 17, 2010 21:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save harrah/404272 to your computer and use it in GitHub Desktop.
Save harrah/404272 to your computer and use it in GitHub Desktop.
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.
@harrah
Copy link
Author

harrah commented Sep 9, 2010

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