diff --git a/example/migrating/javalib/1-maven-complete/.mill-jvm-version b/example/migrating/javalib/1-maven-complete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/javalib/1-maven-complete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/example/migrating/javalib/2-maven-incomplete/.mill-jvm-version b/example/migrating/javalib/2-maven-incomplete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/javalib/2-maven-incomplete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/example/migrating/javalib/2-maven-incomplete/build.mill b/example/migrating/javalib/2-maven-incomplete/build.mill index b169d76f2798..3b984189ac68 100644 --- a/example/migrating/javalib/2-maven-incomplete/build.mill +++ b/example/migrating/javalib/2-maven-incomplete/build.mill @@ -8,10 +8,10 @@ > ./mill init converting Maven build -writing Mill build file to fastexcel-writer/package.mill -writing Mill build file to fastexcel-reader/package.mill -writing Mill build file to e2e/package.mill -writing Mill build file to build.mill +writing fastexcel-writer/package.mill +writing fastexcel-reader/package.mill +writing e2e/package.mill +writing build.mill init completed, run "mill resolve _" to list available tasks > ./mill -k __.compile # Compilation needs manual tweaking to pass diff --git a/example/migrating/javalib/3-maven-complete-large/.mill-jvm-version b/example/migrating/javalib/3-maven-complete-large/.mill-jvm-version new file mode 100644 index 000000000000..3cde2ad313ee --- /dev/null +++ b/example/migrating/javalib/3-maven-complete-large/.mill-jvm-version @@ -0,0 +1 @@ +zulu:21 \ No newline at end of file diff --git a/example/migrating/javalib/3-maven-complete-large/build.mill b/example/migrating/javalib/3-maven-complete-large/build.mill index e2924f5fe169..9c962c5b62af 100644 --- a/example/migrating/javalib/3-maven-complete-large/build.mill +++ b/example/migrating/javalib/3-maven-complete-large/build.mill @@ -6,8 +6,6 @@ > ./mill init -> echo 21 > .mill-jvm-version # Repo needs Java >=21 to build and test - > rm twin/src/test/java/com/iluwatar/twin/BallThreadTest.java # skip flaky test > rm actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java # skip flaky test diff --git a/example/migrating/javalib/4-gradle-complete/.mill-jvm-version b/example/migrating/javalib/4-gradle-complete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/javalib/4-gradle-complete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/example/migrating/javalib/5-gradle-incomplete/.mill-jvm-version b/example/migrating/javalib/5-gradle-incomplete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/javalib/5-gradle-incomplete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/example/migrating/javalib/5-gradle-incomplete/build.mill b/example/migrating/javalib/5-gradle-incomplete/build.mill index d58e09f823cd..d875a5502d7b 100644 --- a/example/migrating/javalib/5-gradle-incomplete/build.mill +++ b/example/migrating/javalib/5-gradle-incomplete/build.mill @@ -4,10 +4,11 @@ > git remote add -f origin https://github.com/mockito/mockito.git > git checkout v5.19.0 # multi-module Java project -> ./mill init --gradle-jvm-id 17 # imported modules are not fully functional +> ./mill init # imported modules are not fully functional converting Gradle build init completed, run "mill resolve _" to list available tasks -> ./mill mockito-core.compile # module jvmId needs tweaking -error: java.lang.UnsupportedClassVersionError: com/google/errorprone/ErrorProneJavacPlugin has been compiled by a more recent version... +> ./mill mockito-core.compile # Compilation needs manual tweaking to pass +error: mockito-core/src/main/java/module-info.java:8:17 +module not found: net.bytebuddy */ diff --git a/example/migrating/scalalib/1-sbt-complete/.mill-jvm-version b/example/migrating/scalalib/1-sbt-complete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/scalalib/1-sbt-complete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/example/migrating/scalalib/2-sbt-incomplete/.mill-jvm-version b/example/migrating/scalalib/2-sbt-incomplete/.mill-jvm-version new file mode 100644 index 000000000000..f8757be815c8 --- /dev/null +++ b/example/migrating/scalalib/2-sbt-incomplete/.mill-jvm-version @@ -0,0 +1 @@ +zulu:17 \ No newline at end of file diff --git a/integration/manual/migrating/src/MillInitGradleAsmTests.scala b/integration/manual/migrating/src/MillInitGradleAsmTests.scala deleted file mode 100644 index 8eba606dffdc..000000000000 --- a/integration/manual/migrating/src/MillInitGradleAsmTests.scala +++ /dev/null @@ -1,13 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleAsmTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://gitlab.ow2.org/asm/asm.git", - gitBranch = "ASM_9_8", - initArgs = Seq("--gradle-jvm-id", "11"), - passingTasks = Seq("asm.compile"), - failingTasks = Seq("tools.retrofitter.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleEhcache3Tests.scala b/integration/manual/migrating/src/MillInitGradleEhcache3Tests.scala deleted file mode 100644 index 960a8c5a0862..000000000000 --- a/integration/manual/migrating/src/MillInitGradleEhcache3Tests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleEhcache3Tests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/ehcache/ehcache3.git", - gitBranch = "v3.10.8", - initArgs = Seq("--gradle-jvm-id", "11"), - failingTasks = Seq("ehcache-api.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleFastCsvTests.scala b/integration/manual/migrating/src/MillInitGradleFastCsvTests.scala deleted file mode 100644 index a36bf39582ee..000000000000 --- a/integration/manual/migrating/src/MillInitGradleFastCsvTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleFastCsvTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/osiegmar/FastCSV.git", - gitBranch = "v4.0.0", - initArgs = Seq("--gradle-jvm-id", "24"), - failingTasks = Seq("lib.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleJCommanderTests.scala b/integration/manual/migrating/src/MillInitGradleJCommanderTests.scala deleted file mode 100644 index 9a7f9a92fcae..000000000000 --- a/integration/manual/migrating/src/MillInitGradleJCommanderTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleJCommanderTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/cbeust/jcommander.git", - gitBranch = "2.0", - initArgs = Seq("--gradle-jvm-id", "11"), - failingTasks = Seq("test.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleMicroconfigTests.scala b/integration/manual/migrating/src/MillInitGradleMicroconfigTests.scala deleted file mode 100644 index 5e633fa7907e..000000000000 --- a/integration/manual/migrating/src/MillInitGradleMicroconfigTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleMicroconfigTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/microconfig/microconfig.git", - gitBranch = "v4.9.5", - initArgs = Seq("--merge"), - passingTasks = Seq("_.test") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleMockitoTests.scala b/integration/manual/migrating/src/MillInitGradleMockitoTests.scala deleted file mode 100644 index 727ec4a8a182..000000000000 --- a/integration/manual/migrating/src/MillInitGradleMockitoTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleMockitoTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/mockito/mockito.git", - gitBranch = "v5.19.0", - initArgs = Seq("--gradle-jvm-id", "17"), - failingTasks = Seq("mockito-core.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleSpotbugsTests.scala b/integration/manual/migrating/src/MillInitGradleSpotbugsTests.scala deleted file mode 100644 index 1f9387c2f6e5..000000000000 --- a/integration/manual/migrating/src/MillInitGradleSpotbugsTests.scala +++ /dev/null @@ -1,13 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleSpotbugsTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/spotbugs/spotbugs.git", - gitBranch = "4.9.4", - initArgs = Seq("--gradle-jvm-id", "17"), - passingTasks = Seq("spotbugs.compile"), - failingTasks = Seq("eclipsePlugin-test.resolvedMvnDeps") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitGradleSpringFrameworkTests.scala b/integration/manual/migrating/src/MillInitGradleSpringFrameworkTests.scala deleted file mode 100644 index c3eecdb6b91c..000000000000 --- a/integration/manual/migrating/src/MillInitGradleSpringFrameworkTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitGradleSpringFrameworkTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/spring-projects/spring-framework.git", - gitBranch = "v6.2.11", - initArgs = Seq("--gradle-jvm-id", "24"), - failingTasks = Seq("spring-instrument.resolvedMvnDeps") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenByteBuddyTests.scala b/integration/manual/migrating/src/MillInitMavenByteBuddyTests.scala deleted file mode 100644 index 75fdc67b910d..000000000000 --- a/integration/manual/migrating/src/MillInitMavenByteBuddyTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenByteBuddyTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - gitUrl = "https://github.com/raphw/byte-buddy.git", - gitBranch = "byte-buddy-1.17.7", - passingTasks = Seq("byte-buddy.compile"), - failingTasks = Seq("byte-buddy-android-test.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenCheckstyleTests.scala b/integration/manual/migrating/src/MillInitMavenCheckstyleTests.scala deleted file mode 100644 index 43f5cbdcdf6b..000000000000 --- a/integration/manual/migrating/src/MillInitMavenCheckstyleTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenCheckstyleTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/checkstyle/checkstyle.git", - "checkstyle-11.0.0", - failingTasks = Seq("compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenErrorProneTests.scala b/integration/manual/migrating/src/MillInitMavenErrorProneTests.scala deleted file mode 100644 index fcf2c2f0c2a5..000000000000 --- a/integration/manual/migrating/src/MillInitMavenErrorProneTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenErrorProneTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/google/error-prone.git", - "v2.41.0", - failingTasks = Seq("annotation.javaHome") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenJansiTests.scala b/integration/manual/migrating/src/MillInitMavenJansiTests.scala deleted file mode 100644 index 53eeb6f93872..000000000000 --- a/integration/manual/migrating/src/MillInitMavenJansiTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenJansiTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/fusesource/jansi.git", - "jansi-2.4.2", - passingTasks = Seq("test") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenJodaBeansTests.scala b/integration/manual/migrating/src/MillInitMavenJodaBeansTests.scala deleted file mode 100644 index 60e6d6b29855..000000000000 --- a/integration/manual/migrating/src/MillInitMavenJodaBeansTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenJodaBeansTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/JodaOrg/joda-beans.git", - "v2.11.1", - passingTasks = Seq("test") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenNettyTests.scala b/integration/manual/migrating/src/MillInitMavenNettyTests.scala deleted file mode 100644 index 8f0a48c39ed0..000000000000 --- a/integration/manual/migrating/src/MillInitMavenNettyTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenNettyTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/netty/netty.git", - "netty-4.2.6.Final", - failingTasks = Seq("buffer.test.discoveredTestClasses") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitMavenSpringAiTests.scala b/integration/manual/migrating/src/MillInitMavenSpringAiTests.scala deleted file mode 100644 index 516029d71c4d..000000000000 --- a/integration/manual/migrating/src/MillInitMavenSpringAiTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitMavenSpringAiTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/spring-projects/spring-ai.git", - "v1.0.3", - failingTasks = Seq("spring-ai-commons.resolvedMvnDeps") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtAirstreamTests.scala b/integration/manual/migrating/src/MillInitSbtAirstreamTests.scala deleted file mode 100644 index 43208497b4fa..000000000000 --- a/integration/manual/migrating/src/MillInitSbtAirstreamTests.scala +++ /dev/null @@ -1,13 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtAirstreamTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/raquo/Airstream.git", - "v17.2.1", - failingTasks = Seq( - ("[2.13.16].test.testOnly", "com.raquo.airstream.web.WebStorageVarSpec") - ) - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtCatsTests.scala b/integration/manual/migrating/src/MillInitSbtCatsTests.scala deleted file mode 100644 index a0db38eae40c..000000000000 --- a/integration/manual/migrating/src/MillInitSbtCatsTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtCatsTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/typelevel/cats.git", - "v2.13.0", - failingTasks = Seq("kernel.jvm[2.13.16].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtEnumeratumTests.scala b/integration/manual/migrating/src/MillInitSbtEnumeratumTests.scala deleted file mode 100644 index 584c46614291..000000000000 --- a/integration/manual/migrating/src/MillInitSbtEnumeratumTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtEnumeratumTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/lloydmeta/enumeratum.git", - "enumeratum-1.9.0", - passingTasks = Seq("enumeratum-core.jvm[2.13.16].compile"), - failingTasks = Seq("macros.jvm[2.13.16].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtFs2Tests.scala b/integration/manual/migrating/src/MillInitSbtFs2Tests.scala deleted file mode 100644 index 65655b9abe3d..000000000000 --- a/integration/manual/migrating/src/MillInitSbtFs2Tests.scala +++ /dev/null @@ -1,18 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtFs2Tests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/typelevel/fs2.git", - "v3.12.0", - passingTasks = Seq( - ("core.js[2.13.16].test.testOnly", "fs2.hashing.HashingSuite"), - ("core.jvm[3.3.5].test.testOnly", "fs2.hashing.HashingSuite") - ), - failingTasks = Seq( - "core.native[2.12.20].test.scalaNativeWorkerClasspath", - "benchmark[3.3.5].compile" - ) - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtGatlingTests.scala b/integration/manual/migrating/src/MillInitSbtGatlingTests.scala deleted file mode 100644 index c179a2a59d6e..000000000000 --- a/integration/manual/migrating/src/MillInitSbtGatlingTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtGatlingTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/gatling/gatling.git", - "v3.14.3", - failingTasks = Seq("gatling-http-client.test.compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtLilaTests.scala b/integration/manual/migrating/src/MillInitSbtLilaTests.scala deleted file mode 100644 index 92e445b2ef4c..000000000000 --- a/integration/manual/migrating/src/MillInitSbtLilaTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtLilaTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/lichess-org/lila.git", - "master", - failingTasks = Seq(("resolve", "_")) - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtNscalaTimeTests.scala b/integration/manual/migrating/src/MillInitSbtNscalaTimeTests.scala deleted file mode 100644 index ed021e3cb458..000000000000 --- a/integration/manual/migrating/src/MillInitSbtNscalaTimeTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtNscalaTimeTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/nscala-time/nscala-time.git", - "releases/3.0.0", - passingTasks = Seq("_.test") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtRefinedTests.scala b/integration/manual/migrating/src/MillInitSbtRefinedTests.scala deleted file mode 100644 index 84003920a87b..000000000000 --- a/integration/manual/migrating/src/MillInitSbtRefinedTests.scala +++ /dev/null @@ -1,12 +0,0 @@ -package mill.integration -import utest.* -object MillInitSbtRefinedTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/fthomas/refined.git", - "v0.11.3", - passingTasks = Seq("__.showModuleDeps"), - failingTasks = Seq("modules.core.jvm[3.3.4].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtScala3Tests.scala b/integration/manual/migrating/src/MillInitSbtScala3Tests.scala deleted file mode 100644 index 31aee9395367..000000000000 --- a/integration/manual/migrating/src/MillInitSbtScala3Tests.scala +++ /dev/null @@ -1,14 +0,0 @@ -package mill.integration - -import utest.* - -object MillInitSbtScala3Tests extends MillInitTestSuite { - def tests = Tests { - // sbt 1.11.0 - test - checkImport( - "https://github.com/scala/scala3.git", - "3.7.1", - failingTasks = Seq(("resolve", "_")) - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtScalaLoggingTests.scala b/integration/manual/migrating/src/MillInitSbtScalaLoggingTests.scala deleted file mode 100644 index ac57da3cad80..000000000000 --- a/integration/manual/migrating/src/MillInitSbtScalaLoggingTests.scala +++ /dev/null @@ -1,16 +0,0 @@ -package mill.integration - -import utest.* - -object MillInitSbtScalaLoggingTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - // sbt 1.6.2 - "https://github.com/lightbend-labs/scala-logging.git", - "v3.9.5", - initArgs = Seq("--sbt-jvm-id", "17"), - passingTasks = Seq("[2.11.12].compile"), - failingTasks = Seq("[2.12.15].compile", "[2.13.8].compile", "[3.1.2].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtScalaPBTests.scala b/integration/manual/migrating/src/MillInitSbtScalaPBTests.scala deleted file mode 100644 index fa586589307c..000000000000 --- a/integration/manual/migrating/src/MillInitSbtScalaPBTests.scala +++ /dev/null @@ -1,14 +0,0 @@ -package mill.integration - -import utest.* - -object MillInitSbtScalaPBTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - // sbt 1.11.2 - // init fails due to use of sbt-projectmatrix plugin - "https://github.com/scalapb/ScalaPB.git", - "v0.11.19" - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtScoptTests.scala b/integration/manual/migrating/src/MillInitSbtScoptTests.scala deleted file mode 100644 index 424faeac2c43..000000000000 --- a/integration/manual/migrating/src/MillInitSbtScoptTests.scala +++ /dev/null @@ -1,16 +0,0 @@ -package mill.integration - -import utest.* - -object MillInitSbtScoptTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - // sbt 1.5.2 - "https://github.com/scopt/scopt.git", - "v4.1.0", - initArgs = Seq("--sbt-jvm-id", "11"), - passingTasks = Seq("jvm[2.11.12].compile"), - failingTasks = Seq("jvm[2.12.16].compile", "jvm[2.13.8].compile", "jvm[3.1.3].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitSbtScryptoTests.scala b/integration/manual/migrating/src/MillInitSbtScryptoTests.scala deleted file mode 100644 index fc2160ae89ab..000000000000 --- a/integration/manual/migrating/src/MillInitSbtScryptoTests.scala +++ /dev/null @@ -1,13 +0,0 @@ -package mill.integration - -import utest.* - -object MillInitSbtScryptoTests extends MillInitTestSuite { - def tests = Tests { - test - checkImport( - "https://github.com/input-output-hk/scrypto.git", - "v3.1.0", - failingTasks = Seq("js[2.13.16].compile") - ) - } -} diff --git a/integration/manual/migrating/src/MillInitTestSuite.scala b/integration/manual/migrating/src/MillInitTestSuite.scala deleted file mode 100644 index fa5f89f022b5..000000000000 --- a/integration/manual/migrating/src/MillInitTestSuite.scala +++ /dev/null @@ -1,43 +0,0 @@ -package mill.integration -import mill.testkit.{IntegrationTester, UtestIntegrationTestSuite} -import utest.assertGoldenLiteral -trait MillInitTestSuite extends UtestIntegrationTestSuite { - def checkImport( - gitUrl: String, - gitBranch: String, - initArgs: Seq[String] = Nil, - passingTasks: Seq[os.Shellable] = Nil, - failingTasks: Seq[os.Shellable] = Nil - ): Unit = { - val tester = new IntegrationTester( - daemonMode, - workspaceSourcePath, - millExecutable, - debugLog, - baseWorkspacePath = os.pwd, - propagateJavaHome = propagateJavaHome - ) { - override val workspacePath = { - val cwd = os.temp.dir(dir = baseWorkspacePath, deleteOnExit = false) - // Clone into a new directory to preserve repo dir name. - os.proc("git", "clone", gitUrl, "--depth", 1, "--branch", gitBranch) - .call(cwd = cwd) - os.list(cwd).head - } - override def initWorkspace() = {} - } - try { - val initRes = tester.eval("init" +: initArgs, stdout = os.Inherit, stderr = os.Inherit) - if (initRes.isSuccess) { - val passingTasks0 = passingTasks.filter { - tester.eval(_, stdout = os.Inherit, stderr = os.Inherit).isSuccess - } - assertGoldenLiteral(passingTasks0, passingTasks) - val failingTasks0 = failingTasks.filterNot { - tester.eval(_, stdout = os.Inherit, stderr = os.Inherit).isSuccess - } - assertGoldenLiteral(failingTasks0, failingTasks) - } - } finally tester.close() - } -} diff --git a/integration/package.mill b/integration/package.mill index cd35e6d07637..292623824153 100644 --- a/integration/package.mill +++ b/integration/package.mill @@ -144,10 +144,9 @@ object `package` extends mill.Module { object failure extends Cross[IntegrationCrossModule](build.listCross) object feature extends Cross[IntegrationCrossModule](build.listCross) object invalidation extends Cross[IntegrationCrossModule](build.listCross) - object manual extends Cross[IntegrationCrossModule](build.listCross) object ide extends Cross[IdeIntegrationCrossModule](build.listCross) object bootstrap extends Cross[IdeIntegrationCrossModule](build.listCross) - object migrating extends Cross[IdeIntegrationCrossModule](build.listCross) + object migrating extends Cross[IntegrationCrossModule](build.listCross) trait IntegrationCrossModule extends MillScalaModule with IntegrationTestModule { override lazy val test: MillScalaTests = new MillScalaTests { // This `test` module isn't used, since `this` contains the test code we want to diff --git a/libs/init/buildgen/api/src/mill/main/buildgen/ModuleConfig.scala b/libs/init/buildgen/api/src/mill/main/buildgen/ModuleConfig.scala deleted file mode 100644 index 00f87d016834..000000000000 --- a/libs/init/buildgen/api/src/mill/main/buildgen/ModuleConfig.scala +++ /dev/null @@ -1,505 +0,0 @@ -package mill.main.buildgen - -import upickle.default.{ReadWriter, macroRW} - -/** - * Data for configuring a Mill build module. - * - * Each subtype of this ADT maps to a module type defined in Mill. - * For example, `ModuleConfig.JavaModule` maps to `mill.javalib.JavaModule` and contains the data - * to configure tasks/members like `mvnDeps`/`moduleDeps`. - */ -sealed trait ModuleConfig -object ModuleConfig { - case class Artifact(group: String, id: String, version: String) - object Artifact { - implicit val rw: ReadWriter[Artifact] = macroRW - } - sealed trait CrossVersion { - def platformed: Boolean - } - object CrossVersion { - case class Constant(value: String, platformed: Boolean) extends CrossVersion - object Constant { - implicit val rw: ReadWriter[Constant] = macroRW - } - case class Binary(platformed: Boolean) extends CrossVersion - object Binary { - implicit val rw: ReadWriter[Binary] = macroRW - } - case class Full(platformed: Boolean) extends CrossVersion - object Full { - implicit val rw: ReadWriter[Full] = macroRW - } - implicit val rw: ReadWriter[CrossVersion] = macroRW - } - case class Developer( - id: String = null, - name: String = null, - url: String = null, - organization: Option[String] = None, - organizationUrl: Option[String] = None - ) - object Developer { - implicit val rw: ReadWriter[Developer] = macroRW - } - case class License( - id: String = null, - name: String = null, - url: String = null, - isOsiApproved: Boolean = false, - isFsfLibre: Boolean = false, - distribution: String = "" - ) - object License { - implicit val rw: ReadWriter[License] = macroRW - } - case class ModuleDep( - segments: Seq[String], - // parameters by segment index, with `-1` reserved for the root module - crossArgs: Map[Int, Seq[String]] = Map() - ) - object ModuleDep { - implicit val rw: ReadWriter[ModuleDep] = macroRW - } - case class MvnDep( - organization: String, - name: String, - version: Option[String] = None, - classifier: Option[String] = None, - `type`: Option[String] = None, - excludes: Seq[(String, String)] = Nil, - cross: CrossVersion = CrossVersion.Constant("", platformed = false) - ) { - def module: (String, String) = (organization, name) - override def toString: String = { - val sep = cross match { - case _: CrossVersion.Full => ":::" - case _: CrossVersion.Binary => "::" - case _ => ":" - } - val nameSuffix = cross match { - case v: CrossVersion.Constant => v.value - case _ => "" - } - var suffix = version.getOrElse("") + classifier.fold("") { - case "" => "" - case attr => s";classifier=$attr" - } + `type`.fold("") { - case "" | "jar" => "" - case attr => s";type=$attr" - } + excludes.map { - case (org, name) => s";exclude=$org:$name" - }.mkString - if (suffix.nonEmpty) { - val sep = if (cross.platformed) "::" else ":" - suffix = sep + suffix - } - s"""mvn"$organization$sep$name$nameSuffix$suffix"""" - } - } - object MvnDep { - implicit val rw: ReadWriter[MvnDep] = macroRW - } - case class PomSettings( - description: Option[String] = None, - organization: Option[String] = None, // used to set artifactMetadata.group - url: Option[String] = None, - licenses: Seq[License] = Nil, - versionControl: VersionControl = VersionControl(), - developers: Seq[Developer] = Nil - ) - object PomSettings { - implicit val rw: ReadWriter[PomSettings] = macroRW - } - case class VersionControl( - browsableRepository: Option[String] = None, - connection: Option[String] = None, - developerConnection: Option[String] = None, - tag: Option[String] = None - ) - object VersionControl { - implicit val rw: ReadWriter[VersionControl] = macroRW - } - - case class CoursierModule(repositories: Seq[String] = Nil) extends ModuleConfig { - def abstracted(that: CoursierModule): CoursierModule = copy( - repositories.intersect(that.repositories) - ) - def inherited(base: CoursierModule): CoursierModule = copy( - repositories.diff(base.repositories) - ) - } - object CoursierModule { - implicit val rw: ReadWriter[CoursierModule] = macroRW - } - case class JavaHomeModule(jvmId: String) extends ModuleConfig { - def abstracted(that: JavaHomeModule): JavaHomeModule = copy( - abstractedValue(jvmId, that.jvmId) - ) - def inherited(base: JavaHomeModule): JavaHomeModule = copy( - inheritedValue(jvmId, base.jvmId) - ) - } - object JavaHomeModule { - implicit val rw: ReadWriter[JavaHomeModule] = macroRW - - def find( - javaVersion: Option[Int] = None, - javacOptions: Seq[String] = Nil, - scalacOptions: Seq[String] = Nil - ): Option[JavaHomeModule] = { - def versionIn(options: Seq[String], regex: String) = - options.indexWhere(_.matches(regex)) match { - case -1 => None - case i => options.lift(i + 1).map(_.stripPrefix("1.").toInt) - } - javaVersion - .orElse(versionIn(javacOptions, "--release|--?target")) - .orElse(versionIn(scalacOptions, "--?release")) - .map { version => - // Mill requires Java 11+ - val version0 = 11.max(version) - // use any distribution that supports all Java versions - JavaHomeModule(jvmId = s"zulu:$version0") - } - } - } - case class RunModule(forkWorkingDir: String = null) extends ModuleConfig { - def abstracted(that: RunModule): RunModule = copy( - abstractedValue(forkWorkingDir, that.forkWorkingDir) - ) - def inherited(base: RunModule): RunModule = copy( - inheritedValue(forkWorkingDir, base.forkWorkingDir) - ) - } - object RunModule { - implicit val rw: ReadWriter[RunModule] = macroRW - } - case class JavaModule( - mvnDeps: Seq[MvnDep] = Nil, - compileMvnDeps: Seq[MvnDep] = Nil, - runMvnDeps: Seq[MvnDep] = Nil, - bomMvnDeps: Seq[MvnDep] = Nil, - moduleDeps: Seq[ModuleDep] = Nil, - compileModuleDeps: Seq[ModuleDep] = Nil, - runModuleDeps: Seq[ModuleDep] = Nil, - javacOptions: Seq[String] = Nil, - artifactName: String = null - ) extends ModuleConfig { - def abstracted(that: JavaModule): JavaModule = copy( - mvnDeps.intersect(that.mvnDeps), - compileMvnDeps.intersect(that.compileMvnDeps), - runMvnDeps.intersect(that.runMvnDeps), - bomMvnDeps.intersect(that.bomMvnDeps), - moduleDeps.intersect(that.moduleDeps), - compileModuleDeps.intersect(that.compileModuleDeps), - runModuleDeps.intersect(that.runModuleDeps), - abstractedOptions(javacOptions, that.javacOptions), - abstractedValue(artifactName, that.artifactName) - ) - def inherited(base: JavaModule): JavaModule = copy( - mvnDeps.diff(base.mvnDeps), - compileMvnDeps.diff(base.compileMvnDeps), - runMvnDeps.diff(base.runMvnDeps), - bomMvnDeps.diff(base.bomMvnDeps), - moduleDeps.diff(base.moduleDeps), - compileModuleDeps.diff(base.compileModuleDeps), - runModuleDeps.diff(base.runModuleDeps), - inheritedOptions(javacOptions, base.javacOptions), - inheritedValue(artifactName, base.artifactName) - ) - } - object JavaModule { - implicit val rw: ReadWriter[JavaModule] = macroRW - } - case class PublishModule( - pomPackagingType: String = null, - pomParentProject: Artifact = null, - pomSettings: PomSettings = null, - publishVersion: String = null, - versionScheme: String = null, - publishProperties: Map[String, String] = Map() - ) extends ModuleConfig { - def abstracted(that: PublishModule): PublishModule = copy( - abstractedValue(pomPackagingType, that.pomPackagingType), - abstractedValue(pomParentProject, that.pomParentProject), - abstractedValue(pomSettings, that.pomSettings), - abstractedValue(publishVersion, that.publishVersion), - abstractedValue(versionScheme, that.versionScheme), - publishProperties.toSeq.intersect(that.publishProperties.toSeq).toMap - ) - def inherited(base: PublishModule): PublishModule = copy( - inheritedValue(pomPackagingType, base.pomPackagingType), - inheritedValue(pomParentProject, base.pomParentProject), - inheritedValue(pomSettings, base.pomSettings), - inheritedValue(publishVersion, base.publishVersion), - inheritedValue(versionScheme, base.versionScheme), - publishProperties.toSeq.diff(base.publishProperties.toSeq).toMap - ) - } - object PublishModule { - implicit val rw: ReadWriter[PublishModule] = macroRW - - def pomPackagingTypeOverride(packaging: String): String = packaging match { - case "" | "jar" => null - case _ => packaging - } - } - case class ErrorProneModule( - errorProneVersion: String = null, - errorProneDeps: Seq[MvnDep] = Nil, - errorProneOptions: Seq[String] = Nil, - errorProneJavacEnableOptions: Seq[String] = Nil - ) extends ModuleConfig { - def abstracted(that: ErrorProneModule): ErrorProneModule = copy( - abstractedValue(errorProneVersion, that.errorProneVersion), - errorProneDeps.intersect(that.errorProneDeps), - errorProneOptions.intersect(that.errorProneOptions), - errorProneJavacEnableOptions.intersect(that.errorProneJavacEnableOptions) - ) - def inherited(base: ErrorProneModule): ErrorProneModule = copy( - inheritedValue(errorProneVersion, base.errorProneVersion), - errorProneDeps.diff(base.errorProneDeps), - errorProneOptions.diff(base.errorProneOptions), - errorProneJavacEnableOptions.diff(base.errorProneJavacEnableOptions) - ) - } - object ErrorProneModule { - implicit val rw: ReadWriter[ErrorProneModule] = macroRW - - def find( - javacOptions: Seq[String], - errorProneMvnDeps: Seq[MvnDep] - ): (Option[ErrorProneModule], Seq[String]) = { - javacOptions.find(_.startsWith("-Xplugin:ErrorProne")).map { epOption => - val epOptions = epOption.split("\\s").toSeq.tail - val epMvnDep = - errorProneMvnDeps.find(_.module == ("com.google.errorprone", "error_prone_core")) - val (epJavacOptions, javacOptions0) = javacOptions - // skip any options added by ErrorProneModule - .filter(s => s != epOption && s != "-XDcompilePolicy=simple") - .partition(_.startsWith("-XD")) - val errorProneModule = Some(apply( - errorProneVersion = epMvnDep.flatMap(_.version).orNull, - errorProneDeps = errorProneMvnDeps.diff(epMvnDep.toSeq), - errorProneOptions = epOptions, - errorProneJavacEnableOptions = epJavacOptions - )) - (errorProneModule, javacOptions0) - }.getOrElse((None, javacOptions)) - } - } - case class ScalaModule( - scalaVersion: String = null, - scalacOptions: Seq[String] = Nil, - scalacPluginMvnDeps: Seq[MvnDep] = Nil - ) extends ModuleConfig { - def abstracted(that: ScalaModule): ScalaModule = copy( - abstractedValue(scalaVersion, that.scalaVersion), - abstractedOptions(scalacOptions, that.scalacOptions), - scalacPluginMvnDeps.intersect(that.scalacPluginMvnDeps) - ) - def inherited(base: ScalaModule): ScalaModule = copy( - inheritedValue(scalaVersion, base.scalaVersion), - inheritedOptions(scalacOptions, base.scalacOptions), - scalacPluginMvnDeps.diff(base.scalacPluginMvnDeps) - ) - } - object ScalaModule { - implicit val rw: ReadWriter[ScalaModule] = macroRW - } - case class ScalaJSModule( - scalaJSVersion: String = null, - moduleKind: String = null - ) extends ModuleConfig { - def abstracted(that: ScalaJSModule): ScalaJSModule = copy( - abstractedValue(scalaJSVersion, that.scalaJSVersion), - abstractedValue(moduleKind, that.moduleKind) - ) - def inherited(base: ScalaJSModule): ScalaJSModule = copy( - inheritedValue(scalaJSVersion, base.scalaJSVersion), - inheritedValue(moduleKind, base.moduleKind) - ) - } - object ScalaJSModule { - implicit val rw: ReadWriter[ScalaJSModule] = macroRW - - def moduleKindOverride(value: String): String = value match { - case "" | "NoModule" => null - case _ => value - } - } - case class ScalaNativeModule(scalaNativeVersion: String = null) extends ModuleConfig { - def abstracted(that: ScalaNativeModule): ScalaNativeModule = copy( - abstractedValue(that.scalaNativeVersion, that.scalaNativeVersion) - ) - def inherited(base: ScalaNativeModule): ScalaNativeModule = copy( - inheritedValue(scalaNativeVersion, base.scalaNativeVersion) - ) - } - object ScalaNativeModule { - implicit val rw: ReadWriter[ScalaNativeModule] = macroRW - } - case class SbtPlatformModule(sourcesRootFolders: Seq[String] = Nil) extends ModuleConfig { - def abstracted(that: SbtPlatformModule): SbtPlatformModule = copy( - sourcesRootFolders.intersect(that.sourcesRootFolders) - ) - def inherited(base: SbtPlatformModule): SbtPlatformModule = copy( - sourcesRootFolders.diff(base.sourcesRootFolders) - ) - } - object SbtPlatformModule { - implicit val rw: ReadWriter[SbtPlatformModule] = macroRW - } - case class TestModule( - testParallelism: String = null, - testSandboxWorkingDir: String = null - ) extends ModuleConfig { - def abstracted(that: TestModule): TestModule = copy( - abstractedValue(testParallelism, that.testParallelism), - abstractedValue(testSandboxWorkingDir, that.testSandboxWorkingDir) - ) - def inherited(base: TestModule): TestModule = copy( - inheritedValue(testParallelism, base.testParallelism), - inheritedValue(testSandboxWorkingDir, base.testSandboxWorkingDir) - ) - } - object TestModule { - implicit val rw: ReadWriter[TestModule] = macroRW - - def mixin(mvnDeps: Seq[MvnDep]): Option[String] = { - val mvnModules = mvnDeps.map(_.module) - // prioritize mixins that integrate with other frameworks - mvnModules.collectFirst { - case ("org.scalatest" | "org.scalatestplus", _) => "TestModule.ScalaTest" - case ("org.specs2", _) => "TestModule.Spec2" - case ("org.scalameta", "munit") => "TestModule.Munit" - // https://scalameta.org/munit/docs/integrations/external-integrations.html - case ("org.typelevel", "discipline-munit") => "TestModule.Munit" - case ("com.alejandrohdezma", "http4s-munit") => "TestModule.Munit" - case ("org.typelevel", name) if name.startsWith("munit-cats-effect") => "TestModule.Munit" - case ("org.scalameta", "munit-scalacheck") => "TestModule.Munit" - case ("com.github.lolgab", "munit-snapshot") => "TestModule.Munit" - case ("com.github.poslegm", "munit-zio") => "TestModule.Munit" - case ("io.github.jbwheatley", name) if name.startsWith("pact4s-") => "TestModule.Munit" - case ("com.alejandrohdezma", "sbt-scripted-munit") => "TestModule.Munit" - case ("qa.hedgehog", "hedgehog-munit") => "TestModule.Munit" - case ("com.alejandrohdezma", "tapir-golden-openapi-munit") => "TestModule.Munit" - }.orElse { - mvnModules.collectFirst { - case ("org.testng", _) => "TestModule.TestNg" - case ("junit", _) => "TestModule.Junit4" - case ("org.junit.jupiter", _) => "TestModule.Junit5" - case ("com.lihaoyi", "utest") => "TestModule.Utest" - case ("com.disneystreaming", "weaver-scalacheck") => "TestModule.Weaver" - case ("dev.zio", "zio-test" | "zio-test-sbt") => "TestModule.ZioTest" - case ("org.scalacheck", _) => "TestModule.ScalaCheck" - } - } - } - } - implicit val rw: ReadWriter[ModuleConfig] = macroRW - - def isBomDep(organization: String, name: String): Boolean = name.endsWith("-bom") || - (organization == "org.springframework.boot" && name == "spring-boot-dependencies") - - def groupedOptions(options: Seq[String]): Seq[Seq[String]] = { - val b = Seq.newBuilder[Seq[String]] - var rem = options - while (rem.nonEmpty) { - val option = rem.head +: rem.tail.takeWhile(!_.startsWith("-")) - b += option - rem = rem.drop(option.length) - } - b.result() - } - - def abstractedOptions(self: Seq[String], that: Seq[String]): Seq[String] = { - groupedOptions(self).intersect(groupedOptions(that)).flatten - } - - def inheritedOptions(self: Seq[String], base: Seq[String]): Seq[String] = { - groupedOptions(self).diff(groupedOptions(base)).flatten - } - - def abstractedValue[A](self: A, that: A, default: A = null): A = - if (self == that) self else default - - def inheritedValue[A](self: A, base: A, default: A = null): A = - if (self == base) default else self - - def abstractedConfigs(self: Seq[ModuleConfig], that: Seq[ModuleConfig]): Seq[ModuleConfig] = - self.flatMap { - case self: CoursierModule => that.collectFirst { - case that: CoursierModule => self.abstracted(that) - } - case self: JavaHomeModule => that.collectFirst { - case that: JavaHomeModule => self.abstracted(that) - } - case self: RunModule => that.collectFirst { - case that: RunModule => self.abstracted(that) - } - case self: JavaModule => that.collectFirst { - case that: JavaModule => self.abstracted(that) - } - case self: PublishModule => that.collectFirst { - case that: PublishModule => self.abstracted(that) - } - case self: ErrorProneModule => that.collectFirst { - case that: ErrorProneModule => self.abstracted(that) - } - case self: ScalaModule => that.collectFirst { - case that: ScalaModule => self.abstracted(that) - } - case self: ScalaJSModule => that.collectFirst { - case that: ScalaJSModule => self.abstracted(that) - } - case self: ScalaNativeModule => that.collectFirst { - case that: ScalaNativeModule => self.abstracted(that) - } - case self: SbtPlatformModule => that.collectFirst { - case that: SbtPlatformModule => self.abstracted(that) - } - case self: TestModule => that.collectFirst { - case that: TestModule => self.abstracted(that) - } - } - - def inheritedConfigs(self: Seq[ModuleConfig], base: Seq[ModuleConfig]): Seq[ModuleConfig] = - self.map { - case self: CoursierModule => base.collectFirst { - case base: CoursierModule => self.inherited(base) - }.getOrElse(self) - case self: JavaHomeModule => base.collectFirst { - case base: JavaHomeModule => self.inherited(base) - }.getOrElse(self) - case self: RunModule => base.collectFirst { - case base: RunModule => self.inherited(base) - }.getOrElse(self) - case self: JavaModule => base.collectFirst { - case base: JavaModule => self.inherited(base) - }.getOrElse(self) - case self: PublishModule => base.collectFirst { - case base: PublishModule => self.inherited(base) - }.getOrElse(self) - case self: ErrorProneModule => base.collectFirst { - case base: ErrorProneModule => self.inherited(base) - }.getOrElse(self) - case self: ScalaModule => base.collectFirst { - case base: ScalaModule => self.inherited(base) - }.getOrElse(self) - case self: ScalaJSModule => base.collectFirst { - case base: ScalaJSModule => self.inherited(base) - }.getOrElse(self) - case self: ScalaNativeModule => base.collectFirst { - case base: ScalaNativeModule => self.inherited(base) - }.getOrElse(self) - case self: SbtPlatformModule => base.collectFirst { - case base: SbtPlatformModule => self.inherited(base) - }.getOrElse(self) - case self: TestModule => base.collectFirst { - case base: TestModule => self.inherited(base) - }.getOrElse(self) - } -} diff --git a/libs/init/buildgen/api/src/mill/main/buildgen/ModuleSpec.scala b/libs/init/buildgen/api/src/mill/main/buildgen/ModuleSpec.scala index c9ecb0efb12b..1d56d4ae1174 100644 --- a/libs/init/buildgen/api/src/mill/main/buildgen/ModuleSpec.scala +++ b/libs/init/buildgen/api/src/mill/main/buildgen/ModuleSpec.scala @@ -1,34 +1,250 @@ package mill.main.buildgen -import upickle.default.{ReadWriter, macroRW} +import mill.main.buildgen.ModuleSpec._ +import upickle.default.{ReadWriter, macroRW, readwriter} -/** - * Specification for encoding a Mill build module as a Scala type. - * @param name Name of this type. - * @param supertypes Names of Scala types that this type extends. - * @param mixins Names of Scala mixin supertypes. - * @param configs Data for configuring this instance. - * @param crossConfigs Data for configuring this instance that varies by cross-value. - * - Cross support is limited to a single `String` value. - * - This is empty for non-cross modules. - */ case class ModuleSpec( name: String, - // TODO Can this be auto-derived from configs? + imports: Seq[String] = Nil, supertypes: Seq[String] = Nil, mixins: Seq[String] = Nil, - configs: Seq[ModuleConfig] = Nil, - crossConfigs: Seq[(String, Seq[ModuleConfig])] = Nil, - nestedModules: Seq[ModuleSpec] = Nil + crossKeys: Seq[String] = Nil, + useParentModuleDir: Boolean = false, + repositories: Values[String] = Nil, + forkArgs: Values[Opt] = Values(), + forkWorkingDir: Value[os.RelPath] = Value(), + mvnDeps: Values[MvnDep] = Values(), + compileMvnDeps: Values[MvnDep] = Values(), + runMvnDeps: Values[MvnDep] = Values(), + bomMvnDeps: Values[MvnDep] = Values(), + depManagement: Values[MvnDep] = Values(), + javacOptions: Values[Opt] = Values(), + moduleDeps: Values[ModuleDep] = Values(), + compileModuleDeps: Values[ModuleDep] = Values(), + runModuleDeps: Values[ModuleDep] = Values(), + bomModuleDeps: Values[ModuleDep] = Values(), + sourcesFolders: Values[os.SubPath] = Values(), + sources: Values[os.RelPath] = Values(), + resources: Values[os.RelPath] = Values(), + artifactName: Value[String] = Value(), + pomPackagingType: Value[String] = Value(), + pomParentProject: Value[Artifact] = Value(), + pomSettings: Value[PomSettings] = Value(), + publishVersion: Value[String] = Value(), + versionScheme: Value[String] = Value(), + publishProperties: Values[(String, String)] = Values(), + errorProneVersion: Value[String] = Value(), + errorProneDeps: Values[MvnDep] = Values(), + errorProneOptions: Values[String] = Values(), + errorProneJavacEnableOptions: Values[Opt] = Values(), + scalaVersion: Value[String] = Value(), + scalacOptions: Values[Opt] = Values(), + scalacPluginMvnDeps: Values[MvnDep] = Values(), + scalaJSVersion: Value[String] = Value(), + moduleKind: Value[String] = Value(), + scalaNativeVersion: Value[String] = Value(), + sourcesRootFolders: Values[os.SubPath] = Values(), + testParallelism: Value[Boolean] = Value(), + testSandboxWorkingDir: Value[Boolean] = Value(), + test: Option[ModuleSpec] = None, + children: Seq[ModuleSpec] = Nil ) { - def isPublishModule: Boolean = configs.exists(_.isInstanceOf[ModuleConfig.PublishModule]) - def isScalaModule: Boolean = configs.exists(_.isInstanceOf[ModuleConfig.ScalaModule]) - def isTestModule: Boolean = configs.exists(_.isInstanceOf[ModuleConfig.TestModule]) + def tree: Seq[ModuleSpec] = this +: children.flatMap(_.tree) - def sequence: Seq[ModuleSpec] = this +: nestedModules.flatMap(_.sequence) - def transform(f: ModuleSpec => ModuleSpec): ModuleSpec = - f(copy(nestedModules = nestedModules.map(_.transform(f)))) + def withErrorProneModule(errorProneMvnDeps: Seq[MvnDep]): ModuleSpec = { + javacOptions.base.find(_.group.head.startsWith("-Xplugin:ErrorProne")).fold(this) { epOption => + val epOptions = epOption.group.head.split("\\s").toSeq.tail + val epMvnDep = errorProneMvnDeps.find(dep => + dep.name == "error_prone_core" && dep.organization == "com.google.errorprone" + ) + val (epJavacOptions, javacOptions0) = javacOptions.base + // Skip options added by ErrorProneModule. + .diff(Seq(epOption, Opt("-XDcompilePolicy=simple"))) + .partition(_.group.head.startsWith("-XD")) + this.copy( + imports = "import mill.javalib.errorprone.ErrorProneModule" +: imports, + supertypes = supertypes :+ "ErrorProneModule", + errorProneVersion = epMvnDep.collect { case dep if dep.version.nonEmpty => dep.version }, + errorProneDeps = errorProneMvnDeps.diff(epMvnDep.toSeq), + errorProneOptions = epOptions, + errorProneJavacEnableOptions = epJavacOptions, + javacOptions = javacOptions0 + ) + } + } } object ModuleSpec { + sealed trait CrossVersion { + def platformed: Boolean + } + object CrossVersion { + case class Constant(value: String, platformed: Boolean) extends CrossVersion + object Constant { + implicit val rw: ReadWriter[Constant] = macroRW + } + case class Binary(platformed: Boolean) extends CrossVersion + object Binary { + implicit val rw: ReadWriter[Binary] = macroRW + } + case class Full(platformed: Boolean) extends CrossVersion + object Full { + implicit val rw: ReadWriter[Full] = macroRW + } + implicit val rw: ReadWriter[CrossVersion] = macroRW + } + case class MvnDep( + organization: String, + name: String, + version: String, + classifier: Option[String] = None, + `type`: Option[String] = None, + excludes: Seq[(String, String)] = Nil, + cross: CrossVersion = CrossVersion.Constant("", platformed = false), + ref: Option[String] = None + ) { + override def toString(): String = { + val binarySeparator = cross match { + case _: CrossVersion.Full => ":::" + case _: CrossVersion.Binary => "::" + case _ => ":" + } + val nameSuffix = cross match { + case v: CrossVersion.Constant => v.value + case _ => "" + } + val classifierAttr = classifier.fold("") { + case "" => "" + case attr => s";classifier=$attr" + } + val typeAttr = `type`.fold("") { + case "" | "jar" => "" + case attr => s";type=$attr" + } + val excludeAttrs = excludes.map { case (org, name) => s";exclude=$org:$name" }.mkString + val suffix = s"$version$classifierAttr$typeAttr$excludeAttrs" + val platformSeparator = if (suffix.isEmpty) "" else if (cross.platformed) "::" else ":" + s"""mvn"$organization$binarySeparator$name$nameSuffix$platformSeparator$suffix"""" + } + } + object MvnDep { + implicit val rw: ReadWriter[MvnDep] = macroRW + } + case class ModuleDep( + moduleDir: os.SubPath, + crossSuffix: Option[String] = None, + nestedModule: Option[String] = None + ) + object ModuleDep { + implicit val rw: ReadWriter[ModuleDep] = macroRW + } + case class Opt(group: Seq[String]) + object Opt { + implicit val rw: ReadWriter[Opt] = macroRW + def apply(head: String, tail: String*): Opt = apply(head +: tail) + def groups(ungrouped: Seq[String]): Seq[Opt] = { + val opts = Seq.newBuilder[Opt] + var rem = ungrouped + while (rem.nonEmpty) { + val group = rem.head +: rem.tail.takeWhile(_.head != '-') + opts += Opt(group) + rem = rem.drop(group.length) + } + opts.result() + } + } + case class Artifact(group: String, id: String, version: String) + object Artifact { + implicit val rw: ReadWriter[Artifact] = macroRW + } + case class License( + id: String = "", + name: String = "", + url: String = "", + isOsiApproved: Boolean = false, + isFsfLibre: Boolean = false, + distribution: String = "" + ) + object License { + implicit val rw: ReadWriter[License] = macroRW + } + case class VersionControl( + browsableRepository: Option[String] = None, + connection: Option[String] = None, + developerConnection: Option[String] = None, + tag: Option[String] = None + ) + object VersionControl { + implicit val rw: ReadWriter[VersionControl] = macroRW + } + case class Developer( + id: String = "", + name: String = "", + url: String = "", + organization: Option[String] = None, + organizationUrl: Option[String] = None + ) + object Developer { + implicit val rw: ReadWriter[Developer] = macroRW + } + case class PomSettings( + description: String = "", + organization: String = "", // maps to artifactMetadata.group + url: String = "", + licenses: Seq[License] = Nil, + versionControl: VersionControl = VersionControl(), + developers: Seq[Developer] = Nil + ) + object PomSettings { + implicit val rw: ReadWriter[PomSettings] = macroRW + } + implicit val rwRelPath: ReadWriter[os.RelPath] = + readwriter[String].bimap(_.toString, os.RelPath(_)) + implicit val rwSubPath: ReadWriter[os.SubPath] = + readwriter[String].bimap(_.toString, os.SubPath(_)) + + case class Value[+A](base: Option[A] = None, cross: Seq[(String, A)] = Nil) + object Value { + implicit def rw[A: ReadWriter]: ReadWriter[Value[A]] = macroRW + implicit def from[A](base: Option[A]): Value[A] = apply(base = base) + } + case class Values[+A]( + extend: Boolean = false, // extend super values with append + base: Seq[A] = Nil, + cross: Seq[(String, Seq[A])] = Nil + ) + object Values { + implicit def rw[A: ReadWriter]: ReadWriter[Values[A]] = macroRW + implicit def from[A](base: Seq[A]): Values[A] = apply(base = base) + } implicit val rw: ReadWriter[ModuleSpec] = macroRW + + def testModuleMixin(mvnDeps: Seq[MvnDep]): Option[String] = { + // Prioritize frameworks that integrate with other frameworks. + mvnDeps.iterator.map(dep => dep.organization -> dep.name).collectFirst { + case ("org.scalatest" | "org.scalatestplus", _) => "TestModule.ScalaTest" + case ("org.specs2", _) => "TestModule.Spec2" + // https://scalameta.org/munit/docs/integrations/external-integrations.html + case ("org.scalameta", "munit") | + ("org.typelevel", "discipline-munit") | + ("com.alejandrohdezma", "http4s-munit") | + ("org.scalameta", "munit-scalacheck") | + ("com.github.lolgab", "munit-snapshot") | + ("com.github.poslegm", "munit-zio") | + ("com.alejandrohdezma", "sbt-scripted-munit") | + ("qa.hedgehog", "hedgehog-munit") | + ("com.alejandrohdezma", "tapir-golden-openapi-munit") => "TestModule.Munit" + case ("org.typelevel", name) if name.startsWith("munit-cats-effect") => "TestModule.Munit" + case ("io.github.jbwheatley", name) if name.startsWith("pact4s-") => "TestModule.Munit" + }.orElse { + mvnDeps.iterator.map(dep => dep.organization -> dep.name).collectFirst { + case ("org.testng", _) => "TestModule.TestNg" + case ("junit", _) => "TestModule.Junit4" + case ("org.junit.jupiter", _) => "TestModule.Junit5" + case ("com.lihaoyi", "utest") => "TestModule.Utest" + case ("com.disneystreaming", "weaver-scalacheck") => "TestModule.Weaver" + case ("dev.zio", "zio-test" | "zio-test-sbt") => "TestModule.ZioTest" + case ("org.scalacheck", _) => "TestModule.ScalaCheck" + } + } + } } diff --git a/libs/init/buildgen/api/src/mill/main/buildgen/PackageSpec.scala b/libs/init/buildgen/api/src/mill/main/buildgen/PackageSpec.scala index 195f700bd5af..67fc2ee7a4c5 100644 --- a/libs/init/buildgen/api/src/mill/main/buildgen/PackageSpec.scala +++ b/libs/init/buildgen/api/src/mill/main/buildgen/PackageSpec.scala @@ -1,13 +1,13 @@ package mill.main.buildgen -import upickle.default.{ReadWriter, macroRW} +import upickle.default.{ReadWriter, macroRW, readwriter} -/** - * Specification for generating source file for a Mill build package. - * @param segments Folder path relative to the workspace, where the build file is located. - * @param module Root module of this package. - */ -case class PackageSpec(segments: Seq[String], module: ModuleSpec) +case class PackageSpec(moduleDir: os.SubPath, module: ModuleSpec) object PackageSpec { + private implicit val rwSubPath: ReadWriter[os.SubPath] = + readwriter[String].bimap(_.toString, os.SubPath(_)) implicit val rw: ReadWriter[PackageSpec] = macroRW + + def root(dir: os.SubPath, children: Seq[ModuleSpec] = Nil): PackageSpec = + apply(dir, ModuleSpec(dir.lastOpt.getOrElse(os.pwd.last), children = children)) } diff --git a/libs/init/buildgen/src/mill/main/buildgen/BuildGen.scala b/libs/init/buildgen/src/mill/main/buildgen/BuildGen.scala new file mode 100644 index 000000000000..739954bfa148 --- /dev/null +++ b/libs/init/buildgen/src/mill/main/buildgen/BuildGen.scala @@ -0,0 +1,586 @@ +package mill.main.buildgen + +import mill.constants.CodeGenConstants.rootModuleAlias +import mill.constants.OutFiles.millBuild +import mill.init.Util.buildFiles +import mill.main.buildgen.ModuleSpec.* +import mill.util.BuildInfo.millVersion +import pprint.Util.literalize + +import java.lang.System.lineSeparator + +object BuildGen { + + def withNamedDeps(packages: Seq[PackageSpec]): (Seq[(MvnDep, String)], Seq[PackageSpec]) = { + val refs = packages.iterator.flatMap(_.module.tree).flatMap { module => + (module +: module.test.toSeq).flatMap(module => + Seq( + module.mvnDeps, + module.compileMvnDeps, + module.runMvnDeps, + module.bomMvnDeps, + module.depManagement, + module.errorProneDeps, + module.scalacPluginMvnDeps + ) + ) + }.flatMap { values => + values.base ++ values.cross.flatMap(_._2) + }.distinct.filter(_.version.nonEmpty).toSeq.groupBy(_.name).flatMap { (name, deps) => + val ref = name.split("\\W") match { + case Array(head) => head + case parts => parts.tail.map(_.capitalize).mkString(parts.head, "", "") + } + deps match { + case Seq(dep) => Seq((dep, ref)) + case _ => deps.sortBy(_.toString).zipWithIndex.map((dep, i) => (dep, s"`$ref#$i`")) + } + } + val lookup = refs.lift.andThen(_.map(ref => s"Deps.$ref")) + + def updateDeps(values: Values[MvnDep]) = values.copy( + base = values.base.map(dep => dep.copy(ref = lookup(dep))), + cross = values.cross.map((k, v) => (k, v.map(dep => dep.copy(ref = lookup(dep))))) + ) + def updateModule0(module: ModuleSpec) = { + import module.* + module.copy( + mvnDeps = updateDeps(mvnDeps), + compileMvnDeps = updateDeps(compileMvnDeps), + runMvnDeps = updateDeps(runMvnDeps), + bomMvnDeps = updateDeps(bomMvnDeps), + depManagement = updateDeps(depManagement), + errorProneDeps = updateDeps(errorProneDeps), + scalacPluginMvnDeps = updateDeps(scalacPluginMvnDeps) + ) + } + def updateModule(module: ModuleSpec): ModuleSpec = updateModule0(module.copy( + imports = "import millbuild.Deps" +: module.imports, + test = module.test.map(updateModule0), + children = module.children.map(updateModule) + )) + val packages0 = for (pkg <- packages) yield pkg.copy(module = updateModule(pkg.module)) + (refs.toSeq, packages0) + } + + def withBaseModule( + packages: Seq[PackageSpec], + testSupertype: String, + moduleHierarchy: String* + ): Option[(ModuleSpec, Seq[PackageSpec])] = { + def parentValue[A](a: Value[A], b: Value[A]) = Value( + if (a.base == b.base) a.base else None, + a.cross.intersect(b.cross) + ) + def parentValues[A](a: Values[A], b: Values[A]) = Values( + a.extend && b.extend, + a.base.intersect(b.base), + (a.cross ++ b.cross).groupMapReduce(_._1)(_._2)(_.intersect(_)).toSeq.filter(_._2.nonEmpty) + ) + def parentModule0(a: ModuleSpec, b: ModuleSpec, defaultSupertypes: Seq[String]) = ModuleSpec( + name = "", + imports = (a.imports ++ b.imports).distinct, + supertypes = a.supertypes.intersect(b.supertypes) match { + case Nil => defaultSupertypes + case seq => seq + }, + mixins = if (a.supertypes == b.supertypes && a.mixins == b.mixins) a.mixins else Nil, + repositories = parentValues(a.repositories, b.repositories), + forkArgs = parentValues(a.forkArgs, b.forkArgs), + forkWorkingDir = parentValue(a.forkWorkingDir, b.forkWorkingDir), + mvnDeps = parentValues(a.mvnDeps, b.mvnDeps), + compileMvnDeps = parentValues(a.compileMvnDeps, b.compileMvnDeps), + runMvnDeps = parentValues(a.runMvnDeps, b.runMvnDeps), + bomMvnDeps = parentValues(a.bomMvnDeps, b.bomMvnDeps), + depManagement = parentValues(a.depManagement, b.depManagement), + javacOptions = parentValues(a.javacOptions, b.javacOptions), + sourcesFolders = parentValues(a.sourcesFolders, b.sourcesFolders), + sources = parentValues(a.sources, b.sources), + resources = parentValues(a.resources, b.resources), + artifactName = parentValue(a.artifactName, b.artifactName), + pomPackagingType = parentValue(a.pomPackagingType, b.pomPackagingType), + pomParentProject = parentValue(a.pomParentProject, b.pomParentProject), + pomSettings = parentValue(a.pomSettings, b.pomSettings), + publishVersion = parentValue(a.publishVersion, b.publishVersion), + versionScheme = parentValue(a.versionScheme, b.versionScheme), + publishProperties = parentValues(a.publishProperties, b.publishProperties), + errorProneVersion = parentValue(a.errorProneVersion, b.errorProneVersion), + errorProneDeps = parentValues(a.errorProneDeps, b.errorProneDeps), + errorProneOptions = parentValues(a.errorProneOptions, b.errorProneOptions), + errorProneJavacEnableOptions = + parentValues(a.errorProneJavacEnableOptions, b.errorProneJavacEnableOptions), + scalaVersion = parentValue(a.scalaVersion, b.scalaVersion), + scalacOptions = parentValues(a.scalacOptions, b.scalacOptions), + scalacPluginMvnDeps = parentValues(a.scalacPluginMvnDeps, b.scalacPluginMvnDeps), + scalaJSVersion = parentValue(a.scalaJSVersion, b.scalaJSVersion), + moduleKind = parentValue(a.moduleKind, b.moduleKind), + scalaNativeVersion = parentValue(a.scalaNativeVersion, b.scalaNativeVersion), + sourcesRootFolders = parentValues(a.sourcesRootFolders, b.sourcesRootFolders), + testParallelism = parentValue(a.testParallelism, b.testParallelism), + testSandboxWorkingDir = parentValue(a.testSandboxWorkingDir, b.testSandboxWorkingDir) + ) + def parentModule(a: ModuleSpec, b: ModuleSpec) = + parentModule0(a, b, moduleHierarchy.take(1)).copy( + test = (a.test.toSeq ++ b.test.toSeq).reduceOption(parentModule0(_, _, Seq(testSupertype))) + ) + def extendValue[A](a: Value[A], parent: Value[A]) = Value( + if (a.base == parent.base) None else a.base, + a.cross.diff(parent.cross) + ) + def extendValues[A](a: Values[A], parent: Values[A]) = Values( + a.extend || parent.base.nonEmpty || parent.cross.nonEmpty, + a.base.diff(parent.base), + a.cross.map((k, a) => + parent.cross.collectFirst { + case (`k`, b) => (k, a.diff(b)) + }.getOrElse((k, a)) + ).filter(_._2.nonEmpty) + ) + def extendModule0(a: ModuleSpec, parent: ModuleSpec): ModuleSpec = a.copy( + supertypes = (parent.name +: a.supertypes).diff(parent.supertypes), + mixins = if (a.mixins == parent.mixins) Nil else a.mixins, + repositories = extendValues(a.repositories, parent.repositories), + forkArgs = extendValues(a.forkArgs, parent.forkArgs), + forkWorkingDir = extendValue(a.forkWorkingDir, parent.forkWorkingDir), + mvnDeps = extendValues(a.mvnDeps, parent.mvnDeps), + compileMvnDeps = extendValues(a.compileMvnDeps, parent.compileMvnDeps), + runMvnDeps = extendValues(a.runMvnDeps, parent.runMvnDeps), + bomMvnDeps = extendValues(a.bomMvnDeps, parent.bomMvnDeps), + depManagement = extendValues(a.depManagement, parent.depManagement), + javacOptions = extendValues(a.javacOptions, parent.javacOptions), + sourcesFolders = extendValues(a.sourcesFolders, parent.sourcesFolders), + sources = extendValues(a.sources, parent.sources), + resources = extendValues(a.resources, parent.resources), + artifactName = extendValue(a.artifactName, parent.artifactName), + pomPackagingType = extendValue(a.pomPackagingType, parent.pomPackagingType), + pomParentProject = extendValue(a.pomParentProject, parent.pomParentProject), + pomSettings = extendValue(a.pomSettings, parent.pomSettings), + publishVersion = extendValue(a.publishVersion, parent.publishVersion), + versionScheme = extendValue(a.versionScheme, parent.versionScheme), + publishProperties = extendValues(a.publishProperties, parent.publishProperties), + errorProneVersion = extendValue(a.errorProneVersion, parent.errorProneVersion), + errorProneDeps = extendValues(a.errorProneDeps, parent.errorProneDeps), + errorProneOptions = extendValues(a.errorProneOptions, parent.errorProneOptions), + errorProneJavacEnableOptions = + extendValues(a.errorProneJavacEnableOptions, parent.errorProneJavacEnableOptions), + scalaVersion = extendValue(a.scalaVersion, parent.scalaVersion), + scalacOptions = extendValues(a.scalacOptions, parent.scalacOptions), + scalacPluginMvnDeps = extendValues(a.scalacPluginMvnDeps, parent.scalacPluginMvnDeps), + scalaJSVersion = extendValue(a.scalaJSVersion, parent.scalaJSVersion), + moduleKind = extendValue(a.moduleKind, parent.moduleKind), + scalaNativeVersion = extendValue(a.scalaNativeVersion, parent.scalaNativeVersion), + sourcesRootFolders = extendValues(a.sourcesRootFolders, parent.sourcesRootFolders), + testParallelism = extendValue(a.testParallelism, parent.testParallelism), + testSandboxWorkingDir = extendValue(a.testSandboxWorkingDir, parent.testSandboxWorkingDir) + ) + def isChild(module: ModuleSpec) = module.supertypes.exists(moduleHierarchy.contains) + def extendModule(a: ModuleSpec, parent: ModuleSpec): ModuleSpec = { + val a0 = if (isChild(a)) extendModule0( + a.copy( + imports = s"import millbuild.${parent.name}" +: a.imports, + test = a.test.zip(parent.test).map(extendModule0) + ), + parent + ) + else a + a0.copy(children = a0.children.map(extendModule(_, parent))) + } + + val childModules = packages.flatMap(_.module.tree).filter(isChild) + Option.when(childModules.length > 1) { + val baseModule = childModules.reduce(parentModule) + val baseModule0 = baseModule.copy( + name = "ProjectBaseModule", + imports = baseModule.imports.diff(Seq("import millbuild.Deps")), + test = baseModule.test.map(_.copy(name = "Tests")) + ) + val packages0 = packages.map(pkg => pkg.copy(module = extendModule(pkg.module, baseModule0))) + (baseModule0, packages0) + } + } + + def writeBuildFiles( + packages: Seq[PackageSpec], + merge: Boolean = false, + depRefs: Seq[(MvnDep, String)] = Nil, + baseModule: Option[ModuleSpec] = None, + millJvmOpts: Seq[String] = Nil + ): Unit = { + val fullSpecs = fill(packages) + val finalSpecs = if (merge) Seq(merged(fullSpecs)) else fullSpecs.sortBy(_.moduleDir) + + val existingBuildFiles = buildFiles(os.pwd) + if (existingBuildFiles.nonEmpty) { + println("removing existing build files ...") + for (file <- existingBuildFiles) do os.remove(file) + } + + if (depRefs.nonEmpty) { + val file = os.sub / millBuild / "src/Deps.scala" + println(s"writing $file") + os.write(os.pwd / file, renderDepsObject(depRefs), createFolders = true) + } + for (spec <- baseModule) do { + val file = os.sub / millBuild / os.SubPath(s"src/${spec.name}.scala") + println(s"writing $file") + os.write(os.pwd / file, renderBaseModule(spec), createFolders = true) + } + val root +: nested = finalSpecs: @unchecked + val millJvmOptsLine = if (millJvmOpts.isEmpty) "" + else millJvmOpts.mkString("//| mill-jvm-opts: [\"", "\", \"", s"\"]$lineSeparator") + println("writing build.mill") + os.write( + os.pwd / "build.mill", + s"""//| mill-version: $millVersion + |//| mill-jvm-version: $millJvmVersion + |$millJvmOptsLine${renderPackage(root)} + |""".stripMargin + ) + for (spec <- nested) do { + val file = spec.moduleDir / "package.mill" + println(s"writing $file") + os.write(os.pwd / file, renderPackage(spec)) + } + } + + private def fill(packages: Seq[PackageSpec]): Seq[PackageSpec] = { + def recurse(dir: os.SubPath): Seq[PackageSpec] = { + val root = packages.find(_.moduleDir == dir).getOrElse(PackageSpec.root(dir)) + val nested = packages.collect { + case spec if spec.moduleDir.startsWith(dir) && spec.moduleDir != dir => + os.sub / spec.moduleDir.segments.take(dir.segments.length + 1) + }.distinct.flatMap(recurse) + root +: nested + } + recurse(os.sub) + } + + private def merged(packages: Seq[PackageSpec]): PackageSpec = { + val root +: nested = packages: @unchecked + def childPackages(rootDir: os.SubPath) = nested.filter(pkg => + pkg.moduleDir.startsWith(rootDir) && + pkg.moduleDir.segments.length == rootDir.segments.length + 1 + ) + def toModule(spec: PackageSpec): ModuleSpec = { + val children = childPackages(spec.moduleDir).map(toModule) + spec.module.copy(children = spec.module.children ++ children) + } + root.copy(module = + root.module.copy(children = + root.module.children ++ childPackages(root.moduleDir).map(toModule) + ) + ) + } + + private def millJvmVersion = { + val file = os.pwd / ".mill-jvm-version" + if (os.exists(file)) os.read(file) else "system" + } + + private val ScalaIdentifier = "^[a-zA-Z_][\\w]*$".r + private def scalaIdentifier(s: String) = if (ScalaIdentifier.matches(s)) s else s"`$s`" + + private def renderDepsObject(depRefs: Seq[(MvnDep, String)]) = { + s"""package millbuild + |import mill.javalib.* + |object Deps { + | + | ${depRefs.sortBy(_._2).map((d, n) => s"val $n = $d").mkString(lineSeparator)} + |}""".stripMargin + } + + private def renderBaseModule(module: ModuleSpec) = { + import module.* + s"""package millbuild + |${renderImports(module)} + |trait $name ${renderExtendsClause(supertypes ++ mixins)} { + | + | ${renderModuleBody(module)} + | + | ${test.fold("")(renderTestModule("trait", _))} + |}""".stripMargin + } + + private def renderImports(module: ModuleSpec) = { + val imports = module.tree.flatMap(_.imports) + ("import mill.*" +: imports).distinct.sorted.mkString(lineSeparator) + } + + private def renderExtendsClause(supertypes: Seq[String]) = { + if (supertypes.isEmpty) "extends Module" + else supertypes.mkString("extends ", ", ", "") + } + + private def renderModuleBody(module: ModuleSpec) = { + import module.* + s"""${render("moduleDeps", moduleDeps, encodeModuleDeps, isTask = false)} + | + |${render("compileModuleDeps", compileModuleDeps, encodeModuleDeps, isTask = false)} + | + |${render("runModuleDeps", runModuleDeps, encodeModuleDeps, isTask = false)} + | + |${render("bomModuleDeps", bomModuleDeps, encodeModuleDeps, isTask = false)} + | + |${render("mvnDeps", mvnDeps, encodeMvnDeps)} + | + |${render("compileMvnDeps", compileMvnDeps, encodeMvnDeps)} + | + |${render("runMvnDeps", runMvnDeps, encodeMvnDeps)} + | + |${render("bomMvnDeps", bomMvnDeps, encodeMvnDeps)} + | + |${render("depManagement", depManagement, encodeMvnDeps)} + | + |${render("scalaJSVersion", scalaJSVersion, encodeString)} + | + |${render("moduleKind", moduleKind, identity[String])} + | + |${render("scalaNativeVersion", scalaNativeVersion, encodeString)} + | + |${render("scalaVersion", scalaVersion, encodeString)} + | + |${render("scalacOptions", scalacOptions, encodeScalacOptions)} + | + |${render("scalacPluginMvnDeps", scalacPluginMvnDeps, encodeMvnDeps)} + | + |${render("javacOptions", javacOptions, encodeOpts)} + | + |${render("sourcesRootFolders", sourcesRootFolders, encodeStrings, isTask = false)} + | + |${render("sourcesFolders", sourcesFolders, encodeStrings, isTask = false)} + | + |${renderSources("sources", sources)} + | + |${renderSources("resources", resources)} + | + |${render("forkArgs", forkArgs, encodeOpts)} + | + |${render("forkWorkingDir", forkWorkingDir, encodeRelPath("moduleDir", _))} + | + |${render("errorProneVersion", errorProneVersion, encodeString)} + | + |${render("errorProneDeps", errorProneDeps, encodeMvnDeps)} + | + |${render("errorProneOptions", errorProneOptions, encodeStrings)} + | + |${render("errorProneJavacEnableOptions", errorProneJavacEnableOptions, encodeOpts)} + | + |${render("artifactName", artifactName, encodeString)} + | + |${render("pomPackagingType", pomPackagingType, encodeString)} + | + |${render("pomParentProject", pomParentProject, a => s"Some(${encodeArtifact(a)})")} + | + |${render("pomSettings", pomSettings, encodePomSettings)} + | + |${render("publishVersion", publishVersion, encodeString)} + | + |${render("versionScheme", versionScheme, a => s"Some($a)")} + | + |${render("publishProperties", publishProperties, encodeProperties)} + | + |${render("testParallelism", testParallelism, _.toString)} + | + |${render("testSandboxWorkingDir", testSandboxWorkingDir, _.toString)} + | + |${render("repositories", repositories, encodeStrings)} + |""".stripMargin + } + + private def renderTestModule(scalaType: String, spec: ModuleSpec) = { + import spec.* + s"""$scalaType $name ${renderExtendsClause(supertypes ++ mixins)} { + | + | ${renderModuleBody(spec)} + |}""".stripMargin + } + + private def renderPackage(spec: PackageSpec) = { + import spec.* + val namespace = (rootModuleAlias +: moduleDir.segments.map(scalaIdentifier)).mkString(".") + s"""package $namespace + |${renderImports(module)} + |${renderModule(module, isPackageRoot = true)} + |""".stripMargin + } + + private def renderModule(spec: ModuleSpec, isPackageRoot: Boolean = false): String = { + import spec.* + val name0 = if (isPackageRoot) "`package`" else scalaIdentifier(name) + val extendsClause = renderExtendsClause(supertypes ++ mixins) + val typeDeclaration = if (crossKeys.isEmpty) s"object $name0 $extendsClause" + else { + val crossTraitName = scalaIdentifier(name.split("\\W") match { + case Array("") => s"`${name}Module`" + case parts => parts.map(_.capitalize).mkString("", "", "Module") + }) + val crossExtendsClause = + crossKeys.sorted.mkString(s"extends Cross[$crossTraitName](\"", "\", \"", "\")") + s"""object $name0 $crossExtendsClause + |trait $crossTraitName $extendsClause""".stripMargin + } + val aliasPart = { if (children.exists(_.useParentModuleDir)) " outer => " else "" } + val renderModuleDir = if (useParentModuleDir) "def moduleDir = outer.moduleDir" else "" + + s"""$typeDeclaration {$aliasPart + | + | $renderModuleDir + | + | ${renderModuleBody(spec)} + | + | ${test.fold("")(renderTestModule("object", _))} + | + | ${children.sortBy(_.name).map(renderModule(_)).mkString(lineSeparator * 2)} + |}""".stripMargin + } + + private def render[A](name: String, value: Value[A], encode: A => String): String = { + import value.* + if (cross.isEmpty) base.fold("")(a => s"def $name = ${encode(a)}") + else encodeCrossMatch(s"def $name = ", cross, base, encode, "") + } + private def render[A]( + name: String, + values: Values[A], + encode: Seq[A] => String, + isTask: Boolean = true + ): String = { + import values.* + if (base.isEmpty && cross.isEmpty) "" + else { + var stmt = s"def $name = " + if (extend) { + stmt += s"super.$name" + if (isTask) stmt += "()" + } + if (base.nonEmpty) { + if (extend) stmt += " ++ " + stmt += encode(base) + } + if (cross.isEmpty) stmt + else { + val stmtEnd = if (extend || base.nonEmpty) { + stmt += " ++ (" + ")" + } else "" + encodeCrossMatch(stmt, cross, Some(Nil), encode, stmtEnd) + } + } + } + private def renderSources(name: String, values: Values[os.RelPath]) = { + def encodeSeq(rels: Seq[os.RelPath]) = rels.map(encodeRelPath("os.rel", _)) + .mkString("Seq(", ", ", ")") + def encode(rels: Seq[os.RelPath]) = rels.map(rel => + if (rel.ups == 0) + if (rel.segments.isEmpty) "os.sub" else rel.segments.mkString("\"", "/", "\"") + else encodeRelPath("os.rel", rel) + ).mkString("Task.Sources(", ", ", ")") + import values.* + if (base.isEmpty && cross.isEmpty) "" + else if (extend) { + var stmt = s"def $name = super.$name()" + if (base.nonEmpty) { + val task = s"custom${name.capitalize}" + stmt = + s"""def $task = ${encode(base)} + |$stmt ++ $task()""".stripMargin + } + if (cross.nonEmpty) { + val task = s"customCross${name.capitalize}" + stmt = + s"""def $task = ${encodeCrossMatch("Task.Sources((", cross, Some(Nil), encodeSeq, ")*)")} + |$stmt ++ $task()""".stripMargin + } + stmt + } else if (cross.isEmpty) s"def $name = ${encode(base)}" + else { + var stmt = s"def $name = Task.Sources((" + val stmtEnd = if (base.isEmpty) ")*)" + else { + stmt += encodeSeq(base) + stmt += " ++ (" + "))*)" + } + encodeCrossMatch(stmt, cross, Some(Nil), encodeSeq, stmtEnd) + } + } + + private def encodeCrossMatch[A]( + stmtStart: String, + crossValues: Seq[(String, A)], + defaultValue: Option[A], + encode: A => String, + stmtEnd: String + ) = { + val defaultCase = defaultValue.fold("")(a => s"case _ => ${encode(a)}") + crossValues.groupMap(_._2)(_._1).map { (a, ks) => + val pattern = ks.sorted.mkString("\"", "\" | \"", "\"") + s"case $pattern => ${encode(a)}" + }.toSeq.sorted.mkString( + s"""${stmtStart}crossScalaVersion match { + | """.stripMargin, + """ + | """.stripMargin, + s""" + | $defaultCase + |}$stmtEnd""".stripMargin + ) + } + + private def encodeString[A](a: A) = s"\"$a\"" + private def encodeRelPath(root: String, a: os.RelPath) = { + val ups = " / os.up" * a.ups + val segments = if (a.segments.isEmpty) "" else a.segments.mkString(" / \"", "/", "\"") + s"$root$ups$segments" + } + private def encodeMvnDep(a: MvnDep) = a.ref.getOrElse(a.toString) + private def encodeModuleDep(a: ModuleDep) = { + import a.* + val segments = rootModuleAlias +: moduleDir.segments.map(scalaIdentifier) + val suffix = crossSuffix.getOrElse("") + nestedModule.fold("")("." + _) + segments.mkString("", ".", suffix) + } + private def encodeArtifact(a: Artifact) = { + import a.* + s"""Artifact("$group", "$id", "$version")""" + } + private def encodePomSettings(a: PomSettings) = { + def encodeOpt(o: Option[String]) = o.fold("None")(s => s"Some(\"$s\")") + def encodeLicense(a: License) = { + import a.* + s"""License("$id", "$name", "$url", $isOsiApproved, $isFsfLibre, "$distribution")""" + } + def encodeVersionControl(a: VersionControl) = { + import a.* + val browsableRepository0 = encodeOpt(browsableRepository) + val connection0 = encodeOpt(connection) + val devloperConnection0 = encodeOpt(developerConnection) + val tag0 = encodeOpt(tag) + s"VersionControl($browsableRepository0, $connection0, $devloperConnection0, $tag0)" + } + def encodeDeveloper(a: Developer) = { + import a.* + val organization0 = encodeOpt(organization) + val organizationUrl0 = encodeOpt(organizationUrl) + s"""Developer("$id", "$name", "$url", $organization0, $organizationUrl0)""" + } + import a.* + val description0 = literalize(description) + val licenses0 = licenses.map(encodeLicense).mkString("Seq(", ",", ")") + val versionControl0 = encodeVersionControl(versionControl) + val developers0 = developers.map(encodeDeveloper).mkString("Seq(", ",", ")") + s"""PomSettings($description0, "$organization", "$url", $licenses0, $versionControl0, $developers0)""" + } + + private def encodeStrings[A](as: Seq[A]) = as.mkString("Seq(\"", "\", \"", "\")") + private def encodeMvnDeps(as: Seq[MvnDep]) = + as.iterator.map(encodeMvnDep).mkString("Seq(", ", ", ")") + private def encodeModuleDeps(as: Seq[ModuleDep]) = + as.iterator.map(encodeModuleDep).mkString("Seq(", ", ", ")") + private def encodeOpts(as: Seq[Opt]) = + as.iterator.flatMap(_.group).mkString("Seq(\"", "\", \"", "\")") + private def encodeScalacOptions(as: Seq[Opt]) = + as.iterator.flatMap(_.group).map(literalize(_)).mkString("Seq(", ", ", ")") + private def encodeProperties(as: Seq[(String, String)]) = + as.iterator.map((k, v) => s"(\"$k\", ${literalize(v)})").mkString("Map(", ", ", ")") +} diff --git a/libs/init/buildgen/src/mill/main/buildgen/BuildSpec.scala b/libs/init/buildgen/src/mill/main/buildgen/BuildSpec.scala deleted file mode 100644 index 3fe33bd1ad76..000000000000 --- a/libs/init/buildgen/src/mill/main/buildgen/BuildSpec.scala +++ /dev/null @@ -1,151 +0,0 @@ -package mill.main.buildgen - -import mill.util.BuildInfo.millVersion - -/** - * Specification for generating source files for a Mill build. - * @see [[BuildSpec.fill]] - */ -case class BuildSpec( - packages: Seq[PackageSpec], - metaBuild: Option[BuildSpec.MetaSpec] = None, - millVersion: String = millVersion, - millJvmOpts: Seq[String] = Nil -) { - - /** - * Moves modules in nested packages to the root module hierarchy. - * This eliminates nested build files without modifying the build semantics. - */ - def merged: BuildSpec = { - def recurse(pkg: PackageSpec): ModuleSpec = { - val mergedModules = packages.collect { - case subPkg - if subPkg.segments.startsWith(pkg.segments) && - subPkg.segments.length == pkg.segments.length + 1 => - recurse(subPkg) - } - import pkg.* - module.copy( - name = segments.last, - nestedModules = module.nestedModules ++ mergedModules - ) - } - - val mergedModules = packages.collect { - case pkg if pkg.segments.length == 1 => recurse(pkg) - } - var rootPackage = packages.head - rootPackage = rootPackage.copy(module = - rootPackage.module.copy(nestedModules = - rootPackage.module.nestedModules ++ mergedModules - ) - ) - copy(packages = Seq(rootPackage)) - } - - /** - * Derives and attaches a meta-build to this build. - * - * At most, 2 base modules are created for sharing settings amongst build modules. - * The first one is derived from all non-test modules, if there are more than one such modules. - * The second one is derived from all publishable modules, if the first one has no publish data. - */ - def withDefaultMetaBuild: BuildSpec = { - def abstractedModule(module1: ModuleSpec, module2: ModuleSpec) = module1.copy( - supertypes = module1.supertypes.intersect(module2.supertypes), - mixins = ModuleConfig.abstractedValue(module1.mixins, module2.mixins, Nil), - configs = ModuleConfig.abstractedConfigs(module1.configs, module2.configs), - crossConfigs = module1.crossConfigs.flatMap((cross, configs1) => - module2.crossConfigs.collectFirst { - case (`cross`, configs2) => - (cross, ModuleConfig.abstractedConfigs(configs1, configs2)) - } - ), - nestedModules = Nil - ) - def inheritedModule(self: ModuleSpec, base: ModuleSpec) = self.copy( - supertypes = self.supertypes.diff(base.supertypes) :+ base.name, - mixins = ModuleConfig.inheritedValue(self.mixins, base.mixins, Nil), - configs = ModuleConfig.inheritedConfigs(self.configs, base.configs), - crossConfigs = self.crossConfigs.map((cross, selfConfigs) => - base.crossConfigs.collectFirst { - case (`cross`, baseConfigs) => - (cross, ModuleConfig.inheritedConfigs(selfConfigs, baseConfigs)) - }.getOrElse((cross, selfConfigs)) - ) - ) - - var packages0 = packages - def baseModuleFor(criteria: ModuleSpec => Boolean, suffix: String) = { - val modules = packages0 - .flatMap(_.module.sequence) - .filter(module => module.configs.nonEmpty && criteria(module)) - Option.when(modules.length > 1)(modules.reduce(abstractedModule)).collect { - case baseModule if baseModule.configs.nonEmpty => - val pwdName = os.pwd.last - val moduleName = pwdName.dropWhile(!_.isLetter).split("\\W") match { - case Array("") => "Project" + suffix - case parts => parts.map(_.capitalize).mkString("", "", suffix) - } - val moduleSupertypes = baseModule.supertypes ++ Option.when( - baseModule.crossConfigs.nonEmpty && baseModule.isScalaModule - )("CrossScalaModule") - val baseModule0 = baseModule.copy( - name = moduleName, - supertypes = moduleSupertypes - ) - packages0 = packages0.map { pkg => - pkg.copy(module = - pkg.module.transform { module => - if (module.configs.isEmpty || !criteria(module)) module - else inheritedModule(module, baseModule0) - } - ) - } - baseModule0 - } - } - - val baseModule = baseModuleFor(!_.isTestModule, "BaseModule") - val basePublishModule = - if (baseModule.exists(_.isPublishModule)) None - else baseModuleFor(_.isPublishModule, "PublishModule") - val baseModules = baseModule.toSeq ++ basePublishModule - copy(packages0, Some(BuildSpec.MetaSpec(baseModules))) - } -} -object BuildSpec { - - /** - * Specification for generating source files for a Mill meta-build. - * @param baseModules Module supertypes, containing shared settings, for build modules. - * @param depsObjectName Name of the Scala object that defines constants for dependencies. - * @param rootModuleName Name of the module at the root of the meta-build tree. - */ - case class MetaSpec( - baseModules: Seq[ModuleSpec] = Nil, - depsObjectName: String = "Deps", - rootModuleName: String = "millbuild" - ) - - /** - * Returns a build specification by adding "unit" packages for any missing path segments. - */ - def fill(packages: Seq[PackageSpec]): BuildSpec = { - def unitPackage(dir: Seq[String]) = { - val module = ModuleSpec(dir.lastOption.getOrElse(os.pwd.last)) - PackageSpec(dir, module) - } - def recurse(dir: Seq[String]): Seq[PackageSpec] = { - val rootPackage = packages.find(_.segments == dir).getOrElse(unitPackage(dir)) - val nestedPackages = packages.map(_.segments).collect { - case segments if segments.startsWith(dir) && segments.length > dir.length => - segments.take(dir.length + 1) - }.distinct.flatMap(recurse).toSeq - rootPackage +: nestedPackages - } - - apply(packages = recurse(Seq.empty)) - } -} diff --git a/libs/init/buildgen/src/mill/main/buildgen/BuildWriter.scala b/libs/init/buildgen/src/mill/main/buildgen/BuildWriter.scala deleted file mode 100644 index 5cee9a9d52a6..000000000000 --- a/libs/init/buildgen/src/mill/main/buildgen/BuildWriter.scala +++ /dev/null @@ -1,607 +0,0 @@ -package mill.main.buildgen - -import mill.constants.CodeGenConstants.rootModuleAlias -import mill.constants.OutFiles.millBuild -import mill.init.Util.buildFiles -import mill.internal.Util.backtickWrap -import mill.main.buildgen.ModuleConfig.* -import pprint.Util.literalize - -import scala.collection.mutable -import scala.math.Ordered.orderingToOrdered -import scala.reflect.TypeTest - -class BuildWriter(build: BuildSpec, renderCrossValueInTask: String = "crossValue") { - - private val mvnDepRef: mutable.Map[MvnDep, String] = mutable.Map.empty - - def writeFiles(): Unit = { - val toRemove = buildFiles(os.pwd) - if (toRemove.nonEmpty) { - println("removing existing Mill build files") - toRemove.foreach(os.remove.apply) - } - - import build.* - val rootPackage +: nestedPackages = packages.toSeq: @unchecked - val rootBuildFile = os.sub / "build.mill" - println(s"writing Mill build file to $rootBuildFile") - os.write(os.pwd / rootBuildFile, renderRootPackage(rootPackage)) - nestedPackages.foreach { pkg => - val buildFile = os.sub / pkg.segments / "package.mill" - println(s"writing Mill build file to $buildFile") - os.write(os.pwd / buildFile, renderPackage(pkg)) - } - - metaBuild.foreach { meta => - val srcDir = os.sub / millBuild / "src" - os.makeDir.all(os.pwd / srcDir) - import meta.* - baseModules.foreach { baseModule => - val srcFile = srcDir / s"${baseModule.name}.scala" - println(s"writing Mill meta-build file to $srcFile") - os.write(os.pwd / srcFile, renderBaseModule(rootModuleName, baseModule)) - } - val depsSrcFile = srcDir / s"$depsObjectName.scala" - println(s"writing Mill meta-build file to $depsSrcFile") - os.write(os.pwd / depsSrcFile, renderDepsObject(rootModuleName, depsObjectName)) - } - - println( - s"NOTE: It is recommended to set `mill-jvm-version` in the header section of the root $rootBuildFile file." - ) - } - - private def renderRootPackage(pkg: PackageSpec) = { - import build.* - val header = Seq("mill-version: " + millVersion) ++ - Option.when(millJvmOpts.nonEmpty) { - "mill-jvm-opts: " + millJvmOpts.map(literalize(_)).mkString("[", ", ", "]") - } - s"""${renderLines(header.map("//| " + _))} - |${renderPackage(pkg)} - |""".stripMargin - } - - private def renderPackage(pkg: PackageSpec) = { - import pkg.* - s"""package ${(rootModuleAlias +: segments.map(backtickWrap)).mkString(".")} - | - |${renderModuleImports(module)} - | - |${renderModule(module, isPackageRoot = true)} - |""".stripMargin - } - - private def renderBaseModule(packageName: String, baseModule: ModuleSpec) = { - import baseModule.* - s"""package $packageName - | - |${renderBaseModuleImports(baseModule)} - | - |trait $name ${renderExtendsClause(supertypes ++ mixins)} { - | - | ${renderModuleConfigs(configs, crossConfigs)} - |}""".stripMargin - } - - private def renderDepsObject(packageName: String, name: String) = { - s"""package $packageName - | - |import mill.javalib.* - | - |object $name { - | - |${renderLines(mvnDepRef.toSeq.sortBy(_._2).map((dep, ref) => - s"val ${backtickWrap(ref)} = $dep" - ))} - |}""".stripMargin - } - - private def renderBaseModuleImports(baseModule: ModuleSpec) = { - val wildcards = mutable.SortedSet.empty[String] - import baseModule.* - wildcards ++= configs.flatMap(imports) - renderLines(wildcards.map(s => s"import $s.*")) - } - - private def renderModuleImports(module: ModuleSpec) = { - val wildcards = mutable.SortedSet.empty[String] - for spec <- module.sequence do { - import spec.* - if (supertypes.isEmpty || crossConfigs.nonEmpty) wildcards += "mill" - if (configs.nonEmpty) wildcards ++= build.metaBuild.map(_.rootModuleName) - wildcards ++= configs.flatMap(imports) - } - renderLines(wildcards.map(s => s"import $s.*")) - } - - private def imports(config: ModuleConfig): Set[String] = { - config match { - case _: PublishModule => Set("mill.javalib", "mill.javalib.publish") - case _: ErrorProneModule => Set("mill.javalib", "mill.javalib.errorprone") - case _: ScalaModule => Set("mill.scalalib") - case _: ScalaJSModule => Set("mill.scalalib", "mill.scalajslib", "mill.scalajslib.api") - case _: ScalaNativeModule => Set("mill.scalalib", "mill.scalanativelib") - case _: SbtPlatformModule => Set("mill.scalalib") - case _ => Set("mill.javalib") - } - } - - private def renderModule(module: ModuleSpec, isPackageRoot: Boolean = false): String = { - import module.* - s"""${renderModuleDeclaration(module, isPackageRoot)} { - | - | ${renderModuleConfigs(configs, crossConfigs)} - | - | ${renderBlocks(nestedModules.sortBy(_.name).map(renderModule(_))*)} - |}""".stripMargin - } - - private def renderModuleDeclaration(module: ModuleSpec, isPackageRoot: Boolean) = { - import module.* - val objectName = backtickWrap(if (isPackageRoot) "package" else name) - if (crossConfigs.isEmpty || isTestModule) { - val objectExtends = renderExtendsClause(supertypes ++ mixins) - s"object $objectName $objectExtends" - } else { - val traitName = name.split("\\W") match { - case Array("") => backtickWrap(name + "Module") - case parts => parts.map(_.capitalize).mkString("", "", "Module") - } - val crossValues = crossConfigs.map((v, _) => literalize(v)).sorted - val objectExtends = s"extends Cross[$traitName](${crossValues.mkString(", ")})" - val traitExtends = renderExtendsClause(supertypes ++ mixins) - s"""object $objectName $objectExtends - |trait $traitName $traitExtends""".stripMargin - } - } - - private def renderExtendsClause(supertypes: Seq[String]) = supertypes match { - case Nil => "extends Module" - case head +: tail => s" extends $head" + tail.map(" with " + _).mkString - } - - private def renderModuleConfigs( - configs: Seq[ModuleConfig], - crossConfigs: Seq[(String, Seq[ModuleConfig])] - ) = { - def crossConfig[T <: ModuleConfig](using T: TypeTest[ModuleConfig, T]) = - crossConfigs.flatMap((k, xs) => - xs.collectFirst { - case t: T => (k, t) - } - ) - renderLines(configs.map { - case config: CoursierModule => renderCoursierModule(config, crossConfig) - case config: JavaHomeModule => renderJavaHomeModule(config, crossConfig) - case config: RunModule => renderRunModule(config, crossConfig) - case config: JavaModule => renderJavaModule(config, crossConfig) - case config: PublishModule => renderPublishModule(config, crossConfig) - case config: ErrorProneModule => renderErrorProneModule(config, crossConfig) - case config: ScalaModule => renderScalaModule(config, crossConfig) - case config: ScalaJSModule => renderScalaJSModule(config, crossConfig) - case config: ScalaNativeModule => renderScalaNativeModule(config, crossConfig) - case config: SbtPlatformModule => renderSbtPlatformModule(config, crossConfig) - case config: TestModule => renderTestModule(config, crossConfig) - }) - } - - private def renderCoursierModule( - config: CoursierModule, - crossConfigs: Seq[(String, CoursierModule)] - ) = { - renderBlocks(renderTaskAsSeq( - "repositories", - config.repositories, - crossConfigs.map((k, v) => (k, v.repositories)), - literalize(_) - )) - } - - private def renderJavaHomeModule( - config: JavaHomeModule, - crossConfigs: Seq[(String, JavaHomeModule)] - ) = { - renderBlocks(renderTask( - "jvmId", - config.jvmId, - crossConfigs.map((k, v) => (k, v.jvmId)), - literalize(_) - )) - } - - private def renderRunModule(config: RunModule, crossConfigs: Seq[(String, RunModule)]) = { - renderBlocks(renderTask( - "forkWorkingDir", - config.forkWorkingDir, - crossConfigs.map((k, v) => (k, v.forkWorkingDir)), - identity - )) - } - - private def renderJavaModule(config: JavaModule, crossConfigs: Seq[(String, JavaModule)]) = { - def cross[A](f: JavaModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderTaskAsSeq("mvnDeps", mvnDeps, cross(_.mvnDeps), renderMvnDep), - renderTaskAsSeq("compileMvnDeps", compileMvnDeps, cross(_.compileMvnDeps), renderMvnDep), - renderTaskAsSeq("runMvnDeps", runMvnDeps, cross(_.runMvnDeps), renderMvnDep), - renderTaskAsSeq("bomMvnDeps", bomMvnDeps, cross(_.bomMvnDeps), renderMvnDep), - renderMemberAsSeq("moduleDeps", moduleDeps, cross(_.moduleDeps), renderModuleDep), - renderMemberAsSeq( - "compileModuleDeps", - compileModuleDeps, - cross(_.compileModuleDeps), - renderModuleDep - ), - renderMemberAsSeq("runModuleDeps", runModuleDeps, cross(_.runModuleDeps), renderModuleDep), - renderTaskAsSeq("javacOptions", javacOptions, cross(_.javacOptions), literalize(_)), - renderTask("artifactName", artifactName, cross(_.artifactName), literalize(_)) - ) - } - - private def renderPublishModule( - config: PublishModule, - crossConfigs: Seq[(String, PublishModule)] - ) = { - def cross[A](f: PublishModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderMember("pomPackagingType", pomPackagingType, cross(_.pomPackagingType), literalize(_)), - renderTask( - "pomParentProject", - pomParentProject, - cross(_.pomParentProject), - v => s"Some(${renderArtifact(v)})" - ), - renderTask("pomSettings", pomSettings, cross(_.pomSettings), renderPomSettings), - renderTask("publishVersion", publishVersion, cross(_.publishVersion), literalize(_)), - renderTask( - "versionScheme", - versionScheme, - cross(_.versionScheme), - v => s"Some(${renderVersionScheme(v)})" - ), - renderTaskAsMap( - "publishProperties", - publishProperties, - cross(_.publishProperties), - literalize(_), - literalize(_) - ) - ) - } - - private def renderErrorProneModule( - config: ErrorProneModule, - crossConfigs: Seq[(String, ErrorProneModule)] - ) = { - def cross[A](f: ErrorProneModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderTask("errorProneVersion", errorProneVersion, cross(_.errorProneVersion), literalize(_)), - renderTaskAsSeq( - "errorProneOptions", - errorProneOptions, - cross(_.errorProneOptions), - literalize(_) - ), - renderTaskAsSeq( - "errorProneJavacEnableOptions", - errorProneJavacEnableOptions, - cross(_.errorProneJavacEnableOptions), - literalize(_) - ), - renderTaskAsSeq("errorProneDeps", errorProneDeps, cross(_.errorProneDeps), renderMvnDep) - ) - } - - private def renderScalaModule(config: ScalaModule, crossConfigs: Seq[(String, ScalaModule)]) = { - def cross[A](f: ScalaModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderTask("scalaVersion", scalaVersion, cross(_.scalaVersion), literalize(_)), - renderTaskAsSeq("scalacOptions", scalacOptions, cross(_.scalacOptions), literalize(_)), - renderTaskAsSeq( - "scalacPluginMvnDeps", - scalacPluginMvnDeps, - cross(_.scalacPluginMvnDeps), - renderMvnDep - ) - ) - } - - private def renderScalaJSModule( - config: ScalaJSModule, - crossConfigs: Seq[(String, ScalaJSModule)] - ) = { - def cross[A](f: ScalaJSModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderTask("scalaJSVersion", scalaJSVersion, cross(_.scalaJSVersion), literalize(_)), - renderTask("moduleKind", moduleKind, cross(_.moduleKind), "ModuleKind." + _) - ) - } - - private def renderScalaNativeModule( - config: ScalaNativeModule, - crossConfigs: Seq[(String, ScalaNativeModule)] - ) = { - renderBlocks(renderTask( - "scalaNativeVersion", - config.scalaNativeVersion, - crossConfigs.map((k, v) => (k, v.scalaNativeVersion)), - literalize(_) - )) - } - - private def renderSbtPlatformModule( - config: SbtPlatformModule, - crossConfigs: Seq[(String, SbtPlatformModule)] - ) = { - renderBlocks(renderMemberAsSeq( - "sourcesRootFolders", - config.sourcesRootFolders, - crossConfigs.map((k, v) => (k, v.sourcesRootFolders)), - s => s"os.sub / ${literalize(s)}" - )) - } - - private def renderTestModule( - config: TestModule, - crossConfigs: Seq[(String, TestModule)] - ) = { - def cross[A](f: TestModule => A) = crossConfigs.map((k, v) => (k, f(v))) - import config.* - renderBlocks( - renderTask("testParallelism", testParallelism, cross(_.testParallelism), identity), - renderTask( - "testSandboxWorkingDir", - testSandboxWorkingDir, - cross(_.testSandboxWorkingDir), - identity - ) - ) - } - - private def renderMvnDep(dep: MvnDep) = build.metaBuild.fold(dep.toString()) { meta => - var ref = mvnDepRef.getOrElse(dep, null) - if (ref == null) { - ref = dep.name.split("\\W") match - case Array(head) => head - case parts => parts.tail.map(_.capitalize).mkString(parts.head, "", "") - if (mvnDepRef.valuesIterator.contains(ref)) { - // generate name for "duplicate" that will stand out visually, eg: - // val catsCore = mvn"org.typelevel::cats-core:2.0.0" - // val `catsCore#0` = mvn"org.typelevel::cats-core:2.6.1" - ref += "#" - ref += mvnDepRef.valuesIterator.count(_.startsWith(ref)) - } - mvnDepRef.put(dep, ref) - } - meta.depsObjectName + "." + backtickWrap(ref) - } - - private def renderModuleDep(dep: ModuleDep) = { - import dep.* - val segments0 = rootModuleAlias +: segments.map(backtickWrap) - def segment(i: Int) = segments0(i) + crossArgs.get(i - 1).fold("")(_.mkString("(", ", ", ")")) - segments0.indices.map(segment).mkString(".") - } - - private def renderArtifact(value: Artifact) = { - import value.* - s"Artifact(${literalize(group)}, ${literalize(id)}, ${literalize(version)})" - } - - private def renderPomSettings(value: PomSettings) = { - import value.* - // TODO Supporting optional tags requires changes in Mill. - val description0 = literalize(description.getOrElse("")) - val organization0 = literalize(organization.getOrElse("")) - val url0 = literalize(url.getOrElse("")) - val licenses0 = s"Seq(${licenses.map(renderLicense).mkString(", ")})" - val versionControl0 = renderVersionControl(versionControl) - val developers0 = s"Seq(${developers.map(renderDeveloper).mkString(", ")})" - s"PomSettings($description0, $organization0, $url0, $licenses0, $versionControl0, $developers0)" - } - - private def renderLicense(value: License) = { - import value.* - val id0 = literalize(if (id == null) "" else id) - val name0 = literalize(if (name == null) "" else name) - val url0 = literalize(if (url == null) "" else url) - val distribution0 = literalize(if (distribution == null) "" else distribution) - s"License($id0, $name0, $url0, $isOsiApproved, $isFsfLibre, $distribution0)" - } - - private def renderVersionControl(value: VersionControl) = { - import value.* - val browsableRepository0 = browsableRepository.fold("None")(v => s"Some(${literalize(v)})") - val connection0 = connection.fold("None")(v => s"Some(${literalize(v)})") - val developerConnection0 = developerConnection.fold("None")(v => s"Some(${literalize(v)})") - val tag0 = tag.fold("None")(v => s"Some(${literalize(v)})") - s"VersionControl($browsableRepository0, $connection0, $developerConnection0, $tag0)" - } - - private def renderDeveloper(value: Developer) = { - import value.* - val id0 = literalize(if (id == null) "" else id) - val name0 = literalize(if (name == null) "" else name) - val url0 = literalize(if (url == null) "" else url) - val organization0 = organization.fold("None")(v => s"Some(${literalize(v)})") - val organizationUrl0 = organizationUrl.fold("None")(v => s"Some(${literalize(v)})") - s"Developer($id0, $name0, $url0, $organization0, $organizationUrl0)" - } - - private def renderVersionScheme(value: String) = value match { - case "early-semver" => "VersionScheme.EarlySemVer" - case "pvp" => "VersionScheme.PVP" - case "semver-spec" => "VersionScheme.SemVerSpec" - case "strict" => "VersionScheme.Strict" - } - - private def renderBlocks(blocks: String*) = blocks.mkString( - """ - |""".stripMargin, - """ - | - |""".stripMargin, - """ - |""".stripMargin - ) - - private def renderLines(lines: Iterable[String]) = lines.mkString( - """ - |""".stripMargin - ) - - private def renderMember[A]( - name: String, - value: A, - crossValues: Seq[(String, A)], - renderValue: A => String - ) = { - val crossValues0 = crossValues.collect { - case (k, v) if v != null => (k, v) - } - if (value == null && crossValues0.isEmpty) "" - else { - val value0 = if (value == null) s"super.$name" else renderValue(value) - s"def $name = " + { - if (crossValues0.isEmpty) value0 - else { - val crossCases = crossValues0.groupMap(_._2)(_._1).toSeq.sortBy(_._2).map { - (value, crosses) => - val matchValues = crosses.sorted.map(literalize(_)).mkString(" | ") - s"case $matchValues => ${renderValue(value)}" - } - s"""crossValue match { - | ${renderLines(crossCases)} - | case _ => $value0 - |}""".stripMargin - } - } - } - } - - private def renderTask[A]( - name: String, - value: A, - crossValues: Seq[(String, A)], - renderValue: A => String - ) = { - val crossValues0 = crossValues.collect { - case (k, v) if v != null => (k, v) - } - if (value == null && crossValues0.isEmpty) "" - else { - val value0 = if (value == null) s"super.$name()" else renderValue(value) - s"def $name = " + { - if (crossValues0.isEmpty) value0 - else { - val crossCases = crossValues0.groupMap(_._2)(_._1).toSeq.sortBy(_._2).map { - (value, crosses) => - val matchValues = crosses.sorted.map(literalize(_)).mkString(" | ") - s"case $matchValues => ${renderValue(value)}" - } - s"""$renderCrossValueInTask match { - | ${renderLines(crossCases)} - | case _ => $value0 - |}""".stripMargin - } - } - } - } - - private def renderMemberAsSeq[A]( - name: String, - value: Seq[A], - crossValues: Seq[(String, Seq[A])], - renderValue: A => String - ) = { - val crossValues0 = crossValues.filter(_._2.nonEmpty) - if (value.isEmpty && crossValues0.isEmpty) "" - else { - def renderSeq(value: Seq[A]) = value.map(renderValue).mkString("Seq(", ", ", ")") - s"def $name = super.$name" + { - if (value.isEmpty) "" else " ++ " + renderSeq(value) - } + { - if (crossValues0.isEmpty) "" - else { - val crossCases = crossValues0.groupMap(_._2)(_._1).toSeq.sortBy(_._2).map { - (value, crosses) => - val matchValues = crosses.sorted.map(literalize(_)).mkString(" | ") - s"case $matchValues => ${renderSeq(value)}" - } - s""" ++ (crossValue match { - | ${renderLines(crossCases)} - | case _ => Nil - |})""".stripMargin - } - } - } - } - - private def renderTaskAsMap[K, V]( - name: String, - value: Map[K, V], - crossValues: Seq[(String, Map[K, V])], - renderKey: K => String, - renderValue: V => String - ) = { - val crossValues0 = crossValues.filter(_._2.nonEmpty) - if (value.isEmpty && crossValues0.isEmpty) "" - else { - def renderMap(value: Map[K, V]) = value - .map((k, v) => s"(${renderKey(k)}, ${renderValue(v)})") - .mkString("Map(", ", ", ")") - s"def $name = super.$name()" + { - if (value.isEmpty) "" else " ++ " + renderMap(value) - } + { - if (crossValues0.isEmpty) "" - else { - val crossCases = crossValues0.groupMap(_._2)(_._1).toSeq.sortBy(_._2).map { - (value, crosses) => - val matchValues = crosses.sorted.map(literalize(_)).mkString(" | ") - s"case $matchValues => ${renderMap(value)}" - } - s""" ++ ($renderCrossValueInTask match { - | ${renderLines(crossCases)} - | case _ => Map() - |})""".stripMargin - } - } - } - } - - private def renderTaskAsSeq[A]( - name: String, - value: Seq[A], - crossValues: Seq[(String, Seq[A])], - renderValue: A => String - ) = { - val crossValues0 = crossValues.filter(_._2.nonEmpty) - if (value.isEmpty && crossValues0.isEmpty) "" - else { - def renderSeq(value: Seq[A]) = value.map(renderValue).mkString("Seq(", ", ", ")") - s"def $name = super.$name()" + { - if (value.isEmpty) "" else " ++ " + renderSeq(value) - } + { - if (crossValues0.isEmpty) "" - else { - val crossCases = crossValues0.groupMap(_._2)(_._1).toSeq.sortBy(_._2).map { - (value, crosses) => - val matchValues = crosses.sorted.map(literalize(_)).mkString(" | ") - s"case $matchValues => ${renderSeq(value)}" - } - s""" ++ ($renderCrossValueInTask match { - | ${renderLines(crossCases)} - | case _ => Nil - |})""".stripMargin - } - } - } - } -} diff --git a/libs/init/buildgen/test/src/mill/main/buildgen/BuildGenChecker.scala b/libs/init/buildgen/test/src/mill/main/buildgen/BuildGenChecker.scala index 580fffd79a97..7f9fe4270c3e 100644 --- a/libs/init/buildgen/test/src/mill/main/buildgen/BuildGenChecker.scala +++ b/libs/init/buildgen/test/src/mill/main/buildgen/BuildGenChecker.scala @@ -4,18 +4,20 @@ import mill.api.Discover import mill.init.Util import mill.scalalib.scalafmt.ScalafmtModule import mill.testkit.{TestRootModule, UnitTester} +import mill.util.Jvm import mill.util.TokenReaders.* import mill.{PathRef, T} import utest.framework.TestPath import java.nio.file.FileSystems -class BuildGenChecker(sourceRoot: os.Path, scalafmtConfigFile: os.Path) { +class BuildGenChecker(mainAssembly: os.Path, sourceRoot: os.Path, scalafmtConfigFile: os.Path) { def check( - generate: => Unit, sourceRel: os.SubPath, expectedRel: os.SubPath, + mainArgs: Seq[String] = Nil, + envJvmId: String = "zulu:17", updateSnapshots: Boolean = false // pass true to update test data on disk )(using tp: TestPath @@ -24,7 +26,11 @@ class BuildGenChecker(sourceRoot: os.Path, scalafmtConfigFile: os.Path) { val testRoot = os.pwd / tp.value os.copy.over(sourceRoot / sourceRel, testRoot, createFolders = true, replaceExisting = true) - os.dynamicPwd.withValue(testRoot)(generate) + val javaHome = Jvm.resolveJavaHome(envJvmId).get + val javaExe = Jvm.javaExe(Some(javaHome)) + val mainEnv = Map("JAVA_HOME" -> javaHome.toString) + os.proc(javaExe, "-jar", mainAssembly, mainArgs) + .call(cwd = testRoot, env = mainEnv, stdout = os.Inherit) val buildFiles = Util.buildFiles(testRoot) object module extends TestRootModule with ScalafmtModule { @@ -74,6 +80,12 @@ class BuildGenChecker(sourceRoot: os.Path, scalafmtConfigFile: os.Path) { } object BuildGenChecker { - def apply(sourceRoot: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_DIR"))): BuildGenChecker = - new BuildGenChecker(sourceRoot, os.temp(Util.scalafmtConfig)) + def apply( + mainAssembly: os.Path = os.Path(sys.env("TEST_MAIN_ASSEMBLY")), + sourceRoot: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) + ): BuildGenChecker = new BuildGenChecker( + mainAssembly = mainAssembly, + sourceRoot = sourceRoot, + scalafmtConfigFile = os.temp(Util.scalafmtConfig) + ) } diff --git a/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelBuilder.scala b/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelBuilder.scala index 9104909e1915..c405953889d3 100644 --- a/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelBuilder.scala +++ b/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelBuilder.scala @@ -1,23 +1,33 @@ package mill.main.gradle import mill.main.buildgen.* -import mill.main.buildgen.ModuleConfig.* +import mill.main.buildgen.ModuleSpec.* +import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.artifacts.* -import org.gradle.api.artifacts.repositories.{ArtifactRepository, UrlArtifactRepository} +import org.gradle.api.artifacts.repositories.ArtifactRepository +import org.gradle.api.artifacts.repositories.UrlArtifactRepository +import org.gradle.api.attributes.Category +import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependencyConstraint +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaPlatformPlugin import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.* import org.gradle.api.publish.maven.internal.publication.DefaultMavenPom import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.api.{Project, Task} -import org.gradle.tooling.provider.model.{ToolingModelBuilder, ToolingModelBuilderRegistry} +import org.gradle.api.tasks.testing.Test +import org.gradle.tooling.provider.model.ToolingModelBuilder +import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import java.io.File import javax.inject.Inject import scala.jdk.CollectionConverters.* import scala.reflect.TypeTest +import scala.util.Try -class BuildModelBuilder(ctx: GradleBuildCtx, workspace: os.Path) extends ToolingModelBuilder { +class BuildModelBuilder(ctx: GradleBuildCtx, objectFactory: ObjectFactory, workspace: os.Path) + extends ToolingModelBuilder { def canBuild(modelName: String) = classOf[BuildModel].getName == modelName @@ -33,172 +43,247 @@ class BuildModelBuilder(ctx: GradleBuildCtx, workspace: os.Path) extends Tooling private def toPackage(project0: Project): PackageSpec = { import project0.* val moduleDir = os.Path(getProjectDir) - val segments = moduleDir.subRelativeTo(workspace).segments - - def isBom(dep: ExternalDependency) = isBomDep(dep.getGroup, dep.getName) - def configurations(names: Seq[String]) = getConfigurations.asScala - .filter(config => names.contains(config.getName)) - def mvnDeps(configs: String*) = configurations(configs) - .flatMap(config => - config.getDependencies.asScala.collect { - case dep: ExternalDependency if !isBom(dep) => toMvnDep(dep, config) - } - ) - .toSeq - def bomMvnDeps(configs: String*) = configurations(configs) - .flatMap(config => - config.getDependencies.asScala.collect { - case dep: ExternalDependency if isBom(dep) => toMvnDep(dep, config) - } - ) - .toSeq - def moduleDeps(configs: String*) = configurations(configs) - .flatMap(_.getDependencies.asScala.collect { - case dep: ProjectDependency => toModuleDep(dep) - }) - .filter(_.segments != segments) - .toSeq + var mainModule = ModuleSpec( + name = moduleDir.last, + repositories = getRepositories.iterator.asScala.collect(toRepositoryUrlString).distinct.toSeq + .diff(Seq( + getRepositories.mavenCentral, + getRepositories.mavenLocal, + getRepositories.gradlePluginPortal + ).collect(toRepositoryUrlString)) + ) + + def configurations(names: String*) = { + val itr = getConfigurations.iterator.asScala + if (names.isEmpty) itr else itr.filter(config => names.contains(config.getName)) + } + def constraints = + configurations().flatMap(_.getDependencyConstraints.iterator.asScala) + def dependencies(configs: String*) = + configurations(configs*).flatMap(_.getDependencies.iterator.asScala) def task[T](name: String)(using T: TypeTest[Task, T]) = getTasks.findByName(name) match { case T(t) => Some(t) case _ => None } - def javacOptionsFromTask(task: JavaCompile) = { - val compatOptions = ctx.releaseVersion(task.getOptions).fold( - // When not configured explicitly, "-source" and "-target" default to the - // `languageVersion` of the Java toolchain. - Option(task.getSourceCompatibility).fold(Nil)(Seq("-source", _)) ++ - Option(task.getTargetCompatibility).fold(Nil)(Seq("-target", _)) - )(n => Seq("--release", n.toString)) - compatOptions ++ - Option(task.getOptions.getEncoding).fold(Nil)(Seq("-encoding", _)) ++ - task.getOptions.getAllCompilerArgs.asScala.toSeq - } - - val skipRepositories = Seq( - getRepositories.mavenCentral(), - getRepositories.mavenLocal(), - getRepositories.gradlePluginPortal() - ).collect(toRepositoryUrlString) - val mainCoursierModule = getRepositories.asScala.collect(toRepositoryUrlString) - .distinct.toSeq.diff(skipRepositories) match { - case Nil => None - case repositories => Some(CoursierModule(repositories = repositories)) - } - val errorProneDeps = mvnDeps("errorprone") - val (mainErrorProneModule, mainJavacOptions) = ErrorProneModule.find( - task[JavaCompile]("compileJava").fold(Nil)(javacOptionsFromTask), - errorProneDeps - ) - val javaPluginExtension = Option(getExtensions.findByType(classOf[JavaPluginExtension])) - val mainJavaHomeModule = - javaPluginExtension.flatMap(ext => - JavaHomeModule.find(ctx.javaVersion(ext), mainJavacOptions) - ) val mavenPublication = Option(getExtensions.findByType(classOf[PublishingExtension])) .flatMap(ext => ext.getPublications.withType(classOf[MavenPublication]).asScala.headOption) - val mainJavaModule = Option.when(getPluginManager.hasPlugin("java")) { - JavaModule( + + if (getPluginManager.hasPlugin("java-platform")) { + val (constrainedModules, constrainedDeps) = + constraints.toSeq.partition(toModuleDep.isDefinedAt) + val (modulesDeps, bomModuleDeps) = constrainedModules.partitionMap(dep => + Either.cond(isBom(dep), toModuleDep(dep), toModuleDep(dep)) + ) + val (bomModuleDeps1, bomDeps) = + dependencies().filter(isBom).toSeq.partitionMap(dep => toModuleDep.lift(dep).toLeft(dep)) + mainModule = mainModule.copy( + imports = "import mill.javalib.*" +: mainModule.imports, + supertypes = "JavaModule" +: "BomModule" +: mainModule.supertypes, + bomMvnDeps = bomDeps.collect(toMvnDep), + depManagement = constrainedDeps.collect(toMvnDep), + moduleDeps = modulesDeps, + bomModuleDeps = bomModuleDeps ++ bomModuleDeps1 + ) + } else if (getPluginManager.hasPlugin("java")) { + def mvnDeps(configs: String*) = + dependencies(configs*).filterNot(isBom).collect(toMvnDep).toSeq + def moduleDeps(configs: String*) = + dependencies(configs*).filterNot(isBom).collect(toModuleDep).toSeq + val (bomModuleDeps, bomDeps) = + dependencies().filter(isBom).toSeq.partitionMap(dep => toModuleDep.lift(dep).toLeft(dep)) + val javaPluginExt = getExtensions.findByType(classOf[JavaPluginExtension]) + val sourceSets = ctx.sourceSets(javaPluginExt) + val (sourcesFolders, sources, resources) = + sourceSets.find(_.getName == "main").fold((Nil, Nil, Nil))(ss => + val (sourcesFolders, sources) = + ss.getJava.getSrcDirs.iterator.asScala + .map(os.Path(_).relativeTo(moduleDir)).toSeq.partition(_.ups == 0) + val resources = ss.getResources.getSrcDirs.iterator.asScala + .map(os.Path(_).relativeTo(moduleDir)).toSeq + ( + if (sourcesFolders == Seq(os.rel / "src/main/java")) Nil + else sourcesFolders.map(_.asSubPath), + sources, + if (resources == Seq(os.rel / "src/main/resources")) Nil else resources + ) + ) + mainModule = mainModule.copy( + imports = "import mill.javalib.*" +: mainModule.imports, + supertypes = "MavenModule" +: mainModule.supertypes, mvnDeps = mvnDeps("implementation", "api"), compileMvnDeps = mvnDeps("compileOnly", "compileOnlyApi"), runMvnDeps = mvnDeps("runtimeOnly"), - bomMvnDeps = bomMvnDeps("implementation", "api"), + bomMvnDeps = bomDeps.collect(toMvnDep), + depManagement = constraints.collect(toMvnDep).toSeq, + javacOptions = task[JavaCompile]("compileJava").fold(Nil)(javacOptions), + sourcesFolders = sourcesFolders, + sources = Values(extend = true, sources), + resources = resources, moduleDeps = moduleDeps("implementation", "api"), compileModuleDeps = moduleDeps("compileOnly", "compileOnlyApi"), runModuleDeps = moduleDeps("runtimeOnly"), - javacOptions = mainJavacOptions, - artifactName = mavenPublication.fold(null)(_.getArtifactId) - ) - } - val mainPublishModule = mavenPublication.map { pub => - PublishModule( - pomPackagingType = PublishModule.pomPackagingTypeOverride(pub.getPom.getPackaging), - pomSettings = toPomSettings(pub.getPom, pub.getGroupId), - publishVersion = getVersion.toString - ) - } + bomModuleDeps = bomModuleDeps, + artifactName = mavenPublication.map(_.getArtifactId) + ).withErrorProneModule(mvnDeps("errorprone")) - val testModule = if (os.exists(moduleDir / "src/test")) { - TestModule.mixin(mvnDeps("testImplementation")).map { testModuleMixin => - val (testErrorProneModule, testJavacOptions) = ErrorProneModule.find( - task[JavaCompile]("compileTestJava").fold(Nil)(javacOptionsFromTask), - errorProneDeps - ) - val testJavaModule = JavaModule( - mvnDeps = mvnDeps("testImplementation"), - compileMvnDeps = mvnDeps("testCompileOnly"), - runMvnDeps = mvnDeps("testRuntimeOnly"), - bomMvnDeps = bomMvnDeps("testImplementation"), - moduleDeps = moduleDeps("testImplementation"), - compileModuleDeps = moduleDeps("testCompileOnly"), - runModuleDeps = moduleDeps("testRuntimeOnly"), - javacOptions = inheritedOptions(testJavacOptions, mainJavacOptions) - ) - val testModule = TestModule( - testParallelism = "false", - testSandboxWorkingDir = "false" - ) - val testConfigs = Seq(testJavaModule) ++ testErrorProneModule ++ Seq(testModule) - val testSupertypes = "MavenTests" +: testConfigs.collect { - case _: ErrorProneModule => "ErrorProneModule" + for { + testSourceSet <- sourceSets.find(_.getName == "test") + if testSourceSet.getJava.getSrcDirs.asScala.exists(_.exists) + } do { + val testMvnDeps = mvnDeps("testImplementation") + ModuleSpec.testModuleMixin(testMvnDeps).foreach { mixin => + val (sourcesFolders, sources) = testSourceSet.getJava.getSrcDirs.iterator.asScala + .map(os.Path(_).relativeTo(moduleDir)).toSeq.partition(_.ups == 0) + val resources = testSourceSet.getResources.getSrcDirs.iterator.asScala + .map(os.Path(_).relativeTo(moduleDir)).toSeq + var testModule = ModuleSpec( + name = "test", + supertypes = Seq("MavenTests"), + mixins = Seq(mixin), + forkArgs = task[Test]("test").fold(Nil)(task => + task.getSystemProperties().asScala.map { + case (k, v) => Opt(s"-D$k=$v") + }.toSeq ++ Opt.groups(task.getJvmArgs.asScala.toSeq) + ), + forkWorkingDir = Some(os.rel), + mvnDeps = testMvnDeps, + compileMvnDeps = mvnDeps("testCompileOnly"), + runMvnDeps = mvnDeps("testRuntimeOnly"), + javacOptions = task[JavaCompile]("compileTestJava").fold(Nil)(javacOptions), + sourcesFolders = if (sourcesFolders == Seq(os.rel / "src/test/java")) Nil + else sourcesFolders.map(_.asSubPath), + sources = Values(extend = true, sources), + resources = if (resources == Seq(os.rel / "src/test/resources")) Nil else resources, + moduleDeps = Values( + extend = true, + moduleDeps("testImplementation") + .diff(Seq(ModuleDep(moduleDir.subRelativeTo(workspace)))) + ), + compileModuleDeps = moduleDeps("testCompileOnly"), + runModuleDeps = moduleDeps("testRuntimeOnly"), + testParallelism = Some(false), + testSandboxWorkingDir = Some(false) + ).withErrorProneModule(mainModule.errorProneDeps.base) + + mixin match { + // Gradle can resolve junit-platform-launcher version using junit-bom transitive + // dependency. Since Coursier cannot do the same, make the dependency explicit. + case "TestModule.Junit5" + if !mainModule.depManagement.base.exists(dep => + dep.name == "junit-platform-launcher" && dep.organization == "org.junit.platform" + ) => + val jplOpt = testModule.runMvnDeps.base.find(dep => + dep.name == "junit-platform-launcher" && dep.organization == "org.junit.platform" + ) + if (jplOpt.forall(_.version.isEmpty)) { + // Some legacy Gradle versions auto-include junit-platform-launcher. + if (jplOpt.isEmpty) { + val jpl = MvnDep("org.junit.platform", "junit-platform-launcher", "") + testModule = testModule.copy(runMvnDeps = jpl +: testModule.runMvnDeps.base) + } + if ( + !mainModule.bomMvnDeps.base.exists(dep => + dep.name == "junit-bom" && dep.organization == "org.junit" + ) + ) { + testModule.mvnDeps.base.collectFirst { + case dep if dep.organization == "org.junit.jupiter" && dep.version.nonEmpty => + dep.version + }.foreach { junitVersion => + val junitBom = MvnDep("org.junit", "junit-bom", junitVersion) + testModule = testModule.copy(bomMvnDeps = + testModule.bomMvnDeps.copy(base = junitBom +: testModule.bomMvnDeps.base) + ) + } + } + } + case _ => + } + + mainModule = mainModule.copy(test = Some(testModule)) } - ModuleSpec( - name = "test", - supertypes = testSupertypes, - mixins = Seq(testModuleMixin), - configs = testConfigs - ) - } - } else None - - val mainConfigs = Seq( - mainJavaModule, - mainErrorProneModule, - mainJavaHomeModule, - mainPublishModule, - mainCoursierModule - ).flatten - val mainModule = if (mainConfigs.isEmpty && testModule.isEmpty) ModuleSpec(moduleDir.last) - else { - val mainSupertypes = "MavenModule" +: mainConfigs.collect { - case _: PublishModule => "PublishModule" - case _: ErrorProneModule => "ErrorProneModule" } - ModuleSpec( - name = moduleDir.last, - supertypes = mainSupertypes, - configs = mainConfigs, - nestedModules = testModule.toSeq + } + + mavenPublication.foreach { pub => + val pom = Option(pub.getPom) + mainModule = mainModule.copy( + imports = "import mill.javalib.*" +: "import mill.javalib.publish.*" +: mainModule.imports, + supertypes = mainModule.supertypes :+ "PublishModule", + pomPackagingType = pom.flatMap(toPomPackagingType), + pomSettings = pom.map(toPomSettings(_, pub.getGroupId)), + publishVersion = Option(getVersion).map(_.toString) ) } - PackageSpec(segments, mainModule) + PackageSpec(moduleDir.subRelativeTo(workspace), mainModule) } private val toRepositoryUrlString: PartialFunction[ArtifactRepository, String] = { case repo: UrlArtifactRepository => repo.getUrl.toURL.toExternalForm } - private def toMvnDep(dep: ExternalDependency, config: Configuration): MvnDep = { - import dep.* - val artifact = getArtifacts.asScala.headOption - MvnDep( - organization = getGroup, - name = getName, - version = Option(getVersion).orElse { - config.getAllDependencyConstraints.asScala.collectFirst { - case dc if dc.getGroup == getGroup || dc.getName == getName => dc.getVersion - } - }, - classifier = artifact.flatMap(a => Option(a.getClassifier)), - `type` = artifact.flatMap(a => Option(a.getType)), - excludes = getExcludeRules.asScala.map(rule => rule.getGroup -> rule.getModule).toSeq - ) + private val platform = objectFactory.named(classOf[Category], Category.REGULAR_PLATFORM) + private val enforcedPlatform = objectFactory.named(classOf[Category], Category.ENFORCED_PLATFORM) + private def isBom(dep: Dependency | DependencyConstraint) = dep match { + case dep: ModuleDependency => + val category = dep.getAttributes.getAttribute(Category.CATEGORY_ATTRIBUTE) + category == platform || category == enforcedPlatform + case dep: DependencyConstraint => + val category = dep.getAttributes.getAttribute(Category.CATEGORY_ATTRIBUTE) + category == platform || category == enforcedPlatform + case _ => false } - private def toModuleDep(dep: ProjectDependency): ModuleDep = - ModuleDep( - os.Path(ctx.project(dep).getProjectDir).subRelativeTo(workspace).segments - ) + private def toCoursierVersionConstraint(version: String) = version match { + case null => "" + case s"]${range}[" => s"($range)" + case s"]${range}" => s"($range" + case s"${range}[" => s"$range)" + case s => s + } + private val toMvnDep: PartialFunction[Dependency | DependencyConstraint, MvnDep] = { + case dep: ExternalDependency => + import dep.* + val artifact = getArtifacts.asScala.headOption + MvnDep( + organization = getGroup, + name = getName, + version = toCoursierVersionConstraint(getVersion), + classifier = artifact.flatMap(a => Option(a.getClassifier)), + `type` = artifact.flatMap(_.getType match { + case null | "jar" | "pom" => None + case tpe => Some(tpe) + }), + excludes = getExcludeRules.asScala.map(rule => rule.getGroup -> rule.getModule).toSeq + ) + case dep: DependencyConstraint => + import dep.* + MvnDep( + organization = getGroup, + name = getName, + version = toCoursierVersionConstraint(getVersion) + ) + } + + private val toModuleDep: PartialFunction[Dependency | DependencyConstraint, ModuleDep] = { + case dep: ProjectDependency => + ModuleDep(os.Path(ctx.project(dep).getProjectDir).subRelativeTo(workspace)) + case dep: DefaultProjectDependencyConstraint => + toModuleDep(dep.getProjectDependency) + } + + private def javacOptions(task: JavaCompile) = { + ctx.releaseVersion(task.getOptions).fold(Seq( + Option(task.getSourceCompatibility).map(Opt("-source", _)), + Option(task.getTargetCompatibility).map(Opt("-target", _)) + ).flatten)(n => Seq(Opt("--release", n.toString))) ++ + Option(task.getOptions.getEncoding).map(Opt("-encoding", _)) ++ + Opt.groups(task.getOptions.getAllCompilerArgs.asScala.toSeq) + } + + private def toPomPackagingType(pom: MavenPom): Option[String] = + Try(pom.getPackaging).filter(_ != "jar").toOption private def toPomSettings(pom: MavenPom, groupId: String): PomSettings = { import pom.* @@ -212,9 +297,9 @@ class BuildModelBuilder(ctx: GradleBuildCtx, workspace: os.Path) extends Tooling case _ => (Nil, VersionControl(), Nil) } PomSettings( - description = Option(getDescription.getOrNull), - organization = Option(groupId), - url = Option(getUrl.getOrNull), + description = getDescription.getOrElse(""), + organization = groupId, + url = getUrl.getOrElse(""), licenses = licenses, versionControl = versionControl, developers = developers @@ -224,9 +309,9 @@ class BuildModelBuilder(ctx: GradleBuildCtx, workspace: os.Path) extends Tooling private def toLicense(license: MavenPomLicense): License = { import license.* License( - name = getName.getOrNull, - url = getUrl.getOrNull, - distribution = getDistribution.getOrNull + name = getName.getOrElse(""), + url = getUrl.getOrElse(""), + distribution = getDistribution.getOrElse("") ) } @@ -245,9 +330,9 @@ class BuildModelBuilder(ctx: GradleBuildCtx, workspace: os.Path) extends Tooling private def toDeveloper(developer: MavenPomDeveloper): Developer = { import developer.* Developer( - id = getId.getOrNull, - name = getName.getOrNull, - url = getUrl.getOrNull, + id = getId.getOrElse(""), + name = getName.getOrElse(""), + url = getUrl.getOrElse(""), organization = Option(getOrganization.getOrNull), organizationUrl = Option(getOrganizationUrl.getOrNull) ) diff --git a/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelPlugin.scala b/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelPlugin.scala index f5d1f8eddb4f..2e4226aa64a6 100644 --- a/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelPlugin.scala +++ b/libs/init/gradle/exportplugin/src/mill/main/gradle/BuildModelPlugin.scala @@ -1,15 +1,19 @@ package mill.main.gradle import org.gradle.api.{Plugin, Project} +import org.gradle.api.model.ObjectFactory import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import javax.inject.Inject -class BuildModelPlugin @Inject (builderRegistry: ToolingModelBuilderRegistry) - extends Plugin[Project] { +class BuildModelPlugin @Inject ( + builderRegistry: ToolingModelBuilderRegistry, + objectFactory: ObjectFactory +) extends Plugin[Project] { def apply(target: Project) = builderRegistry.register(BuildModelBuilder( ctx = GradleBuildCtx(target.getGradle), - workspace = os.Path(target.getRootProject.getRootDir) + objectFactory = objectFactory, + workspace = os.Path(target.getRootDir) )) } diff --git a/libs/init/gradle/exportplugin/src/mill/main/gradle/GradleBuildCtx.scala b/libs/init/gradle/exportplugin/src/mill/main/gradle/GradleBuildCtx.scala index 77e35943e4f6..d6ec7963bf72 100644 --- a/libs/init/gradle/exportplugin/src/mill/main/gradle/GradleBuildCtx.scala +++ b/libs/init/gradle/exportplugin/src/mill/main/gradle/GradleBuildCtx.scala @@ -4,18 +4,19 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.invocation.Gradle import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.CompileOptions -import org.gradle.jvm.toolchain.JavaLanguageVersion +import scala.jdk.CollectionConverters.* import scala.math.Ordered.orderingToOrdered /** * Gradle-version agnostic build API. */ trait GradleBuildCtx { - def javaVersion(ext: JavaPluginExtension): Option[Int] def releaseVersion(opts: CompileOptions): Option[Int] def project(dep: ProjectDependency): Project + def sourceSets(javaPluginExt: JavaPluginExtension): Set[SourceSet] } object GradleBuildCtx { @@ -29,12 +30,6 @@ object GradleBuildCtx { } } - def javaVersion(ext: JavaPluginExtension) = - // When not explicitly configured, the toolchain defaults to the JVM used to run the daemon. - if ((6, 7) <= gradleVersion) Option(ext.getToolchain) - .flatMap(tc => Option(tc.getLanguageVersion.getOrNull())) - .map(_.asInt()) - else None def releaseVersion(opts: CompileOptions) = if ((6, 6) <= gradleVersion) opts.getRelease.getOrElse(0).intValue match { case 0 => None @@ -44,5 +39,8 @@ object GradleBuildCtx { def project(dep: ProjectDependency) = if ((8, 11) <= gradleVersion) gradle.getRootProject.findProject(dep.getPath) else dep.getDependencyProject: @scala.annotation.nowarn("cat=deprecation") + def sourceSets(javaPluginExt: JavaPluginExtension) = + if ((7, 1) <= gradleVersion) javaPluginExt.getSourceSets.asScala.toSet + else Set.empty } } diff --git a/libs/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/libs/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index ddd08e3aa530..029afb27af5a 100644 --- a/libs/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/libs/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -2,7 +2,6 @@ package mill.main.gradle import mill.main.buildgen.* import mill.main.gradle.BuildInfo.exportpluginAssemblyResource -import mill.util.{CoursierConfig, Jvm} import org.gradle.tooling.GradleConnector import org.gradle.tooling.internal.consumer.DefaultGradleConnector import pprint.Util.literalize @@ -11,72 +10,62 @@ import java.util.Properties import java.util.concurrent.TimeUnit import scala.util.Using -/** - * Application that imports a Gradle build to Mill. - */ object GradleBuildGenMain { def main(args: Array[String]): Unit = mainargs.Parser(this).runOrExit(args.toSeq) - @mainargs.main(doc = "Imports a Gradle build located in the current working directory.") - def runImport( - @mainargs.arg(doc = "merge generated build files") + @mainargs.main(doc = "Generates Mill build files that are derived from a Gradle build.") + def init( + @mainargs.arg(doc = "merge package.mill files in to the root build.mill file") merge: mainargs.Flag, @mainargs.arg(doc = "disable generating meta-build files") - noMeta: mainargs.Flag, - @mainargs.arg(doc = "JDK to use to run the Gradle daemon") - // The JDK used to run the daemon may be used to configure certain settings. - gradleJvmId: String = "system" + noMeta: mainargs.Flag ): Unit = { println("converting Gradle build") - val gradleWrapperProperties = { - val properties = new Properties() - val file = os.pwd / "gradle/wrapper/gradle-wrapper.properties" - if (os.isFile(file)) Using.resource(os.read.inputStream(file))(properties.load) - properties - } - val gradleJvmArgs = Option(gradleWrapperProperties.getProperty("org.gradle.jvmargs")) - .fold(Nil)(_.trim.split("\\s").toSeq) - - val gradleJavaHome = - Jvm.resolveJavaHome(id = gradleJvmId, config = CoursierConfig.default()).get val exportPluginJar = Using.resource( - GradleBuildGenMain.getClass.getResourceAsStream(exportpluginAssemblyResource) + getClass.getResourceAsStream(exportpluginAssemblyResource) )(os.temp(_, suffix = ".jar")) - val gradleConnector = GradleConnector.newConnector() match { + val initScript = os.temp( + s"""initscript { + | dependencies { + | classpath files(${literalize(exportPluginJar.toString)}) + | } + |} + |rootProject { + | apply plugin: mill.main.gradle.BuildModelPlugin + |} + |""".stripMargin, + suffix = ".gradle" + ) + val gradleConnector = GradleConnector.newConnector match { case conn: DefaultGradleConnector => conn.daemonMaxIdleTime(1, TimeUnit.SECONDS) conn case conn => conn } val packages = - try { - Using.resource(gradleConnector.forProjectDirectory(os.pwd.toIO).connect()) { connection => - val initScript = os.temp( - s"""initscript { - | dependencies { - | classpath files(${literalize(exportPluginJar.toString())}) - | } - |} - |rootProject { - | apply plugin: mill.main.gradle.BuildModelPlugin - |} - |""".stripMargin, - suffix = ".gradle" - ) - val modelBuilder = connection.model(classOf[BuildModel]) - .setJavaHome(gradleJavaHome.toIO) + try Using.resource(gradleConnector.forProjectDirectory(os.pwd.toIO).connect) { connection => + val model = connection.model(classOf[BuildModel]) .addArguments("--init-script", initScript.toString) - println("connecting to Gradle daemon") - val model = modelBuilder.get + .setStandardOutput(System.out) + .get upickle.default.read[Seq[PackageSpec]](model.asJson) } - } finally gradleConnector.disconnect() + finally gradleConnector.disconnect() - var build = BuildSpec.fill(packages).copy(millJvmOpts = gradleJvmArgs) - if (merge.value) build = build.merged - if (!noMeta.value) build = build.withDefaultMetaBuild - BuildWriter(build).writeFiles() + val (depRefs, packages0) = + if (noMeta.value) (Nil, packages) else BuildGen.withNamedDeps(packages) + val (baseModule, packages1) = + Option.when(!noMeta.value)(BuildGen.withBaseModule(packages0, "MavenTests", "MavenModule")) + .flatten.fold((None, packages0))((base, packages) => (Some(base), packages)) + val millJvmOpts = { + val properties = new Properties() + val file = os.pwd / "gradle/wrapper/gradle-wrapper.properties" + if (os.isFile(file)) Using.resource(os.read.inputStream(file))(properties.load) + val prop = properties.getProperty("org.gradle.jvmargs") + if (prop == null) Nil else prop.trim.split("\\s").toSeq + } + BuildGen.writeBuildFiles(packages1, merge.value, depRefs, baseModule, millJvmOpts) } } diff --git a/libs/init/gradle/test/resources/expected/gradle-6-0/build.mill b/libs/init/gradle/test/resources/expected/gradle-6-0/build.mill deleted file mode 100644 index b696d391b9d5..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-6-0/build.mill +++ /dev/null @@ -1,28 +0,0 @@ -//| mill-version: SNAPSHOT -package build - -import mill.javalib.* -import millbuild.* - -object `package` extends MavenModule { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.guava) - - def javacOptions = super.javacOptions() ++ - Seq("-source", "11", "-target", "11") - - def jvmId = "zulu:11" - - def repositories = super.repositories() ++ Seq("https://jcenter.bintray.com/") - - object test extends MavenTests with TestModule.TestNg { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.testng) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-6-0/mill-build/src/Deps.scala b/libs/init/gradle/test/resources/expected/gradle-6-0/mill-build/src/Deps.scala deleted file mode 100644 index 75d4c18aa44a..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-6-0/mill-build/src/Deps.scala +++ /dev/null @@ -1,9 +0,0 @@ -package millbuild - -import mill.javalib.* - -object Deps { - - val guava = mvn"com.google.guava:guava:28.0-jre" - val testng = mvn"org.testng:testng:6.14.3" -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/app/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-0/app/package.mill deleted file mode 100644 index b854d0883228..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/app/package.mill +++ /dev/null @@ -1,24 +0,0 @@ -package build.app - -import mill.javalib.* -import millbuild.* - -object `package` extends ProjectBaseModule { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.commonsText) - - def moduleDeps = super.moduleDeps ++ Seq(build.utilities) - - object test extends MavenTests with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiterApi) - - def runMvnDeps = super.runMvnDeps() ++ Seq(Deps.junitJupiterEngine) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/list/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-0/list/package.mill deleted file mode 100644 index 911aa4e895bb..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/list/package.mill +++ /dev/null @@ -1,20 +0,0 @@ -package build.list - -import mill.javalib.* -import millbuild.* - -object `package` extends ProjectBaseModule { - - object test extends MavenTests with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiterApi) - - def runMvnDeps = super.runMvnDeps() ++ Seq(Deps.junitJupiterEngine) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/Deps.scala b/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/Deps.scala deleted file mode 100644 index 8e7e7f3c29b6..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/Deps.scala +++ /dev/null @@ -1,10 +0,0 @@ -package millbuild - -import mill.javalib.* - -object Deps { - - val commonsText = mvn"org.apache.commons:commons-text:1.9" - val junitJupiterApi = mvn"org.junit.jupiter:junit-jupiter-api:5.7.1" - val junitJupiterEngine = mvn"org.junit.jupiter:junit-jupiter-engine" -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/ProjectBaseModule.scala b/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/ProjectBaseModule.scala deleted file mode 100644 index 83a2088d2eea..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/mill-build/src/ProjectBaseModule.scala +++ /dev/null @@ -1,12 +0,0 @@ -package millbuild - -import mill.javalib.* - -trait ProjectBaseModule extends MavenModule { - - def javacOptions = super.javacOptions() ++ - Seq("-source", "11", "-target", "11") - - def jvmId = "zulu:11" - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/utilities/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-0/utilities/package.mill deleted file mode 100644 index ed2fbd197899..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/utilities/package.mill +++ /dev/null @@ -1,10 +0,0 @@ -package build.utilities - -import mill.javalib.* -import millbuild.* - -object `package` extends ProjectBaseModule { - - def moduleDeps = super.moduleDeps ++ Seq(build.list) - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-2/app/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-2/app/package.mill new file mode 100644 index 000000000000..f1471a5a67d2 --- /dev/null +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/app/package.mill @@ -0,0 +1,12 @@ +package build.app +import mill.* +import mill.javalib.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { + + def moduleDeps = Seq(build.utilities) + + def mvnDeps = Seq(mvn"org.apache.commons:commons-text") + +} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-0/build.mill b/libs/init/gradle/test/resources/expected/gradle-7-2/build.mill similarity index 63% rename from libs/init/gradle/test/resources/expected/gradle-7-0/build.mill rename to libs/init/gradle/test/resources/expected/gradle-7-2/build.mill index 8958ea4ccb06..c5fac7dc2e5e 100644 --- a/libs/init/gradle/test/resources/expected/gradle-7-0/build.mill +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/build.mill @@ -1,6 +1,6 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* - +import millbuild.Deps object `package` extends Module {} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-2/list/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-2/list/package.mill new file mode 100644 index 000000000000..07934ea04fd7 --- /dev/null +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/list/package.mill @@ -0,0 +1,6 @@ +package build.list +import mill.* +import mill.javalib.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule {} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/Deps.scala b/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/Deps.scala new file mode 100644 index 000000000000..e28ebbfb31a8 --- /dev/null +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/Deps.scala @@ -0,0 +1,6 @@ +package millbuild +import mill.javalib.* +object Deps { + + val commonsText = mvn"org.apache.commons:commons-text:1.9" +} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/ProjectBaseModule.scala b/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/ProjectBaseModule.scala new file mode 100644 index 000000000000..9f620f5e16b5 --- /dev/null +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/mill-build/src/ProjectBaseModule.scala @@ -0,0 +1,10 @@ +package millbuild +import mill.* +import mill.javalib.* +trait ProjectBaseModule extends MavenModule { + + def depManagement = Seq(Deps.commonsText) + + def javacOptions = Seq("-source", "17", "-target", "17") + +} diff --git a/libs/init/gradle/test/resources/expected/gradle-7-2/utilities/package.mill b/libs/init/gradle/test/resources/expected/gradle-7-2/utilities/package.mill new file mode 100644 index 000000000000..e642380e445e --- /dev/null +++ b/libs/init/gradle/test/resources/expected/gradle-7-2/utilities/package.mill @@ -0,0 +1,10 @@ +package build.utilities +import mill.* +import mill.javalib.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { + + def moduleDeps = Seq(build.list) + +} diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/app/package.mill b/libs/init/gradle/test/resources/expected/gradle-8-0/app/package.mill index 7617c99dfe83..f1471a5a67d2 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/app/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/app/package.mill @@ -1,22 +1,12 @@ package build.app - +import mill.* import mill.javalib.* -import millbuild.* - +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends ProjectBaseModule { - def mvnDeps = super.mvnDeps() ++ Seq(Deps.commonsText) - - def moduleDeps = super.moduleDeps ++ Seq(build.utilities) - - object test extends MavenTests with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiter) - - def testParallelism = false - - def testSandboxWorkingDir = false + def moduleDeps = Seq(build.utilities) - } + def mvnDeps = Seq(mvn"org.apache.commons:commons-text") } diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/build.mill b/libs/init/gradle/test/resources/expected/gradle-8-0/build.mill index 8958ea4ccb06..c5fac7dc2e5e 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/build.mill +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/build.mill @@ -1,6 +1,6 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* - +import millbuild.Deps object `package` extends Module {} diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/list/package.mill b/libs/init/gradle/test/resources/expected/gradle-8-0/list/package.mill index 7f447fc382a6..13b7211c6c64 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/list/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/list/package.mill @@ -1,32 +1,14 @@ package build.list - +import mill.* import mill.javalib.* -import mill.javalib.errorprone.* -import millbuild.* - -object `package` extends ErrorProneModule with ProjectBaseModule { +import mill.javalib.errorprone.ErrorProneModule +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule, ErrorProneModule { def errorProneVersion = "2.28.0" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ + def errorProneJavacEnableOptions = Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") - object test extends MavenTests with ErrorProneModule with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiter) - - def errorProneVersion = "2.28.0" - - def errorProneOptions = super.errorProneOptions() ++ - Seq("-XepCompilingTestOnlyCode") - - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/Deps.scala b/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/Deps.scala index bfad701fc0b5..e28ebbfb31a8 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/Deps.scala +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/Deps.scala @@ -1,9 +1,6 @@ package millbuild - import mill.javalib.* - object Deps { val commonsText = mvn"org.apache.commons:commons-text:1.9" - val junitJupiter = mvn"org.junit.jupiter:junit-jupiter:5.9.1" } diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/ProjectBaseModule.scala b/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/ProjectBaseModule.scala index 83a2088d2eea..a98858d12f99 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/ProjectBaseModule.scala +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/mill-build/src/ProjectBaseModule.scala @@ -1,12 +1,11 @@ package millbuild - +import mill.* import mill.javalib.* - +import mill.javalib.errorprone.ErrorProneModule trait ProjectBaseModule extends MavenModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "11", "-target", "11") + def depManagement = Seq(Deps.commonsText) - def jvmId = "zulu:11" + def javacOptions = Seq("-source", "17", "-target", "17") } diff --git a/libs/init/gradle/test/resources/expected/gradle-8-0/utilities/package.mill b/libs/init/gradle/test/resources/expected/gradle-8-0/utilities/package.mill index f2e7c18dada1..0e5ef51239d3 100644 --- a/libs/init/gradle/test/resources/expected/gradle-8-0/utilities/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-8-0/utilities/package.mill @@ -1,16 +1,16 @@ package build.utilities - +import mill.* import mill.javalib.* -import mill.javalib.errorprone.* -import millbuild.* - -object `package` extends ErrorProneModule with ProjectBaseModule { +import mill.javalib.errorprone.ErrorProneModule +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule, ErrorProneModule { - def moduleDeps = super.moduleDeps ++ Seq(build.list) + def moduleDeps = Seq(build.list) def errorProneVersion = "2.28.0" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ + def errorProneJavacEnableOptions = Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") } diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/app/package.mill b/libs/init/gradle/test/resources/expected/gradle-9-0-0/app/package.mill index dc8cc4b94a1f..f1471a5a67d2 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/app/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/app/package.mill @@ -1,24 +1,12 @@ package build.app - +import mill.* import mill.javalib.* -import millbuild.* - +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends ProjectBaseModule { - def mvnDeps = super.mvnDeps() ++ Seq(Deps.commonsText) - - def moduleDeps = super.moduleDeps ++ Seq(build.utilities) - - object test extends MavenTests with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiter) - - def runMvnDeps = super.runMvnDeps() ++ Seq(Deps.junitPlatformLauncher) - - def testParallelism = false - - def testSandboxWorkingDir = false + def moduleDeps = Seq(build.utilities) - } + def mvnDeps = Seq(mvn"org.apache.commons:commons-text") } diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/build.mill b/libs/init/gradle/test/resources/expected/gradle-9-0-0/build.mill index 890787f10447..abfd1163b853 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/build.mill +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/build.mill @@ -1,7 +1,7 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system //| mill-jvm-opts: ["-Xmx2g", "-XX:MaxMetaspaceSize=512m"] package build - import mill.* - +import millbuild.Deps object `package` extends Module {} diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/list/package.mill b/libs/init/gradle/test/resources/expected/gradle-9-0-0/list/package.mill index 38af5a7dea86..0499c4868cf7 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/list/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/list/package.mill @@ -1,23 +1,34 @@ package build.list - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends ProjectPublishModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule, PublishModule { def artifactName = "list" - object test extends MavenTests with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.junitJupiter) - - def runMvnDeps = super.runMvnDeps() ++ Seq(Deps.junitPlatformLauncher) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + def pomSettings = PomSettings( + "", + "org.gradle.sample", + "http://www.example.com/library", + Seq(License( + "", + "The Apache License, Version 2.0", + "http://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "" + )), + VersionControl( + Some("http://example.com/my-library/"), + Some("scm:git:git://example.com/my-library.git"), + Some("scm:git:ssh://example.com/my-library.git"), + None + ), + Seq(Developer("johnd", "John Doe", "", None, None)) + ) + + def publishVersion = "unspecified" } diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/Deps.scala b/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/Deps.scala index 1e94387772d5..874917f9b98f 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/Deps.scala +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/Deps.scala @@ -1,10 +1,6 @@ package millbuild - import mill.javalib.* - object Deps { val commonsText = mvn"org.apache.commons:commons-text:1.13.0" - val junitJupiter = mvn"org.junit.jupiter:junit-jupiter:5.12.1" - val junitPlatformLauncher = mvn"org.junit.platform:junit-platform-launcher" } diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectBaseModule.scala b/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectBaseModule.scala index 220ca6eb4a0c..ecb49910be7b 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectBaseModule.scala +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectBaseModule.scala @@ -1,12 +1,11 @@ package millbuild - +import mill.* import mill.javalib.* - +import mill.javalib.publish.* trait ProjectBaseModule extends MavenModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "21", "-target", "21") + def depManagement = Seq(Deps.commonsText) - def jvmId = "zulu:21" + def javacOptions = Seq("-source", "21", "-target", "21") } diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectPublishModule.scala b/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectPublishModule.scala deleted file mode 100644 index 4dda0873edb0..000000000000 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/mill-build/src/ProjectPublishModule.scala +++ /dev/null @@ -1,31 +0,0 @@ -package millbuild - -import mill.javalib.* -import mill.javalib.publish.* - -trait ProjectPublishModule extends PublishModule with ProjectBaseModule { - - def pomSettings = PomSettings( - "", - "org.gradle.sample", - "http://www.example.com/library", - Seq(License( - "", - "The Apache License, Version 2.0", - "http://www.apache.org/licenses/LICENSE-2.0.txt", - false, - false, - "" - )), - VersionControl( - Some("http://example.com/my-library/"), - Some("scm:git:git://example.com/my-library.git"), - Some("scm:git:ssh://example.com/my-library.git"), - None - ), - Seq(Developer("johnd", "John Doe", "", None, None)) - ) - - def publishVersion = "unspecified" - -} diff --git a/libs/init/gradle/test/resources/expected/gradle-9-0-0/utilities/package.mill b/libs/init/gradle/test/resources/expected/gradle-9-0-0/utilities/package.mill index f05b04a567f9..8c50ff255fdb 100644 --- a/libs/init/gradle/test/resources/expected/gradle-9-0-0/utilities/package.mill +++ b/libs/init/gradle/test/resources/expected/gradle-9-0-0/utilities/package.mill @@ -1,13 +1,36 @@ package build.utilities - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends ProjectPublishModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule, PublishModule { - def moduleDeps = super.moduleDeps ++ Seq(build.list) + def moduleDeps = Seq(build.list) def artifactName = "utilities" + def pomSettings = PomSettings( + "", + "org.gradle.sample", + "http://www.example.com/library", + Seq(License( + "", + "The Apache License, Version 2.0", + "http://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "" + )), + VersionControl( + Some("http://example.com/my-library/"), + Some("scm:git:git://example.com/my-library.git"), + Some("scm:git:ssh://example.com/my-library.git"), + None + ), + Seq(Developer("johnd", "John Doe", "", None, None)) + ) + + def publishVersion = "unspecified" + } diff --git a/libs/init/gradle/test/resources/expected/with-args/gradle-8-0/build.mill b/libs/init/gradle/test/resources/expected/with-args/gradle-8-0/build.mill index dd6d1bad9ca3..b2d85032d366 100644 --- a/libs/init/gradle/test/resources/expected/with-args/gradle-8-0/build.mill +++ b/libs/init/gradle/test/resources/expected/with-args/gradle-8-0/build.mill @@ -1,85 +1,48 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* import mill.javalib.* -import mill.javalib.errorprone.* - +import mill.javalib.errorprone.ErrorProneModule object `package` extends Module { object app extends MavenModule { - def mvnDeps = super.mvnDeps() ++ - Seq(mvn"org.apache.commons:commons-text:1.9") - - def moduleDeps = super.moduleDeps ++ Seq(build.utilities) - - def javacOptions = super.javacOptions() ++ - Seq("-source", "17", "-target", "17") - - def jvmId = "zulu:17" + def moduleDeps = Seq(build.utilities) - object test extends MavenTests with TestModule.Junit5 { + def mvnDeps = Seq(mvn"org.apache.commons:commons-text") - def mvnDeps = super.mvnDeps() ++ - Seq(mvn"org.junit.jupiter:junit-jupiter:5.9.1") + def depManagement = Seq(mvn"org.apache.commons:commons-text:1.9") - def testParallelism = false - - def testSandboxWorkingDir = false - - } + def javacOptions = Seq("-source", "17", "-target", "17") } - object list extends MavenModule with ErrorProneModule { + object list extends MavenModule, ErrorProneModule { + + def depManagement = Seq(mvn"org.apache.commons:commons-text:1.9") - def javacOptions = super.javacOptions() ++ - Seq("-source", "17", "-target", "17") + def javacOptions = Seq("-source", "17", "-target", "17") def errorProneVersion = "2.28.0" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ + def errorProneJavacEnableOptions = Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") - def jvmId = "zulu:17" - - object test - extends MavenTests with ErrorProneModule with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ - Seq(mvn"org.junit.jupiter:junit-jupiter:5.9.1") - - def errorProneVersion = "2.28.0" - - def errorProneOptions = super.errorProneOptions() ++ - Seq("-XepCompilingTestOnlyCode") - - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - object utilities extends MavenModule with ErrorProneModule { + object utilities extends MavenModule, ErrorProneModule { + + def moduleDeps = Seq(build.list) - def moduleDeps = super.moduleDeps ++ Seq(build.list) + def depManagement = Seq(mvn"org.apache.commons:commons-text:1.9") - def javacOptions = super.javacOptions() ++ - Seq("-source", "17", "-target", "17") + def javacOptions = Seq("-source", "17", "-target", "17") def errorProneVersion = "2.28.0" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ + def errorProneJavacEnableOptions = Seq("-XDshould-stop.ifError=FLOW", "-XDshouldStopPolicyIfError=FLOW") - def jvmId = "zulu:17" - } - } diff --git a/libs/init/gradle/test/resources/gradle-6-0/build.gradle b/libs/init/gradle/test/resources/gradle-6-0/build.gradle deleted file mode 100644 index c6da63f77a8f..000000000000 --- a/libs/init/gradle/test/resources/gradle-6-0/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id 'java' - id 'application' -} -repositories { - jcenter() -} -dependencies { - implementation 'com.google.guava:guava:28.0-jre' - testImplementation 'org.testng:testng:6.14.3' -} -application { - mainClassName = 'gradle.App' -} -test { - useTestNG() -} diff --git a/libs/init/gradle/test/resources/gradle-6-0/settings.gradle b/libs/init/gradle/test/resources/gradle-6-0/settings.gradle deleted file mode 100644 index e8ab9bed3e36..000000000000 --- a/libs/init/gradle/test/resources/gradle-6-0/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'gradle-6-0' diff --git a/libs/init/gradle/test/resources/gradle-7-0/gradle/wrapper/gradle-wrapper.properties b/libs/init/gradle/test/resources/gradle-7-0/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index f371643eed77..000000000000 --- a/libs/init/gradle/test/resources/gradle-7-0/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/libs/init/gradle/test/resources/gradle-7-0/list/src/test/.keep b/libs/init/gradle/test/resources/gradle-7-0/list/src/test/.keep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/libs/init/gradle/test/resources/gradle-7-0/app/build.gradle b/libs/init/gradle/test/resources/gradle-7-2/app/build.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/app/build.gradle rename to libs/init/gradle/test/resources/gradle-7-2/app/build.gradle diff --git a/libs/init/gradle/test/resources/gradle-6-0/src/test/.keep b/libs/init/gradle/test/resources/gradle-7-2/app/src/test/.keep similarity index 100% rename from libs/init/gradle/test/resources/gradle-6-0/src/test/.keep rename to libs/init/gradle/test/resources/gradle-7-2/app/src/test/.keep diff --git a/libs/init/gradle/test/resources/gradle-7-0/buildSrc/build.gradle b/libs/init/gradle/test/resources/gradle-7-2/buildSrc/build.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/buildSrc/build.gradle rename to libs/init/gradle/test/resources/gradle-7-2/buildSrc/build.gradle diff --git a/libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-application-conventions.gradle b/libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-application-conventions.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-application-conventions.gradle rename to libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-application-conventions.gradle diff --git a/libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-common-conventions.gradle b/libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-common-conventions.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-common-conventions.gradle rename to libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-common-conventions.gradle diff --git a/libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-library-conventions.gradle b/libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-library-conventions.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/buildSrc/src/main/groovy/gradle.java-library-conventions.gradle rename to libs/init/gradle/test/resources/gradle-7-2/buildSrc/src/main/groovy/gradle.java-library-conventions.gradle diff --git a/libs/init/gradle/test/resources/gradle-6-0/gradle/wrapper/gradle-wrapper.properties b/libs/init/gradle/test/resources/gradle-7-2/gradle/wrapper/gradle-wrapper.properties similarity index 93% rename from libs/init/gradle/test/resources/gradle-6-0/gradle/wrapper/gradle-wrapper.properties rename to libs/init/gradle/test/resources/gradle-7-2/gradle/wrapper/gradle-wrapper.properties index 6ce793f21e85..ffed3a254e91 100644 --- a/libs/init/gradle/test/resources/gradle-6-0/gradle/wrapper/gradle-wrapper.properties +++ b/libs/init/gradle/test/resources/gradle-7-2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/libs/init/gradle/test/resources/gradle-7-0/list/build.gradle b/libs/init/gradle/test/resources/gradle-7-2/list/build.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/list/build.gradle rename to libs/init/gradle/test/resources/gradle-7-2/list/build.gradle diff --git a/libs/init/gradle/test/resources/gradle-7-0/app/src/test/.keep b/libs/init/gradle/test/resources/gradle-7-2/list/src/test/.keep similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/app/src/test/.keep rename to libs/init/gradle/test/resources/gradle-7-2/list/src/test/.keep diff --git a/libs/init/gradle/test/resources/gradle-7-0/settings.gradle b/libs/init/gradle/test/resources/gradle-7-2/settings.gradle similarity index 52% rename from libs/init/gradle/test/resources/gradle-7-0/settings.gradle rename to libs/init/gradle/test/resources/gradle-7-2/settings.gradle index 3d000231bf3b..872f49ddc113 100644 --- a/libs/init/gradle/test/resources/gradle-7-0/settings.gradle +++ b/libs/init/gradle/test/resources/gradle-7-2/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'gradle-7-0' +rootProject.name = 'gradle-7-2' include('app', 'list', 'utilities') diff --git a/libs/init/gradle/test/resources/gradle-7-0/utilities/build.gradle b/libs/init/gradle/test/resources/gradle-7-2/utilities/build.gradle similarity index 100% rename from libs/init/gradle/test/resources/gradle-7-0/utilities/build.gradle rename to libs/init/gradle/test/resources/gradle-7-2/utilities/build.gradle diff --git a/libs/init/gradle/test/src/mill/main/gradle/GradleBuildGenTests.scala b/libs/init/gradle/test/src/mill/main/gradle/GradleBuildGenTests.scala index f4eaf7d48fa4..3b23f16b65a7 100644 --- a/libs/init/gradle/test/src/mill/main/gradle/GradleBuildGenTests.scala +++ b/libs/init/gradle/test/src/mill/main/gradle/GradleBuildGenTests.scala @@ -6,41 +6,31 @@ import utest.* object GradleBuildGenTests extends TestSuite { def tests = Tests { val checker = BuildGenChecker() - test("6.0") { + test("7.2") { assert(checker.check( - generate = GradleBuildGenMain.main(Array("--gradle-jvm-id", "11")), - sourceRel = os.sub / "gradle-6-0", - expectedRel = os.sub / "expected/gradle-6-0" - )) - } - test("7.0") { - assert(checker.check( - generate = GradleBuildGenMain.main(Array("--gradle-jvm-id", "11")), - sourceRel = os.sub / "gradle-7-0", - expectedRel = os.sub / "expected/gradle-7-0" + sourceRel = os.sub / "gradle-7-2", + expectedRel = os.sub / "expected/gradle-7-2" )) } test("8.0") { assert(checker.check( - generate = GradleBuildGenMain.main(Array("--gradle-jvm-id", "11")), sourceRel = "gradle-8-0", expectedRel = os.sub / "expected/gradle-8-0" )) } test("9.0.0") { assert(checker.check( - generate = GradleBuildGenMain.main(Array("--gradle-jvm-id", "17")), sourceRel = "gradle-9-0-0", - expectedRel = os.sub / "expected/gradle-9-0-0" + expectedRel = os.sub / "expected/gradle-9-0-0", + envJvmId = "zulu:21" )) } test("with-args") { test("8.0") { assert(checker.check( - generate = - GradleBuildGenMain.main(Array("--merge", "--no-meta", "--gradle-jvm-id", "17")), sourceRel = "gradle-8-0", - expectedRel = os.sub / "expected/with-args/gradle-8-0" + expectedRel = os.sub / "expected/with-args/gradle-8-0", + mainArgs = Seq("--merge", "--no-meta") )) } } diff --git a/libs/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/libs/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index 9370f223a099..ee79ebd31324 100644 --- a/libs/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/libs/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -1,23 +1,20 @@ package mill.main.maven import mill.main.buildgen.* -import mill.main.buildgen.ModuleConfig.* +import mill.main.buildgen.ModuleSpec.* import org.apache.maven.model.{Developer as MvnDeveloper, License as MvnLicense, *} import scala.jdk.CollectionConverters.* -/** - * Application that imports a Maven build to Mill. - */ object MavenBuildGenMain { def main(args: Array[String]): Unit = mainargs.Parser(this).runOrExit(args.toSeq) - @mainargs.main(doc = "Imports a Maven build located in the current working directory.") - def runImport( - @mainargs.arg(doc = "extract properties for publish") + @mainargs.main(doc = "Generates Mill build files that are derived from a Maven build.") + def init( + @mainargs.arg(doc = "include properties from pom.xml in the generated build") publishProperties: mainargs.Flag, - @mainargs.arg(doc = "merge generated build files") + @mainargs.arg(doc = "merge package.mill files in to the root build.mill file") merge: mainargs.Flag, @mainargs.arg(doc = "disable generating meta-build files") noMeta: mainargs.Flag @@ -25,173 +22,205 @@ object MavenBuildGenMain { println("converting Maven build") val modelBuildingResults = Modeler().buildAll() - val moduleDepsByGav = modelBuildingResults.map { result => - val model = result.getEffectiveModel - val segments = os.Path(model.getProjectDirectory).relativeTo(os.pwd).segments - val gav = (model.getGroupId, model.getArtifactId, model.getVersion) - (gav, ModuleDep(segments)) - }.toMap + val moduleDepLookup: PartialFunction[Dependency, ModuleDep] = + modelBuildingResults.map { result => + val model = result.getEffectiveModel + val key = (model.getGroupId, model.getArtifactId, model.getVersion) + val moduleDep = ModuleDep(os.Path(model.getProjectDirectory).subRelativeTo(os.pwd)) + key -> moduleDep + }.toMap.compose { + case dep: Dependency => (dep.getGroupId, dep.getArtifactId, dep.getVersion) + } + val packages = modelBuildingResults.map { result => val model = result.getEffectiveModel - val plugins = Plugins(model) val moduleDir = os.Path(model.getProjectDirectory) - val segments = moduleDir.relativeTo(os.pwd).segments - - def isBom(dep: Dependency) = isBomDep(dep.getGroupId, dep.getArtifactId) - def dependencies(scopes: Seq[String]) = model.getDependencies.asScala - .filter(dep => scopes.contains(dep.getScope)) - def mvnDeps(scopes: String*) = dependencies(scopes).collect { - case dep if !moduleDepsByGav.contains(toGav(dep)) && !isBom(dep) => toMvnDep(dep) - }.toSeq - def bomMvnDeps(scopes: String*) = dependencies(scopes).collect { - case dep if !moduleDepsByGav.contains(toGav(dep)) && isBom(dep) => toMvnDep(dep) - }.toSeq - def moduleDeps(scopes: String*) = - dependencies(scopes).map(toGav).collect(moduleDepsByGav).toSeq - - val mainCoursierModule = model.getRepositories.asScala.collect { - case repo if repo.getId != "central" => repo.getUrl - }.toSeq match { - case Nil => None - case repositories => Some(CoursierModule(repositories)) - } - // TODO Filter error-prone deps whitelist - val errorProneMvnDeps = plugins.javacAnnotationProcessorMvnDeps - val (mainErrorProneModule, mainJavacOptions) = - ErrorProneModule.find(plugins.javacOptions, errorProneMvnDeps) - val mainJavaHomeModule = JavaHomeModule.find(plugins.javaVersion, mainJavacOptions) - val mainJavaModule = JavaModule( - mvnDeps = mvnDeps("compile"), - compileMvnDeps = mvnDeps("provided"), - runMvnDeps = mvnDeps("runtime"), - bomMvnDeps = bomMvnDeps("compile"), - moduleDeps = moduleDeps("compile"), - compileModuleDeps = moduleDeps("provided"), - runModuleDeps = moduleDeps("runtime"), - javacOptions = mainJavacOptions, - artifactName = model.getArtifactId - ) - val mainPublishModule = Option.when(!plugins.skipDeploy) { - // Use raw model to avoid publishing any derived values returned by the effective model. - val rawModel = result.getRawModel - PublishModule( - pomPackagingType = PublishModule.pomPackagingTypeOverride(rawModel.getPackaging), - pomParentProject = toPomParentProject(rawModel.getParent), - pomSettings = toPomSettings(rawModel), - publishVersion = model.getVersion, - publishProperties = - if (publishProperties.value) rawModel.getProperties.asScala.toMap else Map() - ) - } + val plugins = Plugins(model) - val testModule = if (os.exists(moduleDir / "src/test")) - TestModule.mixin(mvnDeps("test")).map { testModuleMixin => - // provided dependencies are included in run scope to reproduce Maven behavior - val testJavaModule = JavaModule( - mvnDeps = mvnDeps("test"), + var mainModule = ModuleSpec( + name = moduleDir.last, + repositories = model.getRepositories.iterator.asScala.collect { + case repo if repo.getId != "central" => repo.getUrl + }.toSeq + ) + model.getPackaging match { + case "pom" if model.getModules.isEmpty && !os.exists(moduleDir / "src") => + val (bomMvnDeps, depManagement, moduleDeps, bomModuleDeps) = + Option(model.getDependencyManagement).fold((Nil, Nil, Nil, Nil)) { dm => + val (boms, deps) = dm.getDependencies.iterator.asScala.toSeq.partition(isBom) + val (bomMvnDeps, bomModuleDeps) = + boms.partitionMap(dep => moduleDepLookup.lift(dep).toRight(toMvnDep(dep))) + val (depManagement, moduleDeps) = + deps.partitionMap(dep => moduleDepLookup.lift(dep).toRight(toMvnDep(dep))) + (bomMvnDeps, depManagement, moduleDeps, bomModuleDeps) + } + mainModule = mainModule.copy( + imports = "import mill.javalib.*" +: mainModule.imports, + supertypes = "JavaModule" +: "BomModule" +: mainModule.supertypes, + bomMvnDeps = bomMvnDeps, + depManagement = depManagement, + moduleDeps = moduleDeps, + bomModuleDeps = bomModuleDeps + ) + case _ => + val (modules, deps) = + model.getDependencies.iterator.asScala.toSeq.partition(moduleDepLookup.isDefinedAt) + def mvnDeps(scope: String) = deps.collect { + case dep if dep.getScope == scope => toMvnDep(dep) + } + def moduleDeps(scope: String) = modules.collect { + case dep if dep.getScope == scope => moduleDepLookup(dep) + } + val (bomMvnDeps, bomModuleDeps) = + Option(model.getDependencyManagement).fold((Nil, Nil)) { dm => + val boms = dm.getDependencies.iterator.asScala.filter(isBom).toSeq + boms.partitionMap(dep => moduleDepLookup.lift(dep).toRight(toMvnDep(dep))) + } + + mainModule = mainModule.copy( + imports = "import mill.javalib.*" +: mainModule.imports, + supertypes = "MavenModule" +: mainModule.supertypes, + mvnDeps = mvnDeps("compile"), compileMvnDeps = mvnDeps("provided"), - runMvnDeps = mvnDeps("provided"), - bomMvnDeps = bomMvnDeps("test"), - moduleDeps = moduleDeps("test"), + runMvnDeps = mvnDeps("runtime"), + bomMvnDeps = bomMvnDeps, + moduleDeps = moduleDeps("compile"), compileModuleDeps = moduleDeps("provided"), - runModuleDeps = moduleDeps("provided") + runModuleDeps = moduleDeps("runtime"), + bomModuleDeps = bomModuleDeps, + javacOptions = plugins.javacOptions, + artifactName = Option(model.getArtifactId) ) - // ErrorProne is applied to test sources by default - val testErrorProneModule = mainErrorProneModule - val testConfigs = Seq(testJavaModule) ++ testErrorProneModule ++ - // reproduce Maven behavior - Seq( - RunModule( - forkWorkingDir = "moduleDir" - ), - TestModule( - testParallelism = "false", - testSandboxWorkingDir = "false" + + val errorProneDeps = plugins.errorProneMvnDeps + if (errorProneDeps.nonEmpty) { + mainModule = mainModule.withErrorProneModule(errorProneDeps) + } + + if (os.exists(moduleDir / "src/test")) { + val testMvnDeps = mvnDeps("test") + ModuleSpec.testModuleMixin(testMvnDeps).foreach { mixin => + val testModuleDeps = modules.collect { + case dep if dep.getScope == "test" => + moduleDepLookup(dep).copy(nestedModule = + Option.when(dep.getType == "test-jar")("test") + ) + } + var testModule = ModuleSpec( + name = "test", + supertypes = Seq("MavenTests"), + mixins = Seq(mixin), + forkArgs = plugins.testForkArgs, + forkWorkingDir = Some(os.rel), + mvnDeps = testMvnDeps, + compileMvnDeps = mainModule.compileMvnDeps, + runMvnDeps = mainModule.compileMvnDeps.base ++ mainModule.runMvnDeps.base, + moduleDeps = Values(extend = true, testModuleDeps), + compileModuleDeps = mainModule.compileModuleDeps, + runModuleDeps = mainModule.compileModuleDeps.base ++ mainModule.runModuleDeps.base, + testParallelism = Some(false), + testSandboxWorkingDir = Some(false) ) - ) - val testSupertypes = "MavenTests" +: testConfigs.collect { - case _: ErrorProneModule => "ErrorProneModule" + mixin match { + // Maven can resolve junit-platform-launcher version using junit-bom transitive + // dependency. Since Coursier cannot do the same, make the dependency explicit. + case "TestModule.Junit5" + if testModule.runMvnDeps.base.exists(dep => + dep.name == "junit-platform-launcher" && dep.organization == "org.junit.platform" && dep.version.isEmpty + ) && !mainModule.depManagement.base.exists(dep => + dep.name == "junit-platform-launcher" && dep.organization == "org.junit.platform" + ) && !mainModule.bomMvnDeps.base.exists(dep => + dep.name == "junit-bom" && dep.organization == "org.junit" + ) => + testModule.mvnDeps.base.collectFirst { + case dep if dep.organization == "org.junit.jupiter" && dep.version.nonEmpty => + dep.version + }.foreach { junitVersion => + val junitBom = MvnDep("org.junit", "junit-bom", junitVersion) + testModule = testModule.copy(bomMvnDeps = Values(extend = true, Seq(junitBom))) + } + case _ => + } + + mainModule = mainModule.copy(test = Some(testModule)) + } } - ModuleSpec( - name = "test", - supertypes = testSupertypes, - mixins = Seq(testModuleMixin), - configs = testConfigs - ) - } - else None - - val mainConfigs = mainJavaModule +: Seq( - mainErrorProneModule, - mainJavaHomeModule, - mainPublishModule, - mainCoursierModule - ).flatten - val mainSupertypes = "MavenModule" +: mainConfigs.collect { - case _: PublishModule => "PublishModule" - case _: ErrorProneModule => "ErrorProneModule" } - val mainModule = ModuleSpec( - name = segments.lastOption.getOrElse(os.pwd.last), - supertypes = mainSupertypes, - configs = mainConfigs, - nestedModules = testModule.toSeq - ) - PackageSpec(segments, mainModule) + + if (!plugins.skipDeploy) { + mainModule = mainModule.copy( + imports = + "import mill.javalib.*" +: "import mill.javalib.publish.*" +: mainModule.imports, + supertypes = mainModule.supertypes :+ "PublishModule", + pomPackagingType = Option(model.getPackaging).filter(_ != "jar"), + pomParentProject = toPomParentProject(model.getParent), + // Use raw model since the effective one returns derived values for URL fields. + pomSettings = Some(toPomSettings(result.getRawModel)), + publishVersion = Option(model.getVersion), + publishProperties = + if (publishProperties.value) model.getProperties.asScala.toSeq else Nil + ) + } + + PackageSpec(moduleDir.subRelativeTo(os.pwd), mainModule) } - var build = BuildSpec.fill(packages) - if (merge.value) build = build.merged - if (!noMeta.value) build = build.withDefaultMetaBuild - BuildWriter(build).writeFiles() + val (depRefs, packages0) = + if (noMeta.value) (Nil, packages) else BuildGen.withNamedDeps(packages) + val (baseModule, packages1) = + Option.when(!noMeta.value)(BuildGen.withBaseModule(packages0, "MavenTests", "MavenModule")) + .flatten.fold((None, packages0))((base, packages) => (Some(base), packages)) + BuildGen.writeBuildFiles(packages1, merge.value, depRefs, baseModule) } - private def toGav(dep: Dependency): (String, String, String) = - (dep.getGroupId, dep.getArtifactId, dep.getVersion) + private def isBom(dep: Dependency) = dep.getScope == "import" && dep.getType == "pom" - private def toMvnDep(dep: Dependency): MvnDep = { + private def toMvnDep(dep: Dependency) = { import dep.* MvnDep( organization = getGroupId, name = getArtifactId, - version = Option(getVersion), - // prevent evaluation of dynamic values, such as ${os.detected.name}, in generated build - classifier = Option(getClassifier).map(_.replace("$", "$$")), - `type` = Option(getType), + version = Option(getVersion).getOrElse(""), + // Sanitize unresolved properties such as ${os.detected.name} to prevent interpolation. + classifier = Option(getClassifier).map(_.replaceAll("[$]", "")), + `type` = getType match { + case null | "jar" | "pom" => None + case tpe => Some(tpe) + }, excludes = getExclusions.asScala.map(x => (x.getGroupId, x.getArtifactId)).toSeq ) } - private def toPomParentProject(parent: Parent): Artifact = { - if (parent == null) null + private def toPomParentProject(parent: Parent) = { + if (parent == null) None else { import parent.* - Artifact(getGroupId, getArtifactId, getVersion) + Some(Artifact(getGroupId, getArtifactId, getVersion)) } } - private def toPomSettings(model: Model): PomSettings = { + private def toPomSettings(model: Model) = { import model.* PomSettings( - description = Option(getDescription), - organization = Option(getGroupId), - url = Option(getUrl), + description = Option(getDescription).getOrElse(""), + organization = Option(getGroupId).getOrElse(""), + url = Option(getUrl).getOrElse(""), licenses = getLicenses.asScala.map(toLicense).toSeq, versionControl = toVersionControl(getScm), developers = getDevelopers.asScala.map(toDeveloper).toSeq ) } - private def toLicense(license: MvnLicense): License = { + private def toLicense(license: MvnLicense) = { import license.* License( - name = getName, - url = getUrl, + name = Option(getName).getOrElse(""), + url = Option(getUrl).getOrElse(""), distribution = getDistribution ) } - private def toVersionControl(scm: Scm): VersionControl = { + private def toVersionControl(scm: Scm) = { if (scm == null) VersionControl() else import scm.* @@ -203,12 +232,12 @@ object MavenBuildGenMain { ) } - private def toDeveloper(developer: MvnDeveloper): Developer = { + private def toDeveloper(developer: MvnDeveloper) = { import developer.* Developer( - id = getId, - name = getName, - url = getUrl, + id = Option(getId).getOrElse(""), + name = Option(getName).getOrElse(""), + url = Option(getUrl).getOrElse(""), organization = Option(getOrganization), organizationUrl = Option(getOrganizationUrl) ) diff --git a/libs/init/maven/src/mill/main/maven/Modeler.scala b/libs/init/maven/src/mill/main/maven/Modeler.scala index fc275a6f8635..e3cf330857b7 100644 --- a/libs/init/maven/src/mill/main/maven/Modeler.scala +++ b/libs/init/maven/src/mill/main/maven/Modeler.scala @@ -10,14 +10,7 @@ import java.io.File import java.util.Properties import scala.jdk.CollectionConverters.* -/** - * The implementation is inspired by [[https://github.com/sbt/sbt-pom-reader/ sbt-pom-reader]]. - */ -class Modeler( - builder: ModelBuilder, - resolver: ModelResolver, - systemProperties: Properties -) { +class Modeler(builder: ModelBuilder, resolver: ModelResolver, systemProperties: Properties) { /** Returns the [[ModelBuildingResult]] for all projects in `workspace`. */ def buildAll(workspace: os.Path = os.pwd): Seq[ModelBuildingResult] = { @@ -35,11 +28,17 @@ class Modeler( def build(pomFile: File): ModelBuildingResult = { val request = new DefaultModelBuildingRequest() request.setPomFile(pomFile) - request.setModelResolver(resolver.newCopy()) + request.setModelResolver(resolver) request.setSystemProperties(systemProperties) - + request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL) + request.setTwoPhaseBuilding(true) try { - builder.build(request) + val result1 = builder.build(request) + val depMgmt1 = Option(result1.getEffectiveModel.getDependencyManagement).map(_.clone) + val result2 = builder.build(request, result1) + // Restore dep mgmt from Phase 1 since Phase 2 substitutes BOM deps with their components. + depMgmt1.foreach(result2.getEffectiveModel.setDependencyManagement) + result2 } catch { case e: ModelBuildingException => e.getProblems.asScala.foreach(problem => println(s"ignoring $problem")) @@ -73,8 +72,9 @@ object Modeler { ) def defaultSystemProperties: Properties = { - val props = new Properties(System.getProperties) + val props = new Properties() System.getenv().forEach((k, v) => props.put(s"env.$k", v)) + System.getProperties.forEach((k, v) => props.put(k, v)) props } } diff --git a/libs/init/maven/src/mill/main/maven/Plugins.scala b/libs/init/maven/src/mill/main/maven/Plugins.scala index c3f3f76d555d..d9ad2a2af715 100644 --- a/libs/init/maven/src/mill/main/maven/Plugins.scala +++ b/libs/init/maven/src/mill/main/maven/Plugins.scala @@ -1,119 +1,68 @@ package mill.main.maven -import mill.main.buildgen.ModuleConfig -import org.apache.maven.artifact.versioning.VersionRange -import org.apache.maven.model.{ConfigurationContainer, Model, Plugin} +import mill.main.buildgen.ModuleSpec.{MvnDep, Opt} +import org.apache.maven.model.Model import org.codehaus.plexus.util.xml.Xpp3Dom import scala.jdk.CollectionConverters.* +import org.apache.maven.model.Plugin class Plugins(model: Model) { - def javacAnnotationProcessorMvnDeps: Seq[ModuleConfig.MvnDep] = - mavenCompilerPlugin.flatMap(configDom).fold(Nil)( - children(_, "annotationProcessorPaths", "path") - .flatMap(dom => + def javacOptions: Seq[Opt] = config(_.getArtifactId == "maven-compiler-plugin").fold(Nil) { dom => + value(dom, "release").fold(Seq( + value(dom, "source").map(Opt("-source", _)), + value(dom, "target").map(Opt("-target", _)) + ).flatten)(s => Seq(Opt("--release", s))) ++ + value(dom, "encoding").map(Opt("-encoding", _)) ++ + Opt.groups(values(dom, "compilerArgs")) + } + + def errorProneMvnDeps: Seq[MvnDep] = config(_.getArtifactId == "maven-compiler-plugin") + .filter(values(_, "compilierArgs").exists(_.startsWith("-Xplugin:ErrorProne"))) + .fold(Nil)(children(_, "annotationProcessorPaths", "path").flatMap { dom => + for { + organization <- value(dom, "groupId") + name <- value(dom, "artifactId") + version = value(dom, "version").getOrElse("") + excludes = children(dom, "exclusions").flatMap { dom => for { groupId <- value(dom, "groupId") artifactId <- value(dom, "artifactId") - version = value(dom, "version") - exclusions = children(dom, "exclusions").flatMap(dom => - for { - groupId <- value(dom, "groupId") - artifactId <- value(dom, "artifactId") - } yield (groupId, artifactId) - ) - } yield ModuleConfig.MvnDep( - organization = groupId, - name = artifactId, - version = version, - excludes = exclusions - ) - ) - ) - - def javacOptions: Seq[String] = - mavenCompilerPlugin.flatMap(configDom).fold(Nil) { dom => - def opts(name: String) = value(dom, name).fold(Nil)(Seq(s"-$name", _)) - val opts0 = value(dom, "release").filter(_.nonEmpty).fold( - opts("source") ++ opts("target") - )(Seq("--release", _)) - opts0 ++ opts("encoding") ++ values(dom, "compilerArgs") + } yield (groupId, artifactId) + } + } yield MvnDep(organization, name, version, excludes = excludes) + }) + + def skipDeploy: Boolean = config(_.getArtifactId == "maven-deploy-plugin") + .flatMap(value(_, "skip")).fold(false)(_.toBoolean) + + def testForkArgs: Seq[Opt] = config(_.getArtifactId == "maven-surefire-plugin").fold(Nil)( + children(_, "systemPropertyVariables").map { dom => + val key = dom.getName + val value = dom.getValue + Opt(s"-D$key=$value") } + ) - def javaVersion: Option[Int] = - mavenCompilerPlugin - .flatMap(configDom) - .flatMap(value(_, "jdkToolchain", "version")) - .orElse(mavenToolchainsPlugin - .flatMap(configDom) - .flatMap(value(_, "toolchains", "jdk", "version"))) - .orElse(mavenEnforcerPlugin - .flatMap(_.getExecutions.asScala.find(_.getGoals.contains("enforce"))) - .flatMap(configDom) - .flatMap(value(_, "rules", "requireJavaVersion", "version"))) - .flatMap { spec => - val range = VersionRange.createFromVersionSpec(spec) - Option(range.getRecommendedVersion) - .orElse(range.getRestrictions.asScala.headOption.flatMap(r => Option(r.getLowerBound))) - .map(v => if (v.getMajorVersion == 1) v.getMinorVersion else v.getMajorVersion) - } - - def skipDeploy: Boolean = - mavenDeployPlugin.flatMap(configDom).flatMap(value(_, "skip")).fold(false)(_.toBoolean) - - /** - * @see [[https://maven.apache.org/plugins/maven-compiler-plugin/index.html]] - */ - def mavenCompilerPlugin: Option[Plugin] = - findPlugin("org.apache.maven.plugins", "maven-compiler-plugin") - - /** - * @see [[https://maven.apache.org/plugins/maven-deploy-plugin/index.html]] - */ - def mavenDeployPlugin: Option[Plugin] = - findPlugin("org.apache.maven.plugins", "maven-deploy-plugin") - - /** - * @see [[https://maven.apache.org/enforcer/maven-enforcer-plugin/index.html]] - */ - def mavenEnforcerPlugin: Option[Plugin] = - findPlugin("org.apache.maven.plugins", "maven-enforcer-plugin") + private def config(p: Plugin => Boolean): Option[Xpp3Dom] = + model.getBuild.getPlugins.asScala.find(p).flatMap(_.getConfiguration match { + case dom: Xpp3Dom => Some(dom) + case _ => None + }) - /** - * @see [[https://maven.apache.org/plugins/maven-toolchains-plugin/index.html]] - */ - def mavenToolchainsPlugin: Option[Plugin] = - findPlugin("org.apache.maven.plugins", "maven-toolchains-plugin") - - def findPlugin(groupId: String, artifactId: String): Option[Plugin] = - model.getBuild.getPlugins.asScala - .find(p => p.getGroupId == groupId && p.getArtifactId == artifactId) - - def configDom(cc: ConfigurationContainer): Option[Xpp3Dom] = cc.getConfiguration match { - case dom: Xpp3Dom => Some(dom) - case _ => None - } - - def children(dom: Xpp3Dom, names: String*): Seq[Xpp3Dom] = + private def children(dom: Xpp3Dom, names: String*): Seq[Xpp3Dom] = if (dom == null) Nil else if (names.isEmpty) dom.getChildren.toSeq else dom.getChildren(names.head).toSeq.flatMap(children(_, names.tail*)) - def value(dom: Xpp3Dom, names: String*): Option[String] = + private def value(dom: Xpp3Dom, names: String*): Option[String] = if (null == dom) None - else if (names.isEmpty) value0(dom) + else if (names.isEmpty) Option(dom.getValue) else value(dom.getChild(names.head), names.tail*) - def values(dom: Xpp3Dom, names: String*): Seq[String] = + private def values(dom: Xpp3Dom, names: String*): Seq[String] = if (dom == null) Nil - else if (names.isEmpty) dom.getChildren.toSeq.flatMap(value0) + else if (names.isEmpty) dom.getChildren.toSeq.flatMap(dom => Option(dom.getValue)) else dom.getChildren(names.head).toSeq.flatMap(values(_, names.tail*)) - - def value0(dom: Xpp3Dom): Option[String] = dom.getValue match { - case null | "" => None - // This could happen for a BOM module that references a property defined in the root POM. - case s"$${$prop}" => Option(model.getProperties.getProperty(prop)) - case value => Some(value) - } } diff --git a/libs/init/maven/test/resources/expected/maven-samples/build.mill b/libs/init/maven/test/resources/expected/maven-samples/build.mill index c3eebeadbc6e..c0135c08378b 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/build.mill +++ b/libs/init/maven/test/resources/expected/maven-samples/build.mill @@ -1,11 +1,12 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenSamplesBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { def artifactName = "parent" diff --git a/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/Deps.scala b/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/Deps.scala index 3ebfdd8b777e..aa97152d2e0a 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/Deps.scala +++ b/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val hamcrestCore = mvn"org.hamcrest:hamcrest-core:1.2.1" diff --git a/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/MavenSamplesBaseModule.scala b/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/MavenSamplesBaseModule.scala deleted file mode 100644 index 48f9326cb9e0..000000000000 --- a/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/MavenSamplesBaseModule.scala +++ /dev/null @@ -1,10 +0,0 @@ -package millbuild - -import mill.javalib.* -import mill.javalib.publish.* - -trait MavenSamplesBaseModule extends MavenModule with PublishModule { - - def publishVersion = "1.0-SNAPSHOT" - -} diff --git a/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/ProjectBaseModule.scala b/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/ProjectBaseModule.scala new file mode 100644 index 000000000000..ec8886708078 --- /dev/null +++ b/libs/init/maven/test/resources/expected/maven-samples/mill-build/src/ProjectBaseModule.scala @@ -0,0 +1,25 @@ +package millbuild +import mill.* +import mill.javalib.* +import mill.javalib.publish.* +trait ProjectBaseModule extends MavenModule, PublishModule { + + def publishVersion = "1.0-SNAPSHOT" + + trait Tests extends MavenTests, TestModule.Junit4 { + + def mvnDeps = Seq( + Deps.junitDep, + Deps.hamcrestCore, + Deps.hamcrestLibrary, + Deps.mockitoCore + ) + + def forkWorkingDir = moduleDir + + def testParallelism = false + + def testSandboxWorkingDir = false + + } +} diff --git a/libs/init/maven/test/resources/expected/maven-samples/multi-module/package.mill b/libs/init/maven/test/resources/expected/maven-samples/multi-module/package.mill index fa2cb300e2e8..edec60458da8 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/multi-module/package.mill +++ b/libs/init/maven/test/resources/expected/maven-samples/multi-module/package.mill @@ -1,18 +1,15 @@ package build.`multi-module` - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenSamplesBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "multi-module-parent" - def jvmId = "zulu:11" - def pomPackagingType = "pom" def pomSettings = PomSettings( diff --git a/libs/init/maven/test/resources/expected/maven-samples/multi-module/server/package.mill b/libs/init/maven/test/resources/expected/maven-samples/multi-module/server/package.mill index 74071cee72f3..066d9c3e54a1 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/multi-module/server/package.mill +++ b/libs/init/maven/test/resources/expected/maven-samples/multi-module/server/package.mill @@ -1,18 +1,15 @@ package build.`multi-module`.server - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenSamplesBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "server" - def jvmId = "zulu:11" - def pomParentProject = Some( Artifact("com.example.maven-samples", "multi-module-parent", "1.0-SNAPSHOT") ) @@ -26,21 +23,6 @@ object `package` extends MavenSamplesBaseModule { Seq() ) - object test extends MavenTests with TestModule.Junit4 { - - def mvnDeps = super.mvnDeps() ++ Seq( - Deps.junitDep, - Deps.hamcrestCore, - Deps.hamcrestLibrary, - Deps.mockitoCore - ) - - def forkWorkingDir = moduleDir - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + object test extends Tests {} } diff --git a/libs/init/maven/test/resources/expected/maven-samples/multi-module/webapp/package.mill b/libs/init/maven/test/resources/expected/maven-samples/multi-module/webapp/package.mill index 04d63535f5bf..60316e46d505 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/multi-module/webapp/package.mill +++ b/libs/init/maven/test/resources/expected/maven-samples/multi-module/webapp/package.mill @@ -1,23 +1,19 @@ package build.`multi-module`.webapp - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenSamplesBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { - def compileMvnDeps = super.compileMvnDeps() ++ - Seq(Deps.servletApi, Deps.jspApi) + def moduleDeps = Seq(build.`multi-module`.server) - def moduleDeps = super.moduleDeps ++ Seq(build.`multi-module`.server) + def compileMvnDeps = Seq(Deps.servletApi, Deps.jspApi) - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "webapp" - def jvmId = "zulu:11" - def pomPackagingType = "war" def pomParentProject = Some( diff --git a/libs/init/maven/test/resources/expected/maven-samples/single-module/package.mill b/libs/init/maven/test/resources/expected/maven-samples/single-module/package.mill index 42deca2fc205..43845300d2f7 100644 --- a/libs/init/maven/test/resources/expected/maven-samples/single-module/package.mill +++ b/libs/init/maven/test/resources/expected/maven-samples/single-module/package.mill @@ -1,20 +1,17 @@ package build.`single-module` - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenSamplesBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { - def mvnDeps = super.mvnDeps() ++ Seq(Deps.servletApi, Deps.jspApi) + def mvnDeps = Seq(Deps.servletApi, Deps.jspApi) - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "single-module-project" - def jvmId = "zulu:11" - def pomSettings = PomSettings( "Sample single module Maven project with a working, deployable site.", "com.example.maven-samples", @@ -29,21 +26,6 @@ object `package` extends MavenSamplesBaseModule { Seq() ) - object test extends MavenTests with TestModule.Junit4 { - - def mvnDeps = super.mvnDeps() ++ Seq( - Deps.junitDep, - Deps.hamcrestCore, - Deps.hamcrestLibrary, - Deps.mockitoCore - ) - - def forkWorkingDir = moduleDir - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + object test extends Tests {} } diff --git a/libs/init/maven/test/resources/expected/quickstart/build.mill b/libs/init/maven/test/resources/expected/quickstart/build.mill index 73ca114dcf8a..b99ad578ed57 100644 --- a/libs/init/maven/test/resources/expected/quickstart/build.mill +++ b/libs/init/maven/test/resources/expected/quickstart/build.mill @@ -1,22 +1,26 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - +import mill.* import mill.javalib.* -import mill.javalib.errorprone.* import mill.javalib.publish.* -import millbuild.* - -object `package` extends MavenModule with ErrorProneModule with PublishModule { - - def javacOptions = super.javacOptions() ++ Seq("-source", "8", "-target", "8") +import millbuild.Deps +object `package` extends MavenModule, PublishModule { + + def bomMvnDeps = Seq(Deps.junitBom) + + def javacOptions = Seq( + "-source", + "8", + "-target", + "8", + "-XDcompilePolicy=simple", + "-XDshould-stop=ifError=FLOW", + "-Xplugin:ErrorProne" + ) def artifactName = "maven-3-9-11" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop=ifError=FLOW") - - def jvmId = "zulu:11" - def pomSettings = PomSettings( "", "com.mycompany.app", @@ -28,13 +32,9 @@ object `package` extends MavenModule with ErrorProneModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - object test extends MavenTests with ErrorProneModule with TestModule.Junit5 { - - def mvnDeps = super.mvnDeps() ++ - Seq(Deps.junitJupiterApi, Deps.junitJupiterParams) + object test extends MavenTests, TestModule.Junit5 { - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop=ifError=FLOW") + def mvnDeps = Seq(Deps.junitJupiterApi, Deps.junitJupiterParams) def forkWorkingDir = moduleDir diff --git a/libs/init/maven/test/resources/expected/quickstart/mill-build/src/Deps.scala b/libs/init/maven/test/resources/expected/quickstart/mill-build/src/Deps.scala index 0f2e9b45fdcf..cdcc644370de 100644 --- a/libs/init/maven/test/resources/expected/quickstart/mill-build/src/Deps.scala +++ b/libs/init/maven/test/resources/expected/quickstart/mill-build/src/Deps.scala @@ -1,9 +1,8 @@ package millbuild - import mill.javalib.* - object Deps { + val junitBom = mvn"org.junit:junit-bom:5.11.0" val junitJupiterApi = mvn"org.junit.jupiter:junit-jupiter-api:5.11.0" val junitJupiterParams = mvn"org.junit.jupiter:junit-jupiter-params:5.11.0" } diff --git a/libs/init/maven/test/resources/expected/spring-start/build.mill b/libs/init/maven/test/resources/expected/spring-start/build.mill index 097f12c07bea..93d7e63f72a1 100644 --- a/libs/init/maven/test/resources/expected/spring-start/build.mill +++ b/libs/init/maven/test/resources/expected/spring-start/build.mill @@ -1,18 +1,63 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - +import mill.* import mill.javalib.* import mill.javalib.publish.* -import millbuild.* +import millbuild.Deps +object `package` extends MavenModule, PublishModule { -object `package` extends MavenModule with PublishModule { + def mvnDeps = Seq(Deps.springBootStarterDataJdbc) - def mvnDeps = super.mvnDeps() ++ Seq(Deps.springBootStarterDataJdbc) + def bomMvnDeps = Seq( + Deps.activemqBom, + Deps.artemisBom, + Deps.assertjBom, + Deps.zipkinReporterBom, + Deps.braveBom, + Deps.javaDriverBom, + Deps.jaxbBom, + Deps.groovyBom, + Deps.infinispanBom, + Deps.jacksonBom, + Deps.jerseyBom, + Deps.jettyEe10Bom, + Deps.jettyBom, + Deps.junitBom, + Deps.kotlinBom, + Deps.kotlinxCoroutinesBom, + Deps.kotlinxSerializationBom, + Deps.log4jBom, + Deps.micrometerBom, + Deps.micrometerTracingBom, + Deps.mockitoBom, + Deps.mongodbDriverBom, + Deps.nettyBom, + Deps.opentelemetryBom, + Deps.prometheusMetricsBom, + Deps.simpleclient_bom, + Deps.pulsarBom, + Deps.pulsarClientReactiveBom, + Deps.querydslBom, + Deps.reactorBom, + Deps.restAssuredBom, + Deps.rsocketBom, + Deps.seleniumBom, + Deps.springAmqpBom, + Deps.springBatchBom, + Deps.springDataBom, + Deps.springFrameworkBom, + Deps.springIntegrationBom, + Deps.springPulsarBom, + Deps.springRestdocsBom, + Deps.springSecurityBom, + Deps.springSessionBom, + Deps.springWsBom, + Deps.testcontainersBom + ) def artifactName = "demo" - def jvmId = "zulu:17" - def pomParentProject = Some( Artifact("org.springframework.boot", "spring-boot-starter-parent", "3.5.6") ) @@ -21,7 +66,7 @@ object `package` extends MavenModule with PublishModule { "Demo project for Spring Boot", "com.example", "", - Seq(License("", "", "", false, false, "")), + Seq(License("", "", "", false, false, "null")), VersionControl(Some(""), Some(""), Some(""), Some("")), Seq(Developer("", "", "", None, None)) ) diff --git a/libs/init/maven/test/resources/expected/spring-start/mill-build/src/Deps.scala b/libs/init/maven/test/resources/expected/spring-start/mill-build/src/Deps.scala index e75887faadf5..bc3037a80b26 100644 --- a/libs/init/maven/test/resources/expected/spring-start/mill-build/src/Deps.scala +++ b/libs/init/maven/test/resources/expected/spring-start/mill-build/src/Deps.scala @@ -1,9 +1,58 @@ package millbuild - import mill.javalib.* - object Deps { + val activemqBom = mvn"org.apache.activemq:activemq-bom:6.1.7" + val artemisBom = mvn"org.apache.activemq:artemis-bom:2.40.0" + val assertjBom = mvn"org.assertj:assertj-bom:3.27.4" + val braveBom = mvn"io.zipkin.brave:brave-bom:6.1.0" + val groovyBom = mvn"org.apache.groovy:groovy-bom:4.0.28" + val infinispanBom = mvn"org.infinispan:infinispan-bom:15.2.6.Final" + val jacksonBom = mvn"com.fasterxml.jackson:jackson-bom:2.19.2" + val javaDriverBom = mvn"org.apache.cassandra:java-driver-bom:4.19.0" + val jaxbBom = mvn"org.glassfish.jaxb:jaxb-bom:4.0.5" + val jerseyBom = mvn"org.glassfish.jersey:jersey-bom:3.1.11" + val jettyBom = mvn"org.eclipse.jetty:jetty-bom:12.0.27" + val jettyEe10Bom = mvn"org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.27" + val junitBom = mvn"org.junit:junit-bom:5.12.2" + val kotlinBom = mvn"org.jetbrains.kotlin:kotlin-bom:1.9.25" + val kotlinxCoroutinesBom = + mvn"org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1" + val kotlinxSerializationBom = + mvn"org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3" + val log4jBom = mvn"org.apache.logging.log4j:log4j-bom:2.24.3" + val micrometerBom = mvn"io.micrometer:micrometer-bom:1.15.4" + val micrometerTracingBom = mvn"io.micrometer:micrometer-tracing-bom:1.5.4" + val mockitoBom = mvn"org.mockito:mockito-bom:5.17.0" + val mongodbDriverBom = mvn"org.mongodb:mongodb-driver-bom:5.5.1" + val nettyBom = mvn"io.netty:netty-bom:4.1.127.Final" + val opentelemetryBom = mvn"io.opentelemetry:opentelemetry-bom:1.49.0" + val prometheusMetricsBom = mvn"io.prometheus:prometheus-metrics-bom:1.3.10" + val pulsarBom = mvn"org.apache.pulsar:pulsar-bom:4.0.6" + val pulsarClientReactiveBom = + mvn"org.apache.pulsar:pulsar-client-reactive-bom:0.6.0" + val querydslBom = mvn"com.querydsl:querydsl-bom:5.1.0" + val reactorBom = mvn"io.projectreactor:reactor-bom:2024.0.10" + val restAssuredBom = mvn"io.rest-assured:rest-assured-bom:5.5.6" + val rsocketBom = mvn"io.rsocket:rsocket-bom:1.1.5" + val seleniumBom = mvn"org.seleniumhq.selenium:selenium-bom:4.31.0" + val simpleclient_bom = mvn"io.prometheus:simpleclient_bom:0.16.0" + val springAmqpBom = mvn"org.springframework.amqp:spring-amqp-bom:3.2.7" + val springBatchBom = mvn"org.springframework.batch:spring-batch-bom:5.2.3" val springBootStarterDataJdbc = mvn"org.springframework.boot:spring-boot-starter-data-jdbc:3.5.6" + val springDataBom = mvn"org.springframework.data:spring-data-bom:2025.0.4" + val springFrameworkBom = mvn"org.springframework:spring-framework-bom:6.2.11" + val springIntegrationBom = + mvn"org.springframework.integration:spring-integration-bom:6.5.2" + val springPulsarBom = mvn"org.springframework.pulsar:spring-pulsar-bom:1.2.10" + val springRestdocsBom = + mvn"org.springframework.restdocs:spring-restdocs-bom:3.0.5" + val springSecurityBom = + mvn"org.springframework.security:spring-security-bom:6.5.5" + val springSessionBom = + mvn"org.springframework.session:spring-session-bom:3.5.2" + val springWsBom = mvn"org.springframework.ws:spring-ws-bom:4.1.1" + val testcontainersBom = mvn"org.testcontainers:testcontainers-bom:1.21.3" + val zipkinReporterBom = mvn"io.zipkin.reporter2:zipkin-reporter-bom:3.5.1" } diff --git a/libs/init/maven/test/resources/expected/with-args/maven-samples/build.mill b/libs/init/maven/test/resources/expected/with-args/maven-samples/build.mill index 77032e4ae11d..bb0126151b7b 100644 --- a/libs/init/maven/test/resources/expected/with-args/maven-samples/build.mill +++ b/libs/init/maven/test/resources/expected/with-args/maven-samples/build.mill @@ -1,10 +1,10 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - +import mill.* import mill.javalib.* import mill.javalib.publish.* - -object `package` extends MavenModule with PublishModule { +object `package` extends MavenModule, PublishModule { def artifactName = "parent" @@ -21,15 +21,12 @@ object `package` extends MavenModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - object `multi-module` extends MavenModule with PublishModule { + object `multi-module` extends MavenModule, PublishModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "multi-module-parent" - def jvmId = "zulu:11" - def pomPackagingType = "pom" def pomSettings = PomSettings( @@ -48,20 +45,17 @@ object `package` extends MavenModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - def publishProperties = super.publishProperties() ++ Map( + def publishProperties = Map( ("project.build.sourceEncoding", "utf-8"), ("project.reporting.outputEncoding", "utf-8") ) - object server extends MavenModule with PublishModule { + object server extends MavenModule, PublishModule { - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "server" - def jvmId = "zulu:11" - def pomParentProject = Some(Artifact( "com.example.maven-samples", "multi-module-parent", @@ -79,9 +73,14 @@ object `package` extends MavenModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - object test extends MavenTests with TestModule.Junit4 { + def publishProperties = Map( + ("project.build.sourceEncoding", "utf-8"), + ("project.reporting.outputEncoding", "utf-8") + ) + + object test extends MavenTests, TestModule.Junit4 { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"junit:junit-dep:4.10", mvn"org.hamcrest:hamcrest-core:1.2.1", mvn"org.hamcrest:hamcrest-library:1.2.1", @@ -98,22 +97,19 @@ object `package` extends MavenModule with PublishModule { } - object webapp extends MavenModule with PublishModule { + object webapp extends MavenModule, PublishModule { + + def moduleDeps = Seq(build.`multi-module`.server) - def compileMvnDeps = super.compileMvnDeps() ++ Seq( + def compileMvnDeps = Seq( mvn"javax.servlet:servlet-api:2.5", mvn"javax.servlet.jsp:jsp-api:2.2" ) - def moduleDeps = super.moduleDeps ++ Seq(build.`multi-module`.server) - - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "webapp" - def jvmId = "zulu:11" - def pomPackagingType = "war" def pomParentProject = Some(Artifact( @@ -133,24 +129,25 @@ object `package` extends MavenModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - } + def publishProperties = Map( + ("project.build.sourceEncoding", "utf-8"), + ("project.reporting.outputEncoding", "utf-8") + ) + } } - object `single-module` extends MavenModule with PublishModule { + object `single-module` extends MavenModule, PublishModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"javax.servlet:servlet-api:2.5", mvn"javax.servlet.jsp:jsp-api:2.2" ) - def javacOptions = super.javacOptions() ++ - Seq("-source", "1.6", "-target", "1.6") + def javacOptions = Seq("-source", "1.6", "-target", "1.6") def artifactName = "single-module-project" - def jvmId = "zulu:11" - def pomSettings = PomSettings( "Sample single module Maven project with a working, deployable site.", "com.example.maven-samples", @@ -167,14 +164,14 @@ object `package` extends MavenModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - def publishProperties = super.publishProperties() ++ Map( + def publishProperties = Map( ("project.build.sourceEncoding", "utf-8"), ("project.reporting.outputEncoding", "utf-8") ) - object test extends MavenTests with TestModule.Junit4 { + object test extends MavenTests, TestModule.Junit4 { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"junit:junit-dep:4.10", mvn"org.hamcrest:hamcrest-core:1.2.1", mvn"org.hamcrest:hamcrest-library:1.2.1", @@ -190,5 +187,4 @@ object `package` extends MavenModule with PublishModule { } } - } diff --git a/libs/init/maven/test/resources/expected/with-args/quickstart/build.mill b/libs/init/maven/test/resources/expected/with-args/quickstart/build.mill index 458044a36b60..5ac1f4c1a1f6 100644 --- a/libs/init/maven/test/resources/expected/with-args/quickstart/build.mill +++ b/libs/init/maven/test/resources/expected/with-args/quickstart/build.mill @@ -1,21 +1,25 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - +import mill.* import mill.javalib.* -import mill.javalib.errorprone.* import mill.javalib.publish.* - -object `package` extends MavenModule with ErrorProneModule with PublishModule { - - def javacOptions = super.javacOptions() ++ Seq("-source", "8", "-target", "8") +object `package` extends MavenModule, PublishModule { + + def bomMvnDeps = Seq(mvn"org.junit:junit-bom:5.11.0") + + def javacOptions = Seq( + "-source", + "8", + "-target", + "8", + "-XDcompilePolicy=simple", + "-XDshould-stop=ifError=FLOW", + "-Xplugin:ErrorProne" + ) def artifactName = "maven-3-9-11" - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop=ifError=FLOW") - - def jvmId = "zulu:11" - def pomSettings = PomSettings( "", "com.mycompany.app", @@ -27,21 +31,18 @@ object `package` extends MavenModule with ErrorProneModule with PublishModule { def publishVersion = "1.0-SNAPSHOT" - def publishProperties = super.publishProperties() ++ Map( + def publishProperties = Map( ("maven.compiler.release", "17"), ("project.build.sourceEncoding", "UTF-8") ) - object test extends MavenTests with ErrorProneModule with TestModule.Junit5 { + object test extends MavenTests, TestModule.Junit5 { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"org.junit.jupiter:junit-jupiter-api:5.11.0", mvn"org.junit.jupiter:junit-jupiter-params:5.11.0" ) - def errorProneJavacEnableOptions = super.errorProneJavacEnableOptions() ++ - Seq("-XDshould-stop=ifError=FLOW") - def forkWorkingDir = moduleDir def testParallelism = false diff --git a/libs/init/maven/test/src/mill/main/maven/MavenBuildGenTests.scala b/libs/init/maven/test/src/mill/main/maven/MavenBuildGenTests.scala index cc35a0a1fa41..4b0980cc0b33 100644 --- a/libs/init/maven/test/src/mill/main/maven/MavenBuildGenTests.scala +++ b/libs/init/maven/test/src/mill/main/maven/MavenBuildGenTests.scala @@ -8,39 +8,36 @@ object MavenBuildGenTests extends TestSuite { val checker = BuildGenChecker() test("maven-samples") { assert(checker.check( - generate = MavenBuildGenMain.main(Array.empty), sourceRel = os.sub / "maven-samples", expectedRel = os.sub / "expected/maven-samples" )) } test("quickstart") { assert(checker.check( - generate = MavenBuildGenMain.main(Array.empty), sourceRel = os.sub / "quickstart", expectedRel = os.sub / "expected/quickstart" )) } test("spring-start") { assert(checker.check( - generate = MavenBuildGenMain.main(Array.empty), sourceRel = os.sub / "spring-start", expectedRel = os.sub / "expected/spring-start" )) } test("with-args") { - val args = Array("--merge", "--no-meta", "--publish-properties") + val args = Seq("--publish-properties", "--merge", "--no-meta") test("maven-samples") { assert(checker.check( - generate = MavenBuildGenMain.main(args), sourceRel = os.sub / "maven-samples", - expectedRel = os.sub / "expected/with-args/maven-samples" + expectedRel = os.sub / "expected/with-args/maven-samples", + mainArgs = args )) } test("quickstart") { assert(checker.check( - generate = MavenBuildGenMain.main(args), sourceRel = os.sub / "quickstart", - expectedRel = os.sub / "expected/with-args/quickstart" + expectedRel = os.sub / "expected/with-args/quickstart", + mainArgs = args )) } } diff --git a/libs/init/package.mill b/libs/init/package.mill index dbf003bff13a..b3d6e4d90ef1 100644 --- a/libs/init/package.mill +++ b/libs/init/package.mill @@ -1,10 +1,11 @@ package build.libs.init import mill._ -import mill.api.{BuildCtx, Cross} +import mill.api.{BuildCtx, Cross, TaskCtx} import mill.contrib.buildinfo.BuildInfo import mill.scalalib.Assembly.Rule import mill.scalalib.ScalaModule +import mill.scalalib.scalafmt.ScalafmtModule import mill.util.Jvm import millbuild.* @@ -69,17 +70,91 @@ object `package` extends MillPublishScalaModule { } object buildgen extends MillPublishScalaModule { + def mvnDeps = Seq(Deps.pprint) def moduleDeps = - Seq(api(Deps.scalaVersionJava11), build.libs.init, build.core.internal, build.libs.util) - def testModuleDeps = super.testModuleDeps ++ Seq(build.libs.scalalib) + Seq(api(Deps.scalaVersion), build.libs.init, build.core.internal, build.libs.util) + def testModuleDeps = super.testModuleDeps ++ Seq(build.libs.scalalib, build.libs.init) - object api extends Cross[ApiModule](Deps.sbtScalaVersion212, Deps.scalaVersionJava11) + object api extends Cross[ApiModule](Deps.sbtScalaVersion212, Deps.scalaVersion) trait ApiModule extends MillPublishCrossScalaModule { - def mvnDeps = Seq(Deps.upickle) - def jvmId = "11" + def mvnDeps = Seq(Deps.osLib, Deps.upickle) + } + + trait MainModule extends MillPublishScalaModule { + def moduleDeps = Seq(buildgen) + def testModuleDeps = super.testModuleDeps ++ Seq(buildgen.test) + def prependShellScript = "" + def testForkEnv = super.testForkEnv() ++ Map("TEST_MAIN_ASSEMBLY" -> assembly().path.toString) + def scalafmtConfigFile = Task { + val file = Task.dest / ".scalafmt.conf" + os.write( + file, + """version = "3.8.5" + |runner.dialect = scala3 + |newlines.source=fold + |project.includePaths = [ + | "glob:**/build.mill" + | "glob:**/package.mill" + | "glob:**/mill-build/**.scala" + |] + |""".stripMargin + ) + PathRef(file) + } + def millExecutable: T[PathRef] = build.dist.executableRaw() + def mocks: Map[String, Mock] + def runMock(mock: Mock) = Task.Command(persistent = true) { + import mock.* + val cacheDir = Task.dest + val repoName = gitUrl.split("/").last.stripSuffix(".git") + val repoDir = cacheDir / repoName / os.RelPath(gitRev) + if (os.exists(repoDir)) { + os.proc("git", "clean", "-fdx").call(repoDir) + } else { + os.proc("git", "clone", gitUrl, "--depth", 1, "-b", gitRev, repoDir).call(cacheDir) + } + os.write.over(repoDir / ".mill-jvm-version", systemJdk) + val javaHome = Jvm.resolveJavaHome(systemJdk, ctx = Some(Task.ctx())).get + Jvm.callProcess( + mainClass = finalMainClass(), + javaHome = Some(javaHome), + classPath = runClasspath().map(_.path), + cwd = repoDir, + env = Map("JAVA_HOME" -> javaHome.toString), + stdout = os.Inherit + ) + Jvm.callProcess( + mainClass = "org.scalafmt.cli.Cli", + mainArgs = Seq("-c", scalafmtConfigFile().path.toString, repoDir.toString), + classPath = ScalafmtModule.scalafmtClasspath().map(_.path), + cwd = repoDir, + stdout = os.Inherit + ) + val millExe = millExecutable().path + def resolve(task: String) = { + if (task.contains("_")) os.proc(millExe, "resolve", task).call(repoDir).out.lines() + else Seq(task) + } + def check(task: String) = { + val result = os.proc(millExe, task) + .call(repoDir, stdout = os.Inherit, check = false) + result.exitCode == 0 + } + try { + val (passed, failed) = checkTasks.flatMap(resolve).partition(check) + MockResult(mock, passed, failed) + } finally { + os.proc(millExe, "shutdown").call(repoDir, check = false) + } + } + def runMocks(ids: String*) = { + val task = Task.sequence(ids.map(id => runMock(mocks(id)))) + Task.Command { task() } + } + def defaultTask() = "runMocks" } } - object gradle extends MillPublishScalaModule with BuildInfo { + object gradle extends buildgen.MainModule with BuildInfo { def moduleDeps = Seq(api, buildgen) def mvnDeps = Seq(Deps.gradleApi) def testModuleDeps = super.testModuleDeps ++ Seq(build.libs.scalalib, buildgen.test) @@ -92,20 +167,30 @@ object `package` extends MillPublishScalaModule { PathRef(Task.dest) } def resources = Task { super.resources() ++ Seq(exportpluginAssemblyResource()) } + def mocks = Map( + "asm" -> Mock("https://gitlab.ow2.org/asm/asm.git", "ASM_9_9"), + "ehcache3" -> Mock("https://github.com/ehcache/ehcache3.git", "v3.11.1"), + "fastcsv" -> Mock("https://github.com/osiegmar/FastCSV.git", "v4.1.0", "zulu:24"), + "jcommander" -> Mock("https://github.com/cbeust/jcommander.git", "3.0"), + "microconfig" -> Mock("https://github.com/microconfig/microconfig.git", "v4.9.5"), + "mockito" -> Mock("https://github.com/mockito/mockito.git", "v5.20.0"), + "spotbugs" -> Mock("https://github.com/spotbugs/spotbugs.git", "4.9.8", "zulu:21"), + "spring-framework" -> Mock( + "https://github.com/spring-projects/spring-framework.git", + "v6.2.12", + "zulu:24" + ) + ) - object api extends MillPublishJavaModule { - def jvmId = "11" - } + object api extends MillPublishJavaModule object exportplugin extends ScalaModule { - def jvmId = "11" - def scalaVersion = Deps.scalaVersionJava11 - def mvnDeps = Seq(Deps.osLib) - def moduleDeps = Seq(api, buildgen.api(Deps.scalaVersionJava11)) + def scalaVersion = Deps.scalaVersion + def moduleDeps = Seq(api, buildgen.api(Deps.scalaVersion)) def compileMvnDeps = Seq(Deps.gradleApi) } } - object maven extends MillPublishScalaModule { + object maven extends buildgen.MainModule { def moduleDeps = Seq(buildgen) override def mvnDeps = Seq( Deps.MavenInit.mavenEmbedder, @@ -116,10 +201,24 @@ object `package` extends MillPublishScalaModule { Deps.MavenInit.mavenResolverTransportWagon ) def testModuleDeps = super.testModuleDeps ++ Seq(build.libs.scalalib, buildgen.test) + def mocks = Map( + "byte-buddy" -> Mock("https://github.com/raphw/byte-buddy.git", "byte-buddy-1.17.8"), + "checkstyle" -> Mock("https://github.com/checkstyle/checkstyle.git", "checkstyle-12.1.1"), + "error-prone" -> Mock("https://github.com/google/error-prone.git", "v2.43.0", "zulu:21"), + "jansi" -> Mock("https://github.com/fusesource/jansi.git", "jansi-2.4.2"), + "java-design-patterns" -> Mock( + "https://github.com/iluwatar/java-design-patterns", + "open-source-java-design-patterns-2nd-edition", + "zulu:21" + ), + "joda-beans" -> Mock("https://github.com/JodaOrg/joda-beans.git", "v2.11.1", "zulu:21"), + "netty" -> Mock("https://github.com/netty/netty.git", "netty-4.2.7.Final"), + "spring-ai" -> Mock("https://github.com/spring-projects/spring-ai.git", "v1.0.3") + ) } - object sbt extends MillPublishScalaModule with BuildInfo { - def moduleDeps = Seq(api(Deps.scalaVersionJava11), buildgen) + object sbt extends buildgen.MainModule with BuildInfo { + def moduleDeps = Seq(api(Deps.scalaVersion), buildgen) def sbtPluginJarResource = Task { os.copy(exportplugin.assembly().path, Task.dest / "exportplugin-assembly.jar") PathRef(Task.dest) @@ -131,19 +230,49 @@ object `package` extends MillPublishScalaModule { BuildInfo.Value("exportpluginAssemblyResource", "/exportplugin-assembly.jar"), BuildInfo.Value("sbtVersion", Deps.sbt.version) ) + def mocks = Map( + "airstream" -> Mock("https://github.com/raquo/Airstream.git", "v17.2.1"), + "cats" -> Mock( + "https://github.com/typelevel/cats.git", + "v2.13.0", + checkTasks = Nil + ), + "enumeratum" -> Mock("https://github.com/lloydmeta/enumeratum.git", "enumeratum-1.9.0"), + "fs2" -> Mock("https://github.com/typelevel/fs2.git", "v3.12.2"), + "gatling" -> Mock("https://github.com/gatling/gatling.git", "v3.14.7", "zulu:24"), + "lila" -> Mock("https://github.com/lichess-org/lila.git", "master", "zulu:21"), + "nscala-time" -> Mock("https://github.com/nscala-time/nscala-time.git", "releases/3.0.0"), + "refined" -> Mock("https://github.com/fthomas/refined.git", "v0.11.3"), + "scala3" -> Mock("https://github.com/scala/scala3.git", "3.7.3"), + "scala-logging" -> Mock("https://github.com/lightbend-labs/scala-logging.git", "v3.9.6"), + "scalapb" -> Mock("https://github.com/scalapb/ScalaPB.git", "v0.11.20"), + "scopt" -> Mock("https://github.com/scopt/scopt.git", "v4.1.0"), + "scrypto" -> Mock("https://github.com/input-output-hk/scrypto.git", "v3.1.0") + ) - object api extends Cross[ApiModule](Deps.sbtScalaVersion212, Deps.scalaVersionJava11) + object api extends Cross[ApiModule](Deps.sbtScalaVersion212, Deps.scalaVersion) trait ApiModule extends MillPublishCrossScalaModule { def moduleDeps = Seq(buildgen.api()) - def jvmId = "11" } object exportplugin extends ScalaModule { - def jvmId = "11" def scalaVersion = Deps.sbtScalaVersion212 - def mvnDeps = Seq(Deps.osLib) def moduleDeps = Seq(api(Deps.sbtScalaVersion212)) def compileMvnDeps = Seq(Deps.sbt) def assemblyRules = Seq(Rule.ExcludePattern("scala\\.*")) } } } +private case class Mock( + @mainargs.arg(positional = true) gitUrl: String, + @mainargs.arg(positional = true) gitRev: String, + @mainargs.arg(short = 'j') systemJdk: String = "zulu:17", + @mainargs.arg(short = 't') checkTasks: Seq[String] = Seq("__.compile", "__.test") +) derives upickle.default.ReadWriter +private object Mock { + given mainargs.TokensReader[Mock] = mainargs.Parser[Mock] +} +private case class MockResult( + mock: Mock, + passedTasks: Seq[String] = Nil, + failedTasks: Seq[String] = Nil +) derives upickle.default.ReadWriter diff --git a/libs/init/sbt/api/src/mill/main/sbt/SbtModuleSpec.scala b/libs/init/sbt/api/src/mill/main/sbt/SbtModuleSpec.scala index 36981a41b743..dcbf828aa576 100644 --- a/libs/init/sbt/api/src/mill/main/sbt/SbtModuleSpec.scala +++ b/libs/init/sbt/api/src/mill/main/sbt/SbtModuleSpec.scala @@ -1,9 +1,11 @@ package mill.main.sbt import mill.main.buildgen.ModuleSpec -import upickle.default.{ReadWriter, macroRW} +import upickle.default.{ReadWriter, macroRW, readwriter} -case class SbtModuleSpec(moduleType: SbtModuleType, module: ModuleSpec) +case class SbtModuleSpec(sharedModuleDir: Either[os.SubPath, os.SubPath], module: ModuleSpec) object SbtModuleSpec { + implicit val rwSubPath: ReadWriter[os.SubPath] = + readwriter[String].bimap(_.toString, os.SubPath(_)) implicit val rw: ReadWriter[SbtModuleSpec] = macroRW } diff --git a/libs/init/sbt/api/src/mill/main/sbt/SbtModuleType.scala b/libs/init/sbt/api/src/mill/main/sbt/SbtModuleType.scala deleted file mode 100644 index f8ab9b94e6f9..000000000000 --- a/libs/init/sbt/api/src/mill/main/sbt/SbtModuleType.scala +++ /dev/null @@ -1,16 +0,0 @@ -package mill.main.sbt - -import upickle.default.{ReadWriter, macroRW} - -sealed trait SbtModuleType -object SbtModuleType { - case class Default(baseDir: Seq[String]) extends SbtModuleType - object Default { - implicit val rw: ReadWriter[Default] = macroRW - } - case class Platform(rootDir: Seq[String]) extends SbtModuleType - object Platform { - implicit val rw: ReadWriter[Platform] = macroRW - } - implicit val rw: ReadWriter[SbtModuleType] = macroRW -} diff --git a/libs/init/sbt/exportplugin/src/mill/main/sbt/ExportBuildPlugin.scala b/libs/init/sbt/exportplugin/src/mill/main/sbt/ExportBuildPlugin.scala index 0b75b20b81fe..caaa6bb3dde9 100644 --- a/libs/init/sbt/exportplugin/src/mill/main/sbt/ExportBuildPlugin.scala +++ b/libs/init/sbt/exportplugin/src/mill/main/sbt/ExportBuildPlugin.scala @@ -1,25 +1,25 @@ package mill.main.sbt -import _root_.sbt._ -import mill.main.buildgen.ModuleConfig._ -import mill.main.buildgen._ +import _root_.sbt.{Value => _, _} +import mill.main.buildgen.ModuleSpec +import mill.main.buildgen.ModuleSpec._ import scala.util.Using object ExportBuildPlugin extends AutoPlugin { object autoImport { - val millInitExportBuild = taskKey[Unit]("exports data for all projects") + val millInitExportBuild = taskKey[Unit]("") } import autoImport._ - val millInitExportDir = settingKey[os.Path]("directory to export data to") - val millInitExportProject = taskKey[Unit]("exports data for a project") + val millInitExportDir = settingKey[os.Path]("") + val millInitExportProject = taskKey[Unit]("") - // settings derived from third-party plugin settings + // Proxies for third-party plugin settings that are resolved using reflection. val millScalaJSModuleKind = settingKey[Option[String]]("") - // duplicated third-party plugin settings - val crossProjectBaseDirectory = settingKey[File]("base directory of the current cross project") + // Copies of third-party plugin settings that are resolved directly. + val crossProjectBaseDirectory = settingKey[File]("") override def requires = plugins.JvmPlugin override def trigger = allRequirements @@ -32,13 +32,12 @@ object ExportBuildPlugin extends AutoPlugin { millInitExportProject := exportProject.value ) - // https://github.com/scalacenter/bloop/blob/f6dff064ed96698c6d35daef43fe06e6cca74526/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala#L327 private def scalaJSModuleKind = Def.settingDyn { try { val class0 = Class.forName("org.scalajs.linker.interface.StandardConfig") val method = class0.getMethod("moduleKind") Def.setting { - proxyForSetting("scalaJSLinkerConfig", class0).value.map(method.invoke(_).toString) + proxyForSetting("scalaJSLinkerConfig", class0).value.map("ModuleKind." + method.invoke(_)) } } catch { case _: ClassNotFoundException => Def.setting(None) @@ -46,14 +45,13 @@ object ExportBuildPlugin extends AutoPlugin { } } - // https://github.com/scalacenter/bloop/blob/f6dff064ed96698c6d35daef43fe06e6cca74526/integrations/sbt-bloop/src/main/scala/bloop/integrations/sbt/SbtBloop.scala#L305 private def proxyForSetting(id: String, rt: Class[_]) = { val manifest = new Manifest[AnyRef] { def runtimeClass = rt } val anyRefWriter = implicitly[util.OptJsonWriter[AnyRef]] SettingKey(id)(manifest, anyRefWriter).? } - // this is required to export projects that are not aggregated by the root project + // This is required to export projects that are not aggregated by the root project. private def exportBuild = Def.taskDyn { val structure = Project.structure(Keys.state.value) Def.task { @@ -70,262 +68,222 @@ object ExportBuildPlugin extends AutoPlugin { val scalaVersion = Keys.scalaVersion.value val outDir = millInitExportDir.value val outFile = outDir / s"${project.id}-$scalaVersion.json" - // ignore duplicate invocations triggered by cross-build execution + // Ignore duplicate invocations triggered by cross-build execution. Def.task(if (!os.exists(outFile)) { + def hasAutoPlugin(label: String) = project.autoPlugins.exists(_.label == label) + val hasScalaJSPlugin = hasAutoPlugin("org.scalajs.sbtplugin.ScalaJSPlugin") + val hasScalaNativePlugin = hasAutoPlugin("scala.scalanative.sbtplugin.ScalaNativePlugin") + def skipDep(dep: ModuleID) = { + import dep.{name, organization => org} + (org == "org.scala-lang" && ( + name.startsWith("scala-library") || name.startsWith("scala3-library") + )) || + (org == "ch.epfl.lamp" && name.startsWith("dotty")) || + (hasScalaJSPlugin && org == "org.scala-js" && !name.startsWith("scalajs-dom")) || + (hasScalaNativePlugin && org == "org.scala-native") + } + val depsWithConfigs = Keys.libraryDependencies.value.collect { - case dep if !skipDep(dep) => (dep, dep.configurations.toSeq.flatMap(_.split(';'))) + case dep if !skipDep(dep) => (dep, dep.configurations.getOrElse("compile").split(';').toSeq) + } + def mvnDeps(configs: String*) = depsWithConfigs.collect { + case (dep, configs0) if configs.exists(configs0.contains) => toMvnDep(dep) } - def mvnDeps(configsPredicate: Seq[String] => Boolean) = depsWithConfigs.iterator.collect { - case (dep, configs) if configsPredicate(configs) => toMvnDep(dep) - }.toSeq - val isCrossVersion = Keys.crossScalaVersions.value.length > 1 + val scalaVersion = Keys.scalaVersion.value + val crossScalaVersions = Keys.crossScalaVersions.value + val isCrossVersion = crossScalaVersions.length > 1 val projectDepsWithConfigs = project.dependencies - .map(dep => dep -> dep.configuration.toSeq.flatMap(_.split(";"))) + .map(dep => dep -> dep.configuration.getOrElse("compile").split(";").toSeq) val structure = Project.structure(Keys.state.value) - def moduleDeps(configsPredicate: Seq[String] => Boolean) = - projectDepsWithConfigs.collect { - case (dep, configs) if configsPredicate(configs) => - for { - depBaseDir <- (dep.project / Keys.baseDirectory).get(structure.data) - depSegments = toSegments(os.Path(depBaseDir)) - depCrossScalaVersions = + def moduleDeps(p: String => Boolean, nestedSegment: Option[String] = None): Seq[ModuleDep] = + projectDepsWithConfigs.flatMap { + case (dep, configs) if configs.exists(p) => + (dep.project / Keys.baseDirectory).get(structure.data).flatMap { depBaseDir => + val depCrossScalaVersions = (dep.project / Keys.crossScalaVersions).get(structure.data).getOrElse(Nil) - depIsCrossVersion = depCrossScalaVersions.length > 1 - if !depIsCrossVersion || depCrossScalaVersions.contains(scalaVersion) - crossArgs = if (depIsCrossVersion) - Map((depSegments.length - 1, if (isCrossVersion) Nil else Seq("scalaVersion()"))) - else Map.empty[Int, Seq[String]] - } yield ModuleDep(depSegments, crossArgs) - }.flatten + if (depCrossScalaVersions.contains(scalaVersion)) { + val crossSuffix = if (depCrossScalaVersions.length < 2) None + else Some(if (isCrossVersion) "()" else s"""("$scalaVersion")""") + Some(ModuleDep( + moduleDir(os.Path(depBaseDir)), + crossSuffix, + nestedSegment + )) + } else None + } + case _ => None + } val baseDir = os.Path(project.base) - val moduleDir = crossProjectBaseDirectory.?.value.fold( - // crossProjectBaseDirectory was added in v1.3.0 - if (baseDir.last.matches("""^[.]?(js|jvm|native)$""")) baseDir / os.up else baseDir - )(os.Path(_)) - val isCrossPlatform = baseDir != moduleDir - - def hasAutoPlugin(label: String) = project.autoPlugins.exists(_.label == label) + val crossProjectBaseDir = crossProjectBaseDirectory.?.value.map(os.Path(_)).orElse( + if (baseDir.last.matches("""^[.]?(js|jvm|native)$""")) Some(baseDir / os.up) else None + ) + val useVersionRanges = (Compile / Keys.unmanagedSourceDirectories).value + .exists(dir => dir.name.last == '+' || dir.name.last == '-') + val useParentModuleDir = structure.allProjects.exists(p => + p.aggregate.isEmpty && p.base == project.base && p.id != project.id + ) - val mainCoursierModule = Keys.resolvers.value - .diff(Seq(Resolver.mavenCentral, Resolver.mavenLocal)) - .collect { - case r: MavenRepository => r.root - } match { - case Nil => None - case repositories => Some(CoursierModule(repositories)) - } - val mainJavaHomeModule = JavaHomeModule.find( - javacOptions = Keys.javacOptions.value, - scalacOptions = Keys.scalacOptions.value + // Cross values are duplicated for ease of processing when combining cross-version specs. + implicit def value[A](base: Option[A]): Value[A] = Value( + base, + base match { + case Some(a) if isCrossVersion => Seq(scalaVersion -> a) + case _ => Nil + } + ) + implicit def values[A](base: Seq[A]): Values[A] = Values( + base = base, + cross = if (isCrossVersion && base.nonEmpty) Seq(scalaVersion -> base) else Nil ) - val mainJavaModule = JavaModule( - mvnDeps = mvnDeps(cs => cs.isEmpty || cs.contains("compile")), - compileMvnDeps = mvnDeps(_.exists(c => c == "provided" || c == "optional")), - runMvnDeps = mvnDeps(_.contains("runtime")), - moduleDeps = moduleDeps(cs => - cs.isEmpty || cs.exists(c => c == "compile" || c.startsWith("compile->")) + var mainModule = ModuleSpec( + name = if (useParentModuleDir) project.id else moduleName(baseDir.last), + imports = Seq("import mill.scalalib.*"), + supertypes = Seq((crossProjectBaseDir.nonEmpty, isCrossVersion) match { + case (true, true) => "CrossSbtPlatformModule" + case (true, _) => "SbtPlatformModule" + case (_, true) => "CrossSbtModule" + case _ => "SbtModule" + }), + mixins = if (useVersionRanges) Seq("CrossScalaVersionRanges") else Nil, + useParentModuleDir = useParentModuleDir, + crossKeys = if (isCrossVersion) Seq(scalaVersion) else Nil, + repositories = Keys.resolvers.value + .diff(Seq(Resolver.mavenCentral, Resolver.mavenLocal)) + .collect { + case r: MavenRepository => r.root + }, + mvnDeps = mvnDeps("compile"), + compileMvnDeps = mvnDeps("provided", "optional"), + runMvnDeps = mvnDeps("runtime"), + moduleDeps = moduleDeps(config => config == "compile" || config.startsWith("compile->")), + compileModuleDeps = moduleDeps(config => + config == "provided" || config.startsWith("provided->") || + config == "optional" || config.startsWith("optional->") ), - compileModuleDeps = moduleDeps(_.exists(c => - c == "provided" || c == "optional" || - c.startsWith("provided->") || c.startsWith("optional->") - )), - runModuleDeps = moduleDeps(_.exists(c => c == "runtime" || c.startsWith("runtime->"))), - javacOptions = Keys.javacOptions.value, - artifactName = Keys.moduleName.value + runModuleDeps = moduleDeps(config => config == "runtime" || config.startsWith("runtime->")), + javacOptions = Opt.groups(Keys.javacOptions.value), + artifactName = Some(Keys.moduleName.value), + scalaVersion = if (isCrossVersion) None else Some(scalaVersion), + scalacOptions = Opt.groups(Keys.scalacOptions.value.filterNot(skipScalacOption)), + scalacPluginMvnDeps = mvnDeps("plugin->default(compile)"), + sourcesRootFolders = values( + if (crossProjectBaseDir.exists(dir => os.isDir(dir / "shared"))) Seq(os.sub / "shared") + else Nil + ).copy(extend = true) ) - val mainPublishModule = if ((Keys.publish / Keys.skip).value || !Keys.publishArtifact.value) - None - else { - Some(PublishModule( - pomSettings = toPomSettings(Keys.projectInfo.value, Keys.organization.value), - publishVersion = Keys.version.value, - versionScheme = Keys.versionScheme.value.orNull - )) + if (!(Keys.publish / Keys.skip).value && Keys.publishArtifact.value) { + mainModule = mainModule.copy( + imports = + "import mill.javalib.PublishModule" +: "import mill.javalib.publish.*" +: mainModule.imports, + supertypes = "PublishModule" +: mainModule.supertypes, + pomSettings = Some(toPomSettings(Keys.projectInfo.value, Keys.organization.value)), + publishVersion = Some(Keys.version.value), + versionScheme = Keys.versionScheme.value.collect { + case "early-semver" => "VersionScheme.EarlySemVer" + case "pvp" => "VersionScheme.PVP" + case "semver-spec" => "VersionScheme.SemVerSpec" + case "strict" => "VersionScheme.Strict" + } + ) } - val mainScalaModule = ScalaModule( - scalaVersion = if (isCrossVersion) null else scalaVersion, - scalacOptions = Keys.scalacOptions.value.filterNot(skipScalacOption), - scalacPluginMvnDeps = mvnDeps(_.contains("plugin->default(compile)")) - ) - val mainScalaJSModule = if (hasAutoPlugin("org.scalajs.sbtplugin.ScalaJSPlugin")) { + if (hasScalaJSPlugin) { Keys.libraryDependencies.value.collectFirst { case dep if dep.organization == "org.scala-js" && dep.name.startsWith("scalajs-library") => - ScalaJSModule( - scalaJSVersion = dep.revision, - moduleKind = ScalaJSModule.moduleKindOverride(scalaJSModuleKind.value.orNull) + mainModule = mainModule.copy( + imports = + "import mill.scalajslib.ScalaJSModule" +: "import mill.scalajslib.api.*" +: mainModule.imports, + supertypes = "ScalaJSModule" +: mainModule.supertypes, + scalaJSVersion = Some(dep.revision), + moduleKind = millScalaJSModuleKind.value + ) + } + } + if (hasScalaNativePlugin) { + Keys.libraryDependencies.value.collectFirst { + case dep + if dep.organization == "org.scala-native" && + dep.configurations.contains("plugin->default(compile)") => + mainModule = mainModule.copy( + imports = + "import mill.scalanativelib.ScalaNativeModule" +: "import mill.scalanativelib.api.*" +: mainModule.imports, + supertypes = "ScalaNativeModule" +: mainModule.supertypes, + scalaNativeVersion = Some(dep.revision) ) } - } else None - val mainScalaNativeModule = - if (hasAutoPlugin("scala.scalanative.sbtplugin.ScalaNativePlugin")) { - Keys.libraryDependencies.value.collectFirst { - case dep - if dep.organization == "org.scala-native" && - dep.configurations.contains("plugin->default(compile)") => - ScalaNativeModule(dep.revision) + } + if ((Test / Keys.sourceDirectories).value.exists(_.exists)) { + val testMvnDeps = mvnDeps("test") + ModuleSpec.testModuleMixin(testMvnDeps).foreach { mixin => + val supertypes = mainModule.supertypes.collect { + case "ScalaJSModule" => "ScalaJSTests" + case "ScalaNativeModule" => "ScalaNativeTests" + case "CrossSbtPlatformModule" => "CrossSbtPlatformTests" + case "SbtPlatformModule" => "SbtPlatformTests" + case "CrossSbtModule" => "CrossSbtTests" + case "SbtModule" => "SbtTests" } - } else None - val mainSbtPlatformModule = if (isCrossPlatform) { - Some(SbtPlatformModule( - // only sbtcrossproject.CrossType.Full requires additional configuration - sourcesRootFolders = if (os.isDir(moduleDir / "shared")) Seq("shared") else Nil - )) - } else None - val mainConfigs = Seq( - mainScalaJSModule, - mainScalaNativeModule - ).flatten ++ Seq( - mainJavaModule, - mainScalaModule - ) ++ Seq( - mainJavaHomeModule, - mainSbtPlatformModule, - mainPublishModule, - mainCoursierModule - ).flatten - val useVersionRanges = isCrossVersion && os.walk.stream(moduleDir).exists(path => - os.isDir(path) && path.last.matches("""^scala-\d+\.\d*(-.*|\+)$""") - ) - val (mainSupertypes, mainMixins) = - mainHierarchy(isCrossPlatform, isCrossVersion, useVersionRanges, mainConfigs) - - val testModule = TestModule.mixin(mvnDeps(_.contains("test"))).map { testModuleMixin => - val (testSupertypes, testMixins) = - testHierarchy(mainSupertypes ++ mainMixins, testModuleMixin) - // provided dependencies are included in run scope to reproduce SBT behavior - val testJavaModule = JavaModule( - mvnDeps = mvnDeps(_.contains("test")), - compileMvnDeps = mvnDeps(_.contains("provided")), - runMvnDeps = mvnDeps(_.contains("provided")), - moduleDeps = moduleDeps(_.exists(c => c == "test" || c == "test->compile")) ++ - moduleDeps(_.contains("test->test")).map(dep => dep.copy(dep.segments :+ "test")), - compileModuleDeps = - moduleDeps(_.exists(c => c == "provided" || c == "test->provided")), - runModuleDeps = moduleDeps(_.exists(c => c == "provided" || c == "test->runtime")) - ) - val testScalaJSModule = if (mainScalaJSModule.isEmpty) None else Some(ScalaJSModule()) - val testScalaNativeModule = - if (mainScalaJSModule.isEmpty) None else Some(ScalaNativeModule()) - val testConfigs = Seq( - testJavaModule, - // reproduce SBT behavior - TestModule( - testParallelism = "false", - testSandboxWorkingDir = "false" + val testModule = ModuleSpec( + name = "test", + supertypes = supertypes, + mixins = Seq(mixin), + mvnDeps = testMvnDeps, + compileMvnDeps = mainModule.compileMvnDeps, + runMvnDeps = mainModule.compileMvnDeps.base ++ mainModule.runMvnDeps.base, + moduleDeps = values( + moduleDeps(_ == "test") ++ moduleDeps(_ == "test->test", nestedSegment = Some("test")) + ).copy(extend = true), + compileModuleDeps = mainModule.compileModuleDeps.base, + runModuleDeps = mainModule.compileModuleDeps.base ++ mainModule.runModuleDeps.base, + testParallelism = Some(false), + testSandboxWorkingDir = Some(false) ) - ) ++ Seq( - testScalaJSModule, - testScalaNativeModule - ).flatten - ModuleSpec( - name = "test", - supertypes = testSupertypes, - mixins = testMixins, - configs = testConfigs, - crossConfigs = if (isCrossVersion) Seq((scalaVersion, testConfigs)) else Nil - ) + mainModule = mainModule.copy(test = Some(testModule)) + } } - val mainModule = ModuleSpec( - name = toSegment(baseDir.last), - supertypes = mainSupertypes, - mixins = mainMixins, - configs = mainConfigs, - crossConfigs = if (isCrossVersion) Seq((scalaVersion, mainConfigs)) else Nil, - nestedModules = testModule.toSeq + val moduleDir0 = moduleDir(crossProjectBaseDir.getOrElse(baseDir)) + val exportedData = SbtModuleSpec( + Either.cond(crossProjectBaseDir.nonEmpty || useParentModuleDir, moduleDir0, moduleDir0), + mainModule ) - - val segments = toSegments(moduleDir) - val moduleType = - if (isCrossPlatform) SbtModuleType.Platform(segments) else SbtModuleType.Default(segments) - val exportedData = SbtModuleSpec(moduleType, mainModule) Using.resource(os.write.outputStream(outFile))( upickle.default.writeToOutputStream(exportedData, _) ) }) } - private def mainHierarchy( - isCrossPlatform: Boolean, - isCrossVersion: Boolean, - useVersionRanges: Boolean, - configs: Seq[ModuleConfig] - ) = { - val supertypes = Seq.newBuilder[String] - val mixins = Seq.newBuilder[String] - supertypes ++= configs.collect { - case _: ScalaJSModule => "ScalaJSModule" - case _: ScalaNativeModule => "ScalaNativeModule" - case _: PublishModule => "PublishModule" - case _: SbtPlatformModule => "SbtPlatformModule" - } - if (isCrossVersion) { - mixins += (if (isCrossPlatform) "CrossSbtPlatformModule" else "CrossSbtModule") - if (useVersionRanges) mixins += "CrossScalaVersionRanges" - } else { - supertypes += "SbtModule" - } - (supertypes.result(), mixins.result()) - } - - private def testHierarchy(mainHierarchy: Seq[String], testModuleMixin: String) = { - val supertypes = Seq.newBuilder[String] - val mixins = Seq.newBuilder[String] - mainHierarchy.foreach { - case "ScalaJSModule" => supertypes += "ScalaJSTests" - case "ScalaNativeModule" => supertypes += "ScalaNativeTests" - case "SbtModule" => supertypes += "SbtTests" - case "SbtPlatformModule" => supertypes += "SbtPlatformTests" - case "CrossSbtModule" => mixins += "CrossSbtTests" - case "CrossSbtPlatformModule" => mixins += "CrossSbtPlatformTests" - case _ => - } - mixins += testModuleMixin - (supertypes.result(), mixins.result()) - } - - private def skipDep(dep: ModuleID) = { - import dep._ - (organization == "org.scala-lang" && ( - name.startsWith("scala-library") || - name.startsWith("scala3-library") - )) || - (organization == "ch.epfl.lamp" && name.startsWith("dotty")) || - (organization == "org.scala-js" && !name.startsWith("scalajs-dom")) || - organization == "org.scala-native" - } - private def skipScalacOption(s: String) = s.startsWith("-P") || s.startsWith("-Xplugin") || s.startsWith("-scalajs") - private def toSegment(s: String) = - // cleanup `CrossType.Pure` module names - s.stripPrefix(".") + // Cleanup `CrossType.Pure` module names. + private def moduleName(s: String) = s.stripPrefix(".") - private def toSegments(baseDir: os.Path) = - baseDir.relativeTo(os.pwd).segments.map(toSegment) + private def moduleDir(path: os.Path) = path.subRelativeTo(os.pwd).segments match { + case Seq() => os.sub + case init :+ last => os.sub / (init :+ moduleName(last)) + } private def toMvnDep(dep: ModuleID) = { - import ModuleConfig.{CrossVersion => CV} + import ModuleSpec.{CrossVersion => CV} import dep._ import librarymanagement.{For2_13Use3, For3Use2_13, Patch} val artifact = explicitArtifacts.find(_.name == name) MvnDep( organization = organization, name = name, - version = Option(revision), + version = revision, classifier = artifact.flatMap(_.classifier), `type` = artifact.map(_.`type`), excludes = exclusions.map(x => (x.organization, x.name)), cross = crossVersion match { - case v: Binary => CV.Binary(v.prefix.nonEmpty) - case v: Full => CV.Full(v.prefix.nonEmpty) + case v: Binary => CV.Binary(platformed = v.prefix.nonEmpty) + case v: Full => CV.Full(platformed = v.prefix.nonEmpty) case _: Patch => CV.Full(platformed = false) - case v: For2_13Use3 => CV.Constant("_3", v.prefix.nonEmpty) - case v: For3Use2_13 => CV.Constant("_2.13", v.prefix.nonEmpty) + case v: For2_13Use3 => CV.Constant("_3", platformed = v.prefix.nonEmpty) + case v: For3Use2_13 => CV.Constant("_2.13", platformed = v.prefix.nonEmpty) case _ => CV.Constant("", platformed = false) } ) @@ -334,9 +292,9 @@ object ExportBuildPlugin extends AutoPlugin { private def toPomSettings(moduleInfo: ModuleInfo, groupId: String) = { import moduleInfo._ PomSettings( - description = Option(description), - organization = Option(groupId), - url = homepage.map(_.toExternalForm), + description = description, + organization = groupId, + url = homepage.fold("")(_.toExternalForm), licenses = licenses.map(toLicense), versionControl = toVersionControl(scmInfo), developers = developers.map(toDeveloper) @@ -345,10 +303,10 @@ object ExportBuildPlugin extends AutoPlugin { private def toLicense(license: (String, URL)) = { val (name, url) = license - ModuleConfig.License( + ModuleSpec.License( name = name, url = url.toExternalForm, - distribution = "repo" // sbt hardcodes "repo" + distribution = "repo" // hardcoded by sbt ) } @@ -364,6 +322,6 @@ object ExportBuildPlugin extends AutoPlugin { private def toDeveloper(developer: librarymanagement.Developer) = { import developer._ - ModuleConfig.Developer(id, name, url.toExternalForm) + ModuleSpec.Developer(id, name, url.toExternalForm) } } diff --git a/libs/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/libs/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 976163cb0368..7cddd0d23ca9 100644 --- a/libs/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/libs/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -2,177 +2,129 @@ package mill.main.sbt import mill.constants.Util.isWindows import mill.main.buildgen.* -import mill.main.buildgen.ModuleConfig.* +import mill.main.buildgen.ModuleSpec.* import mill.main.sbt.BuildInfo.* -import mill.util.Jvm import pprint.Util.literalize import scala.util.Using -/** - * Application that imports an SBT build to Mill. - */ object SbtBuildGenMain { def main(args: Array[String]): Unit = mainargs.Parser(this).runOrExit(args.toSeq) - @mainargs.main(doc = "Imports an SBT build located in the current working directory.") - def runImport( - @mainargs.arg(doc = "merge generated build files") + @mainargs.main(doc = "Generates Mill build files that are derived from an SBT build.") + def init( + @mainargs.arg(doc = "path to custom SBT executable") + customSbt: Option[String], + @mainargs.arg(doc = "merge package.mill files in to the root build.mill file") merge: mainargs.Flag, @mainargs.arg(doc = "disable generating meta-build files") - noMeta: mainargs.Flag, - @mainargs.arg(doc = "path to sbt executable") - customSbt: Option[String], - @mainargs.arg(doc = "JDK to use to run sbt") - sbtJvmId: String = "system" + noMeta: mainargs.Flag ): Unit = { println("converting sbt build") - val sbtCmd = customSbt.getOrElse(defaultSbt.fold(sys.error, identity)) - addExportPlugin() + val sbtCmd = customSbt.getOrElse { + def systemSbtExists(cmd: String) = os.call((cmd, "--help"), check = false).exitCode == 1 + if (isWindows) { + val cmd = "sbt.bat" + if (systemSbtExists(cmd)) cmd else sys.error(s"No system wide $cmd found") + } else { + val exes = Seq("sbt", "sbtx") + val cmd = "sbt" + exes.collectFirst { + case exe if os.isFile(os.pwd / exe) => s"./$exe" + }.orElse { + Option.when(systemSbtExists(cmd))(cmd) + }.getOrElse { + sys.error( + s"No sbt executable (${exes.mkString("./", ", ./", "")}), or system-wide $cmd found" + ) + } + } + } + val exportPluginJar = + Using.resource(getClass.getResourceAsStream(exportpluginAssemblyResource))( + os.temp(_, suffix = ".jar") + ) + val sbtMetaDir = os.pwd / "project" + if (!os.exists(sbtMetaDir)) os.makeDir(sbtMetaDir) + os.temp( + s"""addSbtPlugin("com.lihaoyi" % "mill-libs-init-sbt-exportplugin" % "dummy-version" from ${ + literalize(exportPluginJar.wrapped.toUri.toString) + })""", + dir = sbtMetaDir, + suffix = ".sbt" + ) val exportDir = os.temp.dir() try { os.proc( sbtCmd, - "-java-home", - Jvm.resolveJavaHome(sbtJvmId).get, s"-DmillInitExportDir=$exportDir", // Run task with cross-build prefix to export data for all cross Scala versions. "+millInitExportBuild" ).call(stdout = os.Inherit) } catch { - case e: os.SubprocessException => throw RuntimeException( + case e: os.SubprocessException => + val message = "The sbt command to run the `millInitExportBuild` sbt task has failed, " + - s"please check out the following solutions and try again:\n" + - s"1. check whether your existing sbt build works properly;\n" + - s"2. make sure there are no other sbt processes running;\n" + - s"3. clear your build output and cache;\n" + + "please check out the following solutions and try again:\n" + + "1. check whether your existing sbt build works properly;\n" + + "2. make sure there are no other sbt processes running;\n" + + "3. clear your build output and cache;\n" + s"4. update the project's sbt version to the latest or our tested version v$sbtVersion;\n" + - "5. check whether you have the appropriate Java version.\n", - e - ) + "5. check whether you have the appropriate Java version.\n" + throw RuntimeException(message, e) } + var exportedBuild = os.list.stream(exportDir) + .map(path => upickle.default.read[SbtModuleSpec](path.toNIO)).toSeq + exportedBuild = normalizeSbtBuild(exportedBuild) - val exportedBuild = os.list.stream(exportDir) - .map(path => upickle.default.read[SbtModuleSpec](path.toNIO)) - .toSeq - - // divide the exported build into packages - val packages = exportedBuild.groupMap(_.moduleType)(_.module).map { - // package with cross-platform members - case (SbtModuleType.Platform(crossRootDir), modules) => - // segregate module specs for each member - val nestedModules = modules.groupBy(_.name).map { - // non-cross version member + val packages = exportedBuild.groupMap(_.sharedModuleDir)(_.module).map { + case (Left(moduleDir), Seq(module)) => + PackageSpec(moduleDir, module) + case (Left(moduleDir), crossVersionSpecs) => + val module = toCrossModule(crossVersionSpecs) + PackageSpec(moduleDir, module) + case (Right(moduleDir), specs) => + val children = specs.groupBy(_.name).map { case (_, Seq(module)) => module - // cross version member - case (_, partialSpecs) => unifyCrossVersionConfigs(partialSpecs) + case (_, crossVersionSpecs) => toCrossModule(crossVersionSpecs) }.toSeq - // create module for cross-root - val rootModule = ModuleSpec( - name = crossRootDir.lastOption.getOrElse(os.pwd.last), - nestedModules = nestedModules - ) - PackageSpec(crossRootDir, rootModule) - // package with non-cross module - case (SbtModuleType.Default(moduleDir), Seq(module)) => - PackageSpec(moduleDir, module) - // package with cross version module - case (SbtModuleType.Default(moduleDir), partialSpecs) => - PackageSpec(moduleDir, unifyCrossVersionConfigs(partialSpecs)) + PackageSpec.root(moduleDir, children) }.toSeq - val packages0 = normalizeJvmPlatformDeps(packages) - - var build = BuildSpec.fill(packages0).copy(millJvmOpts = sbtJvmOpts) - if (merge.value) build = build.merged - if (!noMeta.value) build = build.withDefaultMetaBuild - BuildWriter(build, renderCrossValueInTask = "scalaVersion()").writeFiles() - } - - private def defaultSbt = { - def systemSbtExists(cmd: String) = - // The return code is somehow 1 instead of 0. - os.call((cmd, "--help"), check = false).exitCode == 1 - if (isWindows) { - val cmd = "sbt.bat" - Either.cond(systemSbtExists(cmd), cmd, s"No system wide $cmd found") - } else { - val exes = Seq("sbt", "sbtx") - val cmd = "sbt" - exes.collectFirst { - case exe if os.isFile(os.pwd / exe) => s"./$exe" - }.orElse { - Option.when(systemSbtExists(cmd))(cmd) - }.toRight( - s"No sbt executable (${exes.mkString("./", ", ./", "")}), or system-wide $cmd found" - ) - } - } - - private def addExportPlugin(): Unit = { - val pluginJar = Using.resource(getClass.getResourceAsStream(exportpluginAssemblyResource))( - os.temp(_, suffix = ".jar") - ) - val scriptDir = os.pwd / "project" - if (!os.exists(scriptDir)) os.makeDir(scriptDir) - os.temp( - s"""addSbtPlugin("com.lihaoyi" % "mill-libs-init-sbt-exportplugin" % "dummy-version" from ${ - literalize(pluginJar.wrapped.toUri.toString) - })""", - dir = scriptDir, - suffix = ".sbt" - ) - } - private def sbtJvmOpts: Seq[String] = { - val file = os.pwd / ".jvmopts" - if (os.isFile(file)) os.read.lines(file) - .map(_.trim) - .filter(s => s.nonEmpty && !s.startsWith("#")) - .flatMap(_.split("\\s")) - else Nil - } - - /** - * Returns the full module specification by combining Scala version specific configurations. - */ - private def unifyCrossVersionConfigs(partials: Seq[ModuleSpec]): ModuleSpec = { - def combineSpecs(part1: ModuleSpec, part2: ModuleSpec): ModuleSpec = part1.copy( - configs = abstractedConfigs(part1.configs, part2.configs), - crossConfigs = part1.crossConfigs ++ part2.crossConfigs, - nestedModules = part1.nestedModules.zip(part2.nestedModules).map(combineSpecs) - ) - def normalizeCrossConfigs(spec: ModuleSpec) = spec.transform { module => - module.copy(crossConfigs = - module.crossConfigs.map((k, v) => (k, inheritedConfigs(v, module.configs))) - ) + val (depRefs, packages0) = + if (noMeta.value) (Nil, packages) else BuildGen.withNamedDeps(packages) + val (baseModule, packages1) = Option.when(!noMeta.value)( + BuildGen.withBaseModule(packages0, "CrossSbtPlatformTests", "CrossSbtPlatformModule") + .orElse(BuildGen.withBaseModule( + packages0, + "CrossSbtTests", + "CrossSbtModule", + "CrossSbtPlatformModule" + )) + .orElse(BuildGen.withBaseModule(packages0, "SbtPlatformTests", "SbtPlatformModule")) + .orElse(BuildGen.withBaseModule(packages0, "SbtTests", "SbtModule", "SbtPlatformModule")) + ).flatten.fold((None, packages0))((base, packages) => (Some(base), packages)) + val millJvmOpts = { + val file = os.pwd / ".jvmopts" + if (os.isFile(file)) os.read.lines(file) + .map(_.trim) + .filter(s => s.nonEmpty && !s.startsWith("#")) + .flatMap(_.split("\\s")) + else Nil } - - normalizeCrossConfigs(partials.reduce(combineSpecs)) + BuildGen.writeBuildFiles(packages1, merge.value, depRefs, baseModule, millJvmOpts) } - /** - * A transformation that sets the `platformed` flag for dependencies in JVM modules if required. - * This prevents double entries when defining constants for dependencies. - */ - private def normalizeJvmPlatformDeps(packages: Seq[PackageSpec]): Seq[PackageSpec] = { - val platformedDeps = packages - .flatMap(_.module.sequence) - .flatMap(module => module.configs ++ module.crossConfigs.flatMap(_._2)) - .flatMap { - case c: JavaModule => c.mvnDeps ++ c.compileMvnDeps ++ c.runMvnDeps - case c: ScalaModule => c.scalacPluginMvnDeps - case _ => Nil + private def normalizeSbtBuild(specs: Seq[SbtModuleSpec]) = { + val platformedDeps = specs.iterator.flatMap(spec => spec.module +: spec.module.test.toSeq) + .flatMap { module => + import module.* + Seq(mvnDeps, compileMvnDeps, runMvnDeps, scalacPluginMvnDeps) } - .filter(_.cross.platformed) - .toSet - - def isJvmModule(module: ModuleSpec) = module.configs.collectFirst { - case _: ModuleConfig.ScalaJSModule => false - case _: ModuleConfig.ScalaNativeModule => false - }.getOrElse(true) - def updatedDep(dep: MvnDep) = { + .flatMap(values => values.base ++ values.cross.flatMap(_._2)).filter(_.cross.platformed).toSet + def updateDep(dep: MvnDep) = { val dep0 = if (dep.cross.platformed) dep else dep.copy(cross = dep.cross match { case v: CrossVersion.Constant => v.copy(platformed = true) @@ -181,26 +133,109 @@ object SbtBuildGenMain { }) if (platformedDeps.contains(dep0)) dep0 else dep } - def updatedConfig(config: ModuleConfig) = config match { - case c: JavaModule => c.copy( - mvnDeps = c.mvnDeps.map(updatedDep), - compileMvnDeps = c.compileMvnDeps.map(updatedDep), - runMvnDeps = c.runMvnDeps.map(updatedDep) - ) - case c: ScalaModule => c.copy( - scalacPluginMvnDeps = c.scalacPluginMvnDeps.map(updatedDep) - ) - case _ => config - } - def updatedSpec(spec: ModuleSpec) = spec.transform { module => - if (isJvmModule(module)) module.copy( - configs = module.configs.map(updatedConfig), - crossConfigs = module.crossConfigs.map((k, v) => (k, v.map(updatedConfig))) + def updateDeps(deps: Values[MvnDep]) = deps.copy( + base = deps.base.map(updateDep), + cross = deps.cross.map((k, v) => (k, v.map(updateDep))) + ) + def updateModule0(module: ModuleSpec) = { + import module.* + module.copy( + mvnDeps = updateDeps(mvnDeps), + compileMvnDeps = updateDeps(compileMvnDeps), + runMvnDeps = updateDeps(runMvnDeps), + scalacPluginMvnDeps = updateDeps(scalacPluginMvnDeps) ) - else module } + def updateModule(module: ModuleSpec): ModuleSpec = { + if (module.supertypes.forall(s => s == "ScalaJSModule" || s == "ScalaNativeModule")) + module + else updateModule0(module).copy(test = module.test.map(updateModule0)) + } + + specs.map(spec => spec.copy(module = updateModule(spec.module))) + } - if (platformedDeps.isEmpty) packages - else packages.map(pkg => pkg.copy(module = updatedSpec(pkg.module))) + private def toCrossModule(crossVersionSpecs: Seq[ModuleSpec]) = { + def combineValue[A](a: Value[A], b: Value[A]) = Value( + if (a.base == b.base) a.base else None, + a.cross ++ b.cross + ) + def combineValues[A](a: Values[A], b: Values[A]) = Values( + a.extend && b.extend, + a.base.intersect(b.base), + a.cross ++ b.cross + ) + def combineModule0(a: ModuleSpec, b: ModuleSpec) = ModuleSpec( + name = a.name, + imports = (a.imports ++ b.imports).distinct, + supertypes = a.supertypes.intersect(b.supertypes), + mixins = if (a.mixins == b.mixins) a.mixins else Nil, + crossKeys = a.crossKeys ++ b.crossKeys, + repositories = combineValues(a.repositories, b.repositories), + mvnDeps = combineValues(a.mvnDeps, b.mvnDeps), + compileMvnDeps = combineValues(a.compileMvnDeps, b.compileMvnDeps), + runMvnDeps = combineValues(a.runMvnDeps, b.runMvnDeps), + bomMvnDeps = combineValues(a.bomMvnDeps, b.bomMvnDeps), + depManagement = combineValues(a.depManagement, b.depManagement), + moduleDeps = combineValues(a.moduleDeps, b.moduleDeps), + compileModuleDeps = combineValues(a.compileModuleDeps, b.compileModuleDeps), + runModuleDeps = combineValues(a.runModuleDeps, b.runModuleDeps), + bomModuleDeps = combineValues(a.bomModuleDeps, b.bomModuleDeps), + javacOptions = combineValues(a.javacOptions, b.javacOptions), + artifactName = combineValue(a.artifactName, b.artifactName), + pomPackagingType = combineValue(a.pomPackagingType, b.pomPackagingType), + pomParentProject = combineValue(a.pomParentProject, b.pomParentProject), + pomSettings = combineValue(a.pomSettings, b.pomSettings), + publishVersion = combineValue(a.publishVersion, b.publishVersion), + versionScheme = combineValue(a.versionScheme, b.versionScheme), + publishProperties = combineValues(a.publishProperties, b.publishProperties), + scalacOptions = combineValues(a.scalacOptions, b.scalacOptions), + scalacPluginMvnDeps = combineValues(a.scalacPluginMvnDeps, b.scalacPluginMvnDeps), + scalaJSVersion = combineValue(a.scalaJSVersion, b.scalaJSVersion), + moduleKind = combineValue(a.moduleKind, b.moduleKind), + scalaNativeVersion = combineValue(a.scalaNativeVersion, b.scalaNativeVersion), + sourcesRootFolders = combineValues(a.sourcesRootFolders, b.sourcesRootFolders), + testParallelism = combineValue(a.testParallelism, b.testParallelism), + testSandboxWorkingDir = combineValue(a.testSandboxWorkingDir, b.testSandboxWorkingDir) + ) + def combineModule(a: ModuleSpec, b: ModuleSpec): ModuleSpec = combineModule0(a, b).copy( + test = a.test.zip(b.test).map(combineModule0) + ) + def normalizeValue[A](a: Value[A]): Value[A] = a.copy(cross = a.cross.collect { + case kv @ (_, v) if !a.base.contains(v) => kv + }) + def normalizeValues[A](a: Values[A]): Values[A] = + a.copy(cross = a.cross.map((k, v) => (k, v.diff(a.base))).filter(_._2.nonEmpty)) + def normalizeModule0(a: ModuleSpec) = a.copy( + repositories = normalizeValues(a.repositories), + mvnDeps = normalizeValues(a.mvnDeps), + compileMvnDeps = normalizeValues(a.compileMvnDeps), + runMvnDeps = normalizeValues(a.runMvnDeps), + bomMvnDeps = normalizeValues(a.bomMvnDeps), + depManagement = normalizeValues(a.depManagement), + moduleDeps = normalizeValues(a.moduleDeps), + compileModuleDeps = normalizeValues(a.compileModuleDeps), + runModuleDeps = normalizeValues(a.runModuleDeps), + bomModuleDeps = normalizeValues(a.bomModuleDeps), + javacOptions = normalizeValues(a.javacOptions), + artifactName = normalizeValue(a.artifactName), + pomPackagingType = normalizeValue(a.pomPackagingType), + pomParentProject = normalizeValue(a.pomParentProject), + pomSettings = normalizeValue(a.pomSettings), + publishVersion = normalizeValue(a.publishVersion), + versionScheme = normalizeValue(a.versionScheme), + publishProperties = normalizeValues(a.publishProperties), + scalacOptions = normalizeValues(a.scalacOptions), + scalacPluginMvnDeps = normalizeValues(a.scalacPluginMvnDeps), + scalaJSVersion = normalizeValue(a.scalaJSVersion), + moduleKind = normalizeValue(a.moduleKind), + scalaNativeVersion = normalizeValue(a.scalaNativeVersion), + sourcesRootFolders = normalizeValues(a.sourcesRootFolders), + testParallelism = normalizeValue(a.testParallelism), + testSandboxWorkingDir = normalizeValue(a.testSandboxWorkingDir) + ) + def normalizeModule(a: ModuleSpec): ModuleSpec = + normalizeModule0(a).copy(test = a.test.map(normalizeModule0)) + normalizeModule(crossVersionSpecs.reduce(combineModule)) } } diff --git a/libs/init/sbt/test/resources/expected/cross-version/build.mill b/libs/init/sbt/test/resources/expected/cross-version/build.mill index 241a215be4db..05e12728cad0 100644 --- a/libs/init/sbt/test/resources/expected/cross-version/build.mill +++ b/libs/init/sbt/test/resources/expected/cross-version/build.mill @@ -1,23 +1,20 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system //| mill-jvm-opts: ["-Xms2g", "-Xmx2g"] package build - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* - +import millbuild.Deps object `package` extends Cross[CrossVersionModule]("2.12.18", "2.13.12", "3.3.1") -trait CrossVersionModule extends PublishModule with CrossSbtModule { +trait CrossVersionModule extends PublishModule, CrossSbtModule { - def javacOptions = super.javacOptions() ++ Seq("--release", "8") + def javacOptions = Seq("--release", "8") def artifactName = "my-cross-project" - def jvmId = "zulu:11" - def pomSettings = PomSettings( "my-cross-project", "com.example", @@ -29,9 +26,9 @@ trait CrossVersionModule extends PublishModule with CrossSbtModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends CrossSbtTests with TestModule.ScalaTest { + object test extends CrossSbtTests, TestModule.ScalaTest { - def mvnDeps = super.mvnDeps() ++ Seq(Deps.scalatest) + def mvnDeps = Seq(Deps.scalatest) def testParallelism = false diff --git a/libs/init/sbt/test/resources/expected/cross-version/mill-build/src/Deps.scala b/libs/init/sbt/test/resources/expected/cross-version/mill-build/src/Deps.scala index bacef38a9298..90d26fcb6970 100644 --- a/libs/init/sbt/test/resources/expected/cross-version/mill-build/src/Deps.scala +++ b/libs/init/sbt/test/resources/expected/cross-version/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val scalatest = mvn"org.scalatest::scalatest:3.2.18" diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/build.mill b/libs/init/sbt/test/resources/expected/crossproject-cross-version/build.mill index 8958ea4ccb06..bb1d6fd1ac2c 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/build.mill +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/build.mill @@ -1,6 +1,5 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* - object `package` extends Module {} diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/dummy/package.mill b/libs/init/sbt/test/resources/expected/crossproject-cross-version/dummy/package.mill index fb171f025b3f..d5b93b842495 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/dummy/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/dummy/package.mill @@ -1,25 +1,23 @@ package build.dummy - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") - trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JsModule extends ProjectBaseModule, ScalaJSModule { def scalaJSVersion = "1.16.0" + def moduleKind = ModuleKind.NoModule + def artifactName = "dummy" def pomSettings = PomSettings( @@ -34,10 +32,7 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JvmModule extends ProjectBaseModule { def artifactName = "dummy" @@ -53,11 +48,7 @@ object `package` extends Module { } object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") - trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait NativeModule extends ProjectBaseModule, ScalaNativeModule { def scalaNativeVersion = "0.5.4" @@ -73,5 +64,4 @@ object `package` extends Module { ) } - } diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/full/package.mill b/libs/init/sbt/test/resources/expected/crossproject-cross-version/full/package.mill index 54f283b6c7df..8e332e239486 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/full/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/full/package.mill @@ -1,25 +1,23 @@ package build.full - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") - trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JsModule extends ProjectBaseModule, ScalaJSModule { def scalaJSVersion = "1.16.0" + def moduleKind = ModuleKind.NoModule + def artifactName = "full" def pomSettings = PomSettings( @@ -34,12 +32,9 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JvmModule extends ProjectBaseModule { - def moduleDeps = super.moduleDeps ++ Seq(build.`jvm-util`()) + def moduleDeps = Seq(build.`jvm-util`()) def artifactName = "full" @@ -55,11 +50,7 @@ object `package` extends Module { } object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") - trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait NativeModule extends ProjectBaseModule, ScalaNativeModule { def scalaNativeVersion = "0.5.4" @@ -75,5 +66,4 @@ object `package` extends Module { ) } - } diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/jvm-util/package.mill b/libs/init/sbt/test/resources/expected/crossproject-cross-version/jvm-util/package.mill index 8cf027b7e48d..aa7ac9fe0be8 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/jvm-util/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/jvm-util/package.mill @@ -1,14 +1,25 @@ package build.`jvm-util` - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* - +import millbuild.Deps object `package` extends Cross[JvmUtilModule]("2.12.20", "2.13.14", "3.7.1") -trait JvmUtilModule - extends CrossprojectCrossVersionBaseModule with CrossSbtModule { +trait JvmUtilModule extends PublishModule, CrossSbtModule { + + def mvnDeps = Seq(Deps.upickle) + + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { + case "2.12.20" => Seq( + "-Xlint:_,-unused", + "-Ywarn-numeric-widen", + "-Ywarn-unused:_,-nowarn,-privates" + ) + case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") + case "3.7.1" => Seq("-Wunused") + case _ => Seq() + }) def artifactName = "jvmutil" @@ -21,4 +32,6 @@ trait JvmUtilModule Seq() ) + def publishVersion = "0.1.0-SNAPSHOT" + } diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/CrossprojectCrossVersionBaseModule.scala b/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/CrossprojectCrossVersionBaseModule.scala deleted file mode 100644 index 58a0bef3f3b4..000000000000 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/CrossprojectCrossVersionBaseModule.scala +++ /dev/null @@ -1,26 +0,0 @@ -package millbuild - -import mill.javalib.* -import mill.javalib.publish.* -import mill.scalalib.* - -trait CrossprojectCrossVersionBaseModule - extends PublishModule with CrossScalaModule { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.upickle) - - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { - case "2.12.20" => Seq( - "-Xlint:_,-unused", - "-Ywarn-numeric-widen", - "-Ywarn-unused:_,-nowarn,-privates" - ) - case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") - case "3.7.1" => Seq("-Wunused") - case _ => Nil - }) - - def publishVersion = "0.1.0-SNAPSHOT" - -} diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/Deps.scala b/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/Deps.scala index c66f8a0af386..f4d84c319a75 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/Deps.scala +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val upickle = mvn"com.lihaoyi::upickle::4.3.0" diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/ProjectBaseModule.scala b/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/ProjectBaseModule.scala new file mode 100644 index 000000000000..5d605f14666f --- /dev/null +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/mill-build/src/ProjectBaseModule.scala @@ -0,0 +1,28 @@ +package millbuild +import mill.* +import mill.javalib.PublishModule +import mill.javalib.publish.* +import mill.scalajslib.ScalaJSModule +import mill.scalajslib.api.* +import mill.scalalib.* +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +trait ProjectBaseModule extends PublishModule, CrossSbtPlatformModule { + + def mvnDeps = Seq(Deps.upickle) + + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { + case "2.12.20" => Seq( + "-Xlint:_,-unused", + "-Ywarn-numeric-widen", + "-Ywarn-unused:_,-nowarn,-privates" + ) + case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") + case "3.7.1" => Seq("-Wunused") + case _ => Seq() + }) + + def publishVersion = "0.1.0-SNAPSHOT" + +} diff --git a/libs/init/sbt/test/resources/expected/crossproject-cross-version/pure/package.mill b/libs/init/sbt/test/resources/expected/crossproject-cross-version/pure/package.mill index 1446ca396c25..cd337e19c8ad 100644 --- a/libs/init/sbt/test/resources/expected/crossproject-cross-version/pure/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject-cross-version/pure/package.mill @@ -1,25 +1,23 @@ package build.pure - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") - trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JsModule extends ProjectBaseModule, ScalaJSModule { def scalaJSVersion = "1.16.0" + def moduleKind = ModuleKind.NoModule + def artifactName = "pure" def pomSettings = PomSettings( @@ -34,10 +32,7 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait JvmModule extends ProjectBaseModule { def artifactName = "pure" @@ -53,11 +48,7 @@ object `package` extends Module { } object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") - trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with CrossprojectCrossVersionBaseModule - with CrossSbtPlatformModule { + trait NativeModule extends ProjectBaseModule, ScalaNativeModule { def scalaNativeVersion = "0.5.4" @@ -73,5 +64,4 @@ object `package` extends Module { ) } - } diff --git a/libs/init/sbt/test/resources/expected/crossproject/build.mill b/libs/init/sbt/test/resources/expected/crossproject/build.mill index 8958ea4ccb06..bb1d6fd1ac2c 100644 --- a/libs/init/sbt/test/resources/expected/crossproject/build.mill +++ b/libs/init/sbt/test/resources/expected/crossproject/build.mill @@ -1,6 +1,5 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* - object `package` extends Module {} diff --git a/libs/init/sbt/test/resources/expected/crossproject/dummy/package.mill b/libs/init/sbt/test/resources/expected/crossproject/dummy/package.mill index 80d36507e8bf..e3939b4e098e 100644 --- a/libs/init/sbt/test/resources/expected/crossproject/dummy/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject/dummy/package.mill @@ -1,20 +1,22 @@ package build.dummy - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { - object js extends ScalaJSModule with CrossprojectBaseModule { + object js extends ProjectBaseModule, ScalaJSModule { def scalaJSVersion = "1.16.0" + def moduleKind = ModuleKind.NoModule + def artifactName = "dummy" def pomSettings = PomSettings( @@ -26,23 +28,9 @@ object `package` extends Module { Seq() ) - object test - extends ScalaJSTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - object jvm extends CrossprojectBaseModule { + object jvm extends ProjectBaseModule { def artifactName = "dummy" @@ -55,19 +43,9 @@ object `package` extends Module { Seq() ) - object test extends SbtPlatformTests with SbtTests with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - object native extends ScalaNativeModule with CrossprojectBaseModule { + object native extends ProjectBaseModule, ScalaNativeModule { def scalaNativeVersion = "0.5.4" @@ -82,20 +60,5 @@ object `package` extends Module { Seq() ) - object test - extends ScalaNativeTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - } diff --git a/libs/init/sbt/test/resources/expected/crossproject/full/package.mill b/libs/init/sbt/test/resources/expected/crossproject/full/package.mill index 48bb1ba0c703..b78938392f20 100644 --- a/libs/init/sbt/test/resources/expected/crossproject/full/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject/full/package.mill @@ -1,25 +1,27 @@ package build.full - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { - object js extends ScalaJSModule with CrossprojectBaseModule { + object js extends ProjectBaseModule, ScalaJSModule { + + def moduleDeps = Seq(build.pure.js) def scalaJSVersion = "1.16.0" - def moduleDeps = super.moduleDeps ++ Seq(build.pure.js) + def moduleKind = ModuleKind.NoModule - def artifactName = "full" + def sourcesRootFolders = super.sourcesRootFolders ++ Seq("shared") - def sourcesRootFolders = super.sourcesRootFolders ++ Seq(os.sub / "shared") + def artifactName = "full" def pomSettings = PomSettings( "full", @@ -30,31 +32,21 @@ object `package` extends Module { Seq() ) - object test - extends ScalaJSTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) + object test extends Tests, ScalaJSTests, TestModule.Utest { def moduleDeps = super.moduleDeps ++ Seq(build.pure.js.test) - def testParallelism = false - - def testSandboxWorkingDir = false - } } - object jvm extends CrossprojectBaseModule { + object jvm extends ProjectBaseModule { - def moduleDeps = super.moduleDeps ++ Seq(build.pure.jvm) + def moduleDeps = Seq(build.pure.jvm) - def artifactName = "full" + def sourcesRootFolders = super.sourcesRootFolders ++ Seq("shared") - def sourcesRootFolders = super.sourcesRootFolders ++ Seq(os.sub / "shared") + def artifactName = "full" def pomSettings = PomSettings( "full", @@ -65,30 +57,24 @@ object `package` extends Module { Seq() ) - object test extends SbtPlatformTests with SbtTests with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) + object test extends Tests, TestModule.Utest { def moduleDeps = super.moduleDeps ++ Seq(build.pure.jvm.test) - def testParallelism = false - - def testSandboxWorkingDir = false - } } - object native extends ScalaNativeModule with CrossprojectBaseModule { + object native extends ProjectBaseModule, ScalaNativeModule { + + def moduleDeps = Seq(build.pure.native) def scalaNativeVersion = "0.5.4" - def moduleDeps = super.moduleDeps ++ Seq(build.pure.native) + def sourcesRootFolders = super.sourcesRootFolders ++ Seq("shared") def artifactName = "full" - def sourcesRootFolders = super.sourcesRootFolders ++ Seq(os.sub / "shared") - def pomSettings = PomSettings( "full", "full", @@ -98,22 +84,11 @@ object `package` extends Module { Seq() ) - object test - extends ScalaNativeTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) + object test extends Tests, ScalaNativeTests, TestModule.Utest { def moduleDeps = super.moduleDeps ++ Seq(build.pure.native.test) - def testParallelism = false - - def testSandboxWorkingDir = false - } } - } diff --git a/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/CrossprojectBaseModule.scala b/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/CrossprojectBaseModule.scala deleted file mode 100644 index 75ed6f4db394..000000000000 --- a/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/CrossprojectBaseModule.scala +++ /dev/null @@ -1,16 +0,0 @@ -package millbuild - -import mill.javalib.* -import mill.javalib.publish.* -import mill.scalalib.* - -trait CrossprojectBaseModule - extends SbtPlatformModule with PublishModule with SbtModule { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.upickle) - - def scalaVersion = "2.13.14" - - def publishVersion = "0.1.0-SNAPSHOT" - -} diff --git a/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/Deps.scala b/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/Deps.scala index 12d8be284139..226732798ceb 100644 --- a/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/Deps.scala +++ b/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val osLib = mvn"com.lihaoyi::os-lib:0.11.5" diff --git a/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/ProjectBaseModule.scala b/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/ProjectBaseModule.scala new file mode 100644 index 000000000000..ffbc760e807d --- /dev/null +++ b/libs/init/sbt/test/resources/expected/crossproject/mill-build/src/ProjectBaseModule.scala @@ -0,0 +1,27 @@ +package millbuild +import mill.* +import mill.javalib.PublishModule +import mill.javalib.publish.* +import mill.scalajslib.ScalaJSModule +import mill.scalajslib.api.* +import mill.scalalib.* +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +trait ProjectBaseModule extends PublishModule, SbtPlatformModule { + + def mvnDeps = Seq(Deps.upickle) + + def scalaVersion = "2.13.14" + + def publishVersion = "0.1.0-SNAPSHOT" + + trait Tests extends SbtPlatformTests { + + def mvnDeps = Seq(Deps.utest) + + def testParallelism = false + + def testSandboxWorkingDir = false + + } +} diff --git a/libs/init/sbt/test/resources/expected/crossproject/pure/package.mill b/libs/init/sbt/test/resources/expected/crossproject/pure/package.mill index da498f3e6d4f..d99f815d0234 100644 --- a/libs/init/sbt/test/resources/expected/crossproject/pure/package.mill +++ b/libs/init/sbt/test/resources/expected/crossproject/pure/package.mill @@ -1,20 +1,22 @@ package build.pure - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* -import millbuild.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* +import millbuild.Deps +import millbuild.ProjectBaseModule object `package` extends Module { - object js extends ScalaJSModule with CrossprojectBaseModule { + object js extends ProjectBaseModule, ScalaJSModule { def scalaJSVersion = "1.16.0" + def moduleKind = ModuleKind.NoModule + def artifactName = "pure" def pomSettings = PomSettings( @@ -26,23 +28,9 @@ object `package` extends Module { Seq() ) - object test - extends ScalaJSTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - object jvm extends CrossprojectBaseModule { + object jvm extends ProjectBaseModule { def mvnDeps = super.mvnDeps() ++ Seq(Deps.osLib) @@ -57,19 +45,9 @@ object `package` extends Module { Seq() ) - object test extends SbtPlatformTests with SbtTests with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - object native extends ScalaNativeModule with CrossprojectBaseModule { + object native extends ProjectBaseModule, ScalaNativeModule { def scalaNativeVersion = "0.5.4" @@ -84,20 +62,5 @@ object `package` extends Module { Seq() ) - object test - extends ScalaNativeTests - with SbtPlatformTests - with SbtTests - with TestModule.Utest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.utest) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } - } - } diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill index 8958ea4ccb06..bb1d6fd1ac2c 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill @@ -1,6 +1,5 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* - object `package` extends Module {} diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index d83ddd075969..a06e64a08d9c 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -1,13 +1,13 @@ package build.common - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* - -object `package` extends SbtMultiProjectExampleBaseModule { +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( Deps.logbackClassic, Deps.logstashLogbackEncoder, Deps.scalaLogging, @@ -16,10 +16,10 @@ object `package` extends SbtMultiProjectExampleBaseModule { Deps.akkaStream ) - def artifactName = "common" - def scalacOptions = super.scalacOptions() ++ Seq("-encoding", "utf8") + def artifactName = "common" + def pomSettings = PomSettings( "This is the common module.", "com.pbassiner", @@ -41,14 +41,6 @@ object `package` extends SbtMultiProjectExampleBaseModule { Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) - object test extends SbtTests with TestModule.ScalaTest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.scalatest, Deps.scalacheck) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + object test extends Tests {} } diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/Deps.scala b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/Deps.scala index a85796798fdf..76894226822e 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/Deps.scala +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val akkaStream = mvn"com.typesafe.akka::akka-stream:2.5.6" diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/SbtMultiProjectExampleBaseModule.scala b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/ProjectBaseModule.scala similarity index 54% rename from libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/SbtMultiProjectExampleBaseModule.scala rename to libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/ProjectBaseModule.scala index af1b7f85c54d..bad6f14f56a7 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/SbtMultiProjectExampleBaseModule.scala +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/mill-build/src/ProjectBaseModule.scala @@ -1,14 +1,13 @@ package millbuild - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* - -trait SbtMultiProjectExampleBaseModule extends PublishModule with SbtModule { +trait ProjectBaseModule extends PublishModule, SbtModule { def scalaVersion = "2.12.3" - def scalacOptions = super.scalacOptions() ++ Seq( + def scalacOptions = Seq( "-unchecked", "-feature", "-language:existentials", @@ -18,13 +17,22 @@ trait SbtMultiProjectExampleBaseModule extends PublishModule with SbtModule { "-deprecation" ) - def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ Seq(Deps.wartremover) + def scalacPluginMvnDeps = Seq(Deps.wartremover) def publishVersion = "0.1.0-SNAPSHOT" - def repositories = super.repositories() ++ Seq( + def repositories = Seq( "https://oss.sonatype.org/service/local/repositories/releases/content/", "https://oss.sonatype.org/content/repositories/snapshots" ) + trait Tests extends SbtTests, TestModule.ScalaTest { + + def mvnDeps = Seq(Deps.scalatest, Deps.scalacheck) + + def testParallelism = false + + def testSandboxWorkingDir = false + + } } diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index 87e7ced59315..c5efa274d66e 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -1,13 +1,15 @@ package build.multi1 - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { -object `package` extends SbtMultiProjectExampleBaseModule { + def moduleDeps = Seq(build.common) - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( Deps.logbackClassic, Deps.logstashLogbackEncoder, Deps.scalaLogging, @@ -18,12 +20,10 @@ object `package` extends SbtMultiProjectExampleBaseModule { Deps.monocleMacro ) - def moduleDeps = super.moduleDeps ++ Seq(build.common) + def scalacOptions = super.scalacOptions() ++ Seq("-encoding", "utf8", "-V") def artifactName = "multi1" - def scalacOptions = super.scalacOptions() ++ Seq("-encoding", "utf8", "-V") - def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", @@ -45,14 +45,6 @@ object `package` extends SbtMultiProjectExampleBaseModule { Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) - object test extends SbtTests with TestModule.ScalaTest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.scalatest, Deps.scalacheck) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + object test extends Tests {} } diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index d6b4fba2276e..dae03b192721 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -1,13 +1,15 @@ package build.multi2 - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { -object `package` extends SbtMultiProjectExampleBaseModule { + def moduleDeps = Seq(build.common) - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( Deps.logbackClassic, Deps.logstashLogbackEncoder, Deps.scalaLogging, @@ -17,8 +19,6 @@ object `package` extends SbtMultiProjectExampleBaseModule { Deps.pureconfig ) - def moduleDeps = super.moduleDeps ++ Seq(build.common) - def artifactName = "multi2" def pomSettings = PomSettings( @@ -42,14 +42,6 @@ object `package` extends SbtMultiProjectExampleBaseModule { Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) - object test extends SbtTests with TestModule.ScalaTest { - - def mvnDeps = super.mvnDeps() ++ Seq(Deps.scalatest, Deps.scalacheck) - - def testParallelism = false - - def testSandboxWorkingDir = false - - } + object test extends Tests {} } diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill index 4d12b13f7f96..1c5f421927fe 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill @@ -1,18 +1,18 @@ package build.nested.nested - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* +import millbuild.Deps +import millbuild.ProjectBaseModule +object `package` extends ProjectBaseModule { -object `package` extends SbtMultiProjectExampleBaseModule { + def mvnDeps = Seq(Deps.nettyTransportNativeEpoll) - def mvnDeps = super.mvnDeps() ++ Seq(Deps.nettyTransportNativeEpoll) + def scalacOptions = super.scalacOptions() ++ Seq("-encoding", "utf8") def artifactName = "nested" - def scalacOptions = super.scalacOptions() ++ Seq("-encoding", "utf8") - def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", diff --git a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill index 2ba917ae0300..81a9d809998b 100644 --- a/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill +++ b/libs/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill @@ -1,5 +1,3 @@ package build.nested - import mill.* - object `package` extends Module {} diff --git a/libs/init/sbt/test/resources/expected/scala-seed-project/build.mill b/libs/init/sbt/test/resources/expected/scala-seed-project/build.mill index d2c8210c1848..82d8075b15f1 100644 --- a/libs/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/libs/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -1,17 +1,17 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - -import mill.javalib.* +import mill.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* -import millbuild.* +import millbuild.Deps +object `package` extends PublishModule, SbtModule { -object `package` extends PublishModule with SbtModule { + def scalaVersion = "2.13.12" def artifactName = "scala-seed-project" - def scalaVersion = "2.13.12" - def pomSettings = PomSettings( "Scala Seed Project", "com.example", @@ -23,9 +23,9 @@ object `package` extends PublishModule with SbtModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends SbtTests with TestModule.Munit { + object test extends SbtTests, TestModule.Munit { - def mvnDeps = super.mvnDeps() ++ Seq(Deps.munit) + def mvnDeps = Seq(Deps.munit) def testParallelism = false diff --git a/libs/init/sbt/test/resources/expected/scala-seed-project/mill-build/src/Deps.scala b/libs/init/sbt/test/resources/expected/scala-seed-project/mill-build/src/Deps.scala index 9896f723b1a9..1de272655b05 100644 --- a/libs/init/sbt/test/resources/expected/scala-seed-project/mill-build/src/Deps.scala +++ b/libs/init/sbt/test/resources/expected/scala-seed-project/mill-build/src/Deps.scala @@ -1,7 +1,5 @@ package millbuild - import mill.javalib.* - object Deps { val munit = mvn"org.scalameta::munit:0.7.29" diff --git a/libs/init/sbt/test/resources/expected/with-args/crossproject-cross-version/build.mill b/libs/init/sbt/test/resources/expected/with-args/crossproject-cross-version/build.mill index 1a225aabe646..235db517d732 100644 --- a/libs/init/sbt/test/resources/expected/with-args/crossproject-cross-version/build.mill +++ b/libs/init/sbt/test/resources/expected/with-args/crossproject-cross-version/build.mill @@ -1,33 +1,30 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* -import mill.scalajslib.* +import mill.scalajslib.ScalaJSModule import mill.scalajslib.api.* import mill.scalalib.* -import mill.scalanativelib.* - +import mill.scalanativelib.ScalaNativeModule +import mill.scalanativelib.api.* object `package` extends Module { object dummy extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + extends ScalaJSModule, PublishModule, CrossSbtPlatformModule { + + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") def scalaJSVersion = "1.16.0" - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def moduleKind = ModuleKind.NoModule - def artifactName = "dummy" - - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -36,9 +33,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "dummy" + def pomSettings = PomSettings( "dummy", "dummy", @@ -53,17 +52,12 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + trait JvmModule extends PublishModule, CrossSbtPlatformModule { - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def artifactName = "dummy" - - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -72,9 +66,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "dummy" + def pomSettings = PomSettings( "dummy", "dummy", @@ -90,19 +86,14 @@ object `package` extends Module { object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + extends ScalaNativeModule, PublishModule, CrossSbtPlatformModule { - def scalaNativeVersion = "0.5.4" - - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def artifactName = "dummy" + def scalaNativeVersion = "0.5.4" - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -111,9 +102,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "dummy" + def pomSettings = PomSettings( "dummy", "dummy", @@ -126,26 +119,22 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" } - } object full extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + extends ScalaJSModule, PublishModule, CrossSbtPlatformModule { - def scalaJSVersion = "1.16.0" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def scalaJSVersion = "1.16.0" - def artifactName = "full" + def moduleKind = ModuleKind.NoModule - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -154,9 +143,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "full" + def pomSettings = PomSettings( "full", "full", @@ -171,19 +162,14 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + trait JvmModule extends PublishModule, CrossSbtPlatformModule { - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def moduleDeps = Seq(build.`jvm-util`()) - def moduleDeps = super.moduleDeps ++ Seq(build.`jvm-util`()) - - def artifactName = "full" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -192,9 +178,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "full" + def pomSettings = PomSettings( "full", "full", @@ -210,19 +198,14 @@ object `package` extends Module { object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + extends ScalaNativeModule, PublishModule, CrossSbtPlatformModule { - def scalaNativeVersion = "0.5.4" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") - - def artifactName = "full" + def scalaNativeVersion = "0.5.4" - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -231,9 +214,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "full" + def pomSettings = PomSettings( "full", "full", @@ -246,18 +231,15 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" } - } object `jvm-util` extends Cross[JvmUtilModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmUtilModule extends PublishModule with CrossSbtModule { - - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + trait JvmUtilModule extends PublishModule, CrossSbtModule { - def artifactName = "jvmutil" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -265,9 +247,11 @@ object `package` extends Module { ) case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "jvmutil" + def pomSettings = PomSettings( "jvmUtil", "jvmutil", @@ -285,19 +269,16 @@ object `package` extends Module { object js extends Cross[JsModule]("2.12.20", "2.13.14", "3.7.1") trait JsModule - extends ScalaJSModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { + extends ScalaJSModule, PublishModule, CrossSbtPlatformModule { - def scalaJSVersion = "1.16.0" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def scalaJSVersion = "1.16.0" - def artifactName = "pure" + def moduleKind = ModuleKind.NoModule - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -306,9 +287,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "pure" + def pomSettings = PomSettings( "pure", "pure", @@ -323,17 +306,12 @@ object `package` extends Module { } object jvm extends Cross[JvmModule]("2.12.20", "2.13.14", "3.7.1") - trait JvmModule - extends SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { - - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + trait JvmModule extends PublishModule, CrossSbtPlatformModule { - def artifactName = "pure" + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -342,9 +320,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "pure" + def pomSettings = PomSettings( "pure", "pure", @@ -360,19 +340,14 @@ object `package` extends Module { object native extends Cross[NativeModule]("2.12.20", "2.13.14", "3.7.1") trait NativeModule - extends ScalaNativeModule - with SbtPlatformModule - with PublishModule - with CrossSbtPlatformModule { - - def scalaNativeVersion = "0.5.4" + extends ScalaNativeModule, PublishModule, CrossSbtPlatformModule { - def mvnDeps = super.mvnDeps() ++ Seq(mvn"com.lihaoyi::upickle::4.3.0") + def mvnDeps = Seq(mvn"com.lihaoyi::upickle::4.3.0") - def artifactName = "pure" + def scalaNativeVersion = "0.5.4" - def scalacOptions = super.scalacOptions() ++ Seq("-deprecation") ++ - (scalaVersion() match { + def scalacOptions = Seq("-deprecation") ++ + (crossScalaVersion match { case "2.12.20" => Seq( "-Xlint:_,-unused", "-Ywarn-numeric-widen", @@ -381,9 +356,11 @@ object `package` extends Module { case "2.13.14" => Seq("-Xlint:_,-unused", "-Wnumeric-widen", "-Wunused") case "3.7.1" => Seq("-Wunused") - case _ => Nil + case _ => Seq() }) + def artifactName = "pure" + def pomSettings = PomSettings( "pure", "pure", @@ -396,7 +373,5 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" } - } - } diff --git a/libs/init/sbt/test/resources/expected/with-args/sbt-multi-project-example/build.mill b/libs/init/sbt/test/resources/expected/with-args/sbt-multi-project-example/build.mill index 905a39c01346..b285b629697e 100644 --- a/libs/init/sbt/test/resources/expected/with-args/sbt-multi-project-example/build.mill +++ b/libs/init/sbt/test/resources/expected/with-args/sbt-multi-project-example/build.mill @@ -1,16 +1,15 @@ //| mill-version: SNAPSHOT +//| mill-jvm-version: system package build - import mill.* -import mill.javalib.* +import mill.javalib.PublishModule import mill.javalib.publish.* import mill.scalalib.* - object `package` extends Module { - object common extends PublishModule with SbtModule { + object common extends PublishModule, SbtModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"ch.qos.logback:logback-classic:1.2.3", mvn"net.logstash.logback:logstash-logback-encoder:4.11", mvn"com.typesafe.scala-logging::scala-logging:3.7.2", @@ -19,11 +18,9 @@ object `package` extends Module { mvn"com.typesafe.akka::akka-stream:2.5.6" ) - def artifactName = "common" - def scalaVersion = "2.12.3" - def scalacOptions = super.scalacOptions() ++ Seq( + def scalacOptions = Seq( "-unchecked", "-feature", "-language:existentials", @@ -35,8 +32,9 @@ object `package` extends Module { "utf8" ) - def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ - Seq(mvn"org.wartremover::wartremover:2.2.1") + def scalacPluginMvnDeps = Seq(mvn"org.wartremover::wartremover:2.2.1") + + def artifactName = "common" def pomSettings = PomSettings( "This is the common module.", @@ -63,14 +61,14 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" - def repositories = super.repositories() ++ Seq( + def repositories = Seq( "https://oss.sonatype.org/service/local/repositories/releases/content/", "https://oss.sonatype.org/content/repositories/snapshots" ) - object test extends SbtTests with TestModule.ScalaTest { + object test extends SbtTests, TestModule.ScalaTest { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"org.scalatest::scalatest:3.0.4", mvn"org.scalacheck::scalacheck:1.13.5" ) @@ -83,9 +81,11 @@ object `package` extends Module { } - object multi1 extends PublishModule with SbtModule { + object multi1 extends PublishModule, SbtModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def moduleDeps = Seq(build.common) + + def mvnDeps = Seq( mvn"ch.qos.logback:logback-classic:1.2.3", mvn"net.logstash.logback:logstash-logback-encoder:4.11", mvn"com.typesafe.scala-logging::scala-logging:3.7.2", @@ -96,13 +96,9 @@ object `package` extends Module { mvn"com.github.julien-truffaut::monocle-macro:1.4.0" ) - def moduleDeps = super.moduleDeps ++ Seq(build.common) - - def artifactName = "multi1" - def scalaVersion = "2.12.3" - def scalacOptions = super.scalacOptions() ++ Seq( + def scalacOptions = Seq( "-unchecked", "-feature", "-language:existentials", @@ -115,8 +111,9 @@ object `package` extends Module { "-V" ) - def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ - Seq(mvn"org.wartremover::wartremover:2.2.1") + def scalacPluginMvnDeps = Seq(mvn"org.wartremover::wartremover:2.2.1") + + def artifactName = "multi1" def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", @@ -143,14 +140,14 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" - def repositories = super.repositories() ++ Seq( + def repositories = Seq( "https://oss.sonatype.org/service/local/repositories/releases/content/", "https://oss.sonatype.org/content/repositories/snapshots" ) - object test extends SbtTests with TestModule.ScalaTest { + object test extends SbtTests, TestModule.ScalaTest { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"org.scalatest::scalatest:3.0.4", mvn"org.scalacheck::scalacheck:1.13.5" ) @@ -163,9 +160,11 @@ object `package` extends Module { } - object multi2 extends PublishModule with SbtModule { + object multi2 extends PublishModule, SbtModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def moduleDeps = Seq(build.common) + + def mvnDeps = Seq( mvn"ch.qos.logback:logback-classic:1.2.3", mvn"net.logstash.logback:logstash-logback-encoder:4.11", mvn"com.typesafe.scala-logging::scala-logging:3.7.2", @@ -175,13 +174,9 @@ object `package` extends Module { mvn"com.github.pureconfig::pureconfig:0.8.0" ) - def moduleDeps = super.moduleDeps ++ Seq(build.common) - - def artifactName = "multi2" - def scalaVersion = "2.12.3" - def scalacOptions = super.scalacOptions() ++ Seq( + def scalacOptions = Seq( "-unchecked", "-feature", "-language:existentials", @@ -191,8 +186,9 @@ object `package` extends Module { "-deprecation" ) - def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ - Seq(mvn"org.wartremover::wartremover:2.2.1") + def scalacPluginMvnDeps = Seq(mvn"org.wartremover::wartremover:2.2.1") + + def artifactName = "multi2" def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", @@ -219,14 +215,14 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" - def repositories = super.repositories() ++ Seq( + def repositories = Seq( "https://oss.sonatype.org/service/local/repositories/releases/content/", "https://oss.sonatype.org/content/repositories/snapshots" ) - object test extends SbtTests with TestModule.ScalaTest { + object test extends SbtTests, TestModule.ScalaTest { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"org.scalatest::scalatest:3.0.4", mvn"org.scalacheck::scalacheck:1.13.5" ) @@ -241,17 +237,15 @@ object `package` extends Module { object nested extends Module { - object nested extends PublishModule with SbtModule { + object nested extends PublishModule, SbtModule { - def mvnDeps = super.mvnDeps() ++ Seq( + def mvnDeps = Seq( mvn"io.netty:netty-transport-native-epoll:4.1.118.Final;classifier=linux-x86_64;type=pom;exclude=io.netty:netty-transport-native-epoll" ) - def artifactName = "nested" - def scalaVersion = "2.12.3" - def scalacOptions = super.scalacOptions() ++ Seq( + def scalacOptions = Seq( "-unchecked", "-feature", "-language:existentials", @@ -263,8 +257,9 @@ object `package` extends Module { "utf8" ) - def scalacPluginMvnDeps = super.scalacPluginMvnDeps() ++ - Seq(mvn"org.wartremover::wartremover:2.2.1") + def scalacPluginMvnDeps = Seq(mvn"org.wartremover::wartremover:2.2.1") + + def artifactName = "nested" def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", @@ -295,13 +290,11 @@ object `package` extends Module { def publishVersion = "0.1.0-SNAPSHOT" - def repositories = super.repositories() ++ Seq( + def repositories = Seq( "https://oss.sonatype.org/service/local/repositories/releases/content/", "https://oss.sonatype.org/content/repositories/snapshots" ) } - } - } diff --git a/libs/init/sbt/test/src/mill/main/sbt/SbtBuildGenTests.scala b/libs/init/sbt/test/src/mill/main/sbt/SbtBuildGenTests.scala index c7fa06b2487f..c7abc3d362c6 100644 --- a/libs/init/sbt/test/src/mill/main/sbt/SbtBuildGenTests.scala +++ b/libs/init/sbt/test/src/mill/main/sbt/SbtBuildGenTests.scala @@ -8,7 +8,6 @@ object SbtBuildGenTests extends TestSuite { val checker = BuildGenChecker() test("scala-seed-project") { assert(checker.check( - generate = SbtBuildGenMain.main(Array.empty[String]), sourceRel = os.sub / "scala-seed-project", expectedRel = os.sub / "expected/scala-seed-project" )) @@ -16,45 +15,42 @@ object SbtBuildGenTests extends TestSuite { // from https://github.com/pbassiner/sbt-multi-project-example/tree/master test("sbt-multi-project-example") { assert(checker.check( - generate = SbtBuildGenMain.main(Array.empty[String]), sourceRel = os.sub / "sbt-multi-project-example", expectedRel = "expected/sbt-multi-project-example" )) } test("cross-version") { assert(checker.check( - generate = SbtBuildGenMain.main(Array.empty[String]), sourceRel = os.sub / "cross-version", expectedRel = os.sub / "expected/cross-version" )) } test("crossproject") { assert(checker.check( - generate = SbtBuildGenMain.main(Array.empty[String]), sourceRel = os.sub / "crossproject", expectedRel = os.sub / "expected/crossproject" )) } test("crossproject-cross-version") { assert(checker.check( - generate = SbtBuildGenMain.main(Array.empty[String]), sourceRel = os.sub / "crossproject-cross-version", expectedRel = os.sub / "expected/crossproject-cross-version" )) } test("with-args") { + val args = Seq("--merge", "--no-meta") test("sbt-multi-project-example") { assert(checker.check( - generate = SbtBuildGenMain.main(Array("--merge", "--no-meta")), sourceRel = os.sub / "sbt-multi-project-example", - expectedRel = os.sub / "expected/with-args/sbt-multi-project-example" + expectedRel = os.sub / "expected/with-args/sbt-multi-project-example", + mainArgs = args )) } test("crossproject-cross-version") { assert(checker.check( - generate = SbtBuildGenMain.main(Array("--merge", "--no-meta")), sourceRel = os.sub / "crossproject-cross-version", - expectedRel = os.sub / "expected/with-args/crossproject-cross-version" + expectedRel = os.sub / "expected/with-args/crossproject-cross-version", + mainArgs = args )) } }