Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion core/define/src/mill/define/BaseModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class BaseModule(
millModuleEnclosing0: sourcecode.Enclosing,
millModuleLine0: sourcecode.Line,
millFile0: sourcecode.File
) extends Module.BaseClass()(
) extends Module.BaseClass()(using
mill.define.ModuleCtx.makeRoot(
implicitly,
implicitly,
Expand Down
36 changes: 22 additions & 14 deletions core/define/src/mill/define/Task.scala
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,9 @@ object Task {
val evaluate0: (Seq[Any], mill.define.TaskCtx) => Result[T],
val ctx0: mill.define.ModuleCtx,
val writer: upickle.default.Writer[?],
val isPrivate: Option[Boolean]
val isPrivate: Option[Boolean],
val inputs: Seq[Task[Any]] = Nil
) extends Simple[T] {
val inputs = Nil
override def sideHash: Int = util.Random.nextInt()
// FIXME: deprecated return type: Change to Option
override def writerOpt: Some[Writer[?]] = Some(writer)
Expand All @@ -443,23 +443,27 @@ object Task {
class Sources(
evaluate0: (Seq[Any], mill.define.TaskCtx) => Result[Seq[PathRef]],
ctx0: mill.define.ModuleCtx,
isPrivate: Option[Boolean]
isPrivate: Option[Boolean],
inputs: Seq[Task[Any]]
) extends Input[Seq[PathRef]](
evaluate0,
ctx0,
upickle.default.readwriter[Seq[PathRef]],
isPrivate
isPrivate,
inputs
) {}

class Source(
evaluate0: (Seq[Any], mill.define.TaskCtx) => Result[PathRef],
ctx0: mill.define.ModuleCtx,
isPrivate: Option[Boolean]
isPrivate: Option[Boolean],
inputs: Seq[Task[Any]]
) extends Input[PathRef](
evaluate0,
ctx0,
upickle.default.readwriter[PathRef],
isPrivate
isPrivate,
inputs
) {}

private object Macros {
Expand All @@ -471,9 +475,13 @@ object Task {
Expr[(Seq[Any], mill.define.TaskCtx) => Result[T]]
) => Expr[M[T]],
t: Expr[Result[T]],
allowTaskReferences: Boolean = true
allowedTaskReferences: Applicative.TaskReferences = Applicative.TaskReferences.All
): Expr[M[T]] =
Applicative.impl[M, Task, Result, T, mill.define.TaskCtx](traverseCtx, t, allowTaskReferences)
Applicative.impl[M, Task, Result, T, mill.define.TaskCtx](
traverseCtx,
t,
allowedTaskReferences
)

private def taskIsPrivate()(using Quotes): Expr[Option[Boolean]] =
Cacher.withMacroOwner {
Expand Down Expand Up @@ -513,9 +521,9 @@ object Task {
ctx: Expr[mill.define.ModuleCtx]
): Expr[Simple[Seq[PathRef]]] = {
val expr = appImpl[Simple, Seq[PathRef]](
(in, ev) => '{ new Sources($ev, $ctx, ${ taskIsPrivate() }) },
(in, ev) => '{ new Sources($ev, $ctx, ${ taskIsPrivate() }, $in) },
values,
allowTaskReferences = false
allowedTaskReferences = Applicative.TaskReferences.Sources
)
Cacher.impl0(expr)
}
Expand All @@ -527,9 +535,9 @@ object Task {
): Expr[Simple[PathRef]] = {

val expr = appImpl[Simple, PathRef](
(in, ev) => '{ new Source($ev, $ctx, ${ taskIsPrivate() }) },
(in, ev) => '{ new Source($ev, $ctx, ${ taskIsPrivate() }, $in) },
value,
allowTaskReferences = false
allowedTaskReferences = Applicative.TaskReferences.Sources
)
Cacher.impl0(expr)

Expand All @@ -543,9 +551,9 @@ object Task {
): Expr[Simple[T]] = {

val expr = appImpl[Simple, T](
(in, ev) => '{ new Input[T]($ev, $ctx, $w, ${ taskIsPrivate() }) },
(in, ev) => '{ new Input[T]($ev, $ctx, $w, ${ taskIsPrivate() }, $in) },
value,
allowTaskReferences = false
allowedTaskReferences = Applicative.TaskReferences.None
)
Cacher.impl0(expr)
}
Expand Down
22 changes: 19 additions & 3 deletions core/define/src/mill/define/internal/Applicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package mill.define.internal

import mill.api.internal.internal
import scala.annotation.compileTimeOnly

import scala.quoted.*

import mill.define.internal.Applicative.TaskReferences.All

/**
* A generic Applicative-functor macro: translates calls to
*
Expand All @@ -24,12 +25,24 @@ object Applicative {

type Id[+T] = T

enum TaskReferences {

/** No tasks can be references. */
case None

/** Only `Task.Source` and `Task.Sources` tasks can be references. */
case Sources

/** All tasks can be references. */
case All
}

def impl[M[_]: Type, W[_]: Type, Z[_]: Type, T: Type, Ctx: Type](using
Quotes
)(
traverseCtx: (Expr[Seq[W[Any]]], Expr[(Seq[Any], Ctx) => Z[T]]) => Expr[M[T]],
t: Expr[Z[T]],
allowTaskReferences: Boolean = true
allowedTaskReferences: TaskReferences = All
): Expr[M[T]] = {
import quotes.reflect.*

Expand Down Expand Up @@ -70,8 +83,11 @@ object Applicative {

override def transformTerm(tree: Term)(owner: Symbol): Term = tree match
case t @ Apply(sel @ Select(fun, "apply"), Nil)
if sel.symbol == targetApplySym && allowTaskReferences =>
if sel.symbol == targetApplySym && allowedTaskReferences != TaskReferences.None =>
val localDefs = extractDefs(fun)
// TODO: Support TaskReferences.Sources and TaskReference.Source by filtering by type:
// We only want to accept tasks returning PathRef or Seq[PathRef]
// it should already work, but we don't restrict it properly
visitAllTrees(t) { x =>
val sym = x.symbol
if (sym != Symbol.noSymbol && defs(sym) && !localDefs(sym)) {
Expand Down
24 changes: 18 additions & 6 deletions core/eval/src/mill/eval/EvaluatorImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,16 @@ final class EvaluatorImpl private[mill] (

val selectiveExecutionEnabled = selectiveExecution && !targets.exists(_.isExclusiveCommand)

val selectedTasksOrErr =
if (!selectiveExecutionEnabled) (targets, Map.empty, None)
val selectedTasksOrErr: (
selectedTasks: Seq[Task[T]],
selectiveResults: Map[? <: Task[?], ExecResult[Val]],
maybeNewMetadata: Option[SelectiveExecution.Metadata]
) =
if (!selectiveExecutionEnabled) (
selectedTasks = targets,
selectiveResults = Map.empty,
maybeNewMetadata = None
)
else {
val (named, unnamed) =
targets.partitionMap { case n: Task.Named[?] => Left(n); case t => Right(t) }
Expand All @@ -162,17 +170,21 @@ final class EvaluatorImpl private[mill] (
val selectedSet = changedTasks.downstreamTasks.map(_.ctx.segments.render).toSet

(
unnamed ++ named.filter(t =>
selectedTasks = unnamed ++ named.filter(t =>
t.isExclusiveCommand || selectedSet(t.ctx.segments.render)
),
newComputedMetadata.results,
Some(newComputedMetadata.metadata)
selectiveResults = newComputedMetadata.results,
maybeNewMetadata = Some(newComputedMetadata.metadata)
)
}
}

selectedTasksOrErr match {
case (selectedTasks, selectiveResults, maybeNewMetadata) =>
case (
selectedTasks = selectedTasks,
selectiveResults = selectiveResults,
maybeNewMetadata = maybeNewMetadata
) =>
val evaluated: ExecutionResults =
execution.executeTasks(
selectedTasks,
Expand Down
91 changes: 78 additions & 13 deletions core/exec/test/src/mill/exec/ExecutionTests.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mill.exec

import mill.api.Result
import mill.define.{Discover, Task}
import mill.util.TestGraphs
import mill.testkit.{TestRootModule, UnitTester}
Expand Down Expand Up @@ -97,6 +98,7 @@ object ExecutionTests extends TestSuite {
extraEvaled = -1,
secondRunNoOp = false
)
// second run should only re-run the source/input-tasks
checker(
build.task,
"i am cow hear me moo !",
Expand Down Expand Up @@ -124,6 +126,65 @@ object ExecutionTests extends TestSuite {
)
}

test("sources-with-deps") {
object build extends TestRootModule {
def source = Task.Sources("hello/world.txt", "hello/world2.txt")

def sources2 = Task.Sources({
val res = (source().map(_.path) ++ Seq(moduleDir / "hello/world3.txt"))
.map(Result.create)
// println("res: " + res)
res
}*)

def task = Task {
sources2().map(pr => os.read(pr.path)).mkString + "!"
}

lazy val millDiscover = Discover[this.type]
}

val checker = new Checker(build)

os.write(build.moduleDir / "hello/world.txt", "i am cow ", createFolders = true)
os.write(build.moduleDir / "hello/world2.txt", "hear me moo ", createFolders = true)
os.write(build.moduleDir / "hello/world3.txt", "moo ", createFolders = true)
checker(
target = build.task,
expValue = "i am cow hear me moo moo !",
expEvaled = Seq(build.sources2, build.source, build.task),
extraEvaled = 0,
secondRunNoOp = false
)
// second run should only re-run the source/input-tasks
checker(
build.task,
"i am cow hear me moo moo !",
Seq(build.sources2, build.source),
extraEvaled = 0,
secondRunNoOp = false
)

os.write.over(build.moduleDir / "hello/world.txt", "I AM COW ")

checker(
build.task,
"I AM COW hear me moo moo !",
Seq(build.sources2, build.source, build.task),
extraEvaled = 0,
secondRunNoOp = false
)

os.write.over(build.moduleDir / "hello/world2.txt", "HEAR ME MOO ")
checker(
build.task,
"I AM COW HEAR ME MOO moo !",
Seq(build.sources2, build.source, build.task),
extraEvaled = 0,
secondRunNoOp = false
)
}

test("input") {
var x = 10
object build extends TestRootModule {
Expand Down Expand Up @@ -229,13 +290,13 @@ object ExecutionTests extends TestSuite {
}

UnitTester(build, null).scoped { tester =>
val Right(UnitTester.Result(worker1, _)) = tester.apply(build.worker)
val Right(UnitTester.Result(worker2, _)) = tester.apply(build.worker)
val Right(UnitTester.Result(worker1, _)) = tester.apply(build.worker): @unchecked
val Right(UnitTester.Result(worker2, _)) = tester.apply(build.worker): @unchecked
assert(worker1 == worker2)
assert(worker1.n == 10)
assert(!worker1.closed)
x = 11
val Right(UnitTester.Result(worker3, _)) = tester.apply(build.worker)
val Right(UnitTester.Result(worker3, _)) = tester.apply(build.worker): @unchecked
assert(worker3 != worker2)
assert(worker3.n == 11)
assert(!worker3.closed)
Expand Down Expand Up @@ -298,10 +359,10 @@ object ExecutionTests extends TestSuite {

UnitTester(build, null).scoped { tester =>
assert(y == 0)
val Right(_) = tester.apply(build.task)
val Right(_) = tester.apply(build.task): @unchecked
assert(y == 10)
x = 0
val Left(_) = tester.apply(build.task)
val Left(_) = tester.apply(build.task): @unchecked
assert(y == 10)
}
}
Expand All @@ -315,12 +376,13 @@ object ExecutionTests extends TestSuite {
lazy val millDiscover = Discover[this.type]
}
UnitTester(build, null).scoped { tester =>
val Right(UnitTester.Result(Seq(1, 10, 100), _)) = tester.apply(build.task4)
val Right(UnitTester.Result(Seq(1, 10, 100), _)) = tester.apply(build.task4): @unchecked
}
}
test("traverse") {
UnitTester(traverseBuild, null).scoped { tester =>
val Right(UnitTester.Result(Seq(1, 10, 100), _)) = tester.apply(traverseBuild.task4)
val Right(UnitTester.Result(Seq(1, 10, 100), _)) =
tester.apply(traverseBuild.task4): @unchecked
}
}

Expand All @@ -332,7 +394,7 @@ object ExecutionTests extends TestSuite {
lazy val millDiscover = Discover[this.type]
}
UnitTester(build, null).scoped { tester =>
val Right(UnitTester.Result((1, 10), _)) = tester.apply(build.task4)
val Right(UnitTester.Result((1, 10), _)) = tester.apply(build.task4): @unchecked
}
}

Expand All @@ -344,7 +406,7 @@ object ExecutionTests extends TestSuite {
lazy val millDiscover = Discover[this.type]
}
UnitTester(build, null).scoped { tester =>
val Right(UnitTester.Result(11, _)) = tester.apply(build.task2)
val Right(UnitTester.Result(11, _)) = tester.apply(build.task2): @unchecked
}
}

Expand Down Expand Up @@ -421,11 +483,14 @@ object ExecutionTests extends TestSuite {

test("backticked") {
UnitTester(bactickIdentifiers, null).scoped { tester =>
val Right(UnitTester.Result(1, _)) = tester.apply(bactickIdentifiers.`up-target`)
val Right(UnitTester.Result(3, _)) = tester.apply(bactickIdentifiers.`a-down-target`)
val Right(UnitTester.Result(3, _)) = tester.apply(bactickIdentifiers.`invisible&`)
val Right(UnitTester.Result(1, _)) =
tester.apply(bactickIdentifiers.`up-target`): @unchecked
val Right(UnitTester.Result(3, _)) =
tester.apply(bactickIdentifiers.`a-down-target`): @unchecked
val Right(UnitTester.Result(3, _)) =
tester.apply(bactickIdentifiers.`invisible&`): @unchecked
val Right(UnitTester.Result(4, _)) =
tester.apply(bactickIdentifiers.`nested-module`.`nested-target`)
tester.apply(bactickIdentifiers.`nested-module`.`nested-target`): @unchecked
}
}
test("anonTaskFailure") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ trait WatchTests extends UtestIntegrationTestSuite {
if (show) assert(err == expectedErr)
else assert(err.isEmpty)

val expectedShows = expectedShows0.map('"' + _ + '"')
val expectedShows = expectedShows0.map("\"" + _ + "\"")
if (show) assert(shows == expectedShows)
}
}
Expand Down
1 change: 1 addition & 0 deletions libs/scalalib/src/mill/scalalib/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ trait JavaModule
*/
def assemblyRules: Seq[Assembly.Rule] = Assembly.defaultRules

@deprecated("FOR REMOVAL")
def sourcesFolders: Seq[os.SubPath] = Seq("src")

/**
Expand Down