@@ -4,6 +4,9 @@ import java.io.{ RandomAccessFile, File }
4
4
import java .nio .channels .FileLock
5
5
import scala .reflect .io .Path
6
6
7
+ import org .scalajs .sbtplugin .ScalaJSPlugin
8
+ import org .scalajs .sbtplugin .ScalaJSPlugin .autoImport ._
9
+
7
10
object DottyBuild extends Build {
8
11
9
12
// Currently, this cannot be increased without hitting the maximum amount of memory
@@ -35,8 +38,14 @@ object DottyBuild extends Build {
35
38
)
36
39
}
37
40
41
+ /** Enforce 2.11.5. Do not let it be upgraded by dependencies. */
42
+ private val overrideScalaVersionSetting =
43
+ ivyScala := ivyScala.value.map(_.copy(overrideScalaVersion = true ))
44
+
38
45
lazy val dotty = project.in(file(" ." )).
39
46
settings(
47
+ overrideScalaVersionSetting,
48
+
40
49
// set sources to src/, tests to test/ and resources to resources/
41
50
scalaSource in Compile := baseDirectory.value / " src" ,
42
51
javaSource in Compile := baseDirectory.value / " src" ,
@@ -67,6 +76,8 @@ object DottyBuild extends Build {
67
76
" org.scala-lang.modules" %% " scala-partest" % " 1.0.11" % " test" ,
68
77
" com.novocode" % " junit-interface" % " 0.11" % " test" ,
69
78
" jline" % " jline" % " 2.12" ),
79
+ libraryDependencies +=
80
+ " org.scala-js" %% " scalajs-ir" % scalaJSVersion,
70
81
71
82
// enable improved incremental compilation algorithm
72
83
incOptions := incOptions.value.withNameHashing(true ),
@@ -137,9 +148,54 @@ object DottyBuild extends Build {
137
148
addCommandAlias(" partest-only-no-bootstrap" , " ;test:package;package; lockPartestFile;test:test-only dotc.tests;runPartestRunner" )
138
149
)
139
150
151
+ /** A sandbox to play with the Scala.js back-end of dotty.
152
+ *
153
+ * This sandbox is compiled with dotty with support for Scala.js. It can be
154
+ * used like any regular Scala.js project. In particular, `fastOptJS` will
155
+ * produce a .js file, and `run` will run the JavaScript code with a JS VM.
156
+ *
157
+ * Simply running `dotty/run -scalajs` without this sandbox is not very
158
+ * useful, as that would not provide the linker and JS runners.
159
+ */
160
+ lazy val sjsSandbox = project.in(file(" sandbox/scalajs" )).
161
+ enablePlugins(ScalaJSPlugin ).
162
+ settings(
163
+ overrideScalaVersionSetting,
164
+
165
+ /* Remove the Scala.js compiler plugin for scalac, and enable the
166
+ * Scala.js back-end of dotty instead.
167
+ */
168
+ libraryDependencies ~= { deps =>
169
+ deps.filterNot(_.name.startsWith(" scalajs-compiler" ))
170
+ },
171
+ scalacOptions += " -scalajs" ,
172
+
173
+ // The main class cannot be found automatically due to the empty inc.Analysis
174
+ mainClass in Compile := Some (" hello.world" ),
175
+
176
+ // While developing the Scala.js back-end, it is very useful to see the trees dotc gives us
177
+ scalacOptions += " -Xprint:labelDef" ,
178
+
179
+ /* Debug-friendly Scala.js optimizer options.
180
+ * In particular, typecheck the Scala.js IR found on the classpath.
181
+ */
182
+ scalaJSOptimizerOptions ~= {
183
+ _.withCheckScalaJSIR(true ).withParallel(false )
184
+ }
185
+ ).
186
+ settings(compileWithDottySettings).
187
+ settings(inConfig(Compile )(Seq (
188
+ /* Make sure jsDependencyManifest runs after compile, otherwise compile
189
+ * might remove the entire directory afterwards.
190
+ */
191
+ jsDependencyManifest <<= jsDependencyManifest.dependsOn(compile)
192
+ )))
193
+
140
194
lazy val `dotty-bench` = project.in(file(" bench" )).
141
195
dependsOn(dotty % " compile->test" ).
142
196
settings(
197
+ overrideScalaVersionSetting,
198
+
143
199
baseDirectory in (Test ,run) := (baseDirectory in dotty).value,
144
200
145
201
libraryDependencies ++= Seq (" com.storm-enroute" %% " scalameter" % " 0.6" % Test ,
@@ -191,4 +247,99 @@ object DottyBuild extends Build {
191
247
})
192
248
case None => throw new RuntimeException (" ERROR: sbt getJarPaths: ivyHome not defined" )
193
249
}
250
+
251
+ // Compile with dotty
252
+ lazy val compileWithDottySettings = {
253
+ inConfig(Compile )(inTask(compile)(Defaults .runnerTask) ++ Seq (
254
+ // Compile with dotty
255
+ fork in compile := true ,
256
+
257
+ compile := {
258
+ val inputs = (compileInputs in compile).value
259
+ import inputs .config ._
260
+
261
+ val s = streams.value
262
+ val logger = s.log
263
+ val cacheDir = s.cacheDirectory
264
+
265
+ // Discover classpaths
266
+
267
+ def cpToString (cp : Seq [File ]) =
268
+ cp.map(_.getAbsolutePath).mkString(java.io.File .pathSeparator)
269
+
270
+ val compilerCp = Attributed .data((fullClasspath in (dotty, Compile )).value)
271
+ val cpStr = cpToString(classpath ++ compilerCp)
272
+
273
+ // List all my dependencies (recompile if any of these changes)
274
+
275
+ val allMyDependencies = classpath filterNot (_ == classesDirectory) flatMap { cpFile =>
276
+ if (cpFile.isDirectory) (cpFile ** " *.class" ).get
277
+ else Seq (cpFile)
278
+ }
279
+
280
+ // Compile
281
+
282
+ val cachedCompile = FileFunction .cached(cacheDir / " compile" ,
283
+ FilesInfo .lastModified, FilesInfo .exists) { dependencies =>
284
+
285
+ logger.info(
286
+ " Compiling %d Scala sources to %s..." format (
287
+ sources.size, classesDirectory))
288
+
289
+ if (classesDirectory.exists)
290
+ IO .delete(classesDirectory)
291
+ IO .createDirectory(classesDirectory)
292
+
293
+ val sourcesArgs = sources.map(_.getAbsolutePath()).toList
294
+
295
+ /* run.run() below in doCompile() will emit a call to its
296
+ * logger.info("Running dotty.tools.dotc.Main [...]")
297
+ * which we do not want to see. We use this patched logger to
298
+ * filter out that particular message.
299
+ */
300
+ val patchedLogger = new Logger {
301
+ def log (level : Level .Value , message : => String ) = {
302
+ val msg = message
303
+ if (level != Level .Info ||
304
+ ! msg.startsWith(" Running dotty.tools.dotc.Main" ))
305
+ logger.log(level, msg)
306
+ }
307
+ def success (message : => String ) = logger.success(message)
308
+ def trace (t : => Throwable ) = logger.trace(t)
309
+ }
310
+
311
+ def doCompile (sourcesArgs : List [String ]): Unit = {
312
+ val run = (runner in compile).value
313
+ run.run(" dotty.tools.dotc.Main" , compilerCp,
314
+ " -classpath" :: cpStr ::
315
+ " -d" :: classesDirectory.getAbsolutePath() ::
316
+ options ++:
317
+ sourcesArgs,
318
+ patchedLogger) foreach sys.error
319
+ }
320
+
321
+ // Work around the Windows limitation on command line length.
322
+ val isWindows =
323
+ System .getProperty(" os.name" ).toLowerCase().indexOf(" win" ) >= 0
324
+ if ((fork in compile).value && isWindows &&
325
+ (sourcesArgs.map(_.length).sum > 1536 )) {
326
+ IO .withTemporaryFile(" sourcesargs" , " .txt" ) { sourceListFile =>
327
+ IO .writeLines(sourceListFile, sourcesArgs)
328
+ doCompile(List (" @" + sourceListFile.getAbsolutePath()))
329
+ }
330
+ } else {
331
+ doCompile(sourcesArgs)
332
+ }
333
+
334
+ // Output is all files in classesDirectory
335
+ (classesDirectory ** AllPassFilter ).get.toSet
336
+ }
337
+
338
+ cachedCompile((sources ++ allMyDependencies).toSet)
339
+
340
+ // We do not have dependency analysis when compiling externally
341
+ sbt.inc.Analysis .Empty
342
+ }
343
+ ))
344
+ }
194
345
}
0 commit comments