diff options
Diffstat (limited to 'project')
-rw-r--r-- | project/ExternalCompile.scala | 116 | ||||
-rw-r--r-- | project/JavaLangObject.scala | 243 | ||||
-rw-r--r-- | project/JavaLangString.scala | 215 | ||||
-rw-r--r-- | project/ScalaJSBuild.scala | 891 | ||||
-rw-r--r-- | project/build.properties | 1 | ||||
-rw-r--r-- | project/build.sbt | 48 | ||||
-rw-r--r-- | project/project/ScalaJSEnvGenerator.scala | 31 |
7 files changed, 1545 insertions, 0 deletions
diff --git a/project/ExternalCompile.scala b/project/ExternalCompile.scala new file mode 100644 index 0000000..7d37c81 --- /dev/null +++ b/project/ExternalCompile.scala @@ -0,0 +1,116 @@ +import sbt._ +import Keys._ + +import scala.scalajs.sbtplugin.ScalaJSPlugin +import ScalaJSPlugin.autoImport.jsDependencyManifest + +object ExternalCompile { + + private val isWindows = + System.getProperty("os.name").toLowerCase().indexOf("win") >= 0 + + val scalaJSExternalCompileConfigSettings: Seq[Setting[_]] = inTask(compile)( + Defaults.runnerTask + ) ++ Seq( + fork in compile := true, + trapExit in compile := true, + javaOptions in compile += "-Xmx512M", + + compile := { + val inputs = (compileInputs in compile).value + import inputs.config._ + + val s = streams.value + val logger = s.log + val cacheDir = s.cacheDirectory + + // Discover classpaths + + def cpToString(cp: Seq[File]) = + cp.map(_.getAbsolutePath).mkString(java.io.File.pathSeparator) + + val compilerCp = inputs.compilers.scalac.scalaInstance.allJars + val cpStr = cpToString(classpath) + + // List all my dependencies (recompile if any of these changes) + + val allMyDependencies = classpath filterNot (_ == classesDirectory) flatMap { cpFile => + if (cpFile.isDirectory) (cpFile ** "*.class").get + else Seq(cpFile) + } + + // Compile + + val cachedCompile = FileFunction.cached(cacheDir / "compile", + FilesInfo.lastModified, FilesInfo.exists) { dependencies => + + logger.info( + "Compiling %d Scala sources to %s..." format ( + sources.size, classesDirectory)) + + if (classesDirectory.exists) + IO.delete(classesDirectory) + IO.createDirectory(classesDirectory) + + val sourcesArgs = sources.map(_.getAbsolutePath()).toList + + /* run.run() below in doCompileJS() will emit a call to its + * logger.info("Running scala.tools.nsc.scalajs.Main [...]") + * which we do not want to see. We use this patched logger to + * filter out that particular message. + */ + val patchedLogger = new Logger { + def log(level: Level.Value, message: => String) = { + val msg = message + if (level != Level.Info || + !msg.startsWith("Running scala.tools.nsc.Main")) + logger.log(level, msg) + } + def success(message: => String) = logger.success(message) + def trace(t: => Throwable) = logger.trace(t) + } + + def doCompile(sourcesArgs: List[String]): Unit = { + val run = (runner in compile).value + run.run("scala.tools.nsc.Main", compilerCp, + "-cp" :: cpStr :: + "-d" :: classesDirectory.getAbsolutePath() :: + options ++: + sourcesArgs, + patchedLogger) foreach sys.error + } + + /* Crude way of overcoming the Windows limitation on command line + * length. + */ + if ((fork in compile).value && isWindows && + (sourcesArgs.map(_.length).sum > 1536)) { + IO.withTemporaryFile("sourcesargs", ".txt") { sourceListFile => + IO.writeLines(sourceListFile, sourcesArgs) + doCompile(List("@"+sourceListFile.getAbsolutePath())) + } + } else { + doCompile(sourcesArgs) + } + + // Output is all files in classesDirectory + (classesDirectory ** AllPassFilter).get.toSet + } + + cachedCompile((sources ++ allMyDependencies).toSet) + + // We do not have dependency analysis when compiling externally + sbt.inc.Analysis.Empty + }, + + // Make sure jsDependencyManifest runs after compile, otherwise compile + // might remove the entire directory afterwards. + jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile) + ) + + val scalaJSExternalCompileSettings = ( + inConfig(Compile)(scalaJSExternalCompileConfigSettings) ++ + inConfig(Test)(scalaJSExternalCompileConfigSettings) + ) + +} diff --git a/project/JavaLangObject.scala b/project/JavaLangObject.scala new file mode 100644 index 0000000..ff82b94 --- /dev/null +++ b/project/JavaLangObject.scala @@ -0,0 +1,243 @@ +/* + * Hard-coded IR for java.lang.Object. + */ + +import scala.scalajs.ir +import ir._ +import ir.Definitions._ +import ir.Infos._ +import ir.Trees._ +import ir.Types._ +import ir.Position.NoPosition + +/** Hard-coded IR for java.lang.Object. + * We cannot so much as begin to fake a compilation of java.lang.Object, + * because Object is hijacked so much by scalac itself that it does not like + * at all to try to compile that class. So we have to bypass entirely the + * compiler to define java.lang.Object. + */ +object JavaLangObject { + + /** Optimizer hints with `@inline` + * Unfortunately we do not have access to private details of + * [[OptimizerHints]], so we cannot do this cleanly. But it is fine + * somehow because we're part of the same project implementation. + */ + private def inlineOptimizerHints = new OptimizerHints(2) + + val InfoAndTree = (Info, Definition) + + private def Info = ClassInfo( + name = "java.lang.Object", + encodedName = "O", + ancestorCount = 0, + kind = ClassKind.Class, + superClass = "", + ancestors = List("O"), + methods = List( + MethodInfo("__init__"), + MethodInfo("init___"), + MethodInfo("hashCode__I", + calledMethods = Map( + "jl_System$" -> List("identityHashCode__O__I") + ), + accessedModules = List("jl_System") + ), + MethodInfo("equals__O__Z", + optimizerHints = inlineOptimizerHints + ), + MethodInfo("clone__O", + calledMethods = Map( + "sjsr_package$" -> List("cloneObject__sjs_js_Object__sjs_js_Object"), + "jl_CloneNotSupportedException" -> List("init___") + ), + instantiatedClasses = List("jl_CloneNotSupportedException"), + accessedModules = List("sjsr_package"), + accessedClassData = List("jl_Cloneable"), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("notify__V"), + MethodInfo("notifyAll__V"), + MethodInfo("toString__T", + calledMethods = Map( + "O" -> List("hashCode__I"), + "jl_Class" -> List("getName__T"), + "jl_Integer$" -> List("toHexString__I__T") + ), + accessedModules = List("jl_Integer") + ), + MethodInfo("finalize__V"), + MethodInfo("clone__", + calledMethods = Map( + "O" -> List("clone__O") + ) + ), + MethodInfo("notify__", + calledMethods = Map( + "O" -> List("notify__V") + ) + ), + MethodInfo("notifyAll__", + calledMethods = Map( + "O" -> List("notifyAll__V") + ) + ), + MethodInfo("finalize__", + calledMethods = Map( + "O" -> List("finalize__V") + ) + ) + ) + ) + + private def Definition = { + implicit val DummyPos = NoPosition + + // ClassType(Object) is normally invalid, but not in this class def + val ThisType = ClassType(ObjectClass) + + val classDef = ClassDef( + Ident("O", Some("java.lang.Object")), + ClassKind.Class, + None, + Nil, + List( + /* def this() = () */ + MethodDef( + Ident("init___", Some("<init>")), + Nil, + AnyType, + This()(ThisType))(None), + + /* def hashCode(): Int = System.identityHashCode(this) */ + MethodDef( + Ident("hashCode__I", Some("hashCode__I")), + Nil, + IntType, + { + Apply( + LoadModule(ClassType("jl_System$")), + Ident("identityHashCode__O__I", Some("identityHashCode")), + List(This()(ThisType)))(IntType) + })(None), + + /* def equals(that: Object): Boolean = this eq that */ + MethodDef( + Ident("equals__O__Z", Some("equals__O__Z")), + List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)), + BooleanType, + { + BinaryOp(BinaryOp.===, + This()(ThisType), + VarRef(Ident("that", Some("that")), mutable = false)(AnyType)) + })(None), + + /* protected def clone(): Object = + * if (this.isInstanceOf[Cloneable]) <clone>(this) + * else throw new CloneNotSupportedException() + */ + MethodDef( + Ident("clone__O", Some("clone__O")), + Nil, + AnyType, + { + If(IsInstanceOf(This()(ThisType), ClassType("jl_Cloneable")), { + Apply(LoadModule(ClassType("sjsr_package$")), + Ident("cloneObject__sjs_js_Object__sjs_js_Object", Some("cloneObject")), + List(This()(ThisType)))(AnyType) + }, { + Throw(New(ClassType("jl_CloneNotSupportedException"), + Ident("init___", Some("<init>")), Nil)) + })(AnyType) + })(None), + + /* def toString(): String = + * getClass().getName() + "@" + Integer.toHexString(hashCode()) + */ + MethodDef( + Ident("toString__T", Some("toString__T")), + Nil, + ClassType(StringClass), + { + BinaryOp(BinaryOp.String_+, BinaryOp(BinaryOp.String_+, + Apply( + GetClass(This()(ThisType)), + Ident("getName__T"), Nil)(ClassType(StringClass)), + // + + StringLiteral("@")), + // + + Apply( + LoadModule(ClassType("jl_Integer$")), + Ident("toHexString__I__T"), + List(Apply(This()(ThisType), Ident("hashCode__I"), Nil)(IntType)))( + ClassType(StringClass))) + })(None), + + /* Since wait() is not supported in any way, a correct implementation + * of notify() and notifyAll() is to do nothing. + */ + + /* def notify(): Unit = () */ + MethodDef( + Ident("notify__V", Some("notify__V")), + Nil, + NoType, + Skip())(None), + + /* def notifyAll(): Unit = () */ + MethodDef( + Ident("notifyAll__V", Some("notifyAll__V")), + Nil, + NoType, + Skip())(None), + + /* def finalize(): Unit = () */ + MethodDef( + Ident("finalize__V", Some("finalize__V")), + Nil, + NoType, + Skip())(None), + + /* Reflective proxies + * Note that we do not need to proxy the following methods, since + * they are defined on Any in the Scala hierarchy and therefore a + * reflective call is never emitted: + * - equals + * - getClass + * - hashCode + * - toString + */ + + MethodDef(Ident("clone__"), Nil, AnyType, + Apply(This()(ThisType), Ident("clone__O"), Nil)(AnyType))(None), + + MethodDef(Ident("notify__"), Nil, AnyType, Block( + Apply(This()(ThisType), Ident("notify__V"), Nil)(NoType), + Undefined()))(None), + + MethodDef(Ident("notifyAll__"), Nil, AnyType, Block( + Apply(This()(ThisType), Ident("notifyAll__V"), Nil)(NoType), + Undefined()))(None), + + MethodDef(Ident("finalize__"), Nil, AnyType, Block( + Apply(This()(ThisType), Ident("finalize__V"), Nil)(NoType), + Undefined()))(None), + + // Exports + + /* JSExport for toString(). */ + MethodDef( + StringLiteral("toString"), + Nil, + AnyType, + { + Apply(This()(ThisType), + Ident("toString__T", Some("toString__T")), + Nil)(ClassType(StringClass)) + })(None) + )) + + Hashers.hashClassDef(classDef) + } + +} diff --git a/project/JavaLangString.scala b/project/JavaLangString.scala new file mode 100644 index 0000000..fc68b29 --- /dev/null +++ b/project/JavaLangString.scala @@ -0,0 +1,215 @@ +/* + * Hard-coded IR for java.lang.String. + */ + +import scala.scalajs.ir +import ir._ +import ir.Definitions._ +import ir.Infos._ +import ir.Trees._ +import ir.Types._ +import ir.Position.NoPosition + +/** Hard-coded IR for java.lang.String. + * Unlike for the other hijacked classes, scalac does not like at all to + * compile even a mocked version of java.lang.String. So we have to bypass + * entirely the compiler to define java.lang.String. + */ +object JavaLangString { + + /** Optimizer hints with `@inline` + * Unfortunately we do not have access to private details of + * [[OptimizerHints]], so we cannot do this cleanly. But it is fine + * somehow because we're part of the same project implementation. + */ + private def inlineOptimizerHints = new OptimizerHints(2) + + val InfoAndTree = (Info, Definition) + + private def Info = ClassInfo( + name = "java.lang.String", + encodedName = "T", + ancestorCount = 1, + kind = ClassKind.HijackedClass, + superClass = "O", + ancestors = List( + "T", "Ljava_io_Serializable", "jl_CharSequence", "jl_Comparable", "O"), + methods = List( + MethodInfo("equals__O__Z", + optimizerHints = inlineOptimizerHints + ), + MethodInfo("hashCode__I", + calledMethods = Map( + "sjsr_RuntimeString$" -> List("hashCode__T__I") + ), + accessedModules = List("sjsr_RuntimeString"), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("compareTo__T__I", + calledMethods = Map( + "sjsr_RuntimeString$" -> List("compareTo__T__T__I") + ), + accessedModules = List("sjsr_RuntimeString"), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("compareTo__O__I", + calledMethods = Map( + "T" -> List("compareTo__T__I") + ), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("toString__T", + optimizerHints = inlineOptimizerHints + ), + MethodInfo("charAt__I__C", + calledMethods = Map( + "sjsr_RuntimeString$" -> List("charAt__T__I__C") + ), + accessedModules = List("sjsr_RuntimeString"), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("length__I", + calledMethods = Map( + "sjsr_RuntimeString$" -> List("length__T__I") + ), + accessedModules = List("sjsr_RuntimeString"), + optimizerHints = inlineOptimizerHints + ), + MethodInfo("subSequence__I__I__jl_CharSequence", + calledMethods = Map( + "sjsr_RuntimeString$" -> List("subSequence__T__I__I__jl_CharSequence") + ), + accessedModules = List("sjsr_RuntimeString"), + optimizerHints = inlineOptimizerHints + ) + ) + ) + + private def Definition = { + implicit val DummyPos = NoPosition + + val ThisType = ClassType(StringClass) + + ClassDef( + Ident("T", Some("java.lang.String")), + ClassKind.HijackedClass, + Some(Ident("O", Some("java.lang.Object"))), + List( + Ident("Ljava_io_Serializable", Some("java.io.Serializable")), + Ident("jl_CharSequence", Some("java.lang.CharSequence")), + Ident("jl_Comparable", Some("java.lang.Comparable")), + Ident("O", Some("java.lang.Object")) + ), + List( + /* def equals(that: Object): Boolean = this eq that */ + MethodDef( + Ident("equals__O__Z", Some("equals__O__Z")), + List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)), + BooleanType, + { + BinaryOp(BinaryOp.===, + This()(ThisType), + VarRef(Ident("that", Some("that")), mutable = false)(AnyType)) + })(None), + + /* def hashCode(): Int = RuntimeString.hashCode(this) */ + MethodDef( + Ident("hashCode__I", Some("hashCode__I")), + Nil, + IntType, + { + Apply( + LoadModule(ClassType("sjsr_RuntimeString$")), + Ident("hashCode__T__I", Some("hashCode__T__I")), + List(This()(ThisType)))(IntType) + })(None), + + /* def compareTo(that: String): Int = RuntimeString.compareTo(this, that) */ + MethodDef( + Ident("compareTo__T__I", Some("compareTo__T__I")), + List(ParamDef(Ident("that", Some("that")), ThisType, mutable = false)), + IntType, + { + Apply( + LoadModule(ClassType("sjsr_RuntimeString$")), + Ident("compareTo__T__T__I", Some("compareTo__T__T__I")), + List( + This()(ThisType), + VarRef(Ident("that", Some("that")), mutable = false)(ThisType)))(IntType) + })(None), + + /* def compareTo(that: Object): Int = compareTo(that.asInstanceOf[String]) */ + MethodDef( + Ident("compareTo__O__I", Some("compareTo__O__I")), + List(ParamDef(Ident("that", Some("that")), AnyType, mutable = false)), + IntType, + { + Apply( + This()(ThisType), + Ident("compareTo__T__I", Some("compareTo__T__I")), + List(AsInstanceOf( + VarRef(Ident("that", Some("that")), mutable = false)(AnyType), + ThisType)))(IntType) + })(None), + + /* def toString(): String = this */ + MethodDef( + Ident("toString__T", Some("toString__T")), + Nil, + ClassType(StringClass), + { + This()(ThisType) + })(None), + + /* def charAt(i: Int): Char = RuntimeString.charAt(this, i) */ + MethodDef( + Ident("charAt__I__C", Some("charAt__I__C")), + List(ParamDef(Ident("i", Some("i")), IntType, mutable = false)), + IntType, + { + Apply( + LoadModule(ClassType("sjsr_RuntimeString$")), + Ident("charAt__T__I__C", Some("charAt__T__I__C")), + List( + This()(ThisType), + VarRef(Ident("i", Some("i")), mutable = false)(IntType)))(IntType) + })(None), + + /* def length(): Int = RuntimeString.length(this) */ + MethodDef( + Ident("length__I", Some("length__I")), + Nil, + IntType, + { + Apply( + LoadModule(ClassType("sjsr_RuntimeString$")), + Ident("length__T__I", Some("length__T__I")), + List(This()(ThisType)))(IntType) + })(None), + + /* def subSequence(begin: Int, end: Int): CharSequence = + * RuntimeString.subSequence(this, begin, end) + */ + MethodDef( + Ident("subSequence__I__I__jl_CharSequence", + Some("subSequence__I__I__jl_CharSequence")), + List( + ParamDef(Ident("begin", Some("begin")), IntType, mutable = false), + ParamDef(Ident("end", Some("end")), IntType, mutable = false) + ), + ClassType("jl_CharSequence"), + { + Apply( + LoadModule(ClassType("sjsr_RuntimeString$")), + Ident("subSequence__T__I__I__jl_CharSequence", + Some("subSequence__T__I__I__jl_CharSequence")), + List( + This()(ThisType), + VarRef(Ident("begin", Some("begin")), mutable = false)(IntType), + VarRef(Ident("end", Some("end")), mutable = false)(IntType)))( + ClassType("jl_CharSequence")) + })(None) + )) + } + +} diff --git a/project/ScalaJSBuild.scala b/project/ScalaJSBuild.scala new file mode 100644 index 0000000..5cbc405 --- /dev/null +++ b/project/ScalaJSBuild.scala @@ -0,0 +1,891 @@ +import sbt._ +import Keys._ + +import bintray.Plugin.bintrayPublishSettings +import bintray.Keys.{repository, bintrayOrganization, bintray} + +import java.io.{ + BufferedOutputStream, + FileOutputStream, + BufferedWriter, + FileWriter +} + +import scala.collection.mutable +import scala.util.Properties + +import scala.scalajs.ir +import scala.scalajs.sbtplugin._ +import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv +import ScalaJSPlugin.autoImport._ +import ExternalCompile.scalaJSExternalCompileSettings +import Implicits._ + +import scala.scalajs.tools.sourcemap._ +import scala.scalajs.tools.io.MemVirtualJSFile + +import sbtassembly.Plugin.{AssemblyKeys, assemblySettings} +import AssemblyKeys.{assembly, assemblyOption} + +object ScalaJSBuild extends Build { + + val fetchedScalaSourceDir = settingKey[File]( + "Directory the scala source is fetched to") + val fetchScalaSource = taskKey[File]( + "Fetches the scala source for the current scala version") + val shouldPartest = settingKey[Boolean]( + "Whether we should partest the current scala version (and fail if we can't)") + + val commonSettings = Seq( + organization := "org.scala-lang.modules.scalajs", + version := scalaJSVersion, + + normalizedName ~= { + _.replace("scala.js", "scalajs").replace("scala-js", "scalajs") + }, + + homepage := Some(url("http://scala-js.org/")), + licenses += ("BSD New", + url("https://github.com/scala-js/scala-js/blob/master/LICENSE")), + + shouldPartest := { + val testListDir = ( + (resourceDirectory in (partestSuite, Test)).value / "scala" + / "tools" / "partest" / "scalajs" / scalaVersion.value + ) + testListDir.exists + }, + + scalacOptions ++= Seq( + "-deprecation", + "-unchecked", + "-feature", + "-encoding", "utf8" + ) + ) + + private val snapshotsOrReleases = + if (scalaJSIsSnapshotVersion) "snapshots" else "releases" + + private def publishToScalaJSRepoSettings = Seq( + publishTo := { + Seq("PUBLISH_USER", "PUBLISH_PASS").map(Properties.envOrNone) match { + case Seq(Some(user), Some(pass)) => + Some(Resolver.sftp( + s"scala-js-$snapshotsOrReleases", + "repo.scala-js.org", + s"/home/scalajsrepo/www/repo/$snapshotsOrReleases")( + Resolver.ivyStylePatterns) as (user, pass)) + case _ => + None + } + } + ) + + private def publishToBintraySettings = ( + bintrayPublishSettings + ) ++ Seq( + repository in bintray := "scala-js-releases", + bintrayOrganization in bintray := Some("scala-js") + ) + + val publishSettings = ( + if (Properties.envOrNone("PUBLISH_TO_BINTRAY") == Some("true")) + publishToBintraySettings + else + publishToScalaJSRepoSettings + ) ++ Seq( + publishMavenStyle := false + ) + + val defaultSettings = commonSettings ++ Seq( + scalaVersion := "2.11.2" + ) + + val myScalaJSSettings = ScalaJSPluginInternal.scalaJSAbstractSettings ++ Seq( + autoCompilerPlugins := true, + scalaJSOptimizerOptions ~= (_.withCheckScalaJSIR(true)) + ) + + val scalaJSSourceMapSettings = scalacOptions ++= { + if (scalaJSIsSnapshotVersion) Seq() + else Seq( + // Link source maps to github sources + "-P:scalajs:mapSourceURI:" + root.base.toURI + + "->https://raw.githubusercontent.com/scala-js/scala-js/v" + + scalaJSVersion + "/" + ) + } + + /** Depend library as if (exportJars in library) was set to false */ + val compileWithLibrarySetting = { + internalDependencyClasspath in Compile ++= { + val prods = (products in (library, Compile)).value + val analysis = (compile in (library, Compile)).value + + prods.map(p => Classpaths.analyzed(p, analysis)) + } + } + + // Used when compiling the compiler, adding it to scalacOptions does not help + scala.util.Properties.setProp("scalac.patmat.analysisBudget", "1024") + + override lazy val settings = super.settings ++ Seq( + // Most of the projects cross-compile + crossScalaVersions := Seq( + "2.10.2", + "2.10.3", + "2.10.4", + "2.11.0", + "2.11.1", + "2.11.2", + "2.11.4" + ), + // Default stage + scalaJSStage in Global := PreLinkStage + ) + + lazy val root: Project = Project( + id = "scalajs", + base = file("."), + settings = defaultSettings ++ Seq( + name := "Scala.js", + publishArtifact in Compile := false, + + clean := clean.dependsOn( + // compiler, library and jasmineTestFramework are aggregated + clean in tools, clean in toolsJS, clean in plugin, + clean in javalanglib, clean in javalib, clean in scalalib, + clean in libraryAux, clean in javalibEx, + clean in examples, clean in helloworld, + clean in reversi, clean in testingExample, + clean in testSuite, clean in noIrCheckTest, + clean in javalibExTestSuite, + clean in partest, clean in partestSuite).value, + + publish := {}, + publishLocal := {} + ) + ).aggregate( + compiler, library, testBridge, jasmineTestFramework + ) + + /* This project is not really used in the build. Its sources are actually + * added as sources of the `compiler` project (and meant to be added to the + * `tools` project as well). + * It exists mostly to be used in IDEs, because they don't like very much to + * have code in a project that lives outside the project's directory, and + * like even less that code be shared by different projects. + */ + lazy val irProject: Project = Project( + id = "ir", + base = file("ir"), + settings = defaultSettings ++ Seq( + name := "Scala.js IR", + exportJars := true + ) + ) + + lazy val compiler: Project = Project( + id = "compiler", + base = file("compiler"), + settings = defaultSettings ++ publishSettings ++ Seq( + name := "Scala.js compiler", + crossVersion := CrossVersion.full, // because compiler api is not binary compatible + unmanagedSourceDirectories in Compile += + (scalaSource in (irProject, Compile)).value, + libraryDependencies ++= Seq( + "org.scala-lang" % "scala-compiler" % scalaVersion.value, + "org.scala-lang" % "scala-reflect" % scalaVersion.value, + "com.novocode" % "junit-interface" % "0.9" % "test" + ), + testOptions += Tests.Setup { () => + val testOutDir = (streams.value.cacheDirectory / "scalajs-compiler-test") + IO.createDirectory(testOutDir) + sys.props("scala.scalajs.compiler.test.output") = + testOutDir.getAbsolutePath + sys.props("scala.scalajs.compiler.test.scalajslib") = + (artifactPath in (library, Compile, packageBin)).value.getAbsolutePath + sys.props("scala.scalajs.compiler.test.scalalib") = { + + def isScalaLib(att: Attributed[File]) = { + att.metadata.get(moduleID.key).exists { mId => + mId.organization == "org.scala-lang" && + mId.name == "scala-library" && + mId.revision == scalaVersion.value + } + } + + val lib = (managedClasspath in Test).value.find(isScalaLib) + lib.map(_.data.getAbsolutePath).getOrElse { + streams.value.log.error("Couldn't find Scala library on the classpath. CP: " + (managedClasspath in Test).value); "" + } + } + }, + exportJars := true + ) + ) + + val commonToolsSettings = Seq( + name := "Scala.js tools", + + unmanagedSourceDirectories in Compile ++= Seq( + baseDirectory.value.getParentFile / "shared/src/main/scala", + (scalaSource in (irProject, Compile)).value), + + sourceGenerators in Compile <+= Def.task { + ScalaJSEnvGenerator.generateEnvHolder( + baseDirectory.value.getParentFile, + (sourceManaged in Compile).value) + } + ) + + lazy val tools: Project = Project( + id = "tools", + base = file("tools/jvm"), + settings = defaultSettings ++ publishSettings ++ ( + commonToolsSettings + ) ++ Seq( + scalaVersion := "2.10.4", + + libraryDependencies ++= Seq( + "com.google.javascript" % "closure-compiler" % "v20130603", + "com.googlecode.json-simple" % "json-simple" % "1.1.1", + "com.novocode" % "junit-interface" % "0.9" % "test" + ) + ) + ) + + lazy val toolsJS: Project = Project( + id = "toolsJS", + base = file("tools/js"), + settings = defaultSettings ++ myScalaJSSettings ++ publishSettings ++ ( + commonToolsSettings + ) ++ Seq( + crossVersion := ScalaJSCrossVersion.binary + ) ++ inConfig(Test) { + // Redefine test to run Node.js and link HelloWorld + test := { + if (scalaJSStage.value == Stage.PreLink) + error("Can't run toolsJS/test in preLink stage") + + val cp = { + for (e <- (fullClasspath in Test).value) + yield JSUtils.toJSstr(e.data.getAbsolutePath) + } + + val runCode = """ + var framework = org.scalajs.jasminetest.JasmineTestFramework(); + framework.setTags("typedarray") + + // Load tests + scalajs.TestDetector().loadDetectedTests(); + + var reporter = new scalajs.JasmineConsoleReporter(true); + + // Setup and run Jasmine + var jasmineEnv = jasmine.getEnv(); + jasmineEnv.addReporter(reporter); + jasmineEnv.execute(); + """ + + val code = { + s""" + var lib = scalajs.QuickLinker().linkNode(${cp.mkString(", ")}); + var run = ${JSUtils.toJSstr(runCode)}; + + eval("(function() { 'use strict'; "+ + "var __ScalaJSEnv = null; " + + lib + "; " + run + "}).call(this);"); + """ + } + + val launcher = new MemVirtualJSFile("Generated launcher file") + .withContent(code) + + val runner = jsEnv.value.jsRunner(scalaJSExecClasspath.value, + launcher, streams.value.log, scalaJSConsole.value) + + runner.run() + } + } + ).dependsOn(compiler % "plugin", javalibEx, testSuite % "test->test") + + lazy val plugin: Project = Project( + id = "sbtPlugin", + base = file("sbt-plugin"), + settings = commonSettings ++ publishSettings ++ Seq( + name := "Scala.js sbt plugin", + sbtPlugin := true, + scalaBinaryVersion := + CrossVersion.binaryScalaVersion(scalaVersion.value), + libraryDependencies ++= Seq( + "org.mozilla" % "rhino" % "1.7R4", + "org.webjars" % "envjs" % "1.2", + "com.novocode" % "junit-interface" % "0.9" % "test" + ) ++ ScalaJSPluginInternal.phantomJSJettyModules.map(_ % "provided") + ) + ).dependsOn(tools) + + lazy val delambdafySetting = { + scalacOptions ++= ( + if (scalaBinaryVersion.value == "2.10") Seq() + else Seq("-Ydelambdafy:method")) + } + + private def serializeHardcodedIR(base: File, + infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef)): File = { + // We assume that there are no weird characters in the full name + val fullName = infoAndTree._1.name + val output = base / (fullName.replace('.', '/') + ".sjsir") + + if (!output.exists()) { + IO.createDirectory(output.getParentFile) + val stream = new BufferedOutputStream(new FileOutputStream(output)) + try { + ir.InfoSerializers.serialize(stream, infoAndTree._1) + ir.Serializers.serialize(stream, infoAndTree._2) + } finally { + stream.close() + } + } + output + } + + lazy val javalanglib: Project = Project( + id = "javalanglib", + base = file("javalanglib"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "java.lang library for Scala.js", + publishArtifact in Compile := false, + delambdafySetting, + scalacOptions += "-Yskip:cleanup,icode,jvm", + scalaJSSourceMapSettings, + compileWithLibrarySetting, + + resourceGenerators in Compile <+= Def.task { + val base = (resourceManaged in Compile).value + Seq( + serializeHardcodedIR(base, JavaLangObject.InfoAndTree), + serializeHardcodedIR(base, JavaLangString.InfoAndTree) + ) + } + ) ++ ( + scalaJSExternalCompileSettings + ) + ).dependsOn(compiler % "plugin") + + lazy val javalib: Project = Project( + id = "javalib", + base = file("javalib"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "Java library for Scala.js", + publishArtifact in Compile := false, + delambdafySetting, + scalacOptions += "-Yskip:cleanup,icode,jvm", + scalaJSSourceMapSettings, + compileWithLibrarySetting + ) ++ ( + scalaJSExternalCompileSettings + ) + ).dependsOn(compiler % "plugin") + + lazy val scalalib: Project = Project( + id = "scalalib", + base = file("scalalib"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "Scala library for Scala.js", + publishArtifact in Compile := false, + delambdafySetting, + compileWithLibrarySetting, + + // The Scala lib is full of warnings we don't want to see + scalacOptions ~= (_.filterNot( + Set("-deprecation", "-unchecked", "-feature") contains _)), + + + scalacOptions ++= List( + // Do not generate .class files + "-Yskip:cleanup,icode,jvm", + // Tell plugin to hack fix bad classOf trees + "-P:scalajs:fixClassOf", + // Link source maps to github sources of original Scalalib + "-P:scalajs:mapSourceURI:" + + fetchedScalaSourceDir.value.toURI + + "->https://raw.githubusercontent.com/scala/scala/v" + + scalaVersion.value + "/" + ), + + // Link sources in override directories to our GitHub repo + scalaJSSourceMapSettings, + + fetchedScalaSourceDir := ( + baseDirectory.value / "fetchedSources" / + scalaVersion.value + ), + + fetchScalaSource := { + val s = streams.value + val cacheDir = s.cacheDirectory + val ver = scalaVersion.value + val trgDir = fetchedScalaSourceDir.value + + val report = updateClassifiers.value + val scalaLibSourcesJar = report.select( + configuration = Set("compile"), + module = moduleFilter(name = "scala-library"), + artifact = artifactFilter(`type` = "src")).headOption.getOrElse { + sys.error(s"Could not fetch scala-library sources for version $ver") + } + + FileFunction.cached(cacheDir / s"fetchScalaSource-$ver", + FilesInfo.lastModified, FilesInfo.exists) { dependencies => + s.log.info(s"Fetching Scala library sources to $trgDir...") + + if (trgDir.exists) + IO.delete(trgDir) + IO.createDirectory(trgDir) + IO.unzip(scalaLibSourcesJar, trgDir) + } (Set(scalaLibSourcesJar)) + + trgDir + }, + + unmanagedSourceDirectories in Compile := { + // Calculates all prefixes of the current Scala version + // (including the empty prefix) to construct override + // directories like the following: + // - override-2.10.2-RC1 + // - override-2.10.2 + // - override-2.10 + // - override-2 + // - override + val ver = scalaVersion.value + val base = baseDirectory.value + val parts = ver.split(Array('.','-')) + val verList = parts.inits.map { ps => + val len = ps.mkString(".").length + // re-read version, since we lost '.' and '-' + ver.substring(0, len) + } + def dirStr(v: String) = + if (v.isEmpty) "overrides" else s"overrides-$v" + val dirs = verList.map(base / dirStr(_)).filter(_.exists) + dirs.toSeq // most specific shadow less specific + }, + + // Compute sources + // Files in earlier src dirs shadow files in later dirs + sources in Compile := { + // Sources coming from the sources of Scala + val scalaSrcDir = fetchScalaSource.value + + // All source directories (overrides shadow scalaSrcDir) + val sourceDirectories = + (unmanagedSourceDirectories in Compile).value :+ scalaSrcDir + + // Filter sources with overrides + def normPath(f: File): String = + f.getPath.replace(java.io.File.separator, "/") + + val sources = mutable.ListBuffer.empty[File] + val paths = mutable.Set.empty[String] + + for { + srcDir <- sourceDirectories + normSrcDir = normPath(srcDir) + src <- (srcDir ** "*.scala").get + } { + val normSrc = normPath(src) + val path = normSrc.substring(normSrcDir.length) + val useless = + path.contains("/scala/collection/parallel/") || + path.contains("/scala/util/parsing/") + if (!useless) { + if (paths.add(path)) + sources += src + else + streams.value.log.debug(s"not including $src") + } + } + + sources.result() + }, + + // Continuation plugin (when using 2.10.x) + autoCompilerPlugins := true, + libraryDependencies ++= { + val ver = scalaVersion.value + if (ver.startsWith("2.10.")) + Seq(compilerPlugin("org.scala-lang.plugins" % "continuations" % ver)) + else + Nil + }, + scalacOptions ++= { + if (scalaVersion.value.startsWith("2.10.")) + Seq("-P:continuations:enable") + else + Nil + } + ) ++ ( + scalaJSExternalCompileSettings + ) + ).dependsOn(compiler % "plugin") + + lazy val libraryAux: Project = Project( + id = "libraryAux", + base = file("library-aux"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js aux library", + publishArtifact in Compile := false, + delambdafySetting, + scalacOptions += "-Yskip:cleanup,icode,jvm", + scalaJSSourceMapSettings, + compileWithLibrarySetting + ) ++ ( + scalaJSExternalCompileSettings + ) + ).dependsOn(compiler % "plugin") + + lazy val library: Project = Project( + id = "library", + base = file("library"), + settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js library", + delambdafySetting, + scalaJSSourceMapSettings, + scalacOptions in (Compile, doc) += "-implicits", + exportJars := true + ) ++ ( + scalaJSExternalCompileSettings + ) ++ inConfig(Compile)(Seq( + /* Add the .sjsir files from other lib projects + * (but not .class files) + */ + mappings in packageBin ++= { + val allProducts = ( + (products in javalanglib).value ++ + (products in javalib).value ++ + (products in scalalib).value ++ + (products in libraryAux).value) + val filter = ("*.sjsir": NameFilter) + allProducts.flatMap(base => Path.selectSubpaths(base, filter)) + } + )) + ).dependsOn(compiler % "plugin") + + lazy val javalibEx: Project = Project( + id = "javalibEx", + base = file("javalib-ex"), + settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js JavaLib Ex", + delambdafySetting, + scalacOptions += "-Yskip:cleanup,icode,jvm", + scalaJSSourceMapSettings, + exportJars := true, + jsDependencies += + "org.webjars" % "jszip" % "2.4.0" / "jszip.min.js" commonJSName "JSZip" + ) ++ ( + scalaJSExternalCompileSettings + ) + ).dependsOn(compiler % "plugin", library) + + lazy val stubs: Project = Project( + id = "stubs", + base = file("stubs"), + settings = defaultSettings ++ publishSettings ++ Seq( + name := "Scala.js Stubs" + ) + ) + + // Scala.js command line interface + lazy val cli: Project = Project( + id = "cli", + base = file("cli"), + settings = defaultSettings ++ assemblySettings ++ Seq( + name := "Scala.js CLI", + scalaVersion := "2.10.4", // adapt version to tools + libraryDependencies ++= Seq( + "com.github.scopt" %% "scopt" % "3.2.0" + ), + + // assembly options + mainClass in assembly := None, // don't want an executable JAR + assemblyOption in assembly ~= { _.copy(includeScala = false) }, + AssemblyKeys.jarName in assembly := + s"${normalizedName.value}-assembly_${scalaBinaryVersion.value}-${version.value}.jar" + ) + ).dependsOn(tools) + + // Test framework + lazy val testBridge = Project( + id = "testBridge", + base = file("test-bridge"), + settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js test bridge", + delambdafySetting, + scalaJSSourceMapSettings + ) + ).dependsOn(compiler % "plugin", library) + + lazy val jasmineTestFramework = Project( + id = "jasmineTestFramework", + base = file("jasmine-test-framework"), + settings = defaultSettings ++ publishSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js jasmine test framework", + + jsDependencies ++= Seq( + ProvidedJS / "jasmine-polyfills.js", + "org.webjars" % "jasmine" % "1.3.1" / + "jasmine.js" dependsOn "jasmine-polyfills.js" + ), + scalaJSSourceMapSettings + ) + ).dependsOn(compiler % "plugin", library, testBridge) + + // Examples + + lazy val examples: Project = Project( + id = "examples", + base = file("examples"), + settings = defaultSettings ++ Seq( + name := "Scala.js examples" + ) + ).aggregate(helloworld, reversi, testingExample) + + lazy val exampleSettings = defaultSettings ++ myScalaJSSettings + + lazy val helloworld: Project = Project( + id = "helloworld", + base = file("examples") / "helloworld", + settings = exampleSettings ++ Seq( + name := "Hello World - Scala.js example", + moduleName := "helloworld", + persistLauncher := true + ) + ).dependsOn(compiler % "plugin", library) + + lazy val reversi = Project( + id = "reversi", + base = file("examples") / "reversi", + settings = exampleSettings ++ Seq( + name := "Reversi - Scala.js example", + moduleName := "reversi" + ) + ).dependsOn(compiler % "plugin", library) + + lazy val testingExample = Project( + id = "testingExample", + base = file("examples") / "testing", + settings = exampleSettings ++ Seq( + name := "Testing - Scala.js example", + moduleName := "testing", + + jsDependencies ++= Seq( + RuntimeDOM % "test", + "org.webjars" % "jquery" % "1.10.2" / "jquery.js" % "test" + ) + ) + ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test") + + // Testing + + lazy val testSuite: Project = Project( + id = "testSuite", + base = file("test-suite"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js test suite", + publishArtifact in Compile := false, + + scalacOptions ~= (_.filter(_ != "-deprecation")), + + sources in Test ++= { + if (!scalaVersion.value.startsWith("2.10") && + scalacOptions.value.contains("-Xexperimental")) { + (((sourceDirectory in Test).value / "require-sam") ** "*.scala").get + } else { + Nil + } + }, + + /* Generate a scala source file that throws exceptions in + various places (while attaching the source line to the + exception). When we catch the exception, we can then + compare the attached source line and the source line + calculated via the source maps. + + see test-suite/src/test/resources/SourceMapTestTemplate.scala + */ + sourceGenerators in Test <+= Def.task { + val dir = (sourceManaged in Test).value + IO.createDirectory(dir) + + val template = IO.read((resourceDirectory in Test).value / + "SourceMapTestTemplate.scala") + + def lineNo(cs: CharSequence) = + (0 until cs.length).count(i => cs.charAt(i) == '\n') + 1 + + var i = 0 + val pat = "/\\*{2,3}/".r + val replaced = pat.replaceAllIn(template, { mat => + val lNo = lineNo(mat.before) + val res = + if (mat.end - mat.start == 5) + // matching a /***/ + s"if (TC.is($i)) { throw new TestException($lNo) } else " + else + // matching a /**/ + s"; if (TC.is($i)) { throw new TestException($lNo) } ;" + + i += 1 + + res + }) + + val outFile = dir / "SourceMapTest.scala" + IO.write(outFile, replaced.replace("0/*<testCount>*/", i.toString)) + Seq(outFile) + } + ) + ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test") + + lazy val noIrCheckTest: Project = Project( + id = "noIrCheckTest", + base = file("no-ir-check-test"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "Scala.js not IR checked tests", + scalaJSOptimizerOptions ~= (_.withCheckScalaJSIR(false)), + publishArtifact in Compile := false + ) + ).dependsOn(compiler % "plugin", library, jasmineTestFramework % "test") + + lazy val javalibExTestSuite: Project = Project( + id = "javalibExTestSuite", + base = file("javalib-ex-test-suite"), + settings = defaultSettings ++ myScalaJSSettings ++ Seq( + name := "JavaLib Ex Test Suite", + publishArtifact in Compile := false, + + scalacOptions in Test ~= (_.filter(_ != "-deprecation")) + ) + ).dependsOn(compiler % "plugin", javalibEx, jasmineTestFramework % "test") + + lazy val partest: Project = Project( + id = "partest", + base = file("partest"), + settings = defaultSettings ++ Seq( + name := "Partest for Scala.js", + moduleName := "scalajs-partest", + + resolvers += Resolver.typesafeIvyRepo("releases"), + + fetchedScalaSourceDir := ( + baseDirectory.value / "fetchedSources" / + scalaVersion.value + ), + + fetchScalaSource := { + import org.eclipse.jgit.api._ + + val s = streams.value + val ver = scalaVersion.value + val trgDir = fetchedScalaSourceDir.value + + if (!trgDir.exists) { + s.log.info(s"Fetching Scala source version $ver") + + // Make parent dirs and stuff + IO.createDirectory(trgDir) + + // Clone scala source code + new CloneCommand() + .setDirectory(trgDir) + .setURI("https://github.com/scala/scala.git") + .call() + } + + // Checkout proper ref. We do this anyway so we fail if + // something is wrong + val git = Git.open(trgDir) + s.log.info(s"Checking out Scala source version $ver") + git.checkout().setName(s"v$ver").call() + + trgDir + }, + + libraryDependencies ++= { + if (shouldPartest.value) + Seq( + "org.scala-sbt" % "sbt" % sbtVersion.value, + "org.scala-lang.modules" %% "scala-partest" % "1.0.1", + "com.google.javascript" % "closure-compiler" % "v20130603", + "org.mozilla" % "rhino" % "1.7R4", + "com.googlecode.json-simple" % "json-simple" % "1.1.1" + ) + else Seq() + }, + + sources in Compile := { + if (shouldPartest.value) { + // Partest sources and some sources of sbtplugin (see above) + val baseSrcs = (sources in Compile).value + // Sources for tools (and hence IR) + val toolSrcs = (sources in (tools, Compile)).value + // Individual sources from the sbtplugin + val pluginSrcs = { + val pluginBase = ((scalaSource in (plugin, Compile)).value / + "scala/scalajs/sbtplugin") + + val scalaFilter: FileFilter = "*.scala" + val files = ( + (pluginBase * "JSUtils.scala") +++ + (pluginBase / "env" * scalaFilter) +++ + (pluginBase / "env" / "nodejs" ** scalaFilter) +++ + (pluginBase / "env" / "rhino" ** scalaFilter)) + + files.get + } + toolSrcs ++ baseSrcs ++ pluginSrcs + } else Seq() + } + + ) + ).dependsOn(compiler) + + lazy val partestSuite: Project = Project( + id = "partestSuite", + base = file("partest-suite"), + settings = defaultSettings ++ Seq( + name := "Scala.js partest suite", + + fork in Test := true, + javaOptions in Test += "-Xmx1G", + + testFrameworks ++= { + if (shouldPartest.value) + Seq(new TestFramework("scala.tools.partest.scalajs.Framework")) + else Seq() + }, + + definedTests in Test <++= Def.taskDyn[Seq[sbt.TestDefinition]] { + if (shouldPartest.value) Def.task { + val _ = (fetchScalaSource in partest).value + Seq(new sbt.TestDefinition( + s"partest-${scalaVersion.value}", + // marker fingerprint since there are no test classes + // to be discovered by sbt: + new sbt.testing.AnnotatedFingerprint { + def isModule = true + def annotationName = "partest" + }, + true, + Array() + )) + } else { + Def.task(Seq()) + } + } + ) + ).dependsOn(partest % "test", library) +} diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..64abd37 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.6 diff --git a/project/build.sbt b/project/build.sbt new file mode 100644 index 0000000..b110ef2 --- /dev/null +++ b/project/build.sbt @@ -0,0 +1,48 @@ +resolvers += Resolver.url( + "bintray-sbt-plugin-releases", + url("http://dl.bintray.com/content/sbt/sbt-plugin-releases"))( + Resolver.ivyStylePatterns) + +addSbtPlugin("me.lessis" % "bintray-sbt" % "0.1.2") + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") + +libraryDependencies += "com.google.javascript" % "closure-compiler" % "v20130603" + +libraryDependencies += "org.mozilla" % "rhino" % "1.7R4" + +libraryDependencies += "org.webjars" % "envjs" % "1.2" + +libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit.pgm" % "3.2.0.201312181205-r" + +libraryDependencies += "com.googlecode.json-simple" % "json-simple" % "1.1.1" + +libraryDependencies += "org.eclipse.jetty" % "jetty-websocket" % "8.1.16.v20140903" + +libraryDependencies += "org.eclipse.jetty" % "jetty-server" % "8.1.16.v20140903" + + +unmanagedSourceDirectories in Compile ++= { + val root = baseDirectory.value.getParentFile + Seq( + root / "ir/src/main/scala", + root / "tools/shared/src/main/scala", + root / "tools/jvm/src/main/scala", + root / "sbt-plugin/src/main/scala" + ) +} + +// Add the ScalaJSEnvGenerator to the build (its in the build of the build) +sources in Compile += + baseDirectory.value / "project" / "ScalaJSEnvGenerator.scala" + +sourceGenerators in Compile <+= Def.task { + ScalaJSEnvGenerator.generateEnvHolder( + baseDirectory.value.getParentFile / "tools", + (sourceManaged in Compile).value) +} + +unmanagedResourceDirectories in Compile += { + val root = baseDirectory.value.getParentFile + root / "tools/src/main/resources" +} diff --git a/project/project/ScalaJSEnvGenerator.scala b/project/project/ScalaJSEnvGenerator.scala new file mode 100644 index 0000000..a36479f --- /dev/null +++ b/project/project/ScalaJSEnvGenerator.scala @@ -0,0 +1,31 @@ +import sbt._ + +object ScalaJSEnvGenerator { + + /** Generate a *.scala file that contains the scalajsenv as literal string + * + * We need this so the tools don't rely on I/O and/or resources. + */ + def generateEnvHolder(baseDir: File, sourceDir: File): Seq[File] = { + val trg = sourceDir / "ScalaJSEnvHolder.scala" + val env = baseDir / "scalajsenv.js" + + if (!trg.exists() || trg.lastModified() < env.lastModified()) { + val scalajsenv = IO.read(env).replaceAllLiterally("$", "$$") + + val scalaCode = + s""" + package scala.scalajs.tools.corelib + + private[corelib] object ScalaJSEnvHolder { + final val scalajsenv = raw\"\"\"$scalajsenv\"\"\" + } + """ + + IO.write(trg, scalaCode) + } + + Seq(trg) + } + +} |