Skip to content

Commit

Permalink
Merge pull request #799 from tpetricek/watcher_fix
Browse files Browse the repository at this point in the history
Fix WatchChanges on Mac, fix Dispose, improve Timer usage
  • Loading branch information
forki committed May 17, 2015
2 parents 3f9b431 + e7e9950 commit ec27e1f
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 28 deletions.
46 changes: 27 additions & 19 deletions src/app/FakeLib/ChangeWatcher.fs
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,46 @@ let WatchChanges (onChange : FileChange seq -> unit) (fileIncludes : FileInclude
dirsToWatch
|> Seq.exists (fun p -> p.StartsWith d && p <> d)
|> not)

tracefn "dirs to watch: %A" dirsToWatch

// we collect changes in a mutable ref cell and wait for a few milliseconds to
// receive all notifications when the system sends them repetedly or sends multiple
// updates related to the same file; then we call 'onChange' with all cahnges
let unNotifiedChanges = ref List.empty<FileChange>

let acumChanges (fileChange : FileChange) =
if fileIncludes.IsMatch fileChange.FullPath then
lock unNotifiedChanges (fun () -> unNotifiedChanges := [ fileChange ] @ !unNotifiedChanges)

let timer = new System.Timers.Timer(5.0)
// when running 'onChange' we ignore all notifications to avoid infinite loops
let runningHandlers = ref false

let timer = new System.Timers.Timer(50.0)
timer.AutoReset <- false
timer.Elapsed.Add(fun _ ->
lock unNotifiedChanges (fun () ->
if !unNotifiedChanges
|> Seq.length
> 0 then
if not (Seq.isEmpty !unNotifiedChanges) then
let changes =
!unNotifiedChanges
|> Seq.groupBy (fun c -> c.FullPath)
|> Seq.map (fun (name, changes) ->
changes
|> Seq.sortBy (fun c -> c.Status)
|> Seq.head)
unNotifiedChanges := List.empty<FileChange>
onChange changes))
unNotifiedChanges := []
try
runningHandlers := true
onChange changes
finally
runningHandlers := false ))

tracefn "dirs to watch: %A" dirsToWatch
let acumChanges (fileChange : FileChange) =
// only record the changes if we are not currently running 'onChange' handler
if not !runningHandlers && fileIncludes.IsMatch fileChange.FullPath then
lock unNotifiedChanges (fun () ->
unNotifiedChanges := fileChange :: !unNotifiedChanges
// start the timer (ignores repeated calls) to trigger events in 50ms
(timer:System.Timers.Timer).Start() )

let watchers =
dirsToWatch |> Seq.map (fun dir ->
dirsToWatch |> List.ofSeq |> List.map (fun dir ->
tracefn "watching dir: %s" dir

let watcher = new FileSystemWatcher(FullName dir, "*.*")
watcher.EnableRaisingEvents <- true
watcher.IncludeSubdirectories <- true
Expand All @@ -88,11 +101,6 @@ let WatchChanges (onChange : FileChange seq -> unit) (fileIncludes : FileInclude
Name = e.Name
Status = Created })
watcher)
watchers
|> Seq.length
|> ignore //force iteration

timer.Start()

{ new System.IDisposable with
member this.Dispose() =
Expand Down
30 changes: 21 additions & 9 deletions src/app/FakeLib/Globbing/Globbing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,29 @@ let inline private normalizeOutputPath (p : string) =
.TrimEnd(Path.DirectorySeparatorChar)

let internal getRoot (baseDirectory : string) (pattern : string) =
let baseDirectory = (normalizePath baseDirectory)
let baseDirectory = normalizePath baseDirectory
let normPattern = normalizePath pattern

let patternParts = normPattern.Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries)
let patternPathParts =
patternParts
|> Seq.takeWhile(fun p -> not (p.Contains("*")))
|> Seq.toArray

let globRoot =
// If we did not find any "*", then drop the last bit (it is a file name, not a pattern)
( if patternPathParts.Length = patternParts.Length then
patternPathParts.[0 .. patternPathParts.Length-2]
else patternPathParts )
|> String.concat (Path.DirectorySeparatorChar.ToString())

let globRoot =
(normalizePath pattern).Split([| '/'; '\\' |], StringSplitOptions.RemoveEmptyEntries) |>
Seq.takeWhile(fun p -> not (p.Contains("*"))) |>
String.concat(Path.DirectorySeparatorChar.ToString())

if Path.IsPathRooted globRoot then
globRoot
else
Path.Combine(baseDirectory, globRoot)
// If we dropped "/" from the beginning of the path in the 'Split' call, put it back!
if normPattern.StartsWith("/") then "/" + globRoot
else globRoot

if Path.IsPathRooted globRoot then globRoot
else Path.Combine(baseDirectory, globRoot)

let internal search (baseDir : string) (input : string) =
let baseDir = normalizePath baseDir
Expand Down

0 comments on commit ec27e1f

Please sign in to comment.