Skip to content

Commit c03fa73

Browse files
authored
Convert ./mill init to generate .mill.yaml format, add !append support (#6441)
* We generate `.mill.yaml` files when importing Java projects using `./mill init`. * We still generate Scala code in `mill-build/src/`, which lets us avoid duplication in the `.mill.yaml` files since the yaml files can `extend` the shared `trait` containing the common config * Scala project import makes use of cross values which are currently not supported in `.mill.yaml` so we leave those as generating `.mill` files for now. * You can now tag top-level YAML values with `!append`, which concatenates the Seq the overridden value rather than completely replacing it. This is necessary for `.mill.yaml` Java project import to work, as we make use of `super ++` in some places. This is implemented via `parseYaml0` converting the `!append` tags into a synthetic `{"$millAppend": ...}` wrapper, which is then deserialized into an `Appendable[T]` that lets us decide later on whether to append something or not The `!append` stuff is mentioned in some updated doc examples which demonstrate and exercise it
1 parent 6e96aa8 commit c03fa73

File tree

99 files changed

+2188
-3338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+2188
-3338
lines changed

.scalafix.conf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@ rules = [
22
RemoveUnused,
33
NoAutoTupling
44
]
5-
OrganizeImports.targetDialect = Scala3
5+
OrganizeImports.targetDialect = Scala3
6+
project {
7+
excludePaths = [
8+
"glob:**/out/**",
9+
]
10+
}

core/api/src/mill/api/Evaluator.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ trait Evaluator extends AutoCloseable with EvaluatorApi {
3333
private[mill] def effectiveThreadCount: Int
3434
private[mill] def offline: Boolean
3535
private[mill] def useFileLocks: Boolean = false
36-
private[mill] def staticBuildOverrides: Map[String, Located[BufferedValue]] = Map()
36+
private[mill] def staticBuildOverrides: Map[String, Located[internal.Appendable[BufferedValue]]] =
37+
Map()
3738
def withBaseLogger(newBaseLogger: Logger): Evaluator
3839

3940
def resolveSegments(

core/api/src/mill/api/Module.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ trait Module extends Module.BaseClass with ModuleCtx.Wrapper with ModuleApi {
4545
private[mill] val moduleLinearized: Seq[Class[?]] =
4646
OverrideMapping.computeLinearization(this.getClass)
4747

48-
private[mill] def moduleDynamicBuildOverrides: Map[String, internal.Located[BufferedValue]] =
48+
private[mill] def moduleDynamicBuildOverrides
49+
: Map[String, internal.Located[internal.Appendable[BufferedValue]]] =
4950
Map()
5051
}
5152

core/api/src/mill/api/ModuleCtx.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ object ModuleCtx extends LowPriCtx {
4646
def moduleSegments: Segments = moduleCtx.segments
4747
def moduleCtx: ModuleCtx
4848
private[mill] def moduleLinearized: Seq[Class[?]]
49-
private[mill] def moduleDynamicBuildOverrides: Map[String, internal.Located[BufferedValue]] =
49+
private[mill] def moduleDynamicBuildOverrides
50+
: Map[String, internal.Located[internal.Appendable[BufferedValue]]] =
5051
Map()
5152
}
5253

core/api/src/mill/api/ScriptModule.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ trait ScriptModule extends ExternalModule {
2525
.rest
2626
.map { case (k, v) =>
2727
val newKey = (moduleSegments ++ mill.api.Segment.Label(k.value)).render
28-
(newKey, internal.Located(scriptConfig.scriptFile, k.index, v))
28+
val (actualValue, append) = internal.Appendable.unwrapAppendMarker(v)
29+
(
30+
newKey,
31+
internal.Located(scriptConfig.scriptFile, k.index, internal.Appendable(actualValue, append))
32+
)
2933
}
3034
}
3135
@experimental

core/api/src/mill/api/internal/HeaderData.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ package mill.api.internal
22

33
private[mill] case class HeaderData(
44
`extends`: Located[OneOrMore[Located[String]]] = Located(null, -1, OneOrMore(Nil)),
5-
moduleDeps: Located[Seq[Located[String]]] = Located(null, -1, Nil),
6-
compileModuleDeps: Located[Seq[Located[String]]] = Located(null, -1, Nil),
7-
runModuleDeps: Located[Seq[Located[String]]] = Located(null, -1, Nil),
5+
moduleDeps: Located[Appendable[Seq[Located[String]]]] =
6+
Located(null, -1, Appendable(Nil)),
7+
compileModuleDeps: Located[Appendable[Seq[Located[String]]]] =
8+
Located(null, -1, Appendable(Nil)),
9+
runModuleDeps: Located[Appendable[Seq[Located[String]]]] =
10+
Located(null, -1, Appendable(Nil)),
11+
bomModuleDeps: Located[Appendable[Seq[Located[String]]]] =
12+
Located(null, -1, Appendable(Nil)),
813
@upickle.implicits.flatten rest: Map[Located[String], upickle.core.BufferedValue]
914
)
1015
private[mill] object HeaderData {
@@ -17,6 +22,8 @@ private[mill] object HeaderData {
1722
def headerDataReader(path: os.Path) = {
1823
implicit def locatedReader[T: upickle.Reader]: Located.UpickleReader[T] =
1924
new Located.UpickleReader[T](path)
25+
implicit def appendableReader[T: upickle.Reader]: Appendable.UpickleReader[T] =
26+
new Appendable.UpickleReader[T]
2027
upickle.macroR[HeaderData]
2128
}
2229
}

core/api/src/mill/api/internal/Located.scala

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,56 @@
11
package mill.api.internal
22

3+
/** Tracks the source location (file path and character index) of a parsed value */
34
case class Located[T](path: os.Path, index: Int, value: T)
45

6+
/** Wraps a value with append semantics (whether to replace or append to existing value) */
7+
case class Appendable[T](value: T, append: Boolean = false)
8+
9+
object Appendable {
10+
import upickle.core.BufferedValue
11+
12+
/** Marker key for append mode: `{$millAppend: <actual-value>}` */
13+
val AppendMarkerKey = "$millAppend"
14+
15+
/**
16+
* Extract append flag and actual value from a BufferedValue that may contain
17+
* the marker object `{$millAppend: <value>}` (produced by YAML `!append` tag parsing).
18+
* Returns (actualValue, appendFlag).
19+
*/
20+
def unwrapAppendMarker(v: BufferedValue): (BufferedValue, Boolean) = {
21+
v match {
22+
case obj: BufferedValue.Obj if obj.value0.size == 1 =>
23+
obj.value0.head match {
24+
case (BufferedValue.Str(k, _), value) if k.toString == AppendMarkerKey =>
25+
(value, true)
26+
case _ => (v, false)
27+
}
28+
case _ => (v, false)
29+
}
30+
}
31+
32+
/**
33+
* Upickle reader for Appendable[T] that buffers the value, checks for the
34+
* `!append` marker object, and deserializes accordingly.
35+
*/
36+
class UpickleReader[T](implicit r: upickle.Reader[T])
37+
extends upickle.Reader.MapReader[BufferedValue, BufferedValue, Appendable[T]](
38+
BufferedValue.Builder
39+
) {
40+
def mapNonNullsFunction(buffered: BufferedValue): Appendable[T] = {
41+
val (actualValue, append) = unwrapAppendMarker(buffered)
42+
val deserialized = BufferedValue.transform(actualValue, r)
43+
Appendable(deserialized, append)
44+
}
45+
}
46+
}
47+
548
object Located {
49+
50+
/** Upickle reader for Located[T] that wraps values with their source location */
651
class UpickleReader[T](path: os.Path)(implicit r: upickle.Reader[T])
752
extends upickle.Reader[Located[T]] {
53+
854
private def wrap(index: Int, v: T): Located[T] = Located(path, index, v)
955

1056
def visitArray(length: Int, index: Int): upickle.core.ArrVisitor[Any, Located[T]] = {
@@ -16,8 +62,11 @@ object Located {
1662
}
1763
}
1864

19-
def visitObject(length: Int, jsonableKeys: Boolean, index: Int)
20-
: upickle.core.ObjVisitor[Any, Located[T]] = {
65+
def visitObject(
66+
length: Int,
67+
jsonableKeys: Boolean,
68+
index: Int
69+
): upickle.core.ObjVisitor[Any, Located[T]] = {
2170
val delegate = r.visitObject(length, jsonableKeys, index)
2271
new upickle.core.ObjVisitor[Any, Located[T]] {
2372
def subVisitor = delegate.subVisitor

core/eval/src/mill/eval/ScriptModuleInit.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ class ScriptModuleInit extends ((String, Evaluator) => Seq[Result[ExternalModule
157157
moduleFor(
158158
scriptFile,
159159
parsedHeaderData.`extends`.value.value.headOption,
160-
parsedHeaderData.moduleDeps.value,
161-
parsedHeaderData.compileModuleDeps.value,
162-
parsedHeaderData.runModuleDeps.value,
160+
parsedHeaderData.moduleDeps.value.value,
161+
parsedHeaderData.compileModuleDeps.value.value,
162+
parsedHeaderData.runModuleDeps.value.value,
163163
eval,
164164
parsedHeaderData
165165
)

0 commit comments

Comments
 (0)