/// \note The 'end' iterator is directory_iterator().
virtual directory_iterator dir_begin(const Twine &Dir,
std::error_code &EC) = 0;
+
+ /// Set the working directory. This will affect all following operations on
+ /// this file system and may propagate down for nested file systems.
+ virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0;
+ /// Get the working directory of this file system.
+ virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0;
+
+ /// Make \a Path an absolute path.
+ ///
+ /// Makes \a Path absolute using the current directory if it is not already.
+ /// An empty \a Path will result in the current directory.
+ ///
+ /// /absolute/path => /absolute/path
+ /// relative/../path => <current-directory>/relative/../path
+ ///
+ /// \param Path A path that is modified to be an absolute path.
+ /// \returns success if \a path has been made absolute, otherwise a
+ /// platform-specific error_code.
+ std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const;
};
/// \brief Gets an \p vfs::FileSystem for the 'real' file system, as seen by
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
typedef FileSystemList::reverse_iterator iterator;
/// An in-memory file system.
class InMemoryFileSystem : public FileSystem {
std::unique_ptr<detail::InMemoryDirectory> Root;
+ std::string WorkingDirectory;
public:
InMemoryFileSystem();
llvm::ErrorOr<std::unique_ptr<File>>
openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+ return WorkingDirectory;
+ }
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+ WorkingDirectory = Path.str();
+ return std::error_code();
+ }
};
/// \brief Get a globally unique ID for a virtual file or directory.
return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);
}
+std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
+ auto WorkingDir = getCurrentWorkingDirectory();
+ if (!WorkingDir)
+ return WorkingDir.getError();
+
+ return llvm::sys::fs::make_absolute(WorkingDir.get(), Path);
+}
+
//===-----------------------------------------------------------------------===/
// RealFileSystem implementation
//===-----------------------------------------------------------------------===/
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override;
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
};
} // end anonymous namespace
return std::unique_ptr<File>(new RealFile(FD, Name.str()));
}
+llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const {
+ SmallString<256> Dir;
+ if (std::error_code EC = llvm::sys::fs::current_path(Dir))
+ return EC;
+ return Dir.str().str();
+}
+
+std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+ // FIXME: chdir is thread hostile; on the other hand, creating the same
+ // behavior as chdir is complex: chdir resolves the path once, thus
+ // guaranteeing that all subsequent relative path operations work
+ // on the same path the original chdir resulted in. This makes a
+ // difference for example on network filesystems, where symlinks might be
+ // switched during runtime of the tool. Fixing this depends on having a
+ // file system abstraction that allows openat() style interactions.
+ SmallString<256> Storage;
+ StringRef Dir = Path.toNullTerminatedStringRef(Storage);
+ if (int Err = ::chdir(Dir.data()))
+ return std::error_code(Err, std::generic_category());
+ return std::error_code();
+}
+
IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() {
static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem();
return FS;
// OverlayFileSystem implementation
//===-----------------------------------------------------------------------===/
OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) {
- pushOverlay(BaseFS);
+ FSList.push_back(BaseFS);
}
void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {
FSList.push_back(FS);
+ // Synchronize added file systems by duplicating the working directory from
+ // the first one in the list.
+ FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());
}
ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) {
return make_error_code(llvm::errc::no_such_file_or_directory);
}
+llvm::ErrorOr<std::string>
+OverlayFileSystem::getCurrentWorkingDirectory() const {
+ // All file systems are synchronized, just take the first working directory.
+ return FSList.front()->getCurrentWorkingDirectory();
+}
+std::error_code
+OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) {
+ for (auto &FS : FSList)
+ if (std::error_code EC = FS->setCurrentWorkingDirectory(Path))
+ return EC;
+ return std::error_code();
+}
+
clang::vfs::detail::DirIterImpl::~DirIterImpl() { }
namespace {
P.toVector(Path);
// Fix up relative paths. This just prepends the current working directory.
- std::error_code EC = sys::fs::make_absolute(Path);
+ std::error_code EC = makeAbsolute(Path);
assert(!EC);
(void)EC;
}
static ErrorOr<detail::InMemoryNode *>
-lookupInMemoryNode(detail::InMemoryDirectory *Dir, const Twine &P) {
+lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir,
+ const Twine &P) {
SmallString<128> Path;
P.toVector(Path);
// Fix up relative paths. This just prepends the current working directory.
- std::error_code EC = sys::fs::make_absolute(Path);
+ std::error_code EC = FS.makeAbsolute(Path);
assert(!EC);
(void)EC;
}
llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {
- auto Node = lookupInMemoryNode(Root.get(), Path);
+ auto Node = lookupInMemoryNode(*this, Root.get(), Path);
if (Node)
return (*Node)->getStatus();
return Node.getError();
llvm::ErrorOr<std::unique_ptr<File>>
InMemoryFileSystem::openFileForRead(const Twine &Path) {
- auto Node = lookupInMemoryNode(Root.get(), Path);
+ auto Node = lookupInMemoryNode(*this, Root.get(), Path);
if (!Node)
return Node.getError();
directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir,
std::error_code &EC) {
- auto Node = lookupInMemoryNode(Root.get(), Dir);
+ auto Node = lookupInMemoryNode(*this, Root.get(), Dir);
if (!Node) {
EC = Node.getError();
return directory_iterator(std::make_shared<InMemoryDirIterator>());
ErrorOr<Status> status(const Twine &Path) override;
ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+ return ExternalFS->getCurrentWorkingDirectory();
+ }
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+ return ExternalFS->setCurrentWorkingDirectory(Path);
+ }
+
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{
ErrorOr<Entry *> E = lookupPath(Dir);
if (!E) {
Path_.toVector(Path);
// Handle relative paths
- if (std::error_code EC = sys::fs::make_absolute(Path))
+ if (std::error_code EC = makeAbsolute(Path))
return EC;
if (Path.empty())