| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 | 
							- /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 
-    file Copyright.txt or https://cmake.org/licensing for details.  */
 
- #include "cmFileMonitor.h"
 
- #include <cmsys/SystemTools.hxx>
 
- #include <cassert>
 
- #include <iostream>
 
- #include <set>
 
- #include <unordered_map>
 
- namespace {
 
- void on_directory_change(uv_fs_event_t* handle, const char* filename,
 
-                          int events, int status);
 
- void on_fs_close(uv_handle_t* handle);
 
- } // namespace
 
- class cmIBaseWatcher
 
- {
 
- public:
 
-   cmIBaseWatcher() = default;
 
-   virtual ~cmIBaseWatcher() = default;
 
-   virtual void Trigger(const std::string& pathSegment, int events,
 
-                        int status) const = 0;
 
-   virtual std::string Path() const = 0;
 
-   virtual uv_loop_t* Loop() const = 0;
 
-   virtual void StartWatching() = 0;
 
-   virtual void StopWatching() = 0;
 
-   virtual std::vector<std::string> WatchedFiles() const = 0;
 
-   virtual std::vector<std::string> WatchedDirectories() const = 0;
 
- };
 
- class cmVirtualDirectoryWatcher : public cmIBaseWatcher
 
- {
 
- public:
 
-   ~cmVirtualDirectoryWatcher()
 
-   {
 
-     for (auto i : this->Children) {
 
-       delete i.second;
 
-     }
 
-   }
 
-   cmIBaseWatcher* Find(const std::string& ps)
 
-   {
 
-     const auto i = this->Children.find(ps);
 
-     return (i == this->Children.end()) ? nullptr : i->second;
 
-   }
 
-   void Trigger(const std::string& pathSegment, int events,
 
-                int status) const final
 
-   {
 
-     if (pathSegment.empty()) {
 
-       for (const auto& i : this->Children) {
 
-         i.second->Trigger(std::string(), events, status);
 
-       }
 
-     } else {
 
-       const auto i = this->Children.find(pathSegment);
 
-       if (i != this->Children.end()) {
 
-         i->second->Trigger(std::string(), events, status);
 
-       }
 
-     }
 
-   }
 
-   void StartWatching() override
 
-   {
 
-     for (const auto& i : this->Children) {
 
-       i.second->StartWatching();
 
-     }
 
-   }
 
-   void StopWatching() override
 
-   {
 
-     for (const auto& i : this->Children) {
 
-       i.second->StopWatching();
 
-     }
 
-   }
 
-   std::vector<std::string> WatchedFiles() const final
 
-   {
 
-     std::vector<std::string> result;
 
-     for (const auto& i : this->Children) {
 
-       for (const auto& j : i.second->WatchedFiles()) {
 
-         result.push_back(j);
 
-       }
 
-     }
 
-     return result;
 
-   }
 
-   std::vector<std::string> WatchedDirectories() const override
 
-   {
 
-     std::vector<std::string> result;
 
-     for (const auto& i : this->Children) {
 
-       for (const auto& j : i.second->WatchedDirectories()) {
 
-         result.push_back(j);
 
-       }
 
-     }
 
-     return result;
 
-   }
 
-   void Reset()
 
-   {
 
-     for (auto c : this->Children) {
 
-       delete c.second;
 
-     }
 
-     this->Children.clear();
 
-   }
 
-   void AddChildWatcher(const std::string& ps, cmIBaseWatcher* watcher)
 
-   {
 
-     assert(!ps.empty());
 
-     assert(this->Children.find(ps) == this->Children.end());
 
-     assert(watcher);
 
-     this->Children.emplace(std::make_pair(ps, watcher));
 
-   }
 
- private:
 
-   std::unordered_map<std::string, cmIBaseWatcher*> Children; // owned!
 
- };
 
- // Root of all the different (on windows!) root directories:
 
- class cmRootWatcher : public cmVirtualDirectoryWatcher
 
- {
 
- public:
 
-   cmRootWatcher(uv_loop_t* loop)
 
-     : mLoop(loop)
 
-   {
 
-     assert(loop);
 
-   }
 
-   std::string Path() const final
 
-   {
 
-     assert(false);
 
-     return std::string();
 
-   }
 
-   uv_loop_t* Loop() const final { return this->mLoop; }
 
- private:
 
-   uv_loop_t* const mLoop; // no ownership!
 
- };
 
- // Real directories:
 
- class cmRealDirectoryWatcher : public cmVirtualDirectoryWatcher
 
- {
 
- public:
 
-   cmRealDirectoryWatcher(cmVirtualDirectoryWatcher* p, const std::string& ps)
 
-     : Parent(p)
 
-     , PathSegment(ps)
 
-   {
 
-     assert(p);
 
-     assert(!ps.empty());
 
-     p->AddChildWatcher(ps, this);
 
-   }
 
-   ~cmRealDirectoryWatcher()
 
-   {
 
-     // Handle is freed via uv_handle_close callback!
 
-   }
 
-   void StartWatching() final
 
-   {
 
-     if (!this->Handle) {
 
-       this->Handle = new uv_fs_event_t;
 
-       uv_fs_event_init(this->Loop(), this->Handle);
 
-       this->Handle->data = this;
 
-       uv_fs_event_start(this->Handle, &on_directory_change, Path().c_str(), 0);
 
-     }
 
-     cmVirtualDirectoryWatcher::StartWatching();
 
-   }
 
-   void StopWatching() final
 
-   {
 
-     if (this->Handle) {
 
-       uv_fs_event_stop(this->Handle);
 
-       uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close);
 
-       this->Handle = nullptr;
 
-     }
 
-     cmVirtualDirectoryWatcher::StopWatching();
 
-   }
 
-   uv_loop_t* Loop() const final { return this->Parent->Loop(); }
 
-   std::vector<std::string> WatchedDirectories() const override
 
-   {
 
-     std::vector<std::string> result = { Path() };
 
-     for (const auto& j : cmVirtualDirectoryWatcher::WatchedDirectories()) {
 
-       result.push_back(j);
 
-     }
 
-     return result;
 
-   }
 
- protected:
 
-   cmVirtualDirectoryWatcher* const Parent;
 
-   const std::string PathSegment;
 
- private:
 
-   uv_fs_event_t* Handle = nullptr; // owner!
 
- };
 
- // Root directories:
 
- class cmRootDirectoryWatcher : public cmRealDirectoryWatcher
 
- {
 
- public:
 
-   cmRootDirectoryWatcher(cmRootWatcher* p, const std::string& ps)
 
-     : cmRealDirectoryWatcher(p, ps)
 
-   {
 
-   }
 
-   std::string Path() const final { return this->PathSegment; }
 
- };
 
- // Normal directories below root:
 
- class cmDirectoryWatcher : public cmRealDirectoryWatcher
 
- {
 
- public:
 
-   cmDirectoryWatcher(cmRealDirectoryWatcher* p, const std::string& ps)
 
-     : cmRealDirectoryWatcher(p, ps)
 
-   {
 
-   }
 
-   std::string Path() const final
 
-   {
 
-     return this->Parent->Path() + this->PathSegment + "/";
 
-   }
 
- };
 
- class cmFileWatcher : public cmIBaseWatcher
 
- {
 
- public:
 
-   cmFileWatcher(cmRealDirectoryWatcher* p, const std::string& ps,
 
-                 cmFileMonitor::Callback cb)
 
-     : Parent(p)
 
-     , PathSegment(ps)
 
-     , CbList({ cb })
 
-   {
 
-     assert(p);
 
-     assert(!ps.empty());
 
-     p->AddChildWatcher(ps, this);
 
-   }
 
-   void StartWatching() final {}
 
-   void StopWatching() final {}
 
-   void AppendCallback(cmFileMonitor::Callback cb) { CbList.push_back(cb); }
 
-   std::string Path() const final
 
-   {
 
-     return this->Parent->Path() + this->PathSegment;
 
-   }
 
-   std::vector<std::string> WatchedDirectories() const final { return {}; }
 
-   std::vector<std::string> WatchedFiles() const final
 
-   {
 
-     return { this->Path() };
 
-   }
 
-   void Trigger(const std::string& ps, int events, int status) const final
 
-   {
 
-     assert(ps.empty());
 
-     assert(status == 0);
 
-     static_cast<void>(ps);
 
-     const std::string path = this->Path();
 
-     for (const auto& cb : this->CbList) {
 
-       cb(path, events, status);
 
-     }
 
-   }
 
-   uv_loop_t* Loop() const final { return this->Parent->Loop(); }
 
- private:
 
-   cmRealDirectoryWatcher* Parent;
 
-   const std::string PathSegment;
 
-   std::vector<cmFileMonitor::Callback> CbList;
 
- };
 
- namespace {
 
- void on_directory_change(uv_fs_event_t* handle, const char* filename,
 
-                          int events, int status)
 
- {
 
-   const cmIBaseWatcher* const watcher =
 
-     static_cast<const cmIBaseWatcher*>(handle->data);
 
-   const std::string pathSegment(filename);
 
-   watcher->Trigger(pathSegment, events, status);
 
- }
 
- void on_fs_close(uv_handle_t* handle)
 
- {
 
-   delete reinterpret_cast<uv_fs_event_t*>(handle);
 
- }
 
- } // namespace
 
- cmFileMonitor::cmFileMonitor(uv_loop_t* l)
 
-   : Root(new cmRootWatcher(l))
 
- {
 
- }
 
- cmFileMonitor::~cmFileMonitor()
 
- {
 
-   delete this->Root;
 
- }
 
- void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths,
 
-                                  Callback cb)
 
- {
 
-   for (const auto& p : paths) {
 
-     std::vector<std::string> pathSegments;
 
-     cmsys::SystemTools::SplitPath(p, pathSegments, true);
 
-     const size_t segmentCount = pathSegments.size();
 
-     if (segmentCount < 2) { // Expect at least rootdir and filename
 
-       continue;
 
-     }
 
-     cmVirtualDirectoryWatcher* currentWatcher = this->Root;
 
-     for (size_t i = 0; i < segmentCount; ++i) {
 
-       assert(currentWatcher);
 
-       const bool fileSegment = (i == segmentCount - 1);
 
-       const bool rootSegment = (i == 0);
 
-       assert(
 
-         !(fileSegment &&
 
-           rootSegment)); // Can not be both filename and root part of the path!
 
-       const std::string& currentSegment = pathSegments[i];
 
-       cmIBaseWatcher* nextWatcher = currentWatcher->Find(currentSegment);
 
-       if (!nextWatcher) {
 
-         if (rootSegment) { // Root part
 
-           assert(currentWatcher == this->Root);
 
-           nextWatcher = new cmRootDirectoryWatcher(this->Root, currentSegment);
 
-           assert(currentWatcher->Find(currentSegment) == nextWatcher);
 
-         } else if (fileSegment) { // File part
 
-           assert(currentWatcher != this->Root);
 
-           nextWatcher = new cmFileWatcher(
 
-             dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher),
 
-             currentSegment, cb);
 
-           assert(currentWatcher->Find(currentSegment) == nextWatcher);
 
-         } else { // Any normal directory in between
 
-           nextWatcher = new cmDirectoryWatcher(
 
-             dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher),
 
-             currentSegment);
 
-           assert(currentWatcher->Find(currentSegment) == nextWatcher);
 
-         }
 
-       } else {
 
-         if (fileSegment) {
 
-           auto filePtr = dynamic_cast<cmFileWatcher*>(nextWatcher);
 
-           assert(filePtr);
 
-           filePtr->AppendCallback(cb);
 
-           continue;
 
-         }
 
-       }
 
-       currentWatcher = dynamic_cast<cmVirtualDirectoryWatcher*>(nextWatcher);
 
-     }
 
-   }
 
-   this->Root->StartWatching();
 
- }
 
- void cmFileMonitor::StopMonitoring()
 
- {
 
-   this->Root->StopWatching();
 
-   this->Root->Reset();
 
- }
 
- std::vector<std::string> cmFileMonitor::WatchedFiles() const
 
- {
 
-   std::vector<std::string> result;
 
-   if (this->Root) {
 
-     result = this->Root->WatchedFiles();
 
-   }
 
-   return result;
 
- }
 
- std::vector<std::string> cmFileMonitor::WatchedDirectories() const
 
- {
 
-   std::vector<std::string> result;
 
-   if (this->Root) {
 
-     result = this->Root->WatchedDirectories();
 
-   }
 
-   return result;
 
- }
 
 
  |