From e93f6bd21daca8dbe1ede6c2236ea8a3fcb08ad9 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 15 Nov 2023 09:39:55 +0100 Subject: [PATCH] Add check for right Tests traits in ScalaJS and Native (#2874) ## Motivation People get confused when they do things like: ```scala import mill._ import mill.scalalib._ import mill.scalajslib._ object root extends ScalaJSModule { def scalaVersion: T[String] = "3.3.1" def scalaJSVersion: T[String] = "1.14.0" object test extends ScalaTests with TestModule.Utest } ``` which doesn't work since we need to extend `ScalaJSTests` instead of `ScalaTests`. Now we crash with an exception: ``` [build.sc] [49/53] compile [info] compiling 1 Scala source to /Users/lorenzo/scala/repro/out/mill-build/compile.dest/classes ... [info] done compiling [build.sc] [53/53] methodCodeHashSignatures mill.api.MillException: root is a `ScalaJSModule`. root.test needs to extend `ScalaJSTests`. mill.scalalib.ScalaModule$ScalaTests.$init$(ScalaModule.scala:37) millbuild.build$root$test$.(build.sc:8) millbuild.build$root$.test$lzycompute$1(build.sc:30) millbuild.build$root$.test(build.sc:30) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.base/java.lang.reflect.Method.invoke(Method.java:566) mill.resolve.ResolveCore$.$anonfun$resolveDirectChildren0$10(ResolveCore.scala:272) ``` Pull Request: https://github.com/com-lihaoyi/mill/pull/2874 --------- Co-authored-by: Tobias Roeser --- .../scalajslib/ScalaTestsErrorTests.scala | 31 +++++++++++++++++ scalalib/src/mill/scalalib/ScalaModule.scala | 33 ++++++++++++++++++- .../scalanativelib/ScalaTestsErrorTests.scala | 31 +++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 scalajslib/test/src/mill/scalajslib/ScalaTestsErrorTests.scala create mode 100644 scalanativelib/test/src/mill/scalanativelib/ScalaTestsErrorTests.scala diff --git a/scalajslib/test/src/mill/scalajslib/ScalaTestsErrorTests.scala b/scalajslib/test/src/mill/scalajslib/ScalaTestsErrorTests.scala new file mode 100644 index 00000000000..8a9cb541b1b --- /dev/null +++ b/scalajslib/test/src/mill/scalajslib/ScalaTestsErrorTests.scala @@ -0,0 +1,31 @@ +package mill.scalajslib + +import mill._ +import mill.define.Discover +import mill.scalalib.TestModule +import mill.util.TestUtil +import utest._ + +object ScalaTestsErrorTests extends TestSuite { + object ScalaTestsError extends TestUtil.BaseModule { + object scalaTestsError extends ScalaJSModule { + def scalaVersion = sys.props.getOrElse("TEST_SCALA_3_3_VERSION", ???) + def scalaJSVersion = sys.props.getOrElse("TEST_SCALAJS_VERSION", ???) + object test extends ScalaTests with TestModule.Utest + } + + override lazy val millDiscover = Discover[this.type] + } + + def tests: Tests = Tests { + test("extends-ScalaTests") { + val error = intercept[ExceptionInInitializerError] { + ScalaTestsError.scalaTestsError.test + } + val message = error.getCause.getMessage + assert( + message == s"scalaTestsError is a `ScalaJSModule`. scalaTestsError.test needs to extend `ScalaJSTests`." + ) + } + } +} diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 2071117b7ce..5a391444cb8 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -1,7 +1,15 @@ package mill package scalalib -import mill.api.{DummyInputStream, JarManifest, PathRef, Result, SystemStreams, internal} +import mill.api.{ + DummyInputStream, + JarManifest, + MillException, + PathRef, + Result, + SystemStreams, + internal +} import mill.main.BuildInfo import mill.util.{Jvm, Util} import mill.util.Jvm.createJar @@ -20,6 +28,29 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => type ScalaModuleTests = ScalaTests trait ScalaTests extends JavaModuleTests with ScalaModule { + try { + if ( + Class.forName("mill.scalajslib.ScalaJSModule").isInstance(outer) && !Class.forName( + "mill.scalajslib.ScalaJSModule$ScalaJSTests" + ).isInstance(this) + ) throw new MillException( + s"$outer is a `ScalaJSModule`. $this needs to extend `ScalaJSTests`." + ) + } catch { + case _: ClassNotFoundException => // if we can't find the classes, we certainly are not in a ScalaJSModule + } + try { + if ( + Class.forName("mill.scalanativelib.ScalaNativeModule").isInstance(outer) && !Class.forName( + "mill.scalanativelib.ScalaNativeModule$ScalaNativeTests" + ).isInstance(this) + ) throw new MillException( + s"$outer is a `ScalaNativeModule`. $this needs to extend `ScalaNativeTests`." + ) + } catch { + case _: ClassNotFoundException => // if we can't find the classes, we certainly are not in a ScalaNativeModule + } + override def scalaOrganization: Target[String] = outer.scalaOrganization() override def scalaVersion: Target[String] = outer.scalaVersion() override def scalacPluginIvyDeps: Target[Agg[Dep]] = outer.scalacPluginIvyDeps() diff --git a/scalanativelib/test/src/mill/scalanativelib/ScalaTestsErrorTests.scala b/scalanativelib/test/src/mill/scalanativelib/ScalaTestsErrorTests.scala new file mode 100644 index 00000000000..af843d2102c --- /dev/null +++ b/scalanativelib/test/src/mill/scalanativelib/ScalaTestsErrorTests.scala @@ -0,0 +1,31 @@ +package mill.scalanativelib + +import mill._ +import mill.define.Discover +import mill.scalalib.TestModule +import mill.util.TestUtil +import utest._ + +object ScalaTestsErrorTests extends TestSuite { + object ScalaTestsError extends TestUtil.BaseModule { + object scalaTestsError extends ScalaNativeModule { + def scalaVersion = sys.props.getOrElse("TEST_SCALA_3_3_VERSION", ???) + def scalaNativeVersion = sys.props.getOrElse("TEST_SCALANATIVE_VERSION", ???) + object test extends ScalaTests with TestModule.Utest + } + + override lazy val millDiscover = Discover[this.type] + } + + def tests: Tests = Tests { + test("extends-ScalaTests") { + val error = intercept[ExceptionInInitializerError] { + ScalaTestsError.scalaTestsError.test + } + val message = error.getCause.getMessage + assert( + message == s"scalaTestsError is a `ScalaNativeModule`. scalaTestsError.test needs to extend `ScalaNativeTests`." + ) + } + } +}