summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala28
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala102
-rw-r--r--src/compiler/scala/tools/nsc/io/Jar.scala84
-rw-r--r--src/compiler/scala/tools/nsc/io/package.scala8
-rw-r--r--src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala2
-rw-r--r--src/library/scala/util/Properties.scala10
-rw-r--r--test/files/neg/main1.check26
-rw-r--r--test/files/neg/main1.flags1
-rw-r--r--test/files/neg/main1.scala45
-rw-r--r--test/pending/run/jar-version.scala11
12 files changed, 265 insertions, 54 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
index faf86d37f6..228d4c6191 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
@@ -7,12 +7,12 @@ package scala.tools.nsc
package backend.jvm
import ch.epfl.lamp.fjbg._
-import java.io.{ DataOutputStream, OutputStream }
-import scala.tools.nsc.io.{ Path, Directory }
+import java.io.{ DataOutputStream, OutputStream, File => JFile }
+import scala.tools.nsc.io._
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.util.Javap
-import java.util.jar.{ JarEntry, JarOutputStream }
-import scala.tools.nsc.io.AbstractFile
+import java.util.jar.{ JarEntry, JarOutputStream, Attributes }
+import Attributes.Name
/** For the last mile: turning generated bytecode in memory into
* something you can use. Has implementations for writing to class
@@ -43,17 +43,23 @@ trait BytecodeWriters {
def close(): Unit = ()
}
- class DirectToJarfileWriter(val jarFile: AbstractFile) extends BytecodeWriter {
- private val out = new JarOutputStream(jarFile.bufferedOutput)
+ class DirectToJarfileWriter(jfile: JFile) extends BytecodeWriter {
+ val jarMainAttrs = (
+ if (settings.mainClass.isDefault) Nil
+ else List(Name.MAIN_CLASS -> settings.mainClass.value)
+ )
+ val writer = new Jar(jfile).jarWriter(jarMainAttrs: _*)
+
def writeClass(label: String, jclass: JClass, sym: Symbol) {
val path = jclass.getName + ".class"
- out putNextEntry new JarEntry(path)
- val dataStream = new DataOutputStream(out)
- try jclass writeTo dataStream
- finally dataStream.flush()
+ val out = writer.newOutputStream(path)
+
+ try jclass writeTo out
+ finally out.flush()
+
informProgress("added " + label + path + " to jar")
}
- override def close() = out.close()
+ override def close() = writer.close()
}
trait JavapBytecodeWriter extends BytecodeWriter {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 70dd8cd920..0036fd8060 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -36,7 +36,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
SerializableClass, StringClass, ClassClass, FunctionClass,
DeprecatedAttr, SerializableAttr, SerialVersionUIDAttr, VolatileAttr,
- TransientAttr, CloneableAttr, RemoteAttr
+ TransientAttr, CloneableAttr, RemoteAttr,
+ hasJavaMainMethod
}
val phaseName = "jvm"
@@ -67,29 +68,84 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
override def erasedTypes = true
def apply(cls: IClass) = sys.error("no implementation")
+ def isJavaEntryPoint(clasz: IClass) = {
+ val sym = clasz.symbol
+ def fail(msg: String) = {
+ clasz.cunit.warning(sym.pos,
+ sym.name + " has a main method, but " + sym.fullName('.') + " will not be a runnable program.\n" +
+ " " + msg + ", which means no static forwarder can be generated.\n"
+ // TODO: make this next claim true, if possible
+ // by generating valid main methods as static in module classes
+ // not sure what the jvm allows here
+ // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead."
+ )
+ false
+ }
+ sym.hasModuleFlag && hasJavaMainMethod(sym) && {
+ // At this point we've seen a module with a main method, so if this
+ // doesn't turn out to be a valid entry point, issue a warning.
+ val companion = sym.linkedClassOfClass
+ if (companion.isTrait)
+ fail("Its companion is a trait")
+ else if (hasJavaMainMethod(companion) && !(sym isSubClass companion))
+ fail("Its companion contains its own main method")
+ // this is only because forwarders aren't smart enough yet
+ else if (companion.tpe.member(nme.main) != NoSymbol)
+ fail("Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else
+ true
+ }
+ }
+
override def run() {
// we reinstantiate the bytecode generator at each run, to allow the GC
// to collect everything
- if (settings.debug.value) inform("[running phase " + name + " on icode]")
+ if (settings.debug.value)
+ inform("[running phase " + name + " on icode]")
+
if (settings.Xdce.value)
for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym))
icodes.classes -= sym
+ // For predictably ordered error messages.
+ val sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName)
+ val entryPoints = sortedClasses filter isJavaEntryPoint
+
val bytecodeWriter = settings.outputDirs.getSingleOutput match {
case Some(f) if f hasExtension "jar" =>
- new DirectToJarfileWriter(f)
+ // If no main class was specified, see if there's only one
+ // entry point among the classes going into the jar.
+ if (settings.mainClass.isDefault) {
+ entryPoints map (_.symbol fullName '.') match {
+ case Nil =>
+ log("No Main-Class designated or discovered.")
+ case name :: Nil =>
+ log("Unique entry point: setting Main-Class to " + name)
+ settings.mainClass.value = name
+ case names =>
+ log("No Main-Class due to multiple entry points:\n " + names.mkString("\n "))
+ }
+ }
+ else log("Main-Class was specified: " + settings.mainClass.value)
+
+ new DirectToJarfileWriter(f.file)
+
case _ =>
if (settings.Ygenjavap.isDefault) new ClassBytecodeWriter { }
else new ClassBytecodeWriter with JavapBytecodeWriter { }
}
+
val codeGenerator = new BytecodeGenerator(bytecodeWriter)
- classes.values foreach { c =>
+ log("Created new bytecode generator for " + classes.size + " classes.")
+
+ sortedClasses foreach { c =>
try codeGenerator.genClass(c)
catch {
case e: JCode.CodeSizeTooBigException =>
- log("Skipped class %s because it has methods that are too long.".format(c.toString))
+ log("Skipped class %s because it has methods that are too long.".format(c))
}
}
+
bytecodeWriter.close()
classes.clear()
}
@@ -158,10 +214,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
val isInner = sym.isClass && !sym.rawowner.isPackageClass && !sym.isModuleClass
// TODO: something atPhase(currentRun.flattenPhase.prev) which accounts for
// being nested in parameterized classes (if we're going to selectively flatten.)
- if (isInner) {
- log("Inner class: " + sym.fullLocationString)
+ if (isInner && !innerClassBuffer(sym))
innerClassBuffer += sym
- }
super.javaName(sym)
}
@@ -222,7 +276,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
var isRemoteClass: Boolean = false
var isParcelableClass = false
- private val innerClassBuffer = new mutable.ListBuffer[Symbol]
+ private var innerClassBuffer = mutable.LinkedHashSet[Symbol]()
def genClass(c: IClass) {
clasz = c
@@ -687,26 +741,32 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
// add inner classes which might not have been referenced yet
atPhase(currentRun.erasurePhase.next) {
for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass) ; m <- sym.info.decls ; if m.isClass)
- innerClassBuffer += m
+ if (!innerClassBuffer(m))
+ innerClassBuffer += m
}
val allInners = innerClassBuffer.toList
if (allInners.nonEmpty) {
+ log(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.")
val innerClassesAttr = jclass.getInnerClasses()
// sort them so inner classes succeed their enclosing class
// to satisfy the Eclipse Java compiler
- //for (innerSym <- innerClasses.toList sortBy (_.name.length)) {
- for (innerSym <- allInners.distinct sortBy (_.name.length)) {
- var flags = javaFlags(innerSym)
- if (innerSym.rawowner.hasModuleFlag)
- flags |= ACC_STATIC
-
- innerClassesAttr.addEntry(
- javaName(innerSym),
- outerName(innerSym),
- innerName(innerSym),
- flags & INNER_CLASSES_FLAGS
+ for (innerSym <- allInners sortBy (_.name.length)) {
+ val flags = {
+ val staticFlag = if (innerSym.rawowner.hasModuleFlag) ACC_STATIC else 0
+ (javaFlags(innerSym) | staticFlag) & INNER_CLASSES_FLAGS
+ }
+ val jname = javaName(innerSym)
+ val oname = outerName(innerSym)
+ val iname = innerName(innerSym)
+
+ // Mimicking javap inner class output
+ debuglog(
+ if (oname == null || iname == null) "//class " + jname
+ else "//%s=class %s of class %s".format(iname, jname, oname)
)
+
+ innerClassesAttr.addEntry(jname, oname, iname, flags)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/io/Jar.scala b/src/compiler/scala/tools/nsc/io/Jar.scala
index e8eab682fc..ad1598a85d 100644
--- a/src/compiler/scala/tools/nsc/io/Jar.scala
+++ b/src/compiler/scala/tools/nsc/io/Jar.scala
@@ -6,7 +6,7 @@
package scala.tools.nsc
package io
-import java.io.{ InputStream, OutputStream, IOException, FileNotFoundException, FileInputStream }
+import java.io.{ InputStream, OutputStream, IOException, FileNotFoundException, FileInputStream, DataOutputStream }
import java.util.jar._
import collection.JavaConverters._
import Attributes.Name
@@ -33,7 +33,9 @@ import util.ClassPath
// static Attributes.Name SPECIFICATION_VERSION
class Jar(file: File) extends Iterable[JarEntry] {
+ def this(jfile: JFile) = this(File(jfile))
def this(path: String) = this(File(path))
+
protected def errorFn(msg: String): Unit = Console println msg
lazy val jarFile = new JarFile(file.jfile)
@@ -45,7 +47,9 @@ class Jar(file: File) extends Iterable[JarEntry] {
try f(in)
finally in.close()
}
- def jarWriter() = new JarWriter(file)
+ def jarWriter(mainAttrs: (Attributes.Name, String)*) = {
+ new JarWriter(file, Jar.WManifest(mainAttrs: _*).underlying)
+ }
override def foreach[U](f: JarEntry => U): Unit = withJarInput { in =>
Iterator continually in.getNextJarEntry() takeWhile (_ != null) foreach f
@@ -60,26 +64,40 @@ class Jar(file: File) extends Iterable[JarEntry] {
override def toString = "" + file
}
-class JarWriter(file: File, val manifest: Manifest = new Manifest()) {
+class JarWriter(val file: File, val manifest: Manifest) {
private lazy val out = new JarOutputStream(file.outputStream(), manifest)
- def writeAllFrom(dir: Directory) = {
+
+ /** Adds a jar entry for the given path and returns an output
+ * stream to which the data should immediately be written.
+ * This unusual interface exists to work with fjbg.
+ */
+ def newOutputStream(path: String): DataOutputStream = {
+ val entry = new JarEntry(path)
+ out putNextEntry entry
+ new DataOutputStream(out)
+ }
+
+ def writeAllFrom(dir: Directory) {
try dir.list foreach (x => addEntry(x, ""))
finally out.close()
-
- file
}
- private def addFile(entry: File, prefix: String) {
- out putNextEntry new JarEntry(prefix + entry.name)
- try transfer(entry.inputStream(), out)
+ def addStream(entry: JarEntry, in: InputStream) {
+ out putNextEntry entry
+ try transfer(in, out)
finally out.closeEntry()
}
- private def addEntry(entry: Path, prefix: String) {
+ def addFile(file: File, prefix: String) {
+ val entry = new JarEntry(prefix + file.name)
+ addStream(entry, file.inputStream())
+ }
+ def addEntry(entry: Path, prefix: String) {
if (entry.isFile) addFile(entry.toFile, prefix)
else addDirectory(entry.toDirectory, prefix + entry.name + "/")
}
- private def addDirectory(entry: Directory, prefix: String) {
+ def addDirectory(entry: Directory, prefix: String) {
entry.list foreach (p => addEntry(p, prefix))
}
+
private def transfer(in: InputStream, out: OutputStream) = {
val buf = new Array[Byte](10240)
def loop(): Unit = in.read(buf, 0, buf.length) match {
@@ -88,9 +106,47 @@ class JarWriter(file: File, val manifest: Manifest = new Manifest()) {
}
loop
}
+
+ def close() = out.close()
}
object Jar {
+ type AttributeMap = java.util.Map[Attributes.Name, String]
+
+ object WManifest {
+ def apply(mainAttrs: (Attributes.Name, String)*): WManifest = {
+ val m = WManifest(new JManifest)
+ for ((k, v) <- mainAttrs)
+ m(k) = v
+
+ m
+ }
+ def apply(manifest: JManifest): WManifest = new WManifest(manifest)
+ implicit def unenrichManifest(x: WManifest): JManifest = x.underlying
+ }
+ class WManifest(manifest: JManifest) {
+ for ((k, v) <- initialMainAttrs)
+ this(k) = v
+
+ def underlying = manifest
+ def attrs = manifest.getMainAttributes().asInstanceOf[AttributeMap].asScala withDefaultValue null
+ def initialMainAttrs: Map[Attributes.Name, String] = {
+ import scala.util.Properties._
+ Map(
+ Name.MANIFEST_VERSION -> "1.0",
+ ScalaCompilerVersion -> versionNumberString
+ )
+ }
+
+ def apply(name: Attributes.Name): String = attrs(name)
+ def apply(name: String): String = apply(new Attributes.Name(name))
+ def update(key: Attributes.Name, value: String) = attrs.put(key, value)
+ def update(key: String, value: String) = attrs.put(new Attributes.Name(key), value)
+
+ def mainClass: String = apply(Name.MAIN_CLASS)
+ def mainClass_=(value: String) = update(Name.MAIN_CLASS, value)
+ }
+
// See http://download.java.net/jdk7/docs/api/java/nio/file/Path.html
// for some ideas.
private val ZipMagicNumber = List[Byte](80, 75, 3, 4)
@@ -100,10 +156,8 @@ object Jar {
def isJarOrZip(f: Path, examineFile: Boolean): Boolean =
f.hasExtension("zip", "jar") || (examineFile && magicNumberIsZip(f))
- def create(file: File, sourceDir: Directory, mainClass: String): File = {
- val writer = new Jar(file).jarWriter()
- writer.manifest(Name.MANIFEST_VERSION) = "1.0"
- writer.manifest(Name.MAIN_CLASS) = mainClass
+ def create(file: File, sourceDir: Directory, mainClass: String) {
+ val writer = new Jar(file).jarWriter(Name.MAIN_CLASS -> mainClass)
writer writeAllFrom sourceDir
}
}
diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala
index 2c5e50e970..d0a1d88086 100644
--- a/src/compiler/scala/tools/nsc/io/package.scala
+++ b/src/compiler/scala/tools/nsc/io/package.scala
@@ -12,14 +12,8 @@ import java.util.jar.{ Attributes }
package object io {
type JManifest = java.util.jar.Manifest
type JFile = java.io.File
- private[io] implicit def installManifestOps(m: JManifest) = new ManifestOps(m)
-
- class ManifestOps(manifest: JManifest) {
- def attrs = manifest.getMainAttributes()
- def apply(name: Attributes.Name) = "" + attrs.get(name)
- def update(key: Attributes.Name, value: String) = attrs.put(key, value)
- }
+ implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m)
private lazy val daemonThreadPool = DaemonThreadFactory.newPool()
def runnable(body: => Unit): Runnable = new Runnable { override def run() = body }
diff --git a/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala
index 8f38470129..08b1f7374e 100644
--- a/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AdvancedScalaSettings.scala
@@ -20,6 +20,7 @@ trait AdvancedScalaSettings {
val future: BooleanSetting
val generatephasegraph: StringSetting
val logimplicits: BooleanSetting
+ val mainClass: StringSetting
val migration: BooleanSetting
val noforwarders: BooleanSetting
val nojline: BooleanSetting
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index ed432562dc..2e7cb9595a 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -76,6 +76,7 @@ trait ScalaSettings extends AbsScalaSettings
val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).")
val resident = BooleanSetting ("-Xresident", "Compiler stays resident: read source filenames from standard input.")
val script = StringSetting ("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "")
+ val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "")
val Xshowcls = StringSetting ("-Xshow-class", "class", "Show internal representation of class.", "")
val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "")
val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.")
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index 23b53fb29f..1487b42843 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -25,6 +25,8 @@ object ClassPath {
def scalaLibrary = locate[ScalaObject]
def scalaCompiler = locate[Global]
+ def infoFor[T](value: T) = info(value.getClass)
+ def info[T](clazz: Class[T]) = new ClassAndJarInfo()(ClassManifest fromClass clazz)
def info[T: ClassManifest] = new ClassAndJarInfo[T]
def locate[T: ClassManifest] = info[T] rootClasspath
def locateJar[T: ClassManifest] = info[T].rootPossibles find (x => isJarOrZip(x)) map (x => File(x))
diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala
index d2c9e6770b..998661895b 100644
--- a/src/library/scala/util/Properties.scala
+++ b/src/library/scala/util/Properties.scala
@@ -10,11 +10,16 @@
package scala.util
import java.io.{ IOException, PrintWriter }
+import java.util.jar.Attributes.{ Name => AttributeName }
/** Loads `library.properties` from the jar. */
object Properties extends PropertiesTrait {
protected def propCategory = "library"
protected def pickJarBasedOn = classOf[ScalaObject]
+
+ /** Scala manifest attributes.
+ */
+ val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version")
}
private[scala] trait PropertiesTrait {
@@ -90,6 +95,11 @@ private[scala] trait PropertiesTrait {
Some(s)
}
+ /** Either the development or release version if known, otherwise
+ * the empty string.
+ */
+ def versionNumberString = scalaPropOrEmpty("version.number")
+
/** The version number of the jar this was loaded from plus "version " prefix,
* or "version (unknown)" if it cannot be determined.
*/
diff --git a/test/files/neg/main1.check b/test/files/neg/main1.check
new file mode 100644
index 0000000000..734c78e54d
--- /dev/null
+++ b/test/files/neg/main1.check
@@ -0,0 +1,26 @@
+main1.scala:3: error: Foo has a main method, but foo1.Foo will not be a runnable program.
+ Its companion is a trait, which means no static forwarder can be generated.
+
+ object Foo { // companion is trait
+ ^
+main1.scala:10: error: Foo has a main method, but foo2.Foo will not be a runnable program.
+ Its companion contains its own main method, which means no static forwarder can be generated.
+
+ object Foo { // companion has its own main
+ ^
+main1.scala:22: error: Foo has a main method, but foo3.Foo will not be a runnable program.
+ Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+
+ object Foo { // Companion contains main, but not an interfering main.
+ ^
+main1.scala:31: error: Foo has a main method, but foo4.Foo will not be a runnable program.
+ Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+
+ object Foo extends Foo { // Inherits main from the class
+ ^
+main1.scala:39: error: Foo has a main method, but foo5.Foo will not be a runnable program.
+ Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+
+ object Foo extends Foo { // Overrides main from the class
+ ^
+5 errors found
diff --git a/test/files/neg/main1.flags b/test/files/neg/main1.flags
new file mode 100644
index 0000000000..e8fb65d50c
--- /dev/null
+++ b/test/files/neg/main1.flags
@@ -0,0 +1 @@
+-Xfatal-warnings \ No newline at end of file
diff --git a/test/files/neg/main1.scala b/test/files/neg/main1.scala
new file mode 100644
index 0000000000..2b5551ac38
--- /dev/null
+++ b/test/files/neg/main1.scala
@@ -0,0 +1,45 @@
+// negatives
+package foo1 {
+ object Foo { // companion is trait
+ def main(args: Array[String]): Unit = ()
+ }
+ trait Foo
+}
+
+package foo2 {
+ object Foo { // companion has its own main
+ def main(args: Array[String]): Unit = ()
+ }
+ class Foo {
+ def main(args: Array[String]): Unit = ()
+ }
+}
+
+// these should all be made to work, but are negatives for now
+// because forwarders need more work.
+
+package foo3 {
+ object Foo { // Companion contains main, but not an interfering main.
+ def main(args: Array[String]): Unit = ()
+ }
+ class Foo {
+ def main(args: Int): Unit = ()
+ }
+}
+
+package foo4 {
+ object Foo extends Foo { // Inherits main from the class
+ }
+ class Foo {
+ def main(args: Array[String]): Unit = ()
+ }
+}
+
+package foo5 {
+ object Foo extends Foo { // Overrides main from the class
+ override def main(args: Array[String]): Unit = ()
+ }
+ class Foo {
+ def main(args: Array[String]): Unit = ()
+ }
+}
diff --git a/test/pending/run/jar-version.scala b/test/pending/run/jar-version.scala
new file mode 100644
index 0000000000..b79dfe733d
--- /dev/null
+++ b/test/pending/run/jar-version.scala
@@ -0,0 +1,11 @@
+import scala.util.Properties._
+import scala.tools.nsc.util.ClassPath._
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ infoFor(this).jarManifestMainAttrs get ScalaCompilerVersion match {
+ case Some(v) => println("I was built by scala compiler version " + v)
+ case _ => println("I was not apprised of which scala compiler version built me.")
+ }
+ }
+}