diff --git a/docs/modules/ROOT/pages/Web_Build_Examples.adoc b/docs/modules/ROOT/pages/Web_Build_Examples.adoc index a9c588ead60..5f25c8cbb06 100644 --- a/docs/modules/ROOT/pages/Web_Build_Examples.adoc +++ b/docs/modules/ROOT/pages/Web_Build_Examples.adoc @@ -27,4 +27,8 @@ include::example/web/5-webapp-scalajs-shared.adoc[] == Publishing Cross-Platform Scala Modules -include::example/web/6-cross-platform-publishing.adoc[] \ No newline at end of file +include::example/web/6-cross-version-platform-publishing.adoc[] + +== Publishing Cross-Platform Scala Modules Alternative + +include::example/web/7-cross-platform-version-publishing.adoc[] \ No newline at end of file diff --git a/example/web/6-cross-platform-publishing/build.sc b/example/web/6-cross-version-platform-publishing/build.sc similarity index 85% rename from example/web/6-cross-platform-publishing/build.sc rename to example/web/6-cross-version-platform-publishing/build.sc index f31b0d39629..ee0e06824a8 100644 --- a/example/web/6-cross-platform-publishing/build.sc +++ b/example/web/6-cross-version-platform-publishing/build.sc @@ -16,7 +16,7 @@ trait FooModule extends Cross.Module[String] { def ivyDeps = Agg(ivy"com.lihaoyi::scalatags::0.12.0") - object test extends Tests { + trait FooTestModule extends Tests { def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.7.11") def testFramework = "utest.runner.Framework" } @@ -27,18 +27,26 @@ trait FooModule extends Cross.Module[String] { } object bar extends Module { - object jvm extends Shared - object js extends SharedJS + object jvm extends Shared{ + object test extends Tests with FooTestModule + } + object js extends SharedJS{ + object test extends Tests with FooTestModule + } } object qux extends Module{ object jvm extends Shared{ def moduleDeps = Seq(bar.jvm) def ivyDeps = super.ivyDeps() ++ Agg(ivy"com.lihaoyi::upickle::3.0.0") + + object test extends Tests with FooTestModule } object js extends SharedJS { def moduleDeps = Seq(bar.js) + + object test extends Tests with FooTestModule } } } @@ -83,6 +91,10 @@ Bar.value:

world Specific code for Scala 3.x

Parsing JSON with js.JSON.parse Qux.main: Set(

i

,

cow

,

me

) +> ./mill foo[3.2.2].__.js.test ++ bar.BarTests.test ...

world Specific code for Scala 3.x

++ qux.QuxTests.parseJsonGetKeys ... Set(i, cow, me) + > ./mill __.publishLocal Publishing Artifact(com.lihaoyi,foo-bar_sjs1_2.13,0.0.1) to ivy repo... Publishing Artifact(com.lihaoyi,foo-bar_2.13,0.0.1) to ivy repo... @@ -92,4 +104,5 @@ Publishing Artifact(com.lihaoyi,foo-bar_sjs1_3,0.0.1) to ivy repo... Publishing Artifact(com.lihaoyi,foo-bar_3,0.0.1) to ivy repo... Publishing Artifact(com.lihaoyi,foo-qux_sjs1_3,0.0.1) to ivy repo... Publishing Artifact(com.lihaoyi,foo-qux_3,0.0.1) to ivy repo... + */ diff --git a/example/web/6-cross-platform-publishing/foo/bar/src-2/BarVersionSpecific.scala b/example/web/6-cross-version-platform-publishing/foo/bar/src-2/BarVersionSpecific.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/bar/src-2/BarVersionSpecific.scala rename to example/web/6-cross-version-platform-publishing/foo/bar/src-2/BarVersionSpecific.scala diff --git a/example/web/6-cross-platform-publishing/foo/bar/src-3/BarVersionSpecific.scala b/example/web/6-cross-version-platform-publishing/foo/bar/src-3/BarVersionSpecific.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/bar/src-3/BarVersionSpecific.scala rename to example/web/6-cross-version-platform-publishing/foo/bar/src-3/BarVersionSpecific.scala diff --git a/example/web/6-cross-platform-publishing/foo/bar/src/Bar.scala b/example/web/6-cross-version-platform-publishing/foo/bar/src/Bar.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/bar/src/Bar.scala rename to example/web/6-cross-version-platform-publishing/foo/bar/src/Bar.scala diff --git a/example/web/6-cross-platform-publishing/foo/bar/test/src/BarTests.scala b/example/web/6-cross-version-platform-publishing/foo/bar/test/src/BarTests.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/bar/test/src/BarTests.scala rename to example/web/6-cross-version-platform-publishing/foo/bar/test/src/BarTests.scala diff --git a/example/web/6-cross-platform-publishing/foo/qux/src-js/QuxPlatformSpecific.scala b/example/web/6-cross-version-platform-publishing/foo/qux/src-js/QuxPlatformSpecific.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/qux/src-js/QuxPlatformSpecific.scala rename to example/web/6-cross-version-platform-publishing/foo/qux/src-js/QuxPlatformSpecific.scala diff --git a/example/web/6-cross-platform-publishing/foo/qux/src-jvm/QuxPlatformSpecific.scala b/example/web/6-cross-version-platform-publishing/foo/qux/src-jvm/QuxPlatformSpecific.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/qux/src-jvm/QuxPlatformSpecific.scala rename to example/web/6-cross-version-platform-publishing/foo/qux/src-jvm/QuxPlatformSpecific.scala diff --git a/example/web/6-cross-platform-publishing/foo/qux/src/Qux.scala b/example/web/6-cross-version-platform-publishing/foo/qux/src/Qux.scala similarity index 100% rename from example/web/6-cross-platform-publishing/foo/qux/src/Qux.scala rename to example/web/6-cross-version-platform-publishing/foo/qux/src/Qux.scala diff --git a/example/web/6-cross-platform-publishing/foo/qux/test/src/QuxTests.scala b/example/web/6-cross-version-platform-publishing/foo/qux/test/src/QuxTests.scala similarity index 79% rename from example/web/6-cross-platform-publishing/foo/qux/test/src/QuxTests.scala rename to example/web/6-cross-version-platform-publishing/foo/qux/test/src/QuxTests.scala index bdcf6fa9203..7de6c5ff150 100644 --- a/example/web/6-cross-platform-publishing/foo/qux/test/src/QuxTests.scala +++ b/example/web/6-cross-version-platform-publishing/foo/qux/test/src/QuxTests.scala @@ -3,7 +3,7 @@ import utest._ object QuxTests extends TestSuite { def tests = Tests { test("parseJsonGetKeys") { - val string = """{"i": "am", "cow": "hear", "me": "moo}""" + val string = """{"i": "am", "cow": "hear", "me": "moo"}""" val keys = QuxPlatformSpecific.parseJsonGetKeys(string) assert(keys == Set("i", "cow", "me")) keys diff --git a/example/web/7-cross-platform-version-publishing/bar/src-2/BarVersionSpecific.scala b/example/web/7-cross-platform-version-publishing/bar/src-2/BarVersionSpecific.scala new file mode 100644 index 00000000000..5a352dfeec7 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/bar/src-2/BarVersionSpecific.scala @@ -0,0 +1,4 @@ +package bar +object BarVersionSpecific { + def text(): String = "Specific code for Scala 2.x" +} diff --git a/example/web/7-cross-platform-version-publishing/bar/src-3/BarVersionSpecific.scala b/example/web/7-cross-platform-version-publishing/bar/src-3/BarVersionSpecific.scala new file mode 100644 index 00000000000..4b7da7c369d --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/bar/src-3/BarVersionSpecific.scala @@ -0,0 +1,4 @@ +package bar +object BarVersionSpecific { + def text(): String = "Specific code for Scala 3.x" +} diff --git a/example/web/7-cross-platform-version-publishing/bar/src/Bar.scala b/example/web/7-cross-platform-version-publishing/bar/src/Bar.scala new file mode 100644 index 00000000000..e8e32282c79 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/bar/src/Bar.scala @@ -0,0 +1,5 @@ +package bar +import scalatags.Text.all._ +object Bar { + val value = p("world", " ", BarVersionSpecific.text()) +} diff --git a/example/web/7-cross-platform-version-publishing/bar/test/src/BarTests.scala b/example/web/7-cross-platform-version-publishing/bar/test/src/BarTests.scala new file mode 100644 index 00000000000..8f7f1407f5e --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/bar/test/src/BarTests.scala @@ -0,0 +1,12 @@ +package bar +import utest._ +object BarTests extends TestSuite { + def tests = Tests { + test("test") { + val result = Bar.value.toString + val matcher = "

world Specific code for Scala [23].x

".r + assert(matcher.matches(result)) + result + } + } +} diff --git a/example/web/7-cross-platform-version-publishing/build.sc b/example/web/7-cross-platform-version-publishing/build.sc new file mode 100644 index 00000000000..00af5db1e44 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/build.sc @@ -0,0 +1,112 @@ +import mill._, scalalib._, scalajslib._, publish._ + +trait Shared extends CrossScalaModule with PlatformScalaModule with PublishModule { + def publishVersion = "0.0.1" + + def pomSettings = PomSettings( + description = "Hello", + organization = "com.lihaoyi", + url = "https://github.com/lihaoyi/example", + licenses = Seq(License.MIT), + versionControl = VersionControl.github("lihaoyi", "example"), + developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi")) + ) + + def ivyDeps = Agg(ivy"com.lihaoyi::scalatags::0.12.0") + + trait SharedTestModule extends Tests { + def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.7.11") + def testFramework = "utest.runner.Framework" + } +} + +trait SharedJS extends Shared with ScalaJSModule { + def scalaJSVersion = "1.13.0" +} + +val scalaVersions = Seq("2.13.8", "3.2.2") + +object bar extends Module { + object jvm extends Cross[JvmModule](scalaVersions) + trait JvmModule extends Shared{ + object test extends Tests with SharedTestModule + } + + object js extends Cross[JsModule](scalaVersions) + trait JsModule extends SharedJS{ + object test extends Tests with SharedTestModule + } +} + +object qux extends Module{ + object jvm extends Cross[JvmModule](scalaVersions) + trait JvmModule extends Shared{ + def moduleDeps = Seq(bar.jvm()) + def ivyDeps = super.ivyDeps() ++ Agg(ivy"com.lihaoyi::upickle::3.0.0") + + object test extends Tests with SharedTestModule + } + + object js extends Cross[JsModule](scalaVersions) + trait JsModule extends SharedJS { + def moduleDeps = Seq(bar.js()) + + object test extends Tests with SharedTestModule + } +} + +// This example demonstrates an alternative way of defining your cross-platform +// cross-version modules: rather than wrapping them all in a `foo` +// cross-module to provide the different versions, we instead give each module +// `bar.jvm`, `bar.js`, `qux.jvm`, `qux.js` its own `Cross` module. This +// approach can be useful if the different cross modules need to support +// different sets of Scala versions, as it allows you to specify the +// `scalaVersions` passed to each individual cross module separately. + +/** Usage + +> ./mill show qux.js[3.2.2].sources +[ + ".../qux/src", + ".../qux/src-js", + ".../qux/src-3.2.2", + ".../qux/src-3.2.2-js", + ".../qux/src-3.2", + ".../qux/src-3.2-js", + ".../qux/src-3", + ".../qux/src-3-js" +] + +> ./mill show qux.js[3.2.2].test.sources +[ + ".../qux/test/src", + ".../qux/test/src-js", + ".../qux/test/src-3.2.2", + ".../qux/test/src-3.2.2-js", + ".../qux/test/src-3.2", + ".../qux/test/src-3.2-js", + ".../qux/test/src-3", + ".../qux/test/src-3-js" +] + +> ./mill qux.jvm[2.13.8].run +Bar.value:

world Specific code for Scala 2.x

+Parsing JSON with ujson.read +Qux.main: Set(

i

,

cow

,

me

) + +> ./mill __.js[3.2.2].test ++ bar.BarTests.test ...

world Specific code for Scala 3.x

++ qux.QuxTests.parseJsonGetKeys ... Set(i, cow, me) + +> ./mill __.publishLocal +... +Publishing Artifact(com.lihaoyi,bar_sjs1_2.13,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,bar_2.13,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,qux_sjs1_2.13,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,qux_2.13,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,bar_sjs1_3,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,bar_3,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,qux_sjs1_3,0.0.1) to ivy repo... +Publishing Artifact(com.lihaoyi,qux_3,0.0.1) to ivy repo... + +*/ diff --git a/example/web/7-cross-platform-version-publishing/qux/src-js/QuxPlatformSpecific.scala b/example/web/7-cross-platform-version-publishing/qux/src-js/QuxPlatformSpecific.scala new file mode 100644 index 00000000000..d9f57981da4 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/qux/src-js/QuxPlatformSpecific.scala @@ -0,0 +1,8 @@ +package qux +import scala.scalajs.js +object QuxPlatformSpecific { + def parseJsonGetKeys(s: String): Set[String] = { + println("Parsing JSON with js.JSON.parse") + js.JSON.parse(s).asInstanceOf[js.Dictionary[_]].keys.toSet + } +} diff --git a/example/web/7-cross-platform-version-publishing/qux/src-jvm/QuxPlatformSpecific.scala b/example/web/7-cross-platform-version-publishing/qux/src-jvm/QuxPlatformSpecific.scala new file mode 100644 index 00000000000..8feff343246 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/qux/src-jvm/QuxPlatformSpecific.scala @@ -0,0 +1,7 @@ +package qux +object QuxPlatformSpecific { + def parseJsonGetKeys(s: String): Set[String] = { + println("Parsing JSON with ujson.read") + ujson.read(s).obj.keys.toSet + } +} diff --git a/example/web/7-cross-platform-version-publishing/qux/src/Qux.scala b/example/web/7-cross-platform-version-publishing/qux/src/Qux.scala new file mode 100644 index 00000000000..77bad3e03af --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/qux/src/Qux.scala @@ -0,0 +1,9 @@ +package qux +import scalatags.Text.all._ +object Qux { + def main(args: Array[String]): Unit = { + println("Bar.value: " + bar.Bar.value) + val string = """{"i": "am", "cow": "hear", "me": "moo"}""" + println("Qux.main: " + QuxPlatformSpecific.parseJsonGetKeys(string).map(p(_))) + } +} diff --git a/example/web/7-cross-platform-version-publishing/qux/test/src/QuxTests.scala b/example/web/7-cross-platform-version-publishing/qux/test/src/QuxTests.scala new file mode 100644 index 00000000000..7de6c5ff150 --- /dev/null +++ b/example/web/7-cross-platform-version-publishing/qux/test/src/QuxTests.scala @@ -0,0 +1,12 @@ +package qux +import utest._ +object QuxTests extends TestSuite { + def tests = Tests { + test("parseJsonGetKeys") { + val string = """{"i": "am", "cow": "hear", "me": "moo"}""" + val keys = QuxPlatformSpecific.parseJsonGetKeys(string) + assert(keys == Set("i", "cow", "me")) + keys + } + } +} diff --git a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala index 42a6e35c2ce..4344d7af305 100644 --- a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala +++ b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala @@ -104,8 +104,9 @@ object WatchSourceInputTests extends IntegrationTestSuite { test("sources") { - test("noshow") - retry(3) { testWatchSource(false) } - test("show") - retry(3) { testWatchSource(true) } + // Make sure we clean up the workspace between retries + test("noshow") - retry(3) { initWorkspace(); testWatchSource(false) } + test("show") - retry(3) { initWorkspace(); testWatchSource(true) } } def testWatchInput(show: Boolean) = { @@ -146,8 +147,9 @@ object WatchSourceInputTests extends IntegrationTestSuite { test("input") { - test("noshow") - retry(3) { testWatchInput(false) } - test("show") - retry(3) { testWatchInput(true) } + // Make sure we clean up the workspace between retries + test("noshow") - retry(3) { initWorkspace(); testWatchInput(false) } + test("show") - retry(3) { initWorkspace(); testWatchInput(true) } } } } diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala index 7183dfce181..5d8401af161 100644 --- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala +++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala @@ -188,10 +188,16 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer => } override def mandatoryScalacOptions = T { - super.mandatoryScalacOptions() ++ { - if (ZincWorkerUtil.isScala3(scalaVersion())) Seq("-scalajs") + // Don't add flag twice, e.g. if a test suite inherits it both directly + // ScalaJSModule as well as from the enclosing non-test ScalaJSModule + val scalajsFlag = + if ( + ZincWorkerUtil.isScala3(scalaVersion()) && + !super.mandatoryScalacOptions().contains("-scalajs") + ) Seq("-scalajs") else Seq.empty - } + + super.mandatoryScalacOptions() ++ scalajsFlag } override def scalacPluginIvyDeps = T { diff --git a/scalalib/src/mill/scalalib/CrossScalaModule.scala b/scalalib/src/mill/scalalib/CrossScalaModule.scala index 017ea56f4d1..7c025c90149 100644 --- a/scalalib/src/mill/scalalib/CrossScalaModule.scala +++ b/scalalib/src/mill/scalalib/CrossScalaModule.scala @@ -17,12 +17,4 @@ trait CrossScalaModule extends ScalaModule with CrossModuleBase { super.sources() ++ scalaVersionDirectoryNames.map(s => PathRef(millSourcePath / s"src-$s")) } - - trait CrossScalaModuleTests extends ScalaModuleTests { - override def sources = T.sources { - super.sources() ++ - scalaVersionDirectoryNames.map(s => PathRef(millSourcePath / s"src-$s")) - } - } - trait Tests extends CrossScalaModuleTests } diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index c0b71cc633b..2b17513b3c9 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -41,7 +41,13 @@ trait JavaModule override def zincWorker: ZincWorkerModule = outer.zincWorker override def skipIdea: Boolean = outer.skipIdea override def runUseArgsFile: Target[Boolean] = T { outer.runUseArgsFile() } + override def sources = T.sources { + for (src <- outer.sources()) yield { + PathRef(this.millSourcePath / src.path.relativeTo(outer.millSourcePath)) + } + } } + trait Tests extends JavaModuleTests def defaultCommandName(): String = "run" diff --git a/scalalib/src/mill/scalalib/PlatformScalaModule.scala b/scalalib/src/mill/scalalib/PlatformScalaModule.scala index 21f42e1e26f..5a294d77430 100644 --- a/scalalib/src/mill/scalalib/PlatformScalaModule.scala +++ b/scalalib/src/mill/scalalib/PlatformScalaModule.scala @@ -16,13 +16,15 @@ trait PlatformScalaModule extends ScalaModule { override def millSourcePath = super.millSourcePath / os.up override def sources = T.sources { - val platform = millModuleSegments.parts.last - super.sources().flatMap(source => - Seq( - source, - PathRef(source.path / os.up / s"${source.path.last}-${platform}") - ) - ) + val platform = millModuleSegments + .value + .collect { case l: mill.define.Segment.Label => l.value } + .last + + super.sources().flatMap { source => + val platformPath = PathRef(source.path / _root_.os.up / s"${source.path.last}-${platform}") + Seq(source, platformPath) + } } override def artifactNameParts = super.artifactNameParts().dropRight(1)