Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions core/define/src/mill/define/Task.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,34 +148,31 @@ object Task {
* signature for you source files/folders and decides whether or not downstream
* [[Task.Computed]]s need to be invalidated and re-computed.
*/
inline def Sources(inline values: Result[os.Path]*)(implicit
inline def Sources(inline values: (os.Path | os.SubPath | os.RelPath | String)*)(implicit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This big union type looks pretty out of place. Could we use os.FilePath here? Maybe if we add a few more implicits upstream in OS-Lib to make the conversions from string work?

Copy link
Member Author

@lefou lefou Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Once we use just os.FilePath, we can't profit from String to os.SubPath translation. We either want to support a more generic conversion for String to os.FilePath, which also means we want to handle absolute and relative paths, or find some other way to support conversion.

I committed some compromise acepting os.SubPath | os.FilePath to see if CI likes it.

inline ctx: mill.define.ModuleCtx
): Simple[Seq[PathRef]] = ${
Macros.sourcesImpl('{ Result.sequence(values.map(_.map(PathRef(_)))) })('ctx)
Macros.sourcesImpl('{ values.map(p => PathRef(mapToPath(p))) })('ctx)
}

inline def Sources(inline values: os.SubPath*)(implicit
inline ctx: mill.define.ModuleCtx,
dummy: Boolean = true
): Simple[Seq[PathRef]] = ${
Macros.sourcesImpl(
'{ values.map(sub => PathRef(ctx.millSourcePath / os.up / os.PathChunk.SubPathChunk(sub))) }
)('ctx)
inline private def mapToPath(value: os.Path | os.SubPath | os.RelPath | String)(implicit
inline ctx: mill.define.ModuleCtx
): os.Path = value match {
// TODO: support "."
case "." => ctx.millSourcePath / os.up
case str: String => ctx.millSourcePath / os.up / os.PathChunk.segmentsFromString(str)
case sub: os.SubPath => ctx.millSourcePath / os.up / os.PathChunk.SubPathChunk(sub)
case rel: os.RelPath => ctx.millSourcePath / os.up / os.PathChunk.RelPathChunk(rel)
case p: os.Path => p
}

/**
* Similar to [[Sources]], but only for a single source file or folder. Defined
* using `Task.Source`.
*/
inline def Source(inline value: Result[os.Path])(implicit
inline ctx: mill.define.ModuleCtx
): Simple[PathRef] =
${ Macros.sourceImpl('{ value.map(PathRef(_)) })('ctx) }

inline def Source(inline value: os.SubPath)(implicit
inline def Source(inline value: os.Path | os.SubPath | os.RelPath | String)(implicit
inline ctx: mill.define.ModuleCtx
): Simple[PathRef] =
${ Macros.sourceImpl('{ PathRef(ctx.millSourcePath / os.up / value) })('ctx) }
${ Macros.sourceImpl('{ PathRef(mapToPath(value)) })('ctx) }

/**
* [[Input]]s, normally defined using `Task.Input`, are [[Task.Named]]s that
Expand Down
57 changes: 44 additions & 13 deletions core/exec/test/src/mill/exec/ExecutionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ object ExecutionTests extends TestSuite {
def task = Task[Int] { anon() }
lazy val millDiscover = Discover[this.type]
}

object sourceBuild extends TestRootModule {
def source = Task.Source { "hello/world.txt" }
def task = Task { os.read(source().path) + " !" }
object sub extends mill.define.Module {
def sameDir = Task.Source(".")
}
lazy val millDiscover = Discover[this.type]
}

object sourcesBuild extends TestRootModule {
def source = Task.Sources("hello/world.txt", "hello/world2.txt")
def task = Task { source().map(pr => os.read(pr.path)).mkString + "!" }
object sub extends mill.define.Module {
def sameDirs = Task.Sources(".")
}
lazy val millDiscover = Discover[this.type]
}

class Checker[T <: mill.testkit.TestRootModule](module: T)
extends exec.Checker(module)

Expand All @@ -45,12 +64,7 @@ object ExecutionTests extends TestSuite {
}

test("source") {
object build extends TestRootModule {
def source = Task.Source { "hello/world.txt" }
def task = Task { os.read(source().path) + " !" }
lazy val millDiscover = Discover[this.type]
}

val build = sourceBuild
val checker = new Checker(build)

os.write(build.moduleDir / "hello/world.txt", "i am cow", createFolders = true)
Expand All @@ -61,7 +75,13 @@ object ExecutionTests extends TestSuite {
extraEvaled = -1,
secondRunNoOp = false
)
checker(build.task, "i am cow !", Seq(build.source), extraEvaled = -1, secondRunNoOp = false)
checker(
build.task,
"i am cow !",
Seq(build.source),
extraEvaled = -1,
secondRunNoOp = false
)
os.write.over(build.moduleDir / "hello/world.txt", "hear me moo")

checker(
Expand All @@ -78,14 +98,17 @@ object ExecutionTests extends TestSuite {
extraEvaled = -1,
secondRunNoOp = false
)

checker(
build.sub.sameDir,
PathRef(build.sub.moduleDir),
Seq(build.sub.sameDir),
extraEvaled = 0,
secondRunNoOp = false
)
}
test("sources") {
object build extends TestRootModule {
def source = Task.Sources("hello/world.txt", "hello/world2.txt")
def task = Task { source().map(pr => os.read(pr.path)).mkString + "!" }
lazy val millDiscover = Discover[this.type]
}

val build = sourcesBuild
val checker = new Checker(build)

os.write(build.moduleDir / "hello/world.txt", "i am cow ", createFolders = true)
Expand Down Expand Up @@ -122,6 +145,14 @@ object ExecutionTests extends TestSuite {
extraEvaled = -1,
secondRunNoOp = false
)

checker(
build.sub.sameDirs,
Seq(PathRef(build.sub.moduleDir)),
Seq(build.sub.sameDirs),
extraEvaled = 0,
secondRunNoOp = false
)
}

test("input") {
Expand Down
6 changes: 4 additions & 2 deletions example/fundamentals/tasks/2-primary-tasks/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import mill.{Module, T, _}
def sources = Task.Source { "src" }
def resources = Task.Source { "resources" }

// ``Source``s are defined using `Task.Source{...}` taking one `os.Path`, or `Task.Sources{...}`,
// taking multiple ``os.Path``s as arguments. A ``Source``'s:
// ``Source``s are defined using `Task.Source{...}` taking one path, or `Task.Sources{...}`,
// taking multiple paths as arguments. Both accept `os.Path`, `os.SubPath`, `os.RelPath`
// or a `String` containing a sub-path. All relative paths will be resolve against the `moduleDir`
// of the enclosing module. A ``Source``'s:
// its build signature/`hashCode` depends not just on the path
// it refers to (e.g. `foo/bar/baz`) but also the MD5 hash of the file contents or
// folder tree at that path.
Expand Down
5 changes: 2 additions & 3 deletions example/thirdparty/arrow/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ object `package` extends Module {
sources ++= Seq(nonJvmSourcesPath)
}
}
sources.map(mill.api.Result.Success(_))
sources
}*)

override def kotlincOptions =
Expand All @@ -163,8 +163,7 @@ object `package` extends Module {
override def sources: T[Seq[PathRef]] = Task.Sources(
Seq("common", outer.platformCrossSuffix)
.map(platform => outer.moduleDir / "src" / s"${platform}Test" / "kotlin")
.filter(p => os.exists(p))
.map(mill.api.Result.Success(_))*
.filter(p => os.exists(p))*
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ object RevapiModuleTests extends TestSuite {
object module2 extends module with RevapiModule {
override def revapiConfigFiles: T[Seq[PathRef]] =
Task.Sources(
os.list(conf).iterator.filter(_.ext == "json").toSeq.map(mill.api.Result.Success(_))*
os.list(conf).iterator.filter(_.ext == "json").toSeq*
)
override def revapiClasspath: T[Seq[PathRef]] = Task {
super.revapiClasspath() ++ Seq(PathRef(conf))
Expand Down Expand Up @@ -113,7 +113,7 @@ object RevapiModuleTests extends TestSuite {
}
override def revapiConfigFiles: T[Seq[PathRef]] =
Task.Sources(
os.list(conf).iterator.filter(_.ext == "json").toSeq.map(mill.api.Result.Success(_))*
os.list(conf).iterator.filter(_.ext == "json").toSeq*
)
override def revapiClasspath: T[Seq[PathRef]] = Task {
super.revapiClasspath() ++ Seq(PathRef(conf))
Expand Down
2 changes: 1 addition & 1 deletion runner/meta/src/mill/meta/MillBuildRootModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ trait MillBuildRootModule()(implicit
* @see [[generatedSources]]
*/
def scriptSources: T[Seq[PathRef]] = Task.Sources(
scriptSourcesPaths.map(Result.Success(_))* // Ensure ordering is deterministic
scriptSourcesPaths* // Ensure ordering is deterministic
)

def parseBuildFiles: T[FileImportGraph] = Task {
Expand Down