Skip to content

Commit aa73d4e

Browse files
committed
.
1 parent 9aabca3 commit aa73d4e

File tree

2 files changed

+149
-89
lines changed

2 files changed

+149
-89
lines changed

example/migrating/javalib/2-maven-incomplete/build.mill

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88

99
> ./mill init
1010
converting Maven build
11-
writing fastexcel-writer/package.mill
12-
writing fastexcel-reader/package.mill
13-
writing e2e/package.mill
14-
writing build.mill
11+
...
1512
init completed, run "mill resolve _" to list available tasks
1613

1714
> ./mill -k __.compile # Compilation needs manual tweaking to pass

libs/init/buildgen/src/mill/main/buildgen/BuildGen.scala

Lines changed: 148 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ object BuildGen {
6868
testParallelism = parentValue(a.testParallelism, b.testParallelism),
6969
testSandboxWorkingDir = parentValue(a.testSandboxWorkingDir, b.testSandboxWorkingDir)
7070
)
71+
// For test modules, don't extract common mvnDeps because YAML can't append to parent deps
72+
def parentTestModule(a: ModuleSpec, b: ModuleSpec) = parentModule0(a, b, Seq(testSupertype)).copy(
73+
mandatoryMvnDeps = Values(),
74+
mvnDeps = Values(),
75+
compileMvnDeps = Values(),
76+
runMvnDeps = Values()
77+
)
7178
def parentModule(a: ModuleSpec, b: ModuleSpec) =
7279
parentModule0(a, b, moduleHierarchy.take(1)).copy(
73-
test = (a.test.toSeq ++ b.test.toSeq).reduceOption(parentModule0(_, _, Seq(testSupertype)))
80+
test = (a.test.toSeq ++ b.test.toSeq).reduceOption(parentTestModule)
7481
)
7582
def extendValue[A](a: Value[A], parent: Value[A]) = a.copy(
7683
if (a.base == parent.base) None else a.base,
@@ -86,7 +93,15 @@ object BuildGen {
8693
a.appendSuper || parent.base.nonEmpty || parent.cross.nonEmpty
8794
)
8895
def extendModule0(a: ModuleSpec, parent: ModuleSpec): ModuleSpec = a.copy(
89-
supertypes = (parent.name +: a.supertypes).diff(parent.supertypes),
96+
// Don't subtract nested test traits (MavenTests, SbtTests, etc.) - they can't be inherited
97+
// through a standalone trait and must remain in the child's extends clause.
98+
// Put nested traits first for proper linearization (MavenTests before ProjectBaseModuleTests)
99+
supertypes = {
100+
val filtered = (a.supertypes :+ parent.name).diff(parent.supertypes.filterNot(nestedTestTraits.contains))
101+
// Reorder: nested test traits first, then others
102+
val (nested, others) = filtered.partition(nestedTestTraits.contains)
103+
nested ++ others
104+
},
90105
mixins = if (a.mixins == parent.mixins) Nil else a.mixins,
91106
repositories = extendValues(a.repositories, parent.repositories),
92107
forkArgs = extendValues(a.forkArgs, parent.forkArgs),
@@ -134,11 +149,17 @@ object BuildGen {
134149
Option.when(childModules.length > 1) {
135150
val baseModule = childModules.reduce(parentModule)
136151
val baseModule0 = baseModule.copy(
137-
name = "ProjectBaseModule",
138-
test = baseModule.test.map(_.copy(name = "Tests"))
152+
name = "millbuild.ProjectBaseModule",
153+
// Use a top-level Tests trait instead of nested one for YAML compatibility
154+
test = baseModule.test.map(_.copy(name = "millbuild.ProjectBaseModuleTests"))
139155
)
140156
val packages0 = packages.map(pkg => pkg.copy(module = extendModule(pkg.module, baseModule0)))
141-
(baseModule0, packages0)
157+
// Return the base module with simple name for file generation
158+
val baseModuleForFile = baseModule0.copy(
159+
name = "ProjectBaseModule",
160+
test = baseModule0.test.map(_.copy(name = "ProjectBaseModuleTests"))
161+
)
162+
(baseModuleForFile, packages0)
142163
}
143164
}
144165

@@ -185,6 +206,17 @@ object BuildGen {
185206
val file = os.sub / os.SubPath(s"mill-build/src/${module.name}.scala")
186207
println(s"writing $file")
187208
os.write(os.pwd / file, renderBaseModule(module), createFolders = true)
209+
// Also write a build.mill with "See Also" directive to trigger Mill to include mill-build/src/
210+
val buildMillFile = os.pwd / "build.mill"
211+
if (!os.exists(buildMillFile)) {
212+
println("writing build.mill")
213+
os.write(
214+
buildMillFile,
215+
s"""|/** See Also: build.mill.yaml */
216+
|/** See Also: mill-build/src/${module.name}.scala */
217+
|""".stripMargin
218+
)
219+
}
188220
}
189221

190222
val rootPackage +: nestedPackages = packages0: @unchecked
@@ -195,13 +227,13 @@ object BuildGen {
195227
os.pwd / "build.mill.yaml",
196228
s"""mill-version: SNAPSHOT
197229
|mill-jvm-version: $millJvmVersion
198-
|$millJvmOptsLine${renderYamlPackage(rootPackage)}
230+
|$millJvmOptsLine${renderYamlPackage(rootPackage, isRootBuild = true)}
199231
|""".stripMargin
200232
)
201233
for (pkg <- nestedPackages) do {
202234
val file = os.sub / pkg.dir / "package.mill.yaml"
203235
println(s"writing $file")
204-
os.write(os.pwd / file, renderYamlPackage(pkg))
236+
os.write(os.pwd / file, renderYamlPackage(pkg, isRootBuild = false))
205237
}
206238
}
207239

@@ -234,16 +266,29 @@ object BuildGen {
234266
// Scala rendering for ProjectBaseModule.scala
235267
// ============================================
236268

269+
// These are nested traits that can't be extended by standalone traits
270+
private val nestedTestTraits = Set("MavenTests", "SbtTests", "JavaTests", "ScalaTests")
271+
237272
private def renderBaseModule(module: ModuleSpec) = {
238273
import module.*
239-
s"""package millbuild
240-
|${renderScalaImports(module)}
241-
|trait $name ${renderScalaExtendsClause(supertypes ++ mixins)} {
274+
val mainTrait = s"""trait $name ${renderScalaExtendsClause(supertypes ++ mixins)} {
242275
|
243276
| ${renderScalaModuleBody(module)}
244-
|
245-
| ${test.fold("")(renderScalaTestModule("trait", _))}
246277
|}""".stripMargin
278+
// Generate Tests as a top-level trait for YAML compatibility
279+
// Filter out nested test traits (like MavenTests) that can't be extended by standalone traits
280+
val testTrait = test.fold("") { testSpec =>
281+
val standaloneSupertypes = testSpec.supertypes.filterNot(nestedTestTraits.contains)
282+
s"""
283+
|
284+
|trait ${testSpec.name} ${renderScalaExtendsClause(standaloneSupertypes ++ testSpec.mixins)} {
285+
|
286+
| ${renderScalaModuleBody(testSpec)}
287+
|}""".stripMargin
288+
}
289+
s"""package millbuild
290+
|${renderScalaImports(module)}
291+
|$mainTrait$testTrait""".stripMargin
247292
}
248293

249294
private def renderScalaImports(module: ModuleSpec) = {
@@ -370,7 +415,7 @@ object BuildGen {
370415
collection: String = "Seq"
371416
) = {
372417
val defType = if (isTask) "def" else "override def"
373-
val appender = if (values.appendSuper) s" ++ super.$name" else ""
418+
val appender = if (values.appendSuper) s" ++ super.$name()" else ""
374419
val cross = values.cross.map { (key, values) =>
375420
s"""case "$key" => $collection(${values.map(encode).mkString(", ")})$appender"""
376421
}.mkString(lineSeparator)
@@ -431,7 +476,7 @@ object BuildGen {
431476

432477
private def encodeScalaString(s: String) = literalize(s)
433478

434-
private def encodeScalaOpt(opt: Opt) = opt.group.map(s => literalize(s)).mkString("Seq(", ", ", ")")
479+
private def encodeScalaOpt(opt: Opt) = opt.group.map(s => literalize(s)).mkString(", ")
435480

436481
private def encodeScalaLiteralOpt(opt: Opt) = opt.group.mkString("Seq(", ", ", ")")
437482

@@ -493,87 +538,105 @@ object BuildGen {
493538
// YAML rendering for build.mill.yaml and package.mill.yaml
494539
// ============================================
495540

496-
private def renderYamlPackage(pkg: PackageSpec): String = {
497-
renderYamlModule(pkg.module, indent = 0, isRoot = true)
541+
private def renderYamlPackage(pkg: PackageSpec, isRootBuild: Boolean): String = {
542+
renderYamlModule(pkg.module, indent = 0, isRoot = isRootBuild)
498543
}
499544

500545
private def renderYamlModule(module: ModuleSpec, indent: Int, isRoot: Boolean): String = {
501546
val sb = new StringBuilder
502547
val prefix = " " * indent
503548

504-
// Render extends
505-
val allSupertypes = module.supertypes ++ module.mixins
506-
if (allSupertypes.nonEmpty) {
507-
if (allSupertypes.size == 1) {
508-
sb ++= s"${prefix}extends: ${allSupertypes.head}$lineSeparator"
509-
} else {
510-
sb ++= s"${prefix}extends: [${allSupertypes.mkString(", ")}]$lineSeparator"
549+
// Render extends (not for root module - root modules don't extend regular module traits)
550+
if (!isRoot) {
551+
val allSupertypes = module.supertypes ++ module.mixins
552+
if (allSupertypes.nonEmpty) {
553+
if (allSupertypes.size == 1) {
554+
sb ++= s"${prefix}extends: ${allSupertypes.head}$lineSeparator"
555+
} else {
556+
sb ++= s"${prefix}extends: [${allSupertypes.mkString(", ")}]$lineSeparator"
557+
}
511558
}
512559
}
513560

514-
// Render module deps
515-
renderYamlModuleDeps(sb, prefix, "moduleDeps", module.moduleDeps)
516-
renderYamlModuleDeps(sb, prefix, "compileModuleDeps", module.compileModuleDeps)
517-
renderYamlModuleDeps(sb, prefix, "runModuleDeps", module.runModuleDeps)
518-
renderYamlModuleDeps(sb, prefix, "bomModuleDeps", module.bomModuleDeps)
519-
520-
// Render maven deps
521-
renderYamlMvnDeps(
522-
sb,
523-
prefix,
524-
"mvnDeps",
525-
combineMvnDeps(module.mandatoryMvnDeps, module.mvnDeps)
526-
)
527-
renderYamlMvnDeps(sb, prefix, "compileMvnDeps", module.compileMvnDeps)
528-
renderYamlMvnDeps(sb, prefix, "runMvnDeps", module.runMvnDeps)
529-
renderYamlMvnDeps(sb, prefix, "bomMvnDeps", module.bomMvnDeps)
530-
renderYamlMvnDeps(sb, prefix, "depManagement", module.depManagement)
531-
renderYamlMvnDeps(sb, prefix, "scalacPluginMvnDeps", module.scalacPluginMvnDeps)
532-
533-
// Render scala/java configuration
534-
renderYamlValue(sb, prefix, "scalaVersion", module.scalaVersion)
535-
renderYamlValues(sb, prefix, "scalacOptions", module.scalacOptions, encodeYamlOpt)
536-
renderYamlValues(sb, prefix, "javacOptions", module.javacOptions, encodeYamlOpt)
537-
renderYamlValue(sb, prefix, "scalaJSVersion", module.scalaJSVersion)
538-
renderYamlValue(sb, prefix, "moduleKind", module.moduleKind)
539-
renderYamlValue(sb, prefix, "scalaNativeVersion", module.scalaNativeVersion)
540-
541-
// Render sources and resources
542-
renderYamlPaths(sb, prefix, "sources", module.sources)
543-
renderYamlPaths(sb, prefix, "resources", module.resources)
544-
renderYamlSubPaths(sb, prefix, "sourcesRootFolders", module.sourcesRootFolders)
545-
renderYamlSubPaths(sb, prefix, "sourcesFolders", module.sourcesFolders)
546-
547-
// Render fork configuration
548-
renderYamlValues(sb, prefix, "forkArgs", module.forkArgs, encodeYamlOpt)
549-
renderYamlForkWorkingDir(sb, prefix, module.forkWorkingDir)
550-
551-
// Render repositories
552-
renderYamlStrings(sb, prefix, "repositories", module.repositories)
553-
554-
// Render error prone configuration
555-
renderYamlMvnDeps(sb, prefix, "errorProneDeps", module.errorProneDeps)
556-
renderYamlStrings(sb, prefix, "errorProneOptions", module.errorProneOptions)
557-
renderYamlValues(
558-
sb,
559-
prefix,
560-
"errorProneJavacEnableOptions",
561-
module.errorProneJavacEnableOptions,
562-
encodeYamlOpt
563-
)
561+
// Module-specific configuration (not for root module - it's not a JavaModule)
562+
if (!isRoot) {
563+
// Render module deps
564+
renderYamlModuleDeps(sb, prefix, "moduleDeps", module.moduleDeps)
565+
renderYamlModuleDeps(sb, prefix, "compileModuleDeps", module.compileModuleDeps)
566+
renderYamlModuleDeps(sb, prefix, "runModuleDeps", module.runModuleDeps)
567+
renderYamlModuleDeps(sb, prefix, "bomModuleDeps", module.bomModuleDeps)
568+
569+
// Render maven deps
570+
renderYamlMvnDeps(
571+
sb,
572+
prefix,
573+
"mvnDeps",
574+
combineMvnDeps(module.mandatoryMvnDeps, module.mvnDeps)
575+
)
576+
renderYamlMvnDeps(sb, prefix, "compileMvnDeps", module.compileMvnDeps)
577+
renderYamlMvnDeps(sb, prefix, "runMvnDeps", module.runMvnDeps)
578+
renderYamlMvnDeps(sb, prefix, "bomMvnDeps", module.bomMvnDeps)
579+
renderYamlMvnDeps(sb, prefix, "depManagement", module.depManagement)
580+
renderYamlMvnDeps(sb, prefix, "scalacPluginMvnDeps", module.scalacPluginMvnDeps)
581+
582+
// Render scala/java configuration
583+
renderYamlValue(sb, prefix, "scalaVersion", module.scalaVersion)
584+
renderYamlValues(sb, prefix, "scalacOptions", module.scalacOptions, encodeYamlOpt)
585+
renderYamlValues(sb, prefix, "javacOptions", module.javacOptions, encodeYamlOpt)
586+
renderYamlValue(sb, prefix, "scalaJSVersion", module.scalaJSVersion)
587+
renderYamlValue(sb, prefix, "moduleKind", module.moduleKind)
588+
renderYamlValue(sb, prefix, "scalaNativeVersion", module.scalaNativeVersion)
589+
590+
// Render sources and resources
591+
renderYamlPaths(sb, prefix, "sources", module.sources)
592+
renderYamlPaths(sb, prefix, "resources", module.resources)
593+
renderYamlSubPaths(sb, prefix, "sourcesRootFolders", module.sourcesRootFolders)
594+
renderYamlSubPaths(sb, prefix, "sourcesFolders", module.sourcesFolders)
595+
}
564596

565-
// Render publishing configuration
566-
renderYamlValue(sb, prefix, "artifactName", module.artifactName)
567-
renderYamlValue(sb, prefix, "pomPackagingType", module.pomPackagingType)
568-
renderYamlPomParentProject(sb, prefix, module.pomParentProject)
569-
renderYamlPomSettings(sb, prefix, module.pomSettings)
570-
renderYamlValue(sb, prefix, "publishVersion", module.publishVersion)
571-
renderYamlVersionScheme(sb, prefix, module.versionScheme)
572-
renderYamlPublishProperties(sb, prefix, module.publishProperties)
573-
574-
// Render test configuration
575-
renderYamlBoolValue(sb, prefix, "testParallelism", module.testParallelism)
576-
renderYamlBoolValue(sb, prefix, "testSandboxWorkingDir", module.testSandboxWorkingDir)
597+
// Render additional module-specific configuration (not for root module)
598+
if (!isRoot) {
599+
// Render fork configuration
600+
renderYamlValues(sb, prefix, "forkArgs", module.forkArgs, encodeYamlOpt)
601+
renderYamlForkWorkingDir(sb, prefix, module.forkWorkingDir)
602+
603+
// Render repositories
604+
renderYamlStrings(sb, prefix, "repositories", module.repositories)
605+
606+
// Render error prone configuration
607+
renderYamlMvnDeps(sb, prefix, "errorProneDeps", module.errorProneDeps)
608+
renderYamlStrings(sb, prefix, "errorProneOptions", module.errorProneOptions)
609+
renderYamlValues(
610+
sb,
611+
prefix,
612+
"errorProneJavacEnableOptions",
613+
module.errorProneJavacEnableOptions,
614+
encodeYamlOpt
615+
)
616+
617+
// Render publishing configuration only for modules that extend PublishModule
618+
val extendsPublishModule = module.supertypes.exists(s =>
619+
s.contains("PublishModule") || s.contains("ProjectBaseModule")
620+
)
621+
if (extendsPublishModule) {
622+
renderYamlValue(sb, prefix, "artifactName", module.artifactName)
623+
// Skip pomPackagingType for pom/war - these are aggregator/web modules in Maven
624+
// that don't need special packaging in Mill
625+
val skipPackagingType = module.pomPackagingType.base.exists(p => p == "pom" || p == "war")
626+
if (!skipPackagingType) {
627+
renderYamlValue(sb, prefix, "pomPackagingType", module.pomPackagingType)
628+
}
629+
renderYamlPomParentProject(sb, prefix, module.pomParentProject)
630+
renderYamlPomSettings(sb, prefix, module.pomSettings)
631+
renderYamlValue(sb, prefix, "publishVersion", module.publishVersion)
632+
renderYamlVersionScheme(sb, prefix, module.versionScheme)
633+
renderYamlPublishProperties(sb, prefix, module.publishProperties)
634+
}
635+
636+
// Render test configuration
637+
renderYamlBoolValue(sb, prefix, "testParallelism", module.testParallelism)
638+
renderYamlBoolValue(sb, prefix, "testSandboxWorkingDir", module.testSandboxWorkingDir)
639+
}
577640

578641
// Render test submodule
579642
module.test.foreach { testSpec =>

0 commit comments

Comments
 (0)