diff --git a/project/Build.scala b/project/Build.scala index 2e6e7a242389..a618c5f21768 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -36,15 +36,6 @@ object MyScalaJSPlugin extends AutoPlugin { override def projectSettings: Seq[Setting[_]] = Def.settings( commonBootstrappedSettings, - /* Remove the Scala.js compiler plugin for scalac, and enable the - * Scala.js back-end of dotty instead. - */ - libraryDependencies := { - val deps = libraryDependencies.value - deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value)) - }, - scalacOptions += "-scalajs", - // Replace the JVM JUnit dependency by the Scala.js one libraryDependencies ~= { _.filter(!_.name.startsWith("junit-interface")) @@ -289,8 +280,11 @@ object Build { Some((packageBin in (`dotty-sbt-bridge`, Compile)).value) }, - // Use the same name as the non-bootstrapped projects for the artifacts - moduleName ~= { _.stripSuffix("-bootstrapped") }, + // Use the same name as the non-bootstrapped projects for the artifacts. + // Remove the `js` suffix because JS artifacts are published using their special crossVersion. + // The order of the two `stripSuffix`es is important, so that + // dotty-library-bootstrappedjs becomes dotty-library. + moduleName ~= { _.stripSuffix("js").stripSuffix("-bootstrapped") }, // Enforce that the only Scala 2 classfiles we unpickle come from scala-library /* @@ -778,8 +772,14 @@ object Build { asDottyLibrary(Bootstrapped). enablePlugins(MyScalaJSPlugin). settings( + libraryDependencies += + ("org.scala-js" %% "scalajs-library" % scalaJSVersion).withDottyCompat(scalaVersion.value), unmanagedSourceDirectories in Compile := (unmanagedSourceDirectories in (`dotty-library-bootstrapped`, Compile)).value, + + // Make sure `dotty-bootstrapped/test` doesn't fail on this project for no reason + test in Test := {}, + testOnly in Test := {}, ) lazy val tastyCoreSettings = Seq( @@ -1397,7 +1397,8 @@ object Build { def asDottyRoot(implicit mode: Mode): Project = project.withCommonSettings. aggregate(`dotty-interfaces`, dottyLibrary, dottyCompiler, tastyCore, dottyDoc, `dotty-sbt-bridge`). bootstrappedAggregate(`scala-library`, `scala-compiler`, `scala-reflect`, scalap, - `dotty-language-server`, `dotty-staging`, `dotty-tasty-inspector`, `dotty-tastydoc`). + `dotty-language-server`, `dotty-staging`, `dotty-tasty-inspector`, `dotty-tastydoc`, + `dotty-library-bootstrappedJS`). dependsOn(tastyCore). dependsOn(dottyCompiler). dependsOn(dottyLibrary). diff --git a/sbt-dotty/sbt-test/scalajs/basic/Main.scala b/sbt-dotty/sbt-test/scalajs/basic/Main.scala new file mode 100644 index 000000000000..fdee6e6bef11 --- /dev/null +++ b/sbt-dotty/sbt-test/scalajs/basic/Main.scala @@ -0,0 +1,11 @@ +package test + +import scala.scalajs.js +import org.scalajs.dom + +object Main { + def main(args: Array[String]): Unit = { + println("Hello Scala.js") + dom.console.log("using a Scala.js dependency") + } +} diff --git a/sbt-dotty/sbt-test/scalajs/basic/build.sbt b/sbt-dotty/sbt-test/scalajs/basic/build.sbt new file mode 100644 index 000000000000..53f8bc4e52e6 --- /dev/null +++ b/sbt-dotty/sbt-test/scalajs/basic/build.sbt @@ -0,0 +1,8 @@ +enablePlugins(ScalaJSPlugin) + +scalaVersion := sys.props("plugin.scalaVersion") + +// Test withDottyCompat for %%% dependencies +libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").withDottyCompat(scalaVersion.value) + +scalaJSUseMainModuleInitializer := true diff --git a/sbt-dotty/sbt-test/scalajs/basic/project/plugins.sbt b/sbt-dotty/sbt-test/scalajs/basic/project/plugins.sbt new file mode 100644 index 000000000000..1c988055f92e --- /dev/null +++ b/sbt-dotty/sbt-test/scalajs/basic/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.1.1") diff --git a/sbt-dotty/sbt-test/scalajs/basic/test b/sbt-dotty/sbt-test/scalajs/basic/test new file mode 100644 index 000000000000..62ea636c177f --- /dev/null +++ b/sbt-dotty/sbt-test/scalajs/basic/test @@ -0,0 +1 @@ +> run diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 5b7a2bb5fd03..4c9dffdc034f 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -16,6 +16,7 @@ import scala.util.Properties.isJavaAtLeast object DottyPlugin extends AutoPlugin { object autoImport { val isDotty = settingKey[Boolean]("Is this project compiled with Dotty?") + val isDottyJS = settingKey[Boolean]("Is this project compiled with Dotty and Scala.js?") // NOTE: // - this is a def to support `scalaVersion := dottyLatestNightlyBuild` @@ -93,7 +94,7 @@ object DottyPlugin extends AutoPlugin { val name = moduleID.name if (name != "dotty" && name != "dotty-library" && name != "dotty-compiler") moduleID.crossVersion match { - case _: librarymanagement.Binary => + case binary: librarymanagement.Binary => val compatVersion = CrossVersion.partialVersion(scalaVersion) match { case Some((3, _)) => @@ -107,7 +108,7 @@ object DottyPlugin extends AutoPlugin { "" } if (compatVersion.nonEmpty) - moduleID.cross(CrossVersion.constant(compatVersion)) + moduleID.cross(CrossVersion.constant(binary.prefix + compatVersion + binary.suffix)) else moduleID case _ => @@ -170,6 +171,29 @@ object DottyPlugin extends AutoPlugin { Seq( isDotty := scalaVersion.value.startsWith("0.") || scalaVersion.value.startsWith("3."), + /* The way the integration with Scala.js works basically assumes that the settings of ScalaJSPlugin + * will be applied before those of DottyPlugin. It seems to be the case in the tests I did, perhaps + * because ScalaJSPlugin is explicitly enabled, while DottyPlugin is triggered. However, I could + * not find an authoritative source on the topic. + * + * There is an alternative implementation that would not have that assumption: it would be to have + * another DottyJSPlugin, that would be auto-triggered by the presence of *both* DottyPlugin and + * ScalaJSPlugin. That plugin would be guaranteed to have its settings be applied after both of them, + * by the documented rules. However, that would require sbt-dotty to depend on sbt-scalajs to be + * able to refer to ScalaJSPlugin. + * + * When the logic of sbt-dotty moves to sbt itself, the logic specific to the Dotty-Scala.js + * combination will have to move to sbt-scalajs. Doing so currently wouldn't work since we + * observe that the settings of DottyPlugin are applied after ScalaJSPlugin, so ScalaJSPlugin + * wouldn't be able to fix up things like the dependency on dotty-library. + */ + isDottyJS := { + isDotty.value && (crossVersion.value match { + case binary: librarymanagement.Binary => binary.prefix.contains("sjs1_") + case _ => false + }) + }, + scalaOrganization := { if (isDotty.value) "ch.epfl.lamp" @@ -317,12 +341,51 @@ object DottyPlugin extends AutoPlugin { // Because managedScalaInstance is false, sbt won't add the standard library to our dependencies for us libraryDependencies ++= { - if (isDotty.value && autoScalaLibrary.value) - Seq(scalaOrganization.value %% "dotty-library" % scalaVersion.value) - else + if (isDotty.value && autoScalaLibrary.value) { + val name = + if (isDottyJS.value) "dotty-library_sjs1" + else "dotty-library" + Seq(scalaOrganization.value %% name % scalaVersion.value) + } else Seq() }, + // Patch up some more options if this is Dotty with Scala.js + scalacOptions := { + val prev = scalacOptions.value + /* The `&& !prev.contains("-scalajs")` is future-proof, for when sbt-scalajs adds that + * option itself but sbt-dotty is still required for the other Dotty-related stuff. + */ + if (isDottyJS.value && !prev.contains("-scalajs")) prev :+ "-scalajs" + else prev + }, + libraryDependencies := { + val prev = libraryDependencies.value + if (!isDottyJS.value) { + prev + } else { + prev + /* Remove the dependencies we don't want: + * * We don't want scalajs-library, because we need the one that comes + * as a dependency of dotty-library_sjs1 + * * We don't want scalajs-compiler, because that's a compiler plugin, + * which is replaced by the `-scalajs` flag in dotc. + */ + .filterNot { moduleID => + moduleID.organization == "org.scala-js" && ( + moduleID.name == "scalajs-library" || moduleID.name == "scalajs-compiler" + ) + } + // Apply withDottyCompat to the dependency on scalajs-test-bridge + .map { moduleID => + if (moduleID.organization == "org.scala-js" && moduleID.name == "scalajs-test-bridge") + moduleID.withDottyCompat(scalaVersion.value) + else + moduleID + } + } + }, + // Turns off the warning: // [warn] Binary version (0.9.0-RC1) for dependency ...;0.9.0-RC1 // [warn] in ... differs from Scala binary version in project (0.9).