aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/filewatcher/backend_darwin.go
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:55 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:55 +0800
commitfc8c5fdce62fb229202659408798a7b6c98f6e8b (patch)
tree7554f80e50de4af6fd255afa7c21bcdd58a7af34 /cli/internal/filewatcher/backend_darwin.go
parentdd84b9d64fb98746a230cd24233ff50a562c39c9 (diff)
downloadHydroRoll-fc8c5fdce62fb229202659408798a7b6c98f6e8b.tar.gz
HydroRoll-fc8c5fdce62fb229202659408798a7b6c98f6e8b.zip
Diffstat (limited to 'cli/internal/filewatcher/backend_darwin.go')
-rw-r--r--cli/internal/filewatcher/backend_darwin.go220
1 files changed, 0 insertions, 220 deletions
diff --git a/cli/internal/filewatcher/backend_darwin.go b/cli/internal/filewatcher/backend_darwin.go
deleted file mode 100644
index 4c029c4..0000000
--- a/cli/internal/filewatcher/backend_darwin.go
+++ /dev/null
@@ -1,220 +0,0 @@
-//go:build darwin
-// +build darwin
-
-package filewatcher
-
-import (
- "fmt"
- "strings"
- "sync"
- "time"
-
- "github.com/pkg/errors"
- "github.com/yookoala/realpath"
-
- "github.com/fsnotify/fsevents"
- "github.com/hashicorp/go-hclog"
- "github.com/vercel/turbo/cli/internal/doublestar"
- "github.com/vercel/turbo/cli/internal/fs"
- "github.com/vercel/turbo/cli/internal/turbopath"
-)
-
-type fseventsBackend struct {
- events chan Event
- errors chan error
- logger hclog.Logger
- mu sync.Mutex
- streams []*fsevents.EventStream
- closed bool
-}
-
-func (f *fseventsBackend) Events() <-chan Event {
- return f.events
-}
-
-func (f *fseventsBackend) Errors() <-chan error {
- return f.errors
-}
-
-func (f *fseventsBackend) Close() error {
- f.mu.Lock()
- defer f.mu.Unlock()
- if f.closed {
- return ErrFilewatchingClosed
- }
- f.closed = true
- for _, stream := range f.streams {
- stream.Stop()
- }
- close(f.events)
- close(f.errors)
- return nil
-}
-
-func (f *fseventsBackend) Start() error {
- return nil
-}
-
-var (
- _eventLatency = 10 * time.Millisecond
- _cookieTimeout = 500 * time.Millisecond
-)
-
-// AddRoot starts watching a new directory hierarchy. Events matching the provided excludePatterns
-// will not be forwarded.
-func (f *fseventsBackend) AddRoot(someRoot turbopath.AbsoluteSystemPath, excludePatterns ...string) error {
- // We need to resolve the real path to the hierarchy that we are going to watch
- realRoot, err := realpath.Realpath(someRoot.ToString())
- if err != nil {
- return err
- }
- root := fs.AbsoluteSystemPathFromUpstream(realRoot)
- dev, err := fsevents.DeviceForPath(root.ToString())
- if err != nil {
- return err
- }
-
- // Optimistically set up and start a stream, assuming the watch is still valid.
- s := &fsevents.EventStream{
- Paths: []string{root.ToString()},
- Latency: _eventLatency,
- Device: dev,
- Flags: fsevents.FileEvents | fsevents.WatchRoot,
- }
- s.Start()
- events := s.Events
-
- // fsevents delivers events for all existing files first, so use a cookie to detect when we're ready for new events
- if err := waitForCookie(root, events, _cookieTimeout); err != nil {
- s.Stop()
- return err
- }
-
- // Now try to persist the stream.
- f.mu.Lock()
- defer f.mu.Unlock()
- if f.closed {
- s.Stop()
- return ErrFilewatchingClosed
- }
- f.streams = append(f.streams, s)
- f.logger.Debug(fmt.Sprintf("watching root %v, excluding %v", root, excludePatterns))
-
- go func() {
- for evs := range events {
- for _, ev := range evs {
- isExcluded := false
-
- // 1. Ensure that we have a `/`-prefixed path from the event.
- var eventPath string
- if !strings.HasPrefix("/", ev.Path) {
- eventPath = "/" + ev.Path
- } else {
- eventPath = ev.Path
- }
-
- // 2. We're getting events from the real path, but we need to translate
- // back to the path we were provided since that's what the caller will
- // expect in terms of event paths.
- watchRootRelativePath := eventPath[len(realRoot):]
- processedEventPath := someRoot.UntypedJoin(watchRootRelativePath)
-
- // 3. Compare the event to all exclude patterns, short-circuit if we know
- // we are not watching this file.
- processedPathString := processedEventPath.ToString() // loop invariant
- for _, pattern := range excludePatterns {
- matches, err := doublestar.Match(pattern, processedPathString)
- if err != nil {
- f.errors <- err
- } else if matches {
- isExcluded = true
- break
- }
- }
-
- // 4. Report the file events we care about.
- if !isExcluded {
- f.events <- Event{
- Path: processedEventPath,
- EventType: toFileEvent(ev.Flags),
- }
- }
- }
- }
- }()
-
- return nil
-}
-
-func waitForCookie(root turbopath.AbsoluteSystemPath, events <-chan []fsevents.Event, timeout time.Duration) error {
- // This cookie needs to be in a location that we're watching, and at this point we can't guarantee
- // what the root is, or if something like "node_modules/.cache/turbo" would make sense. As a compromise, ensure
- // that we clean it up even in the event of a failure.
- cookiePath := root.UntypedJoin(".turbo-cookie")
- if err := cookiePath.WriteFile([]byte("cookie"), 0755); err != nil {
- return err
- }
- expected := cookiePath.ToString()[1:] // trim leading slash
- if err := waitForEvent(events, expected, fsevents.ItemCreated, timeout); err != nil {
- // Attempt to not leave the cookie file lying around.
- // Ignore the error, since there's not much we can do with it.
- _ = cookiePath.Remove()
- return err
- }
- if err := cookiePath.Remove(); err != nil {
- return err
- }
- if err := waitForEvent(events, expected, fsevents.ItemRemoved, timeout); err != nil {
- return err
- }
- return nil
-}
-
-func waitForEvent(events <-chan []fsevents.Event, path string, flag fsevents.EventFlags, timeout time.Duration) error {
- ch := make(chan struct{})
- go func() {
- for evs := range events {
- for _, ev := range evs {
- if ev.Path == path && ev.Flags&flag != 0 {
- close(ch)
- return
- }
- }
- }
- }()
- select {
- case <-time.After(timeout):
- return errors.Wrap(ErrFailedToStart, "timed out waiting for initial fsevents cookie")
- case <-ch:
- return nil
- }
-}
-
-var _modifiedMask = fsevents.ItemModified | fsevents.ItemInodeMetaMod | fsevents.ItemFinderInfoMod | fsevents.ItemChangeOwner | fsevents.ItemXattrMod
-
-func toFileEvent(flags fsevents.EventFlags) FileEvent {
- if flags&fsevents.ItemCreated != 0 {
- return FileAdded
- } else if flags&fsevents.ItemRemoved != 0 {
- return FileDeleted
- } else if flags&_modifiedMask != 0 {
- return FileModified
- } else if flags&fsevents.ItemRenamed != 0 {
- return FileRenamed
- } else if flags&fsevents.RootChanged != 0 {
- // count this as a delete, something affected the path to the root
- // of the stream
- return FileDeleted
- }
- return FileOther
-}
-
-// GetPlatformSpecificBackend returns a filewatching backend appropriate for the OS we are
-// running on.
-func GetPlatformSpecificBackend(logger hclog.Logger) (Backend, error) {
- return &fseventsBackend{
- events: make(chan Event),
- errors: make(chan error),
- logger: logger.Named("fsevents"),
- }, nil
-}