diff options
Diffstat (limited to 'examples/scala-js/sbt-plugin/src/main/scala/scala')
30 files changed, 0 insertions, 3516 deletions
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala deleted file mode 100644 index 9eb7f69..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/AbstractJSDeps.scala +++ /dev/null @@ -1,81 +0,0 @@ -package scala.scalajs.sbtplugin - -import sbt._ - -import StringUtilities.nonEmpty - -import scala.scalajs.tools.jsdep.JSDependency - -/** Something JavaScript related a project may depend on. Either a JavaScript - * module/library, or the DOM at runtime. */ -sealed trait AbstractJSDep { - def configurations: Option[String] - - protected def withConfigs(configs: Option[String]): AbstractJSDep - - def %(configurations: Configuration): AbstractJSDep = %(configurations.name) - def %(configurations: String): AbstractJSDep = { - require(this.configurations.isEmpty, - "Configurations already specified for jsModule " + this) - nonEmpty(configurations, "Configurations") - withConfigs(Some(configurations)) - } - -} - -/** A JavaScript module/library a Scala.js project may depend on */ -sealed trait JSModuleID extends AbstractJSDep { - def jsDep: JSDependency - - protected def withJSDep(jsDep: JSDependency): JSModuleID - - def commonJSName(name: String): JSModuleID = - withJSDep(jsDep = jsDep.commonJSName(name)) - - def dependsOn(names: String*): JSModuleID = - withJSDep(jsDep = jsDep.dependsOn(names: _*)) -} - -/** A JavaScript module that resides inside a jar (probably webjar) */ -final case class JarJSModuleID( - module: ModuleID, - jsDep: JSDependency) extends JSModuleID { - - def configurations: Option[String] = module.configurations - - protected def withConfigs(configs: Option[String]): JSModuleID = - copy(module = module.copy(configurations = configs)) - protected def withJSDep(jsDep: JSDependency): JSModuleID = - copy(jsDep = jsDep) -} - -object JarJSModuleID { - def apply(module: ModuleID, name: String): JarJSModuleID = - JarJSModuleID(module, new JSDependency(name, Nil)) -} - -/** A JavaScript module that we depend on, but is provided externally or - * by the project itself */ -final case class ProvidedJSModuleID( - jsDep: JSDependency, - configurations: Option[String]) extends JSModuleID { - - protected def withConfigs(configs: Option[String]): JSModuleID = - copy(configurations = configs) - protected def withJSDep(jsDep: JSDependency): JSModuleID = - copy(jsDep = jsDep) -} - -object ProvidedJSModuleID { - def apply(name: String, configurations: Option[String]): ProvidedJSModuleID = - ProvidedJSModuleID(new JSDependency(name, Nil), configurations) -} - -sealed case class RuntimeDOM( - configurations: Option[String]) extends AbstractJSDep { - - protected def withConfigs(configs: Option[String]): RuntimeDOM = - copy(configurations = configs) -} - -object RuntimeDOM extends RuntimeDOM(None) diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala deleted file mode 100644 index 0c1559f..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Implicits.scala +++ /dev/null @@ -1,34 +0,0 @@ -package scala.scalajs.sbtplugin - -import scala.language.implicitConversions - -import scala.scalajs.tools.logging._ -import sbt.{Logger => SbtLogger, Level => SbtLevel} - -object Implicits { - private class SbtLoggerWrapper(underlying: SbtLogger) extends Logger { - def log(level: Level, message: => String): Unit = - underlying.log(level, message) - def success(message: => String): Unit = - underlying.success(message) - def trace(t: => Throwable): Unit = - underlying.trace(t) - } - - implicit def sbtLogger2ToolsLogger(logger: SbtLogger): Logger = - new SbtLoggerWrapper(logger) - - implicit def sbtLevel2ToolsLevel(level: SbtLevel.Value): Level = level match { - case SbtLevel.Error => Level.Error - case SbtLevel.Warn => Level.Warn - case SbtLevel.Info => Level.Info - case SbtLevel.Debug => Level.Debug - } - - implicit def toolsLevel2sbtLevel(level: Level): SbtLevel.Value = level match { - case Level.Error => SbtLevel.Error - case Level.Warn => SbtLevel.Warn - case Level.Info => SbtLevel.Info - case Level.Debug => SbtLevel.Debug - } -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala deleted file mode 100644 index a59f105..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/JSUtils.scala +++ /dev/null @@ -1,35 +0,0 @@ -package scala.scalajs.sbtplugin - -object JSUtils { - def listToJS(xs: List[String]): String = - xs.map(toJSstr _).mkString("[",",","]") - - /** (almost) stolen from scala.scalajs.compiler.JSPrinters */ - def toJSstr(str: String): String = { - /* Note that Java and JavaScript happen to use the same encoding for - * Unicode, namely UTF-16, which means that 1 char from Java always equals - * 1 char in JavaScript. */ - val builder = new StringBuilder() - builder.append('"') - str foreach { - case '\\' => builder.append("\\\\") - case '"' => builder.append("\\\"") - case '\u0007' => builder.append("\\a") - case '\u0008' => builder.append("\\b") - case '\u0009' => builder.append("\\t") - case '\u000A' => builder.append("\\n") - case '\u000B' => builder.append("\\v") - case '\u000C' => builder.append("\\f") - case '\u000D' => builder.append("\\r") - case c => - if (c >= 32 && c <= 126) builder.append(c.toChar) // ASCII printable characters - else builder.append(f"\\u$c%04x") - } - builder.append('"') - builder.result() - } - - def dot2bracket(name: String): String = { - name.split('.').map(s => s"""[${toJSstr(s)}]""").mkString - } -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala deleted file mode 100644 index ecfb546..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/LoggerJSConsole.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin - -import sbt.Logger -import scala.scalajs.tools.env.JSConsole - -/** A proxy for a Logger that looks like a Mozilla console object */ -class LoggerJSConsole(logger: Logger) extends JSConsole { - def log(msg: Any): Unit = logger.info(msg.toString) -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala deleted file mode 100644 index 25d6178..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/OptimizerOptions.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin - -import OptimizerOptions._ - -/** Various options for the Scala.js optimizer tool chain - * - * This is not a case class and does have a private constructor so that we - * can add fields in a binary-compatible manner. - * - * Use [[OptimizerOptions.apply]] and the `with` methods to create a configured - * instance. - */ -final class OptimizerOptions private ( - /** Whether to parallelize the optimizer (currently fastOptJS only) **/ - val parallel: Boolean = true, - /** Whether to run the optimizer in batch (i.e. non-incremental) mode */ - val batchMode: Boolean = false, - /** Whether to run the Scala.js optimizer */ - val disableOptimizer: Boolean = false, - /** Whether to pretty-print in fullOptJS */ - val prettyPrintFullOptJS: Boolean = false, - /** Perform expensive checks of the sanity of the Scala.js IR */ - val checkScalaJSIR: Boolean = false -) { - - def withParallel(parallel: Boolean): OptimizerOptions = { - new OptimizerOptions(parallel, batchMode, - disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR) - } - - def withBatchMode(batchMode: Boolean): OptimizerOptions = { - new OptimizerOptions(parallel, batchMode, - disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR) - } - - def withDisableOptimizer(disableOptimizer: Boolean): OptimizerOptions = { - new OptimizerOptions(parallel, batchMode, - disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR) - } - - def withPrettyPrintFullOptJS(prettyPrintFullOptJS: Boolean): OptimizerOptions = { - new OptimizerOptions(parallel, batchMode, - disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR) - } - - def withCheckScalaJSIR(checkScalaJSIR: Boolean): OptimizerOptions = { - new OptimizerOptions(parallel, batchMode, - disableOptimizer, prettyPrintFullOptJS, checkScalaJSIR) - } - - override def toString: String = { - s"""OptimizerOptions( - | parallel = $parallel - | batchMode = $batchMode - | disableOptimizer = $disableOptimizer - | prettyPrintFullOptJS = $prettyPrintFullOptJS - | checkScalaJSIR = $checkScalaJSIR - |)""".stripMargin - } - -} - -object OptimizerOptions { - def apply() = new OptimizerOptions() -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala deleted file mode 100644 index d813622..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSCrossVersion.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin - -import sbt._ - -import scala.scalajs.ir.ScalaJSVersions - -object ScalaJSCrossVersion { - private val scalaJSVersionUnmapped: String => String = - _ => s"sjs$currentBinaryVersion" - - private val scalaJSVersionMap: String => String = - version => s"sjs${currentBinaryVersion}_$version" - - private final val ReleaseVersion = - raw"""(\d+)\.(\d+)\.(\d+)""".r - private final val MinorSnapshotVersion = - raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r - - val currentBinaryVersion = binaryScalaJSVersion(ScalaJSVersions.current) - - def binaryScalaJSVersion(full: String): String = full match { - case ReleaseVersion(major, minor, release) => s"$major.$minor" - case MinorSnapshotVersion(major, minor, _) => s"$major.$minor" - case _ => full - } - - def scalaJSMapped(cross: CrossVersion): CrossVersion = cross match { - case CrossVersion.Disabled => - CrossVersion.binaryMapped(scalaJSVersionUnmapped) - case cross: CrossVersion.Binary => - CrossVersion.binaryMapped(cross.remapVersion andThen scalaJSVersionMap) - case cross: CrossVersion.Full => - CrossVersion.fullMapped(cross.remapVersion andThen scalaJSVersionMap) - } - - def binary: CrossVersion = scalaJSMapped(CrossVersion.binary) - - def full: CrossVersion = scalaJSMapped(CrossVersion.full) -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala deleted file mode 100644 index b33e2fb..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPlugin.scala +++ /dev/null @@ -1,179 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin - -import sbt._ - -import scala.scalajs.tools.sem.Semantics -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.io.VirtualJSFile -import scala.scalajs.tools.env.{JSEnv, JSConsole} -import scala.scalajs.tools.optimizer.ScalaJSOptimizer - -import scala.scalajs.ir.ScalaJSVersions - -import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv -import scala.scalajs.sbtplugin.env.phantomjs.PhantomJSEnv - -object ScalaJSPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - - object autoImport extends impl.DependencyBuilders { - import KeyRanks._ - - // Some constants - val scalaJSVersion = ScalaJSVersions.current - val scalaJSIsSnapshotVersion = ScalaJSVersions.currentIsSnapshot - val scalaJSBinaryVersion = ScalaJSCrossVersion.currentBinaryVersion - - // Stage values - val PreLinkStage = Stage.PreLink - val FastOptStage = Stage.FastOpt - val FullOptStage = Stage.FullOpt - - // Factory methods for JSEnvs - - /** - * Creates a [[Def.Initialize]] for a NodeJSEnv. Use this to explicitly - * specify in your build that you would like to run with Node.js: - * - * {{{ - * postLinkJSEnv := NodeJSEnv().value - * }}} - * - * Note that the resulting [[Setting]] is not scoped at all, but must be - * scoped in a project that has the ScalaJSPlugin enabled to work properly. - * Therefore, either put the upper line in your project settings (common - * case) or scope it manually, using [[Project.inScope]]. - */ - def NodeJSEnv( - executable: String = "node", - args: Seq[String] = Seq.empty, - env: Map[String, String] = Map.empty - ): Def.Initialize[Task[NodeJSEnv]] = Def.task { - new NodeJSEnv(executable, args, env) - } - - /** - * Creates a [[Def.Initialize]] for a PhantomJSEnv. Use this to explicitly - * specify in your build that you would like to run with PhantomJS: - * - * {{{ - * postLinkJSEnv := PhantomJSEnv().value - * }}} - * - * Note that the resulting [[Setting]] is not scoped at all, but must be - * scoped in a project that has the ScalaJSPlugin enabled to work properly. - * Therefore, either put the upper line in your project settings (common - * case) or scope it manually, using [[Project.inScope]]. - */ - def PhantomJSEnv( - executable: String = "phantomjs", - args: Seq[String] = Seq.empty, - env: Map[String, String] = Map.empty, - autoExit: Boolean = true - ): Def.Initialize[Task[PhantomJSEnv]] = Def.task { - val loader = scalaJSPhantomJSClassLoader.value - new PhantomJSEnv(executable, args, env, autoExit, loader) - } - - // All our public-facing keys - - val fastOptJS = TaskKey[Attributed[File]]("fastOptJS", - "Quickly link all compiled JavaScript into a single file", APlusTask) - val fullOptJS = TaskKey[Attributed[File]]("fullOptJS", - "Link all compiled JavaScript into a single file and fully optimize", APlusTask) - - val scalaJSStage = SettingKey[Stage]("scalaJSStage", - "The optimization stage at which run and test are executed", APlusSetting) - - val packageScalaJSLauncher = TaskKey[Attributed[File]]("packageScalaJSLauncher", - "Writes the persistent launcher file. Fails if the mainClass is ambigous", CTask) - - val packageJSDependencies = TaskKey[File]("packageJSDependencies", - "Packages all dependencies of the preLink classpath in a single file. " + - "Set skip in packageJSDependencies := false to run automatically", AMinusTask) - - val jsDependencyManifest = TaskKey[File]("jsDependencyManifest", - "Writes the JS_DEPENDENCIES file.", DTask) - - val scalaJSPreLinkClasspath = TaskKey[IRClasspath]("scalaJSPreLinkClasspath", - "Completely resolved classpath just after compilation", DTask) - - val scalaJSExecClasspath = TaskKey[CompleteClasspath]("scalaJSExecClasspath", - "The classpath used for running and testing", DTask) - - val scalaJSLauncher = TaskKey[Attributed[VirtualJSFile]]("scalaJSLauncher", - "Code used to run. (Attributed with used class name)", DTask) - - val scalaJSConsole = TaskKey[JSConsole]("scalaJSConsole", - "The JS console used by the Scala.js runner/tester", DTask) - - val preLinkJSEnv = TaskKey[JSEnv]("preLinkJSEnv", - "The jsEnv used to execute before linking (packaging / optimizing) Scala.js files", BSetting) - val postLinkJSEnv = TaskKey[JSEnv]("postLinkJSEnv", - "The jsEnv used to execute after linking (packaging / optimizing) Scala.js files", AMinusSetting) - - val jsEnv = TaskKey[JSEnv]("jsEnv", - "A JVM-like environment where Scala.js files can be run and tested", DTask) - - val requiresDOM = SettingKey[Boolean]("requiresDOM", - "Whether this projects needs the DOM. Overrides anything inherited through dependencies.", AMinusSetting) - - val scalaJSTestFramework = SettingKey[String]("scalaJSTestFramework", - "The Scala.js class that is used as a test framework, for example a class that wraps Jasmine", ASetting) - - val relativeSourceMaps = SettingKey[Boolean]("relativeSourceMaps", - "Make the referenced paths on source maps relative to target path", BPlusSetting) - - val emitSourceMaps = SettingKey[Boolean]("emitSourceMaps", - "Whether package and optimize stages should emit source maps at all", BPlusSetting) - - val jsDependencies = SettingKey[Seq[AbstractJSDep]]("jsDependencies", - "JavaScript libraries this project depends upon. Also used to depend on the DOM.", APlusSetting) - - val scalaJSSemantics = SettingKey[Semantics]("scalaJSSemantics", - "Configurable semantics of Scala.js.", BPlusSetting) - - val jsDependencyFilter = SettingKey[PartialClasspath.DependencyFilter]("jsDependencyFilter", - "The filter applied to the raw JavaScript dependencies before execution", CSetting) - - val checkScalaJSSemantics = SettingKey[Boolean]("checkScalaJSSemantics", - "Whether to check that the current semantics meet compliance " + - "requirements of dependencies.", CSetting) - - val persistLauncher = SettingKey[Boolean]("persistLauncher", - "Tell optimize/package tasks to write the laucher file to disk. " + - "If this is set, your project may only have a single mainClass or you must explicitly set it", AMinusSetting) - - val scalaJSOptimizerOptions = SettingKey[OptimizerOptions]("scalaJSOptimizerOptions", - "All kinds of options for the Scala.js optimizer stages", DSetting) - - /** Class loader for PhantomJSEnv. Used to load jetty8. */ - val scalaJSPhantomJSClassLoader = TaskKey[ClassLoader]("scalaJSPhantomJSClassLoader", - "Private class loader to load jetty8 without polluting classpath. Only use this " + - "as the `jettyClassLoader` argument of the PhantomJSEnv", - KeyRanks.Invisible) - } - - import autoImport._ - import ScalaJSPluginInternal._ - - override def globalSettings: Seq[Setting[_]] = { - super.globalSettings ++ Seq( - scalaJSStage := Stage.PreLink - ) - } - - override def projectSettings: Seq[Setting[_]] = ( - scalaJSAbstractSettings ++ - scalaJSEcosystemSettings - ) -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala deleted file mode 100644 index fe97f0b..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/ScalaJSPluginInternal.scala +++ /dev/null @@ -1,598 +0,0 @@ -package scala.scalajs.sbtplugin - -import sbt._ -import sbt.inc.{IncOptions, ClassfileManager} -import Keys._ - -import Implicits._ -import JSUtils._ - -import scala.scalajs.tools.sem.Semantics -import scala.scalajs.tools.io.{IO => toolsIO, _} -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.classpath.builder._ -import scala.scalajs.tools.jsdep._ -import scala.scalajs.tools.optimizer.{ - ScalaJSOptimizer, - ScalaJSClosureOptimizer, - IncOptimizer, - ParIncOptimizer -} -import scala.scalajs.tools.corelib.CoreJSLibs - -import scala.scalajs.tools.env._ -import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv -import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv -import scala.scalajs.sbtplugin.env.phantomjs.{PhantomJSEnv, PhantomJettyClassLoader} - -import scala.scalajs.ir.ScalaJSVersions - -import scala.scalajs.sbtplugin.testing.{TestFramework, JSClasspathLoader} - -import scala.util.Try - -import java.nio.charset.Charset -import java.net.URLClassLoader - -/** Contains settings used by ScalaJSPlugin that should not be automatically - * be in the *.sbt file's scope. - */ -object ScalaJSPluginInternal { - - import ScalaJSPlugin.autoImport._ - - /** Dummy setting to ensure we do not fork in Scala.js run & test. */ - val scalaJSEnsureUnforked = SettingKey[Boolean]("ensureUnforked", - "Scala.js internal: Fails if fork is true.", KeyRanks.Invisible) - - /** Dummy setting to persist Scala.js optimizer */ - val scalaJSOptimizer = SettingKey[ScalaJSOptimizer]("scalaJSOptimizer", - "Scala.js internal: Setting to persist the optimizer", KeyRanks.Invisible) - - /** Internal task to calculate whether a project requests the DOM - * (through jsDependencies or requiresDOM) */ - val scalaJSRequestsDOM = TaskKey[Boolean]("scalaJSRequestsDOM", - "Scala.js internal: Whether a project really wants the DOM. " + - "Calculated using requiresDOM and jsDependencies", KeyRanks.Invisible) - - /** Default post link environment */ - val scalaJSDefaultPostLinkJSEnv = TaskKey[JSEnv]("scalaJSDefaultPostLinkJSEnv", - "Scala.js internal: Default for postLinkJSEnv", KeyRanks.Invisible) - - /** Lookup key for CompleteClasspath in attribute maps */ - val scalaJSCompleteClasspath = - AttributeKey[CompleteClasspath]("scalaJSCompleteClasspath") - - /** Patches the IncOptions so that .sjsir files are pruned as needed. - * - * This complicated logic patches the ClassfileManager factory of the given - * IncOptions with one that is aware of .sjsir files emitted by the Scala.js - * compiler. This makes sure that, when a .class file must be deleted, the - * corresponding .sjsir file are also deleted. - */ - def scalaJSPatchIncOptions(incOptions: IncOptions): IncOptions = { - val inheritedNewClassfileManager = incOptions.newClassfileManager - val newClassfileManager = () => new ClassfileManager { - private[this] val inherited = inheritedNewClassfileManager() - - def delete(classes: Iterable[File]): Unit = { - inherited.delete(classes flatMap { classFile => - val scalaJSFiles = if (classFile.getPath endsWith ".class") { - val f = FileVirtualFile.withExtension(classFile, ".class", ".sjsir") - if (f.exists) List(f) - else Nil - } else Nil - classFile :: scalaJSFiles - }) - } - - def generated(classes: Iterable[File]): Unit = inherited.generated(classes) - def complete(success: Boolean): Unit = inherited.complete(success) - } - incOptions.copy(newClassfileManager = newClassfileManager) - } - - private def scalaJSOptimizerSetting(key: TaskKey[_]): Setting[_] = ( - scalaJSOptimizer in key := { - val semantics = (scalaJSSemantics in key).value - if ((scalaJSOptimizerOptions in key).value.parallel) - new ScalaJSOptimizer(semantics, new ParIncOptimizer(_)) - else - new ScalaJSOptimizer(semantics, new IncOptimizer(_)) - } - ) - - val scalaJSConfigSettings: Seq[Setting[_]] = Seq( - incOptions ~= scalaJSPatchIncOptions - ) ++ Seq( - - scalaJSPreLinkClasspath := { - val cp = fullClasspath.value - val pcp = PartialClasspathBuilder.build(Attributed.data(cp).toList) - val ccp = pcp.resolve(jsDependencyFilter.value) - - if (checkScalaJSSemantics.value) - ccp.checkCompliance(scalaJSSemantics.value) - - ccp - }, - - artifactPath in fastOptJS := - ((crossTarget in fastOptJS).value / - ((moduleName in fastOptJS).value + "-fastopt.js")), - - scalaJSOptimizerSetting(fastOptJS), - - fastOptJS := { - val s = streams.value - val output = (artifactPath in fastOptJS).value - val taskCache = - WritableFileVirtualTextFile(s.cacheDirectory / "fastopt-js") - - IO.createDirectory(output.getParentFile) - - val relSourceMapBase = - if ((relativeSourceMaps in fastOptJS).value) - Some(output.getParentFile.toURI()) - else None - - val opts = (scalaJSOptimizerOptions in fastOptJS).value - - import ScalaJSOptimizer._ - val outCP = (scalaJSOptimizer in fastOptJS).value.optimizeCP( - Inputs(input = (scalaJSPreLinkClasspath in fastOptJS).value), - OutputConfig( - output = WritableFileVirtualJSFile(output), - cache = Some(taskCache), - wantSourceMap = (emitSourceMaps in fastOptJS).value, - relativizeSourceMapBase = relSourceMapBase, - checkIR = opts.checkScalaJSIR, - disableOptimizer = opts.disableOptimizer, - batchMode = opts.batchMode), - s.log) - - Attributed.blank(output).put(scalaJSCompleteClasspath, outCP) - }, - fastOptJS <<= - fastOptJS.dependsOn(packageJSDependencies, packageScalaJSLauncher), - - artifactPath in fullOptJS := - ((crossTarget in fullOptJS).value / - ((moduleName in fullOptJS).value + "-opt.js")), - - scalaJSSemantics in fullOptJS := - (scalaJSSemantics in fastOptJS).value.optimized, - - scalaJSOptimizerSetting(fullOptJS), - - fullOptJS := { - val s = streams.value - val output = (artifactPath in fullOptJS).value - val taskCache = - WritableFileVirtualTextFile(s.cacheDirectory / "fullopt-js") - - IO.createDirectory(output.getParentFile) - - val relSourceMapBase = - if ((relativeSourceMaps in fullOptJS).value) - Some(output.getParentFile.toURI()) - else None - - val opts = (scalaJSOptimizerOptions in fullOptJS).value - - val semantics = (scalaJSSemantics in fullOptJS).value - - import ScalaJSClosureOptimizer._ - val outCP = new ScalaJSClosureOptimizer(semantics).optimizeCP( - (scalaJSOptimizer in fullOptJS).value, - Inputs(ScalaJSOptimizer.Inputs( - input = (scalaJSPreLinkClasspath in fullOptJS).value)), - OutputConfig( - output = WritableFileVirtualJSFile(output), - cache = Some(taskCache), - wantSourceMap = (emitSourceMaps in fullOptJS).value, - relativizeSourceMapBase = relSourceMapBase, - checkIR = opts.checkScalaJSIR, - disableOptimizer = opts.disableOptimizer, - batchMode = opts.batchMode, - prettyPrint = opts.prettyPrintFullOptJS), - s.log) - - Attributed.blank(output).put(scalaJSCompleteClasspath, outCP) - }, - - artifactPath in packageScalaJSLauncher := - ((crossTarget in packageScalaJSLauncher).value / - ((moduleName in packageScalaJSLauncher).value + "-launcher.js")), - - skip in packageScalaJSLauncher := !persistLauncher.value, - - packageScalaJSLauncher <<= Def.taskDyn { - if ((skip in packageScalaJSLauncher).value) - Def.task(Attributed.blank((artifactPath in packageScalaJSLauncher).value)) - else Def.task { - mainClass.value map { mainCl => - val file = (artifactPath in packageScalaJSLauncher).value - IO.write(file, launcherContent(mainCl), Charset.forName("UTF-8")) - - // Attach the name of the main class used, (ab?)using the name key - Attributed(file)(AttributeMap.empty.put(name.key, mainCl)) - } getOrElse { - sys.error("Cannot write launcher file, since there is no or multiple mainClasses") - } - } - }, - - artifactPath in packageJSDependencies := - ((crossTarget in packageJSDependencies).value / - ((moduleName in packageJSDependencies).value + "-jsdeps.js")), - - packageJSDependencies <<= Def.taskDyn { - if ((skip in packageJSDependencies).value) - Def.task((artifactPath in packageJSDependencies).value) - else Def.task { - val cp = scalaJSPreLinkClasspath.value - val output = (artifactPath in packageJSDependencies).value - val taskCache = WritableFileVirtualJSFile( - streams.value.cacheDirectory / "package-js-deps") - - IO.createDirectory(output.getParentFile) - - val outFile = WritableFileVirtualTextFile(output) - CacheUtils.cached(cp.version, outFile, Some(taskCache)) { - toolsIO.concatFiles(outFile, cp.jsLibs.map(_.lib)) - } - - output - } - }, - - jsDependencyManifest := { - val myModule = thisProject.value.id - val config = configuration.value.name - - // Collect all libraries - val jsDeps = jsDependencies.value.collect { - case dep: JSModuleID if dep.configurations.forall(_ == config) => - dep.jsDep - } - - val requiresDOM = jsDependencies.value.exists { - case RuntimeDOM(configurations) => - configurations.forall(_ == config) - case _ => false - } - - val compliantSemantics = scalaJSSemantics.value.compliants - - val manifest = new JSDependencyManifest(new Origin(myModule, config), - jsDeps.toList, requiresDOM, compliantSemantics) - - // Write dependency file to class directory - val targetDir = classDirectory.value - IO.createDirectory(targetDir) - - val file = targetDir / JSDependencyManifest.ManifestFileName - val vfile = WritableFileVirtualTextFile(file) - - // Prevent writing if unnecessary to not invalidate dependencies - val needWrite = !vfile.exists || { - Try { - val readManifest = JSDependencyManifest.read(vfile) - readManifest != manifest - } getOrElse true - } - - if (needWrite) - JSDependencyManifest.write(manifest, vfile) - - file - }, - - products <<= products.dependsOn(jsDependencyManifest), - - console <<= console.dependsOn(Def.task( - streams.value.log.warn("Scala REPL doesn't work with Scala.js. You " + - "are running a JVM REPL. JavaScript things won't work.") - )), - - // Give tasks ability to check we are not forking at build reading time - scalaJSEnsureUnforked := { - if (fork.value) - sys.error("Scala.js cannot be run in a forked JVM") - else - true - }, - - scalaJSRequestsDOM := - requiresDOM.?.value.getOrElse(scalaJSExecClasspath.value.requiresDOM), - - // Default jsEnv - jsEnv <<= Def.taskDyn { - scalaJSStage.value match { - case Stage.PreLink => - Def.task { - preLinkJSEnv.?.value.getOrElse { - new RhinoJSEnv(scalaJSSemantics.value, - withDOM = scalaJSRequestsDOM.value) - } - } - case Stage.FastOpt | Stage.FullOpt => - Def.task(scalaJSDefaultPostLinkJSEnv.value) - } - }, - - // Wire jsEnv and sources for other stages - scalaJSDefaultPostLinkJSEnv := postLinkJSEnv.?.value.getOrElse { - if (scalaJSRequestsDOM.value) - new PhantomJSEnv(jettyClassLoader = scalaJSPhantomJSClassLoader.value) - else - new NodeJSEnv - }, - - scalaJSExecClasspath <<= Def.taskDyn { - scalaJSStage.value match { - case Stage.PreLink => - Def.task { scalaJSPreLinkClasspath.value } - case Stage.FastOpt => - Def.task { fastOptJS.value.get(scalaJSCompleteClasspath).get } - case Stage.FullOpt => - Def.task { fullOptJS.value.get(scalaJSCompleteClasspath).get } - } - } - ) - - /** Run a class in a given environment using a given launcher */ - private def jsRun(env: JSEnv, cp: CompleteClasspath, mainCl: String, - launcher: VirtualJSFile, jsConsole: JSConsole, log: Logger) = { - - log.info("Running " + mainCl) - log.debug(s"with JSEnv of type ${env.getClass()}") - log.debug(s"with classpath of type ${cp.getClass}") - - // Actually run code - env.jsRunner(cp, launcher, log, jsConsole).run() - } - - private def launcherContent(mainCl: String) = { - // If we are running in Node.js, we need to bracket select on - // global rather than this - """((typeof global === "object" && global && - global["Object"] === Object) ? global : this)""" + - s"${dot2bracket(mainCl)}().main();\n" - } - - private def memLauncher(mainCl: String) = { - new MemVirtualJSFile("Generated launcher file") - .withContent(launcherContent(mainCl)) - } - - // These settings will be filtered by the stage dummy tasks - val scalaJSRunSettings = Seq( - mainClass in scalaJSLauncher := (mainClass in run).value, - scalaJSLauncher <<= Def.taskDyn { - if (persistLauncher.value) - Def.task(packageScalaJSLauncher.value.map(FileVirtualJSFile)) - else Def.task { - (mainClass in scalaJSLauncher).value map { mainClass => - val memLaunch = memLauncher(mainClass) - Attributed[VirtualJSFile](memLaunch)( - AttributeMap.empty.put(name.key, mainClass)) - } getOrElse { - sys.error("No main class detected.") - } - } - }, - - /* We do currently not discover objects containing a - * - * def main(args: Array[String]): Unit - * - * Support will be added again, as soon as we can run them - * reliably (e.g. without implicitly requiring that an exported - * - * def main(): Unit - * - * exists alongside. - */ - discoveredMainClasses := { - import xsbt.api.{Discovered, Discovery} - - val jsApp = "scala.scalajs.js.JSApp" - - def isJSApp(discovered: Discovered) = - discovered.isModule && discovered.baseClasses.contains(jsApp) - - Discovery(Set(jsApp), Set.empty)(Tests.allDefs(compile.value)) collect { - case (definition, discovered) if isJSApp(discovered) => - definition.name - } - }, - - run <<= Def.inputTask { - // use assert to prevent warning about pure expr in stat pos - assert(scalaJSEnsureUnforked.value) - - val launch = scalaJSLauncher.value - val className = launch.get(name.key).getOrElse("<unknown class>") - jsRun(jsEnv.value, scalaJSExecClasspath.value, className, - launch.data, scalaJSConsole.value, streams.value.log) - }, - - runMain <<= { - // Implicits for parsing - import sbinary.DefaultProtocol.StringFormat - import Cache.seqFormat - - val parser = Defaults.loadForParser(discoveredMainClasses)((s, names) => - Defaults.runMainParser(s, names getOrElse Nil)) - - Def.inputTask { - // use assert to prevent warning about pure expr in stat pos - assert(scalaJSEnsureUnforked.value) - - val mainCl = parser.parsed._1 - jsRun(jsEnv.value, scalaJSExecClasspath.value, mainCl, - memLauncher(mainCl), scalaJSConsole.value, streams.value.log) - } - } - ) - - val scalaJSCompileSettings = ( - scalaJSConfigSettings ++ - scalaJSRunSettings - ) - - val scalaJSTestFrameworkSettings = Seq( - // Copied from Defaults, but scoped. We need a JVM loader in - // loadedTestFrameworks to find out whether the framework exists. - testLoader in loadedTestFrameworks := { - TestFramework.createTestLoader( - Attributed.data(fullClasspath.value), - scalaInstance.value, - IO.createUniqueDirectory(taskTemporaryDirectory.value)) - }, - - loadedTestFrameworks := { - // use assert to prevent warning about pure expr in stat pos - assert(scalaJSEnsureUnforked.value) - - val loader = (testLoader in loadedTestFrameworks).value - val isTestFrameworkDefined = try { - Class.forName(scalaJSTestFramework.value, false, loader) - true - } catch { - case _: ClassNotFoundException => false - } - if (isTestFrameworkDefined) { - loadedTestFrameworks.value.updated( - sbt.TestFramework(classOf[TestFramework].getName), - new TestFramework( - environment = jsEnv.value, - jsConsole = scalaJSConsole.value, - testFramework = scalaJSTestFramework.value) - ) - } else { - loadedTestFrameworks.value - } - }, - - // Pseudo loader to pass classpath to test framework - testLoader := JSClasspathLoader(scalaJSExecClasspath.value) - ) - - val scalaJSTestBuildSettings = ( - scalaJSConfigSettings - ) ++ ( - Seq(fastOptJS, fullOptJS, packageScalaJSLauncher, - packageJSDependencies) map { packageJSTask => - moduleName in packageJSTask := moduleName.value + "-test" - } - ) - - val scalaJSTestSettings = ( - scalaJSTestBuildSettings ++ - scalaJSTestFrameworkSettings - ) - - val scalaJSDependenciesSettings = Seq( - // add all the webjars your jsDependencies depend upon - libraryDependencies ++= jsDependencies.value.collect { - case JarJSModuleID(module, _) => module - } - ) - - val scalaJSDefaultBuildConfigs = ( - inConfig(Compile)(scalaJSConfigSettings) ++ // build settings for Compile - inConfig(Test)(scalaJSTestBuildSettings) ++ - scalaJSDependenciesSettings - ) - - val scalaJSDefaultConfigs = ( - inConfig(Compile)(scalaJSCompileSettings) ++ - inConfig(Test)(scalaJSTestSettings) ++ - scalaJSDependenciesSettings - ) - - val phantomJSJettyModules = Seq( - "org.eclipse.jetty" % "jetty-websocket" % "8.1.16.v20140903", - "org.eclipse.jetty" % "jetty-server" % "8.1.16.v20140903" - ) - - val scalaJSProjectBaseSettings = Seq( - relativeSourceMaps := false, - persistLauncher := false, - - skip in packageJSDependencies := true, - - scalaJSTestFramework := "org.scalajs.jasminetest.JasmineTestFramework", - - emitSourceMaps := true, - - scalaJSOptimizerOptions := OptimizerOptions(), - - jsDependencies := Seq(), - jsDependencyFilter := identity, - - scalaJSSemantics := Semantics.Defaults, - checkScalaJSSemantics := true, - - scalaJSConsole := ConsoleJSConsole, - - clean <<= clean.dependsOn(Def.task { - // have clean reset incremental optimizer state - (scalaJSOptimizer in (Compile, fastOptJS)).value.clean() - (scalaJSOptimizer in (Test, fastOptJS)).value.clean() - }), - - /* Depend on jetty artifacts in dummy configuration to be able to inject - * them into the PhantomJS runner if necessary. - * See scalaJSPhantomJSClassLoader - */ - ivyConfigurations += config("phantom-js-jetty").hide, - libraryDependencies ++= phantomJSJettyModules.map(_ % "phantom-js-jetty"), - scalaJSPhantomJSClassLoader := { - val report = update.value - val jars = report.select(configurationFilter("phantom-js-jetty")) - - val jettyLoader = - new URLClassLoader(jars.map(_.toURI.toURL).toArray, null) - - new PhantomJettyClassLoader(jettyLoader, getClass.getClassLoader) - } - ) - - val scalaJSAbstractSettings: Seq[Setting[_]] = ( - scalaJSProjectBaseSettings ++ - scalaJSDefaultConfigs - ) - - val scalaJSAbstractBuildSettings: Seq[Setting[_]] = ( - scalaJSProjectBaseSettings ++ - scalaJSDefaultBuildConfigs - ) - - val scalaJSReleasesResolver = Resolver.url("scala-js-releases", - url("http://dl.bintray.com/content/scala-js/scala-js-releases"))( - Resolver.ivyStylePatterns) - val scalaJSSnapshotsResolver = Resolver.url("scala-js-snapshots", - url("http://repo.scala-js.org/repo/snapshots/"))( - Resolver.ivyStylePatterns) - - val scalaJSEcosystemSettings = Seq( - // the resolver to find the compiler and library (and others) - resolvers ++= Seq(scalaJSReleasesResolver, scalaJSSnapshotsResolver), - - // you will need the Scala.js compiler plugin - autoCompilerPlugins := true, - addCompilerPlugin( - "org.scala-lang.modules.scalajs" % "scalajs-compiler" % scalaJSVersion cross CrossVersion.full), - - // and of course the Scala.js library - libraryDependencies += "org.scala-lang.modules.scalajs" %% "scalajs-library" % scalaJSVersion, - - // and you will want to be cross-compiled on the Scala.js binary version - crossVersion := ScalaJSCrossVersion.binary - ) - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala deleted file mode 100644 index 7f7b916..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/Stage.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin - -sealed trait Stage - -object Stage { - case object PreLink extends Stage - case object FullOpt extends Stage - case object FastOpt extends Stage -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala deleted file mode 100644 index e0aa557..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala +++ /dev/null @@ -1,200 +0,0 @@ -package scala.scalajs.sbtplugin.env - -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.env._ -import scala.scalajs.tools.logging._ - -import scala.scalajs.sbtplugin.JSUtils._ - -import java.io.{ Console => _, _ } -import scala.io.Source - -import scala.concurrent.{Future, Promise} -import scala.util.Try - -abstract class ExternalJSEnv( - final protected val additionalArgs: Seq[String], - final protected val additionalEnv: Map[String, String]) extends AsyncJSEnv { - - /** Printable name of this VM */ - protected def vmName: String - - /** Command to execute (on shell) for this VM */ - protected def executable: String - - protected class AbstractExtRunner(protected val classpath: CompleteClasspath, - protected val code: VirtualJSFile, protected val logger: Logger, - protected val console: JSConsole) { - - /** JS files used to setup VM */ - protected def initFiles(): Seq[VirtualJSFile] = Nil - - /** Sends required data to VM Stdin (can throw) */ - protected def sendVMStdin(out: OutputStream): Unit = {} - - /** VM arguments excluding executable. Override to adapt. - * Overrider is responsible to add additionalArgs. - */ - protected def getVMArgs(): Seq[String] = additionalArgs - - /** VM environment. Override to adapt. - * - * Default is `sys.env` and [[additionalEnv]] - */ - protected def getVMEnv(): Map[String, String] = - sys.env ++ additionalEnv - - /** Get files that are a library (i.e. that do not run anything) */ - protected def getLibJSFiles(): Seq[VirtualJSFile] = - initFiles() ++ classpath.allCode - - /** Get all files that are passed to VM (libraries and code) */ - protected def getJSFiles(): Seq[VirtualJSFile] = - getLibJSFiles() :+ code - - /** write a single JS file to a writer using an include fct if appropriate */ - protected def writeJSFile(file: VirtualJSFile, writer: Writer): Unit = { - // The only platform-independent way to do this in JS is to dump the file. - writer.write(file.content) - writer.write('\n') - } - - /** Pipe stdin and stdout from/to VM */ - final protected def pipeVMData(vmInst: Process): Unit = { - // Send stdin to VM. - val out = vmInst.getOutputStream() - try { sendVMStdin(out) } - finally { out.close() } - - // Pipe stdout to console - pipeToConsole(vmInst.getInputStream(), console) - - // We are probably done (stdin is closed). Report any errors - val errSrc = Source.fromInputStream(vmInst.getErrorStream(), "UTF-8") - try { errSrc.getLines.foreach(err => logger.error(err)) } - finally { errSrc.close } - } - - /** Wait for the VM to terminate, verify exit code */ - final protected def waitForVM(vmInst: Process): Unit = { - // Make sure we are done. - vmInst.waitFor() - - // Get return value and return - val retVal = vmInst.exitValue - if (retVal != 0) - sys.error(s"$vmName exited with code $retVal") - } - - protected def startVM(): Process = { - val vmArgs = getVMArgs() - val vmEnv = getVMEnv() - - val allArgs = executable +: vmArgs - val pBuilder = new ProcessBuilder(allArgs: _*) - - pBuilder.environment().clear() - for ((name, value) <- vmEnv) - pBuilder.environment().put(name, value) - - pBuilder.start() - } - - /** send a bunch of JS files to an output stream */ - final protected def sendJS(files: Seq[VirtualJSFile], - out: OutputStream): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")) - try sendJS(files, writer) - finally writer.close() - } - - /** send a bunch of JS files to a writer */ - final protected def sendJS(files: Seq[VirtualJSFile], out: Writer): Unit = - files.foreach { writeJSFile(_, out) } - - /** pipe lines from input stream to JSConsole */ - final protected def pipeToConsole(in: InputStream, console: JSConsole) = { - val source = Source.fromInputStream(in, "UTF-8") - try { source.getLines.foreach(console.log _) } - finally { source.close() } - } - - } - - protected class ExtRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole - ) extends AbstractExtRunner(classpath, code, logger, console) - with JSRunner { - - def run(): Unit = { - val vmInst = startVM() - - pipeVMData(vmInst) - waitForVM(vmInst) - } - } - - protected class AsyncExtRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends AbstractExtRunner(classpath, code, logger, console) - with AsyncJSRunner { - - private[this] var vmInst: Process = null - private[this] var ioThreadEx: Throwable = null - private[this] val promise = Promise[Unit] - - private[this] val thread = new Thread { - override def run(): Unit = { - // This thread should not be interrupted, so it is safe to use Trys - val pipeResult = Try(pipeVMData(vmInst)) - val vmComplete = Try(waitForVM(vmInst)) - - // Store IO exception - pipeResult recover { - case e => ioThreadEx = e - } - - // Chain Try's the other way: We want VM failure first, then IO failure - promise.complete(pipeResult orElse vmComplete) - } - } - - def start(): Future[Unit] = { - require(vmInst == null, "start() may only be called once") - vmInst = startVM() - thread.start() - promise.future - } - - def stop(): Unit = { - require(vmInst != null, "start() must have been called") - vmInst.destroy() - } - - def isRunning(): Boolean = { - require(vmInst != null, "start() must have been called") - // Emulate JDK 8 Process.isAlive - try { - vmInst.exitValue() - false - } catch { - case e: IllegalThreadStateException => - true - } - } - - def await(): Unit = { - require(vmInst != null, "start() must have been called") - thread.join() - waitForVM(vmInst) - - // At this point, the VM itself didn't fail. We need to check if - // anything bad happened while piping the data from the VM - - if (ioThreadEx != null) - throw ioThreadEx - } - } - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala deleted file mode 100644 index fca1c47..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala +++ /dev/null @@ -1,67 +0,0 @@ -package scala.scalajs.sbtplugin.env - -import scala.scalajs.tools.io.{IO => _, _} - -import sbt.IO - -import java.io.File - -/** A helper class to temporarily store virtual files to the filesystem. - * - * Can be used with tools that require real files. - * @param singleDir if true, forces files to be copied into - * [[cacheDir]]. Useful to setup include directories for - * example. - */ -final class VirtualFileMaterializer(singleDir: Boolean = false) { - - val cacheDir = { - val dir = IO.createTemporaryDirectory - dir.deleteOnExit() - dir - } - - /** Create a target file to write/copy to. Will also call - * deleteOnExit on the file. - */ - private def trgFile(name: String): File = { - val f = new File(cacheDir, name) - f.deleteOnExit() - f - } - - private def materializeFileVF(vf: FileVirtualFile): File = { - if (!singleDir) vf.file - else { - val trg = trgFile(vf.name) - IO.copyFile(vf.file, trg) - trg - } - } - - def materialize(vf: VirtualTextFile): File = vf match { - case vf: FileVirtualFile => materializeFileVF(vf) - case _ => - val trg = trgFile(vf.name) - IO.write(trg, vf.content) - trg - } - - def materialize(vf: VirtualBinaryFile): File = vf match { - case vf: FileVirtualFile => materializeFileVF(vf) - case _ => - val trg = trgFile(vf.name) - IO.write(trg, vf.content) - trg - } - - /** Removes the cache directory. Any operation on this - * VirtualFileMaterializer is invalid after [[close]] has been - * called. - */ - def close(): Unit = { - cacheDir.listFiles().foreach(_.delete) - cacheDir.delete() - } - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala deleted file mode 100644 index dfabe23..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala +++ /dev/null @@ -1,306 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env.nodejs - -import scala.scalajs.sbtplugin.env._ -import scala.scalajs.sbtplugin.JSUtils.toJSstr - -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.env._ -import scala.scalajs.tools.jsdep._ -import scala.scalajs.tools.logging._ - -import scala.scalajs.sbtplugin.JSUtils._ - -import java.io.{ Console => _, _ } -import java.net._ - -import scala.io.Source - -class NodeJSEnv( - nodejsPath: String = "node", - addArgs: Seq[String] = Seq.empty, - addEnv: Map[String, String] = Map.empty -) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv { - - protected def vmName: String = "node.js" - protected def executable: String = nodejsPath - - override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): JSRunner = { - new NodeRunner(classpath, code, logger, console) - } - - override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): AsyncJSRunner = { - new AsyncNodeRunner(classpath, code, logger, console) - } - - override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): ComJSRunner = { - new ComNodeRunner(classpath, code, logger, console) - } - - protected class NodeRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends ExtRunner(classpath, code, logger, console) - with AbstractNodeRunner - - protected class AsyncNodeRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends AsyncExtRunner(classpath, code, logger, console) - with AbstractNodeRunner - - protected class ComNodeRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends AsyncNodeRunner(classpath, code, logger, console) - with ComJSRunner { - - /** Retry-timeout to wait for the JS VM to connect */ - private final val acceptTimeout = 1000 - - private[this] val serverSocket = - new ServerSocket(0, 0, InetAddress.getByName(null)) // Loopback address - private[this] var comSocket: Socket = _ - private[this] var jvm2js: DataOutputStream = _ - private[this] var js2jvm: DataInputStream = _ - - private def comSetup = new MemVirtualJSFile("comSetup.js").withContent( - s""" - (function() { - // The socket for communication - var socket = null; - // The callback where received messages go - var recvCallback = null; - - // Buffers received data - var inBuffer = new Buffer(0); - - function onData(data) { - inBuffer = Buffer.concat([inBuffer, data]); - tryReadMsg(); - } - - function tryReadMsg() { - if (inBuffer.length < 4) return; - var msgLen = inBuffer.readInt32BE(0); - var byteLen = 4 + msgLen * 2; - - if (inBuffer.length < byteLen) return; - var res = ""; - - for (var i = 0; i < msgLen; ++i) - res += String.fromCharCode(inBuffer.readInt16BE(4 + i * 2)); - - inBuffer = inBuffer.slice(byteLen); - - recvCallback(res); - } - - global.scalajsCom = { - init: function(recvCB) { - if (socket !== null) throw new Error("Com already open"); - - var net = require('net'); - recvCallback = recvCB; - socket = net.connect(${serverSocket.getLocalPort}); - socket.on('data', onData); - }, - send: function(msg) { - if (socket === null) throw new Error("Com not open"); - - var len = msg.length; - var buf = new Buffer(4 + len * 2); - buf.writeInt32BE(len, 0); - for (var i = 0; i < len; ++i) - buf.writeInt16BE(msg.charCodeAt(i), 4 + i * 2); - socket.write(buf); - }, - close: function() { - if (socket === null) throw new Error("Com not open"); - socket.end(); - } - } - }).call(this); - """ - ) - - def send(msg: String): Unit = { - if (awaitConnection()) { - jvm2js.writeInt(msg.length) - jvm2js.writeChars(msg) - jvm2js.flush() - } - } - - def receive(): String = { - if (!awaitConnection()) - throw new ComJSEnv.ComClosedException - try { - val len = js2jvm.readInt() - val carr = Array.fill(len)(js2jvm.readChar()) - String.valueOf(carr) - } catch { - case e: EOFException => - throw new ComJSEnv.ComClosedException - } - } - - def close(): Unit = { - serverSocket.close() - if (jvm2js != null) - jvm2js.close() - if (js2jvm != null) - js2jvm.close() - if (comSocket != null) - comSocket.close() - } - - override def stop(): Unit = { - close() - super.stop() - } - - /** Waits until the JS VM has established a connection or terminates - * @return true if the connection was established - */ - private def awaitConnection(): Boolean = { - serverSocket.setSoTimeout(acceptTimeout) - while (comSocket == null && isRunning) { - try { - comSocket = serverSocket.accept() - jvm2js = new DataOutputStream( - new BufferedOutputStream(comSocket.getOutputStream())) - js2jvm = new DataInputStream( - new BufferedInputStream(comSocket.getInputStream())) - } catch { - case to: SocketTimeoutException => - } - } - - comSocket != null - } - - override protected def initFiles(): Seq[VirtualJSFile] = - super.initFiles :+ comSetup - - override protected def finalize(): Unit = close() - } - - protected trait AbstractNodeRunner extends AbstractExtRunner { - - protected[this] val libCache = new VirtualFileMaterializer(true) - - /** File(s) to automatically install source-map-support. - * Is used by [[initFiles]], override to change/disable. - */ - protected def installSourceMap(): Seq[VirtualJSFile] = Seq( - new MemVirtualJSFile("sourceMapSupport.js").withContent( - """ - try { - require('source-map-support').install(); - } catch (e) {} - """ - ) - ) - - /** File(s) to hack console.log to prevent if from changing `%%` to `%`. - * Is used by [[initFiles]], override to change/disable. - */ - protected def fixPercentConsole(): Seq[VirtualJSFile] = Seq( - new MemVirtualJSFile("nodeConsoleHack.js").withContent( - """ - // Hack console log to duplicate double % signs - (function() { - var oldLog = console.log; - var newLog = function() { - var args = arguments; - if (args.length >= 1 && args[0] !== void 0 && args[0] !== null) { - args[0] = args[0].toString().replace(/%/g, "%%"); - } - oldLog.apply(console, args); - }; - console.log = newLog; - })(); - """ - ) - ) - - /** File(s) to define `__ScalaJSEnv`. Defines `exitFunction`. - * Is used by [[initFiles]], override to change/disable. - */ - protected def runtimeEnv(): Seq[VirtualJSFile] = Seq( - new MemVirtualJSFile("scalaJSEnvInfo.js").withContent( - """ - __ScalaJSEnv = { - exitFunction: function(status) { process.exit(status); } - }; - """ - ) - ) - - /** Concatenates results from [[installSourceMap]], [[fixPercentConsole]] and - * [[runtimeEnv]] (in this order). - */ - override protected def initFiles(): Seq[VirtualJSFile] = - installSourceMap() ++ fixPercentConsole() ++ runtimeEnv() - - /** Libraries are loaded via require in Node.js */ - override protected def getLibJSFiles(): Seq[VirtualJSFile] = { - initFiles() ++ - classpath.jsLibs.map(requireLibrary) :+ - classpath.scalaJSCode - } - - /** Rewrites a library virtual file to a require statement if possible */ - protected def requireLibrary(dep: ResolvedJSDependency): VirtualJSFile = { - dep.info.commonJSName.fold(dep.lib) { varname => - val fname = dep.lib.name - libCache.materialize(dep.lib) - new MemVirtualJSFile(s"require-$fname").withContent( - s"""$varname = require(${toJSstr(fname)});""" - ) - } - } - - // Send code to Stdin - override protected def sendVMStdin(out: OutputStream): Unit = { - sendJS(getJSFiles(), out) - } - - /** write a single JS file to a writer using an include fct if appropriate - * uses `require` if the file exists on the filesystem - */ - override protected def writeJSFile(file: VirtualJSFile, - writer: Writer): Unit = { - file match { - case file: FileVirtualJSFile => - val fname = toJSstr(file.file.getAbsolutePath) - writer.write(s"require($fname);\n") - case _ => - super.writeJSFile(file, writer) - } - } - - // Node.js specific (system) environment - override protected def getVMEnv(): Map[String, String] = { - val baseNodePath = sys.env.get("NODE_PATH").filter(_.nonEmpty) - val nodePath = libCache.cacheDir.getAbsolutePath + - baseNodePath.fold("")(p => File.pathSeparator + p) - - sys.env ++ Seq( - "NODE_MODULE_CONTEXTS" -> "0", - "NODE_PATH" -> nodePath - ) ++ additionalEnv - } - } - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala deleted file mode 100644 index 3dec79c..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala +++ /dev/null @@ -1,126 +0,0 @@ -package scala.scalajs.sbtplugin.env.phantomjs - -import javax.servlet.http.HttpServletRequest - -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.server.nio.SelectChannelConnector -import org.eclipse.jetty.websocket.{WebSocket, WebSocketHandler} -import org.eclipse.jetty.util.component.{LifeCycle, AbstractLifeCycle} -import org.eclipse.jetty.util.log - -private[phantomjs] final class JettyWebsocketManager( - wsListener: WebsocketListener) extends WebsocketManager { thisMgr => - - private[this] var webSocketConn: WebSocket.Connection = null - private[this] var closed = false - - // We can just set the logger here, since we are supposed to be protected by - // the private ClassLoader that loads us reflectively. - log.Log.setLog(new WSLogger("root")) - - private[this] val connector = new SelectChannelConnector - - connector.setHost("localhost") - connector.setPort(0) - - private[this] val server = new Server() - - server.addConnector(connector) - server.setHandler(new WebSocketHandler { - // Support Hixie 76 for Phantom.js - getWebSocketFactory().setMinVersion(-1) - - override def doWebSocketConnect( - request: HttpServletRequest, protocol: String): WebSocket = - new ComWebSocketListener - }) - - server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener { - override def lifeCycleStarted(event: LifeCycle): Unit = { - if (event.isRunning()) - wsListener.onRunning() - } - }) - - private class ComWebSocketListener extends WebSocket.OnTextMessage { - override def onOpen(connection: WebSocket.Connection): Unit = { - thisMgr.synchronized { - if (isConnected) - throw new IllegalStateException("Client connected twice") - webSocketConn = connection - } - wsListener.onOpen() - } - - override def onClose(statusCode: Int, reason: String): Unit = { - thisMgr.synchronized { - webSocketConn = null - closed = true - } - wsListener.onClose() - server.stop() - - if (statusCode != 1000) { - throw new Exception("Abnormal closing of connection. " + - s"Code: $statusCode, Reason: $reason") - } - } - - override def onMessage(message: String): Unit = - wsListener.onMessage(message) - } - - private class WSLogger(fullName: String) extends log.AbstractLogger { - private[this] var debugEnabled = false - - def debug(msg: String, args: Object*): Unit = - if (debugEnabled) log("DEBUG", msg, args) - - def debug(msg: String, thrown: Throwable): Unit = - if (debugEnabled) log("DEBUG", msg, thrown) - - def debug(thrown: Throwable): Unit = - if (debugEnabled) log("DEBUG", thrown) - - def getName(): String = fullName - - def ignore(ignored: Throwable): Unit = () - - def info(msg: String, args: Object*): Unit = log("INFO", msg, args) - def info(msg: String, thrown: Throwable): Unit = log("INFO", msg, thrown) - def info(thrown: Throwable): Unit = log("INFO", thrown) - - def warn(msg: String, args: Object*): Unit = log("WARN", msg, args) - def warn(msg: String, thrown: Throwable): Unit = log("WARN", msg, thrown) - def warn(thrown: Throwable): Unit = log("WARN", thrown) - - def isDebugEnabled(): Boolean = debugEnabled - def setDebugEnabled(enabled: Boolean): Unit = debugEnabled = enabled - - private def log(lvl: String, msg: String, args: Object*): Unit = - wsListener.log(s"$lvl: $msg " + args.mkString(", ")) - - private def log(lvl: String, msg: String, thrown: Throwable): Unit = - wsListener.log(s"$lvl: $msg $thrown\n{$thrown.getStackStrace}") - - private def log(lvl: String, thrown: Throwable): Unit = - wsListener.log(s"$lvl: $thrown\n{$thrown.getStackStrace}") - - protected def newLogger(fullName: String) = new WSLogger(fullName) - } - - def start(): Unit = server.start() - - def stop(): Unit = server.stop() - - def isConnected: Boolean = webSocketConn != null && !closed - def isClosed: Boolean = closed - - def localPort: Int = connector.getLocalPort() - - def sendMessage(msg: String) = synchronized { - if (webSocketConn != null) - webSocketConn.sendMessage(msg) - } - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala deleted file mode 100644 index 7bb47d2..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala +++ /dev/null @@ -1,466 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env.phantomjs - -import scala.scalajs.sbtplugin.env._ - -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.env._ -import scala.scalajs.tools.logging._ - -import scala.scalajs.sbtplugin.JSUtils._ - -import java.io.{ Console => _, _ } -import java.net._ - -import scala.io.Source -import scala.collection.mutable -import scala.annotation.tailrec - -class PhantomJSEnv( - phantomjsPath: String = "phantomjs", - addArgs: Seq[String] = Seq.empty, - addEnv: Map[String, String] = Map.empty, - val autoExit: Boolean = true, - jettyClassLoader: ClassLoader = getClass().getClassLoader() -) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv { - - import PhantomJSEnv._ - - protected def vmName: String = "PhantomJS" - protected def executable: String = phantomjsPath - - override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): JSRunner = { - new PhantomRunner(classpath, code, logger, console) - } - - override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): AsyncJSRunner = { - new AsyncPhantomRunner(classpath, code, logger, console) - } - - override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): ComJSRunner = { - new ComPhantomRunner(classpath, code, logger, console) - } - - protected class PhantomRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends ExtRunner(classpath, code, logger, console) - with AbstractPhantomRunner - - protected class AsyncPhantomRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends AsyncExtRunner(classpath, code, logger, console) - with AbstractPhantomRunner - - protected class ComPhantomRunner(classpath: CompleteClasspath, - code: VirtualJSFile, logger: Logger, console: JSConsole - ) extends AsyncPhantomRunner(classpath, code, logger, console) - with ComJSRunner with WebsocketListener { - - private def loadMgr() = { - val clazz = jettyClassLoader.loadClass( - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager") - - val ctors = clazz.getConstructors() - assert(ctors.length == 1, "JettyWebsocketManager may only have one ctor") - - val mgr = ctors.head.newInstance(this) - - mgr.asInstanceOf[WebsocketManager] - } - - val mgr: WebsocketManager = loadMgr() - - def onRunning(): Unit = synchronized(notifyAll()) - def onOpen(): Unit = synchronized(notifyAll()) - def onClose(): Unit = synchronized(notifyAll()) - - def onMessage(msg: String): Unit = synchronized { - recvBuf.enqueue(msg) - notifyAll() - } - - def log(msg: String): Unit = logger.debug(s"PhantomJS WS Jetty: $msg") - - private[this] val recvBuf = mutable.Queue.empty[String] - - mgr.start() - - /** The websocket server starts asynchronously, but we need the port it is - * running on. This method waits until the port is non-negative and - * returns its value. - */ - private def waitForPort(): Int = { - while (mgr.localPort < 0) - wait() - mgr.localPort - } - - private def comSetup = { - def maybeExit(code: Int) = - if (autoExit) - s"window.callPhantom({ action: 'exit', returnValue: $code });" - else - "" - - val serverPort = waitForPort() - - val code = s""" - |(function() { - | var MaxPayloadSize = $MaxCharPayloadSize; - | - | // The socket for communication - | var websocket = null; - | - | // Buffer for messages sent before socket is open - | var outMsgBuf = null; - | - | function sendImpl(msg) { - | var frags = (msg.length / MaxPayloadSize) | 0; - | - | for (var i = 0; i < frags; ++i) { - | var payload = msg.substring( - | i * MaxPayloadSize, (i + 1) * MaxPayloadSize); - | websocket.send("1" + payload); - | } - | - | websocket.send("0" + msg.substring(frags * MaxPayloadSize)); - | } - | - | function recvImpl(recvCB) { - | var recvBuf = ""; - | - | return function(evt) { - | var newData = recvBuf + evt.data.substring(1); - | if (evt.data.charAt(0) == "0") { - | recvBuf = ""; - | recvCB(newData); - | } else if (evt.data.charAt(0) == "1") { - | recvBuf = newData; - | } else { - | throw new Error("Bad fragmentation flag in " + evt.data); - | } - | }; - | } - | - | window.scalajsCom = { - | init: function(recvCB) { - | if (websocket !== null) throw new Error("Com already open"); - | - | outMsgBuf = []; - | - | websocket = new WebSocket("ws://localhost:$serverPort"); - | - | websocket.onopen = function(evt) { - | for (var i = 0; i < outMsgBuf.length; ++i) - | sendImpl(outMsgBuf[i]); - | outMsgBuf = null; - | }; - | websocket.onclose = function(evt) { - | websocket = null; - | ${maybeExit(0)} - | }; - | websocket.onmessage = recvImpl(recvCB); - | websocket.onerror = function(evt) { - | websocket = null; - | throw new Error("Websocket failed: " + evt); - | }; - | - | // Take over responsibility to auto exit - | window.callPhantom({ - | action: 'setAutoExit', - | autoExit: false - | }); - | }, - | send: function(msg) { - | if (websocket === null) - | return; // we are closed already. ignore message - | - | if (outMsgBuf !== null) - | outMsgBuf.push(msg); - | else - | sendImpl(msg); - | }, - | close: function() { - | if (websocket === null) - | return; // we are closed already. all is well. - | - | if (outMsgBuf !== null) - | // Reschedule ourselves to give onopen a chance to kick in - | window.setTimeout(window.scalajsCom.close, 10); - | else - | websocket.close(); - | } - | } - |}).call(this);""".stripMargin - - new MemVirtualJSFile("comSetup.js").withContent(code) - } - - def send(msg: String): Unit = synchronized { - if (awaitConnection()) { - val fragParts = msg.length / MaxCharPayloadSize - - for (i <- 0 until fragParts) { - val payload = msg.substring( - i * MaxCharPayloadSize, (i + 1) * MaxCharPayloadSize) - mgr.sendMessage("1" + payload) - } - - mgr.sendMessage("0" + msg.substring(fragParts * MaxCharPayloadSize)) - } - } - - def receive(): String = synchronized { - if (recvBuf.isEmpty && !awaitConnection()) - throw new ComJSEnv.ComClosedException - - @tailrec - def loop(acc: String): String = { - val frag = receiveFrag() - val newAcc = acc + frag.substring(1) - - if (frag(0) == '0') - newAcc - else if (frag(0) == '1') - loop(newAcc) - else - throw new AssertionError("Bad fragmentation flag in " + frag) - } - - loop("") - } - - private def receiveFrag(): String = { - while (recvBuf.isEmpty && !mgr.isClosed) - wait() - - if (recvBuf.isEmpty) - throw new ComJSEnv.ComClosedException - else - recvBuf.dequeue() - } - - def close(): Unit = mgr.stop() - - override def stop(): Unit = { - close() - super.stop() - } - - /** Waits until the JS VM has established a connection, or the VM - * terminated. Returns true if a connection was established. - */ - private def awaitConnection(): Boolean = { - while (!mgr.isConnected && !mgr.isClosed && isRunning) - wait(200) // We sleep-wait for isRunning - - mgr.isConnected - } - - override protected def initFiles(): Seq[VirtualJSFile] = - super.initFiles :+ comSetup - } - - protected trait AbstractPhantomRunner extends AbstractExtRunner { - - override protected def getVMArgs() = - // Add launcher file to arguments - additionalArgs :+ createTmpLauncherFile().getAbsolutePath - - /** In phantom.js, we include JS using HTML */ - override protected def writeJSFile(file: VirtualJSFile, writer: Writer) = { - file match { - case file: FileVirtualJSFile => - val fname = htmlEscape(file.file.getAbsolutePath) - writer.write( - s"""<script type="text/javascript" src="$fname"></script>""" + "\n") - case _ => - writer.write("""<script type="text/javascript">""" + "\n") - writer.write(s"// Virtual File: ${file.path}\n") - writer.write(file.content) - writer.write("</script>\n") - } - } - - /** - * PhantomJS doesn't support Function.prototype.bind. We polyfill it. - * https://github.com/ariya/phantomjs/issues/10522 - */ - override protected def initFiles(): Seq[VirtualJSFile] = Seq( - new MemVirtualJSFile("bindPolyfill.js").withContent( - """ - |// Polyfill for Function.bind from Facebook react: - |// https://github.com/facebook/react/blob/3dc10749080a460e48bee46d769763ec7191ac76/src/test/phantomjs-shims.js - |// Originally licensed under Apache 2.0 - |(function() { - | - | var Ap = Array.prototype; - | var slice = Ap.slice; - | var Fp = Function.prototype; - | - | if (!Fp.bind) { - | // PhantomJS doesn't support Function.prototype.bind natively, so - | // polyfill it whenever this module is required. - | Fp.bind = function(context) { - | var func = this; - | var args = slice.call(arguments, 1); - | - | function bound() { - | var invokedAsConstructor = func.prototype && (this instanceof func); - | return func.apply( - | // Ignore the context parameter when invoking the bound function - | // as a constructor. Note that this includes not only constructor - | // invocations using the new keyword but also calls to base class - | // constructors such as BaseClass.call(this, ...) or super(...). - | !invokedAsConstructor && context || this, - | args.concat(slice.call(arguments)) - | ); - | } - | - | // The bound function must share the .prototype of the unbound - | // function so that any object created by one constructor will count - | // as an instance of both constructors. - | bound.prototype = func.prototype; - | - | return bound; - | }; - | } - | - |})(); - |""".stripMargin - ), - new MemVirtualJSFile("scalaJSEnvInfo.js").withContent( - """ - |__ScalaJSEnv = { - | exitFunction: function(status) { - | window.callPhantom({ - | action: 'exit', - | returnValue: status | 0 - | }); - | } - |}; - """.stripMargin - ) - ) - - protected def writeWebpageLauncher(out: Writer): Unit = { - out.write("<html>\n<head>\n<title>Phantom.js Launcher</title>\n") - sendJS(getLibJSFiles(), out) - writeCodeLauncher(code, out) - out.write("</head>\n<body></body>\n</html>\n") - } - - protected def createTmpLauncherFile(): File = { - val webF = createTmpWebpage() - - val launcherTmpF = File.createTempFile("phantomjs-launcher", ".js") - launcherTmpF.deleteOnExit() - - val out = new FileWriter(launcherTmpF) - - try { - out.write( - s"""// Scala.js Phantom.js launcher - |var page = require('webpage').create(); - |var url = ${toJSstr(webF.getAbsolutePath)}; - |var autoExit = $autoExit; - |page.onConsoleMessage = function(msg) { - | console.log(msg); - |}; - |page.onError = function(msg, trace) { - | console.error(msg); - | if (trace && trace.length) { - | console.error(''); - | trace.forEach(function(t) { - | console.error(' ' + t.file + ':' + t.line + (t.function ? ' (in function "' + t.function +'")' : '')); - | }); - | } - | - | phantom.exit(2); - |}; - |page.onCallback = function(data) { - | if (!data.action) { - | console.error('Called callback without action'); - | phantom.exit(3); - | } else if (data.action === 'exit') { - | phantom.exit(data.returnValue || 0); - | } else if (data.action === 'setAutoExit') { - | if (typeof(data.autoExit) === 'boolean') - | autoExit = data.autoExit; - | else - | autoExit = true; - | } else { - | console.error('Unknown callback action ' + data.action); - | phantom.exit(4); - | } - |}; - |page.open(url, function (status) { - | if (autoExit || status !== 'success') - | phantom.exit(status !== 'success'); - |}); - |""".stripMargin) - } finally { - out.close() - } - - logger.debug( - "PhantomJS using launcher at: " + launcherTmpF.getAbsolutePath()) - - launcherTmpF - } - - protected def createTmpWebpage(): File = { - val webTmpF = File.createTempFile("phantomjs-launcher-webpage", ".html") - webTmpF.deleteOnExit() - - val out = new BufferedWriter(new FileWriter(webTmpF)) - try { - writeWebpageLauncher(out) - } finally { - out.close() - } - - logger.debug( - "PhantomJS using webpage launcher at: " + webTmpF.getAbsolutePath()) - - webTmpF - } - - protected def writeCodeLauncher(code: VirtualJSFile, out: Writer): Unit = { - out.write("""<script type="text/javascript">""" + "\n") - out.write("// Phantom.js code launcher\n") - out.write(s"// Origin: ${code.path}\n") - out.write("window.addEventListener('load', function() {\n") - out.write(code.content) - out.write("}, false);\n") - out.write("</script>\n") - } - } - - protected def htmlEscape(str: String): String = str.flatMap { - case '<' => "<" - case '>' => ">" - case '"' => """ - case '&' => "&" - case c => c :: Nil - } - -} - -object PhantomJSEnv { - private final val MaxByteMessageSize = 32768 // 32 KB - private final val MaxCharMessageSize = MaxByteMessageSize / 2 // 2B per char - private final val MaxCharPayloadSize = MaxCharMessageSize - 1 // frag flag -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala deleted file mode 100644 index 02c229b..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala +++ /dev/null @@ -1,63 +0,0 @@ -package scala.scalajs.sbtplugin.env.phantomjs - -import scala.scalajs.tools.io.IO - -/** A special [[ClassLoader]] to load the Jetty 8 dependency of [[PhantomJSEnv]] - * in a private space. - * - * It loads everything that belongs to [[JettyWebsocketManager]] itself (while - * retrieving the requested class file from its parent. - * For all other classes, it first tries to load them from [[jettyLoader]], - * which should only contain the Jetty 8 classpath. - * If this fails, it delegates to its parent. - * - * The rationale is, that [[JettyWebsocketManager]] and its dependees can use - * the classes on the Jetty 8 classpath, while they remain hidden from the rest - * of the Java world. This allows to load another version of Jetty in the same - * JVM for the rest of the project. - */ -private[sbtplugin] class PhantomJettyClassLoader(jettyLoader: ClassLoader, - parent: ClassLoader) extends ClassLoader(parent) { - - def this(loader: ClassLoader) = - this(loader, ClassLoader.getSystemClassLoader()) - - /** Classes needed to bridge private jetty classpath and public PhantomJS - * Basically everything defined in JettyWebsocketManager. - */ - private val bridgeClasses = Set( - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager", - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$WSLogger", - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$ComWebSocketListener", - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$1", - "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$2" - ) - - override protected def loadClass(name: String, resolve: Boolean): Class[_] = { - if (bridgeClasses.contains(name)) { - // Load bridgeClasses manually since they must be associated to this - // class loader, rather than the parent class loader in order to find the - // jetty classes - - // First check if we have loaded it already - Option(findLoadedClass(name)) getOrElse { - val wsManager = - parent.getResourceAsStream(name.replace('.', '/') + ".class") - - if (wsManager == null) { - throw new ClassNotFoundException(name) - } else { - val buf = IO.readInputStreamToByteArray(wsManager) - defineClass(name, buf, 0, buf.length) - } - } - } else { - try { - jettyLoader.loadClass(name) - } catch { - case _: ClassNotFoundException => - super.loadClass(name, resolve) - } - } - } -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala deleted file mode 100644 index 4faac64..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala +++ /dev/null @@ -1,10 +0,0 @@ -package scala.scalajs.sbtplugin.env.phantomjs - -private[phantomjs] trait WebsocketListener { - def onRunning(): Unit - def onOpen(): Unit - def onClose(): Unit - def onMessage(msg: String): Unit - - def log(msg: String): Unit -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala deleted file mode 100644 index a466841..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala +++ /dev/null @@ -1,10 +0,0 @@ -package scala.scalajs.sbtplugin.env.phantomjs - -private[phantomjs] trait WebsocketManager { - def start(): Unit - def stop(): Unit - def sendMessage(msg: String): Unit - def localPort: Int - def isConnected: Boolean - def isClosed: Boolean -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala deleted file mode 100644 index d4cdaee..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env.rhino - -import scala.collection.mutable - -import org.mozilla.javascript.Scriptable - -/** A proxy for a ScalaJS "scope" field that loads scripts lazily - * - * E.g., ScalaJS.c, which is a scope with the Scala.js classes, can be - * turned to a LazyScalaJSScope. Upon first access to a field of ScalaJS.c, - * say ScalaJS.c.scala_Option, the script defining that particular - * field will be loaded. - * This is possible because the relative path to the script can be derived - * from the name of the property being accessed. - * - * It is immensely useful, because it allows to load lazily only the scripts - * that are actually needed. - */ -class LazyScalaJSScope( - coreLib: ScalaJSCoreLib, - globalScope: Scriptable, - base: Scriptable, - isModule: Boolean = false, - isTraitImpl: Boolean = false) extends Scriptable { - - private val fields = mutable.HashMap.empty[String, Any] - private var prototype: Scriptable = _ - private var parentScope: Scriptable = _ - - { - // Pre-fill fields with the properties of `base` - for (id <- base.getIds()) { - (id.asInstanceOf[Any]: @unchecked) match { - case name: String => put(name, this, base.get(name, base)) - case index: Int => put(index, this, base.get(index, base)) - } - } - } - - private def load(name: String): Unit = - coreLib.load(globalScope, propNameToEncodedName(name)) - - private def propNameToEncodedName(name: String): String = { - if (isTraitImpl) name.split("__")(0) - else if (isModule) name + "$" - else name - } - - override def getClassName() = "LazyScalaJSScope" - - override def get(name: String, start: Scriptable) = { - fields.getOrElse(name, { - load(name) - fields.getOrElse(name, Scriptable.NOT_FOUND) - }).asInstanceOf[AnyRef] - } - override def get(index: Int, start: Scriptable) = - get(index.toString, start) - - override def has(name: String, start: Scriptable) = - fields.contains(name) - override def has(index: Int, start: Scriptable) = - has(index.toString, start) - - override def put(name: String, start: Scriptable, value: Any) = { - fields(name) = value - } - override def put(index: Int, start: Scriptable, value: Any) = - put(index.toString, start, value) - - override def delete(name: String) = () - override def delete(index: Int) = () - - override def getPrototype() = prototype - override def setPrototype(value: Scriptable) = prototype = value - - override def getParentScope() = parentScope - override def setParentScope(value: Scriptable) = parentScope = value - - override def getIds() = fields.keys.toArray - - override def getDefaultValue(hint: java.lang.Class[_]) = { - base.getDefaultValue(hint) - } - - override def hasInstance(instance: Scriptable) = false -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala deleted file mode 100644 index cd35ff6..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala +++ /dev/null @@ -1,303 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env.rhino - -import scala.scalajs.tools.sem.Semantics -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.env._ -import scala.scalajs.tools.logging._ - -import scala.io.Source - -import scala.collection.mutable - -import scala.concurrent.{Future, Promise, Await} -import scala.concurrent.duration.Duration - -import org.mozilla.javascript._ - -class RhinoJSEnv(semantics: Semantics, - withDOM: Boolean = false) extends ComJSEnv { - - import RhinoJSEnv._ - - /** Executes code in an environment where the Scala.js library is set up to - * load its classes lazily. - * - * Other .js scripts in the inputs are executed eagerly before the provided - * `code` is called. - */ - override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): JSRunner = { - new Runner(classpath, code, logger, console) - } - - private class Runner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole) extends JSRunner { - def run(): Unit = internalRunJS(classpath, code, logger, console, None) - } - - override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): AsyncJSRunner = { - new AsyncRunner(classpath, code, logger, console) - } - - private class AsyncRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole) extends AsyncJSRunner { - - private[this] val promise = Promise[Unit] - - private[this] val thread = new Thread { - override def run(): Unit = { - try { - internalRunJS(classpath, code, logger, console, optChannel) - promise.success(()) - } catch { - case t: Throwable => - promise.failure(t) - } - } - } - - def start(): Future[Unit] = { - thread.start() - promise.future - } - - def stop(): Unit = thread.interrupt() - - def isRunning(): Boolean = !promise.isCompleted - - def await(): Unit = Await.result(promise.future, Duration.Inf) - - protected def optChannel(): Option[Channel] = None - } - - override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole): ComJSRunner = { - new ComRunner(classpath, code, logger, console) - } - - private class ComRunner(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole) - extends AsyncRunner(classpath, code, logger, console) with ComJSRunner { - - private[this] val channel = new Channel - - override protected def optChannel(): Option[Channel] = Some(channel) - - def send(msg: String): Unit = { - try { - channel.sendToJS(msg) - } catch { - case _: ChannelClosedException => - throw new ComJSEnv.ComClosedException - } - } - - def receive(): String = { - try { - channel.recvJVM() - } catch { - case _: ChannelClosedException => - throw new ComJSEnv.ComClosedException - } - } - - def close(): Unit = channel.close() - - override def stop(): Unit = { - close() - super.stop() - } - - } - - private def internalRunJS(classpath: CompleteClasspath, code: VirtualJSFile, - logger: Logger, console: JSConsole, optChannel: Option[Channel]): Unit = { - - val context = Context.enter() - try { - val scope = context.initStandardObjects() - - if (withDOM) { - // Fetch env.rhino.js from webjar - val name = "env.rhino.js" - val path = "/META-INF/resources/webjars/envjs/1.2/" + name - val resource = getClass.getResource(path) - assert(resource != null, s"need $name as resource") - - // Rhino can't optimize envjs - context.setOptimizationLevel(-1) - - // Don't print envjs header - scope.addFunction("print", args => ()) - - // Pipe file to Rhino - val reader = Source.fromURL(resource).bufferedReader - context.evaluateReader(scope, reader, name, 1, null); - - // No need to actually define print here: It is captured by envjs to - // implement console.log, which we'll override in the next statement - } - - // Make sure Rhino does not do its magic for JVM top-level packages (#364) - val PackagesObject = - ScriptableObject.getProperty(scope, "Packages").asInstanceOf[Scriptable] - val topLevelPackageIds = ScriptableObject.getPropertyIds(PackagesObject) - for (id <- topLevelPackageIds) (id: Any) match { - case name: String => ScriptableObject.deleteProperty(scope, name) - case index: Int => ScriptableObject.deleteProperty(scope, index) - case _ => // should not happen, I think, but with Rhino you never know - } - - // Setup console.log - val jsconsole = context.newObject(scope) - jsconsole.addFunction("log", _.foreach(console.log _)) - ScriptableObject.putProperty(scope, "console", jsconsole) - - // Optionally setup scalaJSCom - var recvCallback: Option[String => Unit] = None - for (channel <- optChannel) { - val comObj = context.newObject(scope) - - comObj.addFunction("send", s => - channel.sendToJVM(Context.toString(s(0)))) - - comObj.addFunction("init", s => s(0) match { - case f: Function => - val cb: String => Unit = - msg => f.call(context, scope, scope, Array(msg)) - recvCallback = Some(cb) - case _ => - sys.error("First argument to init must be a function") - }) - - comObj.addFunction("close", _ => { - // Tell JVM side we won't send anything - channel.close() - // Internally register that we're done - recvCallback = None - }) - - ScriptableObject.putProperty(scope, "scalajsCom", comObj) - } - - try { - // Make the classpath available. Either through lazy loading or by - // simply inserting - classpath match { - case cp: IRClasspath => - // Setup lazy loading classpath and source mapper - val optLoader = if (cp.scalaJSIR.nonEmpty) { - val loader = new ScalaJSCoreLib(semantics, cp) - - // Setup sourceMapper - val scalaJSenv = context.newObject(scope) - - scalaJSenv.addFunction("sourceMapper", args => { - val trace = Context.toObject(args(0), scope) - loader.mapStackTrace(trace, context, scope) - }) - - ScriptableObject.putProperty(scope, "__ScalaJSEnv", scalaJSenv) - - Some(loader) - } else { - None - } - - // Load JS libraries - cp.jsLibs.foreach(dep => context.evaluateFile(scope, dep.lib)) - - optLoader.foreach(_.insertInto(context, scope)) - case cp => - cp.allCode.foreach(context.evaluateFile(scope, _)) - } - - context.evaluateFile(scope, code) - - // Callback the com channel if necessary (if comCallback = None, channel - // wasn't initialized on the client) - for ((channel, callback) <- optChannel zip recvCallback) { - try { - while (recvCallback.isDefined) - callback(channel.recvJS()) - } catch { - case _: ChannelClosedException => - // the JVM side closed the connection - } - } - - // Enusre the channel is closed to release JVM side - optChannel.foreach(_.close) - - } catch { - case e: RhinoException => - // Trace here, since we want to be in the context to trace. - logger.trace(e) - sys.error(s"Exception while running JS code: ${e.getMessage}") - } - } finally { - Context.exit() - } - } - -} - -object RhinoJSEnv { - - /** Communication channel between the Rhino thread and the rest of the JVM */ - private class Channel { - private[this] var _closed = false - private[this] val js2jvm = mutable.Queue.empty[String] - private[this] val jvm2js = mutable.Queue.empty[String] - - def sendToJS(msg: String): Unit = synchronized { - jvm2js.enqueue(msg) - notify() - } - - def sendToJVM(msg: String): Unit = synchronized { - js2jvm.enqueue(msg) - notify() - } - - def recvJVM(): String = synchronized { - while (js2jvm.isEmpty && ensureOpen()) - wait() - - js2jvm.dequeue() - } - - def recvJS(): String = synchronized { - while (jvm2js.isEmpty && ensureOpen()) - wait() - - jvm2js.dequeue() - } - - def close(): Unit = synchronized { - _closed = true - notify() - } - - /** Throws if the channel is closed and returns true */ - private def ensureOpen(): Boolean = { - if (_closed) - throw new ChannelClosedException - true - } - } - - private class ChannelClosedException extends Exception - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala deleted file mode 100644 index e937e5b..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala +++ /dev/null @@ -1,173 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env.rhino - -import scala.collection.mutable - -import org.mozilla.javascript.{Context, Scriptable} - -import scala.scalajs.ir - -import scala.scalajs.tools.sem.Semantics -import scala.scalajs.tools.javascript.{Printers, ScalaJSClassEmitter} -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.corelib._ - -class ScalaJSCoreLib(semantics: Semantics, classpath: IRClasspath) { - import ScalaJSCoreLib._ - - private val (providers, exportedSymbols) = { - val providers = mutable.Map.empty[String, VirtualScalaJSIRFile] - val exportedSymbols = mutable.ListBuffer.empty[String] - - for (irFile <- classpath.scalaJSIR) { - val info = irFile.roughInfo - providers += info.encodedName -> irFile - if (info.isExported) - exportedSymbols += info.encodedName - } - - (providers, exportedSymbols) - } - - def insertInto(context: Context, scope: Scriptable) = { - CoreJSLibs.libs(semantics).foreach(context.evaluateFile(scope, _)) - lazifyScalaJSFields(scope) - - // Make sure exported symbols are loaded - val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope) - val c = Context.toObject(ScalaJS.get("c", ScalaJS), scope) - for (encodedName <- exportedSymbols) - c.get(encodedName, c) - } - - /** Source maps the given stack trace (where possible) */ - def mapStackTrace(stackTrace: Scriptable, - context: Context, scope: Scriptable): Scriptable = { - val count = Context.toNumber(stackTrace.get("length", stackTrace)).toInt - - // Maps file -> max line (0-based) - val neededMaps = mutable.Map.empty[String, Int] - - // Collect required line counts - for (i <- 0 until count) { - val elem = Context.toObject(stackTrace.get(i, stackTrace), scope) - val fileName = Context.toString(elem.get("fileName", elem)) - - if (fileName.endsWith(PseudoFileSuffix) && - providers.contains(fileName.stripSuffix(PseudoFileSuffix))) { - - val curMaxLine = neededMaps.getOrElse(fileName, -1) - val reqLine = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1 - - if (reqLine > curMaxLine) - neededMaps.put(fileName, reqLine) - } - } - - // Map required files - val maps = - for ((fileName, maxLine) <- neededMaps) - yield (fileName, getSourceMapper(fileName, maxLine)) - - // Create new stack trace to return - val res = context.newArray(scope, count) - - for (i <- 0 until count) { - val elem = Context.toObject(stackTrace.get(i, stackTrace), scope) - val fileName = Context.toString(elem.get("fileName", elem)) - val line = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1 - - val pos = maps.get(fileName).fold(ir.Position.NoPosition)(_(line)) - - val newElem = - if (pos.isDefined) newPosElem(scope, context, elem, pos) - else elem - - res.put(i, res, newElem) - } - - res - } - - private def getSourceMapper(fileName: String, untilLine: Int) = { - val irFile = providers(fileName.stripSuffix(PseudoFileSuffix)) - val mapper = new Printers.ReverseSourceMapPrinter(untilLine) - val classDef = irFile.tree - val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef) - mapper.reverseSourceMap(desugared) - mapper - } - - private def newPosElem(scope: Scriptable, context: Context, - origElem: Scriptable, pos: ir.Position): Scriptable = { - assert(pos.isDefined) - - val elem = context.newObject(scope) - - elem.put("declaringClass", elem, origElem.get("declaringClass", origElem)) - elem.put("methodName", elem, origElem.get("methodName", origElem)) - elem.put("fileName", elem, pos.source.toString) - elem.put("lineNumber", elem, pos.line + 1) - elem.put("columnNumber", elem, pos.column + 1) - - elem - } - - private val scalaJSLazyFields = Seq( - Info("d"), - Info("c"), - Info("h"), - Info("i", isTraitImpl = true), - Info("n", isModule = true), - Info("m", isModule = true), - Info("is"), - Info("as"), - Info("isArrayOf"), - Info("asArrayOf")) - - private def lazifyScalaJSFields(scope: Scriptable) = { - val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope) - - def makeLazyScalaJSScope(base: Scriptable, isModule: Boolean, isTraitImpl: Boolean) = - new LazyScalaJSScope(this, scope, base, isModule, isTraitImpl) - - for (Info(name, isModule, isTraitImpl) <- scalaJSLazyFields) { - val base = ScalaJS.get(name, ScalaJS).asInstanceOf[Scriptable] - val lazified = makeLazyScalaJSScope(base, isModule, isTraitImpl) - ScalaJS.put(name, ScalaJS, lazified) - } - } - - private[rhino] def load(scope: Scriptable, encodedName: String): Unit = { - providers.get(encodedName) foreach { irFile => - val codeWriter = new java.io.StringWriter - val printer = new Printers.JSTreePrinter(codeWriter) - val classDef = irFile.tree - val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef) - printer.printTopLevelTree(desugared) - printer.complete() - val ctx = Context.getCurrentContext() - val fakeFileName = encodedName + PseudoFileSuffix - ctx.evaluateString(scope, codeWriter.toString(), - fakeFileName, 1, null) - } - } -} - -object ScalaJSCoreLib { - private case class Info(name: String, - isModule: Boolean = false, isTraitImpl: Boolean = false) - - private val EncodedNameLine = raw""""encodedName": *"([^"]+)"""".r.unanchored - - private final val PseudoFileSuffix = ".sjsir" -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala deleted file mode 100644 index 926fbb2..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.env - -import org.mozilla.javascript._ - -import scala.scalajs.tools.io._ - -package object rhino { - - implicit class ContextOps(val self: Context) extends AnyVal { - def evaluateFile(scope: Scriptable, file: VirtualJSFile, - securityDomain: AnyRef = null): Any = { - self.evaluateString(scope, file.content, file.path, 1, securityDomain) - } - } - - implicit class ScriptableObjectOps(val self: Scriptable) { - def addFunction(name: String, function: Array[AnyRef] => Any) = { - val rhinoFunction = - new BaseFunction { - ScriptRuntime.setFunctionProtoAndParent(this, self) - override def call(context: Context, scope: Scriptable, - thisObj: Scriptable, args: Array[AnyRef]): AnyRef = { - function(args) match { - case () => Undefined.instance - case r => r.asInstanceOf[AnyRef] - } - } - } - - ScriptableObject.putProperty(self, name, rhinoFunction) - } - } -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala deleted file mode 100644 index 32ffb94..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/impl/DependencyBuilders.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin -package impl - -import scala.language.implicitConversions -import scala.language.experimental.macros - -import sbt._ - -import StringUtilities.nonEmpty - -trait DependencyBuilders { - final implicit def toScalaJSGroupID(groupID: String): ScalaJSGroupID = { - nonEmpty(groupID, "Group ID") - new ScalaJSGroupID(groupID) - } - - /** Builder to allow for stuff like: - * - * ProvidedJS / "foo.js" - * ProvidedJS / "foo.js" % "test" - * - */ - object ProvidedJS { - def /(name: String): ProvidedJSModuleID = ProvidedJSModuleID(name, None) - } - - /** Builder to allow for stuff like: - * - * "org.webjars" % "jquery" % "1.10.2" / "jquery.js" - * "org.webjars" % "jquery" % "1.10.2" / "jquery.js" % "test" - * - */ - implicit class JSModuleIDBuilder(module: ModuleID) { - def /(name: String): JarJSModuleID = JarJSModuleID(module, name) - } -} - -final class ScalaJSGroupID private[sbtplugin] (private val groupID: String) { - def %%%(artifactID: String): CrossGroupArtifactID = - macro ScalaJSGroupID.auto_impl - - def %%%!(artifactID: String): CrossGroupArtifactID = - ScalaJSGroupID.withCross(this, artifactID, ScalaJSCrossVersion.binary) -} - -object ScalaJSGroupID { - import scala.reflect.macros.Context - - /** Internal. Used by the macro implementing [[ScalaJSGroupID.%%%]]. Use: - * {{{ - * ("a" % artifactID % revision).cross(cross) - * }}} - * instead. - */ - def withCross(groupID: ScalaJSGroupID, artifactID: String, - cross: CrossVersion): CrossGroupArtifactID = { - nonEmpty(artifactID, "Artifact ID") - new CrossGroupArtifactID(groupID.groupID, artifactID, cross) - } - - def auto_impl(c: Context { type PrefixType = ScalaJSGroupID })( - artifactID: c.Expr[String]): c.Expr[CrossGroupArtifactID] = { - import c.universe._ - - // Hack to work around bug in sbt macros (wrong way of collecting local - // definitions) - val keysSym = rootMirror.staticModule( - "_root_.scala.scalajs.sbtplugin.ScalaJSPlugin.autoImport") - val keys = c.Expr[ScalaJSPlugin.autoImport.type](Ident(keysSym)) - - reify { - val cross = { - if (keys.splice.jsDependencies.?.value.isDefined) - ScalaJSCrossVersion.binary - else - CrossVersion.binary - } - ScalaJSGroupID.withCross(c.prefix.splice, artifactID.splice, cross) - } - } - -} - -final class CrossGroupArtifactID(groupID: String, - artifactID: String, crossVersion: CrossVersion) { - def %(revision: String): ModuleID = { - nonEmpty(revision, "Revision") - ModuleID(groupID, artifactID, revision).cross(crossVersion) - } -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala deleted file mode 100644 index f13c195..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/Events.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import sbt.testing.{Event => SbtEvent, _} - -class Events(taskDef: TaskDef) { - - abstract class Event(val status: Status, - val throwable: OptionalThrowable = new OptionalThrowable) extends SbtEvent { - val fullyQualifiedName = taskDef.fullyQualifiedName - val fingerprint = taskDef.fingerprint - val selector = taskDef.selectors.headOption.getOrElse(new SuiteSelector) - val duration = -1L - } - - case class Error(exception: Throwable) extends Event( - Status.Error, new OptionalThrowable(exception)) - - case class Failure(exception: Throwable) extends Event( - Status.Failure, new OptionalThrowable(exception)) - - case object Succeeded extends Event(Status.Success) - case object Skipped extends Event(Status.Skipped) - case object Pending extends Event(Status.Pending) - case object Ignored extends Event(Status.Ignored) - case object Canceled extends Event(Status.Canceled) -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala deleted file mode 100644 index bfe0ffc..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/JSClasspathLoader.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import scala.scalajs.tools.classpath.CompleteClasspath - -/** A dummy ClassLoader to pass on Scala.js ClasspathContents to tests */ -final case class JSClasspathLoader(cp: CompleteClasspath) extends ClassLoader diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala deleted file mode 100644 index dfebe00..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/SbtTestLoggerAccWrapper.scala +++ /dev/null @@ -1,22 +0,0 @@ -package scala.scalajs.sbtplugin.testing - -import scala.scalajs.tools.logging._ -import sbt.testing.{ Logger => SbtTestLogger } - -class SbtTestLoggerAccWrapper(logger: Seq[SbtTestLogger]) extends Logger { - - import scala.scalajs.sbtplugin.Implicits._ - import Level._ - - def log(level: Level, message: => String): Unit = level match { - case Error => logger.foreach(_.error(message)) - case Warn => logger.foreach(_.warn(message)) - case Info => logger.foreach(_.info(message)) - case Debug => logger.foreach(_.debug(message)) - } - - def success(message: => String): Unit = logger.foreach(_.info(message)) - - def trace(t: => Throwable): Unit = logger.foreach(_.trace(t)) - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala deleted file mode 100644 index b4cb09b..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestException.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.scalajs.sbtplugin.testing - -/** Dummy Exception to wrap stack traces passed to SBT */ -class TestException( - message: String, - stackTrace: Array[StackTraceElement] -) extends Exception(message) { - override def getStackTrace = stackTrace -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala deleted file mode 100644 index ab43bfe..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestFramework.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import scala.scalajs.tools.env._ -import scala.scalajs.tools.classpath._ - -import sbt._ -import sbt.testing._ -import sbt.classpath.ClasspathFilter - -import java.net.URLClassLoader - -class TestFramework( - environment: JSEnv, - jsConsole: JSConsole, - testFramework: String) extends Framework { - - val name = "Scala.js Test Framework" - - lazy val fingerprints = Array[Fingerprint](f1) - - private val f1 = new SubclassFingerprint { - val isModule = true - val superclassName = "scala.scalajs.testbridge.Test" - val requireNoArgConstructor = true - } - - def runner(args: Array[String], remoteArgs: Array[String], - testClassLoader: ClassLoader): Runner = { - - val jsClasspath = extractClasspath(testClassLoader) - new TestRunner(environment, jsClasspath, jsConsole, - testFramework, args, remoteArgs) - } - - /** extract classpath from ClassLoader (which must be a JSClasspathLoader) */ - private def extractClasspath(cl: ClassLoader) = cl match { - case cl: JSClasspathLoader => cl.cp - case _ => - sys.error("The Scala.js framework only works with a class loader of " + - s"type JSClasspathLoader (${cl.getClass} given)") - } - -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala deleted file mode 100644 index 9aad956..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestOutputConsole.scala +++ /dev/null @@ -1,190 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import sbt.testing.Logger -import sbt.testing.EventHandler - -import scala.scalajs.tools.env.JSConsole -import scala.scalajs.tools.sourcemap.SourceMapper -import scala.scalajs.tools.classpath.{CompleteClasspath, IRClasspath} - -import scala.collection.mutable.ArrayBuffer - -import scala.util.Try - -import java.util.regex._ - -/** This parses the messages sent from the test bridge and forwards - * the calls to SBT. It also buffers all log messages and allows to - * pipe them to multiple loggers in a synchronized fashion. This - * ensures that log messages aren't interleaved due to parallelism. - */ -class TestOutputConsole( - base: JSConsole, - handler: EventHandler, - events: Events, - classpath: CompleteClasspath, - noSourceMap: Boolean) extends JSConsole { - - import TestOutputConsole._ - import events._ - - private val traceBuf = new ArrayBuffer[StackTraceElement] - private val logBuffer = new ArrayBuffer[LogElement] - - /* See #727: source mapping does not work with CompleteIRClasspath, so - * don't bother to try. - */ - private val ignoreSourceMapping = - noSourceMap || classpath.isInstanceOf[IRClasspath] - - private lazy val sourceMapper = new SourceMapper(classpath) - - override def log(msg: Any): Unit = { - val data = msg.toString - val sepPos = data.indexOf("|") - - if (sepPos == -1) - log(_.error, s"Malformed message: $data") - else { - val op = data.substring(0, sepPos) - val message = unescape(data.substring(sepPos + 1)) - - op match { - case "console-log" => - base.log(message) - case "error" => - val trace = getTrace() - logWithEvent(_.error, - messageWithStack(message, trace), - Error(new TestException(message, trace)) - ) - case "failure" => - val trace = getTrace() - logWithEvent(_.error, - messageWithStack(message, trace), - Failure(new TestException(message, trace)) - ) - case "succeeded" => - noTrace() - logWithEvent(_.info, message, Succeeded) - case "skipped" => - noTrace() - logWithEvent(_.info, message, Skipped) - case "pending" => - noTrace() - logWithEvent(_.info, message, Pending) - case "ignored" => - noTrace() - logWithEvent(_.info, message, Ignored) - case "canceled" => - noTrace() - logWithEvent(_.info, message, Canceled) - case "error-log" => - noTrace() - log(_.error, message) - case "info" => - noTrace() - log(_.info, message) - case "warn" => - noTrace() - log(_.warn, message) - case "trace" => - val Array(className, methodName, fileName, - lineNumberStr, columnNumberStr) = message.split('|') - - def tryParse(num: String, name: String) = Try(num.toInt).getOrElse { - log(_.warn, s"Couldn't parse $name number in StackTrace: $num") - -1 - } - - val lineNumber = tryParse(lineNumberStr, "line") - val columnNumber = tryParse(columnNumberStr, "column") - - val ste = - new StackTraceElement(className, methodName, fileName, lineNumber) - - if (ignoreSourceMapping) - traceBuf += ste - else - traceBuf += sourceMapper.map(ste, columnNumber) - case _ => - noTrace() - log(_.error, s"Unknown op: $op. Originating log message: $data") - } - } - } - - private def noTrace() = { - if (traceBuf.nonEmpty) - log(_.warn, s"Discarding ${traceBuf.size} stack elements") - traceBuf.clear() - } - - private def getTrace() = { - val res = traceBuf.toArray - traceBuf.clear() - res - } - - private def messageWithStack(message: String, stack: Array[StackTraceElement]): String = - message + stack.mkString("\n", "\n", "") - - private def log(method: LogMethod, message: String): Unit = - logBuffer.append(LogElement(method, message)) - - private def logWithEvent(method: LogMethod, - message: String, event: Event): Unit = { - handler handle event - log(method, message) - } - - def pipeLogsTo(loggers: Array[Logger]): Unit = { - TestOutputConsole.synchronized { - for { - LogElement(method, message) <- logBuffer - logger <- loggers - } method(logger) { - if (logger.ansiCodesSupported) message - else removeColors(message) - } - } - } - - def allLogs: List[LogElement] = logBuffer.toList - - private val colorPattern = raw"\033\[\d{1,2}m" - - private def removeColors(message: String): String = - message.replaceAll(colorPattern, "") - - private val unEscPat = Pattern.compile("(\\\\\\\\|\\\\n|\\\\r)") - private def unescape(message: String): String = { - val m = unEscPat.matcher(message) - val res = new StringBuffer() - while (m.find()) { - val repl = m.group() match { - case "\\\\" => "\\\\" - case "\\n" => "\n" - case "\\r" => "\r" - } - m.appendReplacement(res, repl); - } - m.appendTail(res); - res.toString - } - -} - -object TestOutputConsole { - type LogMethod = Logger => (String => Unit) - case class LogElement(method: LogMethod, message: String) -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala deleted file mode 100644 index e5ca2a2..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestRunner.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import sbt.testing._ - -import scala.scalajs.tools.env._ -import scala.scalajs.tools.classpath._ - -class TestRunner( - environment: JSEnv, - classpath: CompleteClasspath, - jsConsole: JSConsole, - testFramework: String, - val args: Array[String], - val remoteArgs: Array[String]) extends Runner { - - def tasks(taskDefs: Array[TaskDef]): Array[Task] = if (_done) { - throw new IllegalStateException("Done has already been called") - } else { - taskDefs.map(TestTask(environment, classpath, jsConsole, testFramework, args)) - } - - def done(): String = { - _done = true - "" - } - - private var _done = false -} diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala deleted file mode 100644 index b1cabb9..0000000 --- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/testing/TestTask.scala +++ /dev/null @@ -1,110 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ __ ____ Scala.js sbt plugin ** -** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** -** /____/\___/_/ |_/____/_/ | |__/ /____/ ** -** |/____/ ** -\* */ - - -package scala.scalajs.sbtplugin.testing - -import sbt.testing._ - -import scala.scalajs.tools.io._ -import scala.scalajs.tools.classpath._ -import scala.scalajs.tools.env._ - -import scala.scalajs.sbtplugin.JSUtils._ - -import scala.annotation.tailrec -import scala.util.control.NonFatal - -class TestTask( - env: JSEnv, - classpath: CompleteClasspath, - jsConsole: JSConsole, - testFramework: String, - args: Array[String], - val taskDef: TaskDef) extends Task { - - import TestTask._ - - val tags = Array.empty[String] - val options = readArgs(args.toList) - - def execute(eventHandler: EventHandler, - loggers: Array[Logger]): Array[Task] = { - - val runnerFile = testRunnerFile(options.frameworkArgs) - val testConsole = new TestOutputConsole(jsConsole, eventHandler, - new Events(taskDef), classpath, options.noSourceMap) - val logger = new SbtTestLoggerAccWrapper(loggers) - - // Actually execute test - env.jsRunner(classpath, runnerFile, logger, testConsole).run() - - testConsole.pipeLogsTo(loggers) - - Array.empty - } - - private def testRunnerFile(args: List[String]) = { - val testKey = taskDef.fullyQualifiedName - - // Note that taskDef does also have the selector, fingerprint and - // explicitlySpecified value we could pass to the framework. However, we - // believe that these are only moderately useful. Therefore, we'll silently - // ignore them. - - val jsArgArray = listToJS(args) - new MemVirtualJSFile("Generated test launcher file"). - withContent(s"""this${dot2bracket(testFramework)}().safeRunTest( - | scala.scalajs.testbridge.internal.ConsoleTestOutput(), - | $jsArgArray, - | this${dot2bracket(testKey)});""".stripMargin) - } - - -} - -object TestTask { - - def apply(environment: JSEnv, classpath: CompleteClasspath, - jsConsole: JSConsole, testFramework: String, args: Array[String] - )(taskDef: TaskDef) = - new TestTask(environment, classpath, jsConsole, - testFramework, args, taskDef) - - case class ArgOptions( - noSourceMap: Boolean, - frameworkArgs: List[String] - ) - - private def readArgs(args0: List[String]) = { - // State for each option - var noSourceMap = false - - def mkOptions(frameworkArgs: List[String]) = - ArgOptions(noSourceMap, frameworkArgs) - - @tailrec - def read0(args: List[String]): ArgOptions = args match { - case "-no-source-map" :: xs => - noSourceMap = true - read0(xs) - - // Explicitly end our argument list - case "--" :: xs => - mkOptions(xs) - - // Unknown argument - case xs => - mkOptions(xs) - - } - - read0(args0) - } - -} |