aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/config
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/config
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/config')
-rw-r--r--compiler/src/dotty/tools/dotc/config/CompilerCommand.scala128
-rw-r--r--compiler/src/dotty/tools/dotc/config/Config.scala138
-rw-r--r--compiler/src/dotty/tools/dotc/config/JavaPlatform.scala70
-rw-r--r--compiler/src/dotty/tools/dotc/config/OutputDirs.scala116
-rw-r--r--compiler/src/dotty/tools/dotc/config/PathResolver.scala281
-rw-r--r--compiler/src/dotty/tools/dotc/config/Platform.scala39
-rw-r--r--compiler/src/dotty/tools/dotc/config/Printers.scala34
-rw-r--r--compiler/src/dotty/tools/dotc/config/Properties.scala165
-rw-r--r--compiler/src/dotty/tools/dotc/config/ScalaSettings.scala267
-rw-r--r--compiler/src/dotty/tools/dotc/config/ScalaVersion.scala184
-rw-r--r--compiler/src/dotty/tools/dotc/config/Settings.scala270
-rw-r--r--compiler/src/dotty/tools/dotc/config/WrappedProperties.scala34
12 files changed, 1726 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala
new file mode 100644
index 000000000..19ede3cec
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala
@@ -0,0 +1,128 @@
+
+package dotty.tools.dotc
+package config
+
+import java.io.File
+import Settings._
+import core.Contexts._
+import util.DotClass
+import Properties._
+
+object CompilerCommand extends DotClass {
+
+ /** The name of the command */
+ def cmdName = "scalac"
+
+ private def explainAdvanced = """
+ |-- Notes on option parsing --
+ |Boolean settings are always false unless set.
+ |Where multiple values are accepted, they should be comma-separated.
+ | example: -Xplugin:plugin1,plugin2
+ |<phases> means one or a comma-separated list of:
+ | - (partial) phase names with an optional "+" suffix to include the next phase
+ | - the string "all"
+ | example: -Xprint:all prints all phases.
+ | example: -Xprint:front,mixin prints the frontend and mixin phases.
+ | example: -Ylog:erasure+ logs the erasure phase and the phase after the erasure phase.
+ | This is useful because during the tree transform of phase X, we often
+ | already are in phase X + 1.
+ """
+
+ def shortUsage = s"Usage: $cmdName <options> <source files>"
+
+ def versionMsg = s"Dotty compiler $versionString -- $copyrightString"
+
+ /** Distill arguments into summary detailing settings, errors and files to compiler */
+ def distill(args: Array[String])(implicit ctx: Context): ArgsSummary = {
+ /**
+ * Expands all arguments starting with @ to the contents of the
+ * file named like each argument.
+ */
+ def expandArg(arg: String): List[String] = unsupported("expandArg")/*{
+ def stripComment(s: String) = s takeWhile (_ != '#')
+ val file = File(arg stripPrefix "@")
+ if (!file.exists)
+ throw new java.io.FileNotFoundException("argument file %s could not be found" format file.name)
+
+ settings splitParams (file.lines() map stripComment mkString " ")
+ }*/
+
+ // expand out @filename to the contents of that filename
+ def expandedArguments = args.toList flatMap {
+ case x if x startsWith "@" => expandArg(x)
+ case x => List(x)
+ }
+
+ ctx.settings.processArguments(expandedArguments, processAll = true)
+ }
+
+ /** Provide usage feedback on argument summary, assuming that all settings
+ * are already applied in context.
+ * @return The list of files to compile.
+ */
+ def checkUsage(summary: ArgsSummary, sourcesRequired: Boolean)(implicit ctx: Context): List[String] = {
+ val settings = ctx.settings
+
+ /** Creates a help message for a subset of options based on cond */
+ def availableOptionsMsg(cond: Setting[_] => Boolean): String = {
+ val ss = (ctx.settings.allSettings filter cond).toList sortBy (_.name)
+ val width = (ss map (_.name.length)).max
+ def format(s: String) = ("%-" + width + "s") format s
+ def helpStr(s: Setting[_]) = s"${format(s.name)} ${s.description}"
+ ss map helpStr mkString "\n"
+ }
+
+ def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting[_] => Boolean): String = {
+ val prefix = List(
+ Some(shortUsage),
+ Some(explainAdvanced) filter (_ => shouldExplain),
+ Some(label + " options include:")
+ ).flatten mkString "\n"
+
+ prefix + "\n" + availableOptionsMsg(cond)
+ }
+
+ def isStandard(s: Setting[_]): Boolean = !isAdvanced(s) && !isPrivate(s)
+ def isAdvanced(s: Setting[_]): Boolean = s.name startsWith "-X"
+ def isPrivate(s: Setting[_]) : Boolean = s.name startsWith "-Y"
+
+ /** Messages explaining usage and options */
+ def usageMessage = createUsageMsg("where possible standard", shouldExplain = false, isStandard)
+ def xusageMessage = createUsageMsg("Possible advanced", shouldExplain = true, isAdvanced)
+ def yusageMessage = createUsageMsg("Possible private", shouldExplain = true, isPrivate)
+
+ def shouldStopWithInfo = {
+ import settings._
+ Set(help, Xhelp, Yhelp, showPlugins, showPhases) exists (_.value)
+ }
+
+ def infoMessage: String = {
+ import settings._
+ if (help.value) usageMessage
+ else if (Xhelp.value) xusageMessage
+ else if (Yhelp.value) yusageMessage
+// else if (showPlugins.value) global.pluginDescriptions
+// else if (showPhases.value) global.phaseDescriptions + (
+// if (debug.value) "\n" + global.phaseFlagDescriptions else ""
+// )
+ else ""
+ }
+
+ if (summary.errors.nonEmpty) {
+ summary.errors foreach (ctx.error(_))
+ ctx.echo(" dotc -help gives more information")
+ Nil
+ }
+ else if (settings.version.value) {
+ ctx.echo(versionMsg)
+ Nil
+ }
+ else if (shouldStopWithInfo) {
+ ctx.echo(infoMessage)
+ Nil
+ } else {
+ if (sourcesRequired && summary.arguments.isEmpty) ctx.echo(usageMessage)
+ summary.arguments
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala
new file mode 100644
index 000000000..7744a5479
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/Config.scala
@@ -0,0 +1,138 @@
+package dotty.tools.dotc.config
+
+object Config {
+
+ final val cacheMembersNamed = true
+ final val cacheAsSeenFrom = true
+ final val useFingerPrints = true // note: it currently seems to be slightly faster not to use them! my junit test: 548s without, 560s with.
+ final val cacheMemberNames = true
+ final val cacheImplicitScopes = true
+
+ final val checkCacheMembersNamed = false
+
+ /** When updating a constraint bound, check that the constrained parameter
+ * does not appear at the top-level of either of its bounds.
+ */
+ final val checkConstraintsNonCyclic = false
+
+ /** Make sure none of the bounds of a parameter in an OrderingConstraint
+ * contains this parameter at its toplevel (i.e. as an operand of a
+ * combination of &'s and |'s.). The check is performed each time a new bound
+ * is added to the constraint.
+ */
+ final val checkConstraintsSeparated = false
+
+ /** Check that each constraint resulting from a subtype test
+ * is satisfiable.
+ */
+ final val checkConstraintsSatisfiable = false
+
+ /** Check that each constraint is fully propagated. i.e.
+ * If P <: Q then the upper bound of P is a subtype of the upper bound of Q
+ * and the lower bound of Q is a subtype of the lower bound of P.
+ */
+ final val checkConstraintsPropagated = false
+
+ /** Check that constraints of globally committable typer states are closed.
+ * NOTE: When enabled, the check can cause CyclicReference errors because
+ * it traverses all elements of a type. Such failures were observed when
+ * compiling all of dotty together (source seems to be in GenBCode which
+ * accesses javac's settings.)
+ *
+ * It is recommended to turn this option on only when chasing down
+ * a PolyParam instantiation error. See comment in Types.TypeVar.instantiate.
+ */
+ final val debugCheckConstraintsClosed = false
+
+ /** Check that no type appearing as the info of a SymDenotation contains
+ * skolem types.
+ */
+ final val checkNoSkolemsInInfo = false
+
+ /** Type comparer will fail with an assert if the upper bound
+ * of a constrained parameter becomes Nothing. This should be turned
+ * on only for specific debugging as normally instantiation to Nothing
+ * is not an error consdition.
+ */
+ final val failOnInstantiationToNothing = false
+
+ /** Enable noDoubleDef checking if option "-YnoDoubleDefs" is set.
+ * The reason to have an option as well as the present global switch is
+ * that the noDoubleDef checking is done in a hotspot, and we do not
+ * want to incur the overhead of checking an option each time.
+ */
+ final val checkNoDoubleBindings = true
+
+ /** Check positions for consistency after parsing */
+ final val checkPositions = true
+
+ /** Show subtype traces for all deep subtype recursions */
+ final val traceDeepSubTypeRecursions = false
+
+ /** When explaining subtypes and this flag is set, also show the classes of the compared types. */
+ final val verboseExplainSubtype = true
+
+ /** If this flag is set, take the fast path when comparing same-named type-aliases and types */
+ final val fastPathForRefinedSubtype = true
+
+ /** If this flag is set, higher-kinded applications are checked for validity
+ */
+ final val checkHKApplications = false
+
+ /** The recursion depth for showing a summarized string */
+ final val summarizeDepth = 2
+
+ /** Check that variances of lambda arguments match the
+ * variance of the underlying lambda class.
+ */
+ final val checkLambdaVariance = false
+
+ /** Check that certain types cannot be created in erasedTypes phases.
+ * Note: Turning this option on will get some false negatives, since it is
+ * possible that And/Or types are still created during erasure as the result
+ * of some operation on an existing type.
+ */
+ final val checkUnerased = false
+
+ /** In `derivedSelect`, rewrite
+ *
+ * (S & T)#A --> S#A & T#A
+ * (S | T)#A --> S#A | T#A
+ *
+ * Not sure whether this is useful. Preliminary measurements show a slowdown of about
+ * 7% for the build when this option is enabled.
+ */
+ final val splitProjections = false
+
+ /** If this flag is on, always rewrite an application `S[Ts]` where `S` is an alias for
+ * `[Xs] -> U` to `[Xs := Ts]U`.
+ * Turning this flag on was observed to give a ~6% speedup on the JUnit test suite.
+ */
+ final val simplifyApplications = true
+
+ /** Initial size of superId table */
+ final val InitialSuperIdsSize = 4096
+
+ /** Initial capacity of uniques HashMap */
+ final val initialUniquesCapacity = 40000
+
+ /** How many recursive calls to NamedType#underlying are performed before logging starts. */
+ final val LogPendingUnderlyingThreshold = 50
+
+ /** How many recursive calls to isSubType are performed before logging starts. */
+ final val LogPendingSubTypesThreshold = 50
+
+ /** How many recursive calls to findMember are performed before logging names starts
+ * Note: this threshold has to be chosen carefully. Too large, and programs
+ * like tests/pos/IterableSelfRec go into polynomial (or even exponential?)
+ * compile time slowdown. Too small and normal programs will cause the compiler to
+ * do inefficient operations on findMember. The current value is determined
+ * so that (1) IterableSelfRec still compiles in reasonable time (< 10sec) (2) Compiling
+ * dotty itself only causes small pending names lists to be generated (we measured
+ * at max 6 elements) and these lists are never searched with contains.
+ */
+ final val LogPendingFindMemberThreshold = 10
+
+ /** Maximal number of outstanding recursive calls to findMember */
+ final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
+}
diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
new file mode 100644
index 000000000..a695202d3
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
@@ -0,0 +1,70 @@
+package dotty.tools
+package dotc
+package config
+
+import io.{AbstractFile,ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath}
+import ClassPath.{ JavaContext, DefaultJavaContext }
+import core._
+import Symbols._, Types._, Contexts._, Denotations._, SymDenotations._, StdNames._, Names._
+import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
+import transform.ExplicitOuter, transform.SymUtils._
+
+class JavaPlatform extends Platform {
+
+ private var currentClassPath: Option[MergedClassPath] = None
+
+ def classPath(implicit ctx: Context): ClassPath = {
+ if (currentClassPath.isEmpty)
+ currentClassPath = Some(new PathResolver().result)
+ val cp = currentClassPath.get
+ //println(cp)
+ cp
+ }
+
+ // The given symbol is a method with the right name and signature to be a runnable java program.
+ def isJavaMainMethod(sym: SymDenotation)(implicit ctx: Context) =
+ (sym.name == nme.main) && (sym.info match {
+ case t@MethodType(_, defn.ArrayOf(el) :: Nil) => el =:= defn.StringType && (t.resultType isRef defn.UnitClass)
+ case _ => false
+ })
+
+ // The given class has a main method.
+ def hasJavaMainMethod(sym: Symbol)(implicit ctx: Context): Boolean =
+ (sym.info member nme.main).hasAltWith {
+ case x: SymDenotation => isJavaMainMethod(x)
+ case _ => false
+ }
+
+ /** Update classpath with a substituted subentry */
+ def updateClassPath(subst: Map[ClassPath, ClassPath]) =
+ currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst))
+
+ def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = new ctx.base.loaders.PackageLoader(root, classPath)
+
+ /** Is the SAMType `cls` also a SAM under the rules of the JVM? */
+ def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean =
+ cls.is(NoInitsTrait) &&
+ cls.superClass == defn.ObjectClass &&
+ cls.directlyInheritedTraits.forall(_.is(NoInits)) &&
+ !ExplicitOuter.needsOuterIfReferenced(cls) &&
+ cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary
+
+ /** We could get away with excluding BoxedBooleanClass for the
+ * purpose of equality testing since it need not compare equal
+ * to anything but other booleans, but it should be present in
+ * case this is put to other uses.
+ */
+ def isMaybeBoxed(sym: ClassSymbol)(implicit ctx: Context) = {
+ val d = defn
+ import d._
+ (sym == ObjectClass) ||
+ (sym == JavaSerializableClass) ||
+ (sym == ComparableClass) ||
+ (sym derivesFrom BoxedNumberClass) ||
+ (sym derivesFrom BoxedCharClass) ||
+ (sym derivesFrom BoxedBooleanClass)
+ }
+
+ def newClassLoader(bin: AbstractFile)(implicit ctx: Context): SymbolLoader =
+ new ClassfileLoader(bin)
+}
diff --git a/compiler/src/dotty/tools/dotc/config/OutputDirs.scala b/compiler/src/dotty/tools/dotc/config/OutputDirs.scala
new file mode 100644
index 000000000..a87eb9bce
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/OutputDirs.scala
@@ -0,0 +1,116 @@
+package dotty.tools
+package dotc
+package config
+
+import io._
+
+/** A class for holding mappings from source directories to
+ * their output location. This functionality can be accessed
+ * only programmatically. The command line compiler uses a
+ * single output location, but tools may use this functionality
+ * to set output location per source directory.
+ */
+class OutputDirs {
+ /** Pairs of source directory - destination directory. */
+ private var outputDirs: List[(AbstractFile, AbstractFile)] = Nil
+
+ /** If this is not None, the output location where all
+ * classes should go.
+ */
+ private var singleOutDir: Option[AbstractFile] = None
+
+ /** Add a destination directory for sources found under srcdir.
+ * Both directories should exits.
+ */
+ def add(srcDir: String, outDir: String): Unit =
+ add(checkDir(AbstractFile.getDirectory(srcDir), srcDir),
+ checkDir(AbstractFile.getDirectory(outDir), outDir))
+
+ /** Check that dir is exists and is a directory. */
+ private def checkDir(dir: AbstractFile, name: String, allowJar: Boolean = false): AbstractFile = (
+ if (dir != null && dir.isDirectory)
+ dir
+ // was: else if (allowJar && dir == null && Path.isJarOrZip(name, false))
+ else if (allowJar && dir == null && Jar.isJarOrZip(name, false))
+ new PlainFile(Path(name))
+ else
+ throw new FatalError(name + " does not exist or is not a directory"))
+
+ /** Set the single output directory. From now on, all files will
+ * be dumped in there, regardless of previous calls to 'add'.
+ */
+ def setSingleOutput(outDir: String): Unit = {
+ val dst = AbstractFile.getDirectory(outDir)
+ setSingleOutput(checkDir(dst, outDir, true))
+ }
+
+ def getSingleOutput: Option[AbstractFile] = singleOutDir
+
+ /** Set the single output directory. From now on, all files will
+ * be dumped in there, regardless of previous calls to 'add'.
+ */
+ def setSingleOutput(dir: AbstractFile): Unit = {
+ singleOutDir = Some(dir)
+ }
+
+ def add(src: AbstractFile, dst: AbstractFile): Unit = {
+ singleOutDir = None
+ outputDirs ::= ((src, dst))
+ }
+
+ /** Return the list of source-destination directory pairs. */
+ def outputs: List[(AbstractFile, AbstractFile)] = outputDirs
+
+ /** Return the output directory for the given file.
+ */
+ def outputDirFor(src: AbstractFile): AbstractFile = {
+ def isBelow(srcDir: AbstractFile, outDir: AbstractFile) =
+ src.path.startsWith(srcDir.path)
+
+ singleOutDir match {
+ case Some(d) => d
+ case None =>
+ (outputs find (isBelow _).tupled) match {
+ case Some((_, d)) => d
+ case _ =>
+ throw new FatalError("Could not find an output directory for "
+ + src.path + " in " + outputs)
+ }
+ }
+ }
+
+ /** Return the source file path(s) which correspond to the given
+ * classfile path and SourceFile attribute value, subject to the
+ * condition that source files are arranged in the filesystem
+ * according to Java package layout conventions.
+ *
+ * The given classfile path must be contained in at least one of
+ * the specified output directories. If it does not then this
+ * method returns Nil.
+ *
+ * Note that the source file is not required to exist, so assuming
+ * a valid classfile path this method will always return a list
+ * containing at least one element.
+ *
+ * Also that if two or more source path elements target the same
+ * output directory there will be two or more candidate source file
+ * paths.
+ */
+ def srcFilesFor(classFile: AbstractFile, srcPath: String): List[AbstractFile] = {
+ def isBelow(srcDir: AbstractFile, outDir: AbstractFile) =
+ classFile.path.startsWith(outDir.path)
+
+ singleOutDir match {
+ case Some(d) =>
+ d match {
+ case _: VirtualDirectory | _: io.ZipArchive => Nil
+ case _ => List(d.lookupPathUnchecked(srcPath, false))
+ }
+ case None =>
+ (outputs filter (isBelow _).tupled) match {
+ case Nil => Nil
+ case matches => matches.map(_._1.lookupPathUnchecked(srcPath, false))
+ }
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala
new file mode 100644
index 000000000..aa4d8aeb0
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala
@@ -0,0 +1,281 @@
+package dotty.tools
+package dotc
+package config
+
+import java.net.{ URL, MalformedURLException }
+import WrappedProperties.AccessControl
+import io.{ ClassPath, JavaClassPath, File, Directory, Path, AbstractFile }
+import ClassPath.{ JavaContext, DefaultJavaContext, join, split }
+import PartialFunction.condOpt
+import scala.language.postfixOps
+import core.Contexts._
+import Settings._
+
+// Loosely based on the draft specification at:
+// https://wiki.scala-lang.org/display/SW/Classpath
+
+object PathResolver {
+
+ // Imports property/environment functions which suppress
+ // security exceptions.
+ import AccessControl._
+
+ def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse ""
+
+ /** Map all classpath elements to absolute paths and reconstruct the classpath.
+ */
+ def makeAbsolute(cp: String) = ClassPath.map(cp, x => Path(x).toAbsolute.path)
+
+ /** pretty print class path */
+ def ppcp(s: String) = split(s) match {
+ case Nil => ""
+ case Seq(x) => x
+ case xs => xs map ("\n" + _) mkString
+ }
+
+ /** Values found solely by inspecting environment or property variables.
+ */
+ object Environment {
+ private def searchForBootClasspath = (
+ systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse ""
+ )
+
+ /** Environment variables which java pays attention to so it
+ * seems we do as well.
+ */
+ def classPathEnv = envOrElse("CLASSPATH", "")
+ def sourcePathEnv = envOrElse("SOURCEPATH", "")
+
+ def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath)
+
+ def javaExtDirs = propOrEmpty("java.ext.dirs")
+ def scalaHome = propOrEmpty("scala.home")
+ def scalaExtDirs = propOrEmpty("scala.ext.dirs")
+
+ /** The java classpath and whether to use it. */
+ def javaUserClassPath = propOrElse("java.class.path", "")
+ def useJavaClassPath = propOrFalse("scala.usejavacp")
+
+ override def toString = s"""
+ |object Environment {
+ | scalaHome = $scalaHome (useJavaClassPath = $useJavaClassPath)
+ | javaBootClassPath = <${javaBootClassPath.length} chars>
+ | javaExtDirs = ${ppcp(javaExtDirs)}
+ | javaUserClassPath = ${ppcp(javaUserClassPath)}
+ | scalaExtDirs = ${ppcp(scalaExtDirs)}
+ |}""".trim.stripMargin
+ }
+
+ /** Default values based on those in Environment as interpreted according
+ * to the path resolution specification.
+ */
+ object Defaults {
+ def scalaSourcePath = Environment.sourcePathEnv
+ def javaBootClassPath = Environment.javaBootClassPath
+ def javaUserClassPath = Environment.javaUserClassPath
+ def javaExtDirs = Environment.javaExtDirs
+ def useJavaClassPath = Environment.useJavaClassPath
+
+ def scalaHome = Environment.scalaHome
+ def scalaHomeDir = Directory(scalaHome)
+ def scalaHomeExists = scalaHomeDir.isDirectory
+ def scalaLibDir = Directory(scalaHomeDir / "lib")
+ def scalaClassesDir = Directory(scalaHomeDir / "classes")
+
+ def scalaLibAsJar = File(scalaLibDir / "scala-library.jar")
+ def scalaLibAsDir = Directory(scalaClassesDir / "library")
+
+ def scalaLibDirFound: Option[Directory] =
+ if (scalaLibAsJar.isFile) Some(scalaLibDir)
+ else if (scalaLibAsDir.isDirectory) Some(scalaClassesDir)
+ else None
+
+ def scalaLibFound =
+ if (scalaLibAsJar.isFile) scalaLibAsJar.path
+ else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path
+ else ""
+
+ // XXX It must be time for someone to figure out what all these things
+ // are intended to do. This is disabled here because it was causing all
+ // the scala jars to end up on the classpath twice: one on the boot
+ // classpath as set up by the runner (or regular classpath under -nobootcp)
+ // and then again here.
+ def scalaBootClassPath = ""
+ // scalaLibDirFound match {
+ // case Some(dir) if scalaHomeExists =>
+ // val paths = ClassPath expandDir dir.path
+ // join(paths: _*)
+ // case _ => ""
+ // }
+
+ def scalaExtDirs = Environment.scalaExtDirs
+
+ def scalaPluginPath = (scalaHomeDir / "misc" / "scala-devel" / "plugins").path
+
+ override def toString = """
+ |object Defaults {
+ | scalaHome = %s
+ | javaBootClassPath = %s
+ | scalaLibDirFound = %s
+ | scalaLibFound = %s
+ | scalaBootClassPath = %s
+ | scalaPluginPath = %s
+ |}""".trim.stripMargin.format(
+ scalaHome,
+ ppcp(javaBootClassPath),
+ scalaLibDirFound, scalaLibFound,
+ ppcp(scalaBootClassPath), ppcp(scalaPluginPath)
+ )
+ }
+
+ def fromPathString(path: String)(implicit ctx: Context): JavaClassPath = {
+ val settings = ctx.settings.classpath.update(path)
+ new PathResolver()(ctx.fresh.setSettings(settings)).result
+ }
+
+ /** With no arguments, show the interesting values in Environment and Defaults.
+ * If there are arguments, show those in Calculated as if those options had been
+ * given to a scala runner.
+ */
+ def main(args: Array[String]): Unit = {
+ if (args.isEmpty) {
+ println(Environment)
+ println(Defaults)
+ }
+ else {
+ implicit val ctx: Context = (new ContextBase).initialCtx // Dotty deviation: implicits need explicit type
+ val ArgsSummary(sstate, rest, errors) =
+ ctx.settings.processArguments(args.toList, true)
+ errors.foreach(println)
+ val pr = new PathResolver()(ctx.fresh.setSettings(sstate))
+ println(" COMMAND: 'scala %s'".format(args.mkString(" ")))
+ println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" ")))
+ pr.result.show
+ }
+ }
+}
+import PathResolver.{ Defaults, Environment, firstNonEmpty, ppcp }
+
+class PathResolver(implicit ctx: Context) {
+ import ctx.base.settings
+
+ val context = ClassPath.DefaultJavaContext
+
+ private def cmdLineOrElse(name: String, alt: String) = {
+ (commandLineFor(name) match {
+ case Some("") => None
+ case x => x
+ }) getOrElse alt
+ }
+
+ private def commandLineFor(s: String): Option[String] = condOpt(s) {
+ case "javabootclasspath" => settings.javabootclasspath.value
+ case "javaextdirs" => settings.javaextdirs.value
+ case "bootclasspath" => settings.bootclasspath.value
+ case "extdirs" => settings.extdirs.value
+ case "classpath" | "cp" => settings.classpath.value
+ case "sourcepath" => settings.sourcepath.value
+ case "priorityclasspath" => settings.priorityclasspath.value
+ }
+
+ /** Calculated values based on any given command line options, falling back on
+ * those in Defaults.
+ */
+ object Calculated {
+ def scalaHome = Defaults.scalaHome
+ def useJavaClassPath = settings.usejavacp.value || Defaults.useJavaClassPath
+ def javaBootClassPath = cmdLineOrElse("javabootclasspath", Defaults.javaBootClassPath)
+ def javaExtDirs = cmdLineOrElse("javaextdirs", Defaults.javaExtDirs)
+ def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else ""
+ def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath)
+ def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs)
+ def priorityClassPath = cmdLineOrElse("priorityclasspath", "")
+ /** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as:
+ * [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect
+ * [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg)
+ * [scaladoc] ^
+ * because the bootstrapping will look at the sourcepath and create package "reflect" in "<root>"
+ * and then when typing relative names, instead of picking <root>.scala.relect, typedIdentifier will pick up the
+ * <root>.reflect package created by the bootstrapping. Thus, no bootstrapping for scaladoc!
+ * TODO: we should refactor this as a separate -bootstrap option to have a clean implementation, no? */
+ def sourcePath = cmdLineOrElse("sourcepath", Defaults.scalaSourcePath)
+
+ /** Against my better judgment, giving in to martin here and allowing
+ * CLASSPATH to be used automatically. So for the user-specified part
+ * of the classpath:
+ *
+ * - If -classpath or -cp is given, it is that
+ * - Otherwise, if CLASSPATH is set, it is that
+ * - If neither of those, then "." is used.
+ */
+ def userClassPath = {
+ if (!settings.classpath.isDefault)
+ settings.classpath.value
+ else sys.env.getOrElse("CLASSPATH", ".")
+ }
+
+ import context._
+
+ // Assemble the elements!
+ // priority class path takes precedence
+ def basis = List[Traversable[ClassPath]](
+ classesInExpandedPath(priorityClassPath), // 0. The priority class path (for testing).
+ classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
+ contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
+ classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
+ classesInPath(scalaBootClassPath), // 4. The Scala boot class path.
+ contentsOfDirsInPath(scalaExtDirs), // 5. The Scala extension class path.
+ classesInExpandedPath(userClassPath), // 6. The Scala application class path.
+ sourcesInPath(sourcePath) // 7. The Scala source path.
+ )
+
+ lazy val containers = basis.flatten.distinct
+
+ override def toString = """
+ |object Calculated {
+ | scalaHome = %s
+ | priorityClassPath = %s
+ | javaBootClassPath = %s
+ | javaExtDirs = %s
+ | javaUserClassPath = %s
+ | useJavaClassPath = %s
+ | scalaBootClassPath = %s
+ | scalaExtDirs = %s
+ | userClassPath = %s
+ | sourcePath = %s
+ |}""".trim.stripMargin.format(
+ scalaHome, ppcp(priorityClassPath),
+ ppcp(javaBootClassPath), ppcp(javaExtDirs), ppcp(javaUserClassPath),
+ useJavaClassPath,
+ ppcp(scalaBootClassPath), ppcp(scalaExtDirs), ppcp(userClassPath),
+ ppcp(sourcePath)
+ )
+ }
+
+ def containers = Calculated.containers
+
+ lazy val result: JavaClassPath = {
+ // Prioritize `dotty.jar` and `dotty-lib.jar` to shadow others
+ val (dottyJars, others) =
+ containers.partition(x => x.name.contains("dotty-lib.jar") || x.name.contains("dotty.jar"))
+ // Then any jars with `dotty` in the name - putting them before scala-library
+ val (dottyCp, remaining) =
+ others.partition(_.name.contains("dotty-"))
+
+ val cp = new JavaClassPath((dottyJars ++ dottyCp ++ remaining).toIndexedSeq, context)
+
+ if (settings.Ylogcp.value) {
+ Console.println("Classpath built from " + settings.toConciseString(ctx.sstate))
+ Console.println("Defaults: " + PathResolver.Defaults)
+ Console.println("Calculated: " + Calculated)
+
+ val xs = (Calculated.basis drop 2).flatten.distinct
+ println("After java boot/extdirs classpath has %d entries:" format xs.size)
+ xs foreach (x => println(" " + x))
+ }
+ cp
+ }
+
+ def asURLs = result.asURLs
+
+}
diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala
new file mode 100644
index 000000000..062d9002d
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/Platform.scala
@@ -0,0 +1,39 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2012 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package dotty.tools
+package dotc
+package config
+
+import io.{ClassPath, AbstractFile}
+import core.Contexts._, core.Symbols._
+import core.SymbolLoader
+
+/** The platform dependent pieces of Global.
+ */
+abstract class Platform {
+
+ /** The root symbol loader. */
+ def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader
+
+ /** The compiler classpath. */
+ def classPath(implicit ctx: Context): ClassPath
+
+ /** Update classpath with a substitution that maps entries to entries */
+ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit
+
+ /** Any platform-specific phases. */
+ //def platformPhases: List[SubComponent]
+
+ /** Is the SAMType `cls` also a SAM under the rules of the platform? */
+ def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean
+
+ /** The various ways a boxed primitive might materialize at runtime. */
+ def isMaybeBoxed(sym: ClassSymbol)(implicit ctx: Context): Boolean
+
+ /** Create a new class loader to load class file `bin` */
+ def newClassLoader(bin: AbstractFile)(implicit ctx: Context): SymbolLoader
+}
+
diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala
new file mode 100644
index 000000000..002d0f933
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/Printers.scala
@@ -0,0 +1,34 @@
+package dotty.tools.dotc.config
+
+object Printers {
+
+ class Printer {
+ def println(msg: => String): Unit = System.out.println(msg)
+ }
+
+ object noPrinter extends Printer {
+ override def println(msg: => String): Unit = ()
+ }
+
+ val default: Printer = new Printer
+ val dottydoc: Printer = noPrinter
+ val core: Printer = noPrinter
+ val typr: Printer = noPrinter
+ val constr: Printer = noPrinter
+ val checks: Printer = noPrinter
+ val overload: Printer = noPrinter
+ val implicits: Printer = noPrinter
+ val implicitsDetailed: Printer = noPrinter
+ val subtyping: Printer = noPrinter
+ val unapp: Printer = noPrinter
+ val gadts: Printer = noPrinter
+ val hk: Printer = noPrinter
+ val variances: Printer = noPrinter
+ val incremental: Printer = noPrinter
+ val config: Printer = noPrinter
+ val transforms: Printer = noPrinter
+ val completions: Printer = noPrinter
+ val cyclicErrors: Printer = noPrinter
+ val pickling: Printer = noPrinter
+ val inlining: Printer = noPrinter
+}
diff --git a/compiler/src/dotty/tools/dotc/config/Properties.scala b/compiler/src/dotty/tools/dotc/config/Properties.scala
new file mode 100644
index 000000000..ec1f24d06
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/Properties.scala
@@ -0,0 +1,165 @@
+package dotty.tools
+package dotc
+package config
+
+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[Option[_]]
+
+ /** Scala manifest attributes.
+ */
+ @sharable val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version")
+}
+
+trait PropertiesTrait {
+ protected def propCategory: String // specializes the remainder of the values
+ protected def pickJarBasedOn: Class[_] // props file comes from jar containing this
+
+ /** The name of the properties file */
+ protected val propFilename = "/" + propCategory + ".properties"
+
+ /** The loaded properties */
+ @sharable protected lazy val scalaProps: java.util.Properties = {
+ val props = new java.util.Properties
+ val stream = pickJarBasedOn getResourceAsStream propFilename
+ if (stream ne null)
+ quietlyDispose(props load stream, stream.close)
+
+ props
+ }
+
+ private def quietlyDispose(action: => Unit, disposal: => Unit) =
+ try { action }
+ finally {
+ try { disposal }
+ catch { case _: IOException => }
+ }
+
+ def propIsSet(name: String) = System.getProperty(name) != null
+ def propIsSetTo(name: String, value: String) = propOrNull(name) == value
+ def propOrElse(name: String, alt: String) = System.getProperty(name, alt)
+ def propOrEmpty(name: String) = propOrElse(name, "")
+ def propOrNull(name: String) = propOrElse(name, null)
+ def propOrNone(name: String) = Option(propOrNull(name))
+ def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase)
+ def setProp(name: String, value: String) = System.setProperty(name, value)
+ def clearProp(name: String) = System.clearProperty(name)
+
+ def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt
+ def envOrNone(name: String) = Option(System getenv name)
+
+ // for values based on propFilename
+ def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt)
+ def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "")
+ def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name))
+
+ /** The numeric portion of the runtime Scala version, if this is a final
+ * release. If for instance the versionString says "version 2.9.0.final",
+ * this would return Some("2.9.0").
+ *
+ * @return Some(version) if this is a final release build, None if
+ * it is an RC, Beta, etc. or was built from source, or if the version
+ * cannot be read.
+ */
+ val releaseVersion =
+ for {
+ v <- scalaPropOrNone("maven.version.number")
+ if !(v endsWith "-SNAPSHOT")
+ } yield v
+
+ /** The development Scala version, if this is not a final release.
+ * The precise contents are not guaranteed, but it aims to provide a
+ * unique repository identifier (currently the svn revision) in the
+ * fourth dotted segment if the running version was built from source.
+ *
+ * @return Some(version) if this is a non-final version, None if this
+ * is a final release or the version cannot be read.
+ */
+ val developmentVersion =
+ for {
+ v <- scalaPropOrNone("maven.version.number")
+ if v endsWith "-SNAPSHOT"
+ ov <- scalaPropOrNone("version.number")
+ } yield ov
+
+ /** 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.
+ */
+ val versionString = "version " + "0.01" //scalaPropOrElse("version.number", "(unknown)")" +
+ val copyrightString = "(c) 2013 LAMP/EPFL" // scalaPropOrElse("copyright.string", "(c) 2002-2011 LAMP/EPFL")
+
+ /** This is the encoding to use reading in source files, overridden with -encoding
+ * Note that it uses "prop" i.e. looks in the scala jar, not the system properties.
+ */
+ def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8")
+ def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader")
+
+ /** This is the default text encoding, overridden (unreliably) with
+ * `JAVA_OPTS="-Dfile.encoding=Foo"`
+ */
+ def encodingString = propOrElse("file.encoding", "UTF-8")
+
+ /** The default end of line character.
+ */
+ def lineSeparator = propOrElse("line.separator", "\n")
+
+ /** Various well-known properties.
+ */
+ def javaClassPath = propOrEmpty("java.class.path")
+ def javaHome = propOrEmpty("java.home")
+ def javaVendor = propOrEmpty("java.vendor")
+ def javaVersion = propOrEmpty("java.version")
+ def javaVmInfo = propOrEmpty("java.vm.info")
+ def javaVmName = propOrEmpty("java.vm.name")
+ def javaVmVendor = propOrEmpty("java.vm.vendor")
+ def javaVmVersion = propOrEmpty("java.vm.version")
+ def osName = propOrEmpty("os.name")
+ def scalaHome = propOrEmpty("scala.home")
+ def tmpDir = propOrEmpty("java.io.tmpdir")
+ def userDir = propOrEmpty("user.dir")
+ def userHome = propOrEmpty("user.home")
+ def userName = propOrEmpty("user.name")
+
+ /** Some derived values.
+ */
+ def isWin = osName startsWith "Windows"
+ def isMac = javaVendor startsWith "Apple"
+
+ // This is looking for javac, tools.jar, etc.
+ // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME,
+ // and finally the system property based javaHome.
+ def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome))
+
+ def versionMsg = "Scala %s %s -- %s".format(propCategory, versionString, copyrightString)
+ def scalaCmd = if (isWin) "scala.bat" else "scala"
+ def scalacCmd = if (isWin) "scalac.bat" else "scalac"
+
+ /** Can the java version be determined to be at least as high as the argument?
+ * Hard to properly future proof this but at the rate 1.7 is going we can leave
+ * the issue for our cyborg grandchildren to solve.
+ */
+ def isJavaAtLeast(version: String) = {
+ val okVersions = version match {
+ case "1.5" => List("1.5", "1.6", "1.7")
+ case "1.6" => List("1.6", "1.7")
+ case "1.7" => List("1.7")
+ case _ => Nil
+ }
+ okVersions exists (javaVersion startsWith _)
+ }
+
+ // provide a main method so version info can be obtained by running this
+ def main(args: Array[String]): Unit = {
+ val writer = new PrintWriter(Console.err, true)
+ writer println versionMsg
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
new file mode 100644
index 000000000..fd2ded0b5
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -0,0 +1,267 @@
+package dotty.tools.dotc
+package config
+
+import PathResolver.Defaults
+import rewrite.Rewrites
+
+class ScalaSettings extends Settings.SettingGroup {
+
+ protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".")
+
+ /** Path related settings.
+ */
+ val bootclasspath = PathSetting("-bootclasspath", "Override location of bootstrap class files.", Defaults.scalaBootClassPath)
+ val extdirs = PathSetting("-extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs)
+ val javabootclasspath = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath)
+ val javaextdirs = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs)
+ val sourcepath = PathSetting("-sourcepath", "Specify location(s) of source files.", "") // Defaults.scalaSourcePath
+ val argfiles = BooleanSetting("@<file>", "A text file containing compiler arguments (options and source files)")
+ val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp"
+ val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
+ val priorityclasspath = PathSetting("-priorityclasspath", "class path that takes precedence over all other paths (or testing only)", "")
+
+ /** Other settings.
+ */
+ val dependencyfile = StringSetting("-dependencyfile", "file", "Set dependency tracking file.", ".scala_dependencies")
+ val deprecation = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.")
+ val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.")
+ val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding)
+ val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.")
+ val explain = BooleanSetting("-explain", "Explain errors in more detail.")
+ val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.")
+ val g = ChoiceSetting("-g", "level", "Set level of generated debugging info.", List("none", "source", "line", "vars", "notailcalls"), "vars")
+ val help = BooleanSetting("-help", "Print a synopsis of standard options")
+ val nowarn = BooleanSetting("-nowarn", "Generate no warnings.")
+ val color = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/)
+ val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
+ List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"),
+ "jvm-1.8")
+ val scalajs = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).")
+ val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
+ val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.")
+ val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.")
+ val verbose = BooleanSetting("-verbose", "Output messages about what the compiler is doing.")
+ val version = BooleanSetting("-version", "Print product version and exit.")
+ val pageWidth = IntSetting("-pagewidth", "Set page width", 80)
+
+ val jvmargs = PrefixSetting("-J<flag>", "-J", "Pass <flag> directly to the runtime system.")
+ val defines = PrefixSetting("-Dproperty=value", "-D", "Pass -Dproperty=value directly to the runtime system.")
+ val toolcp = PathSetting("-toolcp", "Add to the runner classpath.", "")
+ val nobootcp = BooleanSetting("-nobootcp", "Do not use the boot classpath for the scala jars.")
+ val strict = BooleanSetting("-strict", "Use strict type rules, which means some formerly legal code does not typecheck anymore.")
+
+ val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.")
+ val language = MultiStringSetting("-language", "feature", "Enable one or more language features.")
+ val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax")
+
+ /** -X "Advanced" settings
+ */
+ val Xhelp = BooleanSetting("-X", "Print a synopsis of advanced options.")
+ val assemname = StringSetting("-Xassem-name", "file", "(Requires -target:msil) Name of the output assembly.", "").dependsOn(target, "msil")
+ val assemrefs = StringSetting("-Xassem-path", "path", "(Requires -target:msil) List of assemblies referenced by the program.", ".").dependsOn(target, "msil")
+ val assemextdirs = StringSetting("-Xassem-extdirs", "dirs", "(Requires -target:msil) List of directories containing assemblies. default:lib", Defaults.scalaLibDir.path).dependsOn(target, "msil")
+ val sourcedir = StringSetting("-Xsourcedir", "directory", "(Requires -target:msil) Mirror source folder structure in output directory.", ".").dependsOn(target, "msil")
+ val checkInit = BooleanSetting("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.")
+ val noassertions = BooleanSetting("-Xdisable-assertions", "Generate no assertions or assumptions.")
+// val elidebelow = IntSetting("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument",
+// elidable.MINIMUM, None, elidable.byName get _)
+ val noForwarders = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.")
+ val genPhaseGraph = StringSetting("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "")
+ val XlogImplicits = BooleanSetting("-Xlog-implicits", "Show more detail on why some implicits are not applicable.")
+ val XminImplicitSearchDepth = IntSetting("-Xmin-implicit-search-depth", "Set number of levels of implicit searches undertaken before checking for divergence.", 5)
+ val xmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 70)
+ val logImplicitConv = BooleanSetting("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.")
+ val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated")
+ val logFreeTerms = BooleanSetting("-Xlog-free-terms", "Print a message when reification creates a free term.")
+ val logFreeTypes = BooleanSetting("-Xlog-free-types", "Print a message when reification resorts to generating a free type.")
+ val maxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255)
+ val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.")
+ val Xsource = VersionSetting("-Xsource", "Treat compiler input as Scala source for the specified version.")
+ val Xverify = BooleanSetting("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)")
+ val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.")
+ val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable the given plugin(s).")
+ val showPlugins = BooleanSetting("-Xplugin-list", "Print a synopsis of loaded plugins.")
+ val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless the given plugin(s) are available.")
+ val pluginsDir = StringSetting("-Xpluginsdir", "path", "Path to search compiler plugins.", Defaults.scalaPluginPath)
+ val Xprint = PhasesSetting("-Xprint", "Print out program after")
+ val writeICode = PhasesSetting("-Xprint-icode", "Log internal icode to *.icode files after", "icode")
+ val Xprintpos = BooleanSetting("-Xprint-pos", "Print tree positions, as offsets.")
+ val printtypes = BooleanSetting("-Xprint-types", "Print tree types (debugging option).")
+ val XprintDiff = BooleanSetting("-Xprint-diff", "Print changed parts of the tree since last print.")
+ val XprintDiffDel = BooleanSetting("-Xprint-diff-del", "Print chaged parts of the tree since last print including deleted parts.")
+ val prompt = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).")
+ 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.")
+ val sourceReader = StringSetting("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "")
+ val XnoValueClasses = BooleanSetting("-Xno-value-classes", "Do not use value classes. Helps debugging.")
+ val XreplLineWidth = IntSetting("-Xrepl-line-width", "Maximial number of columns per line for REPL output", 390)
+ val XoldPatmat = BooleanSetting("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.")
+ val XnoPatmatAnalysis = BooleanSetting("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.")
+ val XfullLubs = BooleanSetting("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.")
+
+ /** -Y "Private" settings
+ */
+ val overrideObjects = BooleanSetting("-Yoverride-objects", "Allow member objects to be overridden.")
+ val overrideVars = BooleanSetting("-Yoverride-vars", "Allow vars to be overridden.")
+ val Yhelp = BooleanSetting("-Y", "Print a synopsis of private options.")
+ val browse = PhasesSetting("-Ybrowse", "Browse the abstract syntax tree after")
+ val Ycheck = PhasesSetting("-Ycheck", "Check the tree at the end of")
+ val YcheckMods = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync")
+ val YcheckTypedTrees = BooleanSetting("-YcheckTypedTrees", "Check all constructured typed trees for type correctness")
+ val Yshow = PhasesSetting("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after")
+ val Ycloselim = BooleanSetting("-Yclosure-elim", "Perform closure elimination.")
+ val Ycompacttrees = BooleanSetting("-Ycompact-trees", "Use compact tree printer when displaying trees.")
+ val noCompletion = BooleanSetting("-Yno-completion", "Disable tab-completion in the REPL.")
+ val Ydce = BooleanSetting("-Ydead-code", "Perform dead code elimination.")
+ val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.")
+ val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names")
+ val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations")
+ val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions")
+ val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)")
+ //val doc = BooleanSetting ("-Ydoc", "Generate documentation")
+ val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
+ val inlineHandlers = BooleanSetting("-Yinline-handlers", "Perform exception handler inlining when possible.")
+ val YinlinerWarnings = BooleanSetting("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)")
+ val Ylinearizer = ChoiceSetting("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo")
+ val log = PhasesSetting("-Ylog", "Log operations during")
+ val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.")
+ val Ynogenericsig = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")
+ val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
+ val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.")
+ val noAdaptedArgs = BooleanSetting("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.")
+ val selfInAnnots = BooleanSetting("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.")
+ val Yshowtrees = BooleanSetting("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.")
+ val YshowtreesCompact = BooleanSetting("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.")
+ val YshowtreesStringified = BooleanSetting("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.")
+ val Yshowsyms = BooleanSetting("-Yshow-syms", "Print the AST symbol hierarchy after each phase.")
+ val Yshowsymkinds = BooleanSetting("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.")
+ val Yskip = PhasesSetting("-Yskip", "Skip")
+ val Ygenjavap = StringSetting("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "")
+ val Ydumpclasses = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "")
+ val Ynosqueeze = BooleanSetting("-Yno-squeeze", "Disable creation of compact code in matching.")
+ val YstopAfter = PhasesSetting("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
+ val YstopBefore = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully
+ val refinementMethodDispatch = ChoiceSetting("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
+ val Yrangepos = BooleanSetting("-Yrangepos", "Use range positions for syntax trees.")
+ val Ybuilderdebug = ChoiceSetting("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none")
+ val Yreifycopypaste = BooleanSetting("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
+ val Yreplsync = BooleanSetting("-Yrepl-sync", "Do not use asynchronous code for repl startup")
+ val YmethodInfer = BooleanSetting("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
+ val etaExpandKeepsStar = BooleanSetting("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
+ val Yinvalidate = StringSetting("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "")
+ val noSelfCheck = BooleanSetting("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
+ val YtraceContextCreation = BooleanSetting("-Ytrace-context-creation", "Store stack trace of context creations.")
+ val YshowSuppressedErrors = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally supressed.")
+ val Yheartbeat = BooleanSetting("-Yheartbeat", "show heartbeat stack trace of compiler operations.")
+ val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions.")
+ val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.")
+ val YplainPrinter = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.")
+ val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.")
+ val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
+ val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
+ val YkeepComments = BooleanSetting("-Ykeep-comments", "Keep comments when scanning source files.")
+ val YforceSbtPhases = BooleanSetting("-Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.")
+ val YdumpSbtInc = BooleanSetting("-Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.")
+ val YcheckAllPatmat = BooleanSetting("-Ycheck-all-patmat", "Check exhaustivity and redundancy of all pattern matching (used for testing the algorithm)")
+ def stop = YstopAfter
+
+ /** Area-specific debug output.
+ */
+ val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.")
+ val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.")
+ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.")
+ val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.")
+ val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.")
+ val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.")
+ val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.")
+ val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.")
+ val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.")
+ val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.")
+ val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
+ val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.")
+ val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
+ val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")
+ val Yexplainlowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
+ val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")
+ val YshowVarBounds = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds")
+ val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
+
+ val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize"
+
+ /** IDE-specific settings
+ */
+ val YpresentationVerbose = BooleanSetting("-Ypresentation-verbose", "Print information about presentation compiler tasks.")
+ val YpresentationDebug = BooleanSetting("-Ypresentation-debug", "Enable debugging output for the presentation compiler.")
+ val YpresentationStrict = BooleanSetting("-Ypresentation-strict", "Do not report type errors in sources with syntax errors.")
+
+ val YpresentationLog = StringSetting("-Ypresentation-log", "file", "Log presentation compiler events into file", "")
+ val YpresentationReplay = StringSetting("-Ypresentation-replay", "file", "Replay presentation compiler events from file", "")
+ val YpresentationDelay = IntSetting("-Ypresentation-delay", "Wait number of ms after typing before starting typechecking", 0, 0 to 999)
+
+ /** Doc specific settings */
+ val template = OptionSetting[String](
+ "-template",
+ "A mustache template for rendering each top-level entity in the API"
+ )
+
+ val resources = OptionSetting[String](
+ "-resources",
+ "A directory containing static resources needed for the API documentation"
+ )
+
+ val DocTitle = StringSetting (
+ "-Ydoc-title",
+ "title",
+ "The overall name of the Scaladoc site",
+ ""
+ )
+
+ val DocVersion = StringSetting (
+ "-Ydoc-version",
+ "version",
+ "An optional version number, to be appended to the title",
+ ""
+ )
+
+ val DocOutput = StringSetting (
+ "-Ydoc-output",
+ "outdir",
+ "The output directory in which to place the documentation",
+ "."
+ )
+
+ val DocFooter = StringSetting (
+ "-Ydoc-footer",
+ "footer",
+ "A footer on every Scaladoc page, by default the EPFL/Lightbend copyright notice. Can be overridden with a custom footer.",
+ ""
+ )
+
+ val DocUncompilable = StringSetting (
+ "-Ydoc-no-compile",
+ "path",
+ "A directory containing sources which should be parsed, no more (e.g. AnyRef.scala)",
+ ""
+ )
+
+ //def DocUncompilableFiles(implicit ctx: Context) = DocUncompilable.value match {
+ // case "" => Nil
+ // case path => io.Directory(path).deepFiles.filter(_ hasExtension "scala").toList
+ //}
+
+ val DocExternalDoc = MultiStringSetting (
+ "-Ydoc-external-doc",
+ "external-doc",
+ "comma-separated list of classpath_entry_path#doc_URL pairs describing external dependencies."
+ )
+
+ val DocAuthor = BooleanSetting("-Ydoc-author", "Include authors.", true)
+
+ val DocGroups = BooleanSetting (
+ "-Ydoc:groups",
+ "Group similar functions together (based on the @group annotation)"
+ )
+}
diff --git a/compiler/src/dotty/tools/dotc/config/ScalaVersion.scala b/compiler/src/dotty/tools/dotc/config/ScalaVersion.scala
new file mode 100644
index 000000000..02ba74af9
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/ScalaVersion.scala
@@ -0,0 +1,184 @@
+/* @author James Iry
+ */
+package dotty.tools
+package dotc.config
+
+import scala.util.{Try, Success, Failure}
+
+/**
+ * Represents a single Scala version in a manner that
+ * supports easy comparison and sorting.
+ */
+sealed abstract class ScalaVersion extends Ordered[ScalaVersion] {
+ def unparse: String
+}
+
+/**
+ * A scala version that sorts higher than all actual versions
+ */
+@sharable case object NoScalaVersion extends ScalaVersion {
+ def unparse = "none"
+
+ def compare(that: ScalaVersion): Int = that match {
+ case NoScalaVersion => 0
+ case _ => 1
+ }
+}
+
+/**
+ * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion
+ * may or may not be a released version - i.e. this same class is used to represent
+ * final, release candidate, milestone, and development builds. The build argument is used
+ * to segregate builds
+ */
+case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion {
+ def unparse = s"${major}.${minor}.${rev}.${build.unparse}"
+
+ def compare(that: ScalaVersion): Int = that match {
+ case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) =>
+ // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these
+ // comparisons a lot so I'm using brute force direct style code
+ if (major < thatMajor) -1
+ else if (major > thatMajor) 1
+ else if (minor < thatMinor) -1
+ else if (minor > thatMinor) 1
+ else if (rev < thatRev) -1
+ else if (rev > thatRev) 1
+ else build compare thatBuild
+ case AnyScalaVersion => 1
+ case NoScalaVersion => -1
+ }
+}
+
+/**
+ * A Scala version that sorts lower than all actual versions
+ */
+@sharable case object AnyScalaVersion extends ScalaVersion {
+ def unparse = "any"
+
+ def compare(that: ScalaVersion): Int = that match {
+ case AnyScalaVersion => 0
+ case _ => -1
+ }
+}
+
+/**
+ * Methods for parsing ScalaVersions
+ */
+@sharable object ScalaVersion {
+ private val dot = "\\."
+ private val dash = "\\-"
+ private def not(s:String) = s"[^${s}]"
+ private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r
+
+ def parse(versionString : String): Try[ScalaVersion] = {
+ def failure = Failure(new NumberFormatException(
+ s"There was a problem parsing ${versionString}. " +
+ "Versions should be in the form major[.minor[.revision]] " +
+ "where each part is a positive number, as in 2.10.1. " +
+ "The minor and revision parts are optional."
+ ))
+
+ def toInt(s: String) = s match {
+ case null | "" => 0
+ case _ => s.toInt
+ }
+
+ def isInt(s: String) = Try(toInt(s)).isSuccess
+
+ import ScalaBuild._
+
+ def toBuild(s: String) = s match {
+ case null | "FINAL" => Final
+ case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2)))
+ case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1)))
+ case _ => Development(s)
+ }
+
+ try versionString match {
+ case "" | "any" => Success(AnyScalaVersion)
+ case "none" => Success(NoScalaVersion)
+ case R(_, majorS, _, minorS, _, revS, _, buildS) =>
+ Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS)))
+ case _ => failure
+ } catch {
+ case e: NumberFormatException => failure
+ }
+ }
+
+ /**
+ * The version of the compiler running now
+ */
+ val current = parse(util.Properties.versionNumberString).get
+}
+
+/**
+ * Represents the data after the dash in major.minor.rev-build
+ */
+abstract class ScalaBuild extends Ordered[ScalaBuild] {
+ /**
+ * Return a version of this build information that can be parsed back into the
+ * same ScalaBuild
+ */
+ def unparse: String
+}
+
+object ScalaBuild {
+
+ /** A development, test, nightly, snapshot or other "unofficial" build
+ */
+ case class Development(id: String) extends ScalaBuild {
+ def unparse = s"-${id}"
+
+ def compare(that: ScalaBuild) = that match {
+ // sorting two development builds based on id is reasonably valid for two versions created with the same schema
+ // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions
+ // this is a pragmatic compromise
+ case Development(thatId) => id compare thatId
+ // assume a development build is newer than anything else, that's not really true, but good luck
+ // mapping development build versions to other build types
+ case _ => 1
+ }
+ }
+
+ /** A final build
+ */
+ case object Final extends ScalaBuild {
+ def unparse = ""
+
+ def compare(that: ScalaBuild) = that match {
+ case Final => 0
+ // a final is newer than anything other than a development build or another final
+ case Development(_) => -1
+ case _ => 1
+ }
+ }
+
+ /** A candidate for final release
+ */
+ case class RC(n: Int) extends ScalaBuild {
+ def unparse = s"-RC${n}"
+
+ def compare(that: ScalaBuild) = that match {
+ // compare two rcs based on their RC numbers
+ case RC(thatN) => n - thatN
+ // an rc is older than anything other than a milestone or another rc
+ case Milestone(_) => 1
+ case _ => -1
+ }
+ }
+
+ /** An intermediate release
+ */
+ case class Milestone(n: Int) extends ScalaBuild {
+ def unparse = s"-M${n}"
+
+ def compare(that: ScalaBuild) = that match {
+ // compare two milestones based on their milestone numbers
+ case Milestone(thatN) => n - thatN
+ // a milestone is older than anything other than another milestone
+ case _ => -1
+
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala
new file mode 100644
index 000000000..cffa047fe
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/Settings.scala
@@ -0,0 +1,270 @@
+package dotty.tools.dotc
+package config
+
+import collection.mutable.{ ArrayBuffer }
+import scala.util.{ Try, Success, Failure }
+import scala.reflect.internal.util.StringOps
+import reflect.ClassTag
+import core.Contexts._
+// import annotation.unchecked
+ // Dotty deviation: Imports take precedence over definitions in enclosing package
+ // (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives
+ // us a package, which is not what was intended anyway).
+import language.existentials
+
+object Settings {
+
+ val BooleanTag = ClassTag.Boolean
+ val IntTag = ClassTag.Int
+ val StringTag = ClassTag(classOf[String])
+ val ListTag = ClassTag(classOf[List[_]])
+ val VersionTag = ClassTag(classOf[ScalaVersion])
+ val OptionTag = ClassTag(classOf[Option[_]])
+
+ class SettingsState(initialValues: Seq[Any]) {
+ private var values = ArrayBuffer(initialValues: _*)
+ private var _wasRead: Boolean = false
+
+ override def toString = s"SettingsState(values: ${values.toList})"
+
+ def value(idx: Int): Any = {
+ _wasRead = true
+ values(idx)
+ }
+
+ def update(idx: Int, x: Any): SettingsState =
+ if (_wasRead)
+ new SettingsState(values).update(idx, x)
+ else {
+ values(idx) = x
+ this
+ }
+ }
+
+ case class ArgsSummary(
+ sstate: SettingsState,
+ arguments: List[String],
+ errors: List[String]) {
+
+ def fail(msg: String) =
+ ArgsSummary(sstate, arguments, errors :+ msg)
+ }
+
+ case class Setting[T: ClassTag] private[Settings] (
+ name: String,
+ description: String,
+ default: T,
+ helpArg: String = "",
+ choices: Seq[T] = Nil,
+ prefix: String = "",
+ aliases: List[String] = Nil,
+ depends: List[(Setting[_], Any)] = Nil,
+ propertyClass: Option[Class[_]] = None)(private[Settings] val idx: Int) {
+
+ def withAbbreviation(abbrv: String): Setting[T] =
+ copy(aliases = aliases :+ abbrv)(idx)
+
+ def dependsOn[U](setting: Setting[U], value: U): Setting[T] =
+ copy(depends = depends :+ (setting, value))(idx)
+
+ def valueIn(state: SettingsState): T =
+ state.value(idx).asInstanceOf[T]
+
+ def updateIn(state: SettingsState, x: Any): SettingsState = x match {
+ case _: T => state.update(idx, x)
+ case _ =>
+ // would like to do:
+ // throw new ClassCastException(s"illegal argument, found: $x of type ${x.getClass}, required: ${implicitly[ClassTag[T]]}")
+ // but this runs afoul of primitive types. Concretely: if T is Boolean, then x is a boxed Boolean and the test will fail.
+ // Maybe this is a bug in Scala 2.10?
+ state.update(idx, x.asInstanceOf[T])
+ }
+
+ def isDefaultIn(state: SettingsState) = valueIn(state) == default
+
+ def legalChoices: String =
+ if (choices.isEmpty) ""
+ else choices match {
+ case r: Range => r.head + ".." + r.last
+ case xs: List[_] => xs.mkString(", ")
+ }
+
+ def isLegal(arg: Any): Boolean =
+ if (choices.isEmpty)
+ arg match {
+ case _: T => true
+ case _ => false
+ }
+ else choices match {
+ case r: Range =>
+ arg match {
+ case x: Int => r.head <= x && x <= r.last
+ case _ => false
+ }
+ case xs: List[_] =>
+ xs contains arg
+ }
+
+ def tryToSet(state: ArgsSummary): ArgsSummary = {
+ val ArgsSummary(sstate, arg :: args, errors) = state
+ def update(value: Any, args: List[String]) =
+ ArgsSummary(updateIn(sstate, value), args, errors)
+ def fail(msg: String, args: List[String]) =
+ ArgsSummary(sstate, args, errors :+ msg)
+ def missingArg =
+ fail(s"missing argument for option $name", args)
+ def doSet(argRest: String) = ((implicitly[ClassTag[T]], args): @unchecked) match {
+ case (BooleanTag, _) =>
+ update(true, args)
+ case (OptionTag, _) =>
+ update(Some(propertyClass.get.newInstance), args)
+ case (ListTag, _) =>
+ if (argRest.isEmpty) missingArg
+ else update((argRest split ",").toList, args)
+ case (StringTag, _) if choices.nonEmpty =>
+ if (argRest.isEmpty) missingArg
+ else if (!choices.contains(argRest))
+ fail(s"$arg is not a valid choice for $name", args)
+ else update(argRest, args)
+ case (StringTag, arg2 :: args2) =>
+ update(arg2, args2)
+ case (IntTag, arg2 :: args2) =>
+ try {
+ val x = arg2.toInt
+ choices match {
+ case r: Range if x < r.head || r.last < x =>
+ fail(s"$arg2 is out of legal range $legalChoices for $name", args2)
+ case _ =>
+ update(x, args2)
+ }
+ } catch {
+ case _: NumberFormatException =>
+ fail(s"$arg2 is not an integer argument for $name", args2)
+ }
+ case (VersionTag, _) =>
+ ScalaVersion.parse(argRest) match {
+ case Success(v) => update(v, args)
+ case Failure(ex) => fail(ex.getMessage, args)
+ }
+ case (_, Nil) =>
+ missingArg
+ }
+
+ if (prefix != "" && arg.startsWith(prefix))
+ doSet(arg drop prefix.length)
+ else if (prefix == "" && name == arg.takeWhile(_ != ':'))
+ doSet(arg.dropWhile(_ != ':').drop(1))
+ else
+ state
+ }
+ }
+
+ object Setting {
+ implicit class SettingDecorator[T](val setting: Setting[T]) extends AnyVal {
+ def value(implicit ctx: Context): T = setting.valueIn(ctx.sstate)
+ def update(x: T)(implicit ctx: Context): SettingsState = setting.updateIn(ctx.sstate, x)
+ def isDefault(implicit ctx: Context): Boolean = setting.isDefaultIn(ctx.sstate)
+ }
+ }
+
+ class SettingGroup {
+
+ val _allSettings = new ArrayBuffer[Setting[_]]
+ def allSettings: Seq[Setting[_]] = _allSettings
+
+ def defaultState = new SettingsState(allSettings map (_.default))
+
+ def userSetSettings(state: SettingsState) =
+ allSettings filterNot (_.isDefaultIn(state))
+
+ def toConciseString(state: SettingsState) =
+ userSetSettings(state).mkString("(", " ", ")")
+
+ private def checkDependencies(state: ArgsSummary): ArgsSummary =
+ (state /: userSetSettings(state.sstate))(checkDependenciesOfSetting)
+
+ private def checkDependenciesOfSetting(state: ArgsSummary, setting: Setting[_]) =
+ (state /: setting.depends) { (s, dep) =>
+ val (depSetting, reqValue) = dep
+ if (depSetting.valueIn(state.sstate) == reqValue) s
+ else s.fail(s"incomplete option ${setting.name} (requires ${depSetting.name})")
+ }
+
+ /** Iterates over the arguments applying them to settings where applicable.
+ * Then verifies setting dependencies are met.
+ *
+ * This temporarily takes a boolean indicating whether to keep
+ * processing if an argument is seen which is not a command line option.
+ * This is an expedience for the moment so that you can say
+ *
+ * scalac -d /tmp foo.scala -optimise
+ *
+ * while also allowing
+ *
+ * scala Program opt opt
+ *
+ * to get their arguments.
+ */
+ protected def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = {
+ def stateWithArgs(args: List[String]) = ArgsSummary(state.sstate, args, state.errors)
+ state.arguments match {
+ case Nil =>
+ checkDependencies(stateWithArgs(skipped))
+ case "--" :: args =>
+ checkDependencies(stateWithArgs(skipped ++ args))
+ case x :: _ if x startsWith "-" =>
+ def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
+ case setting :: settings1 =>
+ val state1 = setting.tryToSet(state)
+ if (state1 ne state) processArguments(state1, processAll, skipped)
+ else loop(settings1)
+ case Nil =>
+ state.fail(s"bad option: '$x'")
+ }
+ loop(allSettings.toList)
+ case arg :: args =>
+ if (processAll) processArguments(stateWithArgs(args), processAll, skipped :+ arg)
+ else state
+ }
+ }
+
+ def processArguments(arguments: List[String], processAll: Boolean)(implicit ctx: Context): ArgsSummary =
+ processArguments(ArgsSummary(ctx.sstate, arguments, Nil), processAll, Nil)
+
+ def publish[T](settingf: Int => Setting[T]): Setting[T] = {
+ val setting = settingf(_allSettings.length)
+ _allSettings += setting
+ setting
+ }
+
+ def BooleanSetting(name: String, descr: String, initialValue: Boolean = false): Setting[Boolean] =
+ publish(Setting(name, descr, initialValue))
+
+ def StringSetting(name: String, helpArg: String, descr: String, default: String): Setting[String] =
+ publish(Setting(name, descr, default, helpArg))
+
+ def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): Setting[String] =
+ publish(Setting(name, descr, default, helpArg, choices))
+
+ def IntSetting(name: String, descr: String, default: Int, range: Seq[Int] = Nil): Setting[Int] =
+ publish(Setting(name, descr, default, choices = range))
+
+ def MultiStringSetting(name: String, helpArg: String, descr: String): Setting[List[String]] =
+ publish(Setting(name, descr, Nil, helpArg))
+
+ def PathSetting(name: String, descr: String, default: String): Setting[String] =
+ publish(Setting(name, descr, default))
+
+ def PhasesSetting(name: String, descr: String, default: String = ""): Setting[List[String]] =
+ publish(Setting(name, descr, if (default.isEmpty) Nil else List(default)))
+
+ def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] =
+ publish(Setting(name, descr, Nil, prefix = pre))
+
+ def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] =
+ publish(Setting(name, descr, default))
+
+ def OptionSetting[T: ClassTag](name: String, descr: String): Setting[Option[T]] =
+ publish(Setting(name, descr, None, propertyClass = Some(implicitly[ClassTag[T]].runtimeClass)))
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala b/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala
new file mode 100644
index 000000000..07972b99b
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala
@@ -0,0 +1,34 @@
+package dotty.tools
+package dotc
+package config
+
+import java.security.AccessControlException
+
+/** For placing a wrapper function around property functions.
+ * Motivated by places like google app engine throwing exceptions
+ * on property lookups.
+ */
+trait WrappedProperties extends PropertiesTrait {
+ def wrap[T](body: => T): Option[T]
+
+ protected def propCategory = "wrapped"
+ protected def pickJarBasedOn = this.getClass
+
+ override def propIsSet(name: String) = wrap(super.propIsSet(name)) exists (x => x)
+ override def propOrElse(name: String, alt: String) = wrap(super.propOrElse(name, alt)) getOrElse alt
+ override def setProp(name: String, value: String) = wrap(super.setProp(name, value)).orNull
+ override def clearProp(name: String) = wrap(super.clearProp(name)).orNull
+ override def envOrElse(name: String, alt: String) = wrap(super.envOrElse(name, alt)) getOrElse alt
+ override def envOrNone(name: String) = wrap(super.envOrNone(name)).flatten
+
+ def systemProperties: Iterator[(String, String)] = {
+ import scala.collection.JavaConverters._
+ wrap(System.getProperties.asScala.iterator) getOrElse Iterator.empty
+ }
+}
+
+object WrappedProperties {
+ object AccessControl extends WrappedProperties {
+ def wrap[T](body: => T) = try Some(body) catch { case _: AccessControlException => None }
+ }
+}