cmFileMonitor.cxx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmFileMonitor.h"
  4. #include "cmAlgorithms.h"
  5. #include "cmsys/SystemTools.hxx"
  6. #include <cassert>
  7. #include <stddef.h>
  8. #include <unordered_map>
  9. #include <utility>
  10. namespace {
  11. void on_directory_change(uv_fs_event_t* handle, const char* filename,
  12. int events, int status);
  13. void on_fs_close(uv_handle_t* handle);
  14. } // namespace
  15. class cmIBaseWatcher
  16. {
  17. public:
  18. cmIBaseWatcher() = default;
  19. virtual ~cmIBaseWatcher() = default;
  20. virtual void Trigger(const std::string& pathSegment, int events,
  21. int status) const = 0;
  22. virtual std::string Path() const = 0;
  23. virtual uv_loop_t* Loop() const = 0;
  24. virtual void StartWatching() = 0;
  25. virtual void StopWatching() = 0;
  26. virtual std::vector<std::string> WatchedFiles() const = 0;
  27. virtual std::vector<std::string> WatchedDirectories() const = 0;
  28. };
  29. class cmVirtualDirectoryWatcher : public cmIBaseWatcher
  30. {
  31. public:
  32. ~cmVirtualDirectoryWatcher() override { cmDeleteAll(this->Children); }
  33. cmIBaseWatcher* Find(const std::string& ps)
  34. {
  35. const auto i = this->Children.find(ps);
  36. return (i == this->Children.end()) ? nullptr : i->second;
  37. }
  38. void Trigger(const std::string& pathSegment, int events,
  39. int status) const final
  40. {
  41. if (pathSegment.empty()) {
  42. for (auto const& child : this->Children) {
  43. child.second->Trigger(std::string(), events, status);
  44. }
  45. } else {
  46. const auto i = this->Children.find(pathSegment);
  47. if (i != this->Children.end()) {
  48. i->second->Trigger(std::string(), events, status);
  49. }
  50. }
  51. }
  52. void StartWatching() override
  53. {
  54. for (auto const& child : this->Children) {
  55. child.second->StartWatching();
  56. }
  57. }
  58. void StopWatching() override
  59. {
  60. for (auto const& child : this->Children) {
  61. child.second->StopWatching();
  62. }
  63. }
  64. std::vector<std::string> WatchedFiles() const final
  65. {
  66. std::vector<std::string> result;
  67. for (auto const& child : this->Children) {
  68. for (std::string const& f : child.second->WatchedFiles()) {
  69. result.push_back(f);
  70. }
  71. }
  72. return result;
  73. }
  74. std::vector<std::string> WatchedDirectories() const override
  75. {
  76. std::vector<std::string> result;
  77. for (auto const& child : this->Children) {
  78. for (std::string const& dir : child.second->WatchedDirectories()) {
  79. result.push_back(dir);
  80. }
  81. }
  82. return result;
  83. }
  84. void Reset()
  85. {
  86. cmDeleteAll(this->Children);
  87. this->Children.clear();
  88. }
  89. void AddChildWatcher(const std::string& ps, cmIBaseWatcher* watcher)
  90. {
  91. assert(!ps.empty());
  92. assert(this->Children.find(ps) == this->Children.end());
  93. assert(watcher);
  94. this->Children.emplace(std::make_pair(ps, watcher));
  95. }
  96. private:
  97. std::unordered_map<std::string, cmIBaseWatcher*> Children; // owned!
  98. };
  99. // Root of all the different (on windows!) root directories:
  100. class cmRootWatcher : public cmVirtualDirectoryWatcher
  101. {
  102. public:
  103. cmRootWatcher(uv_loop_t* loop)
  104. : mLoop(loop)
  105. {
  106. assert(loop);
  107. }
  108. std::string Path() const final
  109. {
  110. assert(false);
  111. return std::string();
  112. }
  113. uv_loop_t* Loop() const final { return this->mLoop; }
  114. private:
  115. uv_loop_t* const mLoop; // no ownership!
  116. };
  117. // Real directories:
  118. class cmRealDirectoryWatcher : public cmVirtualDirectoryWatcher
  119. {
  120. public:
  121. cmRealDirectoryWatcher(cmVirtualDirectoryWatcher* p, const std::string& ps)
  122. : Parent(p)
  123. , PathSegment(ps)
  124. {
  125. assert(p);
  126. assert(!ps.empty());
  127. p->AddChildWatcher(ps, this);
  128. }
  129. ~cmRealDirectoryWatcher() override
  130. {
  131. // Handle is freed via uv_handle_close callback!
  132. }
  133. void StartWatching() final
  134. {
  135. if (!this->Handle) {
  136. this->Handle = new uv_fs_event_t;
  137. uv_fs_event_init(this->Loop(), this->Handle);
  138. this->Handle->data = this;
  139. uv_fs_event_start(this->Handle, &on_directory_change, Path().c_str(), 0);
  140. }
  141. cmVirtualDirectoryWatcher::StartWatching();
  142. }
  143. void StopWatching() final
  144. {
  145. if (this->Handle) {
  146. uv_fs_event_stop(this->Handle);
  147. if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(this->Handle))) {
  148. uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close);
  149. }
  150. this->Handle = nullptr;
  151. }
  152. cmVirtualDirectoryWatcher::StopWatching();
  153. }
  154. uv_loop_t* Loop() const final { return this->Parent->Loop(); }
  155. std::vector<std::string> WatchedDirectories() const override
  156. {
  157. std::vector<std::string> result = { Path() };
  158. for (std::string const& dir :
  159. cmVirtualDirectoryWatcher::WatchedDirectories()) {
  160. result.push_back(dir);
  161. }
  162. return result;
  163. }
  164. protected:
  165. cmVirtualDirectoryWatcher* const Parent;
  166. const std::string PathSegment;
  167. private:
  168. uv_fs_event_t* Handle = nullptr; // owner!
  169. };
  170. // Root directories:
  171. class cmRootDirectoryWatcher : public cmRealDirectoryWatcher
  172. {
  173. public:
  174. cmRootDirectoryWatcher(cmRootWatcher* p, const std::string& ps)
  175. : cmRealDirectoryWatcher(p, ps)
  176. {
  177. }
  178. std::string Path() const final { return this->PathSegment; }
  179. };
  180. // Normal directories below root:
  181. class cmDirectoryWatcher : public cmRealDirectoryWatcher
  182. {
  183. public:
  184. cmDirectoryWatcher(cmRealDirectoryWatcher* p, const std::string& ps)
  185. : cmRealDirectoryWatcher(p, ps)
  186. {
  187. }
  188. std::string Path() const final
  189. {
  190. return this->Parent->Path() + this->PathSegment + "/";
  191. }
  192. };
  193. class cmFileWatcher : public cmIBaseWatcher
  194. {
  195. public:
  196. cmFileWatcher(cmRealDirectoryWatcher* p, const std::string& ps,
  197. cmFileMonitor::Callback cb)
  198. : Parent(p)
  199. , PathSegment(ps)
  200. , CbList({ std::move(cb) })
  201. {
  202. assert(p);
  203. assert(!ps.empty());
  204. p->AddChildWatcher(ps, this);
  205. }
  206. void StartWatching() final {}
  207. void StopWatching() final {}
  208. void AppendCallback(cmFileMonitor::Callback const& cb)
  209. {
  210. this->CbList.push_back(cb);
  211. }
  212. std::string Path() const final
  213. {
  214. return this->Parent->Path() + this->PathSegment;
  215. }
  216. std::vector<std::string> WatchedDirectories() const final { return {}; }
  217. std::vector<std::string> WatchedFiles() const final
  218. {
  219. return { this->Path() };
  220. }
  221. void Trigger(const std::string& ps, int events, int status) const final
  222. {
  223. assert(ps.empty());
  224. assert(status == 0);
  225. static_cast<void>(ps);
  226. const std::string path = this->Path();
  227. for (cmFileMonitor::Callback const& cb : this->CbList) {
  228. cb(path, events, status);
  229. }
  230. }
  231. uv_loop_t* Loop() const final { return this->Parent->Loop(); }
  232. private:
  233. cmRealDirectoryWatcher* Parent;
  234. const std::string PathSegment;
  235. std::vector<cmFileMonitor::Callback> CbList;
  236. };
  237. namespace {
  238. void on_directory_change(uv_fs_event_t* handle, const char* filename,
  239. int events, int status)
  240. {
  241. const cmIBaseWatcher* const watcher =
  242. static_cast<const cmIBaseWatcher*>(handle->data);
  243. const std::string pathSegment(filename ? filename : "");
  244. watcher->Trigger(pathSegment, events, status);
  245. }
  246. void on_fs_close(uv_handle_t* handle)
  247. {
  248. delete reinterpret_cast<uv_fs_event_t*>(handle);
  249. }
  250. } // namespace
  251. cmFileMonitor::cmFileMonitor(uv_loop_t* l)
  252. : Root(new cmRootWatcher(l))
  253. {
  254. }
  255. cmFileMonitor::~cmFileMonitor()
  256. {
  257. delete this->Root;
  258. }
  259. void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths,
  260. Callback const& cb)
  261. {
  262. for (std::string const& p : paths) {
  263. std::vector<std::string> pathSegments;
  264. cmsys::SystemTools::SplitPath(p, pathSegments, true);
  265. const size_t segmentCount = pathSegments.size();
  266. if (segmentCount < 2) { // Expect at least rootdir and filename
  267. continue;
  268. }
  269. cmVirtualDirectoryWatcher* currentWatcher = this->Root;
  270. for (size_t i = 0; i < segmentCount; ++i) {
  271. assert(currentWatcher);
  272. const bool fileSegment = (i == segmentCount - 1);
  273. const bool rootSegment = (i == 0);
  274. assert(
  275. !(fileSegment &&
  276. rootSegment)); // Can not be both filename and root part of the path!
  277. const std::string& currentSegment = pathSegments[i];
  278. if (currentSegment.empty()) {
  279. continue;
  280. }
  281. cmIBaseWatcher* nextWatcher = currentWatcher->Find(currentSegment);
  282. if (!nextWatcher) {
  283. if (rootSegment) { // Root part
  284. assert(currentWatcher == this->Root);
  285. nextWatcher = new cmRootDirectoryWatcher(this->Root, currentSegment);
  286. assert(currentWatcher->Find(currentSegment) == nextWatcher);
  287. } else if (fileSegment) { // File part
  288. assert(currentWatcher != this->Root);
  289. nextWatcher = new cmFileWatcher(
  290. dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher),
  291. currentSegment, cb);
  292. assert(currentWatcher->Find(currentSegment) == nextWatcher);
  293. } else { // Any normal directory in between
  294. nextWatcher = new cmDirectoryWatcher(
  295. dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher),
  296. currentSegment);
  297. assert(currentWatcher->Find(currentSegment) == nextWatcher);
  298. }
  299. } else {
  300. if (fileSegment) {
  301. auto filePtr = dynamic_cast<cmFileWatcher*>(nextWatcher);
  302. assert(filePtr);
  303. filePtr->AppendCallback(cb);
  304. continue;
  305. }
  306. }
  307. currentWatcher = dynamic_cast<cmVirtualDirectoryWatcher*>(nextWatcher);
  308. }
  309. }
  310. this->Root->StartWatching();
  311. }
  312. void cmFileMonitor::StopMonitoring()
  313. {
  314. this->Root->StopWatching();
  315. this->Root->Reset();
  316. }
  317. std::vector<std::string> cmFileMonitor::WatchedFiles() const
  318. {
  319. std::vector<std::string> result;
  320. if (this->Root) {
  321. result = this->Root->WatchedFiles();
  322. }
  323. return result;
  324. }
  325. std::vector<std::string> cmFileMonitor::WatchedDirectories() const
  326. {
  327. std::vector<std::string> result;
  328. if (this->Root) {
  329. result = this->Root->WatchedDirectories();
  330. }
  331. return result;
  332. }