Skip to content

Initial infrastructure and hello world for the Scala.js back-end. #1126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import java.io.{ RandomAccessFile, File }
import java.nio.channels.FileLock
import scala.reflect.io.Path

import org.scalajs.sbtplugin.ScalaJSPlugin
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._

object DottyBuild extends Build {

val jenkinsMemLimit = List("-Xmx1300m")
Expand Down Expand Up @@ -33,6 +36,10 @@ object DottyBuild extends Build {
)
}

/** Enforce 2.11.5. Do not let it be upgraded by dependencies. */
private val overrideScalaVersionSetting =
ivyScala := ivyScala.value.map(_.copy(overrideScalaVersion = true))

lazy val `dotty-interfaces` = project.in(file("interfaces")).
settings(
// Do not append Scala versions to the generated artifacts
Expand All @@ -44,6 +51,8 @@ object DottyBuild extends Build {
lazy val dotty = project.in(file(".")).
dependsOn(`dotty-interfaces`).
settings(
overrideScalaVersionSetting,

// set sources to src/, tests to test/ and resources to resources/
scalaSource in Compile := baseDirectory.value / "src",
javaSource in Compile := baseDirectory.value / "src",
Expand Down Expand Up @@ -105,6 +114,38 @@ object DottyBuild extends Build {
runTask(Test, "dotty.partest.DPConsoleRunner", dottyJars + " " + args.mkString(" "))
},

/* Add the sources of scalajs-ir.
* To guarantee that dotty can bootstrap without depending on a version
* of scalajs-ir built with a different Scala compiler, we add its
* sources instead of depending on the binaries.
*/
ivyConfigurations += config("sourcedeps").hide,
libraryDependencies +=
"org.scala-js" %% "scalajs-ir" % scalaJSVersion % "sourcedeps",
sourceGenerators in Compile += Def.task {
val s = streams.value
val cacheDir = s.cacheDirectory
val trgDir = (sourceManaged in Compile).value / "scalajs-ir-src"

val report = updateClassifiers.value
val scalaJSIRSourcesJar = report.select(
configuration = Set("sourcedeps"),
module = (_: ModuleID).name.startsWith("scalajs-ir_"),
artifact = artifactFilter(`type` = "src")).headOption.getOrElse {
sys.error(s"Could not fetch scalajs-ir sources")
}

FileFunction.cached(cacheDir / s"fetchScalaJSIRSource",
FilesInfo.lastModified, FilesInfo.exists) { dependencies =>
s.log.info(s"Unpacking scalajs-ir sources to $trgDir...")
if (trgDir.exists)
IO.delete(trgDir)
IO.createDirectory(trgDir)
IO.unzip(scalaJSIRSourcesJar, trgDir)
(trgDir ** "*.scala").get.toSet
} (Set(scalaJSIRSourcesJar)).toSeq
}.taskValue,

// Adjust classpath for running dotty
mainClass in (Compile, run) := Some("dotty.tools.dotc.Main"),
fork in run := true,
Expand Down Expand Up @@ -144,9 +185,54 @@ object DottyBuild extends Build {
addCommandAlias("partest-only-no-bootstrap", ";test:package;package; lockPartestFile;test:test-only dotc.tests;runPartestRunner")
)

/** A sandbox to play with the Scala.js back-end of dotty.
*
* This sandbox is compiled with dotty with support for Scala.js. It can be
* used like any regular Scala.js project. In particular, `fastOptJS` will
* produce a .js file, and `run` will run the JavaScript code with a JS VM.
*
* Simply running `dotty/run -scalajs` without this sandbox is not very
* useful, as that would not provide the linker and JS runners.
*/
lazy val sjsSandbox = project.in(file("sandbox/scalajs")).
enablePlugins(ScalaJSPlugin).
settings(
overrideScalaVersionSetting,

/* Remove the Scala.js compiler plugin for scalac, and enable the
* Scala.js back-end of dotty instead.
*/
libraryDependencies ~= { deps =>
deps.filterNot(_.name.startsWith("scalajs-compiler"))
},
scalacOptions += "-scalajs",

// The main class cannot be found automatically due to the empty inc.Analysis
mainClass in Compile := Some("hello.world"),

// While developing the Scala.js back-end, it is very useful to see the trees dotc gives us
scalacOptions += "-Xprint:labelDef",

/* Debug-friendly Scala.js optimizer options.
* In particular, typecheck the Scala.js IR found on the classpath.
*/
scalaJSOptimizerOptions ~= {
_.withCheckScalaJSIR(true).withParallel(false)
}
).
settings(compileWithDottySettings).
settings(inConfig(Compile)(Seq(
/* Make sure jsDependencyManifest runs after compile, otherwise compile
* might remove the entire directory afterwards.
*/
jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile)
)))

lazy val `dotty-bench` = project.in(file("bench")).
dependsOn(dotty % "compile->test").
settings(
overrideScalaVersionSetting,

baseDirectory in (Test,run) := (baseDirectory in dotty).value,

libraryDependencies ++= Seq("com.storm-enroute" %% "scalameter" % "0.6" % Test,
Expand Down Expand Up @@ -198,4 +284,99 @@ object DottyBuild extends Build {
})
case None => throw new RuntimeException("ERROR: sbt getJarPaths: ivyHome not defined")
}

// Compile with dotty
lazy val compileWithDottySettings = {
inConfig(Compile)(inTask(compile)(Defaults.runnerTask) ++ Seq(
// Compile with dotty
fork in compile := true,

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 = Attributed.data((fullClasspath in (dotty, Compile)).value)
val cpStr = cpToString(classpath ++ compilerCp)

// 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 doCompile() will emit a call to its
* logger.info("Running dotty.tools.dotc.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 dotty.tools.dotc.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("dotty.tools.dotc.Main", compilerCp,
"-classpath" :: cpStr ::
"-d" :: classesDirectory.getAbsolutePath() ::
options ++:
sourcesArgs,
patchedLogger) foreach sys.error
}

// Work around the Windows limitation on command line length.
val isWindows =
System.getProperty("os.name").toLowerCase().indexOf("win") >= 0
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
}
))
}
}
2 changes: 2 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7")
15 changes: 15 additions & 0 deletions sandbox/scalajs/hello.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hello

import scala.scalajs.js

trait MyTrait {
val x = 5
def foo(y: Int) = x
}

object world extends js.JSApp with MyTrait {
def main(): Unit = {
println("hello dotty.js!")
println(foo(4))
}
}
14 changes: 14 additions & 0 deletions src/dotty/tools/backend/sjs/GenSJSIR.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dotty.tools.backend.sjs

import dotty.tools.dotc.core._
import Contexts._
import Phases._

/** Generates Scala.js IR files for the compilation unit. */
class GenSJSIR extends Phase {
def phaseName: String = "genSJSIR"

def run(implicit ctx: Context): Unit = {
new JSCodeGen().run()
}
}
Loading