github.com/bazelbuild/bazel-watcher@v0.25.2/internal/ibazel/fswatcher/fsevents/fsevents.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build darwin 16 // +build darwin 17 18 package fsevents 19 20 import ( 21 "errors" 22 "path/filepath" 23 "strings" 24 25 "github.com/fsnotify/fsevents" 26 27 "github.com/bazelbuild/bazel-watcher/internal/ibazel/fswatcher/common" 28 ) 29 30 type realFSEventsWatcher struct { 31 es *fsevents.EventStream 32 evs chan common.Event 33 } 34 35 var _ common.Watcher = &realFSEventsWatcher{} 36 37 // Close implements ibazel/fswatcher/common.Watcher 38 func (w *realFSEventsWatcher) Close() error { 39 w.es.Stop() 40 close(w.es.Events) 41 close(w.evs) 42 return nil 43 } 44 45 // UpdateAll implements ibazel/fswatcher/common.Watcher 46 func (w *realFSEventsWatcher) UpdateAll(names []string) error { 47 w.es.Stop() 48 commonRoot, err := findCommonRoot(names) 49 if err != nil { 50 return err 51 } 52 es := &fsevents.EventStream{ 53 Events: make(chan []fsevents.Event), 54 Paths: commonRoot, 55 Flags: w.es.Flags, 56 } 57 w.es = es 58 go w.MapEvents() 59 es.Start() 60 61 return nil 62 } 63 64 // Events implements ibazel/fswatcher/common.Watcher 65 func (w *realFSEventsWatcher) Events() chan common.Event { 66 return w.evs 67 } 68 func (s *realFSEventsWatcher) MapEvents() { 69 for events := range s.es.Events { 70 for _, event := range events { 71 if evt, ok := newEvent(event.Path, event.Flags); ok { 72 s.evs <- evt 73 } 74 } 75 } 76 } 77 78 func newEvent(name string, mask fsevents.EventFlags) (common.Event, bool) { 79 e := common.Event{} 80 81 if mask&fsevents.ItemIsFile != fsevents.ItemIsFile { 82 return e, false 83 } 84 85 if mask&fsevents.ItemRemoved == fsevents.ItemRemoved { 86 e.Op |= common.Remove 87 } 88 if mask&fsevents.ItemCreated == fsevents.ItemCreated { 89 e.Op |= common.Create 90 } 91 if mask&fsevents.ItemRenamed == fsevents.ItemRenamed { 92 e.Op |= common.Rename 93 } 94 if mask&fsevents.ItemModified == fsevents.ItemModified || 95 mask&fsevents.ItemInodeMetaMod == fsevents.ItemInodeMetaMod { 96 e.Op |= common.Write 97 } 98 if mask&fsevents.ItemChangeOwner == fsevents.ItemChangeOwner || 99 mask&fsevents.ItemXattrMod == fsevents.ItemXattrMod { 100 e.Op |= common.Chmod 101 } 102 103 e.Name = name 104 return e, true 105 } 106 107 // Find the longest common root path of all directories to watch. 108 func findCommonRoot(names []string) ([]string, error) { 109 if len(names) == 0 { 110 return []string{}, nil 111 } 112 113 rootSplit := strings.Split(strings.Trim(names[0], "/"), "/") 114 rootLength := len(rootSplit) 115 116 for _, dir := range names { 117 split := strings.Split(strings.Trim(dir, "/"), "/") 118 commonLength := 0 119 for i := 0; i < rootLength && i < len(split); i++ { 120 if rootSplit[i] != split[i] { 121 break 122 } 123 commonLength = i + 1 124 } 125 rootLength = commonLength 126 } 127 128 if rootLength == 0 { 129 return nil, errors.New("could not find common root of directories") 130 } 131 132 return []string{"/" + filepath.Join(rootSplit[:rootLength]...) + "/"}, nil 133 } 134 135 func NewWatcher() (common.Watcher, error) { 136 es := &fsevents.EventStream{ 137 Events: make(chan []fsevents.Event), 138 Paths: []string{}, 139 Flags: fsevents.FileEvents, 140 } 141 watcher := &realFSEventsWatcher{ 142 es: es, 143 evs: make(chan common.Event), 144 } 145 return watcher, nil 146 }