| // Copyright 2011 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef NINJA_DISK_INTERFACE_H_ |
| #define NINJA_DISK_INTERFACE_H_ |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "stat_cache.h" |
| #include "timestamp.h" |
| |
| /// Interface for reading files from disk. See DiskInterface for details. |
| /// This base offers the minimum interface needed just to read files. |
| struct FileReader { |
| virtual ~FileReader() {} |
| |
| /// Result of ReadFile. |
| enum Status { |
| Okay, |
| NotFound, |
| OtherError |
| }; |
| |
| /// Read and store in given string. On success, return Okay. |
| /// On error, return another Status and fill |err|. |
| virtual Status ReadFile(const std::string& path, std::string* contents, |
| std::string* err) = 0; |
| }; |
| |
| /// Interface for accessing the disk. |
| /// |
| /// Abstract so it can be mocked out for tests. The real implementation |
| /// is RealDiskInterface. |
| struct DiskInterface: public FileReader { |
| /// stat() a file, returning the mtime, or 0 if missing and -1 on |
| /// other errors. |
| virtual TimeStamp Stat(const std::string& path, std::string* err) const = 0; |
| |
| /// Create a directory, returning false on failure. |
| virtual bool MakeDir(const std::string& path) = 0; |
| |
| /// Create a file, with the specified name and contents |
| /// Returns true on success, false on failure |
| virtual bool WriteFile(const std::string& path, |
| const std::string& contents) = 0; |
| |
| /// Remove the file named @a path. It behaves like 'rm -f path' so no errors |
| /// are reported if it does not exists. |
| /// @returns 0 if the file has been removed, |
| /// 1 if the file does not exist, and |
| /// -1 if an error occurs. |
| virtual int RemoveFile(const std::string& path) = 0; |
| |
| /// Create all the parent directories for path; like mkdir -p |
| /// `basename path`. |
| bool MakeDirs(const std::string& path); |
| |
| /// Sync with real filesystem state, which is useful when a timestamp cache |
| /// is implemented using a directory-watching system service. Calling this |
| /// is only needed when calling Stat() on files that may have been modified |
| /// through a different interface (e.g. files modified by command |
| /// sub-processes). |
| virtual void Sync() {} |
| }; |
| |
| /// Implementation of FileReader that tracks opened file paths and their |
| /// timestamps. This is useful to quickly check whether one of the Ninja |
| /// input manifests has changed, since even for a large Fuchsia build, there |
| /// are only around 7000 files, compared to the 1,000,000 file paths in the |
| /// corresponding build graph. This class will probably become obsolete when a |
| /// proper filesystem-watching timestamp cache is implemented. |
| struct PathRecordingFileReader : public FileReader { |
| explicit PathRecordingFileReader(DiskInterface* disk_interface) |
| : disk_interface_(disk_interface) {} |
| |
| Status ReadFile(const std::string& path, std::string* contents, |
| std::string* err) override { |
| entries_.emplace_back(path, disk_interface_->Stat(path, err)); |
| return disk_interface_->ReadFile(path, contents, err); |
| } |
| |
| /// Drop all timestamps from the cache. |
| void Reset() { entries_.clear(); } |
| |
| /// Return true if any recorded path was modified. |
| bool CheckOutOfDate() const { |
| for (const auto& entry : entries_) { |
| /// Note that Ninja never follows symlinks by design, even for its input |
| /// manifest files, so this implementation just calls Stat() to avoid |
| /// changing its behavior. |
| if (disk_interface_->Stat(entry.path, nullptr) != entry.mtime) |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| struct Entry { |
| Entry(const std::string& path, TimeStamp mtime) |
| : path(path), mtime(mtime) {} |
| |
| std::string path; |
| TimeStamp mtime; |
| }; |
| |
| DiskInterface* disk_interface_; |
| std::vector<Entry> entries_; |
| }; |
| |
| /// Implementation of DiskInterface that actually hits the disk. |
| struct RealDiskInterface : public DiskInterface { |
| RealDiskInterface() = default; |
| virtual ~RealDiskInterface() = default; |
| TimeStamp Stat(const std::string& path, std::string* err) const override; |
| bool MakeDir(const std::string& path) override; |
| bool WriteFile(const std::string& path, const std::string& contents) override; |
| Status ReadFile(const std::string& path, std::string* contents, |
| std::string* err) override; |
| int RemoveFile(const std::string& path) override; |
| |
| /// Whether stat information can be cached. Only has an effect on Windows. |
| void AllowStatCache(bool allow); |
| |
| /// If StatCache is allowed, synchronize it with the current filesystem |
| /// state. This is used to drain pending events from filesystem monitoring |
| /// services like inotify on Linux. |
| void Sync() override; |
| |
| /// Remove all cached stat information, if any. |
| void FlushCache(); |
| |
| private: |
| StatCache stat_cache_; |
| }; |
| |
| #endif // NINJA_DISK_INTERFACE_H_ |