Index: lldb/trunk/include/lldb/Host/FileSystem.h =================================================================== --- lldb/trunk/include/lldb/Host/FileSystem.h +++ lldb/trunk/include/lldb/Host/FileSystem.h @@ -12,7 +12,10 @@ #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Status.h" + +#include "llvm/ADT/Optional.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/VirtualFileSystem.h" #include "lldb/lldb-types.h" @@ -26,17 +29,101 @@ static const char *DEV_NULL; static const char *PATH_CONVERSION_ERROR; - static Status Symlink(const FileSpec &src, const FileSpec &dst); - static Status Readlink(const FileSpec &src, FileSpec &dst); + FileSystem() : m_fs(llvm::vfs::getRealFileSystem()) {} + FileSystem(llvm::IntrusiveRefCntPtr fs) : m_fs(fs) {} + + static FileSystem &Instance(); + + static void Initialize(); + static void Initialize(llvm::IntrusiveRefCntPtr fs); + static void Terminate(); + + Status Symlink(const FileSpec &src, const FileSpec &dst); + Status Readlink(const FileSpec &src, FileSpec &dst); - static Status ResolveSymbolicLink(const FileSpec &src, FileSpec &dst); + Status ResolveSymbolicLink(const FileSpec &src, FileSpec &dst); /// Wraps ::fopen in a platform-independent way. Once opened, FILEs can be /// manipulated and closed with the normal ::fread, ::fclose, etc. functions. - static FILE *Fopen(const char *path, const char *mode); + FILE *Fopen(const char *path, const char *mode); - static llvm::sys::TimePoint<> GetModificationTime(const FileSpec &file_spec); + /// Returns the modification time of the given file. + /// @{ + llvm::sys::TimePoint<> GetModificationTime(const FileSpec &file_spec) const; + llvm::sys::TimePoint<> GetModificationTime(const llvm::Twine &path) const; + /// @} + + /// Returns the on-disk size of the given file in bytes. + /// @{ + uint64_t GetByteSize(const FileSpec &file_spec) const; + uint64_t GetByteSize(const llvm::Twine &path) const; + /// @} + + /// Return the current permissions of the given file. + /// + /// Returns a bitmask for the current permissions of the file (zero or more + /// of the permission bits defined in File::Permissions). + /// @{ + uint32_t GetPermissions(const FileSpec &file_spec) const; + uint32_t GetPermissions(const llvm::Twine &path) const; + /// @} + + /// Returns whether the given file exists. + /// @{ + bool Exists(const FileSpec &file_spec) const; + bool Exists(const llvm::Twine &path) const; + /// @} + + /// Returns whether the given file is readable. + /// @{ + bool Readable(const FileSpec &file_spec) const; + bool Readable(const llvm::Twine &path) const; + /// @} + + /// Make the given file path absolute. + /// @{ + std::error_code MakeAbsolute(llvm::SmallVectorImpl &path) const; + std::error_code MakeAbsolute(FileSpec &file_spec) const; + /// @} + + /// Resolve path to make it canonical. + /// @{ + void Resolve(llvm::SmallVectorImpl &path); + void Resolve(FileSpec &file_spec); + /// @} + + enum EnumerateDirectoryResult { + /// Enumerate next entry in the current directory. + eEnumerateDirectoryResultNext, + /// Recurse into the current entry if it is a directory or symlink, or next + /// if not. + eEnumerateDirectoryResultEnter, + /// Stop directory enumerations at any level. + eEnumerateDirectoryResultQuit + }; + + typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType)( + void *baton, llvm::sys::fs::file_type file_type, llvm::StringRef); + + typedef std::function + DirectoryCallback; + + void EnumerateDirectory(llvm::Twine path, bool find_directories, + bool find_files, bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton); + + std::error_code GetRealPath(const llvm::Twine &path, + llvm::SmallVectorImpl &output) const; + +protected: + void SetFileSystem(llvm::IntrusiveRefCntPtr fs); + +private: + static llvm::Optional &InstanceImpl(); + llvm::IntrusiveRefCntPtr m_fs; }; -} +} // namespace lldb_private #endif Index: lldb/trunk/include/lldb/Utility/FileSpec.h =================================================================== --- lldb/trunk/include/lldb/Utility/FileSpec.h +++ lldb/trunk/include/lldb/Utility/FileSpec.h @@ -375,6 +375,9 @@ //------------------------------------------------------------------ bool IsAbsolute() const; + /// Temporary helper for FileSystem change. + void SetPath(llvm::StringRef p) { SetFile(p, false); } + //------------------------------------------------------------------ /// Extract the full path to the file. /// Index: lldb/trunk/source/Core/Disassembler.cpp =================================================================== --- lldb/trunk/source/Core/Disassembler.cpp +++ lldb/trunk/source/Core/Disassembler.cpp @@ -934,7 +934,7 @@ out_stream->Printf("Instruction::TestEmulation: Missing file_name."); return false; } - FILE *test_file = FileSystem::Fopen(file_name, "r"); + FILE *test_file = FileSystem::Instance().Fopen(file_name, "r"); if (!test_file) { out_stream->Printf( "Instruction::TestEmulation: Attempt to open test file failed."); Index: lldb/trunk/source/Core/Module.cpp =================================================================== --- lldb/trunk/source/Core/Module.cpp +++ lldb/trunk/source/Core/Module.cpp @@ -171,10 +171,10 @@ } if (module_spec.GetFileSpec()) - m_mod_time = FileSystem::GetModificationTime(module_spec.GetFileSpec()); + m_mod_time = FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec()); else if (matching_module_spec.GetFileSpec()) m_mod_time = - FileSystem::GetModificationTime(matching_module_spec.GetFileSpec()); + FileSystem::Instance().GetModificationTime(matching_module_spec.GetFileSpec()); // Copy the architecture from the actual spec if we got one back, else use // the one that was specified @@ -219,7 +219,7 @@ Module::Module(const FileSpec &file_spec, const ArchSpec &arch, const ConstString *object_name, lldb::offset_t object_offset, const llvm::sys::TimePoint<> &object_mod_time) - : m_mod_time(FileSystem::GetModificationTime(file_spec)), m_arch(arch), + : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch), m_file(file_spec), m_object_offset(object_offset), m_object_mod_time(object_mod_time), m_file_has_changed(false), m_first_file_changed_log(false) { @@ -1062,7 +1062,7 @@ // Container objects whose paths do not specify a file directly can call this // function to correct the file and object names. m_file = file; - m_mod_time = FileSystem::GetModificationTime(file); + m_mod_time = FileSystem::Instance().GetModificationTime(file); m_object_name = object_name; } @@ -1125,7 +1125,7 @@ bool Module::FileHasChanged() const { if (!m_file_has_changed) m_file_has_changed = - (FileSystem::GetModificationTime(m_file) != m_mod_time); + (FileSystem::Instance().GetModificationTime(m_file) != m_mod_time); return m_file_has_changed; } Index: lldb/trunk/source/Core/ModuleList.cpp =================================================================== --- lldb/trunk/source/Core/ModuleList.cpp +++ lldb/trunk/source/Core/ModuleList.cpp @@ -952,7 +952,7 @@ // If we didn't have a UUID in mind when looking for the object file, // then we should make sure the modification time hasn't changed! if (platform_module_spec.GetUUIDPtr() == nullptr) { - auto file_spec_mod_time = FileSystem::GetModificationTime( + auto file_spec_mod_time = FileSystem::Instance().GetModificationTime( located_binary_modulespec.GetFileSpec()); if (file_spec_mod_time != llvm::sys::TimePoint<>()) { if (file_spec_mod_time != module_sp->GetModificationTime()) { Index: lldb/trunk/source/Core/SourceManager.cpp =================================================================== --- lldb/trunk/source/Core/SourceManager.cpp +++ lldb/trunk/source/Core/SourceManager.cpp @@ -370,14 +370,14 @@ SourceManager::File::File(const FileSpec &file_spec, lldb::DebuggerSP debugger_sp) : m_file_spec_orig(file_spec), m_file_spec(file_spec), - m_mod_time(FileSystem::GetModificationTime(file_spec)), + m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_debugger_wp(debugger_sp) { CommonInitializer(file_spec, nullptr); } SourceManager::File::File(const FileSpec &file_spec, Target *target) : m_file_spec_orig(file_spec), m_file_spec(file_spec), - m_mod_time(FileSystem::GetModificationTime(file_spec)), + m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_debugger_wp(target ? target->GetDebugger().shared_from_this() : DebuggerSP()) { CommonInitializer(file_spec, target); @@ -422,7 +422,7 @@ SymbolContext sc; sc_list.GetContextAtIndex(0, sc); m_file_spec = sc.comp_unit; - m_mod_time = FileSystem::GetModificationTime(m_file_spec); + m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec); } } } @@ -435,7 +435,7 @@ if (target->GetSourcePathMap().FindFile(m_file_spec, new_file_spec) || target->GetImages().FindSourceFile(m_file_spec, new_file_spec)) { m_file_spec = new_file_spec; - m_mod_time = FileSystem::GetModificationTime(m_file_spec); + m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec); } } } @@ -515,7 +515,7 @@ // TODO: use host API to sign up for file modifications to anything in our // source cache and only update when we determine a file has been updated. // For now we check each time we want to display info for the file. - auto curr_mod_time = FileSystem::GetModificationTime(m_file_spec); + auto curr_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec); if (curr_mod_time != llvm::sys::TimePoint<>() && m_mod_time != curr_mod_time) { Index: lldb/trunk/source/Host/common/FileSystem.cpp =================================================================== --- lldb/trunk/source/Host/common/FileSystem.cpp +++ lldb/trunk/source/Host/common/FileSystem.cpp @@ -9,7 +9,10 @@ #include "lldb/Host/FileSystem.h" +#include "lldb/Utility/TildeExpressionResolver.h" + #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" #include #include @@ -17,12 +20,160 @@ using namespace lldb; using namespace lldb_private; +using namespace llvm; + +FileSystem &FileSystem::Instance() { return *InstanceImpl(); } + +void FileSystem::Initialize() { + assert(!InstanceImpl()); + InstanceImpl().emplace(); +} + +void FileSystem::Initialize(IntrusiveRefCntPtr fs) { + assert(!InstanceImpl()); + InstanceImpl().emplace(fs); +} + +void FileSystem::Terminate() { + assert(InstanceImpl()); + InstanceImpl().reset(); +} + +Optional &FileSystem::InstanceImpl() { + static Optional g_fs; + return g_fs; +} + +void FileSystem::SetFileSystem(IntrusiveRefCntPtr fs) { + m_fs = fs; +} + +sys::TimePoint<> +FileSystem::GetModificationTime(const FileSpec &file_spec) const { + return GetModificationTime(file_spec.GetPath()); +} + +sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const { + ErrorOr status = m_fs->status(path); + if (!status) + return sys::TimePoint<>(); + return status->getLastModificationTime(); +} + +uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const { + return GetByteSize(file_spec.GetPath()); +} + +uint64_t FileSystem::GetByteSize(const Twine &path) const { + ErrorOr status = m_fs->status(path); + if (!status) + return 0; + return status->getSize(); +} + +uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const { + return GetPermissions(file_spec.GetPath()); +} + +uint32_t FileSystem::GetPermissions(const Twine &path) const { + ErrorOr status = m_fs->status(path); + if (!status) + return sys::fs::perms::perms_not_known; + return status->getPermissions(); +} + +bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); } + +bool FileSystem::Exists(const FileSpec &file_spec) const { + return Exists(file_spec.GetPath()); +} + +bool FileSystem::Readable(const Twine &path) const { + return GetPermissions(path) & sys::fs::perms::all_read; +} + +bool FileSystem::Readable(const FileSpec &file_spec) const { + return Readable(file_spec.GetPath()); +} + +void FileSystem::EnumerateDirectory(Twine path, bool find_directories, + bool find_files, bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton) { + std::error_code EC; + vfs::recursive_directory_iterator Iter(*m_fs, path, EC); + vfs::recursive_directory_iterator End; + for (; Iter != End && !EC; Iter.increment(EC)) { + const auto &Item = *Iter; + ErrorOr Status = m_fs->status(Item.path()); + if (!Status) + break; + if (!find_files && Status->isRegularFile()) + continue; + if (!find_directories && Status->isDirectory()) + continue; + if (!find_other && Status->isOther()) + continue; + + auto Result = callback(callback_baton, Status->getType(), Item.path()); + if (Result == eEnumerateDirectoryResultQuit) + return; + if (Result == eEnumerateDirectoryResultNext) { + // Default behavior is to recurse. Opt out if the callback doesn't want + // this behavior. + Iter.no_push(); + } + } +} + +std::error_code FileSystem::MakeAbsolute(SmallVectorImpl &path) const { + return m_fs->makeAbsolute(path); +} + +std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const { + SmallString<128> path; + file_spec.GetPath(path, false); + + auto EC = MakeAbsolute(path); + if (EC) + return EC; + + FileSpec new_file_spec(path, false, file_spec.GetPathStyle()); + file_spec = new_file_spec; + return {}; +} + +std::error_code FileSystem::GetRealPath(const Twine &path, + SmallVectorImpl &output) const { + return m_fs->getRealPath(path, output); +} + +void FileSystem::Resolve(SmallVectorImpl &path) { + if (path.empty()) + return; + + // Resolve tilde. + SmallString<128> original_path(path.begin(), path.end()); + StandardTildeExpressionResolver Resolver; + Resolver.ResolveFullPath(original_path, path); + + // Try making the path absolute if it exists. + SmallString<128> absolute_path(path.begin(), path.end()); + MakeAbsolute(path); + if (!Exists(path)) { + path.clear(); + path.append(original_path.begin(), original_path.end()); + } +} + +void FileSystem::Resolve(FileSpec &file_spec) { + // Extract path from the FileSpec. + SmallString<128> path; + file_spec.GetPath(path); + + // Resolve the path. + Resolve(path); -llvm::sys::TimePoint<> -FileSystem::GetModificationTime(const FileSpec &file_spec) { - llvm::sys::fs::file_status status; - std::error_code ec = llvm::sys::fs::status(file_spec.GetPath(), status); - if (ec) - return llvm::sys::TimePoint<>(); - return status.getLastModificationTime(); + // Update the FileSpec with the resolved path. + file_spec.SetPath(path); } Index: lldb/trunk/source/Host/common/HostInfoBase.cpp =================================================================== --- lldb/trunk/source/Host/common/HostInfoBase.cpp +++ lldb/trunk/source/Host/common/HostInfoBase.cpp @@ -226,7 +226,7 @@ // This is necessary because when running the testsuite the shlib might be a // symbolic link inside the Python resource dir. - FileSystem::ResolveSymbolicLink(lldb_file_spec, lldb_file_spec); + FileSystem::Instance().ResolveSymbolicLink(lldb_file_spec, lldb_file_spec); // Remove the filename so that this FileSpec only represents the directory. file_spec.GetDirectory() = lldb_file_spec.GetDirectory(); Index: lldb/trunk/source/Host/common/Symbols.cpp =================================================================== --- lldb/trunk/source/Host/common/Symbols.cpp +++ lldb/trunk/source/Host/common/Symbols.cpp @@ -261,7 +261,7 @@ // Add module directory. FileSpec module_file_spec = module_spec.GetFileSpec(); // We keep the unresolved pathname if it fails. - FileSystem::ResolveSymbolicLink(module_file_spec, module_file_spec); + FileSystem::Instance().ResolveSymbolicLink(module_file_spec, module_file_spec); const ConstString &file_dir = module_file_spec.GetDirectory(); debug_file_search_paths.AppendIfUnique( Index: lldb/trunk/source/Host/posix/HostProcessPosix.cpp =================================================================== --- lldb/trunk/source/Host/posix/HostProcessPosix.cpp +++ lldb/trunk/source/Host/posix/HostProcessPosix.cpp @@ -62,7 +62,7 @@ return error; } - error = FileSystem::Readlink(FileSpec{link_path, false}, file_spec); + error = FileSystem::Instance().Readlink(FileSpec{link_path, false}, file_spec); if (!error.Success()) return error; Index: lldb/trunk/source/Initialization/SystemInitializerCommon.cpp =================================================================== --- lldb/trunk/source/Initialization/SystemInitializerCommon.cpp +++ lldb/trunk/source/Initialization/SystemInitializerCommon.cpp @@ -15,6 +15,7 @@ #include "Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h" #include "Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/Log.h" @@ -62,6 +63,7 @@ } #endif + FileSystem::Initialize(); Log::Initialize(); HostInfo::Initialize(); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); @@ -106,4 +108,5 @@ HostInfo::Terminate(); Log::DisableAllLogChannels(); + FileSystem::Terminate(); } Index: lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp +++ lldb/trunk/source/Interpreter/OptionValueFileSpec.cpp @@ -109,7 +109,7 @@ const lldb::DataBufferSP &OptionValueFileSpec::GetFileContents() { if (m_current_value) { - const auto file_mod_time = FileSystem::GetModificationTime(m_current_value); + const auto file_mod_time = FileSystem::Instance().GetModificationTime(m_current_value); if (m_data_sp && m_data_mod_time == file_mod_time) return m_data_sp; m_data_sp = DataBufferLLVM::CreateFromPath(m_current_value.GetPath()); Index: lldb/trunk/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp =================================================================== --- lldb/trunk/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp +++ lldb/trunk/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp @@ -115,7 +115,7 @@ // No UUID, we must rely upon the cached module modification time and the // modification time of the file on disk if (module_sp->GetModificationTime() != - FileSystem::GetModificationTime(module_sp->GetFileSpec())) + FileSystem::Instance().GetModificationTime(module_sp->GetFileSpec())) module_sp.reset(); } Index: lldb/trunk/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp =================================================================== --- lldb/trunk/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ lldb/trunk/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -461,7 +461,7 @@ return 0; const size_t initial_count = specs.GetSize(); - llvm::sys::TimePoint<> file_mod_time = FileSystem::GetModificationTime(file); + llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file); Archive::shared_ptr archive_sp( Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset)); bool set_archive_arch = false; Index: lldb/trunk/source/Plugins/Platform/Android/AdbClient.cpp =================================================================== --- lldb/trunk/source/Plugins/Platform/Android/AdbClient.cpp +++ lldb/trunk/source/Plugins/Platform/Android/AdbClient.cpp @@ -484,7 +484,7 @@ return Status("Failed to send file chunk: %s", error.AsCString()); } error = SendSyncRequest( - kDONE, llvm::sys::toTimeT(FileSystem::GetModificationTime(local_file)), + kDONE, llvm::sys::toTimeT(FileSystem::Instance().GetModificationTime(local_file)), nullptr); if (error.Fail()) return error; Index: lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp =================================================================== --- lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -455,7 +455,7 @@ Status PlatformPOSIX::CreateSymlink(const FileSpec &src, const FileSpec &dst) { if (IsHost()) - return FileSystem::Symlink(src, dst); + return FileSystem::Instance().Symlink(src, dst); else if (m_remote_platform_sp) return m_remote_platform_sp->CreateSymlink(src, dst); else Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -698,7 +698,7 @@ packet.GetHexByteStringTerminatedBy(dst, ','); packet.GetChar(); // Skip ',' char packet.GetHexByteString(src); - Status error = FileSystem::Symlink(FileSpec{src, true}, FileSpec{dst, false}); + Status error = FileSystem::Instance().Symlink(FileSpec{src, true}, FileSpec{dst, false}); StreamString response; response.Printf("F%u,%u", error.GetError(), error.GetError()); return SendPacketNoLock(response.GetString()); Index: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -212,7 +212,7 @@ return local_spec; FileSpec resolved_symlink; - const auto error = FileSystem::Readlink(local_spec, resolved_symlink); + const auto error = FileSystem::Instance().Readlink(local_spec, resolved_symlink); if (error.Success()) return resolved_symlink; Index: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -424,7 +424,7 @@ FileSpec oso_file(oso_path, false); ConstString oso_object; if (oso_file.Exists()) { - auto oso_mod_time = FileSystem::GetModificationTime(oso_file); + auto oso_mod_time = FileSystem::Instance().GetModificationTime(oso_file); if (oso_mod_time != comp_unit_info->oso_mod_time) { obj_file->GetModule()->ReportError( "debug map object file '%s' has changed (actual time is " Index: lldb/trunk/source/Target/Platform.cpp =================================================================== --- lldb/trunk/source/Target/Platform.cpp +++ lldb/trunk/source/Target/Platform.cpp @@ -603,7 +603,7 @@ FileSpec src_resolved; - rc_baton->error = FileSystem::Readlink(src, src_resolved); + rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved); if (rc_baton->error.Fail()) return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out @@ -733,7 +733,7 @@ case fs::file_type::symlink_file: { llvm::sys::fs::remove(fixed_dst.GetPath()); FileSpec src_resolved; - error = FileSystem::Readlink(src, src_resolved); + error = FileSystem::Instance().Readlink(src, src_resolved); if (error.Success()) error = CreateSymlink(dst, src_resolved); } break; Index: lldb/trunk/unittests/Core/MangledTest.cpp =================================================================== --- lldb/trunk/unittests/Core/MangledTest.cpp +++ lldb/trunk/unittests/Core/MangledTest.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/SymbolContext.h" @@ -61,6 +62,7 @@ } TEST(MangledTest, NameIndexes_FindFunctionSymbols) { + FileSystem::Initialize(); HostInfo::Initialize(); ObjectFileELF::Initialize(); SymbolVendorELF::Initialize(); @@ -167,4 +169,5 @@ SymbolVendorELF::Terminate(); ObjectFileELF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); } Index: lldb/trunk/unittests/Expression/ClangParserTest.cpp =================================================================== --- lldb/trunk/unittests/Expression/ClangParserTest.cpp +++ lldb/trunk/unittests/Expression/ClangParserTest.cpp @@ -9,6 +9,7 @@ #include "Plugins/ExpressionParser/Clang/ClangHost.h" #include "TestingSupport/TestUtilities.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/FileSpec.h" #include "lldb/lldb-defines.h" @@ -18,8 +19,14 @@ namespace { struct ClangHostTest : public testing::Test { - static void SetUpTestCase() { HostInfo::Initialize(); } - static void TearDownTestCase() { HostInfo::Terminate(); } + static void SetUpTestCase() { + FileSystem::Initialize(); + HostInfo::Initialize(); + } + static void TearDownTestCase() { + HostInfo::Terminate(); + FileSystem::Terminate(); + } }; } // namespace Index: lldb/trunk/unittests/Host/FileSystemTest.cpp =================================================================== --- lldb/trunk/unittests/Host/FileSystemTest.cpp +++ lldb/trunk/unittests/Host/FileSystemTest.cpp @@ -7,13 +7,154 @@ // //===----------------------------------------------------------------------===// +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "lldb/Host/FileSystem.h" +#include "llvm/Support/Errc.h" extern const char *TestMainArgv0; using namespace lldb_private; +using namespace llvm; +using llvm::sys::fs::UniqueID; + +// Modified from llvm/unittests/Support/VirtualFileSystemTest.cpp +namespace { +struct DummyFile : public vfs::File { + vfs::Status S; + explicit DummyFile(vfs::Status S) : S(S) {} + llvm::ErrorOr status() override { return S; } + llvm::ErrorOr> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + llvm_unreachable("unimplemented"); + } + std::error_code close() override { return std::error_code(); } +}; + +class DummyFileSystem : public vfs::FileSystem { + int FSID; // used to produce UniqueIDs + int FileID; // used to produce UniqueIDs + std::string cwd; + std::map FilesAndDirs; + + static int getNextFSID() { + static int Count = 0; + return Count++; + } + +public: + DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} + + ErrorOr status(const Twine &Path) override { + std::map::iterator I = + FilesAndDirs.find(Path.str()); + if (I == FilesAndDirs.end()) + return make_error_code(llvm::errc::no_such_file_or_directory); + return I->second; + } + ErrorOr> + openFileForRead(const Twine &Path) override { + auto S = status(Path); + if (S) + return std::unique_ptr(new DummyFile{*S}); + return S.getError(); + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return cwd; + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + cwd = Path.str(); + return std::error_code(); + } + // Map any symlink to "/symlink". + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) const override { + auto I = FilesAndDirs.find(Path.str()); + if (I == FilesAndDirs.end()) + return make_error_code(llvm::errc::no_such_file_or_directory); + if (I->second.isSymlink()) { + Output.clear(); + Twine("/symlink").toVector(Output); + return std::error_code(); + } + Output.clear(); + Path.toVector(Output); + return std::error_code(); + } + + struct DirIterImpl : public llvm::vfs::detail::DirIterImpl { + std::map &FilesAndDirs; + std::map::iterator I; + std::string Path; + bool isInPath(StringRef S) { + if (Path.size() < S.size() && S.find(Path) == 0) { + auto LastSep = S.find_last_of('/'); + if (LastSep == Path.size() || LastSep == Path.size() - 1) + return true; + } + return false; + } + DirIterImpl(std::map &FilesAndDirs, + const Twine &_Path) + : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), + Path(_Path.str()) { + for (; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = + vfs::directory_entry(I->second.getName(), I->second.getType()); + break; + } + } + } + std::error_code increment() override { + ++I; + for (; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = + vfs::directory_entry(I->second.getName(), I->second.getType()); + break; + } + } + if (I == FilesAndDirs.end()) + CurrentEntry = vfs::directory_entry(); + return std::error_code(); + } + }; + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return vfs::directory_iterator( + std::make_shared(FilesAndDirs, Dir)); + } + + void addEntry(StringRef Path, const vfs::Status &Status) { + FilesAndDirs[Path] = Status; + } + + void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 1024, + sys::fs::file_type::regular_file, Perms); + addEntry(Path, S); + } + + void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::directory_file, Perms); + addEntry(Path, S); + } + + void addSymlink(StringRef Path) { + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::symlink_file, sys::fs::all_all); + addEntry(Path, S); + } +}; +} // namespace TEST(FileSystemTest, FileAndDirectoryComponents) { using namespace std::chrono; @@ -26,7 +167,125 @@ #endif FileSpec fs2(TestMainArgv0, resolve); - EXPECT_EQ(system_clock::time_point(), FileSystem::GetModificationTime(fs1)); + FileSystem fs; + + EXPECT_EQ(system_clock::time_point(), fs.GetModificationTime(fs1)); EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20), - FileSystem::GetModificationTime(fs2)); + fs.GetModificationTime(fs2)); +} + +static IntrusiveRefCntPtr GetSimpleDummyFS() { + IntrusiveRefCntPtr D(new DummyFileSystem()); + D->addRegularFile("/foo"); + D->addDirectory("/bar"); + D->addSymlink("/baz"); + D->addRegularFile("/qux", ~sys::fs::perms::all_read); + D->setCurrentWorkingDirectory("/"); + return D; +} + +TEST(FileSystemTest, Exists) { + FileSystem fs(GetSimpleDummyFS()); + + EXPECT_TRUE(fs.Exists("/foo")); + EXPECT_TRUE(fs.Exists(FileSpec("/foo", false, FileSpec::Style::posix))); +} + +TEST(FileSystemTest, Readable) { + FileSystem fs(GetSimpleDummyFS()); + + EXPECT_TRUE(fs.Readable("/foo")); + EXPECT_TRUE(fs.Readable(FileSpec("/foo", false, FileSpec::Style::posix))); + + EXPECT_FALSE(fs.Readable("/qux")); + EXPECT_FALSE(fs.Readable(FileSpec("/qux", false, FileSpec::Style::posix))); +} + +TEST(FileSystemTest, GetByteSize) { + FileSystem fs(GetSimpleDummyFS()); + + EXPECT_EQ((uint64_t)1024, fs.GetByteSize("/foo")); + EXPECT_EQ((uint64_t)1024, + fs.GetByteSize(FileSpec("/foo", false, FileSpec::Style::posix))); +} + +TEST(FileSystemTest, GetPermissions) { + FileSystem fs(GetSimpleDummyFS()); + + EXPECT_EQ(sys::fs::all_all, fs.GetPermissions("/foo")); + EXPECT_EQ(sys::fs::all_all, + fs.GetPermissions(FileSpec("/foo", false, FileSpec::Style::posix))); +} + +TEST(FileSystemTest, MakeAbsolute) { + FileSystem fs(GetSimpleDummyFS()); + + { + StringRef foo_relative = "foo"; + SmallString<16> foo(foo_relative); + auto EC = fs.MakeAbsolute(foo); + EXPECT_FALSE(EC); + EXPECT_TRUE(foo.equals("/foo")); + } + + { + FileSpec file_spec("foo", false); + auto EC = fs.MakeAbsolute(file_spec); + EXPECT_FALSE(EC); + EXPECT_EQ("/foo", file_spec.GetPath()); + } +} + +TEST(FileSystemTest, Resolve) { + FileSystem fs(GetSimpleDummyFS()); + + { + StringRef foo_relative = "foo"; + SmallString<16> foo(foo_relative); + fs.Resolve(foo); + EXPECT_TRUE(foo.equals("/foo")); + } + + { + FileSpec file_spec("foo", false); + fs.Resolve(file_spec); + EXPECT_EQ("/foo", file_spec.GetPath()); + } + + { + StringRef foo_relative = "bogus"; + SmallString<16> foo(foo_relative); + fs.Resolve(foo); + EXPECT_TRUE(foo.equals("bogus")); + } + + { + FileSpec file_spec("bogus", false); + fs.Resolve(file_spec); + EXPECT_EQ("bogus", file_spec.GetPath()); + } +} + +FileSystem::EnumerateDirectoryResult +VFSCallback(void *baton, llvm::sys::fs::file_type file_type, + llvm::StringRef path) { + auto visited = static_cast *>(baton); + visited->push_back(path.str()); + return FileSystem::eEnumerateDirectoryResultNext; +} + +TEST(FileSystemTest, EnumerateDirectory) { + FileSystem fs(GetSimpleDummyFS()); + + std::vector visited; + + constexpr bool find_directories = true; + constexpr bool find_files = true; + constexpr bool find_other = true; + + fs.EnumerateDirectory("/", find_directories, find_files, find_other, + VFSCallback, &visited); + + EXPECT_THAT(visited, + testing::UnorderedElementsAre("/foo", "/bar", "/baz", "/qux")); } Index: lldb/trunk/unittests/Host/HostInfoTest.cpp =================================================================== --- lldb/trunk/unittests/Host/HostInfoTest.cpp +++ lldb/trunk/unittests/Host/HostInfoTest.cpp @@ -8,20 +8,27 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/HostInfo.h" -#include "lldb/lldb-defines.h" #include "TestingSupport/TestUtilities.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/lldb-defines.h" #include "gtest/gtest.h" using namespace lldb_private; using namespace llvm; namespace { -class HostInfoTest: public ::testing::Test { - public: - void SetUp() override { HostInfo::Initialize(); } - void TearDown() override { HostInfo::Terminate(); } +class HostInfoTest : public ::testing::Test { +public: + void SetUp() override { + FileSystem::Initialize(); + HostInfo::Initialize(); + } + void TearDown() override { + HostInfo::Terminate(); + FileSystem::Terminate(); + } }; -} +} // namespace TEST_F(HostInfoTest, GetAugmentedArchSpec) { // Fully specified triple should not be changed. Index: lldb/trunk/unittests/Host/SymbolsTest.cpp =================================================================== --- lldb/trunk/unittests/Host/SymbolsTest.cpp +++ lldb/trunk/unittests/Host/SymbolsTest.cpp @@ -9,20 +9,37 @@ #include "gtest/gtest.h" -#include "lldb/Host/Symbols.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/Symbols.h" using namespace lldb_private; -TEST(SymbolsTest, - LocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) { +namespace { +class SymbolsTest : public ::testing::Test { +public: + void SetUp() override { + FileSystem::Initialize(); + HostInfo::Initialize(); + } + void TearDown() override { + HostInfo::Terminate(); + FileSystem::Terminate(); + } +}; +} // namespace + +TEST_F( + SymbolsTest, + TerminateLocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) { ModuleSpec module_spec; FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec); EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); } -TEST(SymbolsTest, - LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) { +TEST_F(SymbolsTest, + LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) { ModuleSpec module_spec; // using a GUID here because the symbol file shouldn't actually exist on disk module_spec.GetSymbolFileSpec().SetFile( Index: lldb/trunk/unittests/ObjectFile/ELF/TestObjectFileELF.cpp =================================================================== --- lldb/trunk/unittests/ObjectFile/ELF/TestObjectFileELF.cpp +++ lldb/trunk/unittests/ObjectFile/ELF/TestObjectFileELF.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Compression.h" @@ -29,6 +30,7 @@ class ObjectFileELFTest : public testing::Test { public: void SetUp() override { + FileSystem::Initialize(); HostInfo::Initialize(); ObjectFileELF::Initialize(); SymbolVendorELF::Initialize(); @@ -38,6 +40,7 @@ SymbolVendorELF::Terminate(); ObjectFileELF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); } protected: Index: lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp =================================================================== --- lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -7,10 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "Plugins/ScriptInterpreter/Python/lldb-python.h" #include "gtest/gtest.h" #include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h" +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "PythonTestSuite.h" @@ -18,6 +19,7 @@ using namespace lldb_private; void PythonTestSuite::SetUp() { + FileSystem::Initialize(); HostInfoBase::Initialize(); // ScriptInterpreterPython::Initialize() depends on HostInfo being // initializedso it can compute the python directory etc. Index: lldb/trunk/unittests/Symbol/TestClangASTContext.cpp =================================================================== --- lldb/trunk/unittests/Symbol/TestClangASTContext.cpp +++ lldb/trunk/unittests/Symbol/TestClangASTContext.cpp @@ -13,6 +13,7 @@ #include "clang/AST/DeclCXX.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangUtil.h" @@ -25,9 +26,15 @@ class TestClangASTContext : public testing::Test { public: - static void SetUpTestCase() { HostInfo::Initialize(); } + static void SetUpTestCase() { + FileSystem::Initialize(); + HostInfo::Initialize(); + } - static void TearDownTestCase() { HostInfo::Terminate(); } + static void TearDownTestCase() { + HostInfo::Terminate(); + FileSystem::Terminate(); + } virtual void SetUp() override { std::string triple = HostInfo::GetTargetTriple(); @@ -357,7 +364,7 @@ RecordDecl *empty_derived_non_empty_base_decl = ClangASTContext::GetAsRecordDecl(empty_derived); EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses( - empty_derived_non_empty_base_cxx_decl, false)); + empty_derived_non_empty_base_cxx_decl, false)); EXPECT_TRUE( ClangASTContext::RecordHasFields(empty_derived_non_empty_base_decl)); @@ -380,7 +387,7 @@ RecordDecl *empty_derived_non_empty_vbase_decl = ClangASTContext::GetAsRecordDecl(empty_derived2); EXPECT_EQ(1u, ClangASTContext::GetNumBaseClasses( - empty_derived_non_empty_vbase_cxx_decl, false)); + empty_derived_non_empty_vbase_cxx_decl, false)); EXPECT_TRUE( ClangASTContext::RecordHasFields(empty_derived_non_empty_vbase_decl)); } @@ -420,7 +427,7 @@ clang::AutoTypeKeyword::Auto, false)); CompilerType int_type(m_ast->getASTContext(), m_ast->getASTContext()->IntTy); - for(CompilerType t: { type, typedef_type, auto_type }) { + for (CompilerType t : {type, typedef_type, auto_type}) { SCOPED_TRACE(t.GetTypeName().AsCString()); EXPECT_EQ(m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 0), Index: lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp =================================================================== --- lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp +++ lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -8,20 +8,24 @@ // //===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "TestingSupport/TestUtilities.h" + #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Utility/StreamString.h" -#include "TestingSupport/TestUtilities.h" + #include "llvm/Support/FileUtilities.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" -#include "gtest/gtest.h" using namespace lldb_private; using namespace lldb; @@ -29,6 +33,7 @@ class DWARFCallFrameInfoTest : public testing::Test { public: void SetUp() override { + FileSystem::Initialize(); HostInfo::Initialize(); ObjectFileELF::Initialize(); } @@ -36,6 +41,7 @@ void TearDown() override { ObjectFileELF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); } protected: Index: lldb/trunk/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp =================================================================== --- lldb/trunk/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp +++ lldb/trunk/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp @@ -22,6 +22,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" @@ -38,13 +39,14 @@ // Initialize and TearDown the plugin every time, so we get a brand new // AST every time so that modifications to the AST from each test don't // leak into the next test. - HostInfo::Initialize(); - ObjectFilePECOFF::Initialize(); - SymbolFileDWARF::Initialize(); - ClangASTContext::Initialize(); - SymbolFilePDB::Initialize(); +FileSystem::Initialize(); +HostInfo::Initialize(); +ObjectFilePECOFF::Initialize(); +SymbolFileDWARF::Initialize(); +ClangASTContext::Initialize(); +SymbolFilePDB::Initialize(); - m_dwarf_test_exe = GetInputFilePath("test-dwarf.exe"); +m_dwarf_test_exe = GetInputFilePath("test-dwarf.exe"); } void TearDown() override { @@ -53,6 +55,7 @@ SymbolFileDWARF::Terminate(); ObjectFilePECOFF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); } protected: Index: lldb/trunk/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp =================================================================== --- lldb/trunk/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp +++ lldb/trunk/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -22,6 +22,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" @@ -49,6 +50,7 @@ ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif + FileSystem::Initialize(); HostInfo::Initialize(); ObjectFilePECOFF::Initialize(); SymbolFileDWARF::Initialize(); @@ -65,6 +67,7 @@ SymbolFileDWARF::Terminate(); ObjectFilePECOFF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); #if defined(_MSC_VER) ::CoUninitialize(); Index: lldb/trunk/unittests/Target/ModuleCacheTest.cpp =================================================================== --- lldb/trunk/unittests/Target/ModuleCacheTest.cpp +++ lldb/trunk/unittests/Target/ModuleCacheTest.cpp @@ -5,12 +5,13 @@ #include "llvm/Support/Path.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "TestingSupport/TestUtilities.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ModuleCache.h" -#include "TestingSupport/TestUtilities.h" using namespace lldb_private; using namespace lldb; @@ -65,6 +66,7 @@ } void ModuleCacheTest::SetUpTestCase() { + FileSystem::Initialize(); HostInfo::Initialize(); ObjectFileELF::Initialize(); @@ -75,6 +77,7 @@ void ModuleCacheTest::TearDownTestCase() { ObjectFileELF::Terminate(); HostInfo::Terminate(); + FileSystem::Terminate(); } static void VerifyDiskState(const FileSpec &cache_dir, const char *hostname) {