12 #include "llvm/ADT/STLExtras.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/Support/Error.h" 15 #include "llvm/Support/Path.h" 16 #include <CoreServices/CoreServices.h> 19 using namespace clang;
46 FSEventStreamRef EventStream,
49 llvm::StringRef WatchedDirPath)
50 : EventStream(EventStream), Receiver(Receiver),
51 WatchedDirPath(WatchedDirPath) {}
53 ~DirectoryWatcherMac()
override {
55 EventStream =
nullptr;
59 DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
""),
64 FSEventStreamRef EventStream;
65 std::function<void(llvm::ArrayRef<Event>,
bool)> Receiver;
66 const std::string WatchedDirPath;
69 struct EventStreamContextData {
70 std::string WatchedPath;
71 std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>,
bool)> Receiver;
73 EventStreamContextData(
74 std::string &&WatchedPath,
77 : WatchedPath(
std::move(WatchedPath)), Receiver(Receiver) {}
80 static void dispose(
const void *ctx) {
81 delete static_cast<const EventStreamContextData *
>(ctx);
87 kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped |
88 kFSEventStreamEventFlagMustScanSubDirs;
91 kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRenamed |
92 kFSEventStreamEventFlagItemModified;
95 void *ClientCallBackInfo,
size_t NumEvents,
97 const FSEventStreamEventFlags EventFlags[],
98 const FSEventStreamEventId EventIds[]) {
99 auto *ctx =
static_cast<EventStreamContextData *
>(ClientCallBackInfo);
101 std::vector<DirectoryWatcher::Event> Events;
102 for (
size_t i = 0; i < NumEvents; ++i) {
103 StringRef Path = ((
const char **)EventPaths)[i];
104 const FSEventStreamEventFlags Flags = EventFlags[i];
106 if (Flags & StreamInvalidatingFlags) {
108 DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
""});
110 }
else if (!(Flags & kFSEventStreamEventFlagItemIsFile)) {
113 if ((Flags & kFSEventStreamEventFlagItemRemoved) &&
114 Path == ctx->WatchedPath) {
116 DirectoryWatcher::Event::EventKind::WatchedDirRemoved,
""});
118 DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
""});
123 }
else if (Flags & kFSEventStreamEventFlagItemRemoved) {
124 Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed,
125 llvm::sys::path::filename(Path));
127 }
else if (Flags & ModifyingFileEvents) {
129 Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed,
130 llvm::sys::path::filename(Path));
132 Events.emplace_back(DirectoryWatcher::Event::EventKind::Modified,
133 llvm::sys::path::filename(Path));
140 DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
""});
141 llvm_unreachable(
"Unknown FSEvent type.");
144 if (!Events.empty()) {
145 ctx->Receiver(Events,
false);
152 dispatch_queue_t Queue) {
156 CFMutableArrayRef PathsToWatch = [&]() {
157 CFMutableArrayRef PathsToWatch =
158 CFArrayCreateMutable(
nullptr, 0, &kCFTypeArrayCallBacks);
159 CFStringRef CfPathStr =
160 CFStringCreateWithBytes(
nullptr, (
const UInt8 *)Path.data(),
161 Path.size(), kCFStringEncodingUTF8,
false);
162 CFArrayAppendValue(PathsToWatch, CfPathStr);
163 CFRelease(CfPathStr);
167 FSEventStreamContext Context = [&]() {
168 std::string RealPath;
171 StringRef
P = llvm::Twine(Path).toNullTerminatedStringRef(Storage);
172 char Buffer[PATH_MAX];
173 if (::realpath(P.begin(), Buffer) !=
nullptr)
179 FSEventStreamContext Context;
181 Context.info =
new EventStreamContextData(std::move(RealPath), Receiver);
182 Context.retain =
nullptr;
183 Context.release = EventStreamContextData::dispose;
184 Context.copyDescription =
nullptr;
188 FSEventStreamRef Result = FSEventStreamCreate(
190 kFSEventStreamEventIdSinceNow, 0.0,
191 kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer);
192 CFRelease(PathsToWatch);
200 FSEventStreamStop(EventStream);
201 FSEventStreamInvalidate(EventStream);
202 FSEventStreamRelease(EventStream);
208 bool WaitForInitialSync) {
209 dispatch_queue_t Queue =
210 dispatch_queue_create(
"DirectoryWatcher", DISPATCH_QUEUE_SERIAL);
213 llvm::report_fatal_error(
214 "DirectoryWatcher::create can not accept an empty Path.");
217 assert(EventStream &&
"EventStream expected to be non-null");
219 std::unique_ptr<DirectoryWatcher> Result =
220 std::make_unique<DirectoryWatcherMac>(EventStream, Receiver, Path);
224 const std::string CopiedPath = Path;
231 FSEventStreamSetDispatchQueue(EventStream, Queue);
232 FSEventStreamStart(EventStream);
236 dispatch_release(Queue);
240 if (WaitForInitialSync) {
241 dispatch_sync(Queue, InitWork);
243 dispatch_async(Queue, InitWork);
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
std::vector< DirectoryWatcher::Event > getAsFileEvents(const std::vector< std::string > &Scan)
Create event with EventKind::Added for every element in Scan.
std::vector< std::string > scanDirectory(StringRef Path)
constexpr const FSEventStreamEventFlags StreamInvalidatingFlags
static llvm::Expected< std::unique_ptr< DirectoryWatcher > > create(llvm::StringRef Path, std::function< void(llvm::ArrayRef< DirectoryWatcher::Event > Events, bool IsInitial)> Receiver, bool WaitForInitialSync)
llvm fatal_error if
constexpr const FSEventStreamEventFlags ModifyingFileEvents
static void stopFSEventStream(FSEventStreamRef)
Optional< sys::fs::file_status > getFileStatus(StringRef Path)
Dataflow Directional Tag Classes.
static void eventStreamCallback(ConstFSEventStreamRef Stream, void *ClientCallBackInfo, size_t NumEvents, void *EventPaths, const FSEventStreamEventFlags EventFlags[], const FSEventStreamEventId EventIds[])
FSEventStreamRef createFSEventStream(StringRef Path, std::function< void(llvm::ArrayRef< DirectoryWatcher::Event >, bool)> Receiver, dispatch_queue_t Queue)
Provides notifications for file changes in a directory.