From ac22329c176a7847d6bcd313ada4e6e15f845ba4 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 5 Feb 2025 01:14:07 +0800 Subject: [PATCH 01/70] Initially copy and adapt some code to support `mill init` from an existing SBT project from its corresponding Gradle support `SbtBuildGenMain.scala` and `BuildGenTests.scala` are not fully adapted yet, and the unadapted code is temporarily commented out. An initial SBT plugin `ProjectTreePlugin` is added but not fully implemented yet. Only a task that writes some content to a file is added to test whether this approach works. Exception in `./mill main.init.sbt.compile`: ```text [1486] java.io.IOException: Scala signature package has wrong version [1486] expected: 5.0 [1486] found: 5.2 in package.class ``` Corresponding code for Maven and Gradle is slightly improved BTW. --- build.mill | 6 +- .../mill/main/gradle/GradleBuildGenMain.scala | 2 +- .../mill/main/maven/MavenBuildGenMain.scala | 2 +- main/init/package.mill | 12 +- .../src/mill/main/sbt/ProjectTreePlugin.scala | 22 + .../src/mill/main/sbt/SbtBuildGenMain.scala | 389 ++++++++++++++++++ .../src/mill/main/sbt/BuildGenTests.scala | 50 +++ main/init/src/mill/init/InitSbtModule.scala | 14 + main/src/mill/main/MainModule.scala | 6 + 9 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala create mode 100644 main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala create mode 100644 main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala create mode 100644 main/init/src/mill/init/InitSbtModule.scala diff --git a/build.mill b/build.mill index edebba07bed..0c1386da6db 100644 --- a/build.mill +++ b/build.mill @@ -53,8 +53,11 @@ object Deps { // and then add to it `bridgeScalaVersions` val scalaVersion = "2.13.15" val scala2Version = "2.13.15" + // The Scala 2.12.x version + val scalaVersion212 = "2.12.20" // The Scala 2.12.x version to use for some workers - val workerScalaVersion212 = "2.12.20" + val workerScalaVersion212 = scalaVersion212 + val sbtScalaVersion212 = scalaVersion212 val testScala213Version = "2.13.15" // Scala Native 4.2 will not get releases for new Scala version @@ -203,6 +206,7 @@ object Deps { ivy"org.apache.maven.resolver:maven-resolver-transport-wagon:$mavenResolverVersion" val coursierJvmIndexVersion = "0.0.4-84-f852c6" val gradleApi = ivy"dev.gradleplugins:gradle-api:8.11.1" + val sbt = ivy"org.scala-sbt:sbt:1.10.7" object RuntimeDeps { val dokkaVersion = "2.0.0" diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index 1906e5dc093..dfdd81cb72b 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -40,7 +40,7 @@ import scala.jdk.CollectionConverters.* */ @mill.api.internal object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { - type C = GradleBuildGenMain.Config + type C = Config def main(args: Array[String]): Unit = { val cfg = ParserForClass[Config].constructOrExit(args.toSeq) diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index 9a77e9c8680..a8d9c0719a9 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -37,7 +37,7 @@ import scala.jdk.CollectionConverters.* */ @mill.api.internal object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { - type C = MavenBuildGenMain.Config + type C = Config def main(args: Array[String]): Unit = { val cfg = ParserForClass[Config].constructOrExit(args.toSeq) diff --git a/main/init/package.mill b/main/init/package.mill index e99417eaee4..1fba01b92a7 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -1,7 +1,6 @@ package build.main.init import mill._ -import scala.util.matching.Regex object `package` extends RootModule with build.MillPublishScalaModule { @@ -86,4 +85,15 @@ object `package` extends RootModule with build.MillPublishScalaModule { ) def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) } + + object sbt extends build.MillPublishScalaModule { + // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. + def scalaVersion = build.Deps.sbtScalaVersion212 + + def moduleDeps = Seq(buildgen) + + def ivyDeps = Agg(build.Deps.sbt, build.Deps.upickle) + + def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) + } } diff --git a/main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala new file mode 100644 index 00000000000..7e8558d54fc --- /dev/null +++ b/main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala @@ -0,0 +1,22 @@ +package mill.main.sbt + +import sbt.Keys.target +import sbt.{AutoPlugin, IO, Setting, taskKey} +import sbt.io.syntax.* + +object ProjectTreePlugin extends AutoPlugin { + // override def requires = ??? + object autoImport { + val millInitGenerateProjectTree = taskKey[File]("generate the project tree for `mill init`") + } + import autoImport.* + + override lazy val globalSettings: Seq[Setting[_]] = Seq( + millInitGenerateProjectTree := { + // TODO + val outputFile = target.value / "mill-init-project-tree.json" + IO.write(outputFile, "test") + outputFile + } + ) +} diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala new file mode 100644 index 00000000000..b87ef0a4fb2 --- /dev/null +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -0,0 +1,389 @@ +package mill.main.sbt + +import mainargs.{ParserForClass, main} +import mill.main.buildgen.* + +/** + * Converts an SBT build to Mill by generating Mill build file(s). + * The implementation uses the SBT + * [[https://www.scala-sbt.org/1.x/docs/Combined+Pages.html#addPluginSbtFile+command addPluginSbtFile command]] + * to add a plugin and a task to extract the settings for a project using a custom model. + * + * The generated output should be considered scaffolding and will likely require edits to complete conversion. + * + * ===Capabilities=== + * The conversion + * - handles deeply nested modules + * - configures dependencies for configurations: + * - Compile + * - Runtime + * - Test + * - configures testing frameworks: + * - JUnit 4 + * - JUnit 5 + * - TestNG + * - ScalaTest + * - Specs2 + * - ScalaCheck + * ===Limitations=== + * The conversion does not support + * - custom configurations + * - custom tasks + * - sources other than Scala on JVM and Java + */ +@mill.api.internal +object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { + type C = Config + + def main(args: Array[String]): Unit = { + val cfg = ParserForClass[Config].constructOrExit(args.toSeq) + run(cfg) + } + + private def run(cfg: Config): Unit = { + val workspace = os.pwd + + println("converting SBT build") + + + import scala.sys.process.* + + // resolve the sbt executable + // https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt + val sbtExecutable = if (os.exists(workspace / "sbt")) + "./sbt" + else if (os.exists(workspace / "sbtx")) + "./sbtx" + else if ("sbt --help".! == 0) + "sbt" + else + throw new RuntimeException( + "No sbt executable (`./sbt`, `./sbtx`, or system-wide `sbt`) found" + ) + + sbtExecutable.! + + /* + val connector = GradleConnector.newConnector() + + val args = + cfg.shared.jvmId.map { id => + println(s"resolving Java home for jvmId $id") + val home = Jvm.resolveJavaHome(id).getOrThrow + s"-Dorg.gradle.java.home=$home" + } ++ Seq("--init-script", writeGradleInitScript.toString()) + + try { + println("connecting to Gradle daemon") + val connection = connector.forProjectDirectory(workspace.toIO).connect() + try { + val root = connection.model(classOf[ProjectTree]) + .withArguments(args.asJava) + .get + + val input = Tree.from(root) { tree => + val project = tree.project() + val dirs = os.Path(project.directory()).subRelativeTo(workspace).segments + val children = tree.children().asScala.sortBy(_.project().name()).iterator + (Node(dirs, project), children) + } + + convertWriteOut(cfg, cfg.shared, input) + + println("converted Gradle build to Mill") + } finally connection.close() + } finally connector.disconnect() + */ + } + + /* + private def writeGradleInitScript: os.Path = { + val file = os.temp.dir() / "init.gradle" + val classpath = + os.Path(classOf[ProjectTreePlugin].getProtectionDomain.getCodeSource.getLocation.toURI) + val plugin = classOf[ProjectTreePlugin].getName + val contents = + s"""initscript { + | dependencies { + | classpath files(${escape(classpath.toString()) /* escape for Windows */}) + | } + |} + | + |allprojects { + | apply plugin: $plugin + |} + |""".stripMargin + os.write(file, contents) + file + } + */ + + override def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[IrBuild]): Seq[String] = + ??? + + override def getBaseInfo( + input: Tree[Node[IrBuild]], + cfg: Config, + baseModule: String, + packagesSize: Int + ): IrBaseInfo = ??? + + override def getPackage(model: IrBuild): (String, String, String) = ??? + + override def getArtifactId(model: IrBuild): String = ??? + + override def extractIrBuild( + cfg: Config, + baseInfo: IrBaseInfo, + build: Node[IrBuild], + packages: Map[(String, String, String), String] + ): IrBuild = ??? + + /* + def getBaseInfo( + input: Tree[Node[ProjectModel]], + cfg: Config, + baseModule: String, + packagesSize: Int + ): IrBaseInfo = { + val project = { + val projects = input.nodes(Tree.Traversal.BreadthFirst).map(_.value).toSeq + cfg.baseProject + .flatMap(name => projects.collectFirst { case m if name == m.name => m }) + .orElse(projects.collectFirst { case m if null != m.maven().pom() => m }) + .orElse(projects.collectFirst { case m if !m.maven().repositories().isEmpty => m }) + .getOrElse(input.node.value) + } + if (packagesSize > 1) { + println(s"settings from ${project.name()} will be shared in base module") + } + val supertypes = + Seq("MavenModule") ++ + Option.when(null != project.maven().pom()) { "PublishModule" } + + val javacOptions = getJavacOptions(project) + val repos = getRepositories(project) + val pomSettings = extractPomSettings(project) + val publishVersion = getPublishVersion(project) + val publishProperties = getPublishProperties(project, cfg.shared) + + val typedef = IrTrait( + cfg.shared.jvmId, + baseModule, + supertypes, + javacOptions, + pomSettings, + publishVersion, + publishProperties, + repos + ) + + IrBaseInfo(javacOptions, repos, pomSettings == null, publishVersion, Seq.empty, typedef) + } + + override def extractIrBuild( + cfg: Config, + baseInfo: IrBaseInfo, + build: Node[ProjectModel], + packages: Map[(String, String, String), String] + ): IrBuild = { + val project = build.value + val scopedDeps = extractScopedDeps(project, packages, cfg) + val version = getPublishVersion(project) + IrBuild( + scopedDeps = scopedDeps, + testModule = cfg.shared.testModule, + hasTest = os.exists(getMillSourcePath(project) / "src/test"), + dirs = build.dirs, + repositories = getRepositories(project).diff(baseInfo.repositories), + javacOptions = getJavacOptions(project).diff(baseInfo.javacOptions), + projectName = getArtifactId(project), + pomSettings = if (baseInfo.noPom) extractPomSettings(project) else null, + publishVersion = if (version == baseInfo.publishVersion) null else version, + packaging = getPomPackaging(project), + // not available + pomParentArtifact = null, + // skipped, requires relatively new API (JavaPluginExtension.getSourceSets) + resources = Nil, + testResources = Nil, + publishProperties = getPublishProperties(project, cfg.shared) + ) + } + + def getModuleSupertypes(cfg: Config): Seq[String] = + Seq(cfg.shared.baseModule.getOrElse("MavenModule")) + + def getPackage(project: ProjectModel): (String, String, String) = { + (project.group(), project.name(), project.version()) + } + + def getArtifactId(model: ProjectModel): String = model.name() + + def getMillSourcePath(model: ProjectModel): Path = os.Path(model.directory()) + + def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = { + Seq("RootModule") ++ + Option.when(null != build.value.maven().pom() && baseInfo.noPom) { "PublishModule" } ++ + Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { + getModuleSupertypes(cfg) + }.toSeq.flatten + } + + def groupArtifactVersion(dep: JavaModel.Dep): (String, String, String) = + (dep.group(), dep.name(), dep.version()) + + def getJavacOptions(project: ProjectModel): Seq[String] = { + val _java = project._java() + if (null == _java) Seq.empty + else _java.javacOptions().asScala.toSeq + } + + def getRepositories(project: ProjectModel): Seq[String] = + project.maven().repositories().asScala.toSeq.sorted.map(uri => + s"coursier.maven.MavenRepository(${escape(uri.toString)})" + ) + + def getPomPackaging(project: ProjectModel): String = { + val pom = project.maven().pom() + if (null == pom) null else pom.packaging() + } + + def getPublishProperties(project: ProjectModel, cfg: BuildGenUtil.Config): Seq[(String, String)] = + if (cfg.publishProperties.value) { + val pom = project.maven().pom() + if (null == pom) Seq.empty + else pom.properties().iterator().asScala + .map(prop => (prop.key(), prop.value())) + .toSeq + } else Seq.empty + + def getPublishVersion(project: ProjectModel): String = + project.version() match { + case "" | "unspecified" => null + case version => version + } + + def interpIvy(dep: JavaModel.Dep): String = { + BuildGenUtil.renderIvyString(dep.group(), dep.name(), dep.version()) + } + + def extractPomSettings(project: ProjectModel): IrPom = { + val pom = project.maven.pom() + if (null == pom) null + else { + IrPom( + pom.description(), + project.group(), // Mill uses group for POM org + pom.url(), + licenses = pom.licenses().asScala + .map(lic => IrLicense(lic.name(), lic.name(), lic.url())) + .toSeq, + versionControl = Option(pom.scm()).fold(IrVersionControl(null, null, null, null))(scm => + IrVersionControl(scm.url(), scm.connection(), scm.devConnection(), scm.tag()) + ), + developers = pom.devs().asScala + .map(dev => IrDeveloper(dev.id(), dev.name(), dev.url(), dev.org(), dev.orgUrl())) + .toSeq + ) + } + } + + def extractScopedDeps( + project: ProjectModel, + packages: PartialFunction[(String, String, String), String], + cfg: Config + ): IrScopedDeps = { + var sd = IrScopedDeps() + val hasTest = os.exists(os.Path(project.directory()) / "src/test") + val _java = project._java() + if (null != _java) { + val ivyDep: JavaModel.Dep => String = + cfg.shared.depsObject.fold(interpIvy(_)) { objName => dep => + val depName = s"`${dep.group()}:${dep.name()}`" + sd = sd.copy(namedIvyDeps = sd.namedIvyDeps :+ (depName, interpIvy(dep))) + s"$objName.$depName" + } + + def appendIvyDepPackage( + deps: IterableOnce[JavaModel.Dep], + onPackage: String => IrScopedDeps, + onIvy: (String, (String, String, String)) => IrScopedDeps + ): Unit = { + for (dep <- deps.iterator) { + val id = groupArtifactVersion(dep) + if (packages.isDefinedAt(id)) sd = onPackage(packages(id)) + else { + val ivy = ivyDep(dep) + sd = onIvy(ivy, id) + } + } + } + _java.configs().forEach { config => + import JavaPlugin.* + + val conf = config.name() + conf match { + case IMPLEMENTATION_CONFIGURATION_NAME | API_CONFIGURATION_NAME => + appendIvyDepPackage( + config.deps.asScala, + onPackage = v => sd.copy(mainModuleDeps = sd.mainModuleDeps + v), + onIvy = (v, id) => + if (isBom(id)) sd.copy(mainBomIvyDeps = sd.mainBomIvyDeps + v) + else sd.copy(mainIvyDeps = sd.mainIvyDeps + v) + ) + + case COMPILE_ONLY_CONFIGURATION_NAME | COMPILE_ONLY_API_CONFIGURATION_NAME => + + appendIvyDepPackage( + config.deps.asScala, + onPackage = v => sd.copy(mainCompileModuleDeps = sd.mainCompileModuleDeps + v), + onIvy = (v, id) => sd.copy(mainCompileIvyDeps = sd.mainCompileIvyDeps + v) + ) + + case RUNTIME_ONLY_CONFIGURATION_NAME => + appendIvyDepPackage( + config.deps.asScala, + onPackage = v => sd.copy(mainRunModuleDeps = sd.mainRunModuleDeps + v), + onIvy = (v, id) => sd.copy(mainRunIvyDeps = sd.mainRunIvyDeps + v) + ) + + case TEST_IMPLEMENTATION_CONFIGURATION_NAME => + + appendIvyDepPackage( + config.deps.asScala, + onPackage = v => sd.copy(testModuleDeps = sd.testModuleDeps + v), + onIvy = (v, id) => + if (isBom(id)) sd.copy(testBomIvyDeps = sd.testBomIvyDeps + v) + else sd.copy(testIvyDeps = sd.testIvyDeps + v) + ) + config.deps.forEach { dep => + if (hasTest && sd.testModule.isEmpty) { + sd = sd.copy(testModule = testModulesByGroup.get(dep.group())) + } + } + + case TEST_COMPILE_ONLY_CONFIGURATION_NAME => + appendIvyDepPackage( + config.deps.asScala, + onPackage = v => sd.copy(testCompileModuleDeps = sd.testCompileModuleDeps + v), + onIvy = (v, id) => sd.copy(testCompileIvyDeps = sd.testCompileIvyDeps + v) + ) + + case name => + config.deps.forEach { dep => + val id = groupArtifactVersion(dep) + println(s"ignoring $name dependency $id") + } + } + } + } + sd + } + */ + + @main + @mill.api.internal + case class Config( + shared: BuildGenUtil.Config + ) +} diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala new file mode 100644 index 00000000000..45762ac9e1b --- /dev/null +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -0,0 +1,50 @@ +package mill.main.sbt + +import mill.main.buildgen.BuildGenChecker +import utest.* + +object BuildGenTests extends TestSuite { + + def tests: Tests = Tests { + val checker = BuildGenChecker() + + /* + test("library") { + val sourceRoot = os.sub / "library" + val expectedRoot = os.sub / "expected/library" + assert( + checker.check(GradleBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) + ) + } + + test("application-library") { + val sourceRoot = os.sub / "application-library" + val expectedRoot = os.sub / "expected/application-library" + assert( + checker.check(GradleBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) + ) + } + + test("config") { + val sourceRoot = os.sub / "application-library" + val expectedRoot = os.sub / "expected/config" + val args = Array( + "--base-module", + "BaseModule", + "--base-project", + "utilities", + "--jvm-id", + "11", + "--test-module", + "tests", + "--deps-object", + "Deps", + "--merge" + ) + assert( + checker.check(GradleBuildGenMain.main(args), sourceRoot, expectedRoot) + ) + } + */ + } +} diff --git a/main/init/src/mill/init/InitSbtModule.scala b/main/init/src/mill/init/InitSbtModule.scala new file mode 100644 index 00000000000..4cf6dbd064a --- /dev/null +++ b/main/init/src/mill/init/InitSbtModule.scala @@ -0,0 +1,14 @@ +package mill.init + +import mill.T +import mill.api.{Loose, PathRef} +import mill.define.{Discover, ExternalModule} + +@mill.api.experimental +object InitSbtModule extends ExternalModule with BuildGenModule { + lazy val millDiscover: Discover = Discover[this.type] + + def buildGenClasspath: T[Loose.Agg[PathRef]] = BuildGenModule.millModule("mill-main-init-sbt") + + def buildGenMainClass: T[String] = "mill.main.sbt.SbtBuildGenMain" +} diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index 61fbc18099c..6f1354fdba1 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -585,6 +585,12 @@ trait MainModule extends BaseModule0 { Seq("mill.init.InitGradleModule/init") ++ args, SelectMode.Separated ) + else if (os.exists(os.pwd / "build.sbt")) + RunScript.evaluateTasksNamed( + evaluator, + Seq("mill.init.InitSbtModule/init") ++ args, + SelectMode.Separated + ) else if (args.headOption.exists(_.toLowerCase.endsWith(".g8"))) RunScript.evaluateTasksNamed( evaluator, From d2ad6889a2a92d53db547bd5e2e3b19cb6dea5f9 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 5 Feb 2025 12:15:15 +0800 Subject: [PATCH 02/70] Add a cross module for the models and split the code because Mill uses Scala 2.13 while SBT uses Scala 2.12 `./mill main.init.sbt.test` passes now. --- build.mill | 11 +++++----- main/init/package.mill | 21 ++++++++++++++----- .../sbt/models/src/mill/main/sbt/Models.scala | 3 +++ .../src/mill/main/sbt/ProjectTreePlugin.scala | 1 + .../src/mill/main/sbt/SbtBuildGenMain.scala | 2 ++ 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 main/init/sbt/models/src/mill/main/sbt/Models.scala rename main/init/sbt/{ => sbt-mill-init-generate-project-tree}/src/mill/main/sbt/ProjectTreePlugin.scala (96%) diff --git a/build.mill b/build.mill index 0c1386da6db..8bfb7a7a934 100644 --- a/build.mill +++ b/build.mill @@ -181,7 +181,8 @@ object Deps { val sourcecode = ivy"com.lihaoyi::sourcecode:0.4.2" val upickle = ivy"com.lihaoyi::upickle:3.3.1" val windowsAnsi = ivy"io.github.alexarchambault.windows-ansi:windows-ansi:0.0.6" - val zinc = ivy"org.scala-sbt::zinc:1.10.7" + val sbtVersion = "1.10.7" + val zinc = ivy"org.scala-sbt::zinc:$sbtVersion" // keep in sync with doc/antora/antory.yml val bsp4j = ivy"ch.epfl.scala:bsp4j:2.2.0-M2" val fansi = ivy"com.lihaoyi::fansi:0.5.0" @@ -206,7 +207,7 @@ object Deps { ivy"org.apache.maven.resolver:maven-resolver-transport-wagon:$mavenResolverVersion" val coursierJvmIndexVersion = "0.0.4-84-f852c6" val gradleApi = ivy"dev.gradleplugins:gradle-api:8.11.1" - val sbt = ivy"org.scala-sbt:sbt:1.10.7" + val sbt = ivy"org.scala-sbt:sbt:$sbtVersion" object RuntimeDeps { val dokkaVersion = "2.0.0" @@ -641,8 +642,8 @@ trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { def pomSettings = commonPomSettings(artifactName()) def crossFullScalaVersion = true def ivyDeps = Agg( - ivy"org.scala-sbt:compiler-interface:${Deps.zinc.version}", - ivy"org.scala-sbt:util-interface:${Deps.zinc.version}", + ivy"org.scala-sbt:compiler-interface:${Deps.sbtVersion}", + ivy"org.scala-sbt:util-interface:${Deps.sbtVersion}", ivy"org.scala-lang:scala-compiler:${crossScalaVersion}" ) @@ -652,7 +653,7 @@ trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { } def compilerBridgeIvyDeps: T[Agg[Dep]] = Agg( - ivy"org.scala-sbt::compiler-bridge:${Deps.zinc.version}".exclude("*" -> "*") + ivy"org.scala-sbt::compiler-bridge:${Deps.sbtVersion}".exclude("*" -> "*") ) def compilerBridgeSourceJars: T[Agg[PathRef]] = Task { diff --git a/main/init/package.mill b/main/init/package.mill index 1fba01b92a7..a75863484fd 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -1,6 +1,7 @@ package build.main.init import mill._ +import mill.scalalib.CrossScalaModule object `package` extends RootModule with build.MillPublishScalaModule { @@ -87,13 +88,23 @@ object `package` extends RootModule with build.MillPublishScalaModule { } object sbt extends build.MillPublishScalaModule { - // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. - def scalaVersion = build.Deps.sbtScalaVersion212 + def moduleDeps = Seq(buildgen, models(build.Deps.scalaVersion)) - def moduleDeps = Seq(buildgen) + def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) - def ivyDeps = Agg(build.Deps.sbt, build.Deps.upickle) + // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. + object models + extends Cross[ModelsModule](build.Deps.sbtScalaVersion212, build.Deps.scalaVersion) + trait ModelsModule extends build.MillPublishJavaModule with CrossScalaModule { + def ivyDeps = Agg(build.Deps.upickle) + } - def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) + // no need to publish this + object `sbt-mill-init-generate-project-tree` extends build.MillScalaModule { + private val scalaVersionString = build.Deps.sbtScalaVersion212 + def scalaVersion = scalaVersionString + def moduleDeps = Seq(models(scalaVersionString)) + def ivyDeps = Agg(build.Deps.sbt) // TODO consider `compileIvyDeps` + } } } diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala new file mode 100644 index 00000000000..b50e0ae3473 --- /dev/null +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -0,0 +1,3 @@ +package mill.main.sbt + +case class ProjectTree() diff --git a/main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala similarity index 96% rename from main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala rename to main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala index 7e8558d54fc..ab4272e60e6 100644 --- a/main/init/sbt/src/mill/main/sbt/ProjectTreePlugin.scala +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala @@ -13,6 +13,7 @@ object ProjectTreePlugin extends AutoPlugin { override lazy val globalSettings: Seq[Setting[_]] = Seq( millInitGenerateProjectTree := { + ProjectTree() // TODO val outputFile = target.value / "mill-init-project-tree.json" IO.write(outputFile, "test") diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index b87ef0a4fb2..c7252d3ab7a 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -63,6 +63,8 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { sbtExecutable.! + ProjectTree() + /* val connector = GradleConnector.newConnector() From 39f928e34beef224d551ff9557ec9ee0d3f81816 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 6 Feb 2025 12:47:51 +0800 Subject: [PATCH 03/70] Initially implement assembling and adding the `sbt-mill-init-generate-project-tree` plugin to an SBT build and writing and reading an empty `ProjectTree` model `./mill main.init.sbt.test` and `./mill main.init.sbt.sbt-mill-init-generate-project-tree.compile` run successfully. Other than these 2, the changes are not fully tested yet. --- main/init/package.mill | 13 ++++++-- .../sbt/models/src/mill/main/sbt/Models.scala | 5 +++ .../src/mill/main/sbt/ProjectTreePlugin.scala | 10 +++--- .../src/mill/main/sbt/SbtBuildGenMain.scala | 32 ++++++++----------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/main/init/package.mill b/main/init/package.mill index a75863484fd..ec9c07fe405 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -1,7 +1,7 @@ package build.main.init import mill._ -import mill.scalalib.CrossScalaModule +import mill.scalalib.{CrossScalaModule, ScalaModule} object `package` extends RootModule with build.MillPublishScalaModule { @@ -90,6 +90,14 @@ object `package` extends RootModule with build.MillPublishScalaModule { object sbt extends build.MillPublishScalaModule { def moduleDeps = Seq(buildgen, models(build.Deps.scalaVersion)) + // generateSbtPlugin + def sbtPluginResources = Task { + val assemblyPathRef = `sbt-mill-init-generate-project-tree`.assembly() + os.copy(assemblyPathRef.path, Task.dest / "sbt-mill-init-generate-project-tree.jar") + PathRef(Task.dest) + } + def resources: T[Seq[PathRef]] = + Task.Sources(super.resources() ++ Seq(sbtPluginResources())) def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. @@ -100,7 +108,8 @@ object `package` extends RootModule with build.MillPublishScalaModule { } // no need to publish this - object `sbt-mill-init-generate-project-tree` extends build.MillScalaModule { + // `test` fails with `build.MillScalaModule`, and since it's an SBT plugin project it's most likely not needed. + object `sbt-mill-init-generate-project-tree` extends ScalaModule { private val scalaVersionString = build.Deps.sbtScalaVersion212 def scalaVersion = scalaVersionString def moduleDeps = Seq(models(scalaVersionString)) diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index b50e0ae3473..983aa85b46b 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -1,3 +1,8 @@ package mill.main.sbt +import upickle.default.{ReadWriter => RW, macroRW} + case class ProjectTree() +object ProjectTree { + implicit val rw: RW[ProjectTree] = macroRW +} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala index ab4272e60e6..93862f7dede 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala @@ -1,22 +1,24 @@ package mill.main.sbt import sbt.Keys.target +import sbt.io.syntax._ import sbt.{AutoPlugin, IO, Setting, taskKey} -import sbt.io.syntax.* +import upickle.default._ object ProjectTreePlugin extends AutoPlugin { // override def requires = ??? object autoImport { val millInitGenerateProjectTree = taskKey[File]("generate the project tree for `mill init`") } - import autoImport.* + import autoImport._ override lazy val globalSettings: Seq[Setting[_]] = Seq( millInitGenerateProjectTree := { - ProjectTree() // TODO + val projectTree = ProjectTree() + val outputFile = target.value / "mill-init-project-tree.json" - IO.write(outputFile, "test") + IO.write(outputFile, write(projectTree)) outputFile } ) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index c7252d3ab7a..6f64c0d3451 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -2,6 +2,7 @@ package mill.main.sbt import mainargs.{ParserForClass, main} import mill.main.buildgen.* +import mill.main.buildgen.BuildGenUtil.escape /** * Converts an SBT build to Mill by generating Mill build file(s). @@ -45,7 +46,6 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { println("converting SBT build") - import scala.sys.process.* // resolve the sbt executable @@ -61,9 +61,11 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { "No sbt executable (`./sbt`, `./sbtx`, or system-wide `sbt`) found" ) - sbtExecutable.! + s"$sbtExecutable -addPluginSbtFile=${writeSbtFile().toString()} millInitGenerateProjectTree".! - ProjectTree() + import upickle.default.* + val projectTree = read[ProjectTree](os.read(workspace / "target" / "mill-init-project-tree.json")) + println("Project tree retrieved: " + projectTree) /* val connector = GradleConnector.newConnector() @@ -98,27 +100,19 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { */ } - /* - private def writeGradleInitScript: os.Path = { - val file = os.temp.dir() / "init.gradle" - val classpath = - os.Path(classOf[ProjectTreePlugin].getProtectionDomain.getCodeSource.getLocation.toURI) - val plugin = classOf[ProjectTreePlugin].getName + private def writeSbtFile(): os.Path = { + val file = os.temp.dir() / "mill-init.sbt" + // TODO copy to a temp file if it doesn't work when packaged in a jar + val sbtPluginJarUrl = + getClass.getResource("/sbt-mill-init-generate-project-tree.jar").toExternalForm val contents = - s"""initscript { - | dependencies { - | classpath files(${escape(classpath.toString()) /* escape for Windows */}) - | } - |} - | - |allprojects { - | apply plugin: $plugin - |} + s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "" from ${escape( + sbtPluginJarUrl + )}) |""".stripMargin os.write(file, contents) file } - */ override def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[IrBuild]): Seq[String] = ??? From 3c9f155d1fd5e4146e22284d7104e02da83bbbcf Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 05:54:11 +0800 Subject: [PATCH 04/70] Add code to add the sbt plugin and run the `millInitGenerateProjectTree` sbt task in `SbtBuildGenMain`, create a "scala-seed-project" test project in test resources with `sbt new`, and update `ProjectTreePlugin` `./mill main.init.sbt.test` fails. In the sbt launched under the directory "main/init/sbt/test/resources/scala-seed-project" with `sbt -addPluginSbtFile=/.../mill-init.sbt` with the following content, the `tasks` sbt command output doesn't include an added `millInitGenerateProjectTree` task: ```sbt addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "0.1.0-SNAPSHOT" from "file:/.../mill/out/main/init/sbt/sbtPluginResources.dest/sbt-mill-init-generate-project-tree.jar") ``` The following content of "mill-init.sbt" yields the following result: ```sbt import mill.main.sbt.ProjectTreePlugin addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "0.1.0-SNAPSHOT" from "file:/.../mill/out/main/init/sbt/sbtPluginResources.dest/sbt-mill-init-generate-project-tree.jar") enablePlugins(ProjectTreePlugin) lazy val root = (project in file(".")) .enablePlugins(ProjectTreePlugin) ``` ```text /Users/shreckye/Projects/mill/mill-init.sbt:1: error: not found: object mill import mill.main.sbt.ProjectTreePlugin ^ /Users/shreckye/Projects/mill/mill-init.sbt:8: error: not found: value ProjectTreePlugin .enablePlugins(ProjectTreePlugin) ^ sbt.compiler.EvalException: Type error in expression ``` Reviewing the contents of https://repo1.maven.org/maven2/com/eed3si9n/sbt-assembly_2.12_1.0/2.3.1/sbt-assembly_2.12_1.0-2.3.1.jar, I see there is an "sbt/sbt.autoplugins" file which has the content: ``` sbtassembly.AssemblyPlugin ``` This probably means that such a file has to be included for an sbt plugin and a plain Mill module doesn't support this. --- .../src/mill/main/sbt/ProjectTreePlugin.scala | 5 ++-- .../src/mill/main/sbt/SbtBuildGenMain.scala | 23 +++++++++++++++---- .../resources/scala-seed-project/.gitignore | 2 ++ .../resources/scala-seed-project/build.sbt | 14 +++++++++++ .../project/Dependencies.scala | 5 ++++ .../project/build.properties | 1 + .../src/main/scala/example/Hello.scala | 9 ++++++++ .../src/test/scala/example/HelloSpec.scala | 7 ++++++ .../src/mill/main/sbt/BuildGenTests.scala | 10 ++++---- 9 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 main/init/sbt/test/resources/scala-seed-project/.gitignore create mode 100644 main/init/sbt/test/resources/scala-seed-project/build.sbt create mode 100644 main/init/sbt/test/resources/scala-seed-project/project/Dependencies.scala create mode 100644 main/init/sbt/test/resources/scala-seed-project/project/build.properties create mode 100644 main/init/sbt/test/resources/scala-seed-project/src/main/scala/example/Hello.scala create mode 100644 main/init/sbt/test/resources/scala-seed-project/src/test/scala/example/HelloSpec.scala diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala index 93862f7dede..ddc2a1aceae 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala @@ -6,13 +6,14 @@ import sbt.{AutoPlugin, IO, Setting, taskKey} import upickle.default._ object ProjectTreePlugin extends AutoPlugin { - // override def requires = ??? + // override def requires = ??? // defaults to `JvmPlugin` + override def trigger = allRequirements object autoImport { val millInitGenerateProjectTree = taskKey[File]("generate the project tree for `mill init`") } import autoImport._ - override lazy val globalSettings: Seq[Setting[_]] = Seq( + override lazy val buildSettings: Seq[Setting[_]] = Seq( millInitGenerateProjectTree := { // TODO val projectTree = ProjectTree() diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 6f64c0d3451..ae5a43e73b8 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -54,17 +54,32 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { "./sbt" else if (os.exists(workspace / "sbtx")) "./sbtx" - else if ("sbt --help".! == 0) + else if ( + // The return code is somehow 1 instead of 0. + Seq("sbt", "--help").!(ProcessLogger(_ => ())) == 1 + ) "sbt" else throw new RuntimeException( "No sbt executable (`./sbt`, `./sbtx`, or system-wide `sbt`) found" ) - s"$sbtExecutable -addPluginSbtFile=${writeSbtFile().toString()} millInitGenerateProjectTree".! + println("Running sbt task to generate the project tree") + + val exitCode = Process( + Seq( + sbtExecutable, + s"-addPluginSbtFile=${writeSbtFile().toString}", + "millInitGenerateProjectTree" + ), + workspace.toIO + ).! + + println("Exit code from running the `millInitGenerateProjectTree` sbt task: " + exitCode) import upickle.default.* - val projectTree = read[ProjectTree](os.read(workspace / "target" / "mill-init-project-tree.json")) + val projectTree = + read[ProjectTree](os.read(workspace / "target" / "mill-init-project-tree.json")) println("Project tree retrieved: " + projectTree) /* @@ -106,7 +121,7 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { val sbtPluginJarUrl = getClass.getResource("/sbt-mill-init-generate-project-tree.jar").toExternalForm val contents = - s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "" from ${escape( + s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "0.1.0-SNAPSHOT" from ${escape( sbtPluginJarUrl )}) |""".stripMargin diff --git a/main/init/sbt/test/resources/scala-seed-project/.gitignore b/main/init/sbt/test/resources/scala-seed-project/.gitignore new file mode 100644 index 00000000000..bddd1888af6 --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/.gitignore @@ -0,0 +1,2 @@ +/.bsp/ +target/ diff --git a/main/init/sbt/test/resources/scala-seed-project/build.sbt b/main/init/sbt/test/resources/scala-seed-project/build.sbt new file mode 100644 index 00000000000..1dd2cd9d59c --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/build.sbt @@ -0,0 +1,14 @@ +import Dependencies._ + +ThisBuild / scalaVersion := "2.13.12" +ThisBuild / version := "0.1.0-SNAPSHOT" +ThisBuild / organization := "com.example" +ThisBuild / organizationName := "example" + +lazy val root = (project in file(".")) + .settings( + name := "Scala Seed Project", + libraryDependencies += munit % Test + ) + +// See https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for instructions on how to publish to Sonatype. diff --git a/main/init/sbt/test/resources/scala-seed-project/project/Dependencies.scala b/main/init/sbt/test/resources/scala-seed-project/project/Dependencies.scala new file mode 100644 index 00000000000..1edb07a723b --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/project/Dependencies.scala @@ -0,0 +1,5 @@ +import sbt._ + +object Dependencies { + lazy val munit = "org.scalameta" %% "munit" % "0.7.29" +} diff --git a/main/init/sbt/test/resources/scala-seed-project/project/build.properties b/main/init/sbt/test/resources/scala-seed-project/project/build.properties new file mode 100644 index 00000000000..73df629ac1a --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.10.7 diff --git a/main/init/sbt/test/resources/scala-seed-project/src/main/scala/example/Hello.scala b/main/init/sbt/test/resources/scala-seed-project/src/main/scala/example/Hello.scala new file mode 100644 index 00000000000..80ea40a9b31 --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/src/main/scala/example/Hello.scala @@ -0,0 +1,9 @@ +package example + +object Hello extends Greeting with App { + println(greeting) +} + +trait Greeting { + lazy val greeting: String = "hello" +} diff --git a/main/init/sbt/test/resources/scala-seed-project/src/test/scala/example/HelloSpec.scala b/main/init/sbt/test/resources/scala-seed-project/src/test/scala/example/HelloSpec.scala new file mode 100644 index 00000000000..d57b5ed4534 --- /dev/null +++ b/main/init/sbt/test/resources/scala-seed-project/src/test/scala/example/HelloSpec.scala @@ -0,0 +1,7 @@ +package example + +class HelloSpec extends munit.FunSuite { + test("say hello") { + assertEquals(Hello.greeting, "hello") + } +} diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index 45762ac9e1b..ee5f9bcc7d0 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -8,15 +8,15 @@ object BuildGenTests extends TestSuite { def tests: Tests = Tests { val checker = BuildGenChecker() - /* - test("library") { - val sourceRoot = os.sub / "library" - val expectedRoot = os.sub / "expected/library" + test("scala-seed-project") { + val sourceRoot = os.sub / "scala-seed-project" + val expectedRoot = os.sub / "expected/scala-seed-project" assert( - checker.check(GradleBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) + checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) ) } + /* test("application-library") { val sourceRoot = os.sub / "application-library" val expectedRoot = os.sub / "expected/application-library" From 923ddf4523f2ad515a71ef9fcadd4cd02e9f973f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 05:57:55 +0800 Subject: [PATCH 05/70] Remove the files generated by sbt in "main/init/sbt/test/resources/scala-seed-project" and run `./mill main.init.sbt.test` The exception: ```text java.nio.file.NoSuchFileException: /Users/shreckye/Projects/mill/out/main/init/sbt/test/test.dest/sandbox/scala-seed-project/target/mill-init-project-tree.json ``` From 268eb6325daea7b91f5ed245b7982c1743e442a5 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 06:22:10 +0800 Subject: [PATCH 06/70] Replace "SBT"s with lowercase "sbt" --- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index ae5a43e73b8..8f67a259004 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -5,8 +5,8 @@ import mill.main.buildgen.* import mill.main.buildgen.BuildGenUtil.escape /** - * Converts an SBT build to Mill by generating Mill build file(s). - * The implementation uses the SBT + * Converts an sbt build to Mill by generating Mill build file(s). + * The implementation uses the sbt * [[https://www.scala-sbt.org/1.x/docs/Combined+Pages.html#addPluginSbtFile+command addPluginSbtFile command]] * to add a plugin and a task to extract the settings for a project using a custom model. * @@ -44,7 +44,7 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { private def run(cfg: Config): Unit = { val workspace = os.pwd - println("converting SBT build") + println("converting sbt build") import scala.sys.process.* From 86e180761e10f47908ca04af4e13dfd5293caa5d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 06:59:27 +0800 Subject: [PATCH 07/70] Move the old "sbt-mill-init-generate-project-tree" Mill module and run `sbt new sbt/sbt-autoplugin.g8` in "main/init/sbt" --- .../src/mill/main/sbt/ProjectTreePlugin.scala | 0 .../.gitignore | 11 ++++ .../README.md | 25 ++++++++ .../build.sbt | 58 +++++++++++++++++++ .../project/build.properties | 1 + .../project/plugins.sbt | 4 ++ .../MillinitgenerateprojecttreePlugin.scala | 27 +++++++++ .../simple/build.sbt | 2 + .../simple/project/plugins.sbt | 7 +++ .../simple/src/main/scala/Main.scala | 19 ++++++ .../simple/test | 1 + .../sbt/MillinitgenerateprojecttreeSpec.scala | 5 ++ 12 files changed, 160 insertions(+) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-generate-project-tree-mill}/src/mill/main/sbt/ProjectTreePlugin.scala (100%) create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/README.md create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test create mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree-mill/src/mill/main/sbt/ProjectTreePlugin.scala similarity index 100% rename from main/init/sbt/sbt-mill-init-generate-project-tree/src/mill/main/sbt/ProjectTreePlugin.scala rename to main/init/sbt/sbt-mill-init-generate-project-tree-mill/src/mill/main/sbt/ProjectTreePlugin.scala diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore b/main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore new file mode 100644 index 00000000000..f931b91f2ed --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore @@ -0,0 +1,11 @@ +*.class +*.log + +target +project/project +project/target +.cache +.classpath +.project +.settings +bin diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/README.md b/main/init/sbt/sbt-mill-init-generate-project-tree/README.md new file mode 100644 index 00000000000..e3d7a84bafc --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/README.md @@ -0,0 +1,25 @@ +# sbt-mill-init-generate-project-tree + +generate the project tree for `mill init` + +## Usage + +This plugin requires sbt 1.0.0+ + +### Testing + +Run `test` for regular unit tests. + +Run `scripted` for [sbt script tests](http://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html). + +### CI + +The generated project uses [sbt-github-actions](https://github.com/djspiewak/sbt-github-actions) as a plugin to generate workflows for GitHub actions. For full details of how to use it [read this](https://github.com/djspiewak/sbt-github-actions/blob/main/README.md) + +### Publishing + +1. publish your source to GitHub +2. Follow the instructions in [sbt-ci-release](https://github.com/olafurpg/sbt-ci-release/blob/main/readme.md) to create a sonatype account and setup your keys +3. `sbt ci-release` +4. [Add your plugin to the community plugins list](https://github.com/sbt/website#attention-plugin-authors) +5. [Claim your project an Scaladex](https://github.com/scalacenter/scaladex-contrib#claim-your-project) diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt new file mode 100644 index 00000000000..daf139cc2ac --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt @@ -0,0 +1,58 @@ + +name := """sbt-mill-init-generate-project-tree""" +organization := "com.lihaoyi" +version := "0.1-SNAPSHOT" + +sbtPlugin := true + +// choose a test framework + +// utest +//libraryDependencies += "com.lihaoyi" %% "utest" % "0.7.10" % "test" +//testFrameworks += new TestFramework("utest.runner.Framework") + +// ScalaTest +//libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.9" % "test" +//libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.9" % "test" + +// Specs2 +//libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "4.12.8" % "test") +//scalacOptions in Test ++= Seq("-Yrangepos") + +inThisBuild(List( + organization := "com.lihaoyi", + homepage := Some(url("https://github.com/com-lihaoyi/mill")), + licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")), + developers := List( + Developer( + "lihaoyi", + "Li Haoyi", + "haoyi.sg@gmail.com", + url("https://scala-lang.org") + ) + ) +)) + +initialCommands in console := """import mill.main.sbt._""" + +enablePlugins(ScriptedPlugin) +// set up 'scripted; sbt plugin for testing sbt plugins +scriptedLaunchOpts ++= + Seq("-Xmx1024M", "-Dplugin.version=" + version.value) + +ThisBuild / githubWorkflowTargetTags ++= Seq("v*") +ThisBuild / githubWorkflowPublishTargetBranches := + Seq(RefPredicate.StartsWith(Ref.Tag("v"))) + +ThisBuild / githubWorkflowPublish := Seq( + WorkflowStep.Sbt( + List("ci-release"), + env = Map( + "PGP_PASSPHRASE" -> "${{ secrets.PGP_PASSPHRASE }}", + "PGP_SECRET" -> "${{ secrets.PGP_SECRET }}", + "SONATYPE_PASSWORD" -> "${{ secrets.SONATYPE_PASSWORD }}", + "SONATYPE_USERNAME" -> "${{ secrets.SONATYPE_USERNAME }}" + ) + ) +) + diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties b/main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties new file mode 100644 index 00000000000..73df629ac1a --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.10.7 diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt new file mode 100644 index 00000000000..65d78ae94a6 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt @@ -0,0 +1,4 @@ +libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value +addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7") +addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.13.0") + diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala new file mode 100644 index 00000000000..206c66d5f44 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala @@ -0,0 +1,27 @@ +package mill.main.sbt + +import sbt._ +import sbt.Keys._ +import sbt.plugins.JvmPlugin + +object MillinitgenerateprojecttreePlugin extends AutoPlugin { + + override def trigger = allRequirements + override def requires = JvmPlugin + + object autoImport { + val exampleSetting = settingKey[String]("A setting that is automatically imported to the build") + val exampleTask = taskKey[String]("A task that is automatically imported to the build") + } + + import autoImport._ + + override lazy val projectSettings = Seq( + exampleSetting := "just an example", + exampleTask := "computed from example setting: " + exampleSetting.value + ) + + override lazy val buildSettings = Seq() + + override lazy val globalSettings = Seq() +} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt new file mode 100644 index 00000000000..bce0be88ace --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt @@ -0,0 +1,2 @@ +version := "0.1" +scalaVersion := "2.12.1" diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt new file mode 100644 index 00000000000..1bf98d6cba8 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt @@ -0,0 +1,7 @@ +{ + val pluginVersion = System.getProperty("plugin.version") + if(pluginVersion == null) + throw new RuntimeException("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) + else addSbtPlugin("com.lihaoyi" % """sbt-mill-init-generate-project-tree""" % pluginVersion) +} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala new file mode 100644 index 00000000000..363b8d4f10a --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala @@ -0,0 +1,19 @@ +package simple + +/** + * A simple class and objects to write tests against. + */ +class Main { + val default = "the function returned" + def method = default + " " + Main.function +} + +object Main { + + val constant = 1 + def function = 2*constant + + def main(args: Array[String]): Unit = { + println(new Main().default) + } +} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test new file mode 100644 index 00000000000..f458fe34665 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test @@ -0,0 +1 @@ +> show exampleTask diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala new file mode 100644 index 00000000000..c4d6adc1969 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala @@ -0,0 +1,5 @@ +package mill.main.sbt + +class MillinitgenerateprojecttreeTest { + // write tests with your preferred framework +} From 9991d8a766c9b9de6bd2d294d2bae11cea514dec Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 07:22:31 +0800 Subject: [PATCH 08/70] =?UTF-8?q?Remove=20unneeded=20code=20in=20the=20gen?= =?UTF-8?q?erated=20sbt=20plugin=20project=20and=20add=20=C2=B5Test=20to?= =?UTF-8?q?=20the=20testing=20frameworks=20BTW=20in=20the=20Scaladoc=20of?= =?UTF-8?q?=20`SbtBuildGenMain`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../build.sbt | 52 +------------------ .../project/plugins.sbt | 4 -- .../simple/build.sbt | 2 - .../simple/project/plugins.sbt | 7 --- .../simple/src/main/scala/Main.scala | 19 ------- .../simple/test | 1 - .../sbt/MillinitgenerateprojecttreeSpec.scala | 5 -- .../src/mill/main/sbt/SbtBuildGenMain.scala | 1 + 8 files changed, 2 insertions(+), 89 deletions(-) delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt index daf139cc2ac..65d0e5fc85d 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt @@ -5,54 +5,4 @@ version := "0.1-SNAPSHOT" sbtPlugin := true -// choose a test framework - -// utest -//libraryDependencies += "com.lihaoyi" %% "utest" % "0.7.10" % "test" -//testFrameworks += new TestFramework("utest.runner.Framework") - -// ScalaTest -//libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.9" % "test" -//libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.9" % "test" - -// Specs2 -//libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "4.12.8" % "test") -//scalacOptions in Test ++= Seq("-Yrangepos") - -inThisBuild(List( - organization := "com.lihaoyi", - homepage := Some(url("https://github.com/com-lihaoyi/mill")), - licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")), - developers := List( - Developer( - "lihaoyi", - "Li Haoyi", - "haoyi.sg@gmail.com", - url("https://scala-lang.org") - ) - ) -)) - -initialCommands in console := """import mill.main.sbt._""" - -enablePlugins(ScriptedPlugin) -// set up 'scripted; sbt plugin for testing sbt plugins -scriptedLaunchOpts ++= - Seq("-Xmx1024M", "-Dplugin.version=" + version.value) - -ThisBuild / githubWorkflowTargetTags ++= Seq("v*") -ThisBuild / githubWorkflowPublishTargetBranches := - Seq(RefPredicate.StartsWith(Ref.Tag("v"))) - -ThisBuild / githubWorkflowPublish := Seq( - WorkflowStep.Sbt( - List("ci-release"), - env = Map( - "PGP_PASSPHRASE" -> "${{ secrets.PGP_PASSPHRASE }}", - "PGP_SECRET" -> "${{ secrets.PGP_SECRET }}", - "SONATYPE_PASSWORD" -> "${{ secrets.SONATYPE_PASSWORD }}", - "SONATYPE_USERNAME" -> "${{ secrets.SONATYPE_USERNAME }}" - ) - ) -) - +console / initialCommands := """import mill.main.sbt._""" diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt index 65d78ae94a6..e69de29bb2d 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt @@ -1,4 +0,0 @@ -libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value -addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7") -addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.13.0") - diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt deleted file mode 100644 index bce0be88ace..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/build.sbt +++ /dev/null @@ -1,2 +0,0 @@ -version := "0.1" -scalaVersion := "2.12.1" diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt deleted file mode 100644 index 1bf98d6cba8..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/project/plugins.sbt +++ /dev/null @@ -1,7 +0,0 @@ -{ - val pluginVersion = System.getProperty("plugin.version") - if(pluginVersion == null) - throw new RuntimeException("""|The system property 'plugin.version' is not defined. - |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) - else addSbtPlugin("com.lihaoyi" % """sbt-mill-init-generate-project-tree""" % pluginVersion) -} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala deleted file mode 100644 index 363b8d4f10a..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/src/main/scala/Main.scala +++ /dev/null @@ -1,19 +0,0 @@ -package simple - -/** - * A simple class and objects to write tests against. - */ -class Main { - val default = "the function returned" - def method = default + " " + Main.function -} - -object Main { - - val constant = 1 - def function = 2*constant - - def main(args: Array[String]): Unit = { - println(new Main().default) - } -} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test b/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test deleted file mode 100644 index f458fe34665..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/sbt-test/sbt-mill-init-generate-project-tree/simple/test +++ /dev/null @@ -1 +0,0 @@ -> show exampleTask diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala deleted file mode 100644 index c4d6adc1969..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/test/scala/mill/main/sbt/MillinitgenerateprojecttreeSpec.scala +++ /dev/null @@ -1,5 +0,0 @@ -package mill.main.sbt - -class MillinitgenerateprojecttreeTest { - // write tests with your preferred framework -} diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 8f67a259004..04056e9e6c5 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -23,6 +23,7 @@ import mill.main.buildgen.BuildGenUtil.escape * - JUnit 4 * - JUnit 5 * - TestNG + * - µTest * - ScalaTest * - Specs2 * - ScalaCheck From a253537f43c5defaf7d60f3851a2861a2af07a17 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Fri, 7 Feb 2025 11:45:56 +0800 Subject: [PATCH 09/70] Move `ProjectTreePlugin` into the sbt project and set up the build logic that jumps between Mill and sbt The issues in commit 3c9f155d1fd5e4146e22284d7104e02da83bbbcf are resolved and `./mill main.init.sbt.test` now passes. --- main/init/package.mill | 39 ++++++++++++------- .../README.md | 27 +++---------- .../build.sbt | 4 +- .../project/plugins.sbt | 1 + .../MillinitgenerateprojecttreePlugin.scala | 27 ------------- .../mill/main/sbt/ProjectTreePlugin.scala | 13 ++++--- .../src/mill/main/sbt/SbtBuildGenMain.scala | 2 +- 7 files changed, 43 insertions(+), 70 deletions(-) delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala rename main/init/sbt/{sbt-mill-init-generate-project-tree-mill/src => sbt-mill-init-generate-project-tree/src/main/scala}/mill/main/sbt/ProjectTreePlugin.scala (74%) diff --git a/main/init/package.mill b/main/init/package.mill index ec9c07fe405..91d51a866ea 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -1,7 +1,7 @@ package build.main.init import mill._ -import mill.scalalib.{CrossScalaModule, ScalaModule} +import mill.scalalib.CrossScalaModule object `package` extends RootModule with build.MillPublishScalaModule { @@ -90,14 +90,32 @@ object `package` extends RootModule with build.MillPublishScalaModule { object sbt extends build.MillPublishScalaModule { def moduleDeps = Seq(buildgen, models(build.Deps.scalaVersion)) - // generateSbtPlugin - def sbtPluginResources = Task { - val assemblyPathRef = `sbt-mill-init-generate-project-tree`.assembly() - os.copy(assemblyPathRef.path, Task.dest / "sbt-mill-init-generate-project-tree.jar") + // generateSbtPluginJar + def sbtPluginJarResources = Task { + models(build.Deps.sbtScalaVersion212).publishLocal()() + + import scala.sys.process._ + val sbtProjectPath = millModuleBasePath.value / "sbt-mill-init-generate-project-tree" + val version = build.millVersion() + if ( + Process( + // The version is passed to the sbt build so it correctly resolves the "models" dependency version and sets its own version. + s"""sbt 'set version := "$version"' assembly""", + sbtProjectPath.toIO + ).! != 0 + ) + throw new RuntimeException( + "fail to run `sbt assembly` in the \"sbt-mill-init-generate-project-tree\" sbt project." + ) + + os.copy( + sbtProjectPath / "target" / "scala-2.12" / "sbt-1.0" / s"sbt-mill-init-generate-project-tree-assembly-$version.jar", + Task.dest / "sbt-mill-init-generate-project-tree-assembly.jar" + ) PathRef(Task.dest) } def resources: T[Seq[PathRef]] = - Task.Sources(super.resources() ++ Seq(sbtPluginResources())) + Task.Sources(super.resources() ++ Seq(sbtPluginJarResources())) def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib, buildgen.test) // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. @@ -106,14 +124,5 @@ object `package` extends RootModule with build.MillPublishScalaModule { trait ModelsModule extends build.MillPublishJavaModule with CrossScalaModule { def ivyDeps = Agg(build.Deps.upickle) } - - // no need to publish this - // `test` fails with `build.MillScalaModule`, and since it's an SBT plugin project it's most likely not needed. - object `sbt-mill-init-generate-project-tree` extends ScalaModule { - private val scalaVersionString = build.Deps.sbtScalaVersion212 - def scalaVersion = scalaVersionString - def moduleDeps = Seq(models(scalaVersionString)) - def ivyDeps = Agg(build.Deps.sbt) // TODO consider `compileIvyDeps` - } } } diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/README.md b/main/init/sbt/sbt-mill-init-generate-project-tree/README.md index e3d7a84bafc..55e348119f8 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/README.md +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/README.md @@ -1,25 +1,10 @@ # sbt-mill-init-generate-project-tree -generate the project tree for `mill init` +An sbt plugin to generate the project tree for `mill init` -## Usage +## Working on this project -This plugin requires sbt 1.0.0+ - -### Testing - -Run `test` for regular unit tests. - -Run `scripted` for [sbt script tests](http://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html). - -### CI - -The generated project uses [sbt-github-actions](https://github.com/djspiewak/sbt-github-actions) as a plugin to generate workflows for GitHub actions. For full details of how to use it [read this](https://github.com/djspiewak/sbt-github-actions/blob/main/README.md) - -### Publishing - -1. publish your source to GitHub -2. Follow the instructions in [sbt-ci-release](https://github.com/olafurpg/sbt-ci-release/blob/main/readme.md) to create a sonatype account and setup your keys -3. `sbt ci-release` -4. [Add your plugin to the community plugins list](https://github.com/sbt/website#attention-plugin-authors) -5. [Claim your project an Scaladex](https://github.com/scalacenter/scaladex-contrib#claim-your-project) +Make sure you are on the SNAPSHOT version of Mill, +and run `./mill "main.init.sbt.models[2.12.20].publishLocal"`, +to publish the models to Ivy's local repository before working on this project. +Then open this directory as a separate IntelliJ IDEA project as this is an sbt project. diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt index 65d0e5fc85d..9ee673f5ba5 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt @@ -1,8 +1,10 @@ name := """sbt-mill-init-generate-project-tree""" organization := "com.lihaoyi" -version := "0.1-SNAPSHOT" +version := "SNAPSHOT" sbtPlugin := true console / initialCommands := """import mill.main.sbt._""" + +libraryDependencies += "com.lihaoyi" %% "mill-main-init-sbt-models" % version.value diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt index e69de29bb2d..f8ea5d0fbb0 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala deleted file mode 100644 index 206c66d5f44..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/MillinitgenerateprojecttreePlugin.scala +++ /dev/null @@ -1,27 +0,0 @@ -package mill.main.sbt - -import sbt._ -import sbt.Keys._ -import sbt.plugins.JvmPlugin - -object MillinitgenerateprojecttreePlugin extends AutoPlugin { - - override def trigger = allRequirements - override def requires = JvmPlugin - - object autoImport { - val exampleSetting = settingKey[String]("A setting that is automatically imported to the build") - val exampleTask = taskKey[String]("A task that is automatically imported to the build") - } - - import autoImport._ - - override lazy val projectSettings = Seq( - exampleSetting := "just an example", - exampleTask := "computed from example setting: " + exampleSetting.value - ) - - override lazy val buildSettings = Seq() - - override lazy val globalSettings = Seq() -} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree-mill/src/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala similarity index 74% rename from main/init/sbt/sbt-mill-init-generate-project-tree-mill/src/mill/main/sbt/ProjectTreePlugin.scala rename to main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala index ddc2a1aceae..3fe31be4d2e 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree-mill/src/mill/main/sbt/ProjectTreePlugin.scala +++ b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala @@ -1,19 +1,22 @@ package mill.main.sbt import sbt.Keys.target -import sbt.io.syntax._ +import sbt.io.syntax.* import sbt.{AutoPlugin, IO, Setting, taskKey} -import upickle.default._ +import upickle.default.* object ProjectTreePlugin extends AutoPlugin { - // override def requires = ??? // defaults to `JvmPlugin` override def trigger = allRequirements + // override def requires = ??? // defaults to `JvmPlugin` + object autoImport { val millInitGenerateProjectTree = taskKey[File]("generate the project tree for `mill init`") } - import autoImport._ - override lazy val buildSettings: Seq[Setting[_]] = Seq( + import autoImport.* + + // `target.value` doesn't work in `globalSettings` and `buildSettings` + override lazy val projectSettings: Seq[Setting[_]] = Seq( millInitGenerateProjectTree := { // TODO val projectTree = ProjectTree() diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 04056e9e6c5..ff0ea7e3acd 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -120,7 +120,7 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { val file = os.temp.dir() / "mill-init.sbt" // TODO copy to a temp file if it doesn't work when packaged in a jar val sbtPluginJarUrl = - getClass.getResource("/sbt-mill-init-generate-project-tree.jar").toExternalForm + getClass.getResource("/sbt-mill-init-generate-project-tree-assembly.jar").toExternalForm val contents = s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "0.1.0-SNAPSHOT" from ${escape( sbtPluginJarUrl From 01bfcf5fbd26c15ce8b88483506f0dd43e4c0943 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 15 Feb 2025 20:48:49 +0800 Subject: [PATCH 10/70] Initially complete implementing `SbtBuildGenMain` and the sbt plugin project "sbt-mill-init-export-build", and generate build files for "scala-seed-project" in the test resources The generated "scala-seed-project" "build.mill" file is not completely correct yet. Changes: * All kinds of necessary model case classes are added in "Models.scala" in the "models" module. * A `MillPublishCrossScalaModule` is extracted in the Mill build files. * The sbt plugin project "sbt-mill-init-generate-project-tree" is renamed to "sbt-mill-init-export-build" as the plugin doesn't just export a tree of projects. * An `sbtPluginProjectSource` `Source` task is added in the `sbt` module's Mill build configuration to react to changes in the sbt Plugin project. * `BuildGenBase` is updated with a new type parameter `I` for `input`, as the `IrBaseInfo` is not retrieved from a module in sbt but from `ThisBuild`, and thus `BuildExport.defaultBuildInfo` needs to be passed. * A `BaseInfoFromSubproject` subtype trait is added. * A `BasicConfig` is extracted from `Config` for sbt. * `MavenBuildGenMain` and `GradleBuildGenMain` are updated accordingly and slightly improved. * Changes in `BuildGenUtil`: * `scalacOptions` is added similar to `javacOptions`. * More Scala test frameworks are added in `testModulesByGroup`. --- build.mill | 4 +- .../src/mill/main/buildgen/BuildGenBase.scala | 25 +- .../src/mill/main/buildgen/BuildGenUtil.scala | 49 +- .../buildgen/src/mill/main/buildgen/ir.scala | 13 + .../mill/main/gradle/GradleBuildGenMain.scala | 27 +- .../mill/main/maven/MavenBuildGenMain.scala | 28 +- main/init/package.mill | 31 +- .../sbt/models/src/mill/main/sbt/Models.scala | 307 ++++++++++- .../.gitignore | 0 .../README.md | 2 +- .../build.sbt | 3 +- .../project/build.properties | 0 .../project/plugins.sbt | 0 .../mill/main/sbt/ExportBuildPlugin.scala | 247 +++++++++ .../mill/main/sbt/ProjectTreePlugin.scala | 29 -- .../src/mill/main/sbt/SbtBuildGenMain.scala | 487 +++++++++--------- .../expected/scala-seed-project/build.mill | 29 ++ .../src/mill/main/sbt/BuildGenTests.scala | 2 +- 18 files changed, 953 insertions(+), 330 deletions(-) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-export-build}/.gitignore (100%) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-export-build}/README.md (84%) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-export-build}/build.sbt (80%) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-export-build}/project/build.properties (100%) rename main/init/sbt/{sbt-mill-init-generate-project-tree => sbt-mill-init-export-build}/project/plugins.sbt (100%) create mode 100644 main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala delete mode 100644 main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala create mode 100644 main/init/sbt/test/resources/expected/scala-seed-project/build.mill diff --git a/build.mill b/build.mill index 8bfb7a7a934..a16a98e60d7 100644 --- a/build.mill +++ b/build.mill @@ -634,8 +634,10 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima { def skipPreviousVersions: T[Seq[String]] = T(Seq.empty[String]) } +trait MillPublishCrossScalaModule extends MillPublishJavaModule with CrossScalaModule + object bridge extends Cross[BridgeModule](compilerBridgeScalaVersions) -trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { +trait BridgeModule extends MillPublishCrossScalaModule { def scalaVersion = crossScalaVersion def publishVersion = bridgeVersion def artifactName = "mill-scala-compiler-bridge" diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 73e846c04bf..261e5d58615 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -4,26 +4,29 @@ import mill.main.buildgen.BuildGenUtil.{buildPackages, compactBuildTree, writeBu import scala.collection.immutable.SortedMap -trait BuildGenBase[M, D] { +trait BuildGenBase[M, D, I] { type C - def convertWriteOut(cfg: C, shared: BuildGenUtil.Config, input: Tree[Node[M]]): Unit = { + def convertWriteOut(cfg: C, shared: BuildGenUtil.BasicConfig, input: I): Unit = { val output = convert(input, cfg, shared) writeBuildObject(if (shared.merge.value) compactBuildTree(output) else output) } + def getProjectTree(input: I): Tree[Node[M]] + def convert( - input: Tree[Node[M]], + input: I, cfg: C, - shared: BuildGenUtil.Config + shared: BuildGenUtil.BasicConfig ): Tree[Node[BuildObject]] = { - // for resolving moduleDeps + val projectTree = getProjectTree(input) - val packages = buildPackages(input)(getPackage) + // for resolving moduleDeps + val packages = buildPackages(projectTree)(getPackage) val baseInfo = shared.baseModule.fold(IrBaseInfo()) { getBaseInfo(input, cfg, _, packages.size) } - input.map { build => + projectTree.map { build => val name = getArtifactId(build.value) println(s"converting module $name") @@ -50,7 +53,7 @@ trait BuildGenBase[M, D] { def getSuperTypes(cfg: C, baseInfo: IrBaseInfo, build: Node[M]): Seq[String] def getBaseInfo( - input: Tree[Node[M]], + input: I, cfg: C, baseModule: String, packagesSize: Int @@ -67,3 +70,9 @@ trait BuildGenBase[M, D] { packages: Map[(String, String, String), String] ): IrBuild } + +object BuildGenBase { + trait BaseInfoFromSubproject[M, D] extends BuildGenBase[M, D, Tree[Node[M]]] { + override def getProjectTree(input: Tree[Node[M]]): Tree[Node[M]] = input + } +} diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index b34a8a6e700..9a879a590e4 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -32,6 +32,8 @@ object BuildGenUtil { | |${renderJavacOptions(javacOptions)} | + |${renderScalacOptions(scalacOptions)} + | |${renderPomSettings(renderIrPom(pomSettings))} | |${renderPublishVersion(publishVersion)} @@ -83,6 +85,8 @@ object BuildGenUtil { | |${renderJavacOptions(javacOptions)} | + |${renderScalacOptions(scalacOptions)} + | |${renderRepositories(repositories)} | |${renderBomIvyDeps(scopedDeps.mainBomIvyDeps)} @@ -127,7 +131,12 @@ object BuildGenUtil { isNested: Boolean, packagesSize: Int ): SortedSet[String] = { - scala.collection.immutable.SortedSet("mill._", "mill.javalib._", "mill.javalib.publish._") ++ + scala.collection.immutable.SortedSet( + "mill._", + "mill.javalib._", + "mill.javalib.publish._", + "mill.scalalib.SbtModule" + ) ++ (if (isNested) baseModule.map(name => s"$$file.$name") else if (packagesSize > 1) Seq("$packages._") else None) @@ -379,6 +388,14 @@ object BuildGenUtil { args.iterator.map(escape) ) + def renderScalacOptions(args: Option[IterableOnce[String]]): String = + args.fold("")(args => + optional( + "def scalacOptions = super.scalacOptions() ++ Seq", + args.iterator.map(escape) + ) + ) + def renderRepositories(args: IterableOnce[String]): String = optional( "def repositoriesTask = Task.Anon { super.repositoriesTask() ++ Seq(", @@ -425,7 +442,13 @@ object BuildGenUtil { val testModulesByGroup: Map[String, String] = Map( "junit" -> "TestModule.Junit4", "org.junit.jupiter" -> "TestModule.Junit5", - "org.testng" -> "TestModule.TestNg" + "org.testng" -> "TestModule.TestNg", + "org.scalatest" -> "TestModule.ScalaTest", + "org.specs2" -> "TestModule.Specs2", + "com.lihaoyi.utest" -> "TestModule.UTest", + "org.scalameta" -> "TestModule.Munit", + "com.disneystreaming" -> "Weaver", + "dev.zio" -> "TestModule.ZioTest" ) def writeBuildObject(tree: Tree[Node[BuildObject]]): Unit = { @@ -453,20 +476,28 @@ object BuildGenUtil { } @mainargs.main - case class Config( + case class BasicConfig( @arg(doc = "name of generated base module trait defining shared settings", short = 'b') baseModule: Option[String] = None, - @arg( - doc = "distribution and version of custom JVM to configure in --base-module", - short = 'j' - ) - jvmId: Option[String] = None, @arg(doc = "name of generated nested test module", short = 't') testModule: String = "test", @arg(doc = "name of generated companion object defining dependency constants", short = 'd') depsObject: Option[String] = None, @arg(doc = "merge build files generated for a multi-module build", short = 'm') - merge: Flag = Flag(), + merge: Flag = Flag() + ) + object BasicConfig { + implicit def parser: mainargs.ParserForClass[BasicConfig] = mainargs.ParserForClass[BasicConfig] + } + // TODO alternative names: `MavenAndGradleConfig`, `MavenAndGradleSharedConfig` + @mainargs.main + case class Config( + basicConfig: BasicConfig, + @arg( + doc = "distribution and version of custom JVM to configure in --base-module", + short = 'j' + ) + jvmId: Option[String] = None, @arg(doc = "capture Maven publish properties", short = 'p') publishProperties: Flag = Flag() ) diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index e43d81e0d29..d835498b66c 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -41,6 +41,7 @@ case class IrTrait( baseModule: String, moduleSupertypes: Seq[String], javacOptions: Seq[String], + scalacOptions: Option[Seq[String]], pomSettings: IrPom, publishVersion: String, publishProperties: Seq[(String, String)], @@ -75,6 +76,7 @@ case class IrLicense( distribution: String = "repo" ) +// TODO Consider renaming to `IrModule(Build)` to disambiguate? sbt, for example, uses `ThisBuild` and `buildSettings` to refer to the whole build. case class IrBuild( scopedDeps: IrScopedDeps, testModule: String, @@ -82,6 +84,7 @@ case class IrBuild( dirs: Seq[String], repositories: Seq[String], javacOptions: Seq[String], + scalacOptions: Option[Seq[String]], projectName: String, pomSettings: IrPom, publishVersion: String, @@ -93,6 +96,7 @@ case class IrBuild( ) case class IrScopedDeps( + // TODO The type is `Seq` and this is deduplicated and sorted in `BuildGenUtil`. Make the type `SortedMap` here for consistency? namedIvyDeps: Seq[(String, String)] = Nil, mainBomIvyDeps: SortedSet[String] = SortedSet(), mainIvyDeps: SortedSet[String] = SortedSet(), @@ -111,9 +115,18 @@ case class IrScopedDeps( case class IrBaseInfo( javacOptions: Seq[String] = Nil, + scalacOptions : Option[Seq[String]] = None, repositories: Seq[String] = Nil, noPom: Boolean = true, publishVersion: String = "", publishProperties: Seq[(String, String)] = Nil, moduleTypedef: IrTrait = null ) + +sealed class IrDependencyType +object IrDependencyType { + case object Default extends IrDependencyType + case object Test extends IrDependencyType + case object Compile extends IrDependencyType + case object Run extends IrDependencyType +} diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index dfdd81cb72b..2cfdb7fdeaf 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -39,7 +39,7 @@ import scala.jdk.CollectionConverters.* * - non-Java sources */ @mill.api.internal -object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { +object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectModel, JavaModel.Dep] { type C = Config def main(args: Array[String]): Unit = { @@ -75,7 +75,7 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { (Node(dirs, project), children) } - convertWriteOut(cfg, cfg.shared, input) + convertWriteOut(cfg, cfg.shared.basicConfig, input) println("converted Gradle build to Mill") } finally connection.close() @@ -124,6 +124,7 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { Option.when(null != project.maven().pom()) { "PublishModule" } val javacOptions = getJavacOptions(project) + val scalacOptions = None val repos = getRepositories(project) val pomSettings = extractPomSettings(project) val publishVersion = getPublishVersion(project) @@ -134,13 +135,22 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { baseModule, supertypes, javacOptions, + scalacOptions, pomSettings, publishVersion, publishProperties, repos ) - IrBaseInfo(javacOptions, repos, pomSettings == null, publishVersion, Seq.empty, typedef) + IrBaseInfo( + javacOptions, + scalacOptions, + repos, + pomSettings == null, + publishVersion, + Seq.empty, + typedef + ) } override def extractIrBuild( @@ -154,11 +164,12 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { val version = getPublishVersion(project) IrBuild( scopedDeps = scopedDeps, - testModule = cfg.shared.testModule, + testModule = cfg.shared.basicConfig.testModule, hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, repositories = getRepositories(project).diff(baseInfo.repositories), javacOptions = getJavacOptions(project).diff(baseInfo.javacOptions), + scalacOptions = None, projectName = getArtifactId(project), pomSettings = if (baseInfo.noPom) extractPomSettings(project) else null, publishVersion = if (version == baseInfo.publishVersion) null else version, @@ -173,7 +184,7 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { } def getModuleSupertypes(cfg: Config): Seq[String] = - Seq(cfg.shared.baseModule.getOrElse("MavenModule")) + Seq(cfg.shared.basicConfig.baseModule.getOrElse("MavenModule")) def getPackage(project: ProjectModel): (String, String, String) = { (project.group(), project.name(), project.version()) @@ -183,13 +194,12 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { def getMillSourcePath(model: ProjectModel): Path = os.Path(model.directory()) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = { + def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = Seq("RootModule") ++ Option.when(null != build.value.maven().pom() && baseInfo.noPom) { "PublishModule" } ++ Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { getModuleSupertypes(cfg) }.toSeq.flatten - } def groupArtifactVersion(dep: JavaModel.Dep): (String, String, String) = (dep.group(), dep.name(), dep.version()) @@ -250,6 +260,7 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { } } + // TODO consider renaming to `extractConfigurationDeps` as Gradle calls them configurations instead of scopes def extractScopedDeps( project: ProjectModel, packages: PartialFunction[(String, String, String), String], @@ -260,7 +271,7 @@ object GradleBuildGenMain extends BuildGenBase[ProjectModel, JavaModel.Dep] { val _java = project._java() if (null != _java) { val ivyDep: JavaModel.Dep => String = - cfg.shared.depsObject.fold(interpIvy(_)) { objName => dep => + cfg.shared.basicConfig.depsObject.fold(interpIvy(_)) { objName => dep => val depName = s"`${dep.group()}:${dep.name()}`" sd = sd.copy(namedIvyDeps = sd.namedIvyDeps :+ (depName, interpIvy(dep))) s"$objName.$depName" diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index a8d9c0719a9..7edc0add46a 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -36,7 +36,7 @@ import scala.jdk.CollectionConverters.* * - build profiles */ @mill.api.internal -object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { +object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Dependency] { type C = Config def main(args: Array[String]): Unit = { @@ -54,7 +54,7 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { (Node(dirs, model), model.getModules.iterator().asScala.map(dirs :+ _)) } - convertWriteOut(cfg, cfg.shared, input) + convertWriteOut(cfg, cfg.shared.basicConfig, input) println("converted Maven build to Mill") } @@ -67,7 +67,8 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { ): IrBaseInfo = { val model = input.node.value val javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model) - val repositores = getRepositories(model) + val scalacOptions = None + val repositories = getRepositories(model) val pomSettings = extractPomSettings(model) val publishVersion = model.getVersion val publishProperties = getPublishProperties(model, cfg.shared) @@ -77,13 +78,22 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { baseModule, getModuleSupertypes(cfg), javacOptions, + scalacOptions, pomSettings, publishVersion, publishProperties, getRepositories(model) ) - IrBaseInfo(javacOptions, repositores, noPom = false, publishVersion, publishProperties, typedef) + IrBaseInfo( + javacOptions, + scalacOptions, + repositories, + noPom = false, + publishVersion, + publishProperties, + typedef + ) } override def extractIrBuild( @@ -97,11 +107,12 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { val version = model.getVersion IrBuild( scopedDeps = scopedDeps, - testModule = cfg.shared.testModule, + testModule = cfg.shared.basicConfig.testModule, hasTest = os.exists(getMillSourcePath(model) / "src/test"), dirs = build.dirs, repositories = getRepositories(model), javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model).diff(baseInfo.javacOptions), + scalacOptions = None, projectName = getArtifactId(model), pomSettings = if (baseInfo.noPom) extractPomSettings(model) else null, publishVersion = if (version == baseInfo.publishVersion) null else version, @@ -127,10 +138,9 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { def getMillSourcePath(model: Model): Path = os.Path(model.getProjectDirectory) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = { + def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = Seq("RootModule") ++ - cfg.shared.baseModule.fold(getModuleSupertypes(cfg))(Seq(_)) - } + cfg.shared.basicConfig.baseModule.fold(getModuleSupertypes(cfg))(Seq(_)) def processResources( input: java.util.List[org.apache.maven.model.Resource], @@ -205,7 +215,7 @@ object MavenBuildGenMain extends BuildGenBase[Model, Dependency] { val hasTest = os.exists(os.Path(model.getProjectDirectory) / "src/test") val ivyDep: Dependency => String = { - cfg.shared.depsObject.fold(interpIvy(_)) { objName => dep => + cfg.shared.basicConfig.depsObject.fold(interpIvy(_)) { objName => dep => { val depName = s"`${dep.getGroupId}:${dep.getArtifactId}`" sd = sd.copy(namedIvyDeps = sd.namedIvyDeps :+ (depName, interpIvy(dep))) diff --git a/main/init/package.mill b/main/init/package.mill index 91d51a866ea..90ff867d9c4 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -1,7 +1,6 @@ package build.main.init import mill._ -import mill.scalalib.CrossScalaModule object `package` extends RootModule with build.MillPublishScalaModule { @@ -63,8 +62,16 @@ object `package` extends RootModule with build.MillPublishScalaModule { } object buildgen extends build.MillPublishScalaModule { - def moduleDeps = Seq(build.runner) + def moduleDeps = Seq(build.runner /*, tree(build.Deps.scalaVersion)*/ ) def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib) + + // I tried moving `Tree` into this module, but it doesn't compile with Scala 2.12.20. + /* + object tree extends Cross[TreeModule](build.Deps.sbtScalaVersion212, build.Deps.scalaVersion) + trait TreeModule extends build.MillPublishCrossScalaModule { + def ivyDeps = Agg(build.Deps.upickle) + } + */ } object gradle extends build.MillPublishScalaModule { def moduleDeps = Seq(buildgen) @@ -90,27 +97,33 @@ object `package` extends RootModule with build.MillPublishScalaModule { object sbt extends build.MillPublishScalaModule { def moduleDeps = Seq(buildgen, models(build.Deps.scalaVersion)) + val sbtPluginProjectPath = millModuleBasePath.value / "sbt-mill-init-export-build" + + def sbtPluginProjectSource = Task.Source { sbtPluginProjectPath } + // generateSbtPluginJar def sbtPluginJarResources = Task { models(build.Deps.sbtScalaVersion212).publishLocal()() + sbtPluginProjectSource() + import scala.sys.process._ - val sbtProjectPath = millModuleBasePath.value / "sbt-mill-init-generate-project-tree" val version = build.millVersion() if ( Process( // The version is passed to the sbt build so it correctly resolves the "models" dependency version and sets its own version. s"""sbt 'set version := "$version"' assembly""", - sbtProjectPath.toIO + sbtPluginProjectPath.toIO ).! != 0 ) throw new RuntimeException( - "fail to run `sbt assembly` in the \"sbt-mill-init-generate-project-tree\" sbt project." + "Failed to run `sbt assembly` in the \"sbt-mill-init-export-build\" sbt project. " + + "Check if the project builds correctly and if you have sbt available on your system, and install it if you don't." ) os.copy( - sbtProjectPath / "target" / "scala-2.12" / "sbt-1.0" / s"sbt-mill-init-generate-project-tree-assembly-$version.jar", - Task.dest / "sbt-mill-init-generate-project-tree-assembly.jar" + sbtPluginProjectPath / "target" / "scala-2.12" / "sbt-1.0" / s"sbt-mill-init-export-build-assembly-$version.jar", + Task.dest / "sbt-mill-init-export-build-assembly.jar" ) PathRef(Task.dest) } @@ -121,8 +134,10 @@ object `package` extends RootModule with build.MillPublishScalaModule { // An SBT plugin is built with Scala 2.12. See https://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin. object models extends Cross[ModelsModule](build.Deps.sbtScalaVersion212, build.Deps.scalaVersion) - trait ModelsModule extends build.MillPublishJavaModule with CrossScalaModule { + trait ModelsModule extends build.MillPublishCrossScalaModule { + // def moduleDeps = Seq(buildgen.tree()) def ivyDeps = Agg(build.Deps.upickle) + def compileIvyDeps = Agg(build.Deps.sbt) // for definition references only } } } diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 983aa85b46b..6c39a33bf5f 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -1,8 +1,307 @@ package mill.main.sbt -import upickle.default.{ReadWriter => RW, macroRW} +import mill.main.sbt.BuildPublicationInfo.License +import mill.main.sbt.Models.URL +import sbt.Keys +import upickle.default.{macroRW, ReadWriter => RW} -case class ProjectTree() -object ProjectTree { - implicit val rw: RW[ProjectTree] = macroRW +object Models { + type URL = String + + // TODO Remove. This seems not supported. + + type Id[X] = X // not working with uPickle + /*object Id { + //implicit def rw[T](tRw : RW[T]) : RW[Id[T]] = tRw + implicit def rw[T: RW]: RW[Id[T]] = macroRW + }*/ +} + +// TODO Remove. Not used. +/** + * A wrapper added because [[Models.Id]] does not work with uPickle. + * @param wrappedValue named this way in order not to conflict with [[sbt.std.MacroValue.value]] + */ +case class Wrapper[+A](wrappedValue: A) extends AnyVal { + def map[B](f: A => B): Wrapper[B] = Wrapper(f(wrappedValue)) +} +object Wrapper { + import scala.language.implicitConversions + implicit def to[A](value: A): Wrapper[A] = + Wrapper(value) + implicit def from[A](wrapper: Wrapper[A]): A = + wrapper.wrappedValue + + implicit def rw[T: RW]: RW[Wrapper[T]] = macroRW +} + +case class BuildExport( + /** @see [[sbt.AutoPlugin.buildSettings]] and [[sbt.AutoPlugin.globalSettings]] */ + defaultBuildInfo: BuildInfo, + projects: Seq[Project] +) +object BuildExport { + implicit val rw: RW[BuildExport] = macroRW +} + +// TODO remove these plain ones + +case class PlainBuildInfo( + buildPublicationInfo: PlainBuildPublicationInfo, + /** @see [[Keys.javacOptions]] */ + javacOptions: Seq[String], + /** @see [[Keys.resolvers]] */ + resolvers: Seq[Resolver] +) +object PlainBuildInfo { + implicit val rw: RW[PlainBuildInfo] = macroRW +} + +case class PlainBuildPublicationInfo( + /** @see [[Keys.description]] */ + description: String, + /** + * corresponds to `url` in POM + * + * @see [[Keys.homepage]] + */ + homepage: Option[String], + /** @see [[Keys.licenses]] */ + licenses: Seq[License], + // organization: Option[String], + /* + /** + * corresponds to Maven's `organization` in POM + * + * @see [[Keys.organization]] + */ + organizationName: String, + */ + /** + * corresponds to `organizationUrl` in POM + * + * @see [[Keys.organizationHomepage]] + */ + organizationHomepage: Option[String], + /** @see [[Keys.developers]] */ + developers: Seq[Developer], + /** @see [[Keys.scmInfo]] */ + scmInfo: Option[ScmInfo] +) +object PlainBuildPublicationInfo { + implicit val rw: RW[PlainBuildPublicationInfo] = macroRW + + /** @see [[sbt.librarymanagement.License]] */ + type License = (String, URL) +} + +/* +TODO I used higher-kinded types here along with some higher-rank types (`GetValueFn` and `MapFn`) in the sbt plugin, + because I first thought that the default build info's members don't need to be wrapped with `Option` while the project build info's members do, + and we can reuse some more code this way, + but it turns out that both kinds have to be wrapped with `Option` and the abstraction doesn't work with the sbt `value` macros. + Most likely we can just replace `F` with `Option` in them and remove the `TC` suffixes, + resulting in `mill.main.sbt.BuildInfo` below, + unless they can possibly provide more extensibility, which is unlikely as I think now. + See other related definitions such as `Id`, `Wrapper`, + `getBuildInfo[F[_]](ref: Reference, getValue: GetValueFn[F], map: MapFn[F]): BuildInfoTC[F]`, `GetValueFn`, and `MapFn`. + */ + +case class BuildInfoTC[F[_]]( + buildPublicationInfo: BuildPublicationInfoTC[F], + /** @see [[Keys.javacOptions]] */ + javacOptions: F[Seq[String]], + /** @see [[Keys.scalacOptions]] */ + scalacOptions: F[Seq[String]], + /** @see [[Keys.resolvers]] */ + resolvers: F[Seq[Resolver]] +) +object BuildInfoTC { + type BuildInfo = BuildInfoTC[Wrapper] + implicit val wrapperRw: RW[BuildInfo] = macroRW + type OptionBuildInfo = BuildInfoTC[Option] + implicit val optionRw: RW[OptionBuildInfo] = macroRW +} + +case class BuildPublicationInfoTC[F[_]]( + /** @see [[Keys.description]] */ + description: F[String], + /** + * corresponds to `url` in POM + * + * @see [[Keys.homepage]] + */ + homepage: F[Option[String]], + /** @see [[Keys.licenses]] */ + licenses: F[Seq[PlainBuildPublicationInfo.License]], + // organization: Option[String], + /* + /** + * corresponds to Maven's `organization` in POM + * + * @see [[Keys.organization]] + */ + organizationName: F[String], + */ + /** + * corresponds to `organizationUrl` in POM + * + * @see [[Keys.organizationHomepage]] + */ + organizationHomepage: F[Option[String]], + /** @see [[Keys.developers]] */ + developers: F[Seq[Developer]], + /** @see [[Keys.scmInfo]] */ + scmInfo: F[Option[ScmInfo]] +) +object BuildPublicationInfoTC { + + /** @see [[sbt.librarymanagement.License]] */ + type License = (String, URL) + + /* + // This does not work: "could not find implicit value for parameter e: upickle.default.Reader[F[String]]". + implicit def rw[F[_]]: RW[BuildPublicationInfoTC[F]] = macroRW + */ + /* + // This does not work: "could not find implicit value for parameter e: upickle.default.Reader[mill.main.sbt.Models.Id[Seq[mill.main.sbt.BuildPublicationInfo.License]]]". + type BuildPublicationInfo = BuildPublicationInfoTC[Id] + implicit val IdRw: RW[BuildPublicationInfo] = macroRW + */ + type BuildPublicationInfo = BuildPublicationInfoTC[Wrapper] + implicit val wrapperRw: RW[BuildPublicationInfo] = macroRW + type OptionBuildPublicationInfo = BuildPublicationInfoTC[Option] + implicit val optionRw: RW[OptionBuildPublicationInfo] = macroRW +} + +case class BuildInfo( + buildPublicationInfo: BuildPublicationInfo, + /** @see [[Keys.javacOptions]] */ + javacOptions: Option[Seq[String]], + /** @see [[Keys.scalacOptions]] */ + scalacOptions: Option[Seq[String]], + /** @see [[Keys.resolvers]] */ + resolvers: Option[Seq[Resolver]] +) +object BuildInfo { + implicit val rw: RW[BuildInfo] = macroRW +} + +/** + * Members ordered by their order in [[Keys]]. + */ +case class BuildPublicationInfo( + /** @see [[Keys.description]] */ + description: Option[String], + /** + * corresponds to `url` in POM + * + * @see [[Keys.homepage]] + */ + homepage: Option[Option[String]], + /** @see [[Keys.licenses]] */ + licenses: Option[Seq[PlainBuildPublicationInfo.License]], + /** + * corresponds to `groupId` in POM and Mill's `PomSettings.organization` + * + * @see [[Keys.organization]] + */ + organization: Option[String], + // not needed + /* + /** + * corresponds to Maven's `organization` in POM + * + * @see [[Keys.organizationName]] + */ + organizationName: Option[String], + */ + /** + * corresponds to `organizationUrl` in POM + * + * @see [[Keys.organizationHomepage]] + */ + organizationHomepage: Option[Option[String]], + /** @see [[Keys.developers]] */ + developers: Option[Seq[Developer]], + /** @see [[Keys.scmInfo]] */ + scmInfo: Option[Option[ScmInfo]], + /** @see [[Keys.version]] */ + version: Option[String] +) +object BuildPublicationInfo { + + /** @see [[sbt.librarymanagement.License]] */ + type License = (String, URL) + + implicit val rw: RW[BuildPublicationInfo] = macroRW +} + +/** + * @see [[sbt.librarymanagement.ScmInfo]] + */ +case class ScmInfo( + browseUrl: URL, + connection: String, + devConnection: Option[String] +) +object ScmInfo { + implicit val rw: RW[ScmInfo] = macroRW +} + +/** + * @see [[sbt.librarymanagement.Developer]] + */ +case class Developer(id: String, name: String, email: String, url: URL) +object Developer { + implicit val rw: RW[Developer] = macroRW +} + +/** + * Only Maven repositories are supported now. + * @see [[sbt.librarymanagement.Resolver]] + */ +case class Resolver(root: String) +object Resolver { + implicit val rw: RW[Resolver] = macroRW +} + +case class Project( + // organization: String, // `groupId` in Maven, moved inside `buildInfo` + name: String, // `artifactId` in Maven + // version: String, // `groupId` in Maven, moved inside `buildInfo` + // dirs: ProjectDirs, // relative + projectDirectory: String, + buildInfo: BuildInfo, + // dependencyConfigurations: Seq[DependencyConfiguration] // TODO remove + allDependencies: Seq[Dependency] +) +object Project { + implicit val rw: RW[Project] = macroRW + + // TODO Remove. Not used. + type ProjectDirs = Seq[String] +} + +// TODO Remove. For an old implementation of retrieving `Configuration / allDependencies` that doesn't work. +case class DependencyConfiguration( + id: String, + allDependencies: Seq[Dependency] + /*, projectDependencies: Seq[ProjectDirs], libraryDependencies: Seq[LibraryDependency]*/ +) +object DependencyConfiguration { + implicit val rw: RW[DependencyConfiguration] = macroRW +} + +case class Dependency( + organization: String, // `groupId` in Maven + name: String, // `artifactId` in Maven + crossVersion: Boolean = false, + revision: String, + configurations: Option[String] + // BOM seems not supported by sbt. See https://stackoverflow.com/questions/42032303/how-do-i-use-a-maven-bom-bill-of-materials-to-manage-my-dependencies-in-sbt. + // isBom : Boolean = false +) +object Dependency { + implicit val rw: RW[Dependency] = macroRW } diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore b/main/init/sbt/sbt-mill-init-export-build/.gitignore similarity index 100% rename from main/init/sbt/sbt-mill-init-generate-project-tree/.gitignore rename to main/init/sbt/sbt-mill-init-export-build/.gitignore diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/README.md b/main/init/sbt/sbt-mill-init-export-build/README.md similarity index 84% rename from main/init/sbt/sbt-mill-init-generate-project-tree/README.md rename to main/init/sbt/sbt-mill-init-export-build/README.md index 55e348119f8..344d9991bac 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/README.md +++ b/main/init/sbt/sbt-mill-init-export-build/README.md @@ -5,6 +5,6 @@ An sbt plugin to generate the project tree for `mill init` ## Working on this project Make sure you are on the SNAPSHOT version of Mill, -and run `./mill "main.init.sbt.models[2.12.20].publishLocal"`, +and run `./mill "main.init.sbt.models[2.12.20].publishLocal"` to publish the models to Ivy's local repository before working on this project. Then open this directory as a separate IntelliJ IDEA project as this is an sbt project. diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt b/main/init/sbt/sbt-mill-init-export-build/build.sbt similarity index 80% rename from main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt rename to main/init/sbt/sbt-mill-init-export-build/build.sbt index 9ee673f5ba5..9cbfecbaca8 100644 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/build.sbt +++ b/main/init/sbt/sbt-mill-init-export-build/build.sbt @@ -1,5 +1,4 @@ - -name := """sbt-mill-init-generate-project-tree""" +name := """sbt-mill-init-export-build""" organization := "com.lihaoyi" version := "SNAPSHOT" diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties b/main/init/sbt/sbt-mill-init-export-build/project/build.properties similarity index 100% rename from main/init/sbt/sbt-mill-init-generate-project-tree/project/build.properties rename to main/init/sbt/sbt-mill-init-export-build/project/build.properties diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt b/main/init/sbt/sbt-mill-init-export-build/project/plugins.sbt similarity index 100% rename from main/init/sbt/sbt-mill-init-generate-project-tree/project/plugins.sbt rename to main/init/sbt/sbt-mill-init-export-build/project/plugins.sbt diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala new file mode 100644 index 00000000000..64c6e71d4c4 --- /dev/null +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -0,0 +1,247 @@ +package mill.main.sbt + +import sbt.Keys.* +import sbt.Project.extract +import sbt.io.IO +import sbt.librarymanagement.Disabled +import sbt.{Def, Developer as _, Project as _, Resolver as _, ScmInfo as _, *} +import upickle.default.* + +import java.io.File +import scala.language.higherKinds + +object ExportBuildPlugin extends AutoPlugin { + override def trigger = allRequirements + // override def requires = ??? // defaults to `JvmPlugin` + + object autoImport { + val millInitBuildInfo = taskKey[BuildInfo]( + "get the `mill.main.sbt.BuildInfo` model of this build or this project" + ) + // TODO Remove. No longer used. + val millInitDependencyConfiguration = taskKey[DependencyConfiguration]( + "get the `mill.main.sbt.DependencyConfiguration` model of this project and configuration" + ) + val millInitAllDependencies = taskKey[Seq[Dependency]]( + "get the all the `mill.main.sbt.Dependency`s of this project" + ) + val millInitProject = taskKey[Project]("get the `mill.main.sbt.Project` model of this project") + val millInitExportBuild = taskKey[File]("export the build in a JSON file for `mill init`") + } + + // TODO `getBuildInfo` along with `MapFn` and `GetValueFn` does not work with the `value` macro. Remove. + + trait MapFn[F[_]] { + // def apply[A, B](fa : F[A], f: A => B): F[B] // Type inference doesn't work well with this. + def apply[A, B](fa: F[A])(f: A => B): F[B] + } + + trait GetValueFn[F[_]] { + def apply[T](key: SettingKey[T]): F[T] + + def apply[T](key: TaskKey[T]): F[T] + } + + def getBuildInfo[F[_]](ref: Reference, getValue: GetValueFn[F], map: MapFn[F]): BuildInfoTC[F] = + BuildInfoTC( + BuildPublicationInfoTC( + getValue(ref / description), + map(getValue(ref / homepage))(_.map(_.toExternalForm)), + map(getValue(ref / licenses))(_.map { case (name, url) => (name, url.toExternalForm) }), + //getValue(ref / organizationName), // not needed + map(getValue(ref / organizationHomepage))(_.map(_.toExternalForm)), + map(getValue(ref / developers))(_.map(developer => + Developer(developer.id, developer.name, developer.email, developer.url.toExternalForm) + )), + map(getValue(ref / scmInfo))(_.map(scmInfo => + ScmInfo(scmInfo.browseUrl.toExternalForm, scmInfo.connection, scmInfo.devConnection) + )) + ), + getValue(ref / javacOptions), + getValue(ref / scalacOptions), + map(getValue(ref / resolvers))(_.flatMap { + case mavenRepository: MavenRepository => Some(Resolver(mavenRepository.root)) + case resolver => + println(s"A `Resolver` which is not a `MavenRepository` is skipped: $resolver") + None + }) + ) + + import autoImport.* + + val buildInfoSetting = millInitBuildInfo := BuildInfo( + BuildPublicationInfo( + description.?.value, + homepage.?.value.map(_.map(_.toExternalForm)), + licenses.?.value.map(_.map { case (name, url) => + (name, url.toExternalForm) + }), + organization.?.value, + //organizationName.?.value, // not needed + organizationHomepage.?.value.map(_.map(_.toExternalForm)), + developers.?.value.map(_.map(developer => + Developer(developer.id, developer.name, developer.email, developer.url.toExternalForm) + )), + scmInfo.?.value.map(_.map(scmInfo => + ScmInfo(scmInfo.browseUrl.toExternalForm, scmInfo.connection, scmInfo.devConnection) + )), + version.?.value + ), + javacOptions.?.value, + scalacOptions.?.value, + resolvers.?.value.map(_.flatMap { + case mavenRepository: MavenRepository => Some(Resolver(mavenRepository.root)) + case resolver => + println(s"A `Resolver` which is not a `MavenRepository` is skipped: $resolver") + None + }) + ) + + override lazy val buildSettings: Seq[Def.Setting[?]] = Seq( + buildInfoSetting + ) + + // TODO Remove. For an old implementation that depends on retrieving `configuration / allDependencies` which doesn't work. + private val neededConfigurations = Seq(Compile, Test, Runtime, Default, Provided, Optional) + + // TODO Remove. No longer used. Replaced by `millInitAllDependencies`. + val millInitDependencyConfigurationSetting = + millInitDependencyConfiguration := DependencyConfiguration( + configuration.value.id, + + /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ + /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => + Dependency( + moduleID.organization, + moduleID.name, + moduleID.crossVersion match { + case Disabled => false + case _: Binary => true + case crossVersion => + println(s"Unsupported `CrossVersion`: $crossVersion") + false + }, + moduleID.revision, + moduleID.configurations + ) + } + ) + + override lazy val projectSettings: Seq[Setting[?]] = Seq( + Seq(buildInfoSetting), + // TODO Remove. For an old implementation that depends on `configuration / allDependencies` which doesn't work. + neededConfigurations.flatMap(configuration => inConfig(configuration)(Seq(millInitDependencyConfigurationSetting))), + Seq( + millInitAllDependencies := { + /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ + /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => + Dependency( + moduleID.organization, + moduleID.name, + moduleID.crossVersion match { + case Disabled => false + case _: Binary => true + case crossVersion => + println(s"Unsupported `CrossVersion`: $crossVersion") + false + }, + moduleID.revision, + moduleID.configurations + ) + } + }, + millInitProject := { + // TODO remove + val extracted = extract(state.value) // TODO move to a separate task if used + Project( + //organization.value, + name.value, + //version.value, + //baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), + baseDirectory.value.getPath, + // TODO remove old implementation + /* + getBuildInfo[Option]( + ThisProject, + new GetValueFn[Option] { + override def apply[T](key: SettingKey[T]): Option[T] = extracted.getOpt(key) + + override def apply[T](key: TaskKey[T]): Option[T] = + ??? // extracted.getOpt(key).map(_.value) + }, + new MapFn[Option] { + override def apply[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f) + } + ) + */ + { + // keep the project `BuildInfo` members only when they are different + val defaultBi = (ThisBuild / millInitBuildInfo).value + val projectBi = millInitBuildInfo.value + import projectBi.* + BuildInfo({ + val defaultBpi = defaultBi.buildPublicationInfo + import projectBi.buildPublicationInfo.* + BuildPublicationInfo( + if (description != defaultBpi.description) description else None, + if (homepage != defaultBpi.homepage) homepage else None, + if (licenses != defaultBpi.licenses) licenses else None, + if (organization != defaultBpi.organization) organization else None, + //if (organizationName != defaultBpi.organizationName) organizationName else None, // not needed + if (organizationHomepage != defaultBpi.organizationHomepage) organizationHomepage else None, + if (developers != defaultBpi.developers) developers else None, + if (scmInfo != defaultBpi.scmInfo) scmInfo else None, + if (version != defaultBpi.version) version else None + ) + }, + if (javacOptions != defaultBi.javacOptions) javacOptions else None, + if (scalacOptions != defaultBi.scalacOptions) scalacOptions else None, + if (resolvers != defaultBi.resolvers) resolvers else None + ) + }, + { + // TODO Remove. For an old implementation that depends on retrieving `configuration / allDependencies` which doesn't work. + millInitDependencyConfiguration.all( + ScopeFilter(configurations = inConfigurations(neededConfigurations *)) + ).value + millInitAllDependencies.value + } + ) + }, + // `target.value` doesn't work in `globalSettings` and `buildSettings`, so this is added to `projectSettings. + millInitExportBuild := { + // TODO This does not work with the `value` macro. Remove. + /* + val defaultBuildInfo = getBuildInfo[Wrapper]( + ThisBuild, + new GetValueFn[Wrapper] { + override def apply[T](key: SettingKey[T]): Wrapper[T] = + ??? // Wrapper(key.value) // Could not find proxy + override def apply[T](key: TaskKey[T]): Wrapper[T] = + ??? // Wrapper(key.value) // Could not find proxy + }, + new MapFn[Wrapper] { + override def apply[A, B](fa: Wrapper[A])(f: A => B): Wrapper[B] = fa.map(f) + } + ) + */ + + val defaultBuildInfo = (ThisBuild / millInitBuildInfo).value + + // TODO remove the old implementation code + // `(projectRef / ...)` can't be used directly here due to macro limitations: "java.lang.IllegalArgumentException: Could not find proxy for projectRef: sbt.ProjectRef in ..." + /* + val allProjectPairs = buildStructure.value.allProjectPairs + val projects = allProjectPairs.map { case (resolvedProject, projectRef) => ??? } + */ + + val projects = millInitProject.all(ScopeFilter(inAnyProject)).value + + val buildExport = BuildExport(defaultBuildInfo, projects) + + val outputFile = target.value / "mill-init-build-export.json" + IO.write(outputFile, write(buildExport)) + outputFile + } + )).flatten +} diff --git a/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala b/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala deleted file mode 100644 index 3fe31be4d2e..00000000000 --- a/main/init/sbt/sbt-mill-init-generate-project-tree/src/main/scala/mill/main/sbt/ProjectTreePlugin.scala +++ /dev/null @@ -1,29 +0,0 @@ -package mill.main.sbt - -import sbt.Keys.target -import sbt.io.syntax.* -import sbt.{AutoPlugin, IO, Setting, taskKey} -import upickle.default.* - -object ProjectTreePlugin extends AutoPlugin { - override def trigger = allRequirements - // override def requires = ??? // defaults to `JvmPlugin` - - object autoImport { - val millInitGenerateProjectTree = taskKey[File]("generate the project tree for `mill init`") - } - - import autoImport.* - - // `target.value` doesn't work in `globalSettings` and `buildSettings` - override lazy val projectSettings: Seq[Setting[_]] = Seq( - millInitGenerateProjectTree := { - // TODO - val projectTree = ProjectTree() - - val outputFile = target.value / "mill-init-project-tree.json" - IO.write(outputFile, write(projectTree)) - outputFile - } - ) -} diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index ff0ea7e3acd..4977a3168d0 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -2,7 +2,12 @@ package mill.main.sbt import mainargs.{ParserForClass, main} import mill.main.buildgen.* -import mill.main.buildgen.BuildGenUtil.escape +import mill.main.buildgen.BuildGenUtil.* +import mill.main.buildgen.IrDependencyType.* +import os.Path + +import scala.collection.View +import scala.collection.immutable.SortedSet /** * Converts an sbt build to Mill by generating Mill build file(s). @@ -16,17 +21,24 @@ import mill.main.buildgen.BuildGenUtil.escape * The conversion * - handles deeply nested modules * - configures dependencies for configurations: + * - no configuration * - Compile + * - Provided + * - Optional * - Runtime * - Test - * - configures testing frameworks: - * - JUnit 4 - * - JUnit 5 - * - TestNG - * - µTest - * - ScalaTest - * - Specs2 - * - ScalaCheck + * - configures testing frameworks (@see [[mill.scalalib.TestModule]]): + * - Java: + * - JUnit 4 + * - JUnit 5 + * - TestNG + * - Scala: + * - ScalaTest + * - Specs2 + * - µTest + * - MUnit + * - Weaver + * - ZIOTest * ===Limitations=== * The conversion does not support * - custom configurations @@ -34,7 +46,7 @@ import mill.main.buildgen.BuildGenUtil.escape * - sources other than Scala on JVM and Java */ @mill.api.internal -object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { +object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[Node[Project]])] { type C = Config def main(args: Array[String]): Unit = { @@ -67,62 +79,52 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { println("Running sbt task to generate the project tree") - val exitCode = Process( + Process( Seq( sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", - "millInitGenerateProjectTree" + "millInitExportBuild" ), workspace.toIO ).! - println("Exit code from running the `millInitGenerateProjectTree` sbt task: " + exitCode) + // println("Exit code from running the `millInitExportBuild` sbt task: " + exitCode) + val buildExportPickled = os.read(workspace / "target" / "mill-init-build-export.json") + // TODO This is mainly for debugging purposes. Comment out this line if it's unnecessary. + println("sbt build export retrieved: " + buildExportPickled) import upickle.default.* - val projectTree = - read[ProjectTree](os.read(workspace / "target" / "mill-init-project-tree.json")) - println("Project tree retrieved: " + projectTree) - - /* - val connector = GradleConnector.newConnector() - - val args = - cfg.shared.jvmId.map { id => - println(s"resolving Java home for jvmId $id") - val home = Jvm.resolveJavaHome(id).getOrThrow - s"-Dorg.gradle.java.home=$home" - } ++ Seq("--init-script", writeGradleInitScript.toString()) - - try { - println("connecting to Gradle daemon") - val connection = connector.forProjectDirectory(workspace.toIO).connect() - try { - val root = connection.model(classOf[ProjectTree]) - .withArguments(args.asJava) - .get - - val input = Tree.from(root) { tree => - val project = tree.project() - val dirs = os.Path(project.directory()).subRelativeTo(workspace).segments - val children = tree.children().asScala.sortBy(_.project().name()).iterator - (Node(dirs, project), children) - } + val buildExport = read[BuildExport](buildExportPickled) + + // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. + val projectNodesByParentDirs: Map[Option[Seq[String]], View[Node[Project]]] = + buildExport.projects.view + .map(project => + Node(os.Path(project.projectDirectory).subRelativeTo(workspace).segments, project) + ) + .groupBy(node => { + val dirs = node.dirs + Option.when(dirs.nonEmpty)(dirs.dropRight(1)) + }) + + val input = Tree.from(projectNodesByParentDirs(None).head) { node => + val dirs = node.dirs + val children = projectNodesByParentDirs.getOrElse(Some(dirs), Seq.empty) + (node, children) + } - convertWriteOut(cfg, cfg.shared, input) + convertWriteOut(cfg, cfg.shared, (buildExport.defaultBuildInfo, input)) - println("converted Gradle build to Mill") - } finally connection.close() - } finally connector.disconnect() - */ + println("converted sbt build to Mill") } private def writeSbtFile(): os.Path = { val file = os.temp.dir() / "mill-init.sbt" // TODO copy to a temp file if it doesn't work when packaged in a jar val sbtPluginJarUrl = - getClass.getResource("/sbt-mill-init-generate-project-tree-assembly.jar").toExternalForm + getClass.getResource("/sbt-mill-init-export-build-assembly.jar").toExternalForm val contents = - s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-generate-project-tree" % "0.1.0-SNAPSHOT" from ${escape( + s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-export-build" % "dummy-version" from ${escape( sbtPluginJarUrl )}) |""".stripMargin @@ -130,272 +132,257 @@ object SbtBuildGenMain extends BuildGenBase[IrBuild, String] { file } - override def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[IrBuild]): Seq[String] = - ??? - - override def getBaseInfo( - input: Tree[Node[IrBuild]], - cfg: Config, - baseModule: String, - packagesSize: Int - ): IrBaseInfo = ??? - - override def getPackage(model: IrBuild): (String, String, String) = ??? + override def getProjectTree(input: (BuildInfo, Tree[Node[Project]])): Tree[Node[Project]] = + input._2 - override def getArtifactId(model: IrBuild): String = ??? + def sbtSupertypes = Seq("SbtModule", "PublishModule") // always publish - override def extractIrBuild( - cfg: Config, - baseInfo: IrBaseInfo, - build: Node[IrBuild], - packages: Map[(String, String, String), String] - ): IrBuild = ??? - - /* def getBaseInfo( - input: Tree[Node[ProjectModel]], + input: (BuildInfo, Tree[Node[Project]]), cfg: Config, baseModule: String, packagesSize: Int ): IrBaseInfo = { - val project = { - val projects = input.nodes(Tree.Traversal.BreadthFirst).map(_.value).toSeq - cfg.baseProject - .flatMap(name => projects.collectFirst { case m if name == m.name => m }) - .orElse(projects.collectFirst { case m if null != m.maven().pom() => m }) - .orElse(projects.collectFirst { case m if !m.maven().repositories().isEmpty => m }) - .getOrElse(input.node.value) - } - if (packagesSize > 1) { - println(s"settings from ${project.name()} will be shared in base module") - } - val supertypes = - Seq("MavenModule") ++ - Option.when(null != project.maven().pom()) { "PublishModule" } + val buildInfo = input._1 - val javacOptions = getJavacOptions(project) - val repos = getRepositories(project) - val pomSettings = extractPomSettings(project) - val publishVersion = getPublishVersion(project) - val publishProperties = getPublishProperties(project, cfg.shared) + val javacOptions = getJavacOptions(buildInfo) + val scalacOptions = buildInfo.scalacOptions + val repositories = getRepositories(buildInfo) + val pomSettings = extractPomSettings(buildInfo.buildPublicationInfo) + val publishVersion = getPublishVersion(buildInfo) val typedef = IrTrait( - cfg.shared.jvmId, + None, // There doesn't seem to be a Java version setting in sbt. See https://stackoverflow.com/a/76456295/5082913. baseModule, - supertypes, + sbtSupertypes, javacOptions, + scalacOptions, pomSettings, publishVersion, - publishProperties, - repos + null, // not available in sbt as it seems + repositories ) - IrBaseInfo(javacOptions, repos, pomSettings == null, publishVersion, Seq.empty, typedef) + IrBaseInfo( + javacOptions, + scalacOptions, + repositories, + noPom = false, // always publish + publishVersion, + Seq.empty, + typedef + ) } override def extractIrBuild( cfg: Config, baseInfo: IrBaseInfo, - build: Node[ProjectModel], + build: Node[Project], packages: Map[(String, String, String), String] ): IrBuild = { val project = build.value - val scopedDeps = extractScopedDeps(project, packages, cfg) - val version = getPublishVersion(project) + val buildInfo = project.buildInfo + val configurationDeps = extractConfigurationDeps(project, packages, cfg) + val version = getPublishVersion(buildInfo) IrBuild( - scopedDeps = scopedDeps, + scopedDeps = configurationDeps, testModule = cfg.shared.testModule, hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, - repositories = getRepositories(project).diff(baseInfo.repositories), - javacOptions = getJavacOptions(project).diff(baseInfo.javacOptions), - projectName = getArtifactId(project), - pomSettings = if (baseInfo.noPom) extractPomSettings(project) else null, + repositories = getRepositories(buildInfo).diff(baseInfo.repositories), + javacOptions = getJavacOptions(buildInfo).diff(baseInfo.javacOptions), + scalacOptions = buildInfo.scalacOptions.map(scalacOptions => + baseInfo.scalacOptions.fold(scalacOptions)(baseScalacOptions => + scalacOptions.diff(baseScalacOptions) + ) + ), + projectName = project.name, + pomSettings = + if (baseInfo.noPom) extractPomSettings(buildInfo.buildPublicationInfo) else null, publishVersion = if (version == baseInfo.publishVersion) null else version, - packaging = getPomPackaging(project), - // not available - pomParentArtifact = null, - // skipped, requires relatively new API (JavaPluginExtension.getSourceSets) + packaging = null, // not available in sbt as it seems + pomParentArtifact = null, // not available resources = Nil, testResources = Nil, - publishProperties = getPublishProperties(project, cfg.shared) + publishProperties = Nil // not available in sbt as it seems ) } def getModuleSupertypes(cfg: Config): Seq[String] = - Seq(cfg.shared.baseModule.getOrElse("MavenModule")) + cfg.shared.baseModule.fold(sbtSupertypes)(Seq(_)) - def getPackage(project: ProjectModel): (String, String, String) = { - (project.group(), project.name(), project.version()) + def getPackage(project: Project): (String, String, String) = { + val buildPublicationInfo = project.buildInfo.buildPublicationInfo + (buildPublicationInfo.organization.orNull, project.name, buildPublicationInfo.version.orNull) } - def getArtifactId(model: ProjectModel): String = model.name() + def getArtifactId(project: Project): String = project.name - def getMillSourcePath(model: ProjectModel): Path = os.Path(model.directory()) + def getMillSourcePath(project: Project): Path = os.Path(project.projectDirectory) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = { + def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Project]): Seq[String] = Seq("RootModule") ++ - Option.when(null != build.value.maven().pom() && baseInfo.noPom) { "PublishModule" } ++ Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { getModuleSupertypes(cfg) - }.toSeq.flatten - } + }.iterator.toSeq.flatten - def groupArtifactVersion(dep: JavaModel.Dep): (String, String, String) = - (dep.group(), dep.name(), dep.version()) + def groupArtifactVersion(dep: Dependency): (String, String, String) = + (dep.organization, dep.name, dep.revision) - def getJavacOptions(project: ProjectModel): Seq[String] = { - val _java = project._java() - if (null == _java) Seq.empty - else _java.javacOptions().asScala.toSeq - } + def getJavacOptions(buildInfo: BuildInfo): Seq[String] = + buildInfo.javacOptions.getOrElse(Seq.empty) - def getRepositories(project: ProjectModel): Seq[String] = - project.maven().repositories().asScala.toSeq.sorted.map(uri => - s"coursier.maven.MavenRepository(${escape(uri.toString)})" + def getRepositories(buildInfo: BuildInfo): Seq[String] = + buildInfo.resolvers.getOrElse(Seq.empty).map(resolver => + s"coursier.maven.MavenRepository(${escape(resolver.root)})" ) - def getPomPackaging(project: ProjectModel): String = { - val pom = project.maven().pom() - if (null == pom) null else pom.packaging() - } - - def getPublishProperties(project: ProjectModel, cfg: BuildGenUtil.Config): Seq[(String, String)] = - if (cfg.publishProperties.value) { - val pom = project.maven().pom() - if (null == pom) Seq.empty - else pom.properties().iterator().asScala - .map(prop => (prop.key(), prop.value())) - .toSeq - } else Seq.empty - - def getPublishVersion(project: ProjectModel): String = - project.version() match { - case "" | "unspecified" => null - case version => version - } + def getPublishVersion(buildInfo: BuildInfo): String = + buildInfo.buildPublicationInfo.version.orNull - def interpIvy(dep: JavaModel.Dep): String = { - BuildGenUtil.renderIvyString(dep.group(), dep.name(), dep.version()) + // originally named `ivyInterp` in the Maven and module + def renderIvy(dependency: Dependency): String = { + // type, classifier, and exclusions are not processed yet + import dependency.* + s"ivy\"$organization:$name${if (crossVersion) "::" else ":"}$revision\"" } - def extractPomSettings(project: ProjectModel): IrPom = { - val pom = project.maven.pom() - if (null == pom) null - else { - IrPom( - pom.description(), - project.group(), // Mill uses group for POM org - pom.url(), - licenses = pom.licenses().asScala - .map(lic => IrLicense(lic.name(), lic.name(), lic.url())) - .toSeq, - versionControl = Option(pom.scm()).fold(IrVersionControl(null, null, null, null))(scm => - IrVersionControl(scm.url(), scm.connection(), scm.devConnection(), scm.tag()) - ), - developers = pom.devs().asScala - .map(dev => IrDeveloper(dev.id(), dev.name(), dev.url(), dev.org(), dev.orgUrl())) - .toSeq - ) - } + def extractPomSettings(buildPublicationInfo: BuildPublicationInfo): IrPom = { + import buildPublicationInfo.* + // always publish + /* + if ( + Seq( + description, + homepage, + licenses, + organizationName, + organizationHomepage, + developers, + scmInfo + ).forall(_.isEmpty) + ) + null + else + */ + IrPom( + description.getOrElse(""), + organization.getOrElse(""), + organizationHomepage.fold("")(_.getOrElse("")), + licenses.getOrElse(Seq.empty).map(license => IrLicense(license._1, license._1, license._2)), + scmInfo.flatten.fold(IrVersionControl(null, null, null, null))(scmInfo => { + import scmInfo.* + IrVersionControl(browseUrl, connection, devConnection.orNull, null) + }), + developers.getOrElse(Seq.empty).map { developer => + import developer.* + IrDeveloper(id, name, url, null, null) + } + ) } - def extractScopedDeps( - project: ProjectModel, + def extractConfigurationDeps( + project: Project, packages: PartialFunction[(String, String, String), String], cfg: Config ): IrScopedDeps = { - var sd = IrScopedDeps() - val hasTest = os.exists(os.Path(project.directory()) / "src/test") - val _java = project._java() - if (null != _java) { - val ivyDep: JavaModel.Dep => String = - cfg.shared.depsObject.fold(interpIvy(_)) { objName => dep => - val depName = s"`${dep.group()}:${dep.name()}`" - sd = sd.copy(namedIvyDeps = sd.namedIvyDeps :+ (depName, interpIvy(dep))) - s"$objName.$depName" + // refactored to a functional approach from the original imperative code in Maven and Gradle + + val allDepsByConfiguration = project.allDependencies.groupBy(_.configurations match { + case None => Default + case Some(configuration) => configuration match { + case "compile" => Default + case "test" => Test + case "runtime" => Run + case "provided" | "optional" => Compile } + }) - def appendIvyDepPackage( - deps: IterableOnce[JavaModel.Dep], - onPackage: String => IrScopedDeps, - onIvy: (String, (String, String, String)) => IrScopedDeps - ): Unit = { - for (dep <- deps.iterator) { - val id = groupArtifactVersion(dep) - if (packages.isDefinedAt(id)) sd = onPackage(packages(id)) - else { - val ivy = ivyDep(dep) - sd = onIvy(ivy, id) - } - } - } - _java.configs().forEach { config => - import JavaPlugin.* - - val conf = config.name() - conf match { - case IMPLEMENTATION_CONFIGURATION_NAME | API_CONFIGURATION_NAME => - appendIvyDepPackage( - config.deps.asScala, - onPackage = v => sd.copy(mainModuleDeps = sd.mainModuleDeps + v), - onIvy = (v, id) => - if (isBom(id)) sd.copy(mainBomIvyDeps = sd.mainBomIvyDeps + v) - else sd.copy(mainIvyDeps = sd.mainIvyDeps + v) - ) - - case COMPILE_ONLY_CONFIGURATION_NAME | COMPILE_ONLY_API_CONFIGURATION_NAME => + case class Deps[I, M](ivy: Seq[I], module: Seq[M]) - appendIvyDepPackage( - config.deps.asScala, - onPackage = v => sd.copy(mainCompileModuleDeps = sd.mainCompileModuleDeps + v), - onIvy = (v, id) => sd.copy(mainCompileIvyDeps = sd.mainCompileIvyDeps + v) - ) - - case RUNTIME_ONLY_CONFIGURATION_NAME => - appendIvyDepPackage( - config.deps.asScala, - onPackage = v => sd.copy(mainRunModuleDeps = sd.mainRunModuleDeps + v), - onIvy = (v, id) => sd.copy(mainRunIvyDeps = sd.mainRunIvyDeps + v) - ) - - case TEST_IMPLEMENTATION_CONFIGURATION_NAME => - - appendIvyDepPackage( - config.deps.asScala, - onPackage = v => sd.copy(testModuleDeps = sd.testModuleDeps + v), - onIvy = (v, id) => - if (isBom(id)) sd.copy(testBomIvyDeps = sd.testBomIvyDeps + v) - else sd.copy(testIvyDeps = sd.testIvyDeps + v) - ) - config.deps.forEach { dep => - if (hasTest && sd.testModule.isEmpty) { - sd = sd.copy(testModule = testModulesByGroup.get(dep.group())) - } - } - - case TEST_COMPILE_ONLY_CONFIGURATION_NAME => - appendIvyDepPackage( - config.deps.asScala, - onPackage = v => sd.copy(testCompileModuleDeps = sd.testCompileModuleDeps + v), - onIvy = (v, id) => sd.copy(testCompileIvyDeps = sd.testCompileIvyDeps + v) + // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. + val ivyAndModuleDepsByConfiguration: Map[IrDependencyType, Deps[Dependency, String]] = + allDepsByConfiguration.view.mapValues(deps => { + val tuple2 = deps.partitionMap(dep => { + val id = groupArtifactVersion(dep) + if (packages.isDefinedAt(id)) Right(packages(id)) + else Left(dep) + }) + Deps(tuple2._1, tuple2._2) + }).toMap + + val testIvyAndModuleDeps = ivyAndModuleDepsByConfiguration.get(Test) + val testIvyDeps = testIvyAndModuleDeps.map(_.ivy) + val hasTest = os.exists(os.Path(project.projectDirectory) / "src/test") + val testModule = Option.when(hasTest)( + testIvyDeps.flatMap(_.collectFirst(Function.unlift(dep => + testModulesByGroup.get(dep.organization) + ))) + ).flatten + + cfg.shared.depsObject.fold({ + val default = ivyAndModuleDepsByConfiguration.get(Default) + val compile = ivyAndModuleDepsByConfiguration.get(Compile) + val run = ivyAndModuleDepsByConfiguration.get(Run) + val test = testIvyAndModuleDeps + IrScopedDeps( + Seq.empty, + SortedSet.empty, + // Using `fold` here causes issues with type inference. + SortedSet.from(default.map(_.ivy.iterator.map(renderIvy)).getOrElse(Iterator.empty)), + SortedSet.from(default.map(_.module).getOrElse(Seq.empty)), + SortedSet.from(compile.map(_.ivy.iterator.map(renderIvy)).getOrElse(Iterator.empty)), + SortedSet.from(compile.map(_.module).getOrElse(Seq.empty)), + SortedSet.from(run.map(_.ivy.iterator.map(renderIvy)).getOrElse(Iterator.empty)), + SortedSet.from(run.map(_.module).getOrElse(Seq.empty)), + testModule, + SortedSet.empty, + SortedSet.from(testIvyDeps.map(_.iterator.map(renderIvy)).getOrElse(Iterator.empty)), + SortedSet.from(test.map(_.module).getOrElse(Seq.empty)), + SortedSet.empty, + SortedSet.empty + ) + })(objectName => { + // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. + val extractedIvyAndModuleDepsByConfiguration + : Map[IrDependencyType, Deps[((String, String), String), String]] = + ivyAndModuleDepsByConfiguration.view.mapValues({ + case Deps(ivy, module) => + Deps( + ivy.map(dep => { + val depName = s"`${dep.organization}:${dep.name}`" + ((depName, renderIvy(dep)), s"$objectName.$depName") + }), + module ) - - case name => - config.deps.forEach { dep => - val id = groupArtifactVersion(dep) - println(s"ignoring $name dependency $id") - } - } - } - } - sd + }).toMap + + val default = extractedIvyAndModuleDepsByConfiguration.get(Default) + val compile = extractedIvyAndModuleDepsByConfiguration.get(Compile) + val run = extractedIvyAndModuleDepsByConfiguration.get(Run) + val test = extractedIvyAndModuleDepsByConfiguration.get(Test) + IrScopedDeps( + extractedIvyAndModuleDepsByConfiguration.values.flatMap(_.ivy.iterator.map(_._1)).toSeq, + SortedSet.empty, + SortedSet.from(default.map(_.ivy.iterator.map(_._2)).getOrElse(Iterator.empty)), + SortedSet.from(default.map(_.module).getOrElse(Seq.empty)), + SortedSet.from(compile.map(_.ivy.iterator.map(_._2)).getOrElse(Iterator.empty)), + SortedSet.from(compile.map(_.module).getOrElse(Seq.empty)), + SortedSet.from(run.map(_.ivy.iterator.map(_._2)).getOrElse(Iterator.empty)), + SortedSet.from(run.map(_.module).getOrElse(Seq.empty)), + testModule, + SortedSet.empty, + SortedSet.from(test.map(_.ivy.iterator.map(_._2)).getOrElse(Iterator.empty)), + SortedSet.from(test.map(_.module).getOrElse(Seq.empty)), + SortedSet.empty, + SortedSet.empty + ) + }) } - */ @main @mill.api.internal case class Config( - shared: BuildGenUtil.Config + shared: BuildGenUtil.BasicConfig ) } diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill new file mode 100644 index 00000000000..7cbc35ca8e5 --- /dev/null +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -0,0 +1,29 @@ +package build + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule with SbtModule with PublishModule { + + def artifactName = "Scala Seed Project" + + def ivyDeps = super.ivyDeps() ++ + Agg(ivy"org.scala-lang:scala-library:2.13.12") + + def pomSettings = PomSettings( + "Scala Seed Project", + "", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + object test extends MavenTests with TestModule.Munit { + + def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalameta:munit::0.7.29") + + } +} diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index ee5f9bcc7d0..5331e959f9d 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -12,7 +12,7 @@ object BuildGenTests extends TestSuite { val sourceRoot = os.sub / "scala-seed-project" val expectedRoot = os.sub / "expected/scala-seed-project" assert( - checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) + checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot, true) ) } From 8b141aa11153c8987ba655244a57327d89d8c805 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 15 Feb 2025 21:08:14 +0800 Subject: [PATCH 11/70] Remove the unnecessary code marked with TODOs to be removed in the previous commit, adjust the format of some code, and inline an sbt task --- .../sbt/models/src/mill/main/sbt/Models.scala | 172 +------------- .../mill/main/sbt/ExportBuildPlugin.scala | 218 ++++-------------- 2 files changed, 48 insertions(+), 342 deletions(-) diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 6c39a33bf5f..23b608d227e 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -7,32 +7,6 @@ import upickle.default.{macroRW, ReadWriter => RW} object Models { type URL = String - - // TODO Remove. This seems not supported. - - type Id[X] = X // not working with uPickle - /*object Id { - //implicit def rw[T](tRw : RW[T]) : RW[Id[T]] = tRw - implicit def rw[T: RW]: RW[Id[T]] = macroRW - }*/ -} - -// TODO Remove. Not used. -/** - * A wrapper added because [[Models.Id]] does not work with uPickle. - * @param wrappedValue named this way in order not to conflict with [[sbt.std.MacroValue.value]] - */ -case class Wrapper[+A](wrappedValue: A) extends AnyVal { - def map[B](f: A => B): Wrapper[B] = Wrapper(f(wrappedValue)) -} -object Wrapper { - import scala.language.implicitConversions - implicit def to[A](value: A): Wrapper[A] = - Wrapper(value) - implicit def from[A](wrapper: Wrapper[A]): A = - wrapper.wrappedValue - - implicit def rw[T: RW]: RW[Wrapper[T]] = macroRW } case class BuildExport( @@ -44,136 +18,6 @@ object BuildExport { implicit val rw: RW[BuildExport] = macroRW } -// TODO remove these plain ones - -case class PlainBuildInfo( - buildPublicationInfo: PlainBuildPublicationInfo, - /** @see [[Keys.javacOptions]] */ - javacOptions: Seq[String], - /** @see [[Keys.resolvers]] */ - resolvers: Seq[Resolver] -) -object PlainBuildInfo { - implicit val rw: RW[PlainBuildInfo] = macroRW -} - -case class PlainBuildPublicationInfo( - /** @see [[Keys.description]] */ - description: String, - /** - * corresponds to `url` in POM - * - * @see [[Keys.homepage]] - */ - homepage: Option[String], - /** @see [[Keys.licenses]] */ - licenses: Seq[License], - // organization: Option[String], - /* - /** - * corresponds to Maven's `organization` in POM - * - * @see [[Keys.organization]] - */ - organizationName: String, - */ - /** - * corresponds to `organizationUrl` in POM - * - * @see [[Keys.organizationHomepage]] - */ - organizationHomepage: Option[String], - /** @see [[Keys.developers]] */ - developers: Seq[Developer], - /** @see [[Keys.scmInfo]] */ - scmInfo: Option[ScmInfo] -) -object PlainBuildPublicationInfo { - implicit val rw: RW[PlainBuildPublicationInfo] = macroRW - - /** @see [[sbt.librarymanagement.License]] */ - type License = (String, URL) -} - -/* -TODO I used higher-kinded types here along with some higher-rank types (`GetValueFn` and `MapFn`) in the sbt plugin, - because I first thought that the default build info's members don't need to be wrapped with `Option` while the project build info's members do, - and we can reuse some more code this way, - but it turns out that both kinds have to be wrapped with `Option` and the abstraction doesn't work with the sbt `value` macros. - Most likely we can just replace `F` with `Option` in them and remove the `TC` suffixes, - resulting in `mill.main.sbt.BuildInfo` below, - unless they can possibly provide more extensibility, which is unlikely as I think now. - See other related definitions such as `Id`, `Wrapper`, - `getBuildInfo[F[_]](ref: Reference, getValue: GetValueFn[F], map: MapFn[F]): BuildInfoTC[F]`, `GetValueFn`, and `MapFn`. - */ - -case class BuildInfoTC[F[_]]( - buildPublicationInfo: BuildPublicationInfoTC[F], - /** @see [[Keys.javacOptions]] */ - javacOptions: F[Seq[String]], - /** @see [[Keys.scalacOptions]] */ - scalacOptions: F[Seq[String]], - /** @see [[Keys.resolvers]] */ - resolvers: F[Seq[Resolver]] -) -object BuildInfoTC { - type BuildInfo = BuildInfoTC[Wrapper] - implicit val wrapperRw: RW[BuildInfo] = macroRW - type OptionBuildInfo = BuildInfoTC[Option] - implicit val optionRw: RW[OptionBuildInfo] = macroRW -} - -case class BuildPublicationInfoTC[F[_]]( - /** @see [[Keys.description]] */ - description: F[String], - /** - * corresponds to `url` in POM - * - * @see [[Keys.homepage]] - */ - homepage: F[Option[String]], - /** @see [[Keys.licenses]] */ - licenses: F[Seq[PlainBuildPublicationInfo.License]], - // organization: Option[String], - /* - /** - * corresponds to Maven's `organization` in POM - * - * @see [[Keys.organization]] - */ - organizationName: F[String], - */ - /** - * corresponds to `organizationUrl` in POM - * - * @see [[Keys.organizationHomepage]] - */ - organizationHomepage: F[Option[String]], - /** @see [[Keys.developers]] */ - developers: F[Seq[Developer]], - /** @see [[Keys.scmInfo]] */ - scmInfo: F[Option[ScmInfo]] -) -object BuildPublicationInfoTC { - - /** @see [[sbt.librarymanagement.License]] */ - type License = (String, URL) - - /* - // This does not work: "could not find implicit value for parameter e: upickle.default.Reader[F[String]]". - implicit def rw[F[_]]: RW[BuildPublicationInfoTC[F]] = macroRW - */ - /* - // This does not work: "could not find implicit value for parameter e: upickle.default.Reader[mill.main.sbt.Models.Id[Seq[mill.main.sbt.BuildPublicationInfo.License]]]". - type BuildPublicationInfo = BuildPublicationInfoTC[Id] - implicit val IdRw: RW[BuildPublicationInfo] = macroRW - */ - type BuildPublicationInfo = BuildPublicationInfoTC[Wrapper] - implicit val wrapperRw: RW[BuildPublicationInfo] = macroRW - type OptionBuildPublicationInfo = BuildPublicationInfoTC[Option] - implicit val optionRw: RW[OptionBuildPublicationInfo] = macroRW -} - case class BuildInfo( buildPublicationInfo: BuildPublicationInfo, /** @see [[Keys.javacOptions]] */ @@ -200,7 +44,7 @@ case class BuildPublicationInfo( */ homepage: Option[Option[String]], /** @see [[Keys.licenses]] */ - licenses: Option[Seq[PlainBuildPublicationInfo.License]], + licenses: Option[Seq[License]], /** * corresponds to `groupId` in POM and Mill's `PomSettings.organization` * @@ -273,24 +117,10 @@ case class Project( // dirs: ProjectDirs, // relative projectDirectory: String, buildInfo: BuildInfo, - // dependencyConfigurations: Seq[DependencyConfiguration] // TODO remove allDependencies: Seq[Dependency] ) object Project { implicit val rw: RW[Project] = macroRW - - // TODO Remove. Not used. - type ProjectDirs = Seq[String] -} - -// TODO Remove. For an old implementation of retrieving `Configuration / allDependencies` that doesn't work. -case class DependencyConfiguration( - id: String, - allDependencies: Seq[Dependency] - /*, projectDependencies: Seq[ProjectDirs], libraryDependencies: Seq[LibraryDependency]*/ -) -object DependencyConfiguration { - implicit val rw: RW[DependencyConfiguration] = macroRW } case class Dependency( diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index 64c6e71d4c4..bff727ea561 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -1,7 +1,6 @@ package mill.main.sbt import sbt.Keys.* -import sbt.Project.extract import sbt.io.IO import sbt.librarymanagement.Disabled import sbt.{Def, Developer as _, Project as _, Resolver as _, ScmInfo as _, *} @@ -18,55 +17,12 @@ object ExportBuildPlugin extends AutoPlugin { val millInitBuildInfo = taskKey[BuildInfo]( "get the `mill.main.sbt.BuildInfo` model of this build or this project" ) - // TODO Remove. No longer used. - val millInitDependencyConfiguration = taskKey[DependencyConfiguration]( - "get the `mill.main.sbt.DependencyConfiguration` model of this project and configuration" - ) - val millInitAllDependencies = taskKey[Seq[Dependency]]( - "get the all the `mill.main.sbt.Dependency`s of this project" - ) + // not used anymore + val millInitAllDependencies = taskKey[Seq[Dependency]]("get the all the `mill.main.sbt.Dependency`s of this project") val millInitProject = taskKey[Project]("get the `mill.main.sbt.Project` model of this project") val millInitExportBuild = taskKey[File]("export the build in a JSON file for `mill init`") } - // TODO `getBuildInfo` along with `MapFn` and `GetValueFn` does not work with the `value` macro. Remove. - - trait MapFn[F[_]] { - // def apply[A, B](fa : F[A], f: A => B): F[B] // Type inference doesn't work well with this. - def apply[A, B](fa: F[A])(f: A => B): F[B] - } - - trait GetValueFn[F[_]] { - def apply[T](key: SettingKey[T]): F[T] - - def apply[T](key: TaskKey[T]): F[T] - } - - def getBuildInfo[F[_]](ref: Reference, getValue: GetValueFn[F], map: MapFn[F]): BuildInfoTC[F] = - BuildInfoTC( - BuildPublicationInfoTC( - getValue(ref / description), - map(getValue(ref / homepage))(_.map(_.toExternalForm)), - map(getValue(ref / licenses))(_.map { case (name, url) => (name, url.toExternalForm) }), - //getValue(ref / organizationName), // not needed - map(getValue(ref / organizationHomepage))(_.map(_.toExternalForm)), - map(getValue(ref / developers))(_.map(developer => - Developer(developer.id, developer.name, developer.email, developer.url.toExternalForm) - )), - map(getValue(ref / scmInfo))(_.map(scmInfo => - ScmInfo(scmInfo.browseUrl.toExternalForm, scmInfo.connection, scmInfo.devConnection) - )) - ), - getValue(ref / javacOptions), - getValue(ref / scalacOptions), - map(getValue(ref / resolvers))(_.flatMap { - case mavenRepository: MavenRepository => Some(Resolver(mavenRepository.root)) - case resolver => - println(s"A `Resolver` which is not a `MavenRepository` is skipped: $resolver") - None - }) - ) - import autoImport.* val buildInfoSetting = millInitBuildInfo := BuildInfo( @@ -101,38 +57,41 @@ object ExportBuildPlugin extends AutoPlugin { buildInfoSetting ) - // TODO Remove. For an old implementation that depends on retrieving `configuration / allDependencies` which doesn't work. - private val neededConfigurations = Seq(Compile, Test, Runtime, Default, Provided, Optional) - - // TODO Remove. No longer used. Replaced by `millInitAllDependencies`. - val millInitDependencyConfigurationSetting = - millInitDependencyConfiguration := DependencyConfiguration( - configuration.value.id, - - /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ - /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => - Dependency( - moduleID.organization, - moduleID.name, - moduleID.crossVersion match { - case Disabled => false - case _: Binary => true - case crossVersion => - println(s"Unsupported `CrossVersion`: $crossVersion") - false + override lazy val projectSettings: Seq[Setting[?]] = Seq( + buildInfoSetting, + millInitProject := + Project( + //organization.value, + name.value, + //version.value, + //baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), + baseDirectory.value.getPath, + { + // keep the project `BuildInfo` members only when they are different + val defaultBi = (ThisBuild / millInitBuildInfo).value + val projectBi = millInitBuildInfo.value + import projectBi.* + BuildInfo({ + val defaultBpi = defaultBi.buildPublicationInfo + import projectBi.buildPublicationInfo.* + BuildPublicationInfo( + if (description != defaultBpi.description) description else None, + if (homepage != defaultBpi.homepage) homepage else None, + if (licenses != defaultBpi.licenses) licenses else None, + if (organization != defaultBpi.organization) organization else None, + //if (organizationName != defaultBpi.organizationName) organizationName else None, // not needed + if (organizationHomepage != defaultBpi.organizationHomepage) organizationHomepage else None, + if (developers != defaultBpi.developers) developers else None, + if (scmInfo != defaultBpi.scmInfo) scmInfo else None, + if (version != defaultBpi.version) version else None + ) }, - moduleID.revision, - moduleID.configurations - ) - } - ) + if (javacOptions != defaultBi.javacOptions) javacOptions else None, + if (scalacOptions != defaultBi.scalacOptions) scalacOptions else None, + if (resolvers != defaultBi.resolvers) resolvers else None + ) + }, - override lazy val projectSettings: Seq[Setting[?]] = Seq( - Seq(buildInfoSetting), - // TODO Remove. For an old implementation that depends on `configuration / allDependencies` which doesn't work. - neededConfigurations.flatMap(configuration => inConfig(configuration)(Seq(millInitDependencyConfigurationSetting))), - Seq( - millInitAllDependencies := { /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => Dependency( @@ -149,99 +108,16 @@ object ExportBuildPlugin extends AutoPlugin { moduleID.configurations ) } - }, - millInitProject := { - // TODO remove - val extracted = extract(state.value) // TODO move to a separate task if used - Project( - //organization.value, - name.value, - //version.value, - //baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), - baseDirectory.value.getPath, - // TODO remove old implementation - /* - getBuildInfo[Option]( - ThisProject, - new GetValueFn[Option] { - override def apply[T](key: SettingKey[T]): Option[T] = extracted.getOpt(key) - - override def apply[T](key: TaskKey[T]): Option[T] = - ??? // extracted.getOpt(key).map(_.value) - }, - new MapFn[Option] { - override def apply[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f) - } - ) - */ - { - // keep the project `BuildInfo` members only when they are different - val defaultBi = (ThisBuild / millInitBuildInfo).value - val projectBi = millInitBuildInfo.value - import projectBi.* - BuildInfo({ - val defaultBpi = defaultBi.buildPublicationInfo - import projectBi.buildPublicationInfo.* - BuildPublicationInfo( - if (description != defaultBpi.description) description else None, - if (homepage != defaultBpi.homepage) homepage else None, - if (licenses != defaultBpi.licenses) licenses else None, - if (organization != defaultBpi.organization) organization else None, - //if (organizationName != defaultBpi.organizationName) organizationName else None, // not needed - if (organizationHomepage != defaultBpi.organizationHomepage) organizationHomepage else None, - if (developers != defaultBpi.developers) developers else None, - if (scmInfo != defaultBpi.scmInfo) scmInfo else None, - if (version != defaultBpi.version) version else None - ) - }, - if (javacOptions != defaultBi.javacOptions) javacOptions else None, - if (scalacOptions != defaultBi.scalacOptions) scalacOptions else None, - if (resolvers != defaultBi.resolvers) resolvers else None - ) - }, - { - // TODO Remove. For an old implementation that depends on retrieving `configuration / allDependencies` which doesn't work. - millInitDependencyConfiguration.all( - ScopeFilter(configurations = inConfigurations(neededConfigurations *)) - ).value - millInitAllDependencies.value - } - ) - }, - // `target.value` doesn't work in `globalSettings` and `buildSettings`, so this is added to `projectSettings. - millInitExportBuild := { - // TODO This does not work with the `value` macro. Remove. - /* - val defaultBuildInfo = getBuildInfo[Wrapper]( - ThisBuild, - new GetValueFn[Wrapper] { - override def apply[T](key: SettingKey[T]): Wrapper[T] = - ??? // Wrapper(key.value) // Could not find proxy - override def apply[T](key: TaskKey[T]): Wrapper[T] = - ??? // Wrapper(key.value) // Could not find proxy - }, - new MapFn[Wrapper] { - override def apply[A, B](fa: Wrapper[A])(f: A => B): Wrapper[B] = fa.map(f) - } - ) - */ - - val defaultBuildInfo = (ThisBuild / millInitBuildInfo).value - - // TODO remove the old implementation code - // `(projectRef / ...)` can't be used directly here due to macro limitations: "java.lang.IllegalArgumentException: Could not find proxy for projectRef: sbt.ProjectRef in ..." - /* - val allProjectPairs = buildStructure.value.allProjectPairs - val projects = allProjectPairs.map { case (resolvedProject, projectRef) => ??? } - */ - - val projects = millInitProject.all(ScopeFilter(inAnyProject)).value - - val buildExport = BuildExport(defaultBuildInfo, projects) - - val outputFile = target.value / "mill-init-build-export.json" - IO.write(outputFile, write(buildExport)) - outputFile - } - )).flatten + ), + // `target.value` doesn't work in `globalSettings` and `buildSettings`, so this is added to `projectSettings. + millInitExportBuild := { + val defaultBuildInfo = (ThisBuild / millInitBuildInfo).value + val projects = millInitProject.all(ScopeFilter(inAnyProject)).value + val buildExport = BuildExport(defaultBuildInfo, projects) + + val outputFile = target.value / "mill-init-build-export.json" + IO.write(outputFile, write(buildExport)) + outputFile + } + ) } From 1fd0b652cffe8d14d2eebc9b652624495be5550a Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 15:41:32 +0800 Subject: [PATCH 12/70] Do not diff a subproject's `BuildInfo` with `ThisBuild`'s anymore in `ExportBuildPlugin` because its members are always needed when there is no `baseModule` * Add a `takeIrPomIfNeeded` function to also set a module's `PomSettings` when it's different from a base module's `PomSettings`. * Regenerate and update "test/resources/expected/scala-seed-project/build.mill". --- main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala | 3 +++ .../src/main/scala/mill/main/sbt/ExportBuildPlugin.scala | 5 +++-- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 3 +-- .../test/resources/expected/scala-seed-project/build.mill | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index 9a879a590e4..cb140f386e3 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -47,6 +47,9 @@ object BuildGenUtil { } + def takeIrPomIfNeeded(baseInfo: IrBaseInfo, irPom: IrPom): IrPom = + if (baseInfo.noPom || irPom != baseInfo.moduleTypedef.pomSettings) irPom else null + def renderIrPom(value: IrPom): String = { if (value == null) "" else { diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index bff727ea561..04f70b17633 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -66,7 +66,7 @@ object ExportBuildPlugin extends AutoPlugin { //version.value, //baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), baseDirectory.value.getPath, - { + /*{ // keep the project `BuildInfo` members only when they are different val defaultBi = (ThisBuild / millInitBuildInfo).value val projectBi = millInitBuildInfo.value @@ -90,7 +90,8 @@ object ExportBuildPlugin extends AutoPlugin { if (scalacOptions != defaultBi.scalacOptions) scalacOptions else None, if (resolvers != defaultBi.resolvers) resolvers else None ) - }, + }*/ + millInitBuildInfo.value, /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 4977a3168d0..5c6390c9860 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -197,8 +197,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ) ), projectName = project.name, - pomSettings = - if (baseInfo.noPom) extractPomSettings(buildInfo.buildPublicationInfo) else null, + pomSettings = takeIrPomIfNeeded(baseInfo, extractPomSettings(buildInfo.buildPublicationInfo)), publishVersion = if (version == baseInfo.publishVersion) null else version, packaging = null, // not available in sbt as it seems pomParentArtifact = null, // not available diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index 7cbc35ca8e5..c992176f2b5 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -14,13 +14,15 @@ object `package` extends RootModule with SbtModule with PublishModule { def pomSettings = PomSettings( "Scala Seed Project", - "", + "com.example", "", Seq(), VersionControl(None, None, None, None), Seq() ) + def publishVersion = "0.1.0-SNAPSHOT" + object test extends MavenTests with TestModule.Munit { def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalameta:munit::0.7.29") From f38991b2dff60f8f2741f3abc5b403e6f771aa5f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 15:53:35 +0800 Subject: [PATCH 13/70] Move the "mill.scalalib.SbtModule" import into `SbtBuildGenMain` to fix the tests `./mill main.init.maven.test` and `./mill main.init.gradle.test` which were broken --- .../buildgen/src/mill/main/buildgen/BuildGenBase.scala | 4 +++- .../buildgen/src/mill/main/buildgen/BuildGenUtil.scala | 7 ++++--- .../gradle/src/mill/main/gradle/GradleBuildGenMain.scala | 2 ++ .../init/maven/src/mill/main/maven/MavenBuildGenMain.scala | 2 ++ main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 2 ++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 261e5d58615..964d1f712fd 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -35,7 +35,7 @@ trait BuildGenBase[M, D, I] { val isNested = build.dirs.nonEmpty build.copy(value = BuildObject( - imports = BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size), + imports = BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size, extraImports), companions = shared.depsObject.fold(SortedMap.empty[String, BuildObject.Constants])(name => SortedMap((name, SortedMap(inner.scopedDeps.namedIvyDeps.toSeq *))) @@ -50,6 +50,8 @@ trait BuildGenBase[M, D, I] { } } + def extraImports: Seq[String] + def getSuperTypes(cfg: C, baseInfo: IrBaseInfo, build: Node[M]): Seq[String] def getBaseInfo( diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index cb140f386e3..0a84560c2e0 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -132,14 +132,15 @@ object BuildGenUtil { def renderImports( baseModule: Option[String], isNested: Boolean, - packagesSize: Int + packagesSize: Int, + extraImports: Seq[String] ): SortedSet[String] = { scala.collection.immutable.SortedSet( "mill._", "mill.javalib._", - "mill.javalib.publish._", - "mill.scalalib.SbtModule" + "mill.javalib.publish._" ) ++ + extraImports ++ (if (isNested) baseModule.map(name => s"$$file.$name") else if (packagesSize > 1) Seq("$packages._") else None) diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index 2cfdb7fdeaf..e6bf7b9dd33 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -183,6 +183,8 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod ) } + override def extraImports: Seq[String] = Seq.empty + def getModuleSupertypes(cfg: Config): Seq[String] = Seq(cfg.shared.basicConfig.baseModule.getOrElse("MavenModule")) diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index 7edc0add46a..abfe12ae98f 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -128,6 +128,8 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe ) } + override def extraImports: Seq[String] = Seq.empty + def getModuleSupertypes(cfg: Config): Seq[String] = Seq("PublishModule", "MavenModule") def getPackage(model: Model): (String, String, String) = { diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 5c6390c9860..b610866f252 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -207,6 +207,8 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ) } + override def extraImports: Seq[String] = Seq("mill.scalalib.SbtModule") + def getModuleSupertypes(cfg: Config): Seq[String] = cfg.shared.baseModule.fold(sbtSupertypes)(Seq(_)) From f70e81805c267c53f48c8afabfde2af4438b8c26 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 16:31:13 +0800 Subject: [PATCH 14/70] Export and import sbt's `scalaVersion` too --- .../buildgen/src/mill/main/buildgen/BuildGenUtil.scala | 7 +++++++ main/init/buildgen/src/mill/main/buildgen/ir.scala | 3 +++ main/init/sbt/models/src/mill/main/sbt/Models.scala | 2 ++ .../src/main/scala/mill/main/sbt/ExportBuildPlugin.scala | 1 + main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 8 ++++++-- .../test/resources/expected/scala-seed-project/build.mill | 2 ++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index 0a84560c2e0..8ad38581ad2 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -32,6 +32,8 @@ object BuildGenUtil { | |${renderJavacOptions(javacOptions)} | + |${renderScalaVersion(scalaVersion)} + | |${renderScalacOptions(scalacOptions)} | |${renderPomSettings(renderIrPom(pomSettings))} @@ -88,6 +90,8 @@ object BuildGenUtil { | |${renderJavacOptions(javacOptions)} | + |${renderScalaVersion(scalaVersion)} + | |${renderScalacOptions(scalacOptions)} | |${renderRepositories(repositories)} @@ -392,6 +396,9 @@ object BuildGenUtil { args.iterator.map(escape) ) + def renderScalaVersion(scalaVersion: Option[String]): String = + scalaVersion.fold("")(scalaVersion => s"def scalaVersion = ${escape(scalaVersion)}") + def renderScalacOptions(args: Option[IterableOnce[String]]): String = args.fold("")(args => optional( diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index d835498b66c..c41c272ec5b 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -41,6 +41,7 @@ case class IrTrait( baseModule: String, moduleSupertypes: Seq[String], javacOptions: Seq[String], + scalaVersion: Option[String], scalacOptions: Option[Seq[String]], pomSettings: IrPom, publishVersion: String, @@ -84,6 +85,7 @@ case class IrBuild( dirs: Seq[String], repositories: Seq[String], javacOptions: Seq[String], + scalaVersion : Option[String], scalacOptions: Option[Seq[String]], projectName: String, pomSettings: IrPom, @@ -115,6 +117,7 @@ case class IrScopedDeps( case class IrBaseInfo( javacOptions: Seq[String] = Nil, + scalaVersion : Option[String] = None, scalacOptions : Option[Seq[String]] = None, repositories: Seq[String] = Nil, noPom: Boolean = true, diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 23b608d227e..ae6a982ebac 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -22,6 +22,8 @@ case class BuildInfo( buildPublicationInfo: BuildPublicationInfo, /** @see [[Keys.javacOptions]] */ javacOptions: Option[Seq[String]], + /** @see [[Keys.scalaVersion]] */ + scalaVersion: Option[String], /** @see [[Keys.scalacOptions]] */ scalacOptions: Option[Seq[String]], /** @see [[Keys.resolvers]] */ diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index 04f70b17633..f80705477e1 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -44,6 +44,7 @@ object ExportBuildPlugin extends AutoPlugin { version.?.value ), javacOptions.?.value, + scalaVersion.?.value, scalacOptions.?.value, resolvers.?.value.map(_.flatMap { case mavenRepository: MavenRepository => Some(Resolver(mavenRepository.root)) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index b610866f252..277e9edc176 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -145,10 +145,10 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ): IrBaseInfo = { val buildInfo = input._1 + import buildInfo.* val javacOptions = getJavacOptions(buildInfo) - val scalacOptions = buildInfo.scalacOptions val repositories = getRepositories(buildInfo) - val pomSettings = extractPomSettings(buildInfo.buildPublicationInfo) + val pomSettings = extractPomSettings(buildPublicationInfo) val publishVersion = getPublishVersion(buildInfo) val typedef = IrTrait( @@ -156,6 +156,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No baseModule, sbtSupertypes, javacOptions, + scalaVersion, scalacOptions, pomSettings, publishVersion, @@ -165,6 +166,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No IrBaseInfo( javacOptions, + scalaVersion, scalacOptions, repositories, noPom = false, // always publish @@ -191,6 +193,8 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No dirs = build.dirs, repositories = getRepositories(buildInfo).diff(baseInfo.repositories), javacOptions = getJavacOptions(buildInfo).diff(baseInfo.javacOptions), + scalaVersion = + if (buildInfo.scalaVersion != baseInfo.scalaVersion) buildInfo.scalaVersion else null, scalacOptions = buildInfo.scalacOptions.map(scalacOptions => baseInfo.scalacOptions.fold(scalacOptions)(baseScalacOptions => scalacOptions.diff(baseScalacOptions) diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index c992176f2b5..de2f48bf26e 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -9,6 +9,8 @@ object `package` extends RootModule with SbtModule with PublishModule { def artifactName = "Scala Seed Project" + def scalaVersion = "2.13.12" + def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scala-lang:scala-library:2.13.12") From 7e1e2b6b8e23b18cc8fce3a51f5d79dde27bd1c3 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 16:52:53 +0800 Subject: [PATCH 15/70] Exclude the Scala Standard Libraries --- .../src/mill/main/sbt/SbtBuildGenMain.scala | 25 ++++++++++++------- .../expected/scala-seed-project/build.mill | 3 --- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 277e9edc176..5e42570af98 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -286,6 +286,10 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ) } + private def isScalaStandardLibrary(dep: Dependency) = + Seq("ch.epfl.lamp", "org.scala-lang").contains(dep.organization) && + Seq("scala-library", "dotty-library", "scala3-library").contains(dep.name) + def extractConfigurationDeps( project: Project, packages: PartialFunction[(String, String, String), String], @@ -293,15 +297,18 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ): IrScopedDeps = { // refactored to a functional approach from the original imperative code in Maven and Gradle - val allDepsByConfiguration = project.allDependencies.groupBy(_.configurations match { - case None => Default - case Some(configuration) => configuration match { - case "compile" => Default - case "test" => Test - case "runtime" => Run - case "provided" | "optional" => Compile - } - }) + val allDepsByConfiguration = project.allDependencies + // .view // This makes the types hard to deal with here thus commented out. + .filterNot(isScalaStandardLibrary) + .groupBy(_.configurations match { + case None => Default + case Some(configuration) => configuration match { + case "compile" => Default + case "test" => Test + case "runtime" => Run + case "provided" | "optional" => Compile + } + }) case class Deps[I, M](ivy: Seq[I], module: Seq[M]) diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index de2f48bf26e..27e391369ee 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -11,9 +11,6 @@ object `package` extends RootModule with SbtModule with PublishModule { def scalaVersion = "2.13.12" - def ivyDeps = super.ivyDeps() ++ - Agg(ivy"org.scala-lang:scala-library:2.13.12") - def pomSettings = PomSettings( "Scala Seed Project", "com.example", From 6f4c45dd336ec30827a33361b71555a5777b2322 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 17:23:30 +0800 Subject: [PATCH 16/70] Add the https://github.com/pbassiner/sbt-multi-project-example project to the test resources Both `./mill main.init.sbt.test` and running `sbt` in the project directory fail with: ``` java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release ``` This is likely because I am using JDK 21. --- .../sbt-multi-project-example/LICENSE | 21 +++ .../sbt-multi-project-example/README.md | 23 +++ .../sbt-multi-project-example/build.sbt | 139 ++++++++++++++++++ .../common/src/main/scala/Model.scala | 2 + .../common/src/test/scala/Test.scala | 6 + .../multi1/src/main/scala/Main.scala | 11 ++ .../multi1/src/test/scala/Test.scala | 13 ++ .../multi2/src/main/scala/Main.scala | 11 ++ .../multi2/src/test/scala/Test.scala | 14 ++ .../project/build.properties | 1 + .../project/plugins.sbt | 12 ++ .../src/mill/main/sbt/BuildGenTests.scala | 11 +- 12 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/LICENSE create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/README.md create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/build.sbt create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/common/src/main/scala/Model.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/common/src/test/scala/Test.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/main/scala/Main.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/test/scala/Test.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/main/scala/Main.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/test/scala/Test.scala create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties create mode 100644 main/init/sbt/test/resources/sbt-multi-project-example/project/plugins.sbt diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/LICENSE b/main/init/sbt/test/resources/sbt-multi-project-example/LICENSE new file mode 100644 index 00000000000..b54a8a16e99 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-2018 Pol Bassiner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/README.md b/main/init/sbt/test/resources/sbt-multi-project-example/README.md new file mode 100644 index 00000000000..3c7a82b0293 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/README.md @@ -0,0 +1,23 @@ +# sbt-multi-project-example + +The goal of this example is to provide a multi-project build using `sbt` providing: +* A single `build.sbt` file which allows for centralized configuration, dependency and build management +* Each sub-project contains only its source code +* Sub-projects can depend on other sub-projects +* Only *deliverable* sub-projects produce a *fat-jar* using [sbt-assembly](https://github.com/sbt/sbt-assembly) + +# Example structure +* sbt-multi-project-example/ + * common/ + * src/ + * test/ + * multi1/ + * src/ + * test/ + * multi2/ + * src/ + * test/ + * project/ + * build.properties + * plugins.sbt + * build.sbt diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt new file mode 100644 index 00000000000..667fcaa1a66 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -0,0 +1,139 @@ +name := "sbt-multi-project-example" +organization in ThisBuild := "com.pbassiner" +scalaVersion in ThisBuild := "2.12.3" + +// PROJECTS + +lazy val global = project + .in(file(".")) + .settings(settings) + .disablePlugins(AssemblyPlugin) + .aggregate( + common, + multi1, + multi2 + ) + +lazy val common = project + .settings( + name := "common", + settings, + libraryDependencies ++= commonDependencies + ) + .disablePlugins(AssemblyPlugin) + +lazy val multi1 = project + .settings( + name := "multi1", + settings, + assemblySettings, + libraryDependencies ++= commonDependencies ++ Seq( + dependencies.monocleCore, + dependencies.monocleMacro + ) + ) + .dependsOn( + common + ) + +lazy val multi2 = project + .settings( + name := "multi2", + settings, + assemblySettings, + libraryDependencies ++= commonDependencies ++ Seq( + dependencies.pureconfig + ) + ) + .dependsOn( + common + ) + +// DEPENDENCIES + +lazy val dependencies = + new { + val logbackV = "1.2.3" + val logstashV = "4.11" + val scalaLoggingV = "3.7.2" + val slf4jV = "1.7.25" + val typesafeConfigV = "1.3.1" + val pureconfigV = "0.8.0" + val monocleV = "1.4.0" + val akkaV = "2.5.6" + val scalatestV = "3.0.4" + val scalacheckV = "1.13.5" + + val logback = "ch.qos.logback" % "logback-classic" % logbackV + val logstash = "net.logstash.logback" % "logstash-logback-encoder" % logstashV + val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingV + val slf4j = "org.slf4j" % "jcl-over-slf4j" % slf4jV + val typesafeConfig = "com.typesafe" % "config" % typesafeConfigV + val akka = "com.typesafe.akka" %% "akka-stream" % akkaV + val monocleCore = "com.github.julien-truffaut" %% "monocle-core" % monocleV + val monocleMacro = "com.github.julien-truffaut" %% "monocle-macro" % monocleV + val pureconfig = "com.github.pureconfig" %% "pureconfig" % pureconfigV + val scalatest = "org.scalatest" %% "scalatest" % scalatestV + val scalacheck = "org.scalacheck" %% "scalacheck" % scalacheckV + } + +lazy val commonDependencies = Seq( + dependencies.logback, + dependencies.logstash, + dependencies.scalaLogging, + dependencies.slf4j, + dependencies.typesafeConfig, + dependencies.akka, + dependencies.scalatest % "test", + dependencies.scalacheck % "test" +) + +// SETTINGS + +lazy val settings = +commonSettings ++ +wartremoverSettings ++ +scalafmtSettings + +lazy val compilerOptions = Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" +) + +lazy val commonSettings = Seq( + scalacOptions ++= compilerOptions, + resolvers ++= Seq( + "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository", + Resolver.sonatypeRepo("releases"), + Resolver.sonatypeRepo("snapshots") + ) +) + +lazy val wartremoverSettings = Seq( + wartremoverWarnings in (Compile, compile) ++= Warts.allBut(Wart.Throw) +) + +lazy val scalafmtSettings = + Seq( + scalafmtOnCompile := true, + scalafmtTestOnCompile := true, + scalafmtVersion := "1.2.0" + ) + +lazy val assemblySettings = Seq( + assemblyJarName in assembly := name.value + ".jar", + assemblyMergeStrategy in assembly := { + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case "application.conf" => MergeStrategy.concat + case x => + val oldStrategy = (assemblyMergeStrategy in assembly).value + oldStrategy(x) + } +) diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/common/src/main/scala/Model.scala b/main/init/sbt/test/resources/sbt-multi-project-example/common/src/main/scala/Model.scala new file mode 100644 index 00000000000..4a64c26b8cc --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/common/src/main/scala/Model.scala @@ -0,0 +1,2 @@ +final case class Entity(id: String, nested: NestedEntity) +final case class NestedEntity(value: String) diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/common/src/test/scala/Test.scala b/main/init/sbt/test/resources/sbt-multi-project-example/common/src/test/scala/Test.scala new file mode 100644 index 00000000000..81dc49be006 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/common/src/test/scala/Test.scala @@ -0,0 +1,6 @@ +import org.scalatest.FunSuite + +class Test extends FunSuite { + + test("common") {} +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/main/scala/Main.scala b/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/main/scala/Main.scala new file mode 100644 index 00000000000..0ef089fe5f1 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/main/scala/Main.scala @@ -0,0 +1,11 @@ +import monocle.macros.GenLens + +object Main extends App { + println("multi1 can use common sub-project") + + val entity = Entity("id", NestedEntity("value")) + + println("multi1 can use monocle dependency") + + val idLens = GenLens[Entity](_.id) +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/test/scala/Test.scala b/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/test/scala/Test.scala new file mode 100644 index 00000000000..f8eb44cc353 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/multi1/src/test/scala/Test.scala @@ -0,0 +1,13 @@ +import monocle.macros.GenLens +import org.scalatest.FunSuite + +class Test extends FunSuite { + + test("multi1 can use common sub-project") { + val entity = Entity("id", NestedEntity("value")) + } + + test("multi1 can use monocle dependency ") { + val idLens = GenLens[Entity](_.id) + } +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/main/scala/Main.scala b/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/main/scala/Main.scala new file mode 100644 index 00000000000..9af1d57f7fb --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/main/scala/Main.scala @@ -0,0 +1,11 @@ +object Main extends App { + println("multi2 can use common sub-project") + + val entity = Entity("id", NestedEntity("value")) + + println("multi2 can use pureconfig dependency") + + import pureconfig._ + + implicit def hint[T]: ProductHint[T] = ProductHint[T](ConfigFieldMapping(CamelCase, KebabCase)) +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/test/scala/Test.scala b/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/test/scala/Test.scala new file mode 100644 index 00000000000..b5781c76add --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/multi2/src/test/scala/Test.scala @@ -0,0 +1,14 @@ +import org.scalatest.FunSuite + +class Test extends FunSuite { + + test("multi2 can use common sub-project") { + val entity = Entity("id", NestedEntity("value")) + } + + test("multi2 can use pureconfig dependency") { + import pureconfig._ + + implicit def hint[T]: ProductHint[T] = ProductHint[T](ConfigFieldMapping(CamelCase, KebabCase)) + } +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties b/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties new file mode 100644 index 00000000000..017bb86a23d --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.0.2 diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/project/plugins.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/project/plugins.sbt new file mode 100644 index 00000000000..7c3cee85fd4 --- /dev/null +++ b/main/init/sbt/test/resources/sbt-multi-project-example/project/plugins.sbt @@ -0,0 +1,12 @@ +logLevel := Level.Warn + +addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.2") + +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.3.2") + +addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.2.1") + +addSbtPlugin("com.lucidchart" % "sbt-scalafmt-coursier" % "1.12") + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index 5331e959f9d..48e92fb127e 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -16,15 +16,16 @@ object BuildGenTests extends TestSuite { ) } - /* - test("application-library") { - val sourceRoot = os.sub / "application-library" - val expectedRoot = os.sub / "expected/application-library" + // from https://github.com/pbassiner/sbt-multi-project-example/tree/master + test("sbt-multi-project-example") { + val sourceRoot = os.sub / "sbt-multi-project-example" + val expectedRoot = os.sub / "expected/sbt-multi-project-example" assert( - checker.check(GradleBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) + checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot, true) ) } + /* test("config") { val sourceRoot = os.sub / "application-library" val expectedRoot = os.sub / "expected/config" From 913ef1d731ed98d08ecd46c93269f36574e9a20f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 21:42:46 +0800 Subject: [PATCH 17/70] Generate the expected snapshots for "sbt-multi-project-example" and improve 1. Bump the sbt version in "sbt-multi-project-example" to v1.10.7 1. Print an info message to remind the user when the sbt command to run the `millInitExportBuild` sbt task fails. 1. Drop dependencies of unknown configurations. 1. Generate the expected snapshots for "sbt-multi-project-example" and review. 1. Comment the Local Maven Repository as it's machine-dependent. --- .../src/mill/main/sbt/SbtBuildGenMain.scala | 37 +++++++--- .../sbt-multi-project-example/build.mill | 49 ++++++++++++++ .../common/package.mill | 63 +++++++++++++++++ .../multi1/package.mill | 67 +++++++++++++++++++ .../multi2/package.mill | 66 ++++++++++++++++++ .../sbt-multi-project-example/build.sbt | 3 +- .../project/build.properties | 2 +- 7 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 5e42570af98..8b3e6b40fb4 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -6,7 +6,7 @@ import mill.main.buildgen.BuildGenUtil.* import mill.main.buildgen.IrDependencyType.* import os.Path -import scala.collection.View +import scala.collection.{MapView, View} import scala.collection.immutable.SortedSet /** @@ -79,7 +79,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("Running sbt task to generate the project tree") - Process( + val exitCode = Process( Seq( sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", @@ -89,6 +89,10 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ).! // println("Exit code from running the `millInitExportBuild` sbt task: " + exitCode) + if (exitCode != 0) + println( + "The sbt command to run the `millInitExportBuild` sbt task has likely failed, please update the project's sbt version to the latest or our tested version v1.10.7, and try again." + ) val buildExportPickled = os.read(workspace / "target" / "mill-init-build-export.json") // TODO This is mainly for debugging purposes. Comment out this line if it's unnecessary. @@ -300,21 +304,32 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No val allDepsByConfiguration = project.allDependencies // .view // This makes the types hard to deal with here thus commented out. .filterNot(isScalaStandardLibrary) - .groupBy(_.configurations match { - case None => Default - case Some(configuration) => configuration match { - case "compile" => Default - case "test" => Test - case "runtime" => Run - case "provided" | "optional" => Compile + .flatMap(dep => + (dep.configurations match { + case None => Some(Default) + case Some(configuration) => configuration match { + case "compile" => Some(Default) + case "test" => Some(Test) + case "runtime" => Some(Run) + case "provided" | "optional" => Some(Compile) + case other => + println(s"Dependency $dep with an unknown configuration ${escape(other)} is dropped.") + None } - }) + }) + .map(tpe => (dep, tpe)) + ) + .groupBy(_._2) + .view + .mapValues(_.map(_._1)) + // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. + .asInstanceOf[MapView[IrDependencyType, Seq[Dependency]]] case class Deps[I, M](ivy: Seq[I], module: Seq[M]) // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. val ivyAndModuleDepsByConfiguration: Map[IrDependencyType, Deps[Dependency, String]] = - allDepsByConfiguration.view.mapValues(deps => { + allDepsByConfiguration.mapValues(deps => { val tuple2 = deps.partitionMap(dep => { val id = groupArtifactVersion(dep) if (packages.isDefinedAt(id)) Right(packages(id)) diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill new file mode 100644 index 00000000000..5535e285a56 --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill @@ -0,0 +1,49 @@ +package build + +import $packages._ +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule { + + def artifactName = "sbt-multi-project-example" + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def pomSettings = PomSettings( + "sbt-multi-project-example", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + def publishVersion = "0.1.0-SNAPSHOT" + +} diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill new file mode 100644 index 00000000000..a783fb2796a --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -0,0 +1,63 @@ +package build.common + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule with SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"ch.qos.logback:logback-classic:1.2.3", + ivy"com.typesafe.akka:akka-stream::2.5.6", + ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.typesafe:config:1.3.1", + ivy"net.logstash.logback:logstash-logback-encoder:4.11", + ivy"org.slf4j:jcl-over-slf4j:1.7.25" + ) + + def pomSettings = PomSettings( + "common", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + def publishVersion = "0.1.0-SNAPSHOT" + + object test extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"org.scalacheck:scalacheck::1.13.5", + ivy"org.scalatest:scalatest::3.0.4" + ) + + } +} diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill new file mode 100644 index 00000000000..5af49c4b576 --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -0,0 +1,67 @@ +package build.multi1 + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule with SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"ch.qos.logback:logback-classic:1.2.3", + ivy"com.github.julien-truffaut:monocle-core::1.4.0", + ivy"com.github.julien-truffaut:monocle-macro::1.4.0", + ivy"com.typesafe.akka:akka-stream::2.5.6", + ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.typesafe:config:1.3.1", + ivy"net.logstash.logback:logstash-logback-encoder:4.11", + ivy"org.slf4j:jcl-over-slf4j:1.7.25" + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "multi1", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + def publishVersion = "0.1.0-SNAPSHOT" + + object test extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"org.scalacheck:scalacheck::1.13.5", + ivy"org.scalatest:scalatest::3.0.4" + ) + + } +} diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill new file mode 100644 index 00000000000..d42c7c8f864 --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -0,0 +1,66 @@ +package build.multi2 + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule with SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"ch.qos.logback:logback-classic:1.2.3", + ivy"com.github.pureconfig:pureconfig::0.8.0", + ivy"com.typesafe.akka:akka-stream::2.5.6", + ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.typesafe:config:1.3.1", + ivy"net.logstash.logback:logstash-logback-encoder:4.11", + ivy"org.slf4j:jcl-over-slf4j:1.7.25" + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "multi2", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + def publishVersion = "0.1.0-SNAPSHOT" + + object test extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"org.scalacheck:scalacheck::1.13.5", + ivy"org.scalatest:scalatest::3.0.4" + ) + + } +} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index 667fcaa1a66..fc367bfebdf 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -110,7 +110,8 @@ lazy val compilerOptions = Seq( lazy val commonSettings = Seq( scalacOptions ++= compilerOptions, resolvers ++= Seq( - "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository", + // commented out as this is different on different machines + //"Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository", Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots") ) diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties b/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties index 017bb86a23d..fe69360b7c0 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties +++ b/main/init/sbt/test/resources/sbt-multi-project-example/project/build.properties @@ -1 +1 @@ -sbt.version = 1.0.2 +sbt.version = 1.10.7 From ac9d6574ebd86888d759bdf80cc77e97069a3ea5 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 22:38:59 +0800 Subject: [PATCH 18/70] Generate the expected snapshots for "sbt-multi-project-example" with the config args, fixing some bugs --- .../src/mill/main/sbt/SbtBuildGenMain.scala | 24 +- .../sbt-multi-project-example/build.mill | 248 ++++++++++++++++++ .../src/mill/main/sbt/BuildGenTests.scala | 14 +- 3 files changed, 265 insertions(+), 21 deletions(-) create mode 100644 main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 8b3e6b40fb4..f5ea0b4c306 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -6,8 +6,8 @@ import mill.main.buildgen.BuildGenUtil.* import mill.main.buildgen.IrDependencyType.* import os.Path -import scala.collection.{MapView, View} import scala.collection.immutable.SortedSet +import scala.collection.{MapView, View} /** * Converts an sbt build to Mill by generating Mill build file(s). @@ -164,7 +164,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No scalacOptions, pomSettings, publishVersion, - null, // not available in sbt as it seems + Seq.empty, // not available in sbt as it seems repositories ) @@ -198,7 +198,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No repositories = getRepositories(buildInfo).diff(baseInfo.repositories), javacOptions = getJavacOptions(buildInfo).diff(baseInfo.javacOptions), scalaVersion = - if (buildInfo.scalaVersion != baseInfo.scalaVersion) buildInfo.scalaVersion else null, + if (buildInfo.scalaVersion != baseInfo.scalaVersion) buildInfo.scalaVersion else None, scalacOptions = buildInfo.scalacOptions.map(scalacOptions => baseInfo.scalacOptions.fold(scalacOptions)(baseScalacOptions => scalacOptions.diff(baseScalacOptions) @@ -308,14 +308,16 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No (dep.configurations match { case None => Some(Default) case Some(configuration) => configuration match { - case "compile" => Some(Default) - case "test" => Some(Test) - case "runtime" => Some(Run) - case "provided" | "optional" => Some(Compile) - case other => - println(s"Dependency $dep with an unknown configuration ${escape(other)} is dropped.") - None - } + case "compile" => Some(Default) + case "test" => Some(Test) + case "runtime" => Some(Run) + case "provided" | "optional" => Some(Compile) + case other => + println( + s"Dependency $dep with an unknown configuration ${escape(other)} is dropped." + ) + None + } }) .map(tpe => (dep, tpe)) ) diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill new file mode 100644 index 00000000000..9ecf8d78364 --- /dev/null +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -0,0 +1,248 @@ +package build + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object Deps { + + val `ch.qos.logback:logback-classic` = + ivy"ch.qos.logback:logback-classic:1.2.3" + + val `com.github.julien-truffaut:monocle-core` = + ivy"com.github.julien-truffaut:monocle-core::1.4.0" + + val `com.github.julien-truffaut:monocle-macro` = + ivy"com.github.julien-truffaut:monocle-macro::1.4.0" + + val `com.github.pureconfig:pureconfig` = + ivy"com.github.pureconfig:pureconfig::0.8.0" + + val `com.typesafe.akka:akka-stream` = + ivy"com.typesafe.akka:akka-stream::2.5.6" + + val `com.typesafe.scala-logging:scala-logging` = + ivy"com.typesafe.scala-logging:scala-logging::3.7.2" + val `com.typesafe:config` = ivy"com.typesafe:config:1.3.1" + + val `net.logstash.logback:logstash-logback-encoder` = + ivy"net.logstash.logback:logstash-logback-encoder:4.11" + val `org.scalacheck:scalacheck` = ivy"org.scalacheck:scalacheck::1.13.5" + val `org.scalatest:scalatest` = ivy"org.scalatest:scalatest::3.0.4" + val `org.slf4j:jcl-over-slf4j` = ivy"org.slf4j:jcl-over-slf4j:1.7.25" +} + +object `package` extends RootModule { + + def artifactName = "sbt-multi-project-example" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def pomSettings = PomSettings( + "sbt-multi-project-example", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + object multi1 extends BaseModule { + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.github.julien-truffaut:monocle-core`, + Deps.`com.github.julien-truffaut:monocle-macro`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "multi1", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + object tests extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } + + object multi2 extends BaseModule { + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.github.pureconfig:pureconfig`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "multi2", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + object tests extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } + + object common extends BaseModule { + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def ivyDeps = super.ivyDeps() ++ Agg( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + def pomSettings = PomSettings( + "common", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + object tests extends MavenTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } +} + +trait BaseModule extends SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def pomSettings = PomSettings( + "", + "com.pbassiner", + "", + Seq(), + VersionControl(None, None, None, None), + Seq() + ) + + def publishVersion = "0.1.0-SNAPSHOT" + +} diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index 48e92fb127e..191ff33966b 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -25,17 +25,12 @@ object BuildGenTests extends TestSuite { ) } - /* - test("config") { - val sourceRoot = os.sub / "application-library" - val expectedRoot = os.sub / "expected/config" + test("config-sbt-multi-project-example") { + val sourceRoot = os.sub / "sbt-multi-project-example" + val expectedRoot = os.sub / "expected/config/sbt-multi-project-example" val args = Array( "--base-module", "BaseModule", - "--base-project", - "utilities", - "--jvm-id", - "11", "--test-module", "tests", "--deps-object", @@ -43,9 +38,8 @@ object BuildGenTests extends TestSuite { "--merge" ) assert( - checker.check(GradleBuildGenMain.main(args), sourceRoot, expectedRoot) + checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot, true) ) } - */ } } From f8d748b628a873fcba7eb6929fc53e484c8f5c5f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 22:46:09 +0800 Subject: [PATCH 19/70] Set the settings in `ThisBuild` in "sbt-multi-project-example" so they are in the `BaseModule` in the generated build files --- .../sbt-multi-project-example/build.mill | 115 ++++-------------- .../sbt-multi-project-example/README.md | 2 + .../sbt-multi-project-example/build.sbt | 15 +-- 3 files changed, 31 insertions(+), 101 deletions(-) diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index 9ecf8d78364..8e05b83b13e 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -37,29 +37,6 @@ object `package` extends RootModule { def artifactName = "sbt-multi-project-example" - def scalacOptions = super.scalacOptions() ++ Seq( - "-unchecked", - "-feature", - "-language:existentials", - "-language:higherKinds", - "-language:implicitConversions", - "-language:postfixOps", - "-deprecation", - "-encoding", - "utf8" - ) - - def repositoriesTask = Task.Anon { - super.repositoriesTask() ++ Seq( - coursier.maven.MavenRepository( - "https://oss.sonatype.org/service/local/repositories/releases/content/" - ), - coursier.maven.MavenRepository( - "https://oss.sonatype.org/content/repositories/snapshots" - ) - ) - } - def pomSettings = PomSettings( "sbt-multi-project-example", "com.pbassiner", @@ -71,29 +48,6 @@ object `package` extends RootModule { object multi1 extends BaseModule { - def scalacOptions = super.scalacOptions() ++ Seq( - "-unchecked", - "-feature", - "-language:existentials", - "-language:higherKinds", - "-language:implicitConversions", - "-language:postfixOps", - "-deprecation", - "-encoding", - "utf8" - ) - - def repositoriesTask = Task.Anon { - super.repositoriesTask() ++ Seq( - coursier.maven.MavenRepository( - "https://oss.sonatype.org/service/local/repositories/releases/content/" - ), - coursier.maven.MavenRepository( - "https://oss.sonatype.org/content/repositories/snapshots" - ) - ) - } - def ivyDeps = super.ivyDeps() ++ Agg( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.julien-truffaut:monocle-core`, @@ -126,29 +80,6 @@ object `package` extends RootModule { object multi2 extends BaseModule { - def scalacOptions = super.scalacOptions() ++ Seq( - "-unchecked", - "-feature", - "-language:existentials", - "-language:higherKinds", - "-language:implicitConversions", - "-language:postfixOps", - "-deprecation", - "-encoding", - "utf8" - ) - - def repositoriesTask = Task.Anon { - super.repositoriesTask() ++ Seq( - coursier.maven.MavenRepository( - "https://oss.sonatype.org/service/local/repositories/releases/content/" - ), - coursier.maven.MavenRepository( - "https://oss.sonatype.org/content/repositories/snapshots" - ) - ) - } - def ivyDeps = super.ivyDeps() ++ Agg( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.pureconfig:pureconfig`, @@ -180,29 +111,6 @@ object `package` extends RootModule { object common extends BaseModule { - def scalacOptions = super.scalacOptions() ++ Seq( - "-unchecked", - "-feature", - "-language:existentials", - "-language:higherKinds", - "-language:implicitConversions", - "-language:postfixOps", - "-deprecation", - "-encoding", - "utf8" - ) - - def repositoriesTask = Task.Anon { - super.repositoriesTask() ++ Seq( - coursier.maven.MavenRepository( - "https://oss.sonatype.org/service/local/repositories/releases/content/" - ), - coursier.maven.MavenRepository( - "https://oss.sonatype.org/content/repositories/snapshots" - ) - ) - } - def ivyDeps = super.ivyDeps() ++ Agg( Deps.`ch.qos.logback:logback-classic`, Deps.`com.typesafe.akka:akka-stream`, @@ -234,6 +142,18 @@ trait BaseModule extends SbtModule with PublishModule { def scalaVersion = "2.12.3" + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + def pomSettings = PomSettings( "", "com.pbassiner", @@ -245,4 +165,15 @@ trait BaseModule extends SbtModule with PublishModule { def publishVersion = "0.1.0-SNAPSHOT" + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + } diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/README.md b/main/init/sbt/test/resources/sbt-multi-project-example/README.md index 3c7a82b0293..4534701ff33 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/README.md +++ b/main/init/sbt/test/resources/sbt-multi-project-example/README.md @@ -1,5 +1,7 @@ # sbt-multi-project-example +Adapted from . + The goal of this example is to provide a multi-project build using `sbt` providing: * A single `build.sbt` file which allows for centralized configuration, dependency and build management * Each sub-project contains only its source code diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index fc367bfebdf..81f9e458450 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -91,7 +91,6 @@ lazy val commonDependencies = Seq( // SETTINGS lazy val settings = -commonSettings ++ wartremoverSettings ++ scalafmtSettings @@ -107,14 +106,12 @@ lazy val compilerOptions = Seq( "utf8" ) -lazy val commonSettings = Seq( - scalacOptions ++= compilerOptions, - resolvers ++= Seq( - // commented out as this is different on different machines - //"Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository", - Resolver.sonatypeRepo("releases"), - Resolver.sonatypeRepo("snapshots") - ) +ThisBuild / scalacOptions ++= compilerOptions +ThisBuild / resolvers ++= Seq( + // commented out as this is different on different machines + //"Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository", + Resolver.sonatypeRepo("releases"), + Resolver.sonatypeRepo("snapshots") ) lazy val wartremoverSettings = Seq( From 4156577ec5f46fb7fb672010b4122cb966e66581 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 16 Feb 2025 23:54:12 +0800 Subject: [PATCH 20/70] Add POM settings in `ThisBuild` in "sbt-multi-project-example" to test the conversion, regenerate the snapshots, and fix a bug that `organizationHomepage` is passed as `IrPom.url` instead of `homepage` and regenerate again Code related to `organizationHomepage` is commented out as it's actually unnecessary. --- .../sbt/models/src/mill/main/sbt/Models.scala | 3 + .../mill/main/sbt/ExportBuildPlugin.scala | 2 +- .../src/mill/main/sbt/SbtBuildGenMain.scala | 2 +- .../sbt-multi-project-example/build.mill | 58 ++++++------------- .../sbt-multi-project-example/build.mill | 22 +++++-- .../common/package.mill | 22 +++++-- .../multi1/package.mill | 22 +++++-- .../multi2/package.mill | 22 +++++-- .../sbt-multi-project-example/build.sbt | 7 +++ 9 files changed, 97 insertions(+), 63 deletions(-) diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index ae6a982ebac..68cedd06d07 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -62,12 +62,15 @@ case class BuildPublicationInfo( */ organizationName: Option[String], */ + // not needed + /* /** * corresponds to `organizationUrl` in POM * * @see [[Keys.organizationHomepage]] */ organizationHomepage: Option[Option[String]], + */ /** @see [[Keys.developers]] */ developers: Option[Seq[Developer]], /** @see [[Keys.scmInfo]] */ diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index f80705477e1..8597c6d5376 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -34,7 +34,7 @@ object ExportBuildPlugin extends AutoPlugin { }), organization.?.value, //organizationName.?.value, // not needed - organizationHomepage.?.value.map(_.map(_.toExternalForm)), + //organizationHomepage.?.value.map(_.map(_.toExternalForm)), // not needed developers.?.value.map(_.map(developer => Developer(developer.id, developer.name, developer.email, developer.url.toExternalForm) )), diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index f5ea0b4c306..e6c3222f122 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -277,7 +277,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No IrPom( description.getOrElse(""), organization.getOrElse(""), - organizationHomepage.fold("")(_.getOrElse("")), + homepage.fold("")(_.getOrElse("")), licenses.getOrElse(Seq.empty).map(license => IrLicense(license._1, license._1, license._2)), scmInfo.flatten.fold(IrVersionControl(null, null, null, null))(scmInfo => { import scmInfo.* diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index 8e05b83b13e..def71c87d0c 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -37,15 +37,6 @@ object `package` extends RootModule { def artifactName = "sbt-multi-project-example" - def pomSettings = PomSettings( - "sbt-multi-project-example", - "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() - ) - object multi1 extends BaseModule { def ivyDeps = super.ivyDeps() ++ Agg( @@ -61,15 +52,6 @@ object `package` extends RootModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) - def pomSettings = PomSettings( - "multi1", - "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() - ) - object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ @@ -92,15 +74,6 @@ object `package` extends RootModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) - def pomSettings = PomSettings( - "multi2", - "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() - ) - object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ @@ -120,15 +93,6 @@ object `package` extends RootModule { Deps.`org.slf4j:jcl-over-slf4j` ) - def pomSettings = PomSettings( - "common", - "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() - ) - object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ @@ -155,12 +119,24 @@ trait BaseModule extends SbtModule with PublishModule { ) def pomSettings = PomSettings( - "", + "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) def publishVersion = "0.1.0-SNAPSHOT" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill index 5535e285a56..d2e18d855d0 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill @@ -36,12 +36,24 @@ object `package` extends RootModule { } def pomSettings = PomSettings( - "sbt-multi-project-example", + "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) def publishVersion = "0.1.0-SNAPSHOT" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index a783fb2796a..21acfc30af1 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -42,12 +42,24 @@ object `package` extends RootModule with SbtModule with PublishModule { ) def pomSettings = PomSettings( - "common", + "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) def publishVersion = "0.1.0-SNAPSHOT" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index 5af49c4b576..408f966cd80 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -46,12 +46,24 @@ object `package` extends RootModule with SbtModule with PublishModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) def pomSettings = PomSettings( - "multi1", + "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) def publishVersion = "0.1.0-SNAPSHOT" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index d42c7c8f864..02a8ac8c169 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -45,12 +45,24 @@ object `package` extends RootModule with SbtModule with PublishModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) def pomSettings = PomSettings( - "multi2", + "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", - "", - Seq(), - VersionControl(None, None, None, None), - Seq() + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) ) def publishVersion = "0.1.0-SNAPSHOT" diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index 81f9e458450..7ac330b18b3 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -2,6 +2,13 @@ name := "sbt-multi-project-example" organization in ThisBuild := "com.pbassiner" scalaVersion in ThisBuild := "2.12.3" +val urlString = "https://github.com/com-lihaoyi/mill" +ThisBuild / homepage := Some(url(urlString)) +ThisBuild / description := "This is an sbt sample project for testing Mill's init command." +ThisBuild / licenses := Seq(License.Apache2) +ThisBuild / developers := List(Developer("johnd", "John Doe", "john.doe@example.com", url("https://example.com/johnd"))) +ThisBuild / scmInfo := Some(ScmInfo(url(urlString), s"scm:git:$urlString.git")) + // PROJECTS lazy val global = project From f0d1378b40481c21d61743db85d86bb8dc2aab72 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 17 Feb 2025 00:02:26 +0800 Subject: [PATCH 21/70] Stop updating the expected test sample snapshots --- main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index 191ff33966b..3708220fe81 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -12,7 +12,7 @@ object BuildGenTests extends TestSuite { val sourceRoot = os.sub / "scala-seed-project" val expectedRoot = os.sub / "expected/scala-seed-project" assert( - checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot, true) + checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) ) } @@ -21,7 +21,7 @@ object BuildGenTests extends TestSuite { val sourceRoot = os.sub / "sbt-multi-project-example" val expectedRoot = os.sub / "expected/sbt-multi-project-example" assert( - checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot, true) + checker.check(SbtBuildGenMain.main(Array.empty), sourceRoot, expectedRoot) ) } @@ -38,7 +38,7 @@ object BuildGenTests extends TestSuite { "--merge" ) assert( - checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot, true) + checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot) ) } } From ea780d7f43ca21aaaa7508ad6d3d97025788e92b Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 17 Feb 2025 00:52:11 +0800 Subject: [PATCH 22/70] Copy and adapt the docs and some code for sbt from those for Gradle referring to #4363 --- dist/package.mill | 1 + docs/modules/ROOT/nav.adoc | 1 + docs/modules/ROOT/pages/migrating/sbt.adoc | 120 ++++++++++++++++++ .../mill/main/gradle/GradleBuildGenMain.scala | 3 +- .../src/mill/main/sbt/SbtBuildGenMain.scala | 11 +- 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 docs/modules/ROOT/pages/migrating/sbt.adoc diff --git a/dist/package.mill b/dist/package.mill index f226bfe7999..1f40a9d299c 100644 --- a/dist/package.mill +++ b/dist/package.mill @@ -75,6 +75,7 @@ object `package` extends RootModule with InstallModule { build.main.graphviz.testDep(), build.main.init.maven.testDep(), build.main.init.gradle.testDep(), + build.main.init.sbt.testDep(), build.runner.linenumbers.testDep(), build.scalalib.backgroundwrapper.testDep(), build.contrib.bloop.testDep(), diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index ec9156fa191..658494ec3ca 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -58,6 +58,7 @@ * xref:migrating/migrating.adoc[] ** xref:migrating/maven.adoc[] ** xref:migrating/gradle.adoc[] +** xref:migrating/sbt.adoc[] // This section gives a tour of the various user-facing features of Mill: // library deps, out folder, queries, tasks, etc.. These are things that // every Mill user will likely encounter, and are touched upon in the various diff --git a/docs/modules/ROOT/pages/migrating/sbt.adoc b/docs/modules/ROOT/pages/migrating/sbt.adoc new file mode 100644 index 00000000000..5441f084c1c --- /dev/null +++ b/docs/modules/ROOT/pages/migrating/sbt.adoc @@ -0,0 +1,120 @@ += Migrating From sbt to Mill +:page-aliases: Migrating_An_sbt_Build_to_Mill.adoc +:icons: font + + + +The Mill `init` command can be used to convert an sbt build to Mill. This has +xref:#limitations[limitations] and is not intended to reliably migrate 100% of +sbt builds out there in the wild, but is instead meant to provide the basic +scaffolding of a Mill build for you to further refine and update manually. + +Each sbt project in a build tree is converted to a Mill module. +A nested `test` module is defined, if `src/test` exists, and is configured with a supported xref:scalalib/testing.adoc[test framework], if found. + +Again, note that `mill init` imports an sbt build on a best-effort basis. +This means that while simple projects can be expected to complete without issue: + +include::partial$example/javalib/migrating/3-sbt-complete.adoc[] + +Projects with a complex build often require some manual tweaking in order to work: + +include::partial$example/javalib/migrating/4-sbt-incomplete.adoc[] + +== Capabilities + +The conversion + +* handles deeply nested modules +* captures publish settings +* configures dependencies for configurations: +** no configuration +** Compile +** Test +** Runtime +** Provided +** Optional +* configures testing frameworks: +** Java: +*** JUnit 4 +*** JUnit 5 +*** TestNG +** Scala: +*** ScalaTest +*** Specs2 +*** µTest +*** MUnit +*** Weaver +*** ZIOTest + +[#arguments] +=== Command line arguments + +The conversion and its output (the generated Mill build files) can be customized using + +* `--base-module` (`-b`): name of generated base module trait defining shared settings ++ +[source,sh] +---- +./mill init --base-module MyModule +---- + +* `--test-module` (`-t`): name of generated nested test module (defaults to `test`) ++ +[source,sh] +---- +./mill init --test-module test +---- + +* `--deps-object` (`-d`): name of generated companion object defining dependency constants ++ +[source,sh] +---- +./mill init --deps-object Deps +---- + +* `--merge` (`-m`): merge build files generated for a multi-module build ++ +[source,sh] +---- +./mill init --merge +---- + +TIP: You can run `mill init` multiple times. It is recommended to run it first without any options. + +[#limitations] +== Limitations + +The conversion does not support: + +* custom dependency configurations +* custom settings including custom tasks +* sources other than Scala on JVM and Java + +sbt plugin support is limited to: + +* https://www.scala-sbt.org/1.x/api/sbt/plugins/JvmPlugin$.html[`JvmPlugin`] + +[TIP] +==== +These limitations can be overcome by: + +* configuring equivalent Mill xref:extending/contrib-plugins.adoc[contrib] +or xref:extending/thirdparty-plugins.adoc[third party] plugins +* defining custom xref:extending/writing-plugins.adoc[plugins] +* defining custom xref:fundamentals/tasks.adoc[tasks] +* defining custom xref:fundamentals/cross-builds.adoc[cross modules] +==== + +== FAQ + +How to fix `java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release` error thrown by the sbt command invoked by `mill init`? + +Update the project's sbt version to the latest or our tested version v1.10.7, and try again. + +How to fix test compilation errors? + +* The test framework configured may be for an unsupported version; try upgrading the +corresponding dependencies. +* Mill does not add `compileIvyDeps` dependencies to the transitive dependencies of the nested +test module; specify the dependencies again, in `ivyDeps` or `runIvyDeps`. diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index e6bf7b9dd33..66e40016cad 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -21,6 +21,7 @@ import scala.jdk.CollectionConverters.* * ===Capabilities=== * The conversion * - handles deeply nested modules + * - captures publish settings * - configures dependencies for configurations: * - implementation / api * - compileOnly / compileOnlyApi @@ -33,7 +34,7 @@ import scala.jdk.CollectionConverters.* * - TestNG * * ===Limitations=== - * The conversion does not support + * The conversion does not support: * - custom dependency configurations * - custom tasks * - non-Java sources diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index e6c3222f122..159ab16dd34 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -20,13 +20,14 @@ import scala.collection.{MapView, View} * ===Capabilities=== * The conversion * - handles deeply nested modules + * - captures publish settings * - configures dependencies for configurations: * - no configuration * - Compile + * - Test + * - Runtime * - Provided * - Optional - * - Runtime - * - Test * - configures testing frameworks (@see [[mill.scalalib.TestModule]]): * - Java: * - JUnit 4 @@ -40,9 +41,9 @@ import scala.collection.{MapView, View} * - Weaver * - ZIOTest * ===Limitations=== - * The conversion does not support - * - custom configurations - * - custom tasks + * The conversion does not support: + * - custom dependency configurations + * - custom settings including custom tasks * - sources other than Scala on JVM and Java */ @mill.api.internal From b59e33f71e274336b5fe75166d369fb4eff2c090 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 18 Feb 2025 06:14:47 +0800 Subject: [PATCH 23/70] Copy and adapt the integration tests for sbt from those for Gradle, run them, review their results, and fix some bugs revealed - Fix compiling issues in `MavenBuildGenMain.scala` and `GradleBuildGenMain.scala` caused by `scalaVersion` introduced. - Copy the sbt plugin "sbt-mill-init-export-build-assembly.jar" to a temp directory to be consumed so it works in integration tests when Mill is packaged. - Improve a message in `ExportBuildPlugin`. - Make the generated `package` object always extends the `BaseModule` or `SbtModule` trait. - Fix a bug in `renderIvy` that the double colon `::` is placed at the wrong place. - Update the expected build file snapshots accordingly in test resources. - Update docs. --- docs/modules/ROOT/pages/migrating/sbt.adoc | 3 +- .../feature/init/src/MillInitSbtTests.scala | 149 ++++++++++++++++++ .../mill/main/gradle/GradleBuildGenMain.scala | 4 + .../mill/main/maven/MavenBuildGenMain.scala | 4 + .../mill/main/sbt/ExportBuildPlugin.scala | 2 +- .../src/mill/main/sbt/SbtBuildGenMain.scala | 34 ++-- .../sbt-multi-project-example/build.mill | 16 +- .../sbt-multi-project-example/build.mill | 2 +- .../common/package.mill | 8 +- .../multi1/package.mill | 12 +- .../multi2/package.mill | 10 +- .../expected/scala-seed-project/build.mill | 2 +- 12 files changed, 207 insertions(+), 39 deletions(-) create mode 100644 integration/feature/init/src/MillInitSbtTests.scala diff --git a/docs/modules/ROOT/pages/migrating/sbt.adoc b/docs/modules/ROOT/pages/migrating/sbt.adoc index 5441f084c1c..51c3956bae2 100644 --- a/docs/modules/ROOT/pages/migrating/sbt.adoc +++ b/docs/modules/ROOT/pages/migrating/sbt.adoc @@ -89,7 +89,8 @@ The conversion does not support: * custom dependency configurations * custom settings including custom tasks -* sources other than Scala on JVM and Java +* sources other than Scala on JVM and Java, such as Scala.js and Scala Native +* cross builds sbt plugin support is limited to: diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala new file mode 100644 index 00000000000..183d8e50b5e --- /dev/null +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -0,0 +1,149 @@ +package mill.integration + +import mill.integration.MillInitSbtTests.initCommand +import utest.* + +object MillInitSbtTests { + val initCommand = ("init", "--base-module", "BaseModule", "--deps-object", "Deps", "--merge") +} + +// Relatively large libraries + +// Scala.js and scala-native projects are not properly imported +object MillInitSbtScalazTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 0.8 MB + - sbt 1.10.7 + */ + val url = "https://github.com/scalaz/scalaz/archive/refs/tags/v7.3.8.zip" + + test - integrationTest(url) { tester => + import tester.* + + os.call(("chmod", "+x", "sbt"), cwd = workspacePath) + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + } + } +} + +// Scala.js and scala-native projects are not properly imported +object MillInitSbtCatsTests extends BuildGenTestSuite { + + def tests: Tests = Tests { + /* + - 2 MB + - sbt 1.10.7 + - MUnit + */ + val url = "https://github.com/typelevel/cats/archive/refs/tags/v2.13.0.zip" + + test - integrationTest(url) { tester => + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + } + } +} + +// Converting child projects nested in a parent directory which is not a project is not supported yet. +object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 5 MB + - sbt 1.10.5 + */ + val url = "https://github.com/playframework/playframework/archive/refs/tags/3.0.6.zip" + + test - integrationTest(url) { tester => + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + } + } +} + +// Scala.js and scala-native projects are not properly imported +object MillInitSbtScalaCheckTests extends BuildGenTestSuite { + + def tests: Tests = Tests { + /* + - 0.5 MB + - sbt 1.10.1 + */ + val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" + + test - integrationTest(url) { tester => + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + } + } +} + +// Relatively small libraries + +object MillInitScalaCsvTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 25 KB + - sbt 1.10.7 + */ + val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/2.0.0.zip" + + test - integrationTest(url) { tester => + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + // Cross builds are not supported yet. + assert(!compileResult.isSuccess) + } + } +} + +// same as the one in the unit tests +object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 10 KB + */ + val url = + "https://github.com/pbassiner/sbt-multi-project-example/archive/152b31df9837115b183576b0080628b43c505389.zip" + + test - integrationTest(url) { tester => + import tester.* + + // sbt version bumped so it works with JDK 21 + os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + + val testResult = eval("test") + assert(testResult.isSuccess) + } + } +} diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index 66e40016cad..4df87a053fc 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -125,6 +125,7 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod Option.when(null != project.maven().pom()) { "PublishModule" } val javacOptions = getJavacOptions(project) + val scalaVersion = None val scalacOptions = None val repos = getRepositories(project) val pomSettings = extractPomSettings(project) @@ -136,6 +137,7 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod baseModule, supertypes, javacOptions, + scalaVersion, scalacOptions, pomSettings, publishVersion, @@ -145,6 +147,7 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod IrBaseInfo( javacOptions, + scalaVersion, scalacOptions, repos, pomSettings == null, @@ -170,6 +173,7 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod dirs = build.dirs, repositories = getRepositories(project).diff(baseInfo.repositories), javacOptions = getJavacOptions(project).diff(baseInfo.javacOptions), + scalaVersion = None, scalacOptions = None, projectName = getArtifactId(project), pomSettings = if (baseInfo.noPom) extractPomSettings(project) else null, diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index abfe12ae98f..d7b6b905039 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -67,6 +67,7 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe ): IrBaseInfo = { val model = input.node.value val javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model) + val scalaVersion = None val scalacOptions = None val repositories = getRepositories(model) val pomSettings = extractPomSettings(model) @@ -78,6 +79,7 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe baseModule, getModuleSupertypes(cfg), javacOptions, + scalaVersion, scalacOptions, pomSettings, publishVersion, @@ -87,6 +89,7 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe IrBaseInfo( javacOptions, + scalaVersion, scalacOptions, repositories, noPom = false, @@ -112,6 +115,7 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe dirs = build.dirs, repositories = getRepositories(model), javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model).diff(baseInfo.javacOptions), + scalaVersion = None, scalacOptions = None, projectName = getArtifactId(model), pomSettings = if (baseInfo.noPom) extractPomSettings(model) else null, diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index 8597c6d5376..bc16baea4c9 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -103,7 +103,7 @@ object ExportBuildPlugin extends AutoPlugin { case Disabled => false case _: Binary => true case crossVersion => - println(s"Unsupported `CrossVersion`: $crossVersion") + println(s"Dependency $moduleID with unsupported `CrossVersion`: $crossVersion") false }, moduleID.revision, diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 159ab16dd34..f21dc02dbfc 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -44,7 +44,8 @@ import scala.collection.{MapView, View} * The conversion does not support: * - custom dependency configurations * - custom settings including custom tasks - * - sources other than Scala on JVM and Java + * - sources other than Scala on JVM and Java, such as Scala.js and Scala Native + * - cross builds */ @mill.api.internal object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[Node[Project]])] { @@ -78,7 +79,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No "No sbt executable (`./sbt`, `./sbtx`, or system-wide `sbt`) found" ) - println("Running sbt task to generate the project tree") + println("Running the added `millInitExportBuild` sbt task to export the build") val exitCode = Process( Seq( @@ -96,8 +97,8 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No ) val buildExportPickled = os.read(workspace / "target" / "mill-init-build-export.json") - // TODO This is mainly for debugging purposes. Comment out this line if it's unnecessary. - println("sbt build export retrieved: " + buildExportPickled) + // TODO This is mainly for debugging purposes. Comment out or uncomment this line as needed. + // println("sbt build export retrieved: " + buildExportPickled) import upickle.default.* val buildExport = read[BuildExport](buildExportPickled) @@ -112,6 +113,11 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No Option.when(dirs.nonEmpty)(dirs.dropRight(1)) }) + /* + TODO This does not support converting child projects nested in a parent directory which is not a project yet. + Supporting this may involve refactoring `Node[Project]` into `Node[Option[Project]]` + or adding a subproject as a direct child with its ancestor directory project (I am not sure whether this works). + */ val input = Tree.from(projectNodesByParentDirs(None).head) { node => val dirs = node.dirs val children = projectNodesByParentDirs.getOrElse(Some(dirs), Seq.empty) @@ -124,13 +130,20 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No } private def writeSbtFile(): os.Path = { - val file = os.temp.dir() / "mill-init.sbt" - // TODO copy to a temp file if it doesn't work when packaged in a jar + val tempDir = os.temp.dir() + // This doesn't work in integration tests when Mill is packaged. + /* val sbtPluginJarUrl = getClass.getResource("/sbt-mill-init-export-build-assembly.jar").toExternalForm + */ + val file = tempDir / "mill-init.sbt" + val sbtPluginJarName = "sbt-mill-init-export-build-assembly.jar" + val sbtPluginJarStream = getClass.getResourceAsStream(s"/$sbtPluginJarName") + val sbtPluginJarPath = tempDir / sbtPluginJarName + os.write(sbtPluginJarPath, sbtPluginJarStream) val contents = s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-export-build" % "dummy-version" from ${escape( - sbtPluginJarUrl + sbtPluginJarPath.wrapped.toUri.toString )}) |""".stripMargin os.write(file, contents) @@ -231,10 +244,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No def getMillSourcePath(project: Project): Path = os.Path(project.projectDirectory) def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Project]): Seq[String] = - Seq("RootModule") ++ - Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { - getModuleSupertypes(cfg) - }.iterator.toSeq.flatten + Seq("RootModule") ++ getModuleSupertypes(cfg) def groupArtifactVersion(dep: Dependency): (String, String, String) = (dep.organization, dep.name, dep.revision) @@ -254,7 +264,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No def renderIvy(dependency: Dependency): String = { // type, classifier, and exclusions are not processed yet import dependency.* - s"ivy\"$organization:$name${if (crossVersion) "::" else ":"}$revision\"" + s"ivy\"$organization${if (crossVersion) "::" else ":"}$name:$revision\"" } def extractPomSettings(buildPublicationInfo: BuildPublicationInfo): IrPom = { diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index def71c87d0c..4eb6a02ebff 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -11,29 +11,29 @@ object Deps { ivy"ch.qos.logback:logback-classic:1.2.3" val `com.github.julien-truffaut:monocle-core` = - ivy"com.github.julien-truffaut:monocle-core::1.4.0" + ivy"com.github.julien-truffaut::monocle-core:1.4.0" val `com.github.julien-truffaut:monocle-macro` = - ivy"com.github.julien-truffaut:monocle-macro::1.4.0" + ivy"com.github.julien-truffaut::monocle-macro:1.4.0" val `com.github.pureconfig:pureconfig` = - ivy"com.github.pureconfig:pureconfig::0.8.0" + ivy"com.github.pureconfig::pureconfig:0.8.0" val `com.typesafe.akka:akka-stream` = - ivy"com.typesafe.akka:akka-stream::2.5.6" + ivy"com.typesafe.akka::akka-stream:2.5.6" val `com.typesafe.scala-logging:scala-logging` = - ivy"com.typesafe.scala-logging:scala-logging::3.7.2" + ivy"com.typesafe.scala-logging::scala-logging:3.7.2" val `com.typesafe:config` = ivy"com.typesafe:config:1.3.1" val `net.logstash.logback:logstash-logback-encoder` = ivy"net.logstash.logback:logstash-logback-encoder:4.11" - val `org.scalacheck:scalacheck` = ivy"org.scalacheck:scalacheck::1.13.5" - val `org.scalatest:scalatest` = ivy"org.scalatest:scalatest::3.0.4" + val `org.scalacheck:scalacheck` = ivy"org.scalacheck::scalacheck:1.13.5" + val `org.scalatest:scalatest` = ivy"org.scalatest::scalatest:3.0.4" val `org.slf4j:jcl-over-slf4j` = ivy"org.slf4j:jcl-over-slf4j:1.7.25" } -object `package` extends RootModule { +object `package` extends RootModule with BaseModule { def artifactName = "sbt-multi-project-example" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill index d2e18d855d0..f5bb1ab05f6 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/build.mill @@ -6,7 +6,7 @@ import mill.javalib._ import mill.javalib.publish._ import mill.scalalib.SbtModule -object `package` extends RootModule { +object `package` extends RootModule with SbtModule with PublishModule { def artifactName = "sbt-multi-project-example" diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index 21acfc30af1..5fd5231ef42 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -34,8 +34,8 @@ object `package` extends RootModule with SbtModule with PublishModule { def ivyDeps = super.ivyDeps() ++ Agg( ivy"ch.qos.logback:logback-classic:1.2.3", - ivy"com.typesafe.akka:akka-stream::2.5.6", - ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.typesafe.akka::akka-stream:2.5.6", + ivy"com.typesafe.scala-logging::scala-logging:3.7.2", ivy"com.typesafe:config:1.3.1", ivy"net.logstash.logback:logstash-logback-encoder:4.11", ivy"org.slf4j:jcl-over-slf4j:1.7.25" @@ -67,8 +67,8 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Agg( - ivy"org.scalacheck:scalacheck::1.13.5", - ivy"org.scalatest:scalatest::3.0.4" + ivy"org.scalacheck::scalacheck:1.13.5", + ivy"org.scalatest::scalatest:3.0.4" ) } diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index 408f966cd80..289c6ad4c5e 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -34,10 +34,10 @@ object `package` extends RootModule with SbtModule with PublishModule { def ivyDeps = super.ivyDeps() ++ Agg( ivy"ch.qos.logback:logback-classic:1.2.3", - ivy"com.github.julien-truffaut:monocle-core::1.4.0", - ivy"com.github.julien-truffaut:monocle-macro::1.4.0", - ivy"com.typesafe.akka:akka-stream::2.5.6", - ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.github.julien-truffaut::monocle-core:1.4.0", + ivy"com.github.julien-truffaut::monocle-macro:1.4.0", + ivy"com.typesafe.akka::akka-stream:2.5.6", + ivy"com.typesafe.scala-logging::scala-logging:3.7.2", ivy"com.typesafe:config:1.3.1", ivy"net.logstash.logback:logstash-logback-encoder:4.11", ivy"org.slf4j:jcl-over-slf4j:1.7.25" @@ -71,8 +71,8 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Agg( - ivy"org.scalacheck:scalacheck::1.13.5", - ivy"org.scalatest:scalatest::3.0.4" + ivy"org.scalacheck::scalacheck:1.13.5", + ivy"org.scalatest::scalatest:3.0.4" ) } diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index 02a8ac8c169..539322907ea 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -34,9 +34,9 @@ object `package` extends RootModule with SbtModule with PublishModule { def ivyDeps = super.ivyDeps() ++ Agg( ivy"ch.qos.logback:logback-classic:1.2.3", - ivy"com.github.pureconfig:pureconfig::0.8.0", - ivy"com.typesafe.akka:akka-stream::2.5.6", - ivy"com.typesafe.scala-logging:scala-logging::3.7.2", + ivy"com.github.pureconfig::pureconfig:0.8.0", + ivy"com.typesafe.akka::akka-stream:2.5.6", + ivy"com.typesafe.scala-logging::scala-logging:3.7.2", ivy"com.typesafe:config:1.3.1", ivy"net.logstash.logback:logstash-logback-encoder:4.11", ivy"org.slf4j:jcl-over-slf4j:1.7.25" @@ -70,8 +70,8 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Agg( - ivy"org.scalacheck:scalacheck::1.13.5", - ivy"org.scalatest:scalatest::3.0.4" + ivy"org.scalacheck::scalacheck:1.13.5", + ivy"org.scalatest::scalatest:3.0.4" ) } diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index 27e391369ee..70b81474009 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -24,7 +24,7 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.Munit { - def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalameta:munit::0.7.29") + def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalameta::munit:0.7.29") } } From 31154ac02590f5d1230d7452d235751eeccbdee7 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 18 Feb 2025 06:16:46 +0800 Subject: [PATCH 24/70] Add a TODO --- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index f21dc02dbfc..eae0a42c3e4 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -262,7 +262,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No // originally named `ivyInterp` in the Maven and module def renderIvy(dependency: Dependency): String = { - // type, classifier, and exclusions are not processed yet + // TODO type, classifier, and exclusions are not processed yet import dependency.* s"ivy\"$organization${if (crossVersion) "::" else ":"}$name:$revision\"" } From 3d2fafed6a2cdcbce2f8c6cc79a54e3c63e68e66 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 02:43:23 +0800 Subject: [PATCH 25/70] Add the example tests, and add more and rearrange the integration tests after manually running and tweaking them --- docs/modules/ROOT/pages/migrating/sbt.adoc | 9 +- example/package.mill | 1 + .../migrating/1-sbt-complete/build.mill | 26 +++ .../migrating/2-sbt-incomplete/build.mill | 35 ++++ .../feature/init/src/MillInitSbtTests.scala | 151 +++++++++++++----- .../sbt/models/src/mill/main/sbt/Models.scala | 2 +- 6 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 example/scalalib/migrating/1-sbt-complete/build.mill create mode 100644 example/scalalib/migrating/2-sbt-incomplete/build.mill diff --git a/docs/modules/ROOT/pages/migrating/sbt.adoc b/docs/modules/ROOT/pages/migrating/sbt.adoc index 51c3956bae2..825b0710296 100644 --- a/docs/modules/ROOT/pages/migrating/sbt.adoc +++ b/docs/modules/ROOT/pages/migrating/sbt.adoc @@ -15,11 +15,11 @@ A nested `test` module is defined, if `src/test` exists, and is configured with Again, note that `mill init` imports an sbt build on a best-effort basis. This means that while simple projects can be expected to complete without issue: -include::partial$example/javalib/migrating/3-sbt-complete.adoc[] +include::partial$example/scalalib/migrating/1-sbt-complete.adoc[] Projects with a complex build often require some manual tweaking in order to work: -include::partial$example/javalib/migrating/4-sbt-incomplete.adoc[] +include::partial$example/scalalib/migrating/2-sbt-incomplete.adoc[] == Capabilities @@ -109,7 +109,10 @@ or xref:extending/thirdparty-plugins.adoc[third party] plugins == FAQ -How to fix `java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release` error thrown by the sbt command invoked by `mill init`? +How to fix errors such as +`java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release` and +`java.io.IOError: java.lang.RuntimeException: /packages cannot be represented as URI` +thrown by the sbt command invoked by `mill init`? Update the project's sbt version to the latest or our tested version v1.10.7, and try again. diff --git a/example/package.mill b/example/package.mill index ea564d19c17..aee97163090 100644 --- a/example/package.mill +++ b/example/package.mill @@ -65,6 +65,7 @@ object `package` extends RootModule with Module { object publishing extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "publishing")) object web extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "web")) object native extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "native")) + object migrating extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "migrating")) } object javascriptlib extends Module { object basic extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "basic")) diff --git a/example/scalalib/migrating/1-sbt-complete/build.mill b/example/scalalib/migrating/1-sbt-complete/build.mill new file mode 100644 index 00000000000..5eb364e717a --- /dev/null +++ b/example/scalalib/migrating/1-sbt-complete/build.mill @@ -0,0 +1,26 @@ +/** Usage + + > rm build.mill # remove any existing build file + + > git init . + > git remote add -f origin https://github.com/scalacenter/library-example.git + > git checkout v1.0.1 + + > ./mill init +converting module library-example +Dependency Dependency(com.lightbend.paradox,paradox-theme-generic,false,0.4.4,Some(paradox-theme)) with an unknown configuration "paradox-theme" is dropped. +Dependency Dependency(com.lightbend.paradox,paradox-theme-generic,false,0.4.4,Some(paradox-theme)) with an unknown configuration "paradox-theme" is dropped. +generated 1 Mill build file(s) +removing existing Mill build files +writing Mill build file to build.mill +converted sbt build to Mill +formatting Mill build files +Formatting 1 Scala sources +parsed config (v3.8.4): /var/folders/xh/9t7mxl615nv8627_v54lc9ph0000gn/T/3390681381000847719.tmp +init completed, run "mill resolve _" to list available tasks + + > ./mill compile + [46/46] compile + [46] [info] compiling 1 Scala source to /.../libsam/out/compile.dest/classes ... + [46] [info] done compiling +*/ diff --git a/example/scalalib/migrating/2-sbt-incomplete/build.mill b/example/scalalib/migrating/2-sbt-incomplete/build.mill new file mode 100644 index 00000000000..1508ddbdd3b --- /dev/null +++ b/example/scalalib/migrating/2-sbt-incomplete/build.mill @@ -0,0 +1,35 @@ +/** Usage + +> rm build.mill # remove any existing build file + +> git init . +> git remote add -f origin https://github.com/tototoshi/scala-csv.git +> git checkout 2.0.0 + +> ./mill init +converting sbt build +Running the added `millInitExportBuild` sbt task to export the build +[info] welcome to sbt 1.10.0 (Eclipse Adoptium Java 17.0.14) +[info] loading settings for project test-build from plugins.sbt,mill-init.sbt ... +[info] loading project definition from /.../project +[info] loading settings for project test from build.sbt,doctest.sbt ... +[info] set current project to scala-csv (in build file:/.../) +[success] Total time: 0 s, completed Feb 19, 2025, 1:42:45 AM +converting module scala-csv +generated 1 Mill build file(s) +removing existing Mill build files +writing Mill build file to build.mill +converted sbt build to Mill +formatting Mill build files +Formatting 1 Scala sources +parsed config (v3.8.4): /var/folders/xh/9t7mxl615nv8627_v54lc9ph0000gn/T/17040603558958375710.tmp +init completed, run "mill resolve _" to list available tasks + +> ./mill compile # You will have to further configure the `CrossScalaModule` for different Scala versions + [46/46] compile + [46] [info] compiling 7 Scala sources and 3 Java sources to /.../out/compile.dest/classes ... + [46] [error] /.../src/main/scala/com/github/tototoshi/csv/CSVReader.scala:24:115: not found: type CSVReaderCompat + [46] [error] class CSVReader protected (private val lineReader: LineReader)(implicit format: CSVFormat) extends Closeable with CSVReaderCompat { + [46] [error] ^ + [46] [error] one error found +*/ diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 183d8e50b5e..14017277173 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -1,28 +1,28 @@ package mill.integration -import mill.integration.MillInitSbtTests.initCommand +import mill.integration.MillInitSbtTests.{bumpSbtTo1107, initCommand} import utest.* object MillInitSbtTests { val initCommand = ("init", "--base-module", "BaseModule", "--deps-object", "Deps", "--merge") + def bumpSbtTo1107(workspacePath: os.Path) = + // bump sbt version to resolve compatibility issues with lower sbt versions and higher JDK versions + os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") } -// Relatively large libraries +// relatively small libraries -// Scala.js and scala-native projects are not properly imported -object MillInitSbtScalazTests extends BuildGenTestSuite { +object MillInitLibraryExampleTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 0.8 MB - - sbt 1.10.7 + - 21 KB + - sbt 1.5.2 */ - val url = "https://github.com/scalaz/scalaz/archive/refs/tags/v7.3.8.zip" + val url = "https://github.com/scalacenter/library-example/archive/refs/tags/v1.0.1.zip" test - integrationTest(url) { tester => import tester.* - os.call(("chmod", "+x", "sbt"), cwd = workspacePath) - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) @@ -32,59 +32,87 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { } } -// Scala.js and scala-native projects are not properly imported -object MillInitSbtCatsTests extends BuildGenTestSuite { - +object MillInitScalaCsv200Tests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 2 MB + - 34 KB - sbt 1.10.7 - - MUnit */ - val url = "https://github.com/typelevel/cats/archive/refs/tags/v2.13.0.zip" + val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/2.0.0.zip" + + test - integrationTest(url) { tester => + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + // Cross builds are not supported yet. + assert(!compileResult.isSuccess) + } + } +} + +object MillInitScalaCsv136Tests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 28 KB + - originally sbt 1.2.8 + */ + val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/1.3.6.zip" test - integrationTest(url) { tester => import tester.* + bumpSbtTo1107(workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") + // It works here, but scala 2.11 with JDK 6 seems not supported when run with JDK 17 in shell. assert(compileResult.isSuccess) } } } -// Converting child projects nested in a parent directory which is not a project is not supported yet. -object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { +// same as the one in the unit tests +object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 5 MB - - sbt 1.10.5 + - 10 KB + - originally sbt 1.0.2 */ - val url = "https://github.com/playframework/playframework/archive/refs/tags/3.0.6.zip" + val url = + "https://github.com/pbassiner/sbt-multi-project-example/archive/152b31df9837115b183576b0080628b43c505389.zip" test - integrationTest(url) { tester => import tester.* + bumpSbtTo1107(workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") assert(compileResult.isSuccess) + + // Submodules don't compile well, which seems to be due to incompatible bytecode versions in dependencies. + val compileSubmodulesResult = eval("_.compile") + assert(!compileSubmodulesResult.isSuccess) } } } -// Scala.js and scala-native projects are not properly imported -object MillInitSbtScalaCheckTests extends BuildGenTestSuite { +// relatively large libraries +object MillInitZioHttpTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 0.5 MB - - sbt 1.10.1 + - 1.4 MB + - sbt 1.10.6 */ - val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" + val url = "https://github.com/zio/zio-http/archive/refs/tags/v3.0.1.zip" test - integrationTest(url) { tester => import tester.* @@ -94,56 +122,97 @@ object MillInitSbtScalaCheckTests extends BuildGenTestSuite { val compileResult = eval("compile") assert(compileResult.isSuccess) + + val compileSubmodulesResult = eval("_.compile") + // Some dependencies with currently unsupported `CrossVersion` `For3Use2_13` are not imported properly + assert(!compileSubmodulesResult.isSuccess) } } } -// Relatively small libraries - -object MillInitScalaCsvTests extends BuildGenTestSuite { +// Scala.js and scala-native projects are not properly imported +object MillInitSbtScalazTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 25 KB + - 0.8 MB - sbt 1.10.7 */ - val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/2.0.0.zip" + val url = "https://github.com/scalaz/scalaz/archive/refs/tags/v7.3.8.zip" test - integrationTest(url) { tester => import tester.* + os.call(("chmod", "+x", "sbt"), cwd = workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") - // Cross builds are not supported yet. - assert(!compileResult.isSuccess) + assert(compileResult.isSuccess) } } } -// same as the one in the unit tests -object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { +// Scala.js and scala-native projects are not properly imported +object MillInitSbtCatsTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 10 KB + - 2 MB + - sbt 1.10.7 + - MUnit */ - val url = - "https://github.com/pbassiner/sbt-multi-project-example/archive/152b31df9837115b183576b0080628b43c505389.zip" + val url = "https://github.com/typelevel/cats/archive/refs/tags/v2.13.0.zip" test - integrationTest(url) { tester => import tester.* - // sbt version bumped so it works with JDK 21 - os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) + } + } +} + +// Converting child projects nested in a parent directory which is not a project is not supported yet. +object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 5 MB + - sbt 1.10.5 + */ + val url = "https://github.com/playframework/playframework/archive/refs/tags/3.0.6.zip" + + test - integrationTest(url) { tester => + import tester.* val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") assert(compileResult.isSuccess) + } + } +} + +// Scala.js and scala-native projects are not properly imported +object MillInitSbtScalaCheckTests extends BuildGenTestSuite { + def tests: Tests = Tests { + /* + - 0.5 MB + - sbt 1.10.1 + */ + val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" + + test - integrationTest(url) { tester => + import tester.* - val testResult = eval("test") - assert(testResult.isSuccess) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess) + + val compileResult = eval("compile") + assert(compileResult.isSuccess) } } } diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 68cedd06d07..9fb9d7e8812 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -131,7 +131,7 @@ object Project { case class Dependency( organization: String, // `groupId` in Maven name: String, // `artifactId` in Maven - crossVersion: Boolean = false, + crossVersion: Boolean = false, // TODO support `CrossVersion` types other than binary? revision: String, configurations: Option[String] // BOM seems not supported by sbt. See https://stackoverflow.com/questions/42032303/how-do-i-use-a-maven-bom-bill-of-materials-to-manage-my-dependencies-in-sbt. From 44f6ada53212ba0e0f123334f2a5013d43147d21 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 17:58:06 +0800 Subject: [PATCH 26/70] Set up sbt in the CI to fix the failing CI tests --- .github/actions/pre-build-setup/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/pre-build-setup/action.yml b/.github/actions/pre-build-setup/action.yml index 585dc24d21b..85608ea991c 100644 --- a/.github/actions/pre-build-setup/action.yml +++ b/.github/actions/pre-build-setup/action.yml @@ -65,4 +65,6 @@ runs: - uses: actions/checkout@v4 - run: echo temurin:${{ inputs.java-version }} > .mill-jvm-version - shell: bash \ No newline at end of file + shell: bash + + - uses: sbt/setup-sbt@v1 From 802f0057237bf0f96f6b5d1541953a206d900fdf Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 18:37:22 +0800 Subject: [PATCH 27/70] Move `setup-sbt` to `post-build-setup` --- .github/actions/post-build-setup/action.yml | 4 +++- .github/actions/pre-build-setup/action.yml | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/post-build-setup/action.yml b/.github/actions/post-build-setup/action.yml index ab854420f53..63ee9c9c38e 100644 --- a/.github/actions/post-build-setup/action.yml +++ b/.github/actions/post-build-setup/action.yml @@ -34,4 +34,6 @@ runs: name: ${{ inputs.os }}-selective-execution-artifact - run: mv out/mill-selective-execution/mill-selective-execution.json out/mill-selective-execution.json - shell: bash \ No newline at end of file + shell: bash + + - uses: sbt/setup-sbt@v1 diff --git a/.github/actions/pre-build-setup/action.yml b/.github/actions/pre-build-setup/action.yml index 85608ea991c..720e58df130 100644 --- a/.github/actions/pre-build-setup/action.yml +++ b/.github/actions/pre-build-setup/action.yml @@ -66,5 +66,3 @@ runs: - run: echo temurin:${{ inputs.java-version }} > .mill-jvm-version shell: bash - - - uses: sbt/setup-sbt@v1 From 39baa2dc5980477d60a39c4eb51a6fb3403c26eb Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 18:47:42 +0800 Subject: [PATCH 28/70] Refine a TODO --- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index eae0a42c3e4..3e286818dfe 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -262,7 +262,11 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No // originally named `ivyInterp` in the Maven and module def renderIvy(dependency: Dependency): String = { - // TODO type, classifier, and exclusions are not processed yet + /* + TODO `type, `classifier`, and `exclusions` are not processed yet. + Processing them involves extracting information from `ModuleID.explicitArtifacts` + which is a `Vector` of `sbt.librarymanagement.Artifact` and `sbt.librarymanagement.InclExclRule`s. + */ import dependency.* s"ivy\"$organization${if (crossVersion) "::" else ":"}$name:$revision\"" } From 3db82884d3a016664804885a923a81b797cfbad6 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 19:48:27 +0800 Subject: [PATCH 29/70] Fix compiling and test issues after the merge The following tests are fixed: * `./mill main.init.test` * `./mill main.init.sbt.test` * `./mill 'integration.feature[init].packaged.server'` Expected snapshots in test resource are updated accordingly. --- .../config/sbt-multi-project-example/build.mill | 12 ++++++------ .../sbt-multi-project-example/common/package.mill | 4 ++-- .../sbt-multi-project-example/multi1/package.mill | 4 ++-- .../sbt-multi-project-example/multi2/package.mill | 4 ++-- .../resources/expected/scala-seed-project/build.mill | 2 +- main/init/src/mill/init/InitSbtModule.scala | 6 +++--- main/src/mill/main/MainModule.scala | 3 +-- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index 4eb6a02ebff..4807d5264e1 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -39,7 +39,7 @@ object `package` extends RootModule with BaseModule { object multi1 extends BaseModule { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.julien-truffaut:monocle-core`, Deps.`com.github.julien-truffaut:monocle-macro`, @@ -55,14 +55,14 @@ object `package` extends RootModule with BaseModule { object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ - Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) } } object multi2 extends BaseModule { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.pureconfig:pureconfig`, Deps.`com.typesafe.akka:akka-stream`, @@ -77,14 +77,14 @@ object `package` extends RootModule with BaseModule { object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ - Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) } } object common extends BaseModule { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.typesafe.akka:akka-stream`, Deps.`com.typesafe.scala-logging:scala-logging`, @@ -96,7 +96,7 @@ object `package` extends RootModule with BaseModule { object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ - Agg(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) } } diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index 5fd5231ef42..d95ee7c1d5e 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -32,7 +32,7 @@ object `package` extends RootModule with SbtModule with PublishModule { ) } - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"ch.qos.logback:logback-classic:1.2.3", ivy"com.typesafe.akka::akka-stream:2.5.6", ivy"com.typesafe.scala-logging::scala-logging:3.7.2", @@ -66,7 +66,7 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", ivy"org.scalatest::scalatest:3.0.4" ) diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index 289c6ad4c5e..80aea6a7077 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -32,7 +32,7 @@ object `package` extends RootModule with SbtModule with PublishModule { ) } - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"ch.qos.logback:logback-classic:1.2.3", ivy"com.github.julien-truffaut::monocle-core:1.4.0", ivy"com.github.julien-truffaut::monocle-macro:1.4.0", @@ -70,7 +70,7 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", ivy"org.scalatest::scalatest:3.0.4" ) diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index 539322907ea..c5df60d3df4 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -32,7 +32,7 @@ object `package` extends RootModule with SbtModule with PublishModule { ) } - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"ch.qos.logback:logback-classic:1.2.3", ivy"com.github.pureconfig::pureconfig:0.8.0", ivy"com.typesafe.akka::akka-stream:2.5.6", @@ -69,7 +69,7 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.ScalaTest { - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", ivy"org.scalatest::scalatest:3.0.4" ) diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index 70b81474009..a605637f48e 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -24,7 +24,7 @@ object `package` extends RootModule with SbtModule with PublishModule { object test extends MavenTests with TestModule.Munit { - def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.scalameta::munit:0.7.29") + def ivyDeps = super.ivyDeps() ++ Seq(ivy"org.scalameta::munit:0.7.29") } } diff --git a/main/init/src/mill/init/InitSbtModule.scala b/main/init/src/mill/init/InitSbtModule.scala index 4cf6dbd064a..fa55853a950 100644 --- a/main/init/src/mill/init/InitSbtModule.scala +++ b/main/init/src/mill/init/InitSbtModule.scala @@ -1,14 +1,14 @@ package mill.init import mill.T -import mill.api.{Loose, PathRef} +import mill.api.PathRef import mill.define.{Discover, ExternalModule} @mill.api.experimental object InitSbtModule extends ExternalModule with BuildGenModule { - lazy val millDiscover: Discover = Discover[this.type] + lazy val millDiscover = Discover[this.type] - def buildGenClasspath: T[Loose.Agg[PathRef]] = BuildGenModule.millModule("mill-main-init-sbt") + def buildGenClasspath: T[Seq[PathRef]] = BuildGenModule.millModule("mill-main-init-sbt") def buildGenMainClass: T[String] = "mill.main.sbt.SbtBuildGenMain" } diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index 449a0b88503..2f6c126b71e 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -279,8 +279,7 @@ trait MainModule extends BaseModule { SelectMode.Separated ) else if (os.exists(os.pwd / "build.sbt")) - RunScript.evaluateTasksNamed( - evaluator, + evaluator.resolveEvaluate( Seq("mill.init.InitSbtModule/init") ++ args, SelectMode.Separated ) From 918359e5d649f7cd6df809206c3c4f34bb8beb21 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 19 Feb 2025 21:54:00 +0800 Subject: [PATCH 30/70] Split the `sbt assembly` command into a tuple, and replace `scala.sys.process` APIs with `os.call` APIs for consistency --- main/init/package.mill | 9 ++++----- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/main/init/package.mill b/main/init/package.mill index 90ff867d9c4..811626b8758 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -107,14 +107,13 @@ object `package` extends RootModule with build.MillPublishScalaModule { sbtPluginProjectSource() - import scala.sys.process._ val version = build.millVersion() if ( - Process( + os.call( // The version is passed to the sbt build so it correctly resolves the "models" dependency version and sets its own version. - s"""sbt 'set version := "$version"' assembly""", - sbtPluginProjectPath.toIO - ).! != 0 + ("sbt", s"""set version := "$version"""", "assembly"), + cwd = sbtPluginProjectPath + ).exitCode != 0 ) throw new RuntimeException( "Failed to run `sbt assembly` in the \"sbt-mill-init-export-build\" sbt project. " + diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 3e286818dfe..2a3103aa897 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -71,7 +71,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No "./sbtx" else if ( // The return code is somehow 1 instead of 0. - Seq("sbt", "--help").!(ProcessLogger(_ => ())) == 1 + os.call(("sbt", "--help"), check = false).exitCode == 1 ) "sbt" else @@ -81,14 +81,14 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("Running the added `millInitExportBuild` sbt task to export the build") - val exitCode = Process( + val exitCode = os.call( Seq( sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", "millInitExportBuild" ), - workspace.toIO - ).! + cwd = workspace + ).exitCode // println("Exit code from running the `millInitExportBuild` sbt task: " + exitCode) if (exitCode != 0) From 4419d7fd1b7d398f02917a8292e63e94d132feee Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 01:18:46 +0800 Subject: [PATCH 31/70] Fix calling the sbt command in the `sbtPluginJarResources` Mill task on Windows, and try fixing a similar issue in `SbtBuildGenMain` The command `sbt -addPluginSbtFile=test.sbt` doesn't run properly on my Windows machine with the following error: ```text [error] Expected ':' [error] Expected '=' [error] Expected whitespace character [error] -addPluginSbtFile [error] ^ ``` `./mill main.init.sbt.test` fails with the error above in all 3 tests too. --- main/init/package.mill | 7 +++++-- .../src/mill/main/sbt/SbtBuildGenMain.scala | 18 ++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/main/init/package.mill b/main/init/package.mill index 811626b8758..716171fc646 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -107,12 +107,15 @@ object `package` extends RootModule with build.MillPublishScalaModule { sbtPluginProjectSource() + val isWindows = System.getProperty("os.name").toLowerCase.startsWith("windows") val version = build.millVersion() if ( os.call( // The version is passed to the sbt build so it correctly resolves the "models" dependency version and sets its own version. - ("sbt", s"""set version := "$version"""", "assembly"), - cwd = sbtPluginProjectPath + if (isWindows) ("sbt.bat", s"""set version := \\"$version\\"""", "assembly") + else ("sbt", s"""set version := "$version"""", "assembly"), + cwd = sbtPluginProjectPath, + stdout = os.Inherit ).exitCode != 0 ) throw new RuntimeException( diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 2a3103aa897..5637fdb01c4 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -1,6 +1,7 @@ package mill.main.sbt import mainargs.{ParserForClass, main} +import mill.constants.Util import mill.main.buildgen.* import mill.main.buildgen.BuildGenUtil.* import mill.main.buildgen.IrDependencyType.* @@ -61,7 +62,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("converting sbt build") - import scala.sys.process.* + val systemSbt = if (Util.isWindows) "sbt.bat" else "sbt" // resolve the sbt executable // https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt @@ -71,23 +72,20 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No "./sbtx" else if ( // The return code is somehow 1 instead of 0. - os.call(("sbt", "--help"), check = false).exitCode == 1 + os.call((systemSbt, "--help"), check = false).exitCode == 1 ) - "sbt" + systemSbt else throw new RuntimeException( - "No sbt executable (`./sbt`, `./sbtx`, or system-wide `sbt`) found" + s"No sbt executable (`./sbt`, `./sbtx`, or system-wide `$systemSbt`) found" ) println("Running the added `millInitExportBuild` sbt task to export the build") val exitCode = os.call( - Seq( - sbtExecutable, - s"-addPluginSbtFile=${writeSbtFile().toString}", - "millInitExportBuild" - ), - cwd = workspace + (sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", "millInitExportBuild"), + cwd = workspace, + stdout = os.Inherit ).exitCode // println("Exit code from running the `millInitExportBuild` sbt task: " + exitCode) From 68d830f670ba552c278a8149f4d05264839643a7 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 02:44:14 +0800 Subject: [PATCH 32/70] Get the `sbt millInitExportBuild` command to work on Windows with a workaround of putting the ".sbt" file directly in the sbt "project" directory `main.init.sbt.test.test` result: ```text Tests: 3, Passed: 2, Failed: 1 ``` --- .../src/mill/main/sbt/SbtBuildGenMain.scala | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 5637fdb01c4..d4d9907f4a1 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -62,7 +62,8 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("converting sbt build") - val systemSbt = if (Util.isWindows) "sbt.bat" else "sbt" + val isWindows = Util.isWindows + val systemSbt = if (isWindows) "sbt.bat" else "sbt" // resolve the sbt executable // https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt @@ -82,11 +83,35 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("Running the added `millInitExportBuild` sbt task to export the build") - val exitCode = os.call( - (sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", "millInitExportBuild"), - cwd = workspace, - stdout = os.Inherit - ).exitCode + val exitCode = ( + if (isWindows) { + /* + `-addPluginSbtFile` somehow doesn't work on Windows, therefore, the ".sbt" file is put directly in the sbt "project" directory. + The error message: + ```text + [error] Expected ':' + [error] Expected '=' + [error] Expected whitespace character + [error] -addPluginSbtFile + [error] ^ + ``` + */ + val sbtFile = writeTempSbtFileInSbtProjectDirectory(workspace) + val commandResult = os.call( + (sbtExecutable, "millInitExportBuild"), + cwd = workspace, + stdout = os.Inherit + ) + os.remove(sbtFile) + commandResult + } else + os.call( + (sbtExecutable, s"-addPluginSbtFile=${writeSbtFile().toString}", "millInitExportBuild"), + cwd = workspace, + stdout = os.Inherit + ) + ) + .exitCode // println("Exit code from running the `millInitExportBuild` sbt task: " + exitCode) if (exitCode != 0) @@ -127,27 +152,42 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("converted sbt build to Mill") } - private def writeSbtFile(): os.Path = { + /** + * @return the temp directory the jar is in and the sbt file contents. + */ + private def copyExportBuildAssemblyJarOutAndGetSbtFileContents(): (os.Path, String) = { val tempDir = os.temp.dir() // This doesn't work in integration tests when Mill is packaged. /* val sbtPluginJarUrl = getClass.getResource("/sbt-mill-init-export-build-assembly.jar").toExternalForm */ - val file = tempDir / "mill-init.sbt" val sbtPluginJarName = "sbt-mill-init-export-build-assembly.jar" val sbtPluginJarStream = getClass.getResourceAsStream(s"/$sbtPluginJarName") val sbtPluginJarPath = tempDir / sbtPluginJarName os.write(sbtPluginJarPath, sbtPluginJarStream) val contents = - s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-export-build" % "dummy-version" from ${escape( - sbtPluginJarPath.wrapped.toUri.toString - )}) + s"""addSbtPlugin("com.lihaoyi" % "mill-main-init-sbt-sbt-mill-init-export-build" % "dummy-version" from ${ + escape(sbtPluginJarPath.wrapped.toUri.toString) + }) |""".stripMargin - os.write(file, contents) - file + (tempDir, contents) } + private def writeSbtFile(): os.Path = { + val (tempDir, contents) = copyExportBuildAssemblyJarOutAndGetSbtFileContents() + val sbtFile = tempDir / "mill-init.sbt" + os.write(sbtFile, contents) + sbtFile + } + + private def writeTempSbtFileInSbtProjectDirectory(workspace: os.Path) = + os.temp( + copyExportBuildAssemblyJarOutAndGetSbtFileContents()._2, + workspace / "project", + suffix = ".sbt" + ) + override def getProjectTree(input: (BuildInfo, Tree[Node[Project]])): Tree[Node[Project]] = input._2 From 7155ec8beb27e95a2d51b40e5085ab8aca676a0b Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 20 Feb 2025 03:07:51 +0800 Subject: [PATCH 33/70] Fix a bug that the exported projects are ordered differently in different `sbt millInitExportBuild` runs and on different OSs, causing `main.init.sbt.test` to fail on other OSs --- .../sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 3 +++ .../config/sbt-multi-project-example/build.mill | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index d4d9907f4a1..d49e235f207 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -125,12 +125,15 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No import upickle.default.* val buildExport = read[BuildExport](buildExportPickled) + import scala.math.Ordering.Implicits.* // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. val projectNodesByParentDirs: Map[Option[Seq[String]], View[Node[Project]]] = buildExport.projects.view .map(project => Node(os.Path(project.projectDirectory).subRelativeTo(workspace).segments, project) ) + // The projects are ordered differently in different `sbt millInitExportBuild` runs and on different OSs, which is strange. + .sortBy(_.dirs) .groupBy(node => { val dirs = node.dirs Option.when(dirs.nonEmpty)(dirs.dropRight(1)) diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index 4807d5264e1..b65e2b7c1d0 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -37,12 +37,10 @@ object `package` extends RootModule with BaseModule { def artifactName = "sbt-multi-project-example" - object multi1 extends BaseModule { + object common extends BaseModule { def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, - Deps.`com.github.julien-truffaut:monocle-core`, - Deps.`com.github.julien-truffaut:monocle-macro`, Deps.`com.typesafe.akka:akka-stream`, Deps.`com.typesafe.scala-logging:scala-logging`, Deps.`com.typesafe:config`, @@ -50,8 +48,6 @@ object `package` extends RootModule with BaseModule { Deps.`org.slf4j:jcl-over-slf4j` ) - def moduleDeps = super.moduleDeps ++ Seq(build.common) - object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ @@ -60,11 +56,12 @@ object `package` extends RootModule with BaseModule { } } - object multi2 extends BaseModule { + object multi1 extends BaseModule { def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, - Deps.`com.github.pureconfig:pureconfig`, + Deps.`com.github.julien-truffaut:monocle-core`, + Deps.`com.github.julien-truffaut:monocle-macro`, Deps.`com.typesafe.akka:akka-stream`, Deps.`com.typesafe.scala-logging:scala-logging`, Deps.`com.typesafe:config`, @@ -82,10 +79,11 @@ object `package` extends RootModule with BaseModule { } } - object common extends BaseModule { + object multi2 extends BaseModule { def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, + Deps.`com.github.pureconfig:pureconfig`, Deps.`com.typesafe.akka:akka-stream`, Deps.`com.typesafe.scala-logging:scala-logging`, Deps.`com.typesafe:config`, @@ -93,6 +91,8 @@ object `package` extends RootModule with BaseModule { Deps.`org.slf4j:jcl-over-slf4j` ) + def moduleDeps = super.moduleDeps ++ Seq(build.common) + object tests extends MavenTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ From 3ef1bef6c24dd4fe441f9671d2901a2695d68c3c Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 16:57:50 +0800 Subject: [PATCH 34/70] Try fixing integration test timeout in the CI --- integration/feature/init/src/MillInitSbtTests.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 14017277173..8076b4bb7b5 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -80,7 +80,7 @@ object MillInitScalaCsv136Tests extends BuildGenTestSuite { object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 10 KB + - 12 KB - originally sbt 1.0.2 */ val url = @@ -157,7 +157,7 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { object MillInitSbtCatsTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 2 MB + - 1.9 MB - sbt 1.10.7 - MUnit */ @@ -177,9 +177,11 @@ object MillInitSbtCatsTests extends BuildGenTestSuite { // Converting child projects nested in a parent directory which is not a project is not supported yet. object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { + // Commented out as it causes `java.util.concurrent.TimeoutException: Future timed out after [600000 milliseconds]` in the CI. + /* def tests: Tests = Tests { /* - - 5 MB + - 4.8 MB - sbt 1.10.5 */ val url = "https://github.com/playframework/playframework/archive/refs/tags/3.0.6.zip" @@ -194,13 +196,14 @@ object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { assert(compileResult.isSuccess) } } + */ } // Scala.js and scala-native projects are not properly imported object MillInitSbtScalaCheckTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - - 0.5 MB + - 245 KB - sbt 1.10.1 */ val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" From 1655bb934cd16ae5880e4c18fcfb4af5d4578784 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 17:27:01 +0800 Subject: [PATCH 35/70] Fix a compiling error introduced in the previous commit --- integration/feature/init/src/MillInitSbtTests.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 8076b4bb7b5..bea432a0b4a 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -177,9 +177,9 @@ object MillInitSbtCatsTests extends BuildGenTestSuite { // Converting child projects nested in a parent directory which is not a project is not supported yet. object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { - // Commented out as it causes `java.util.concurrent.TimeoutException: Future timed out after [600000 milliseconds]` in the CI. - /* def tests: Tests = Tests { + // Commented out as it causes `java.util.concurrent.TimeoutException: Future timed out after [600000 milliseconds]` in the CI. + /* /* - 4.8 MB - sbt 1.10.5 @@ -195,8 +195,8 @@ object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { val compileResult = eval("compile") assert(compileResult.isSuccess) } + */ } - */ } // Scala.js and scala-native projects are not properly imported From 6b717b7423dd802a50bc25a2110fc6f1843d4d30 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 20 Feb 2025 19:47:46 +0800 Subject: [PATCH 36/70] Try fixing more integration test failures on CI, both on Linux with JDK 11 and Windows with JDK 17 --- .../feature/init/src/MillInitSbtTests.scala | 21 +++++++--- .../src/mill/main/sbt/SbtBuildGenMain.scala | 42 +++++++++++-------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index bea432a0b4a..4eb7f9ac838 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -36,13 +36,15 @@ object MillInitScalaCsv200Tests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 34 KB - - sbt 1.10.7 + - originally sbt 1.10.0 */ val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/2.0.0.zip" test - integrationTest(url) { tester => import tester.* + bumpSbtTo1107(workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) @@ -97,9 +99,12 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { val compileResult = eval("compile") assert(compileResult.isSuccess) - // Submodules don't compile well, which seems to be due to incompatible bytecode versions in dependencies. val compileSubmodulesResult = eval("_.compile") - assert(!compileSubmodulesResult.isSuccess) + if (System.getProperty("java.version").split('.').head.toInt <= 11) + assert(compileSubmodulesResult.isSuccess) + else + // Submodules don't compile well with JDK 17 and 21, which seems to be due to incompatible bytecode versions in dependencies. + assert(!compileSubmodulesResult.isSuccess) } } } @@ -110,13 +115,15 @@ object MillInitZioHttpTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 1.4 MB - - sbt 1.10.6 + - originally sbt 1.10.0 */ val url = "https://github.com/zio/zio-http/archive/refs/tags/v3.0.1.zip" test - integrationTest(url) { tester => import tester.* + bumpSbtTo1107(workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) @@ -135,7 +142,7 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 0.8 MB - - sbt 1.10.7 + - sbt 1.9.7 */ val url = "https://github.com/scalaz/scalaz/archive/refs/tags/v7.3.8.zip" @@ -204,13 +211,15 @@ object MillInitSbtScalaCheckTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 245 KB - - sbt 1.10.1 + - originally sbt 1.10.1 */ val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" test - integrationTest(url) { tester => import tester.* + bumpSbtTo1107(workspacePath) + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index d49e235f207..89e7bc64f5b 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -62,24 +62,32 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No println("converting sbt build") - val isWindows = Util.isWindows - val systemSbt = if (isWindows) "sbt.bat" else "sbt" - - // resolve the sbt executable - // https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt - val sbtExecutable = if (os.exists(workspace / "sbt")) - "./sbt" - else if (os.exists(workspace / "sbtx")) - "./sbtx" - else if ( + def systemSbtExists(sbt: String) = // The return code is somehow 1 instead of 0. - os.call((systemSbt, "--help"), check = false).exitCode == 1 - ) - systemSbt - else - throw new RuntimeException( - s"No sbt executable (`./sbt`, `./sbtx`, or system-wide `$systemSbt`) found" - ) + os.call((sbt, "--help"), check = false).exitCode == 1 + + val isWindows = Util.isWindows + val sbtExecutable = if (isWindows) { + val systemSbt = "sbt.bat" + if (systemSbtExists(systemSbt)) + systemSbt + else + throw new RuntimeException(s"No system-wide `$systemSbt` found") + } else { + val systemSbt = "sbt" + // resolve the sbt executable + // https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt + if (os.exists(workspace / "sbt")) + "./sbt" + else if (os.exists(workspace / "sbtx")) + "./sbtx" + else if (systemSbtExists(systemSbt)) + systemSbt + else + throw new RuntimeException( + s"No sbt executable (`./sbt`, `./sbtx`, or system-wide `$systemSbt`) found" + ) + } println("Running the added `millInitExportBuild` sbt task to export the build") From 018cd53f7fef453d1560dc502f2d365c5e8bfa4a Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 20 Feb 2025 21:07:35 +0800 Subject: [PATCH 37/70] Fix the failing example tests added in commit 3d2fafed6a2cdcbce2f8c6cc79a54e3c63e68e66 --- .../migrating/1-sbt-complete/build.mill | 17 +++++++---------- .../migrating/2-sbt-incomplete/build.mill | 16 +++------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/example/scalalib/migrating/1-sbt-complete/build.mill b/example/scalalib/migrating/1-sbt-complete/build.mill index 5eb364e717a..590ce14912b 100644 --- a/example/scalalib/migrating/1-sbt-complete/build.mill +++ b/example/scalalib/migrating/1-sbt-complete/build.mill @@ -1,12 +1,12 @@ /** Usage - > rm build.mill # remove any existing build file +> rm build.mill # remove any existing build file - > git init . - > git remote add -f origin https://github.com/scalacenter/library-example.git - > git checkout v1.0.1 +> git init . +> git remote add -f origin https://github.com/scalacenter/library-example.git +> git checkout v1.0.1 - > ./mill init +> ./mill init converting module library-example Dependency Dependency(com.lightbend.paradox,paradox-theme-generic,false,0.4.4,Some(paradox-theme)) with an unknown configuration "paradox-theme" is dropped. Dependency Dependency(com.lightbend.paradox,paradox-theme-generic,false,0.4.4,Some(paradox-theme)) with an unknown configuration "paradox-theme" is dropped. @@ -16,11 +16,8 @@ writing Mill build file to build.mill converted sbt build to Mill formatting Mill build files Formatting 1 Scala sources -parsed config (v3.8.4): /var/folders/xh/9t7mxl615nv8627_v54lc9ph0000gn/T/3390681381000847719.tmp init completed, run "mill resolve _" to list available tasks - > ./mill compile - [46/46] compile - [46] [info] compiling 1 Scala source to /.../libsam/out/compile.dest/classes ... - [46] [info] done compiling +> ./mill compile +done compiling */ diff --git a/example/scalalib/migrating/2-sbt-incomplete/build.mill b/example/scalalib/migrating/2-sbt-incomplete/build.mill index 1508ddbdd3b..69e4a9bec2e 100644 --- a/example/scalalib/migrating/2-sbt-incomplete/build.mill +++ b/example/scalalib/migrating/2-sbt-incomplete/build.mill @@ -9,12 +9,6 @@ > ./mill init converting sbt build Running the added `millInitExportBuild` sbt task to export the build -[info] welcome to sbt 1.10.0 (Eclipse Adoptium Java 17.0.14) -[info] loading settings for project test-build from plugins.sbt,mill-init.sbt ... -[info] loading project definition from /.../project -[info] loading settings for project test from build.sbt,doctest.sbt ... -[info] set current project to scala-csv (in build file:/.../) -[success] Total time: 0 s, completed Feb 19, 2025, 1:42:45 AM converting module scala-csv generated 1 Mill build file(s) removing existing Mill build files @@ -22,14 +16,10 @@ writing Mill build file to build.mill converted sbt build to Mill formatting Mill build files Formatting 1 Scala sources -parsed config (v3.8.4): /var/folders/xh/9t7mxl615nv8627_v54lc9ph0000gn/T/17040603558958375710.tmp init completed, run "mill resolve _" to list available tasks > ./mill compile # You will have to further configure the `CrossScalaModule` for different Scala versions - [46/46] compile - [46] [info] compiling 7 Scala sources and 3 Java sources to /.../out/compile.dest/classes ... - [46] [error] /.../src/main/scala/com/github/tototoshi/csv/CSVReader.scala:24:115: not found: type CSVReaderCompat - [46] [error] class CSVReader protected (private val lineReader: LineReader)(implicit format: CSVFormat) extends Closeable with CSVReaderCompat { - [46] [error] ^ - [46] [error] one error found +error: class CSVReader protected (private val lineReader: LineReader)(implicit format: CSVFormat) extends Closeable with CSVReaderCompat { +error: ^ +error: one error found */ From 4112c6fc64c311337ffda3eb530a8e25b1374fc9 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 20 Feb 2025 21:15:19 +0800 Subject: [PATCH 38/70] Run `./mill mill.scalalib.scalafmt.ScalafmtModule/reformatAll` since `lint-autofix` fails on CI --- .../init/buildgen/src/mill/main/buildgen/BuildGenBase.scala | 3 ++- main/init/buildgen/src/mill/main/buildgen/ir.scala | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 50a4c84dad2..3b1bed816bd 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -35,7 +35,8 @@ trait BuildGenBase[M, D, I] { val isNested = build.dirs.nonEmpty build.copy(value = BuildObject( - imports = BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size, extraImports), + imports = + BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size, extraImports), companions = shared.depsObject.fold(SortedMap.empty[String, BuildObject.Constants])(name => SortedMap((name, SortedMap(inner.scopedDeps.namedIvyDeps.toSeq*))) diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index c41c272ec5b..c4852b44b30 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -85,7 +85,7 @@ case class IrBuild( dirs: Seq[String], repositories: Seq[String], javacOptions: Seq[String], - scalaVersion : Option[String], + scalaVersion: Option[String], scalacOptions: Option[Seq[String]], projectName: String, pomSettings: IrPom, @@ -117,8 +117,8 @@ case class IrScopedDeps( case class IrBaseInfo( javacOptions: Seq[String] = Nil, - scalaVersion : Option[String] = None, - scalacOptions : Option[Seq[String]] = None, + scalaVersion: Option[String] = None, + scalacOptions: Option[Seq[String]] = None, repositories: Seq[String] = Nil, noPom: Boolean = true, publishVersion: String = "", From 40cdaa33636a0d019082768e04e560c586b9e473 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Thu, 20 Feb 2025 21:32:09 +0800 Subject: [PATCH 39/70] Add one more exception in docs from the integration test failures fixed in commit 6b717b7423dd802a50bc25a2110fc6f1843d4d30 --- website/docs/modules/ROOT/pages/migrating/sbt.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/modules/ROOT/pages/migrating/sbt.adoc b/website/docs/modules/ROOT/pages/migrating/sbt.adoc index 825b0710296..bf7a60abe0e 100644 --- a/website/docs/modules/ROOT/pages/migrating/sbt.adoc +++ b/website/docs/modules/ROOT/pages/migrating/sbt.adoc @@ -110,8 +110,9 @@ or xref:extending/thirdparty-plugins.adoc[third party] plugins == FAQ How to fix errors such as -`java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release` and -`java.io.IOError: java.lang.RuntimeException: /packages cannot be represented as URI` +`java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release`, +`java.io.IOError: java.lang.RuntimeException: /packages cannot be represented as URI`, +and `java.lang.RuntimeException: java.lang.reflect.InvocationTargetException` thrown by the sbt command invoked by `mill init`? Update the project's sbt version to the latest or our tested version v1.10.7, and try again. From 3cfb3aa04595405177e79ff26f127b2e59144c13 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 23:07:11 +0800 Subject: [PATCH 40/70] Add `setup-sbt` in `autofix.ci` too as it fails without it --- .github/workflows/autofix.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index ce356a24ebc..fff08f9d91e 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -7,6 +7,8 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: sbt/setup-sbt@v1 + - run: | ./mill __.fix + mill.javalib.palantirformat.PalantirFormatModule/ + mill.scalalib.scalafmt.ScalafmtModule/ + mill.kotlinlib.ktlint.KtlintModule/ ./mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/ From 7ed2c3a1a7dbf32aab392ab6d66f2e91617f05d3 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Thu, 20 Feb 2025 23:20:16 +0800 Subject: [PATCH 41/70] Fix the merge --- main/src/mill/main/MainModule.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index ba8f14036be..d08d5f20600 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -279,7 +279,7 @@ trait MainModule extends BaseModule { SelectMode.Separated ) else if (os.exists(os.pwd / "build.sbt")) - evaluator.resolveEvaluate( + evaluator.evaluate( Seq("mill.init.InitSbtModule/init") ++ args, SelectMode.Separated ) From ee2837700dd7f577ddb308ec010e0407ac6e98d1 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 22 Feb 2025 16:21:49 +0800 Subject: [PATCH 42/70] Support converting child projects nested in a parent directory which is not a project - An extra `OM` abstract type is added in `BuildGenBase`. - `OM = M` in the Maven and Gradle implementations. - `OM = Option[Project]` in `SbtBuildGenMain`. - A `merge` algorithm is added to create `Tree[Node[Option[T]]]`s. - Improve code in the Maven and Gradle implementations BTW. - Add a nested module in `main/init/sbt/test/resources/sbt-multi-project-example/build.sbt` to test the change and fix revealed bugs. --- .../src/mill/main/buildgen/BuildGenBase.scala | 82 ++++++++++++------- .../src/mill/main/buildgen/BuildGenUtil.scala | 9 +- .../mill/main/buildgen/OptionNodeTree.scala | 33 ++++++++ .../buildgen/src/mill/main/buildgen/ir.scala | 22 +++++ .../mill/main/gradle/GradleBuildGenMain.scala | 12 ++- .../mill/main/maven/MavenBuildGenMain.scala | 6 +- .../src/mill/main/sbt/SbtBuildGenMain.scala | 35 ++++---- .../sbt-multi-project-example/build.mill | 5 ++ .../nested/nested/package.mill | 58 +++++++++++++ .../nested/package.mill | 5 ++ .../sbt-multi-project-example/build.sbt | 3 + 11 files changed, 208 insertions(+), 62 deletions(-) create mode 100644 main/init/buildgen/src/mill/main/buildgen/OptionNodeTree.scala create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill create mode 100644 main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 3b1bed816bd..5feaf83e372 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -2,8 +2,13 @@ package mill.main.buildgen import mill.main.buildgen.BuildGenUtil.{buildPackages, compactBuildTree, writeBuildObject} -import scala.collection.immutable.SortedMap +import scala.collection.immutable.{SortedMap, SortedSet} +/* +TODO Can we just convert all generic type parameters to abstract type members? + See https://stackoverflow.com/a/1154727/5082913. + I think abstract type members are preferred in this case. + */ trait BuildGenBase[M, D, I] { type C def convertWriteOut(cfg: C, shared: BuildGenUtil.BasicConfig, input: I): Unit = { @@ -11,49 +16,63 @@ trait BuildGenBase[M, D, I] { writeBuildObject(if (shared.merge.value) compactBuildTree(output) else output) } - def getProjectTree(input: I): Tree[Node[M]] + /** + * A possibly optional module model which might be a parent directory of an actual module without its own sources. + */ + type OM + extension (om: OM) def toOption(): Option[M] + + def getModuleTree(input: I): Tree[Node[OM]] def convert( input: I, cfg: C, shared: BuildGenUtil.BasicConfig ): Tree[Node[BuildObject]] = { - val projectTree = getProjectTree(input) + val moduleTree = getModuleTree(input) + val moduleOptionTree = moduleTree.map(node => node.copy(value = node.value.toOption())) // for resolving moduleDeps - val packages = buildPackages(projectTree)(getPackage) + val packages = buildPackages( + moduleOptionTree.nodes().flatMap(node => node.value.map(m => node.copy(value = m))) + )(getPackage) val baseInfo = shared.baseModule.fold(IrBaseInfo()) { getBaseInfo(input, cfg, _, packages.size) } - projectTree.map { build => - val name = getArtifactId(build.value) - println(s"converting module $name") - - val inner = extractIrBuild(cfg, baseInfo, build, packages) - - val isNested = build.dirs.nonEmpty - build.copy(value = - BuildObject( - imports = - BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size, extraImports), - companions = - shared.depsObject.fold(SortedMap.empty[String, BuildObject.Constants])(name => - SortedMap((name, SortedMap(inner.scopedDeps.namedIvyDeps.toSeq*))) - ), - supertypes = getSuperTypes(cfg, baseInfo, build), - inner = BuildGenUtil.renderIrBuild(inner), - outer = - if (isNested || baseInfo.moduleTypedef == null) "" - else BuildGenUtil.renderIrTrait(baseInfo.moduleTypedef) - ) + moduleOptionTree.map(optionalBuild => + optionalBuild.copy(value = + optionalBuild.value.fold( + BuildObject(SortedSet("mill._"), SortedMap.empty, Seq("RootModule", "Module"), "", "") + )(moduleModel => { + val name = getArtifactId(moduleModel) + println(s"converting module $name") + + val build = optionalBuild.copy(value = moduleModel) + val inner = extractIrBuild(cfg, baseInfo, build, packages) + + val isNested = optionalBuild.dirs.nonEmpty + BuildObject( + imports = + BuildGenUtil.renderImports(shared.baseModule, isNested, packages.size, extraImports), + companions = + shared.depsObject.fold(SortedMap.empty[String, BuildObject.Constants])(name => + SortedMap((name, SortedMap(inner.scopedDeps.namedIvyDeps.toSeq*))) + ), + supertypes = getSupertypes(cfg, baseInfo, build), + inner = BuildGenUtil.renderIrBuild(inner), + outer = + if (isNested || baseInfo.moduleTypedef == null) "" + else BuildGenUtil.renderIrTrait(baseInfo.moduleTypedef) + ) + }) ) - } + ) } def extraImports: Seq[String] - def getSuperTypes(cfg: C, baseInfo: IrBaseInfo, build: Node[M]): Seq[String] + def getSupertypes(cfg: C, baseInfo: IrBaseInfo, build: Node[M]): Seq[String] def getBaseInfo( input: I, @@ -62,9 +81,9 @@ trait BuildGenBase[M, D, I] { packagesSize: Int ): IrBaseInfo - def getPackage(model: M): (String, String, String) + def getPackage(moduleModel: M): (String, String, String) - def getArtifactId(model: M): String + def getArtifactId(moduleModel: M): String def extractIrBuild( cfg: C, @@ -75,7 +94,8 @@ trait BuildGenBase[M, D, I] { } object BuildGenBase { - trait BaseInfoFromSubproject[M, D] extends BuildGenBase[M, D, Tree[Node[M]]] { - override def getProjectTree(input: Tree[Node[M]]): Tree[Node[M]] = input + trait MavenAndGradle[M, D] extends BuildGenBase[M, D, Tree[Node[M]]] { + override def getModuleTree(input: Tree[Node[M]]): Tree[Node[M]] = input + override type OM = M } } diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index 1cc9279ab7b..ccc8dbce456 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -1,14 +1,15 @@ package mill.main.buildgen +import geny.Generator import mainargs.{Flag, arg} -import mill.constants.OutFiles -import mill.main.buildgen.BuildObject.Companions import mill.constants.CodeGenConstants.{ buildFileExtensions, nestedBuildFileNames, rootBuildFileNames, rootModuleAlias } +import mill.constants.OutFiles +import mill.main.buildgen.BuildObject.Companions import mill.runner.FileImportGraph.backtickWrap import scala.collection.immutable.SortedSet @@ -160,9 +161,9 @@ object BuildGenUtil { def buildPackage(dirs: Seq[String]): String = (rootModuleAlias +: dirs).iterator.map(backtickWrap).mkString(".") - def buildPackages[Module, Key](input: Tree[Node[Module]])(key: Module => Key) + def buildPackages[Module, Key](input: Generator[Node[Module]])(key: Module => Key) : Map[Key, String] = - input.nodes() + input .map(node => (key(node.value), buildPackage(node.dirs))) .toSeq .toMap diff --git a/main/init/buildgen/src/mill/main/buildgen/OptionNodeTree.scala b/main/init/buildgen/src/mill/main/buildgen/OptionNodeTree.scala new file mode 100644 index 00000000000..c136d5b3bfb --- /dev/null +++ b/main/init/buildgen/src/mill/main/buildgen/OptionNodeTree.scala @@ -0,0 +1,33 @@ +package mill.main.buildgen + +def toTree[T](prefixDirs: Seq[String], dirs: List[String], node: Node[T]): Tree[Node[Option[T]]] = + dirs match + case Nil => Tree(node.copy(value = Some(node.value))) + case dir :: nextDirs => + Tree(Node(prefixDirs, None), Seq(toTree(prefixDirs :+ dir, nextDirs, node))) + +def merge[T]( + tree: Tree[Node[Option[T]]], + prefixDirs: Seq[String], + dirs: List[String], + node: Node[T] +): Tree[Node[Option[T]]] = + dirs match + case Nil => tree.copy(node = + tree.node.value.fold(node.copy(value = Some(node.value)))(existingProject => + throw IllegalArgumentException( + s"Project at duplicate locations: $existingProject and $node" + ) + ) + ) + case dir :: nextDirs => + tree.copy(children = { + def nextPrefixDirs = prefixDirs :+ dir + tree.children.iterator.zipWithIndex.find(_._1.node.dirs.last == dir) match + case Some((childTree, index)) => + tree.children.updated(index, merge(childTree, nextPrefixDirs, nextDirs, node)) + case None => tree.children :+ toTree(nextPrefixDirs, nextDirs, node) + }) + +def merge[T](tree: Tree[Node[Option[T]]], node: Node[T]): Tree[Node[Option[T]]] = + merge(tree, Seq.empty, node.dirs.toList, node) diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index c4852b44b30..7fe838f1327 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -97,6 +97,28 @@ case class IrBuild( publishProperties: Seq[(String, String)] ) +object IrBuild { + // TODO not used + def empty(dirs: Seq[String]) = IrBuild( + IrScopedDeps(), + null, + false, + dirs, + Seq.empty, + Seq.empty, + None, + None, + dirs.last, + null, + null, + null, + null, + Seq.empty, + Seq.empty, + Seq.empty + ) +} + case class IrScopedDeps( // TODO The type is `Seq` and this is deduplicated and sorted in `BuildGenUtil`. Make the type `SortedMap` here for consistency? namedIvyDeps: Seq[(String, String)] = Nil, diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index bedce60427e..64ce64ac682 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -40,7 +40,7 @@ import scala.jdk.CollectionConverters.* * - non-Java sources */ @mill.api.internal -object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectModel, JavaModel.Dep] { +object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, JavaModel.Dep] { type C = Config def main(args: Array[String]): Unit = { @@ -103,6 +103,10 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod file } + extension (om: ProjectModel) override def toOption(): Option[ProjectModel] = + // TODO consider filtering out projects without the `java` plugin applied + Some(om) + def getBaseInfo( input: Tree[Node[ProjectModel]], cfg: Config, @@ -197,11 +201,11 @@ object GradleBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[ProjectMod (project.group(), project.name(), project.version()) } - def getArtifactId(model: ProjectModel): String = model.name() + def getArtifactId(project: ProjectModel): String = project.name() - def getMillSourcePath(model: ProjectModel): Path = os.Path(model.directory()) + def getMillSourcePath(project: ProjectModel): Path = os.Path(project.directory()) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = + def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = Seq("RootModule") ++ Option.when(null != build.value.maven().pom() && baseInfo.noPom) { "PublishModule" } ++ Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index d7b6b905039..89af57b1b7a 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -36,7 +36,7 @@ import scala.jdk.CollectionConverters.* * - build profiles */ @mill.api.internal -object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Dependency] { +object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] { type C = Config def main(args: Array[String]): Unit = { @@ -59,6 +59,8 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe println("converted Maven build to Mill") } + extension (om: Model) override def toOption(): Option[Model] = Some(om) + def getBaseInfo( input: Tree[Node[Model]], cfg: Config, @@ -144,7 +146,7 @@ object MavenBuildGenMain extends BuildGenBase.BaseInfoFromSubproject[Model, Depe def getMillSourcePath(model: Model): Path = os.Path(model.getProjectDirectory) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = + def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = Seq("RootModule") ++ cfg.shared.basicConfig.baseModule.fold(getModuleSupertypes(cfg))(Seq(_)) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 89e7bc64f5b..0acfc9df432 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -7,8 +7,8 @@ import mill.main.buildgen.BuildGenUtil.* import mill.main.buildgen.IrDependencyType.* import os.Path +import scala.collection.MapView import scala.collection.immutable.SortedSet -import scala.collection.{MapView, View} /** * Converts an sbt build to Mill by generating Mill build file(s). @@ -49,8 +49,10 @@ import scala.collection.{MapView, View} * - cross builds */ @mill.api.internal -object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[Node[Project]])] { +object SbtBuildGenMain + extends BuildGenBase[Project, String, (BuildInfo, Tree[Node[Option[Project]]])] { type C = Config + override type OM = Option[Project] def main(args: Array[String]): Unit = { val cfg = ParserForClass[Config].constructOrExit(args.toSeq) @@ -135,30 +137,17 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No import scala.math.Ordering.Implicits.* // Types have to be specified explicitly here for the code to be resolved correctly in IDEA. - val projectNodesByParentDirs: Map[Option[Seq[String]], View[Node[Project]]] = + val projectNodes = buildExport.projects.view .map(project => Node(os.Path(project.projectDirectory).subRelativeTo(workspace).segments, project) ) // The projects are ordered differently in different `sbt millInitExportBuild` runs and on different OSs, which is strange. .sortBy(_.dirs) - .groupBy(node => { - val dirs = node.dirs - Option.when(dirs.nonEmpty)(dirs.dropRight(1)) - }) - /* - TODO This does not support converting child projects nested in a parent directory which is not a project yet. - Supporting this may involve refactoring `Node[Project]` into `Node[Option[Project]]` - or adding a subproject as a direct child with its ancestor directory project (I am not sure whether this works). - */ - val input = Tree.from(projectNodesByParentDirs(None).head) { node => - val dirs = node.dirs - val children = projectNodesByParentDirs.getOrElse(Some(dirs), Seq.empty) - (node, children) - } + val projectNodeTree = projectNodes.foldLeft(Tree(Node(Seq.empty, None)))(merge) - convertWriteOut(cfg, cfg.shared, (buildExport.defaultBuildInfo, input)) + convertWriteOut(cfg, cfg.shared, (buildExport.defaultBuildInfo, projectNodeTree)) println("converted sbt build to Mill") } @@ -199,13 +188,17 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No suffix = ".sbt" ) - override def getProjectTree(input: (BuildInfo, Tree[Node[Project]])): Tree[Node[Project]] = + extension (om: Option[Project]) override def toOption(): Option[Project] = om + + override def getModuleTree( + input: (BuildInfo, Tree[Node[Option[Project]]]) + ): Tree[Node[Option[Project]]] = input._2 def sbtSupertypes = Seq("SbtModule", "PublishModule") // always publish def getBaseInfo( - input: (BuildInfo, Tree[Node[Project]]), + input: (BuildInfo, Tree[Node[Option[Project]]]), cfg: Config, baseModule: String, packagesSize: Int @@ -292,7 +285,7 @@ object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[No def getMillSourcePath(project: Project): Path = os.Path(project.projectDirectory) - def getSuperTypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Project]): Seq[String] = + override def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Project]): Seq[String] = Seq("RootModule") ++ getModuleSupertypes(cfg) def groupArtifactVersion(dep: Dependency): (String, String, String) = diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index b65e2b7c1d0..d3568bfed01 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -100,6 +100,11 @@ object `package` extends RootModule with BaseModule { } } + + object nested extends Module { + + object nested extends BaseModule {} + } } trait BaseModule extends SbtModule with PublishModule { diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill new file mode 100644 index 00000000000..e4c9672c52f --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill @@ -0,0 +1,58 @@ +package build.nested.nested + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object `package` extends RootModule with SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def pomSettings = PomSettings( + "This is an sbt sample project for testing Mill's init command.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) + ) + + def publishVersion = "0.1.0-SNAPSHOT" + +} diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill new file mode 100644 index 00000000000..681780798ae --- /dev/null +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/package.mill @@ -0,0 +1,5 @@ +package build.nested + +import mill._ + +object `package` extends RootModule with Module {} diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index 7ac330b18b3..64f33161bda 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -56,6 +56,9 @@ lazy val multi2 = project common ) +lazy val nested = project + .in(file("nested/nested")) + // DEPENDENCIES lazy val dependencies = From 88c31b18e667274563ca953ef7442d877ef2a129 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 22 Feb 2025 21:00:19 +0800 Subject: [PATCH 43/70] Run more `compile` and `test` tasks in the submodules of part of the generated builds in the integration tests Some common code is extracted and added in `IntegrationTesterExt.scala`. --- .../init/src/IntegrationTesterExt.scala | 75 +++++++++ .../feature/init/src/MillInitSbtTests.scala | 148 ++++++++++-------- 2 files changed, 157 insertions(+), 66 deletions(-) create mode 100644 integration/feature/init/src/IntegrationTesterExt.scala diff --git a/integration/feature/init/src/IntegrationTesterExt.scala b/integration/feature/init/src/IntegrationTesterExt.scala new file mode 100644 index 00000000000..e87f6ba043a --- /dev/null +++ b/integration/feature/init/src/IntegrationTesterExt.scala @@ -0,0 +1,75 @@ +package mill.integration + +import mill.testkit.IntegrationTester +import utest.* + +val defaultInitCommand = + Seq("init", "--base-module", "BaseModule", "--deps-object", "Deps", "--merge") + +case class SplitResolvedTasks(successful: Seq[String], failed: Seq[String]) { + val all = (successful ++ failed).toSet +} + +extension (tester: IntegrationTester) + /** + * @param expectedCompileTasks [[ None ]] to denote that the `resolve __.compile` task fails + * @param expectedTestTasks [[ None ]] to denote that the `resolve __.test` task fails + * @return + */ + def testMillInit( + initCommand: Seq[String] = defaultInitCommand, + expectedInitResult: Boolean = true, + // expectedCompileResult: Boolean, + expectedCompileTasks: Option[SplitResolvedTasks], + expectedTestTasks: Option[SplitResolvedTasks] + ) = { + import tester.* + + val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + assert(initResult.isSuccess == expectedInitResult) + + /* + val compileResult = eval("compile") + assert(compileResult.isSuccess == expectedCompileResult) + */ + + def testAllResolvedTasks(taskName: String, expected: Option[SplitResolvedTasks]) = { + val resolveAllTasksResult = eval(("resolve", s"__.$taskName")) + expected.fold( + assert(!resolveAllTasksResult.isSuccess) + )(expected => { + assert(resolveAllTasksResult.isSuccess) + val resolvedAllTasks = resolveAllTasksResult.out.split('\n').toSet + Predef.assert(resolvedAllTasks == expected.all, "resolved tasks: " + resolvedAllTasks) + + for (task <- expected.successful) + Predef.assert(eval(task).isSuccess, s"task $task failed") + + for (task <- expected.failed) + Predef.assert(!eval(task).isSuccess, s"task $task succeeded") + }) + } + + testAllResolvedTasks("compile", expectedCompileTasks) + testAllResolvedTasks("test", expectedTestTasks) + } + +extension (module: String) { + def compileTask: String = + s"$module.compile" + private def testModuleOrTask: String = + s"$module.test" + def testTask: String = + testModuleOrTask + def testModule: String = + testModuleOrTask + def testCompileTask: String = + testModule.compileTask + def testTestTask: String = + testModule.testTask + def allCompileTasks: Seq[String] = + Seq(compileTask, testCompileTask) + // TODO This seems not needed. + def allTestTasks: Seq[String] = + Seq(testTask, testTestTask) +} diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 4eb7f9ac838..c9bb1e4bd31 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -1,14 +1,11 @@ package mill.integration -import mill.integration.MillInitSbtTests.{bumpSbtTo1107, initCommand} +import mill.integration.testMillInit import utest.* -object MillInitSbtTests { - val initCommand = ("init", "--base-module", "BaseModule", "--deps-object", "Deps", "--merge") - def bumpSbtTo1107(workspacePath: os.Path) = - // bump sbt version to resolve compatibility issues with lower sbt versions and higher JDK versions - os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") -} +def bumpSbtTo1107(workspacePath: os.Path) = + // bump sbt version to resolve compatibility issues with lower sbt versions and higher JDK versions + os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") // relatively small libraries @@ -20,15 +17,12 @@ object MillInitLibraryExampleTests extends BuildGenTestSuite { */ val url = "https://github.com/scalacenter/library-example/archive/refs/tags/v1.0.1.zip" - test - integrationTest(url) { tester => - import tester.* - - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") - assert(compileResult.isSuccess) - } + test - integrationTest(url)( + _.testMillInit( + expectedCompileTasks = Some(SplitResolvedTasks(Seq("compile"), Seq())), + expectedTestTasks = None // scalaprops not supported in `TestModule` + ) + ) } } @@ -41,16 +35,13 @@ object MillInitScalaCsv200Tests extends BuildGenTestSuite { val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/2.0.0.zip" test - integrationTest(url) { tester => - import tester.* + bumpSbtTo1107(tester.workspacePath) - bumpSbtTo1107(workspacePath) - - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") // Cross builds are not supported yet. - assert(!compileResult.isSuccess) + tester.testMillInit( + expectedCompileTasks = Some(SplitResolvedTasks(Seq(), Seq("compile", "test.compile"))), + expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test" /*, "test.test"*/ ))) + ) } } } @@ -64,16 +55,18 @@ object MillInitScalaCsv136Tests extends BuildGenTestSuite { val url = "https://github.com/tototoshi/scala-csv/archive/refs/tags/1.3.6.zip" test - integrationTest(url) { tester => - import tester.* - - bumpSbtTo1107(workspacePath) - - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") - // It works here, but scala 2.11 with JDK 6 seems not supported when run with JDK 17 in shell. - assert(compileResult.isSuccess) + bumpSbtTo1107(tester.workspacePath) + + tester.testMillInit( + expectedCompileTasks = Some(SplitResolvedTasks( + // It works here, but scala 2.11 with JDK 6 seems not supported when run with JDK 17 in shell. + Seq("compile"), + // `Not a Java dependency: Dep(Dependency(com.storm-enroute:scalameter, 0.8.2, Configuration(), Set(), Publication(, Type(), Extension(), Classifier()), false, true),Binary(false),false)` + // probably due to inheriting `MavenTests` instead of `SbtTests` + Seq("test.compile") + )), + expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test" /*, "test.test"*/ ))) + ) } } } @@ -89,22 +82,28 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { "https://github.com/pbassiner/sbt-multi-project-example/archive/152b31df9837115b183576b0080628b43c505389.zip" test - integrationTest(url) { tester => - import tester.* - - bumpSbtTo1107(workspacePath) - - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") - assert(compileResult.isSuccess) - - val compileSubmodulesResult = eval("_.compile") - if (System.getProperty("java.version").split('.').head.toInt <= 11) - assert(compileSubmodulesResult.isSuccess) - else - // Submodules don't compile well with JDK 17 and 21, which seems to be due to incompatible bytecode versions in dependencies. - assert(!compileSubmodulesResult.isSuccess) + bumpSbtTo1107(tester.workspacePath) + + val submodules = Seq("common", "multi1", "multi2") + + tester.testMillInit( + expectedCompileTasks = + Some(if (System.getProperty("java.version").split('.').head.toInt <= 11) + SplitResolvedTasks( + Seq("compile") ++ submodules.map(_.compileTask), + // probably due to inheriting `MavenTests` instead of `SbtTests` + submodules.map(_.testCompileTask) + ) + else { + // Submodules don't compile well with JDK 17 and 21, which seems to be due to incompatible bytecode versions in dependencies. + val succeededSubmoduleCompileTasks = Seq("common.compile", "multi2.compile") + SplitResolvedTasks( + Seq("compile") ++ succeededSubmoduleCompileTasks, + (submodules.flatMap(_.allCompileTasks).toSet -- succeededSubmoduleCompileTasks).toSeq + ) + }), + expectedTestTasks = Some(SplitResolvedTasks(Seq(), submodules.map(_.testTask))) + ) } } } @@ -120,19 +119,32 @@ object MillInitZioHttpTests extends BuildGenTestSuite { val url = "https://github.com/zio/zio-http/archive/refs/tags/v3.0.1.zip" test - integrationTest(url) { tester => - import tester.* - - bumpSbtTo1107(workspacePath) - - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") - assert(compileResult.isSuccess) - - val compileSubmodulesResult = eval("_.compile") + bumpSbtTo1107(tester.workspacePath) + + val submodules = Seq( + "sbt-zio-http-grpc-tests", + "sbt-zio-http-grpc", + "zio-http-benchmarks.compile", + "zio-http-cli", + "zio-http-docs", + "zio-http-example", + "zio-http-gen", + "zio-http-htmx", + "zio-http-testkit", + "zio-http-tools", + "zio-http.js", + "zio-http.jvm" + ) + + // TODO + /* + // probably due to inheriting `MavenTests` instead of `SbtTests` // Some dependencies with currently unsupported `CrossVersion` `For3Use2_13` are not imported properly - assert(!compileSubmodulesResult.isSuccess) + tester.testMillInit( + expectedCompileTasks = Some(SplitResolvedTasks(Seq("compile"), submodules.flatMap(_.allCompileTasks))), + expectedTestTasks = Some(SplitResolvedTasks(Seq(), submodules.flatMap(_.allTestTasks))) + ) + */ } } } @@ -151,11 +163,15 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { os.call(("chmod", "+x", "sbt"), cwd = workspacePath) - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") assert(compileResult.isSuccess) + + // TODO + val resolveCompilesResult = eval(("resolve", "__.compile")) + println("Resolve result: " + resolveCompilesResult.out) } } } @@ -173,7 +189,7 @@ object MillInitSbtCatsTests extends BuildGenTestSuite { test - integrationTest(url) { tester => import tester.* - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") @@ -196,7 +212,7 @@ object MillInitSbtPlayFrameworkTests extends BuildGenTestSuite { test - integrationTest(url) { tester => import tester.* - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") @@ -220,7 +236,7 @@ object MillInitSbtScalaCheckTests extends BuildGenTestSuite { bumpSbtTo1107(workspacePath) - val initResult = eval(initCommand, stdout = os.Inherit, stderr = os.Inherit) + val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) val compileResult = eval("compile") From f53312645297e7da4f45ad40f933ec6f1ecc8dc6 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 22 Feb 2025 21:00:31 +0800 Subject: [PATCH 44/70] Reformat `GradleBuildGenMain.scala` --- .../gradle/src/mill/main/gradle/GradleBuildGenMain.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index 64ce64ac682..ee687544675 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -103,9 +103,10 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java file } - extension (om: ProjectModel) override def toOption(): Option[ProjectModel] = - // TODO consider filtering out projects without the `java` plugin applied - Some(om) + extension (om: ProjectModel) + override def toOption(): Option[ProjectModel] = + // TODO consider filtering out projects without the `java` plugin applied + Some(om) def getBaseInfo( input: Tree[Node[ProjectModel]], From 6e48c2b8553d598971d4989c69c13fa997992836 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:16:22 +0000 Subject: [PATCH 45/70] [autofix.ci] apply automated fixes --- integration/feature/init/src/IntegrationTesterExt.scala | 2 +- integration/feature/init/src/MillInitSbtTests.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/feature/init/src/IntegrationTesterExt.scala b/integration/feature/init/src/IntegrationTesterExt.scala index e87f6ba043a..a1e99be9ca7 100644 --- a/integration/feature/init/src/IntegrationTesterExt.scala +++ b/integration/feature/init/src/IntegrationTesterExt.scala @@ -39,7 +39,7 @@ extension (tester: IntegrationTester) assert(!resolveAllTasksResult.isSuccess) )(expected => { assert(resolveAllTasksResult.isSuccess) - val resolvedAllTasks = resolveAllTasksResult.out.split('\n').toSet + val resolvedAllTasks = resolveAllTasksResult.out.split('\n').toSet Predef.assert(resolvedAllTasks == expected.all, "resolved tasks: " + resolvedAllTasks) for (task <- expected.successful) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index c9bb1e4bd31..278e90699f7 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -121,7 +121,7 @@ object MillInitZioHttpTests extends BuildGenTestSuite { test - integrationTest(url) { tester => bumpSbtTo1107(tester.workspacePath) - val submodules = Seq( + Seq( "sbt-zio-http-grpc-tests", "sbt-zio-http-grpc", "zio-http-benchmarks.compile", From 2286ed01d0b834d80148637d2b5c429874622f42 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 23 Feb 2025 03:01:39 +0800 Subject: [PATCH 46/70] Fix the integration tests failing on windows caused by `split('\n')` An `java.io.IOException: Cannot run program "chmod"` exception thrown when the tests run in PowerShell is fixed BTW. --- integration/feature/init/src/IntegrationTesterExt.scala | 7 +++++-- integration/feature/init/src/MillInitSbtTests.scala | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/integration/feature/init/src/IntegrationTesterExt.scala b/integration/feature/init/src/IntegrationTesterExt.scala index a1e99be9ca7..76ef0a77371 100644 --- a/integration/feature/init/src/IntegrationTesterExt.scala +++ b/integration/feature/init/src/IntegrationTesterExt.scala @@ -39,8 +39,11 @@ extension (tester: IntegrationTester) assert(!resolveAllTasksResult.isSuccess) )(expected => { assert(resolveAllTasksResult.isSuccess) - val resolvedAllTasks = resolveAllTasksResult.out.split('\n').toSet - Predef.assert(resolvedAllTasks == expected.all, "resolved tasks: " + resolvedAllTasks) + val resolvedAllTasks = resolveAllTasksResult.out.linesIterator.toSet + Predef.assert( + expected.all == resolvedAllTasks, + "expected: " + expected.all + ", resolved: " + resolvedAllTasks + ) for (task <- expected.successful) Predef.assert(eval(task).isSuccess, s"task $task failed") diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 278e90699f7..3560d5d9a84 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -1,5 +1,6 @@ package mill.integration +import mill.constants.Util import mill.integration.testMillInit import utest.* @@ -161,7 +162,8 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { test - integrationTest(url) { tester => import tester.* - os.call(("chmod", "+x", "sbt"), cwd = workspacePath) + if (!Util.isWindows) + os.call(("chmod", "+x", "sbt"), cwd = workspacePath) val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) assert(initResult.isSuccess) From 768a2b104f97d0275a47b9e0f4e1f492dec647fd Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 23 Feb 2025 22:48:05 +0800 Subject: [PATCH 47/70] Fix the bug the converted sbt test modules extend `MavenTests` instead of `SbtTests` and update the unit test expected snapshots accordingly --- .../src/mill/main/buildgen/BuildGenUtil.scala | 14 +++++++++----- main/init/buildgen/src/mill/main/buildgen/ir.scala | 2 ++ .../src/mill/main/gradle/GradleBuildGenMain.scala | 1 + .../src/mill/main/maven/MavenBuildGenMain.scala | 1 + .../sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 1 + .../config/sbt-multi-project-example/build.mill | 6 +++--- .../sbt-multi-project-example/common/package.mill | 2 +- .../sbt-multi-project-example/multi1/package.mill | 2 +- .../sbt-multi-project-example/multi2/package.mill | 2 +- .../expected/scala-seed-project/build.mill | 2 +- 10 files changed, 21 insertions(+), 12 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index ccc8dbce456..f1683c54a27 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -70,7 +70,7 @@ object BuildGenUtil { if (!hasTest) "" else { val declare = - BuildGenUtil.renderTestModuleDecl(testModule, scopedDeps.testModule) + BuildGenUtil.renderTestModuleDecl(testModule, testModuleMainType, scopedDeps.testModule) s"""$declare { | @@ -482,11 +482,15 @@ object BuildGenUtil { } } - def renderTestModuleDecl(testModule: String, testModuleType: Option[String]): String = { + def renderTestModuleDecl( + testModule: String, + testModuleMainType: String, + testModuleExtraType: Option[String] + ): String = { val name = backtickWrap(testModule) - testModuleType match { - case Some(supertype) => s"object $name extends MavenTests with $supertype" - case None => s"trait $name extends MavenTests" + testModuleExtraType match { + case Some(supertype) => s"object $name extends $testModuleMainType with $supertype" + case None => s"trait $name extends $testModuleMainType" } } diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index 7fe838f1327..affdfe6ead5 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -81,6 +81,7 @@ case class IrLicense( case class IrBuild( scopedDeps: IrScopedDeps, testModule: String, + testModuleMainType: String, hasTest: Boolean, dirs: Seq[String], repositories: Seq[String], @@ -102,6 +103,7 @@ object IrBuild { def empty(dirs: Seq[String]) = IrBuild( IrScopedDeps(), null, + null, false, dirs, Seq.empty, diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index ee687544675..a47d5a2a886 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -174,6 +174,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java IrBuild( scopedDeps = scopedDeps, testModule = cfg.shared.basicConfig.testModule, + testModuleMainType = "MavenTests", hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, repositories = getRepositories(project).diff(baseInfo.repositories), diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index 89af57b1b7a..dd5ec88aac6 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -113,6 +113,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] IrBuild( scopedDeps = scopedDeps, testModule = cfg.shared.basicConfig.testModule, + testModuleMainType = "MavenTests", hasTest = os.exists(getMillSourcePath(model) / "src/test"), dirs = build.dirs, repositories = getRepositories(model), diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 0acfc9df432..23e6f397ba4 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -249,6 +249,7 @@ object SbtBuildGenMain IrBuild( scopedDeps = configurationDeps, testModule = cfg.shared.testModule, + testModuleMainType = "SbtTests", hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, repositories = getRepositories(buildInfo).diff(baseInfo.repositories), diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index d3568bfed01..b6cd981a090 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -48,7 +48,7 @@ object `package` extends RootModule with BaseModule { Deps.`org.slf4j:jcl-over-slf4j` ) - object tests extends MavenTests with TestModule.ScalaTest { + object tests extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) @@ -71,7 +71,7 @@ object `package` extends RootModule with BaseModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) - object tests extends MavenTests with TestModule.ScalaTest { + object tests extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) @@ -93,7 +93,7 @@ object `package` extends RootModule with BaseModule { def moduleDeps = super.moduleDeps ++ Seq(build.common) - object tests extends MavenTests with TestModule.ScalaTest { + object tests extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index d95ee7c1d5e..b4e5626284d 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -64,7 +64,7 @@ object `package` extends RootModule with SbtModule with PublishModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends MavenTests with TestModule.ScalaTest { + object test extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index 80aea6a7077..ddd1fcbe2b9 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -68,7 +68,7 @@ object `package` extends RootModule with SbtModule with PublishModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends MavenTests with TestModule.ScalaTest { + object test extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index c5df60d3df4..16bc9ddac87 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -67,7 +67,7 @@ object `package` extends RootModule with SbtModule with PublishModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends MavenTests with TestModule.ScalaTest { + object test extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ Seq( ivy"org.scalacheck::scalacheck:1.13.5", diff --git a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill index a605637f48e..aa7f1f841a1 100644 --- a/main/init/sbt/test/resources/expected/scala-seed-project/build.mill +++ b/main/init/sbt/test/resources/expected/scala-seed-project/build.mill @@ -22,7 +22,7 @@ object `package` extends RootModule with SbtModule with PublishModule { def publishVersion = "0.1.0-SNAPSHOT" - object test extends MavenTests with TestModule.Munit { + object test extends SbtTests with TestModule.Munit { def ivyDeps = super.ivyDeps() ++ Seq(ivy"org.scalameta::munit:0.7.29") From cd168ff7cdc4f7f30d9d8fd2ef59f9ac2bf88d53 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 23 Feb 2025 23:24:32 +0800 Subject: [PATCH 48/70] Update (fix) an integration test accordingly --- integration/feature/init/src/MillInitSbtTests.scala | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 3560d5d9a84..814be9fa80e 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -41,7 +41,7 @@ object MillInitScalaCsv200Tests extends BuildGenTestSuite { // Cross builds are not supported yet. tester.testMillInit( expectedCompileTasks = Some(SplitResolvedTasks(Seq(), Seq("compile", "test.compile"))), - expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test" /*, "test.test"*/ ))) + expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test"))) ) } } @@ -60,13 +60,10 @@ object MillInitScalaCsv136Tests extends BuildGenTestSuite { tester.testMillInit( expectedCompileTasks = Some(SplitResolvedTasks( - // It works here, but scala 2.11 with JDK 6 seems not supported when run with JDK 17 in shell. - Seq("compile"), - // `Not a Java dependency: Dep(Dependency(com.storm-enroute:scalameter, 0.8.2, Configuration(), Set(), Publication(, Type(), Extension(), Classifier()), false, true),Binary(false),false)` - // probably due to inheriting `MavenTests` instead of `SbtTests` - Seq("test.compile") + Seq("compile", "test.compile"), + Seq.empty )), - expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test" /*, "test.test"*/ ))) + expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test"))) ) } } From 9ee23dee3ebac7c2548b4c6b7f00dfc536d6c1a3 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 23 Feb 2025 23:43:12 +0800 Subject: [PATCH 49/70] Try fixing Mill `test` in the converted project of `MillInitScalaCsv136Tests` without success, and add a comment about it --- integration/feature/init/src/MillInitSbtTests.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 814be9fa80e..ab80ca9c224 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -63,7 +63,17 @@ object MillInitScalaCsv136Tests extends BuildGenTestSuite { Seq("compile", "test.compile"), Seq.empty )), - expectedTestTasks = Some(SplitResolvedTasks(Seq(), Seq("test"))) + expectedTestTasks = Some(SplitResolvedTasks( + Seq(), + /* + Relative paths to the workspace are used in the test sources such as `new File("src/test/resources/simple.csv")` + and they seem to cause the test to fail with Mill: + ```text + java.io.FileNotFoundException: src/test/resources/simple.csv (No such file or directory) + ``` + */ + Seq("test") + )) ) } } From 87afa17864805dec3aef1440ce8378b0b46fbd65 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 01:17:57 +0800 Subject: [PATCH 50/70] Move the `jvmId` config arg to `BasicConfig` and support it in `SbtBuildGenMain`, and update the tests (unit tests and integration tests) accordingly --- integration/feature/init/src/MillInitSbtTests.scala | 13 ++++++++++++- .../src/mill/main/buildgen/BuildGenUtil.scala | 10 +++++----- .../src/mill/main/gradle/GradleBuildGenMain.scala | 4 ++-- .../src/mill/main/maven/MavenBuildGenMain.scala | 2 +- .../sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 2 +- .../config/sbt-multi-project-example/build.mill | 5 +++++ .../sbt/test/src/mill/main/sbt/BuildGenTests.scala | 2 ++ 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index ab80ca9c224..de0760bcf0a 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -95,6 +95,7 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { val submodules = Seq("common", "multi1", "multi2") tester.testMillInit( + //initCommand = defaultInitCommand ++ Seq("--jvm-id", "11"), expectedCompileTasks = Some(if (System.getProperty("java.version").split('.').head.toInt <= 11) SplitResolvedTasks( @@ -103,7 +104,17 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { submodules.map(_.testCompileTask) ) else { - // Submodules don't compile well with JDK 17 and 21, which seems to be due to incompatible bytecode versions in dependencies. + /* + `multi1.compile` doesn't work well when Mill is run with JDK 17 and 21: + ```text + 1 tasks failed + multi1.compile java.io.IOError: java.lang.RuntimeException: /packages cannot be represented as URI + java.base/jdk.internal.jrtfs.JrtPath.toUri(JrtPath.java:175) + scala.tools.nsc.classpath.JrtClassPath.asURLs(DirectoryClassPath.scala:183) + ... + ``` + Passing a `jvmId` 11 doesn't work. + */ val succeededSubmoduleCompileTasks = Seq("common.compile", "multi2.compile") SplitResolvedTasks( Seq("compile") ++ succeededSubmoduleCompileTasks, diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index f1683c54a27..c62336ac728 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -498,6 +498,11 @@ object BuildGenUtil { case class BasicConfig( @arg(doc = "name of generated base module trait defining shared settings", short = 'b') baseModule: Option[String] = None, + @arg( + doc = "distribution and version of custom JVM to configure in --base-module", + short = 'j' + ) + jvmId: Option[String] = None, @arg(doc = "name of generated nested test module", short = 't') testModule: String = "test", @arg(doc = "name of generated companion object defining dependency constants", short = 'd') @@ -512,11 +517,6 @@ object BuildGenUtil { @mainargs.main case class Config( basicConfig: BasicConfig, - @arg( - doc = "distribution and version of custom JVM to configure in --base-module", - short = 'j' - ) - jvmId: Option[String] = None, @arg(doc = "capture Maven publish properties", short = 'p') publishProperties: Flag = Flag() ) diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index a47d5a2a886..e32b6580a11 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -55,7 +55,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java val connector = GradleConnector.newConnector() val args = - cfg.shared.jvmId.map { id => + cfg.shared.basicConfig.jvmId.map { id => println(s"resolving Java home for jvmId $id") val home = Jvm.resolveJavaHome(id).get s"-Dorg.gradle.java.home=$home" @@ -138,7 +138,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java val publishProperties = getPublishProperties(project, cfg.shared) val typedef = IrTrait( - cfg.shared.jvmId, + cfg.shared.basicConfig.jvmId, baseModule, supertypes, javacOptions, diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index dd5ec88aac6..4660229315a 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -77,7 +77,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] val publishProperties = getPublishProperties(model, cfg.shared) val typedef = IrTrait( - cfg.shared.jvmId, + cfg.shared.basicConfig.jvmId, baseModule, getModuleSupertypes(cfg), javacOptions, diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 23e6f397ba4..b33f6a3767c 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -212,7 +212,7 @@ object SbtBuildGenMain val publishVersion = getPublishVersion(buildInfo) val typedef = IrTrait( - None, // There doesn't seem to be a Java version setting in sbt. See https://stackoverflow.com/a/76456295/5082913. + cfg.shared.jvmId, // There doesn't seem to be a Java version setting in sbt though. See https://stackoverflow.com/a/76456295/5082913. baseModule, sbtSupertypes, javacOptions, diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill index b6cd981a090..a87471bad19 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill @@ -157,4 +157,9 @@ trait BaseModule extends SbtModule with PublishModule { ) } + def zincWorker = mill.define.ModuleRef(BaseModuleZincWorker) + + object BaseModuleZincWorker extends ZincWorkerModule { + def jvmId = "11" + } } diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index 3708220fe81..f031147a1ce 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -31,6 +31,8 @@ object BuildGenTests extends TestSuite { val args = Array( "--base-module", "BaseModule", + "--jvm-id", + "11", "--test-module", "tests", "--deps-object", From 02206a6bc8cce12198e2d5e0a11c1b54c230cede Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 03:19:17 +0800 Subject: [PATCH 51/70] Support the `baseProject` config arg too in `SbtBuildGenMain` and update the expected snapshots in test resources accordingly --- .../src/mill/main/sbt/SbtBuildGenMain.scala | 17 +- .../all/sbt-multi-project-example/build.mill | 261 ++++++++++++++++++ .../sbt-multi-project-example/build.mill | 23 ++ .../common/package.mill | 2 +- .../sbt-multi-project-example/build.sbt | 2 +- .../src/mill/main/sbt/BuildGenTests.scala | 27 +- 6 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill rename main/init/sbt/test/resources/expected/config/{ => without-base-project}/sbt-multi-project-example/build.mill (88%) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index b33f6a3767c..be87963d5cf 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -1,6 +1,6 @@ package mill.main.sbt -import mainargs.{ParserForClass, main} +import mainargs.{ParserForClass, arg, main} import mill.constants.Util import mill.main.buildgen.* import mill.main.buildgen.BuildGenUtil.* @@ -203,7 +203,12 @@ object SbtBuildGenMain baseModule: String, packagesSize: Int ): IrBaseInfo = { - val buildInfo = input._1 + val buildInfo = cfg.baseProject.fold(input._1)(name => + // TODO This can simplified if `buildExport.projects` is passed here. + input._2.nodes().collectFirst(Function.unlift(_.value.flatMap(project => + Option.when(project.name == name)(project) + ))).get.buildInfo + ) import buildInfo.* val javacOptions = getJavacOptions(buildInfo) @@ -470,6 +475,12 @@ object SbtBuildGenMain @main @mill.api.internal case class Config( - shared: BuildGenUtil.BasicConfig + shared: BuildGenUtil.BasicConfig, + @arg( + doc = "name of the sbt project to extract settings for --base-module, " + + "if not specified, settings are extracted from `ThisBuild`", + short = 'g' + ) + baseProject: Option[String] = None ) } diff --git a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill new file mode 100644 index 00000000000..8e5e0a141bd --- /dev/null +++ b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill @@ -0,0 +1,261 @@ +package build + +import mill._ +import mill.javalib._ +import mill.javalib.publish._ +import mill.scalalib.SbtModule + +object Deps { + + val `ch.qos.logback:logback-classic` = + ivy"ch.qos.logback:logback-classic:1.2.3" + + val `com.github.julien-truffaut:monocle-core` = + ivy"com.github.julien-truffaut::monocle-core:1.4.0" + + val `com.github.julien-truffaut:monocle-macro` = + ivy"com.github.julien-truffaut::monocle-macro:1.4.0" + + val `com.github.pureconfig:pureconfig` = + ivy"com.github.pureconfig::pureconfig:0.8.0" + + val `com.typesafe.akka:akka-stream` = + ivy"com.typesafe.akka::akka-stream:2.5.6" + + val `com.typesafe.scala-logging:scala-logging` = + ivy"com.typesafe.scala-logging::scala-logging:3.7.2" + val `com.typesafe:config` = ivy"com.typesafe:config:1.3.1" + + val `net.logstash.logback:logstash-logback-encoder` = + ivy"net.logstash.logback:logstash-logback-encoder:4.11" + val `org.scalacheck:scalacheck` = ivy"org.scalacheck::scalacheck:1.13.5" + val `org.scalatest:scalatest` = ivy"org.scalatest::scalatest:3.0.4" + val `org.slf4j:jcl-over-slf4j` = ivy"org.slf4j:jcl-over-slf4j:1.7.25" +} + +object `package` extends RootModule with BaseModule { + + def artifactName = "sbt-multi-project-example" + + def pomSettings = PomSettings( + "This is an sbt sample project for testing Mill's init command.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) + ) + + object common extends BaseModule { + + def ivyDeps = super.ivyDeps() ++ Seq( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + object tests extends SbtTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } + + object multi1 extends BaseModule { + + def ivyDeps = super.ivyDeps() ++ Seq( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.github.julien-truffaut:monocle-core`, + Deps.`com.github.julien-truffaut:monocle-macro`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "This is an sbt sample project for testing Mill's init command.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq( + Developer("johnd", "John Doe", "https://example.com/johnd", None, None) + ) + ) + + object tests extends SbtTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } + + object multi2 extends BaseModule { + + def ivyDeps = super.ivyDeps() ++ Seq( + Deps.`ch.qos.logback:logback-classic`, + Deps.`com.github.pureconfig:pureconfig`, + Deps.`com.typesafe.akka:akka-stream`, + Deps.`com.typesafe.scala-logging:scala-logging`, + Deps.`com.typesafe:config`, + Deps.`net.logstash.logback:logstash-logback-encoder`, + Deps.`org.slf4j:jcl-over-slf4j` + ) + + def moduleDeps = super.moduleDeps ++ Seq(build.common) + + def pomSettings = PomSettings( + "This is an sbt sample project for testing Mill's init command.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq( + Developer("johnd", "John Doe", "https://example.com/johnd", None, None) + ) + ) + + object tests extends SbtTests with TestModule.ScalaTest { + + def ivyDeps = super.ivyDeps() ++ + Seq(Deps.`org.scalacheck:scalacheck`, Deps.`org.scalatest:scalatest`) + + } + } + + object nested extends Module { + + object nested extends BaseModule { + + def pomSettings = PomSettings( + "This is an sbt sample project for testing Mill's init command.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer( + "johnd", + "John Doe", + "https://example.com/johnd", + None, + None + )) + ) + + } + } +} + +trait BaseModule extends SbtModule with PublishModule { + + def scalaVersion = "2.12.3" + + def scalacOptions = super.scalacOptions() ++ Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation", + "-encoding", + "utf8" + ) + + def pomSettings = PomSettings( + "This is the common module.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq(Developer("johnd", "John Doe", "https://example.com/johnd", None, None)) + ) + + def publishVersion = "0.1.0-SNAPSHOT" + + def repositoriesTask = Task.Anon { + super.repositoriesTask() ++ Seq( + coursier.maven.MavenRepository( + "https://oss.sonatype.org/service/local/repositories/releases/content/" + ), + coursier.maven.MavenRepository( + "https://oss.sonatype.org/content/repositories/snapshots" + ) + ) + } + + def zincWorker = mill.define.ModuleRef(BaseModuleZincWorker) + + object BaseModuleZincWorker extends ZincWorkerModule { + def jvmId = "11" + } +} diff --git a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill similarity index 88% rename from main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill rename to main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill index a87471bad19..de6ca5569e5 100644 --- a/main/init/sbt/test/resources/expected/config/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill @@ -48,6 +48,29 @@ object `package` extends RootModule with BaseModule { Deps.`org.slf4j:jcl-over-slf4j` ) + def pomSettings = PomSettings( + "This is the common module.", + "com.pbassiner", + "https://github.com/com-lihaoyi/mill", + Seq(License( + "Apache-2.0", + "Apache-2.0", + "https://www.apache.org/licenses/LICENSE-2.0.txt", + false, + false, + "repo" + )), + VersionControl( + Some("https://github.com/com-lihaoyi/mill"), + Some("scm:git:https://github.com/com-lihaoyi/mill.git"), + None, + None + ), + Seq( + Developer("johnd", "John Doe", "https://example.com/johnd", None, None) + ) + ) + object tests extends SbtTests with TestModule.ScalaTest { def ivyDeps = super.ivyDeps() ++ diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill index b4e5626284d..72e8561ca20 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/common/package.mill @@ -42,7 +42,7 @@ object `package` extends RootModule with SbtModule with PublishModule { ) def pomSettings = PomSettings( - "This is an sbt sample project for testing Mill's init command.", + "This is the common module.", "com.pbassiner", "https://github.com/com-lihaoyi/mill", Seq(License( diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index 64f33161bda..f6e0ad0168d 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -24,7 +24,7 @@ lazy val global = project lazy val common = project .settings( name := "common", - settings, + settings ++ Seq(description := "This is the common module."), libraryDependencies ++= commonDependencies ) .disablePlugins(AssemblyPlugin) diff --git a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala index f031147a1ce..2a15135d51a 100644 --- a/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala +++ b/main/init/sbt/test/src/mill/main/sbt/BuildGenTests.scala @@ -25,10 +25,8 @@ object BuildGenTests extends TestSuite { ) } - test("config-sbt-multi-project-example") { - val sourceRoot = os.sub / "sbt-multi-project-example" - val expectedRoot = os.sub / "expected/config/sbt-multi-project-example" - val args = Array( + test("config") { + val commonArgs = Array( "--base-module", "BaseModule", "--jvm-id", @@ -39,9 +37,24 @@ object BuildGenTests extends TestSuite { "Deps", "--merge" ) - assert( - checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot) - ) + test("sbt-multi-project-example") { + val sourceRoot = os.sub / "sbt-multi-project-example" + test("without-base-project") { + val expectedRoot = + os.sub / "expected/config/without-base-project/sbt-multi-project-example" + val args = commonArgs + assert( + checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot) + ) + } + test("all") { + val expectedRoot = os.sub / "expected/config/all/sbt-multi-project-example" + val args = commonArgs ++ Array("--baseProject", "common") + assert( + checker.check(SbtBuildGenMain.main(args), sourceRoot, expectedRoot) + ) + } + } } } } From c819207ff0e8ad7ad81304ba448e07fa00d85629 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 05:16:13 +0800 Subject: [PATCH 52/70] Fix assertions in `MillInitSbtMultiProjectExampleTests` that fail due to commit 768a2b104f97d0275a47b9e0f4e1f492dec647fd --- integration/feature/init/src/MillInitSbtTests.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index de0760bcf0a..6d002f52fcb 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -95,14 +95,10 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { val submodules = Seq("common", "multi1", "multi2") tester.testMillInit( - //initCommand = defaultInitCommand ++ Seq("--jvm-id", "11"), + // initCommand = defaultInitCommand ++ Seq("--jvm-id", "11"), expectedCompileTasks = Some(if (System.getProperty("java.version").split('.').head.toInt <= 11) - SplitResolvedTasks( - Seq("compile") ++ submodules.map(_.compileTask), - // probably due to inheriting `MavenTests` instead of `SbtTests` - submodules.map(_.testCompileTask) - ) + SplitResolvedTasks(Seq("compile") ++ submodules.flatMap(_.allCompileTasks), Seq.empty) else { /* `multi1.compile` doesn't work well when Mill is run with JDK 17 and 21: From 22033e909dd461689b374815ca4602b087b8830f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 10:53:04 +0800 Subject: [PATCH 53/70] Support comparing a module's `def`'s with and extending super base module's `def`s in converted builds as required in https://github.com/com-lihaoyi/mill/pull/4586#issuecomment-2674530292 - Refactor some types to use Scala 3 explicit nulls BTW. - Fix a bug that `pomSettings` in modules different from that in the base module is lost in builds converted from Maven, BTW. - Update unit test code to test the changes and the expected snapshots in test resources correspondingly. --- .../src/mill/main/buildgen/BuildGenBase.scala | 6 +- .../src/mill/main/buildgen/BuildGenUtil.scala | 134 +++++++++++++----- .../buildgen/src/mill/main/buildgen/ir.scala | 19 ++- .../mill/main/gradle/GradleBuildGenMain.scala | 30 ++-- .../mill/main/maven/MavenBuildGenMain.scala | 21 +-- .../test/resources/expected/config/build.mill | 56 ++++++++ .../src/mill/main/sbt/SbtBuildGenMain.scala | 32 ++--- .../all/sbt-multi-project-example/build.mill | 12 ++ .../sbt-multi-project-example/build.mill | 12 ++ .../multi1/package.mill | 3 +- .../multi2/package.mill | 4 +- .../sbt-multi-project-example/build.sbt | 12 +- 12 files changed, 230 insertions(+), 111 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 5feaf83e372..0cfd344c391 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -49,7 +49,7 @@ trait BuildGenBase[M, D, I] { println(s"converting module $name") val build = optionalBuild.copy(value = moduleModel) - val inner = extractIrBuild(cfg, baseInfo, build, packages) + val inner = extractIrBuild(cfg, build, packages) val isNested = optionalBuild.dirs.nonEmpty BuildObject( @@ -60,7 +60,7 @@ trait BuildGenBase[M, D, I] { SortedMap((name, SortedMap(inner.scopedDeps.namedIvyDeps.toSeq*))) ), supertypes = getSupertypes(cfg, baseInfo, build), - inner = BuildGenUtil.renderIrBuild(inner), + inner = BuildGenUtil.renderIrBuild(inner, baseInfo), outer = if (isNested || baseInfo.moduleTypedef == null) "" else BuildGenUtil.renderIrTrait(baseInfo.moduleTypedef) @@ -87,7 +87,7 @@ trait BuildGenBase[M, D, I] { def extractIrBuild( cfg: C, - baseInfo: IrBaseInfo, + // baseInfo: IrBaseInfo, // `baseInfo` is no longer needed as we compare the `IrBuild` with `IrBaseInfo`/`IrTrait` in common code now. build: Node[M], packages: Map[(String, String, String), String] ): IrBuild diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index c62336ac728..ef415327b16 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -51,10 +51,7 @@ object BuildGenUtil { } - def takeIrPomIfNeeded(baseInfo: IrBaseInfo, irPom: IrPom): IrPom = - if (baseInfo.noPom || irPom != baseInfo.moduleTypedef.pomSettings) irPom else null - - def renderIrPom(value: IrPom): String = { + def renderIrPom(value: IrPom | Null): String = { if (value == null) "" else { import value.* @@ -64,8 +61,12 @@ object BuildGenUtil { } } - def renderIrBuild(value: IrBuild): String = { - import value.* + /** + * @param baseInfo to compare with [[build]] and render the values only if they are different. + */ + def renderIrBuild(build: IrBuild, baseInfo: IrBaseInfo): String = { + val baseTrait = baseInfo.moduleTypedef + import build.* val testModuleTypedef = if (!hasTest) "" else { @@ -90,13 +91,22 @@ object BuildGenUtil { s"""${renderArtifactName(projectName, dirs)} | - |${renderJavacOptions(javacOptions)} + |${renderJavacOptions( + javacOptions, + if (baseTrait != null) baseTrait.javacOptions else Seq.empty + )} | - |${renderScalaVersion(scalaVersion)} + |${renderScalaVersion(scalaVersion, if (baseTrait != null) baseTrait.scalaVersion else None)} | - |${renderScalacOptions(scalacOptions)} + |${renderScalacOptions( + scalacOptions, + if (baseTrait != null) baseTrait.scalacOptions else None + )} | - |${renderRepositories(repositories)} + |${renderRepositories( + repositories, + if (baseTrait != null) baseTrait.repositories else Seq.empty + )} | |${renderBomIvyDeps(scopedDeps.mainBomIvyDeps)} | @@ -112,9 +122,16 @@ object BuildGenUtil { | |${renderRunModuleDeps(scopedDeps.mainRunModuleDeps)} | - |${if (pomSettings == null) "" else renderPomSettings(renderIrPom(pomSettings))} + |${ + if (pomSettings != (if (baseTrait != null) baseTrait.pomSettings else null)) + renderPomSettings(renderIrPom(pomSettings)) + else "" + } | - |${renderPublishVersion(publishVersion)} + |${renderPublishVersion( + publishVersion, + if (baseTrait != null) baseTrait.publishVersion else null + )} | |${renderPomPackaging(packaging)} | @@ -303,7 +320,7 @@ object BuildGenUtil { def isBom(groupArtifactVersion: (String, String, String)): Boolean = groupArtifactVersion._2.endsWith("-bom") - def isNullOrEmpty(value: String): Boolean = + def isNullOrEmpty(value: String | Null): Boolean = null == value || value.isEmpty val linebreak: String = @@ -347,6 +364,7 @@ object BuildGenUtil { | def jvmId = "$jvmId" |}""".stripMargin + // TODO consider renaming to `renderOptionalDef` or `renderIfArgsNonEmpty`? def optional(construct: String, args: IterableOnce[String]): String = optional(construct + "(", args, ",", ")") @@ -356,6 +374,43 @@ object BuildGenUtil { else itr.mkString(start, sep, end) } + def renderStringSeqWithSuper( + defName: String, + args: Seq[String], + superArgs: Seq[String] = Seq.empty, + transform: String => String + ): Option[String] = + if (args.startsWith(superArgs)) { + val superLength = superArgs.length + if (args.length == superLength) None + else + // Note that the super def is called even when it's empty. + // Some super functions can be called without parentheses, but we just add it here for simplicity. + Some(optional( + s"super.$defName() ++ Seq", + args.iterator.drop(superLength).map(transform) + )) + } else + Some(optional(s"Seq", args.iterator.map(transform))) + + def renderStringSeqTargetDefWithSuper( + defName: String, + args: Seq[String], + superArgs: Seq[String] = Seq.empty, + transform: String => String + ) = + renderStringSeqWithSuper(defName, args, superArgs, transform).map(s"def $defName = " + _) + + def renderStringSeqTaskDefWithSuper( + defName: String, + args: Seq[String], + superArgs: Seq[String] = Seq.empty, + transform: String => String + ) = + renderStringSeqWithSuper(defName, args, superArgs, transform).map(s => + s"def $defName = Task.Anon { $s }" + ) + def scalafmtConfigFile: os.Path = os.temp( """version = "3.8.4" @@ -394,30 +449,26 @@ object BuildGenUtil { def renderRunModuleDeps(args: IterableOnce[String]): String = optional("def runModuleDeps = super.runModuleDeps ++ Seq", args) - def renderJavacOptions(args: IterableOnce[String]): String = - optional( - "def javacOptions = super.javacOptions() ++ Seq", - args.iterator.map(escape) - ) + def renderJavacOptions(args: Seq[String], superArgs: Seq[String] = Seq.empty): String = + renderStringSeqTargetDefWithSuper("javacOptions", args, superArgs, escape).getOrElse("") - def renderScalaVersion(scalaVersion: Option[String]): String = - scalaVersion.fold("")(scalaVersion => s"def scalaVersion = ${escape(scalaVersion)}") + def renderScalaVersion(arg: Option[String], superArg: Option[String] = None): String = + if (arg != superArg) arg.fold("")(scalaVersion => s"def scalaVersion = ${escape(scalaVersion)}") + else "" - def renderScalacOptions(args: Option[IterableOnce[String]]): String = - args.fold("")(args => - optional( - "def scalacOptions = super.scalacOptions() ++ Seq", - args.iterator.map(escape) - ) - ) + def renderScalacOptions( + args: Option[Seq[String]], + superArgs: Option[Seq[String]] = None + ): String = + renderStringSeqTargetDefWithSuper( + "scalacOptions", + args.getOrElse(Seq.empty), + superArgs.getOrElse(Seq.empty), + escape + ).getOrElse("") - def renderRepositories(args: IterableOnce[String]): String = - optional( - "def repositoriesTask = Task.Anon { super.repositoriesTask() ++ Seq(", - args, - ", ", - ") }" - ) + def renderRepositories(args: Seq[String], superArgs: Seq[String] = Seq.empty): String = + renderStringSeqTaskDefWithSuper("repositoriesTask", args, superArgs, identity).getOrElse("") def renderResources(args: IterableOnce[os.SubPath]): String = optional( @@ -438,15 +489,20 @@ object BuildGenUtil { if (isNullOrEmpty(artifact)) "" else s"def pomParentProject = Some($artifact)" - def renderPomSettings(arg: String): String = + def renderPomSettings(arg: String | Null, superArg: String | Null = null): String = if (isNullOrEmpty(arg)) "" else s"def pomSettings = $arg" - def renderPublishVersion(arg: String): String = - if (isNullOrEmpty(arg)) "" - else s"def publishVersion = ${escape(arg)}" + def renderPublishVersion(arg: String | Null, superArg: String | Null = null): String = + if (arg != superArg) + if (isNullOrEmpty(arg)) "" + else s"def publishVersion = ${escape(arg)}" + else "" - def renderPublishProperties(args: IterableOnce[(String, String)]): String = { + def renderPublishProperties( + args: Seq[(String, String)], + superArgs: Seq[(String, String)] = Seq.empty + ): String = { val tuples = args.iterator.map { case (k, v) => s"(${escape(k)}, ${escape(v)})" } optional("def publishProperties = super.publishProperties() ++ Map", tuples) } diff --git a/main/init/buildgen/src/mill/main/buildgen/ir.scala b/main/init/buildgen/src/mill/main/buildgen/ir.scala index affdfe6ead5..bb9fa119297 100644 --- a/main/init/buildgen/src/mill/main/buildgen/ir.scala +++ b/main/init/buildgen/src/mill/main/buildgen/ir.scala @@ -43,8 +43,8 @@ case class IrTrait( javacOptions: Seq[String], scalaVersion: Option[String], scalacOptions: Option[Seq[String]], - pomSettings: IrPom, - publishVersion: String, + pomSettings: IrPom | Null, + publishVersion: String | Null, publishProperties: Seq[(String, String)], repositories: Seq[String] ) @@ -78,6 +78,7 @@ case class IrLicense( ) // TODO Consider renaming to `IrModule(Build)` to disambiguate? sbt, for example, uses `ThisBuild` and `buildSettings` to refer to the whole build. +// TODO reuse the members in `IrTrait`? case class IrBuild( scopedDeps: IrScopedDeps, testModule: String, @@ -89,10 +90,10 @@ case class IrBuild( scalaVersion: Option[String], scalacOptions: Option[Seq[String]], projectName: String, - pomSettings: IrPom, - publishVersion: String, - packaging: String, - pomParentArtifact: IrArtifact, + pomSettings: IrPom | Null, + publishVersion: String | Null, + packaging: String | Null, + pomParentArtifact: IrArtifact | Null, resources: Seq[os.SubPath], testResources: Seq[os.SubPath], publishProperties: Seq[(String, String)] @@ -139,7 +140,9 @@ case class IrScopedDeps( testCompileModuleDeps: SortedSet[String] = SortedSet() ) +// TODO remove `IrBaseInfo` and just use `IrTrait` directly? case class IrBaseInfo( + /* javacOptions: Seq[String] = Nil, scalaVersion: Option[String] = None, scalacOptions: Option[Seq[String]] = None, @@ -147,7 +150,9 @@ case class IrBaseInfo( noPom: Boolean = true, publishVersion: String = "", publishProperties: Seq[(String, String)] = Nil, - moduleTypedef: IrTrait = null + */ + // TODO consider renaming directly to `trait` or `baseTrait`? + moduleTypedef: IrTrait | Null = null ) sealed class IrDependencyType diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index e32b6580a11..da0620bba53 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -150,21 +150,12 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java repos ) - IrBaseInfo( - javacOptions, - scalaVersion, - scalacOptions, - repos, - pomSettings == null, - publishVersion, - Seq.empty, - typedef - ) + IrBaseInfo(typedef) } override def extractIrBuild( cfg: Config, - baseInfo: IrBaseInfo, + // baseInfo: IrBaseInfo, build: Node[ProjectModel], packages: Map[(String, String, String), String] ): IrBuild = { @@ -177,13 +168,13 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java testModuleMainType = "MavenTests", hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, - repositories = getRepositories(project).diff(baseInfo.repositories), - javacOptions = getJavacOptions(project).diff(baseInfo.javacOptions), + repositories = getRepositories(project), + javacOptions = getJavacOptions(project), scalaVersion = None, scalacOptions = None, projectName = getArtifactId(project), - pomSettings = if (baseInfo.noPom) extractPomSettings(project) else null, - publishVersion = if (version == baseInfo.publishVersion) null else version, + pomSettings = extractPomSettings(project), + publishVersion = version, packaging = getPomPackaging(project), // not available pomParentArtifact = null, @@ -209,7 +200,10 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = Seq("RootModule") ++ - Option.when(null != build.value.maven().pom() && baseInfo.noPom) { "PublishModule" } ++ + Option.when(null != build.value.maven().pom() && { + val baseTrait = baseInfo.moduleTypedef + baseTrait == null || !baseTrait.moduleSupertypes.contains("PublishModule") + }) { "PublishModule" } ++ Option.when(build.dirs.nonEmpty || os.exists(getMillSourcePath(build.value) / "src")) { getModuleSupertypes(cfg) }.toSeq.flatten @@ -242,7 +236,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java .toSeq } else Seq.empty - def getPublishVersion(project: ProjectModel): String = + def getPublishVersion(project: ProjectModel): String | Null = project.version() match { case "" | "unspecified" => null case version => version @@ -252,7 +246,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java BuildGenUtil.renderIvyString(dep.group(), dep.name(), dep.version()) } - def extractPomSettings(project: ProjectModel): IrPom = { + def extractPomSettings(project: ProjectModel): IrPom | Null = { val pom = project.maven.pom() if (null == pom) null else { diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index 4660229315a..ef79db544c0 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -89,21 +89,12 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] getRepositories(model) ) - IrBaseInfo( - javacOptions, - scalaVersion, - scalacOptions, - repositories, - noPom = false, - publishVersion, - publishProperties, - typedef - ) + IrBaseInfo(typedef) } override def extractIrBuild( cfg: Config, - baseInfo: IrBaseInfo, + // baseInfo: IrBaseInfo, build: Node[Model], packages: Map[(String, String, String), String] ): IrBuild = { @@ -117,12 +108,12 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] hasTest = os.exists(getMillSourcePath(model) / "src/test"), dirs = build.dirs, repositories = getRepositories(model), - javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model).diff(baseInfo.javacOptions), + javacOptions = Plugins.MavenCompilerPlugin.javacOptions(model), scalaVersion = None, scalacOptions = None, projectName = getArtifactId(model), - pomSettings = if (baseInfo.noPom) extractPomSettings(model) else null, - publishVersion = if (version == baseInfo.publishVersion) null else version, + pomSettings = extractPomSettings(model), + publishVersion = version, packaging = model.getPackaging, pomParentArtifact = mkPomParent(model.getParent), resources = @@ -131,7 +122,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] testResources = processResources(model.getBuild.getTestResources, getMillSourcePath(model)) .filterNot(_ == mavenTestResourceDir), - publishProperties = getPublishProperties(model, cfg.shared).diff(baseInfo.publishProperties) + publishProperties = getPublishProperties(model, cfg.shared) ) } diff --git a/main/init/maven/test/resources/expected/config/build.mill b/main/init/maven/test/resources/expected/config/build.mill index 9cf6310e09c..9d563d4bfb2 100644 --- a/main/init/maven/test/resources/expected/config/build.mill +++ b/main/init/maven/test/resources/expected/config/build.mill @@ -27,6 +27,20 @@ object `package` extends RootModule with MyModule { def javacOptions = super.javacOptions() ++ Seq("-source", "1.6", "-target", "1.6") + def pomSettings = PomSettings( + "Sample multi module Maven project with a working, deployable site.", + "com.example.maven-samples", + "http://www.example.com", + Seq(), + VersionControl( + Some("http://github.com/gabrielf/maven-samples"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git"), + Some("HEAD") + ), + Seq() + ) + def pomPackagingType = PackagingType.Pom def publishProperties = super.publishProperties() ++ Map( @@ -39,6 +53,20 @@ object `package` extends RootModule with MyModule { def javacOptions = super.javacOptions() ++ Seq("-source", "1.6", "-target", "1.6") + def pomSettings = PomSettings( + "Logic.", + "com.example.maven-samples", + "http://www.example.com/server", + Seq(), + VersionControl( + Some("http://github.com/gabrielf/maven-samples/server"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git/server"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git/server"), + Some("HEAD") + ), + Seq() + ) + def pomParentProject = Some(Artifact( "com.example.maven-samples", "multi-module-parent", @@ -72,6 +100,20 @@ object `package` extends RootModule with MyModule { def compileIvyDeps = super.compileIvyDeps() ++ Seq(Deps.`javax.servlet.jsp:jsp-api`, Deps.`javax.servlet:servlet-api`) + def pomSettings = PomSettings( + "Webapp.", + "com.example.maven-samples", + "http://www.example.com/webapp", + Seq(), + VersionControl( + Some("http://github.com/gabrielf/maven-samples/webapp"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git/webapp"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git/webapp"), + Some("HEAD") + ), + Seq() + ) + def pomPackagingType = "war" def pomParentProject = Some(Artifact( @@ -98,6 +140,20 @@ object `package` extends RootModule with MyModule { def ivyDeps = super.ivyDeps() ++ Seq(Deps.`javax.servlet.jsp:jsp-api`, Deps.`javax.servlet:servlet-api`) + def pomSettings = PomSettings( + "Sample single module Maven project with a working, deployable site.", + "com.example.maven-samples", + "http://www.example.com", + Seq(), + VersionControl( + Some("http://github.com/gabrielf/maven-samples"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git"), + Some("scm:git:git@github.com:gabrielf/maven-samples.git"), + Some("HEAD") + ), + Seq() + ) + def publishProperties = super.publishProperties() ++ Map( ("project.build.sourceEncoding", "utf-8"), ("project.reporting.outputEncoding", "utf-8") diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index be87963d5cf..c60928b35f8 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -229,21 +229,12 @@ object SbtBuildGenMain repositories ) - IrBaseInfo( - javacOptions, - scalaVersion, - scalacOptions, - repositories, - noPom = false, // always publish - publishVersion, - Seq.empty, - typedef - ) + IrBaseInfo(typedef) } override def extractIrBuild( cfg: Config, - baseInfo: IrBaseInfo, + // baseInfo: IrBaseInfo, build: Node[Project], packages: Map[(String, String, String), String] ): IrBuild = { @@ -257,18 +248,13 @@ object SbtBuildGenMain testModuleMainType = "SbtTests", hasTest = os.exists(getMillSourcePath(project) / "src/test"), dirs = build.dirs, - repositories = getRepositories(buildInfo).diff(baseInfo.repositories), - javacOptions = getJavacOptions(buildInfo).diff(baseInfo.javacOptions), - scalaVersion = - if (buildInfo.scalaVersion != baseInfo.scalaVersion) buildInfo.scalaVersion else None, - scalacOptions = buildInfo.scalacOptions.map(scalacOptions => - baseInfo.scalacOptions.fold(scalacOptions)(baseScalacOptions => - scalacOptions.diff(baseScalacOptions) - ) - ), + repositories = getRepositories(buildInfo), + javacOptions = getJavacOptions(buildInfo), + scalaVersion = buildInfo.scalaVersion, + scalacOptions = buildInfo.scalacOptions, projectName = project.name, - pomSettings = takeIrPomIfNeeded(baseInfo, extractPomSettings(buildInfo.buildPublicationInfo)), - publishVersion = if (version == baseInfo.publishVersion) null else version, + pomSettings = extractPomSettings(buildInfo.buildPublicationInfo), + publishVersion = version, packaging = null, // not available in sbt as it seems pomParentArtifact = null, // not available resources = Nil, @@ -305,7 +291,7 @@ object SbtBuildGenMain s"coursier.maven.MavenRepository(${escape(resolver.root)})" ) - def getPublishVersion(buildInfo: BuildInfo): String = + def getPublishVersion(buildInfo: BuildInfo): String | Null = buildInfo.buildPublicationInfo.version.orNull // originally named `ivyInterp` in the Maven and module diff --git a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill index 8e5e0a141bd..250cbc075e2 100644 --- a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill @@ -79,6 +79,8 @@ object `package` extends RootModule with BaseModule { object multi1 extends BaseModule { + def scalacOptions = super.scalacOptions() ++ Seq("-V") + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.julien-truffaut:monocle-core`, @@ -125,6 +127,16 @@ object `package` extends RootModule with BaseModule { object multi2 extends BaseModule { + def scalacOptions = Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation" + ) + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.pureconfig:pureconfig`, diff --git a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill index de6ca5569e5..53453cefe08 100644 --- a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill @@ -81,6 +81,8 @@ object `package` extends RootModule with BaseModule { object multi1 extends BaseModule { + def scalacOptions = super.scalacOptions() ++ Seq("-V") + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.julien-truffaut:monocle-core`, @@ -104,6 +106,16 @@ object `package` extends RootModule with BaseModule { object multi2 extends BaseModule { + def scalacOptions = Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation" + ) + def ivyDeps = super.ivyDeps() ++ Seq( Deps.`ch.qos.logback:logback-classic`, Deps.`com.github.pureconfig:pureconfig`, diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill index ddd1fcbe2b9..a112998394a 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi1/package.mill @@ -18,7 +18,8 @@ object `package` extends RootModule with SbtModule with PublishModule { "-language:postfixOps", "-deprecation", "-encoding", - "utf8" + "utf8", + "-V" ) def repositoriesTask = Task.Anon { diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill index 16bc9ddac87..2432745489e 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/multi2/package.mill @@ -16,9 +16,7 @@ object `package` extends RootModule with SbtModule with PublishModule { "-language:higherKinds", "-language:implicitConversions", "-language:postfixOps", - "-deprecation", - "-encoding", - "utf8" + "-deprecation" ) def repositoriesTask = Task.Anon { diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index f6e0ad0168d..a97b9cff956 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -32,7 +32,7 @@ lazy val common = project lazy val multi1 = project .settings( name := "multi1", - settings, + settings ++ Seq(scalacOptions ++= Seq("-V")), assemblySettings, libraryDependencies ++= commonDependencies ++ Seq( dependencies.monocleCore, @@ -46,7 +46,15 @@ lazy val multi1 = project lazy val multi2 = project .settings( name := "multi2", - settings, + settings ++ Seq(scalacOptions := Seq( + "-unchecked", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-language:implicitConversions", + "-language:postfixOps", + "-deprecation" + )), assemblySettings, libraryDependencies ++= commonDependencies ++ Seq( dependencies.pureconfig From 34b837d1fec2b135086a481476dfae5805b69d3d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 11:16:39 +0800 Subject: [PATCH 54/70] Fix assertions in `MillInitSbtMultiProjectExampleTests` that fail due to commit 768a2b104f97d0275a47b9e0f4e1f492dec647fd, which is uncompleted in commit c819207ff0e8ad7ad81304ba448e07fa00d85629 Somehow this fails in the CI on Linux with JDK 11 but not on my device: ```text java.lang.AssertionError: assertion failed: task common.test succeeded ``` --- .../feature/init/src/MillInitSbtTests.scala | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 6d002f52fcb..f2047ab6af6 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -93,13 +93,18 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { bumpSbtTo1107(tester.workspacePath) val submodules = Seq("common", "multi1", "multi2") - - tester.testMillInit( - // initCommand = defaultInitCommand ++ Seq("--jvm-id", "11"), - expectedCompileTasks = - Some(if (System.getProperty("java.version").split('.').head.toInt <= 11) - SplitResolvedTasks(Seq("compile") ++ submodules.flatMap(_.allCompileTasks), Seq.empty) - else { + if (System.getProperty("java.version").split('.').head.toInt <= 11) + tester.testMillInit( + expectedCompileTasks = Some(SplitResolvedTasks( + Seq("compile") ++ submodules.flatMap(_.allCompileTasks), + Seq.empty + )), + expectedTestTasks = Some(SplitResolvedTasks(submodules.map(_.testTask), Seq.empty)) + ) + else + tester.testMillInit( + // initCommand = defaultInitCommand ++ Seq("--jvm-id", "11"), + expectedCompileTasks = Some({ /* `multi1.compile` doesn't work well when Mill is run with JDK 17 and 21: ```text @@ -109,7 +114,7 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { scala.tools.nsc.classpath.JrtClassPath.asURLs(DirectoryClassPath.scala:183) ... ``` - Passing a `jvmId` 11 doesn't work. + Passing a `jvmId` 11 doesn't work here. */ val succeededSubmoduleCompileTasks = Seq("common.compile", "multi2.compile") SplitResolvedTasks( @@ -117,8 +122,8 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { (submodules.flatMap(_.allCompileTasks).toSet -- succeededSubmoduleCompileTasks).toSeq ) }), - expectedTestTasks = Some(SplitResolvedTasks(Seq(), submodules.map(_.testTask))) - ) + expectedTestTasks = Some(SplitResolvedTasks(Seq.empty, submodules.map(_.testTask))) + ) } } } From 729fc9d619ec4a9d30be6bf2347fb13d310544de Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 11:44:51 +0800 Subject: [PATCH 55/70] Fix bugs introduced in commit 22033e909dd461689b374815ca4602b087b8830f revealed by `MillInitMavenNettyTests` in the integration tests --- .../src/mill/main/buildgen/BuildGenUtil.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index ef415327b16..56e4297f5f8 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -386,12 +386,13 @@ object BuildGenUtil { else // Note that the super def is called even when it's empty. // Some super functions can be called without parentheses, but we just add it here for simplicity. - Some(optional( - s"super.$defName() ++ Seq", - args.iterator.drop(superLength).map(transform) - )) + Some(args.iterator.drop(superLength).map(transform) + .mkString(s"super.$defName() ++ Seq(", ",", ")")) } else - Some(optional(s"Seq", args.iterator.map(transform))) + Some( + if (args.isEmpty) "Seq.empty[String]" // The inference type is `Seq[Nothing]` otherwise. + else args.iterator.map(transform).mkString("Seq(", ",", ")") + ) def renderStringSeqTargetDefWithSuper( defName: String, From f1bfd9a9d9520d41297b87eb3224edce6b894db4 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 11:52:47 +0800 Subject: [PATCH 56/70] Fix some grammar mistakes --- main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index 56e4297f5f8..3d14f4a5f32 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -385,12 +385,12 @@ object BuildGenUtil { if (args.length == superLength) None else // Note that the super def is called even when it's empty. - // Some super functions can be called without parentheses, but we just add it here for simplicity. + // Some super functions can be called without parentheses, but we just add them here for simplicity. Some(args.iterator.drop(superLength).map(transform) .mkString(s"super.$defName() ++ Seq(", ",", ")")) } else Some( - if (args.isEmpty) "Seq.empty[String]" // The inference type is `Seq[Nothing]` otherwise. + if (args.isEmpty) "Seq.empty[String]" // The inferred type is `Seq[Nothing]` otherwise. else args.iterator.map(transform).mkString("Seq(", ",", ")") ) From af5454d96a0bac552c687284f77083100eebe6f0 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 11:59:56 +0800 Subject: [PATCH 57/70] Rename some integration tests for explicitness and consistency --- integration/feature/init/src/MillInitSbtTests.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index f2047ab6af6..3a2e931e68e 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -10,7 +10,7 @@ def bumpSbtTo1107(workspacePath: os.Path) = // relatively small libraries -object MillInitLibraryExampleTests extends BuildGenTestSuite { +object MillInitSbtLibraryExampleTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 21 KB @@ -27,7 +27,7 @@ object MillInitLibraryExampleTests extends BuildGenTestSuite { } } -object MillInitScalaCsv200Tests extends BuildGenTestSuite { +object MillInitSbtScalaCsv200Tests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 34 KB @@ -47,7 +47,7 @@ object MillInitScalaCsv200Tests extends BuildGenTestSuite { } } -object MillInitScalaCsv136Tests extends BuildGenTestSuite { +object MillInitSbtScalaCsv136Tests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 28 KB @@ -130,7 +130,7 @@ object MillInitSbtMultiProjectExampleTests extends BuildGenTestSuite { // relatively large libraries -object MillInitZioHttpTests extends BuildGenTestSuite { +object MillInitSbtZioHttpTests extends BuildGenTestSuite { def tests: Tests = Tests { /* - 1.4 MB From 87940401beec8fb61cc730a64d6f352306057f85 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 12:38:44 +0800 Subject: [PATCH 58/70] Update `MillInitSbtZioHttpTests` with the result and improve `testAllResolvedTasks` in the meantime --- .../init/src/IntegrationTesterExt.scala | 12 ++--- .../feature/init/src/MillInitSbtTests.scala | 45 +++++++++++-------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/integration/feature/init/src/IntegrationTesterExt.scala b/integration/feature/init/src/IntegrationTesterExt.scala index 76ef0a77371..e0a71cc0bcb 100644 --- a/integration/feature/init/src/IntegrationTesterExt.scala +++ b/integration/feature/init/src/IntegrationTesterExt.scala @@ -7,7 +7,7 @@ val defaultInitCommand = Seq("init", "--base-module", "BaseModule", "--deps-object", "Deps", "--merge") case class SplitResolvedTasks(successful: Seq[String], failed: Seq[String]) { - val all = (successful ++ failed).toSet + val all = (successful ++ failed).sorted } extension (tester: IntegrationTester) @@ -39,10 +39,13 @@ extension (tester: IntegrationTester) assert(!resolveAllTasksResult.isSuccess) )(expected => { assert(resolveAllTasksResult.isSuccess) - val resolvedAllTasks = resolveAllTasksResult.out.linesIterator.toSet + val resolvedAllTasks = resolveAllTasksResult.out.linesIterator.toSeq.sorted Predef.assert( expected.all == resolvedAllTasks, - "expected: " + expected.all + ", resolved: " + resolvedAllTasks + s""" + |expected: ${expected.all} + |resolved: $resolvedAllTasks + |""".stripMargin ) for (task <- expected.successful) @@ -72,7 +75,4 @@ extension (module: String) { testModule.testTask def allCompileTasks: Seq[String] = Seq(compileTask, testCompileTask) - // TODO This seems not needed. - def allTestTasks: Seq[String] = - Seq(testTask, testTestTask) } diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 3a2e931e68e..059dca51e98 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -141,30 +141,37 @@ object MillInitSbtZioHttpTests extends BuildGenTestSuite { test - integrationTest(url) { tester => bumpSbtTo1107(tester.workspacePath) - Seq( - "sbt-zio-http-grpc-tests", - "sbt-zio-http-grpc", - "zio-http-benchmarks.compile", - "zio-http-cli", - "zio-http-docs", - "zio-http-example", - "zio-http-gen", - "zio-http-htmx", - "zio-http-testkit", - "zio-http-tools", - "zio-http.js", - "zio-http.jvm" - ) + object submodules { + val withTests = Seq( + "sbt-zio-http-grpc-tests", + "zio-http-cli", + "zio-http-gen", + "zio-http-htmx", + "zio-http-testkit", + "zio-http.js", + "zio-http.jvm" + ) + val withoutTests = Seq( + "sbt-zio-http-grpc", + "zio-http-benchmarks", + "zio-http-docs", + "zio-http-example", + "zio-http-tools" + ) + } - // TODO - /* // probably due to inheriting `MavenTests` instead of `SbtTests` // Some dependencies with currently unsupported `CrossVersion` `For3Use2_13` are not imported properly tester.testMillInit( - expectedCompileTasks = Some(SplitResolvedTasks(Seq("compile"), submodules.flatMap(_.allCompileTasks))), - expectedTestTasks = Some(SplitResolvedTasks(Seq(), submodules.flatMap(_.allTestTasks))) + expectedCompileTasks = + Some(SplitResolvedTasks( + Seq("compile"), + submodules.withTests.flatMap(_.allCompileTasks) ++ + submodules.withoutTests.map(_.compileTask) + )), + expectedTestTasks = + Some(SplitResolvedTasks(Seq(), submodules.withTests.map(_.testTask))) ) - */ } } } From ca1c82766b8325e3f50312e70a184e6b499461d6 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 15:36:47 +0800 Subject: [PATCH 59/70] Support more `CrossVersion` types - Reformat `ExportBuildPlugin.scala` BTW. - The existing unit tests and integration tests pass as tested on my device. --- .../sbt/models/src/mill/main/sbt/Models.scala | 28 ++++++++++- .../mill/main/sbt/ExportBuildPlugin.scala | 50 +++++++++++-------- .../src/mill/main/sbt/SbtBuildGenMain.scala | 8 ++- 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 9fb9d7e8812..1963334175e 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -131,7 +131,7 @@ object Project { case class Dependency( organization: String, // `groupId` in Maven name: String, // `artifactId` in Maven - crossVersion: Boolean = false, // TODO support `CrossVersion` types other than binary? + crossVersion: CrossVersion = CrossVersion.Disabled, revision: String, configurations: Option[String] // BOM seems not supported by sbt. See https://stackoverflow.com/questions/42032303/how-do-i-use-a-maven-bom-bill-of-materials-to-manage-my-dependencies-in-sbt. @@ -140,3 +140,29 @@ case class Dependency( object Dependency { implicit val rw: RW[Dependency] = macroRW } + +/** + * @see [[sbt.librarymanagement.CrossVersion]] + */ +sealed trait CrossVersion +object CrossVersion { + case object Disabled extends CrossVersion { + implicit val rw: RW[Disabled.type] = macroRW + } + case object Binary extends CrossVersion { + implicit val rw: RW[Binary.type] = macroRW + } + case object Full extends CrossVersion { + implicit val rw: RW[Full.type] = macroRW + } + + /** + * Including the cases [[sbt.librarymanagement.Constant]], [[sbt.librarymanagement.For2_13Use3]], and [[sbt.librarymanagement.For3Use2_13]]. + */ + case class Constant(value: String) extends CrossVersion + object Constant { + implicit val rw: RW[Constant] = macroRW + } + + implicit val rw: RW[CrossVersion] = macroRW +} diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index bc16baea4c9..a6c26ffc796 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -2,8 +2,8 @@ package mill.main.sbt import sbt.Keys.* import sbt.io.IO -import sbt.librarymanagement.Disabled -import sbt.{Def, Developer as _, Project as _, Resolver as _, ScmInfo as _, *} +import sbt.librarymanagement.{Constant, Disabled, For2_13Use3, For3Use2_13} +import sbt.{Def, CrossVersion as _, Developer as _, Project as _, Resolver as _, ScmInfo as _, *} import upickle.default.* import java.io.File @@ -18,7 +18,8 @@ object ExportBuildPlugin extends AutoPlugin { "get the `mill.main.sbt.BuildInfo` model of this build or this project" ) // not used anymore - val millInitAllDependencies = taskKey[Seq[Dependency]]("get the all the `mill.main.sbt.Dependency`s of this project") + val millInitAllDependencies = + taskKey[Seq[Dependency]]("get the all the `mill.main.sbt.Dependency`s of this project") val millInitProject = taskKey[Project]("get the `mill.main.sbt.Project` model of this project") val millInitExportBuild = taskKey[File]("export the build in a JSON file for `mill init`") } @@ -33,8 +34,8 @@ object ExportBuildPlugin extends AutoPlugin { (name, url.toExternalForm) }), organization.?.value, - //organizationName.?.value, // not needed - //organizationHomepage.?.value.map(_.map(_.toExternalForm)), // not needed + // organizationName.?.value, // not needed + // organizationHomepage.?.value.map(_.map(_.toExternalForm)), // not needed developers.?.value.map(_.map(developer => Developer(developer.id, developer.name, developer.email, developer.url.toExternalForm) )), @@ -62,10 +63,10 @@ object ExportBuildPlugin extends AutoPlugin { buildInfoSetting, millInitProject := Project( - //organization.value, + // organization.value, name.value, - //version.value, - //baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), + // version.value, + // baseDirectory.value.relativeTo((ThisBuild / baseDirectory).value).get.getPath.split(File.separator), baseDirectory.value.getPath, /*{ // keep the project `BuildInfo` members only when they are different @@ -95,20 +96,25 @@ object ExportBuildPlugin extends AutoPlugin { millInitBuildInfo.value, /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ - /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { moduleID => - Dependency( - moduleID.organization, - moduleID.name, - moduleID.crossVersion match { - case Disabled => false - case _: Binary => true - case crossVersion => - println(s"Dependency $moduleID with unsupported `CrossVersion`: $crossVersion") - false - }, - moduleID.revision, - moduleID.configurations - ) + /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { + moduleID => + Dependency( + moduleID.organization, + moduleID.name, + moduleID.crossVersion match { + case Disabled => CrossVersion.Disabled + case _: Binary => CrossVersion.Binary + case _: Full => CrossVersion.Full + case _: For3Use2_13 => CrossVersion.Constant("2.13") + case _: For2_13Use3 => CrossVersion.Constant("3") + case constant: Constant => CrossVersion.Constant(constant.value) + case crossVersion => + println(s"Dependency $moduleID with unsupported `CrossVersion`: $crossVersion") + CrossVersion.Disabled + }, + moduleID.revision, + moduleID.configurations + ) } ), // `target.value` doesn't work in `globalSettings` and `buildSettings`, so this is added to `projectSettings. diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index c60928b35f8..7b63b19c8bd 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -302,7 +302,13 @@ object SbtBuildGenMain which is a `Vector` of `sbt.librarymanagement.Artifact` and `sbt.librarymanagement.InclExclRule`s. */ import dependency.* - s"ivy\"$organization${if (crossVersion) "::" else ":"}$name:$revision\"" + s"ivy\"$organization${ + crossVersion match + case CrossVersion.Disabled => s":$name" + case CrossVersion.Binary => s"::$name" + case CrossVersion.Full => s":::$name" + case CrossVersion.Constant(value) => s":${name}_$value" + }:$revision\"" } def extractPomSettings(buildPublicationInfo: BuildPublicationInfo): IrPom = { From 1cd32b0658f6d353d7a4a976ac44ca935800dc9e Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 15:43:29 +0800 Subject: [PATCH 60/70] Fix a warning discovered by CI `lint-autofix` --- main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index ef79db544c0..a5a382f8f1f 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -86,7 +86,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] pomSettings, publishVersion, publishProperties, - getRepositories(model) + repositories ) IrBaseInfo(typedef) From d34d38583439272aa153aa0f26f60f1d70c9890d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Mon, 24 Feb 2025 15:44:49 +0800 Subject: [PATCH 61/70] Fix a warning by making a def private --- main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index 7b63b19c8bd..d6b05c0513e 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -195,7 +195,7 @@ object SbtBuildGenMain ): Tree[Node[Option[Project]]] = input._2 - def sbtSupertypes = Seq("SbtModule", "PublishModule") // always publish + private def sbtSupertypes = Seq("SbtModule", "PublishModule") // always publish def getBaseInfo( input: (BuildInfo, Tree[Node[Option[Project]]]), From 1d8011cec7464e1dc87136e29ca4ed8c07fa0451 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 25 Feb 2025 03:34:09 +0800 Subject: [PATCH 62/70] Supported converting `type, `classifier`, and `exclusions` in sbt dependencies - Fix a bug that the custom `RuntimeException` is not thrown "main/init/package.mill" after the code is refactored to use OS-Lib. - Update unit test code to test the changes and the expected snapshots in test resources correspondingly. - The existing unit tests and integration tests pass as tested on my device. - Remove a redundant import `scala.language.higherKinds` in `ExportBuildPlugin.scala`. --- .../src/mill/main/buildgen/BuildGenUtil.scala | 18 ++++++++--- .../mill/main/gradle/GradleBuildGenMain.scala | 2 +- .../mill/main/maven/MavenBuildGenMain.scala | 1 + main/init/package.mill | 17 +++++++---- .../sbt/models/src/mill/main/sbt/Models.scala | 5 +++- .../mill/main/sbt/ExportBuildPlugin.scala | 30 +++++++++++++++---- .../src/mill/main/sbt/SbtBuildGenMain.scala | 27 +++++++++-------- .../all/sbt-multi-project-example/build.mill | 6 ++++ .../sbt-multi-project-example/build.mill | 10 ++++++- .../nested/nested/package.mill | 4 +++ .../sbt-multi-project-example/build.sbt | 5 ++++ 11 files changed, 94 insertions(+), 31 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala index 3d14f4a5f32..f8319e51e32 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenUtil.scala @@ -11,6 +11,7 @@ import mill.constants.CodeGenConstants.{ import mill.constants.OutFiles import mill.main.buildgen.BuildObject.Companions import mill.runner.FileImportGraph.backtickWrap +import mill.scalalib.CrossVersion import scala.collection.immutable.SortedSet import scala.util.boundary @@ -287,11 +288,20 @@ object BuildGenUtil { def renderIvyString( group: String, artifact: String, - version: String = null, - tpe: String = null, - classifier: String = null, + crossVersion: Option[CrossVersion] = None, + version: String | Null = null, + tpe: String | Null = null, + classifier: String | Null = null, excludes: IterableOnce[(String, String)] = Seq.empty ): String = { + val sepArtifact = crossVersion match { + case None => s":$artifact" + case Some(value) => value match { + case CrossVersion.Constant(value, _) => s":${artifact}_$value" + case CrossVersion.Binary(_) => s"::$artifact" + case CrossVersion.Full(_) => s":::$artifact" + } + } val sepVersion = if (null == version) { println( @@ -314,7 +324,7 @@ object BuildGenUtil { .map { case (group, artifact) => s";exclude=$group:$artifact" } .mkString - s"ivy\"$group:$artifact$sepVersion$sepTpe$sepClassifier$sepExcludes\"" + s"ivy\"$group$sepArtifact$sepVersion$sepTpe$sepClassifier$sepExcludes\"" } def isBom(groupArtifactVersion: (String, String, String)): Boolean = diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index da0620bba53..b9c72425c1d 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -243,7 +243,7 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java } def interpIvy(dep: JavaModel.Dep): String = { - BuildGenUtil.renderIvyString(dep.group(), dep.name(), dep.version()) + BuildGenUtil.renderIvyString(dep.group(), dep.name(), version = dep.version()) } def extractPomSettings(project: ProjectModel): IrPom | Null = { diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index a5a382f8f1f..e5ceb32c4bf 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -173,6 +173,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] BuildGenUtil.renderIvyString( dep.getGroupId, dep.getArtifactId, + None, dep.getVersion, dep.getType, dep.getClassifier, diff --git a/main/init/package.mill b/main/init/package.mill index 716171fc646..65b11006c4d 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -109,19 +109,24 @@ object `package` extends RootModule with build.MillPublishScalaModule { val isWindows = System.getProperty("os.name").toLowerCase.startsWith("windows") val version = build.millVersion() - if ( + + try { os.call( // The version is passed to the sbt build so it correctly resolves the "models" dependency version and sets its own version. if (isWindows) ("sbt.bat", s"""set version := \\"$version\\"""", "assembly") else ("sbt", s"""set version := "$version"""", "assembly"), cwd = sbtPluginProjectPath, stdout = os.Inherit - ).exitCode != 0 - ) - throw new RuntimeException( - "Failed to run `sbt assembly` in the \"sbt-mill-init-export-build\" sbt project. " + - "Check if the project builds correctly and if you have sbt available on your system, and install it if you don't." ) + } catch { + case e: os.SubprocessException => + throw new RuntimeException( + "Failed to run `sbt assembly` in the \"sbt-mill-init-export-build\" sbt project. " + + "Check if the project builds correctly and if you have sbt available on your system, and install it if you don't.", + e + ) + case t : Throwable => throw t + } os.copy( sbtPluginProjectPath / "target" / "scala-2.12" / "sbt-1.0" / s"sbt-mill-init-export-build-assembly-$version.jar", diff --git a/main/init/sbt/models/src/mill/main/sbt/Models.scala b/main/init/sbt/models/src/mill/main/sbt/Models.scala index 1963334175e..b813dd88fc7 100644 --- a/main/init/sbt/models/src/mill/main/sbt/Models.scala +++ b/main/init/sbt/models/src/mill/main/sbt/Models.scala @@ -133,9 +133,12 @@ case class Dependency( name: String, // `artifactId` in Maven crossVersion: CrossVersion = CrossVersion.Disabled, revision: String, - configurations: Option[String] + configurations: Option[String], // BOM seems not supported by sbt. See https://stackoverflow.com/questions/42032303/how-do-i-use-a-maven-bom-bill-of-materials-to-manage-my-dependencies-in-sbt. // isBom : Boolean = false + tpe: Option[String], + classifier: Option[String], + excludes: Seq[(String, String)] ) object Dependency { implicit val rw: RW[Dependency] = macroRW diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index a6c26ffc796..270fcebcefa 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -7,7 +7,6 @@ import sbt.{Def, CrossVersion as _, Developer as _, Project as _, Resolver as _, import upickle.default.* import java.io.File -import scala.language.higherKinds object ExportBuildPlugin extends AutoPlugin { override def trigger = allRequirements @@ -96,9 +95,9 @@ object ExportBuildPlugin extends AutoPlugin { millInitBuildInfo.value, /** See the TODO in [[sbt.Defaults]] above `allDependencies :=` for more details (v1.10.7, Lines 3210 - 3212). */ - /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).map { - moduleID => - Dependency( + /*allDependencies.value*/ (projectDependencies.value ++ libraryDependencies.value).flatMap( + moduleID => { + val dependency = Dependency( moduleID.organization, moduleID.name, moduleID.crossVersion match { @@ -113,9 +112,28 @@ object ExportBuildPlugin extends AutoPlugin { CrossVersion.Disabled }, moduleID.revision, - moduleID.configurations + moduleID.configurations, + None, + None, + moduleID.exclusions.map(inclExclRule => + (inclExclRule.organization, inclExclRule.name) + ) ) - } + val explicitArtifacts = moduleID.explicitArtifacts + if (explicitArtifacts.isEmpty) + Seq(dependency) + else + explicitArtifacts.map(artifact => + dependency.copy( + tpe = Some(artifact.`type`) /*{ + val tpe = artifact.`type` + Option.when(tpe != DefaultType)(tpe) + }*/, + classifier = artifact.classifier + ) + ) + } + ) ), // `target.value` doesn't work in `globalSettings` and `buildSettings`, so this is added to `projectSettings. millInitExportBuild := { diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index d6b05c0513e..ed811d6be62 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -5,6 +5,7 @@ import mill.constants.Util import mill.main.buildgen.* import mill.main.buildgen.BuildGenUtil.* import mill.main.buildgen.IrDependencyType.* +import mill.scalalib.CrossVersion as MillCrossVersion import os.Path import scala.collection.MapView @@ -296,19 +297,21 @@ object SbtBuildGenMain // originally named `ivyInterp` in the Maven and module def renderIvy(dependency: Dependency): String = { - /* - TODO `type, `classifier`, and `exclusions` are not processed yet. - Processing them involves extracting information from `ModuleID.explicitArtifacts` - which is a `Vector` of `sbt.librarymanagement.Artifact` and `sbt.librarymanagement.InclExclRule`s. - */ import dependency.* - s"ivy\"$organization${ - crossVersion match - case CrossVersion.Disabled => s":$name" - case CrossVersion.Binary => s"::$name" - case CrossVersion.Full => s":::$name" - case CrossVersion.Constant(value) => s":${name}_$value" - }:$revision\"" + renderIvyString( + organization, + name, + crossVersion match { + case CrossVersion.Disabled => None + case CrossVersion.Binary => Some(MillCrossVersion.Binary(false)) + case CrossVersion.Full => Some(MillCrossVersion.Full(false)) + case CrossVersion.Constant(value) => Some(MillCrossVersion.Constant(value, false)) + }, + version = revision, + tpe = tpe.orNull, + classifier = classifier.orNull, + excludes = excludes + ) } def extractPomSettings(buildPublicationInfo: BuildPublicationInfo): IrPom = { diff --git a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill index 250cbc075e2..82875313c59 100644 --- a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill @@ -26,6 +26,9 @@ object Deps { ivy"com.typesafe.scala-logging::scala-logging:3.7.2" val `com.typesafe:config` = ivy"com.typesafe:config:1.3.1" + val `io.netty:netty-transport-native-epoll` = + ivy"io.netty:netty-transport-native-epoll:4.1.118.Final;type=pom;classifier=linux-x86_64;exclude=io.netty:netty-transport-native-epoll" + val `net.logstash.logback:logstash-logback-encoder` = ivy"net.logstash.logback:logstash-logback-encoder:4.11" val `org.scalacheck:scalacheck` = ivy"org.scalacheck::scalacheck:1.13.5" @@ -184,6 +187,9 @@ object `package` extends RootModule with BaseModule { object nested extends BaseModule { + def ivyDeps = super.ivyDeps() ++ + Seq(Deps.`io.netty:netty-transport-native-epoll`) + def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", diff --git a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill index 53453cefe08..bdae91e9065 100644 --- a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill @@ -26,6 +26,9 @@ object Deps { ivy"com.typesafe.scala-logging::scala-logging:3.7.2" val `com.typesafe:config` = ivy"com.typesafe:config:1.3.1" + val `io.netty:netty-transport-native-epoll` = + ivy"io.netty:netty-transport-native-epoll:4.1.118.Final;type=pom;classifier=linux-x86_64;exclude=io.netty:netty-transport-native-epoll" + val `net.logstash.logback:logstash-logback-encoder` = ivy"net.logstash.logback:logstash-logback-encoder:4.11" val `org.scalacheck:scalacheck` = ivy"org.scalacheck::scalacheck:1.13.5" @@ -138,7 +141,12 @@ object `package` extends RootModule with BaseModule { object nested extends Module { - object nested extends BaseModule {} + object nested extends BaseModule { + + def ivyDeps = super.ivyDeps() ++ + Seq(Deps.`io.netty:netty-transport-native-epoll`) + + } } } diff --git a/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill index e4c9672c52f..2459deec0e2 100644 --- a/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill +++ b/main/init/sbt/test/resources/expected/sbt-multi-project-example/nested/nested/package.mill @@ -32,6 +32,10 @@ object `package` extends RootModule with SbtModule with PublishModule { ) } + def ivyDeps = super.ivyDeps() ++ Seq( + ivy"io.netty:netty-transport-native-epoll:4.1.118.Final;type=pom;classifier=linux-x86_64;exclude=io.netty:netty-transport-native-epoll" + ) + def pomSettings = PomSettings( "This is an sbt sample project for testing Mill's init command.", "com.pbassiner", diff --git a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt index a97b9cff956..b9fe3451faa 100644 --- a/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt +++ b/main/init/sbt/test/resources/sbt-multi-project-example/build.sbt @@ -66,6 +66,11 @@ lazy val multi2 = project lazy val nested = project .in(file("nested/nested")) + .settings( + libraryDependencies += ("io.netty" % "netty-transport-native-epoll" % "4.1.118.Final") + .artifacts(Artifact("netty-transport-native-epoll").withType("pom").withClassifier(Some("linux-x86_64"))) + .exclude("io.netty", "netty-transport-native-epoll") + ) // DEPENDENCIES From 12e9d734c11f59cf673eaf5eef0a482e55efcb82 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 25 Feb 2025 11:16:10 +0800 Subject: [PATCH 63/70] Run more `compile` and `test` tasks in the submodules of all the converted builds in the integration tests This completes commit 88c31b18e667274563ca953ef7442d877ef2a129. Add `millInitExportBuild / aggregate := false` in `ExportBuildPlugin` to prevent the `millInitExportBuild` sbt task run in every child project, which seems to have sped up the tests. --- .../feature/init/src/MillInitSbtTests.scala | 78 +++++++++++++++---- .../mill/main/sbt/ExportBuildPlugin.scala | 3 +- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 059dca51e98..9e1ab62b175 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -4,12 +4,14 @@ import mill.constants.Util import mill.integration.testMillInit import utest.* -def bumpSbtTo1107(workspacePath: os.Path) = +private def bumpSbtTo1107(workspacePath: os.Path) = // bump sbt version to resolve compatibility issues with lower sbt versions and higher JDK versions os.write.over(workspacePath / "project" / "build.properties", "sbt.version = 1.10.7") // relatively small libraries +private val scalaPlatforms = Seq("js", "jvm", "native") + object MillInitSbtLibraryExampleTests extends BuildGenTestSuite { def tests: Tests = Tests { /* @@ -160,8 +162,7 @@ object MillInitSbtZioHttpTests extends BuildGenTestSuite { ) } - // probably due to inheriting `MavenTests` instead of `SbtTests` - // Some dependencies with currently unsupported `CrossVersion` `For3Use2_13` are not imported properly + // The sources in "zio-http/shared" in cross-builds are not supported in conversion yet, causing all dependent project's `compile` tasks to file. tester.testMillInit( expectedCompileTasks = Some(SplitResolvedTasks( @@ -191,15 +192,27 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { if (!Util.isWindows) os.call(("chmod", "+x", "sbt"), cwd = workspacePath) - val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) - - val compileResult = eval("compile") - assert(compileResult.isSuccess) + val crossDirs = Seq("core", "effect", "example", "iteratee", "scalacheck-binding", "tests") + val crossSubmodules = crossDirs.flatMap(dir => scalaPlatforms.map(name => s"$dir.$name")) + val rootModules = Seq("rootJS", "rootJVM", "rootNative") - // TODO - val resolveCompilesResult = eval(("resolve", "__.compile")) - println("Resolve result: " + resolveCompilesResult.out) + tester.testMillInit( + expectedCompileTasks = + Some(SplitResolvedTasks( + /* + These modules are converted from sbt's aggregated projects without sources or dependencies, + therefore, their no-op `compile` tasks succeed. + */ + Seq("compile") ++ rootModules.map(_.compileTask), + /* + Common sources in directories such as "core/src" are defined as projects in and therefore not converted, + therefore, the tasks in modules such as "core/jvm" as common definitions are not found. + */ + crossSubmodules.map(_.compileTask) + )), + // Scalaz uses ScalaCheck which is not supported in conversion yet. + expectedTestTasks = None + ) } } } @@ -215,13 +228,44 @@ object MillInitSbtCatsTests extends BuildGenTestSuite { val url = "https://github.com/typelevel/cats/archive/refs/tags/v2.13.0.zip" test - integrationTest(url) { tester => - import tester.* - - val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) + val sbtCrossProjects = Seq( + "algebra-laws", + "alleycats-laws", + "kernel-laws", + "tests" + ) + /* + These sbt cross projects have `CrossType.Pure` set, + so there platform projects have directories named ".js", ".jvm", and ".native", + and such modules starting with "." are not properly recognized by Mill + */ + val sbtCrossProjectsWithCrossTypePure = + Seq("algebra-core", "alleycats-core", "core", "free", "kernel", "laws", "testkit") + + assert((sbtCrossProjects intersect sbtCrossProjectsWithCrossTypePure).isEmpty) + + val nonCrossModules = Seq("bench", "binCompatTest", "site", "unidocs") + val resolvedTestModules = (scalaPlatforms.map(name => s"alleycats-laws.$name") ++ + Seq("binCompatTest", "tests.js")) + .map(module => s"$module.test") + val submoduleCompileTasks = (sbtCrossProjects.flatMap(project => + scalaPlatforms.map(platform => s"$project.$platform") + ) ++ + nonCrossModules ++ + resolvedTestModules) + .map(module => s"$module.compile") - val compileResult = eval("compile") - assert(compileResult.isSuccess) + tester.testMillInit( + expectedCompileTasks = Some({ + val succeededSubmoduleCompileTasks = + Seq("tests.js.compile", "tests.jvm.compile", "unidocs.compile") + SplitResolvedTasks( + Seq("compile") ++ succeededSubmoduleCompileTasks, + submoduleCompileTasks diff succeededSubmoduleCompileTasks + ) + }), + expectedTestTasks = Some(SplitResolvedTasks(Seq(), resolvedTestModules)) + ) } } } diff --git a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala index 270fcebcefa..fdc4b56ec31 100644 --- a/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala +++ b/main/init/sbt/sbt-mill-init-export-build/src/main/scala/mill/main/sbt/ExportBuildPlugin.scala @@ -144,6 +144,7 @@ object ExportBuildPlugin extends AutoPlugin { val outputFile = target.value / "mill-init-build-export.json" IO.write(outputFile, write(buildExport)) outputFile - } + }, + millInitExportBuild / aggregate := false ) } From 2be0d803fbeec14ef38f638babb05e9a7a1f350b Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 25 Feb 2025 23:38:35 +0800 Subject: [PATCH 64/70] Reformat "main/init/package.mill" as `lint-autofix` fails on CI --- main/init/package.mill | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/init/package.mill b/main/init/package.mill index 65b11006c4d..c895eda7b10 100644 --- a/main/init/package.mill +++ b/main/init/package.mill @@ -125,7 +125,7 @@ object `package` extends RootModule with build.MillPublishScalaModule { "Check if the project builds correctly and if you have sbt available on your system, and install it if you don't.", e ) - case t : Throwable => throw t + case t: Throwable => throw t } os.copy( From 44c7381189454a35ed83040c8dc83e03edaa7297 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 01:56:21 +0800 Subject: [PATCH 65/70] Add a comment --- integration/feature/init/src/MillInitSbtTests.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 9e1ab62b175..64646b2d683 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -246,7 +246,11 @@ object MillInitSbtCatsTests extends BuildGenTestSuite { val nonCrossModules = Seq("bench", "binCompatTest", "site", "unidocs") val resolvedTestModules = (scalaPlatforms.map(name => s"alleycats-laws.$name") ++ - Seq("binCompatTest", "tests.js")) + Seq( + "binCompatTest", + // "tests.jvm" and "tests.native" don't have their own test sources, so their test modules are not converted. + "tests.js" + )) .map(module => s"$module.test") val submoduleCompileTasks = (sbtCrossProjects.flatMap(project => scalaPlatforms.map(platform => s"$project.$platform") From 8ca434a656e42ff3cfaaf50fd66b328d266a99ce Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 03:00:22 +0800 Subject: [PATCH 66/70] Review the reasons why the tasks succeed or fail and improve comments in `MillInitSbtTests.scala` --- integration/feature/init/src/MillInitSbtTests.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 64646b2d683..88f5bbea36d 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -68,7 +68,7 @@ object MillInitSbtScalaCsv136Tests extends BuildGenTestSuite { expectedTestTasks = Some(SplitResolvedTasks( Seq(), /* - Relative paths to the workspace are used in the test sources such as `new File("src/test/resources/simple.csv")` + Paths relative to the workspace are used in the test sources such as `new File("src/test/resources/simple.csv")` and they seem to cause the test to fail with Mill: ```text java.io.FileNotFoundException: src/test/resources/simple.csv (No such file or directory) @@ -162,7 +162,11 @@ object MillInitSbtZioHttpTests extends BuildGenTestSuite { ) } - // The sources in "zio-http/shared" in cross-builds are not supported in conversion yet, causing all dependent project's `compile` tasks to file. + /* + The sources shared among multiple platforms (JVM and Scala.js) in "zio-http/shared" in cross-builds + are not supported in conversion yet, + causing all dependent project's `compile` tasks to fail. + */ tester.testMillInit( expectedCompileTasks = Some(SplitResolvedTasks( @@ -205,8 +209,9 @@ object MillInitSbtScalazTests extends BuildGenTestSuite { */ Seq("compile") ++ rootModules.map(_.compileTask), /* - Common sources in directories such as "core/src" are defined as projects in and therefore not converted, - therefore, the tasks in modules such as "core/jvm" as common definitions are not found. + Common sources shared among multiple platforms (JVM, Scala.js, and Scala Native) in directories such as "core/src" + are not defined as sbt projects and therefore not converted. + This leads to modules such as "core/jvm" not compiling as common definitions are not found. */ crossSubmodules.map(_.compileTask) )), From 75af776d2eaddd6b13a21aafe38a28ae7ba457f0 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 03:22:26 +0800 Subject: [PATCH 67/70] Run more `compile` and `test` tasks in `MillInitSbtScalaCheckTests` --- .../feature/init/src/MillInitSbtTests.scala | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/integration/feature/init/src/MillInitSbtTests.scala b/integration/feature/init/src/MillInitSbtTests.scala index 88f5bbea36d..b6ec4fe4cdb 100644 --- a/integration/feature/init/src/MillInitSbtTests.scala +++ b/integration/feature/init/src/MillInitSbtTests.scala @@ -313,15 +313,20 @@ object MillInitSbtScalaCheckTests extends BuildGenTestSuite { val url = "https://github.com/typelevel/scalacheck/archive/refs/tags/v1.18.1.zip" test - integrationTest(url) { tester => - import tester.* - - bumpSbtTo1107(workspacePath) - - val initResult = eval(defaultInitCommand, stdout = os.Inherit, stderr = os.Inherit) - assert(initResult.isSuccess) + bumpSbtTo1107(tester.workspacePath) - val compileResult = eval("compile") - assert(compileResult.isSuccess) + /* + The sources shared among multiple platforms (JVM, Scala.js, and Scala Native) in "core/shared" in cross-builds + are not supported in conversion yet, + causing all dependent project's `compile` tasks to fail. + */ + val submodules = scalaPlatforms.map(platform => s"core.$platform") :+ "bench" + tester.testMillInit( + expectedCompileTasks = + Some(SplitResolvedTasks(Seq("compile"), submodules.map(_.compileTask))), + // ScalaCheck itself as a test framework is not supported in conversion yet. + expectedTestTasks = None + ) } } } From c2256f8af08b369e8aed15299fd0425e0a0f4ec3 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 03:51:26 +0800 Subject: [PATCH 68/70] Reformat --- .../gradle/src/mill/main/gradle/GradleBuildGenMain.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index 1977da79437..bc83b1def07 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -198,7 +198,11 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java def getMillSourcePath(project: ProjectModel): Path = os.Path(project.directory()) - override def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[ProjectModel]): Seq[String] = + override def getSupertypes( + cfg: Config, + baseInfo: IrBaseInfo, + build: Node[ProjectModel] + ): Seq[String] = Seq("RootModule") ++ Option.when(null != build.value.maven().pom() && { val baseTrait = baseInfo.moduleTypedef From 10d2076e64ab0661645526666c72eb29b44a681c Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 04:03:17 +0800 Subject: [PATCH 69/70] Add some `override`s following commit 57ab8e6218f5fab869e238037edfc8d6cc94fd59 whose changes were just merged --- .../src/mill/main/buildgen/BuildGenBase.scala | 2 ++ .../src/mill/main/gradle/GradleBuildGenMain.scala | 4 +--- .../src/mill/main/maven/MavenBuildGenMain.scala | 12 +++++------- .../init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala index 0cfd344c391..f365df07f5a 100644 --- a/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala +++ b/main/init/buildgen/src/mill/main/buildgen/BuildGenBase.scala @@ -97,5 +97,7 @@ object BuildGenBase { trait MavenAndGradle[M, D] extends BuildGenBase[M, D, Tree[Node[M]]] { override def getModuleTree(input: Tree[Node[M]]): Tree[Node[M]] = input override type OM = M + + override def extraImports: Seq[String] = Seq.empty } } diff --git a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala index bc83b1def07..123a2dcaacf 100644 --- a/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala +++ b/main/init/gradle/src/mill/main/gradle/GradleBuildGenMain.scala @@ -41,7 +41,7 @@ import scala.jdk.CollectionConverters.* */ @mill.api.internal object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, JavaModel.Dep] { - type C = Config + override type C = Config def main(args: Array[String]): Unit = { val cfg = ParserForClass[Config].constructOrExit(args.toSeq) @@ -185,8 +185,6 @@ object GradleBuildGenMain extends BuildGenBase.MavenAndGradle[ProjectModel, Java ) } - override def extraImports: Seq[String] = Seq.empty - def getModuleSupertypes(cfg: Config): Seq[String] = Seq(cfg.shared.basicConfig.baseModule.getOrElse("MavenModule")) diff --git a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala index e5ceb32c4bf..fcdab872d4c 100644 --- a/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala +++ b/main/init/maven/src/mill/main/maven/MavenBuildGenMain.scala @@ -37,7 +37,7 @@ import scala.jdk.CollectionConverters.* */ @mill.api.internal object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] { - type C = Config + override type C = Config def main(args: Array[String]): Unit = { val cfg = ParserForClass[Config].constructOrExit(args.toSeq) @@ -61,7 +61,7 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] extension (om: Model) override def toOption(): Option[Model] = Some(om) - def getBaseInfo( + override def getBaseInfo( input: Tree[Node[Model]], cfg: Config, baseModule: String, @@ -126,19 +126,17 @@ object MavenBuildGenMain extends BuildGenBase.MavenAndGradle[Model, Dependency] ) } - override def extraImports: Seq[String] = Seq.empty - def getModuleSupertypes(cfg: Config): Seq[String] = Seq("PublishModule", "MavenModule") - def getPackage(model: Model): (String, String, String) = { + override def getPackage(model: Model): (String, String, String) = { (model.getGroupId, model.getArtifactId, model.getVersion) } - def getArtifactId(model: Model): String = model.getArtifactId + override def getArtifactId(model: Model): String = model.getArtifactId def getMillSourcePath(model: Model): Path = os.Path(model.getProjectDirectory) - def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = + override def getSupertypes(cfg: Config, baseInfo: IrBaseInfo, build: Node[Model]): Seq[String] = Seq("RootModule") ++ cfg.shared.basicConfig.baseModule.fold(getModuleSupertypes(cfg))(Seq(_)) diff --git a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala index ed811d6be62..409b4795410 100644 --- a/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala +++ b/main/init/sbt/src/mill/main/sbt/SbtBuildGenMain.scala @@ -52,7 +52,7 @@ import scala.collection.immutable.SortedSet @mill.api.internal object SbtBuildGenMain extends BuildGenBase[Project, String, (BuildInfo, Tree[Node[Option[Project]]])] { - type C = Config + override type C = Config override type OM = Option[Project] def main(args: Array[String]): Unit = { @@ -198,7 +198,7 @@ object SbtBuildGenMain private def sbtSupertypes = Seq("SbtModule", "PublishModule") // always publish - def getBaseInfo( + override def getBaseInfo( input: (BuildInfo, Tree[Node[Option[Project]]]), cfg: Config, baseModule: String, From d0d1e39f836b2199a1034e9a3d1b71786ea71d91 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 26 Feb 2025 04:15:10 +0800 Subject: [PATCH 70/70] Update the expected test snapshots and fix the unit tests broken by the merge commit 6cac44b60dd2e01e9eb26748018d8a30ce895b5e --- .../expected/config/all/sbt-multi-project-example/build.mill | 1 + .../without-base-project/sbt-multi-project-example/build.mill | 1 + 2 files changed, 2 insertions(+) diff --git a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill index 82875313c59..b514dff1fbd 100644 --- a/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/all/sbt-multi-project-example/build.mill @@ -1,5 +1,6 @@ package build +import _root_.build_.BaseModule import mill._ import mill.javalib._ import mill.javalib.publish._ diff --git a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill index bdae91e9065..74948bdee64 100644 --- a/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill +++ b/main/init/sbt/test/resources/expected/config/without-base-project/sbt-multi-project-example/build.mill @@ -1,5 +1,6 @@ package build +import _root_.build_.BaseModule import mill._ import mill.javalib._ import mill.javalib.publish._