@@ -2,11 +2,12 @@ package dotty.tools.sbtplugin
2
2
3
3
import sbt ._
4
4
import sbt .Keys ._
5
- import sbt .librarymanagement .DependencyResolution
5
+ import sbt .librarymanagement ._
6
6
import sbt .internal .inc .ScalaInstance
7
7
import xsbti .compile ._
8
8
import java .net .URLClassLoader
9
9
import java .util .Optional
10
+ import scala .util .Properties .isJavaAtLeast
10
11
11
12
object DottyPlugin extends AutoPlugin {
12
13
object autoImport {
@@ -165,7 +166,13 @@ object DottyPlugin extends AutoPlugin {
165
166
166
167
scalaCompilerBridgeBinaryJar := Def .taskDyn {
167
168
if (isDotty.value) Def .task {
168
- val dottyBridgeArtifacts = fetchArtifactsOf(" dotty-sbt-bridge" , CrossVersion .disabled).value
169
+ val dottyBridgeArtifacts = fetchArtifactsOf(
170
+ dependencyResolution.value,
171
+ scalaModuleInfo.value,
172
+ updateConfiguration.value,
173
+ (unresolvedWarningConfiguration in update).value,
174
+ streams.value.log,
175
+ scalaOrganization.value % " dotty-sbt-bridge" % scalaVersion.value).allFiles
169
176
val jars = dottyBridgeArtifacts.filter(art => art.getName.startsWith(" dotty-sbt-bridge" ) && art.getName.endsWith(" .jar" )).toArray
170
177
if (jars.size == 0 )
171
178
throw new MessageOnlyException (" No jar found for dotty-sbt-bridge" )
@@ -187,19 +194,95 @@ object DottyPlugin extends AutoPlugin {
187
194
scalaBinaryVersion.value
188
195
},
189
196
197
+ // Ideally, we should have:
198
+ //
199
+ // 1. Nothing but the Java standard library on the _JVM_ bootclasspath
200
+ // (starting with Java 9 we cannot inspect it so we don't have a choice)
201
+ //
202
+ // 2. scala-library, dotty-library, dotty-compiler, dotty-doc on the _JVM_
203
+ // classpath, because we need all of those to actually run the compiler
204
+ // and the doc tool.
205
+ // NOTE: All of those should have the *same version* (equal to scalaVersion
206
+ // for everything but scala-library).
207
+ //
208
+ // 3. scala-library, dotty-library on the _compiler_ bootclasspath because
209
+ // user code should always have access to the symbols from these jars but
210
+ // should not be able to shadow them (the compiler bootclasspath has
211
+ // higher priority than the compiler classpath).
212
+ // NOTE: the versions of {scala,dotty}-library used here do not necessarily
213
+ // match the one used in 2. because a dependency of the current project might
214
+ // require more recent versions, this is OK.
215
+ //
216
+ // 4. every other dependency of the user project on the _compiler_
217
+ // classpath.
218
+ //
219
+ // Unfortunately, zinc assumes that the compiler bootclasspath is only
220
+ // made of one jar (scala-library), so to make this work we'll need to
221
+ // either change sbt's bootclasspath handling or wait until the
222
+ // dotty-library jar and scala-library jars are merged into one jar.
223
+ // Meanwhile, let's just put nothing at all on the compiler
224
+ // bootclasspath, and instead put scala-library and dotty-library on the
225
+ // compiler classpath. This means that user code could shadow symbols
226
+ // from these jars but we can live with that for now.
227
+ classpathOptions := {
228
+ val old = classpathOptions.value
229
+ if (isDotty.value)
230
+ old
231
+ .withAutoBoot(false ) // we don't put the library on the compiler bootclasspath (as explained above)
232
+ .withFilterLibrary(false ) // ...instead, we put it on the compiler classpath
233
+ else
234
+ old
235
+ },
236
+ // ... but when running under Java 8, we still need a compiler bootclasspath
237
+ // that contains the JVM bootclasspath, otherwise sbt incremental
238
+ // compilation breaks.
239
+ scalacOptions ++= {
240
+ if (isDotty.value && ! isJavaAtLeast(" 9" ))
241
+ Seq (" -bootclasspath" , sys.props(" sun.boot.class.path" ))
242
+ else
243
+ Seq ()
244
+ },
245
+ managedScalaInstance := {
246
+ val old = managedScalaInstance.value
247
+ if (isDotty.value)
248
+ false
249
+ else
250
+ old
251
+ },
190
252
scalaInstance := Def .taskDyn {
191
- val si = scalaInstance.value
192
- if (isDotty.value) {
193
- Def .task {
194
- val dottydocArtifacts = fetchArtifactsOf(" dotty-doc" , CrossVersion .binary).value
195
- val includeArtifact = (f : File ) => f.getName.endsWith(" .jar" )
196
- val dottydocJars = dottydocArtifacts.filter(includeArtifact).toArray
197
- val allJars = (si.allJars ++ dottydocJars).distinct
198
- val loader = new URLClassLoader (Path .toURLs(dottydocJars), si.loader)
199
- new ScalaInstance (si.version, loader, si.loaderLibraryOnly, si.libraryJar, si.compilerJar, allJars, si.explicitActual)
200
- }
201
- } else {
202
- Def .task { si }
253
+ if (isDotty.value) Def .task {
254
+ val updateReport =
255
+ fetchArtifactsOf(
256
+ dependencyResolution.value,
257
+ scalaModuleInfo.value,
258
+ updateConfiguration.value,
259
+ (unresolvedWarningConfiguration in update).value,
260
+ streams.value.log,
261
+ scalaOrganization.value %% " dotty-doc" % scalaVersion.value)
262
+ val scalaLibraryJar = getJar(updateReport,
263
+ " org.scala-lang" , " scala-library" , revision = AllPassFilter )
264
+ val dottyLibraryJar = getJar(updateReport,
265
+ scalaOrganization.value, s " dotty-library_ ${scalaBinaryVersion.value}" , scalaVersion.value)
266
+ val compilerJar = getJar(updateReport,
267
+ scalaOrganization.value, s " dotty-compiler_ ${scalaBinaryVersion.value}" , scalaVersion.value)
268
+ val allJars =
269
+ getJars(updateReport, AllPassFilter , AllPassFilter , AllPassFilter )
270
+
271
+ makeScalaInstance(
272
+ state.value,
273
+ scalaVersion.value,
274
+ scalaLibraryJar,
275
+ dottyLibraryJar,
276
+ compilerJar,
277
+ allJars
278
+ )
279
+ }
280
+ else Def .task {
281
+ // This should really be `old` with `val old = scalaInstance.value`
282
+ // above, except that this would force the original definition of the
283
+ // `scalaInstance` task to be computed when `isDotty` is true, which
284
+ // would fail because `managedScalaInstance` is false.
285
+ Defaults .scalaInstanceTask.value
203
286
}
204
287
}.value,
205
288
@@ -220,7 +303,7 @@ object DottyPlugin extends AutoPlugin {
220
303
// circular dependency found:
221
304
// ch.epfl.lamp#scala-library;0.9.0-RC1->ch.epfl.lamp#dotty-library_0.9;0.9.0-RC1->...
222
305
// (This should go away once we merge dotty-library and scala-library in one artefact)
223
- old.withCircularDependencyLevel(sbt.librarymanagement. ivy.CircularDependencyLevel .Ignore )
306
+ old.withCircularDependencyLevel(ivy.CircularDependencyLevel .Ignore )
224
307
} else old
225
308
}
226
309
) ++ inConfig(Compile )(docSettings) ++ inConfig(Test )(docSettings)
@@ -236,23 +319,57 @@ object DottyPlugin extends AutoPlugin {
236
319
scalacOptions += " -from-tasty"
237
320
))
238
321
239
- /** Fetch artifacts for scalaOrganization.value %% moduleName % scalaVersion.value */
240
- private def fetchArtifactsOf (moduleName : String , crossVersion : CrossVersion ) = Def .task {
241
- val dependencyResolution = Keys .dependencyResolution.value
242
- val log = streams.value.log
243
- val scalaInfo = scalaModuleInfo.value
244
- val updateConfiguration = Keys .updateConfiguration.value
245
- val warningConfiguration = (unresolvedWarningConfiguration in update).value
246
-
247
- val moduleID = (scalaOrganization.value % moduleName % scalaVersion.value).cross(crossVersion)
248
- val descriptor = dependencyResolution.wrapDependencyInModule(moduleID, scalaInfo)
322
+ /** Fetch artifacts for moduleID */
323
+ def fetchArtifactsOf (
324
+ dependencyRes : DependencyResolution ,
325
+ scalaInfo : Option [ScalaModuleInfo ],
326
+ updateConfig : UpdateConfiguration ,
327
+ warningConfig : UnresolvedWarningConfiguration ,
328
+ log : Logger ,
329
+ moduleID : ModuleID ): UpdateReport = {
330
+ val descriptor = dependencyRes.wrapDependencyInModule(moduleID, scalaInfo)
249
331
250
- dependencyResolution .update(descriptor, updateConfiguration, warningConfiguration , log) match {
332
+ dependencyRes .update(descriptor, updateConfig, warningConfig , log) match {
251
333
case Right (report) =>
252
- report.allFiles
334
+ report
253
335
case _ =>
254
336
throw new MessageOnlyException (
255
- s " Couldn't retrieve ` ${scalaOrganization.value} %% $moduleName %% ${scalaVersion.value} `. " )
337
+ s " Couldn't retrieve ` $moduleID `. " )
256
338
}
257
339
}
340
+
341
+ /** Get all jars in updateReport that match the given filter. */
342
+ def getJars (updateReport : UpdateReport , organization : NameFilter , name : NameFilter , revision : NameFilter ): Seq [File ] = {
343
+ updateReport.select(
344
+ configurationFilter(Runtime .name),
345
+ moduleFilter(organization, name, revision),
346
+ artifactFilter(extension = " jar" )
347
+ )
348
+ }
349
+
350
+ /** Get the single jar in updateReport that match the given filter.
351
+ * If zero or more than one jar match, an exception will be thrown. */
352
+ def getJar (updateReport : UpdateReport , organization : NameFilter , name : NameFilter , revision : NameFilter ): File = {
353
+ val jars = getJars(updateReport, organization, name, revision)
354
+ assert(jars.size == 1 , s " There should only be one $name jar but found: $jars" )
355
+ jars.head
356
+ }
357
+
358
+ def makeScalaInstance (
359
+ state : State , dottyVersion : String , scalaLibrary : File , dottyLibrary : File , compiler : File , all : Seq [File ]
360
+ ): ScalaInstance = {
361
+ val loader = state.classLoaderCache(all.toList)
362
+ val loaderLibraryOnly = state.classLoaderCache(List (dottyLibrary, scalaLibrary))
363
+ new ScalaInstance (
364
+ dottyVersion,
365
+ loader,
366
+ loaderLibraryOnly,
367
+ scalaLibrary, // Should be a Seq also containing dottyLibrary but zinc
368
+ // doesn't support this, see comment above our redefinition
369
+ // of `classpathOption`
370
+ compiler,
371
+ all.toArray,
372
+ None )
373
+
374
+ }
258
375
}
0 commit comments