blob: 27960aa04a2edada5f6e7dff08bbcb3190628a0a [file] [log] [blame]
// 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_