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