From 8a61ff432543a29234193cd1f7c14abd3f3d31a0 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Wed, 2 Nov 2016 11:08:28 +0100 Subject: Move compiler and compiler tests to compiler dir --- .../dotty/tools/dotc/config/CompilerCommand.scala | 128 ++++++++++ compiler/src/dotty/tools/dotc/config/Config.scala | 138 ++++++++++ .../src/dotty/tools/dotc/config/JavaPlatform.scala | 70 +++++ .../src/dotty/tools/dotc/config/OutputDirs.scala | 116 +++++++++ .../src/dotty/tools/dotc/config/PathResolver.scala | 281 +++++++++++++++++++++ .../src/dotty/tools/dotc/config/Platform.scala | 39 +++ .../src/dotty/tools/dotc/config/Printers.scala | 34 +++ .../src/dotty/tools/dotc/config/Properties.scala | 165 ++++++++++++ .../dotty/tools/dotc/config/ScalaSettings.scala | 267 ++++++++++++++++++++ .../src/dotty/tools/dotc/config/ScalaVersion.scala | 184 ++++++++++++++ .../src/dotty/tools/dotc/config/Settings.scala | 270 ++++++++++++++++++++ .../tools/dotc/config/WrappedProperties.scala | 34 +++ 12 files changed, 1726 insertions(+) create mode 100644 compiler/src/dotty/tools/dotc/config/CompilerCommand.scala create mode 100644 compiler/src/dotty/tools/dotc/config/Config.scala create mode 100644 compiler/src/dotty/tools/dotc/config/JavaPlatform.scala create mode 100644 compiler/src/dotty/tools/dotc/config/OutputDirs.scala create mode 100644 compiler/src/dotty/tools/dotc/config/PathResolver.scala create mode 100644 compiler/src/dotty/tools/dotc/config/Platform.scala create mode 100644 compiler/src/dotty/tools/dotc/config/Printers.scala create mode 100644 compiler/src/dotty/tools/dotc/config/Properties.scala create mode 100644 compiler/src/dotty/tools/dotc/config/ScalaSettings.scala create mode 100644 compiler/src/dotty/tools/dotc/config/ScalaVersion.scala create mode 100644 compiler/src/dotty/tools/dotc/config/Settings.scala create mode 100644 compiler/src/dotty/tools/dotc/config/WrappedProperties.scala (limited to 'compiler/src/dotty/tools/dotc/config') 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 + | 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 " + + 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 "" + * and then when typing relative names, instead of picking .scala.relect, typedIdentifier will pick up the + * .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("@", "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", "-J", "Pass 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 )", "") + 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 } + } +} -- cgit v1.2.3