Skip to content

Draft: Implement watching file paths via oslib.watch #5068

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8ec73a8
WIP: fs-watching
arturaz May 5, 2025
7a2eace
Merge remote-tracking branch 'upstream/main' into improvement/fs-watc…
arturaz May 5, 2025
b4af95f
[autofix.ci] apply automated fixes
autofix-ci[bot] May 5, 2025
e7faaa8
fixes
arturaz May 6, 2025
1000593
Merge remote-tracking branch 'upstream/main' into improvement/fs-watc…
arturaz May 6, 2025
4175938
Merge remote-tracking branch 'origin/improvement/fs-watching' into im…
arturaz May 6, 2025
3e7c22c
Forward porting from 0.12.x branch.
arturaz May 8, 2025
51cbab5
[autofix.ci] apply automated fixes
autofix-ci[bot] May 8, 2025
895c077
Merge remote-tracking branch 'upstream/main' into improvement/fs-watc…
arturaz May 9, 2025
14b94ef
Merge remote-tracking branch 'refs/remotes/origin/improvement/fs-watc…
arturaz May 9, 2025
c331c46
[autofix.ci] apply automated fixes
autofix-ci[bot] May 9, 2025
2726072
Catch if `writeToWatchLog` tries to write to a closed file.
arturaz May 8, 2025
8b76abf
Merge remote-tracking branch 'refs/remotes/origin/improvement/fs-watc…
arturaz May 9, 2025
9021c19
CI test debug
arturaz May 13, 2025
2dcf557
CI test debug
arturaz May 13, 2025
dfbba1f
debug watch tests with sleeps
arturaz May 13, 2025
1b797e9
Move stale checking after we start the watch
arturaz May 13, 2025
ad139ab
[autofix.ci] apply automated fixes
autofix-ci[bot] May 13, 2025
69390dd
Refactor setIdle
arturaz May 13, 2025
2a84a72
Make tests more robust.
arturaz May 13, 2025
50bfa30
Merge remote-tracking branch 'refs/remotes/origin/improvement/fs-watc…
arturaz May 13, 2025
a4de281
[autofix.ci] apply automated fixes
autofix-ci[bot] May 13, 2025
35deeed
Revert `run-tests.yml`
arturaz May 13, 2025
8851638
Merge remote-tracking branch 'refs/remotes/origin/improvement/fs-watc…
arturaz May 13, 2025
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
22 changes: 20 additions & 2 deletions core/api/src/mill/api/Watchable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ package mill.api
*/
private[mill] sealed trait Watchable
private[mill] object Watchable {
case class Path(p: java.nio.file.Path, quick: Boolean, signature: Int) extends Watchable
case class Value(f: () => Long, signature: Long, pretty: String) extends Watchable

/** A [[Watchable]] that is being watched via polling. */
private[mill] sealed trait Pollable extends Watchable

/** A [[Watchable]] that is being watched via a notification system (like inotify). */
private[mill] sealed trait Notifiable extends Watchable
Comment on lines +13 to +16
Copy link
Member

Choose a reason for hiding this comment

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

Do we actually need these two additional marker traits? It seems to me that working directly with Watchable.Path and Watchable.Value will be enough. There aren't any compatibility/extensibility concerns since all this stuff is private[mill] anyway


/**
* @param p the path to watch
* @param quick if true, only watch file attributes
* @param signature the initial hash of the path contents
*/
case class Path(p: java.nio.file.Path, quick: Boolean, signature: Int) extends Notifiable

/**
* @param f the expression to watch, returns some sort of hash
* @param signature the initial hash from the first invocation of the expression
* @param pretty human-readable name
*/
case class Value(f: () => Long, signature: Long, pretty: String) extends Pollable
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,9 @@ trait WatchTests extends UtestIntegrationTestSuite {
val expectedShows0 = mutable.Buffer.empty[String]
val res = f(expectedOut, expectedErr, expectedShows0)
val (shows, out) = res.out.linesIterator.toVector.partition(_.startsWith("\""))
val err = res.err.linesIterator.toVector
.filter(!_.contains("Compiling compiler interface..."))
.filter(!_.contains("Watching for changes"))
.filter(!_.contains("[info] compiling"))
.filter(!_.contains("[info] done compiling"))
.filter(!_.contains("mill-server/ exitCode file not found"))
val err = res.err.linesIterator.toVector.filter(s =>
s.startsWith("Setting up ") || s.startsWith("Running ")
)

assert(out == expectedOut)

Expand Down
4 changes: 2 additions & 2 deletions libs/main/src/mill/main/MainModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ abstract class MainRootModule()(implicit
* [[show]], [[inspect]], [[plan]], etc.
*/
trait MainModule extends BaseModule with MainModuleApi {
protected[mill] val watchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty[Watchable]
protected[mill] val evalWatchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty[Watchable]
protected[mill] val watchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty
protected[mill] val evalWatchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty
object interp {
def watchValue[T](v0: => T)(implicit fn: sourcecode.FileName, ln: sourcecode.Line): T = {
os.checker.withValue(os.Checker.Nop) {
Expand Down
4 changes: 3 additions & 1 deletion mill-build/src/millbuild/Deps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ object Deps {
val junitInterface = mvn"com.github.sbt:junit-interface:0.13.3"
val commonsIo = mvn"commons-io:commons-io:2.18.0"
val log4j2Core = mvn"org.apache.logging.log4j:log4j-core:2.24.3"
val osLib = mvn"com.lihaoyi::os-lib:0.11.5-M8"
val osLibVersion = "0.11.5-M8"
val osLib = mvn"com.lihaoyi::os-lib:$osLibVersion"
val osLibWatch = mvn"com.lihaoyi::os-lib-watch:$osLibVersion"
val pprint = mvn"com.lihaoyi::pprint:0.9.0"
val mainargs = mvn"com.lihaoyi::mainargs:0.7.6"
val millModuledefsVersion = "0.11.4"
Expand Down
1 change: 1 addition & 0 deletions runner/daemon/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object `package` extends MillPublishScalaModule {
def mvnDeps = Seq(
Deps.sourcecode,
Deps.osLib,
Deps.osLibWatch,
Deps.mainargs,
Deps.upickle,
Deps.pprint,
Expand Down
2 changes: 1 addition & 1 deletion runner/daemon/src/mill/daemon/MillBuildBootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class MillBuildBootstrap(
// look at the `moduleWatched` of one frame up (`prevOuterFrameOpt`),
// and not the `moduleWatched` from the current frame (`prevFrameOpt`)
val moduleWatchChanged =
prevOuterFrameOpt.exists(_.moduleWatched.exists(w => !Watching.validate(w)))
prevOuterFrameOpt.exists(_.moduleWatched.exists(w => !Watching.validateAnyWatchable(w)))

val classLoader = if (runClasspathChanged || moduleWatchChanged) {
// Make sure we close the old classloader every time we create a new
Expand Down
5 changes: 5 additions & 0 deletions runner/daemon/src/mill/daemon/MillCliConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ case class MillCliConfig(
doc = """Watch and re-run the given tasks when when their inputs change."""
)
watch: Flag = Flag(),
@arg(
name = "watch-via-fs-notify",
Copy link
Member

Choose a reason for hiding this comment

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

Let's shorten this to --notify-watch here and in the other PR, the current name is a bit of a mouthful

doc = "Use filesystem based file watching instead of polling based one (defaults to true)."
)
watchViaFsNotify: Boolean = true,
@arg(
short = 's',
doc =
Expand Down
11 changes: 7 additions & 4 deletions runner/daemon/src/mill/daemon/MillMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,13 @@ object MillMain {
if (config.watch.value) os.remove(out / OutFiles.millSelectiveExecution)
Watching.watchLoop(
ringBell = config.ringBell.value,
watch = config.watch.value,
watch = Option.when(config.watch.value)(Watching.WatchArgs(
setIdle = setIdle,
colors,
useNotify = config.watchViaFsNotify,
serverDir = serverDir
)),
streams = streams,
setIdle = setIdle,
evaluate = (enterKeyPressed: Boolean, prevState: Option[RunnerState]) => {
adjustJvmProperties(userSpecifiedProperties, initialSystemProperties)
runMillBootstrap(
Expand All @@ -361,8 +365,7 @@ object MillMain {
config.leftoverArgs.value,
streams
)
},
colors = colors
}
)
}
}
Expand Down
Loading