Skip to content

Repository files navigation

Supported Platforms License: MIT CMake Build Matrix Build Status Build Status Coverage Status Latest Release Tag

Filesystem

This is a header-only single-file std::filesystem compatible helper library, based on the C++17 and C++20 specifications, but implemented for C++11, C++14, C++17, and C++20. It closely follows the C++17 standard with a few documented exceptions.

Automated CI currently covers macOS, Windows, Ubuntu, Rocky Linux, and FreeBSD, including both current and legacy compiler toolchains. The library has also been reported to work on Android, iOS, Emscripten, QNX, GNU/Hurd, Haiku, Solaris, and other Unix-like systems, but those platforms are not continuously tested. PRs that allow testing any of those are welcome.

It is of course in its own namespace ghc::filesystem to not interfere with a regular std::filesystem should you use it in a mixed C++17 environment (which is possible).

Test coverage is well above 90%, and starting with v1.3.6 and in v1.5.0 more time was invested in benchmarking and optimizing parts of the library. I'll try to continue to optimize some parts and refactor others, striving to improve it as long as it doesn't introduce additional C++17/C++20 compatibility issues. Feedback is always welcome. Simply open an issue if you see something missing or wrong or not behaving as expected and I'll comment.

Motivation

I'm often in need of filesystem functionality, mostly fs::path, but directory access too, and when beginning to use C++11, I used that language update to try to reduce my third-party dependencies. I could drop most of what I used, but still missed some stuff that I started implementing for the fun of it. Originally I based these helpers on my own coding- and naming conventions. When C++17 was finalized, I wanted to use that interface, but it took a while, to push myself to convert my classes.

The implementation is closely based on chapter 30.10 from the C++17 standard and a draft close to that version is Working Draft N4687. It is from after the standardization of C++17 but it contains the latest filesystem interface changes compared to the Working Draft N4659. Staring with v1.4.0, when compiled using C++20, it adapts to the changes according to path sorting order and std::u8string handling from Working Draft N4860.

I want to thank the people working on improving C++, I really liked how the language evolved with C++11 and the following standards. Keep on the good work!

Why the namespace GHC?

If you ask yourself, what ghc is standing for, it is simply gulraks helper classes, yeah, I know, not very imaginative, but I wanted a short namespace and I use it in some of my private classes (so it has nothing to do with Haskell, sorry for the name clash).

Platforms

ghc::filesystem is developed on macOS and continuously tested using GitHub Actions, AppVeyor, and Cirrus CI. The current automated matrix includes:

  • macOS 14 and 15 on Apple Silicon with AppleClang
  • Windows with Visual Studio 2015, 2017, 2019, and 2022 toolchains, including the MSVC v142 toolset
  • Windows MSYS2 with MinGW64 GCC, UCRT64 GCC, and Clang64
  • Ubuntu 22.04 and 24.04 with GCC 11-13 and Clang 15/18
  • Legacy Docker builds with GCC 5-8 and Clang 6-9
  • Rocky Linux 8 and 9
  • FreeBSD 14

Modern CI jobs exercise C++11, C++17, and C++20 where supported. Platforms not listed above may still work, but are not covered by the current automated test matrix.

People use it on Android, iOS, Emscripten, QNX, GNU/Hurd, Haiku, Solaris, and other Unix-like systems but continous CI coverage is missing.

It should work on any Windows or POSIX platform with a C++11-capable compiler. All in all, I don't see it replacing std::filesystem where full C++17 or C++20 is available, it doesn't try to be a "better" std::filesystem, just an almost drop-in if you can't use it (with the exception of the UTF-8 preference).

ℹ️ Important: This implementation is following the "UTF-8 Everywhere" philosophy in that all std::string instances will be interpreted the same as std::u8string encoding wise and as being in UTF-8. The std::u16string will be seen as UTF-16. See Differences in API for more information.

Tests

The header comes with a set of unit-tests and uses CMake as a build tool and Catch2 as test framework. All tests are registered with CMake, so the ctest command can be used to run the tests.

All tests against this implementation should succeed, depending on your environment it might be that there are some warnings, e.g. if you have no rights to create Symlinks on Windows or at least the test thinks so, but these are just informative.

To build the tests from inside the project directory under macOS or Linux just:

mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
ctest

This generates the test binaries that run the tests and the last command executes them.

If the default compiler is a GCC 8 or newer, or Clang 7 or newer, it additionally tries to build a version of the test binary compiled against GCCs/Clangs std::filesystem implementation, named std_filesystem_test as an additional test of conformance. Ideally all tests should compile and succeed with all filesystem implementations, but in reality, there are some differences in behavior, sometimes due to room for interpretation in in the standard, and there might be issues in these implementations too.

Usage

Downloads

The latest release version is v1.5.16 and source archives can be found here.

The latest pre-native-backend version is v1.4.0 and source archives can be found here.

The latest pre-C++20-support release version is v1.3.10 and source archives can be found here.

Currently only the latest minor release version receives bugfixes, so if possible, you should use the latest release.

Using it as Single-File-Header

As ghc::filesystem is at first a header-only library, it should be enough to copy the header or the include/ghc directory into your project folder or point your include path to this place and simply include the filesystem.hpp header (or ghc/filesystem.hpp if you use the subdirectory).

Everything is in the namespace ghc::filesystem, so one way to use it only as a fallback could be:

#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
    // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
    // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
    #if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
        #define GHC_USE_STD_FS

        // Old Apple OSs don't support std::filesystem, though the header is available at compile
        // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
        // and watchOS 6.0.
        #ifdef __APPLE__
            #include <Availability.h>
            // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
            // released after std::filesystem, where std::filesystem is always available.
            // (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
            #if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
             || __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
             || __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
             || __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
                #undef GHC_USE_STD_FS
            #endif  
        #endif
    #endif
#endif

#ifdef GHC_USE_STD_FS
    #include <filesystem>
    namespace fs = std::filesystem;
#else
    #include "filesystem.hpp"
    namespace fs = ghc::filesystem;
#endif

If you want to also use the fstream wrapper with path support as fallback, you might use:

#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
    // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
    // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
    #if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
        #define GHC_USE_STD_FS

        // Old Apple OSs don't support std::filesystem, though the header is available at compile
        // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
        // and watchOS 6.0.
        #ifdef __APPLE__
            #include <Availability.h>
            // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
            // released after std::filesystem, where std::filesystem is always available.
            // (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
            #if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
             || __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
             || __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
             || __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
                #undef GHC_USE_STD_FS
            #endif  
        #endif
    #endif
#endif

#ifdef GHC_USE_STD_FS
    #include <filesystem>
    #include <fstream>
    namespace fs {
        using namespace std::filesystem;
        using ifstream = std::ifstream;
        using ofstream = std::ofstream;
        using fstream = std::fstream;
    }
#else
    #include "filesystem.hpp"
    namespace fs {
        using namespace ghc::filesystem;
        using ifstream = ghc::filesystem::ifstream;
        using ofstream = ghc::filesystem::ofstream;
        using fstream = ghc::filesystem::fstream;
    }
#endif

Now you have e.g. fs::ofstream out(somePath); and it is either the wrapper or the C++17 std::ofstream.

ℹ️ Be aware, as a header-only library, it is not hiding the fact, that it uses system includes, so they "pollute" your global namespace. Use the forwarding-/implementation-header based approach (see below) to avoid this. For Windows it needs Windows.h and it might be a good idea to define WIN32_LEAN_AND_MEAN or NOMINMAX prior to including filesystem.hpp or fs_std.hpp headers to reduce pollution of your global namespace and compile time. They are not defined by ghc::filesystem to allow combination with contexts where the full Windows.his needed, e.g. for UI elements.

ℹ️ Hint: There is an additional header named ghc/fs_std.hpp that implements this dynamic selection of a filesystem implementation, that you can include instead of ghc/filesystem.hpp when you want std::filesystem where available and ghc::filesystem where not.

Using it as Forwarding-/Implementation-Header

Alternatively, starting from v1.1.0 ghc::filesystem can also be used by including one of two additional wrapper headers. These allow to include a forwarded version in most places (ghc/fs_fwd.hpp) while hiding the implementation details in a single cpp file that includes ghc/fs_impl.hpp to implement the needed code. Using ghc::filesystem this way makes sure system includes are only visible from inside the cpp file, all other places are clean.

Be aware, that it is currently not supported to hide the implementation into a Windows-DLL, as a DLL interface with C++ standard templates in interfaces is a different beast. If someone is willing to give it a try, I might integrate a PR but currently working on that myself is not a priority.

If you use the forwarding/implementation approach, you can still use the dynamic switching like this:

#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
    // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
    // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
    #if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
        #define GHC_USE_STD_FS

        // Old Apple OSs don't support std::filesystem, though the header is available at compile
        // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
        // and watchOS 6.0.
        #ifdef __APPLE__
            #include <Availability.h>
            // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
            // released after std::filesystem, where std::filesystem is always available.
            // (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
            #if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
             || __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
             || __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
             || __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
                #undef GHC_USE_STD_FS
            #endif  
        #endif
    #endif
#endif

#ifdef GHC_USE_STD_FS
    #include <filesystem>
    #include <fstream>
    namespace fs {
        using namespace std::filesystem;
        using ifstream = std::ifstream;
        using ofstream = std::ofstream;
        using fstream = std::fstream;
    }
#else
    #include "fs_fwd.hpp"
    namespace fs {
        using namespace ghc::filesystem;
        using ifstream = ghc::filesystem::ifstream;
        using ofstream = ghc::filesystem::ofstream;
        using fstream = ghc::filesystem::fstream;
    }
#endif

and in the implementation hiding cpp, you might use (before any include that includes ghc/fs_fwd.hpp to take precedence:

#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
    // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
    // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
    #if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
        #define GHC_USE_STD_FS

        // Old Apple OSs don't support std::filesystem, though the header is available at compile
        // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
        // and watchOS 6.0.
        #ifdef __APPLE__
            #include <Availability.h>
            // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
            // released after std::filesystem, where std::filesystem is always available.
            // (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
            #if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
             || __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
             || __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
             || __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
                #undef GHC_USE_STD_FS
            #endif  
        #endif
    #endif
#endif

#ifndef GHC_USE_STD_FS
    #include "fs_impl.hpp"
#endif

ℹ️ Hint: There are additional helper headers, named ghc/fs_std_fwd.hpp and ghc/fs_std_impl.hpp that use this technique, so you can simply include them if you want to dynamically select the filesystem implementation.

Git Submodule and CMake

Starting from v1.1.0, it is possible to add ghc::filesystem as a git submodule, add the directory to your CMakeLists.txt with add_subdirectory() and then simply use target_link_libraries(your-target ghc_filesystem) to ensure correct include path that allow #include <ghc/filesystem.hpp> to work.

The CMakeLists.txt offers a few options to customize its behavior:

  • GHC_FILESYSTEM_BUILD_TESTING - Compile tests, default is OFF when used as a submodule, else ON.
  • GHC_FILESYSTEM_BUILD_EXAMPLES - Compile the examples, default is OFF when used as a submodule, else ON.
  • GHC_FILESYSTEM_WITH_INSTALL - Add install target to build, default is OFF when used as a submodule, else ON.
  • GHC_FILESYSTEM_BUILD_STD_TESTING - Compile std_filesystem_test, the variant of the test suite running against std::filesystem, defaulting to GHC_FILESYSTEM_BUILD_TESTING. This is only done if the compiler is detected as being able to do it.
  • GHC_FILESYSTEM_TEST_COMPILE_FEATURES can be set to a list of features to override CMAKE_CXX_COMPILE_FEATURES when the detection of C++17 or C++20 for additional tests is not working (e.g. cxx_std_20 to enforce building a filesystem_test_cpp20 with C++20).

Bazel

Please use hedronvision/bazel-cc-filesystem-backport, which will automatically set everything up for you.

Versioning

There is a version macro GHC_FILESYSTEM_VERSION defined in case future changes might make it needed to react on the version, but I don't plan to break anything. It's the version as decimal number (major * 10000 + minor * 100 + patch).

ℹ️ Note: Only even patch versions will be used for releases and odd patch version will only be used for in between commits while working on the next version.

Documentation

There is almost no documentation in this release, as any std::filesystem documentation would work, besides the few differences explained in the next section. So you might head over to https://en.cppreference.com/w/cpp/filesystem for a description of the components of this library.

When compiling with C++11, C++14 or C++17, the API is following the C++17 standard, where possible, with the exception that std::string_view parameters are only supported on C++17. When Compiling with C++20, ghc::filesysytem defaults to the C++20 API, with the char8_t and std::u8string interfaces and the deprecated fs::u8path factory method.

ℹ️ Note: If the C++17 API should be enforced even in C++20 mode, use the define GHC_FILESYSTEM_ENFORCE_CPP17_API. Even then it is possible to create fws::path from std::u8string but fs::path::u8string() and fs::path::generic_u8string() return normal UTF-8 encoded std::string instances, so code written for C++17 could still work with ghc::filesystem when compiled with C++20.

The only additions to the standard are documented here:

ghc::filesystem::ifstream, ghc::filesystem::ofstream, ghc::filesystem::fstream

These are simple wrappers around std::ifstream, std::ofstream and std::fstream. They simply add an open() method and a constructor with an ghc::filesystem::path argument as the fstream variants in C++17 have them.

ghc::filesystem::u8arguments

This is a helper class that currently checks for UTF-8 encoding on non-Windows platforms but on Windows it fetches the command line arguments as Unicode strings from the OS with

::CommandLineToArgvW(::GetCommandLineW(), &argc)

and then converts them to UTF-8, and replaces argc and argv. It is a guard-like class that reverts its changes when going out of scope.

So basic usage is:

namespace fs = ghc::filesystem;

int main(int argc, char* argv[])
{
    fs::u8arguments u8guard(argc, argv);
    if(!u8guard.valid()) {
        std::cerr << "Bad encoding, needs UTF-8." << std::endl;
        exit(EXIT_FAILURE);
    }

    // now use argc/argv as usual, they have utf-8 encoding on windows
    // ...

    return 0;
}

That way argv is UTF-8 encoded as long as the scope from main is valid.

Note: On macOS, while debugging under Xcode the code currently will return false as Xcode starts the application with US-ASCII as encoding, no matter what encoding is actually used and even setting LC_ALL in the product scheme doesn't change anything. I still need to investigate this.

Differences

As this implementation is based on existing code from my private helper classes, it derived some constraints of it. Starting from v1.5.0 most of the differences between this and the standard C++17/C++20 API where removed.

LWG Defects

This implementation has switchable behavior for the LWG defects #2682, #2935, #2936 and #2937. The currently selected behavior (starting from v1.4.0) is following #2682, #2936, #2937 but not following #2935, as I feel it is a bug to report no error on a create_directory() or create_directories() where a regular file of the same name prohibits the creation of a directory and forces the user of those functions to double-check via fs::is_directory if it really worked. The more intuitive approach to directory creation of treating a file with that name as an error is also advocated by the newer paper WG21 P1164R0, the revision P1161R1 was agreed upon on Kona 2019 meeting see merge and GCC by now switched to following its proposal (GCC #86910).

Not Implemented on C++ before C++17

// methods in ghc::filesystem::path:
path& operator+=(basic_string_view<value_type> x);
int compare(basic_string_view<value_type> s) const;

These are not implemented under C++11 and C++14, as there is no std::basic_string_view available and I did want to keep this implementation self-contained and not write a full C++17-upgrade for C++11/14. Starting with v1.1.0 these are supported when compiling ghc::filesystem under C++17 of C++20.

Starting with v1.5.2 ghc::filesystem will try to allow the use of std::experimental::basic_string_view where it detects its availability. Additionally if you have a basic_string_view compatible c++11 implementation it can be used instead of std::basic_string_view by defining GHC_HAS_CUSTOM_STRING_VIEW and importing the implementation into the ghc::filesystem namespace with:

namespace ghc {
    namespace filesystem {
        using my::basic_string_view;
    }
}

before including the filesystem header.

Differences in API

To not depend on any external third party libraries and still stay portable and compact, this implementation is following the "UTF-8 Everywhere" philosophy in that all std::string instances will be interpreted the same as std::u8string encoding wise and as being in UTF-8. The std::u16string will be seen as UTF-16 and std::u32string will be seen as Unicode codepoints. Depending on the size of std::wstring characters, it will handle std::wstring as being UTF-16 (e.g. Windows) or char32_t Unicode codepoints (currently all other platforms).

Differences of Specific Interfaces

Starting with v1.5.0 ghc::filesystem is following the C++17 standard in using wchar_t and std::wstring on Windows as the types internally used for path representation. It is still possible to get the old behavior by defining GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE and get filesystem::path::string_type as std::string and filesystem::path::value_type as wchar_t.

If you need to call some Windows API, with v1.5.0 and above, simply use the W-variant of the Windows-API call (e.g. GetFileAttributesW(p.c_str())).

ℹ️ Note: When using the old behavior by defining GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE, use the path::wstring() member (e.g. GetFileAttributesW(p.wstring().c_str())). This gives you the Unicode variant independent of the UNICODE macro and makes sharing code between Windows, Linux and macOS easier and works with std::filesystem and ghc::filesystem.

std::string path::u8string() const;
std::string path::generic_u8string() const;
vs.
std::u8string path::u8string() const;
std::u8string path::generic_u8string() const;

The return type of these two methods is depending on the used C++ standard and if GHC_FILESYSTEM_ENFORCE_CPP17_API is defined. On C++11, C++14 and C++17 or when GHC_FILESYSTEM_ENFORCE_CPP17_API is defined, the return type is std::string, and on C++20 without the define it is std::u8string.

Differences in Behavior

I created a wiki entry about quite a lot of behavioral differences between different std::filesystem implementations that could result in a mention here, but this readme only tries to address the design choice differences between ghc::filesystem and those. I try to update the wiki page from time to time.

Any additional observations are welcome!

fs.path (ref)

Since v1.5.0 the complete inner mechanics of this implementations fs::path where changed to the native format as the internal representation. Creating any mixed slash fs::path object under Windows (e.g. with "C:\foo/bar") will lead clean path with "C:\foo\bar" via native() and "C:/foo/bar" via generic_string() API. On all platforms redundant additional separators are removed, even if this is not enforced by the standard and other implementations mostly not do this.

Additionally this implementation follows the standards suggestion to handle posix paths of the form "//host/path" and USC path on windows also as having a root-name (e.g. "//host"). The GCC implementation didn't choose to do that while testing on Ubuntu 18.04 and macOS with GCC 8.1.0 or Clang 7.0.0. This difference will show as warnings under std::filesystem. This leads to a change in the algorithm described in the standard for operator/=(path& p) where any path p with p.is_absolute() will degrade to an assignment, while this implementation has the exception where *this == *this.root_name() and p == preferred_separator a normal append will be done, to allow:

fs::path p1 = "//host/foo/bar/file.txt";
fs::path p2;
for (auto p : p1) p2 /= p;
ASSERT(p1 == p2);

For all non-host-leading paths the behavior will match the one described by the standard.

Open Issues

Windows

Symbolic Links on Windows

As symbolic links on Windows, while being supported more or less since Windows Vista (with some strict security constraints) and fully since some earlier build of Windows 10, when "Developer Mode" is activated, are at time of writing (2018) rarely used, still they are supported with this implementation.

Permissions

The Windows ACL permission feature translates badly to the POSIX permission bit mask used in the interface of C++17 filesystem. The permissions returned in the file_status are therefore currently synthesized for the user-level and copied to the group- and other-level. There is still some potential for more interaction with the Windows permission system, but currently setting or reading permissions with this implementation will most certainly not lead to the expected behavior.

Release Notes

v1.5.16

  • Fix for #203, directory iteration and proximate() are available in builds without exception support, with iteration errors terminating the process.
  • Fix for #191, appending to a host-only UNC path now inserts the missing separator.
  • Fix for #170, lexically_normal() now preserves IPv6 components in device UNC paths.
  • Fix for #171, canonical() now supports device UNC paths.
  • Fix for #204, weakly_canonical() now propagates component lookup errors.
  • Fix for #205, copy_file() now reports an error for non-regular sources.
  • Fix for #206, Windows error formatting now handles unknown error codes safely.
  • Fix for #207, last_write_time() now consistently follows symlinks.
  • Fix for #208, filesystem metadata now preserves subsecond modification times.
  • Fix for #209, malformed UTF-16 no longer drops subsequent valid code units.
  • Fix for #210, equivalent() now compares only filesystem identity.
  • Fix for #178, recursive iteration no longer resolves symlinks unless requested.
  • Fix for #185, lexically_normal() now preserves unresolved parent components in relative paths.
  • Pull requests #198 and #199, updated CI for current runners and compiler toolchains, retaining legacy compiler and MSVC v142 coverage and adding MSYS2 GCC and Clang builds, fixing #189.
  • Pull requests #177 and #197, fixed EINTR retry handling in POSIX read(), opendir() and readdir() loops.
  • Avoided unnecessary temporary string creation during Windows directory iteration.
  • Pull request #190, fixed preprocessor checks for builds using -Wundef.
  • Pull request #188, replaced use of the GNU getcwd(NULL, 0) extension.
  • Pull request #179, fixed lexically_relative() when the normalized base equals the target.
  • Pull request #176, documented the external Bazel rules.
  • Pull request #174, CMake no longer prints diagnostic messages when used as a subproject.
  • Pull request #172, allowed wide-path file streams with recent libstdc++ versions on Windows.
  • Pull request #167, improved dynamic selection and deployment target handling across Apple platforms, fixing #168.
  • Fix for #166, extension() did return non empty result for the directory name "..".
  • Pull request #163, build support for Haiku (also fixes #159)
  • Pull request #162, fix for directory iterator treating all files subsequent to a symlink as symlink on Windows
  • Pull request #161, the CMake alias ghcFilesystem::ghc_filesystem is now set unconditionally
  • Fix for #160, the cmake config now only sets install targets by default if the project is no subproject, as documented
  • Fix for #157, suppress C4191 warning on MSVC for GetProcAddress casts
  • Fix for #156, on POSIX stem(), filename() and extension() of fs::path would return wrong result if a colon was in the filename
  • Pull request #154, build support for GNU/Hurd
  • Pull request #153, fixed fs::last_write_time(path, time, ec) setter on iOS, tvOS and watchOS
  • Fix for #151, fs::directory_entry::refresh() now, consistently with status() will not throw on symlinks to non-existing targets, but make the entry have file_type::not_found as the type
  • Pull request #149, add version to CMake project and export it
  • Fix for #146, handle EINTR on POSIX directory iteration and file copy to avoid errors on network filesystems
  • Pull request #145, fix for Y2038 bug in timeToFILETIME on Windows
  • Pull request #144, fs::copy_file() now also copies the permissions
  • Pull request #143, fix for fs::copy_file() ignoring the skip_existing option.
  • Fix for #142, removed need for GHC_NO_DIRENT_D_TYPE on systems that don't support dirent::d_type and fixed build configuration and tests to support Solaris as new platform.
  • Pull request #138, if the platform uses the POSIX backend and has no PATH_MAX, one is defined.
  • Pull request #137, update of Catch2 to version v2.13.7
  • Added macOS 11 to the automatically tested platforms.
  • Pull request #136, the Windows implementation used some unnecessary expensive shared pointer for resource management and these where replaced by a dedicated code.
  • Fix for #132, pull request #135, fs::remove_all now just deletes symbolic links instead of following them.
  • Pull request #133, fix for fs::space where a numerical overflow could happen in a multiplication.
  • Replaced travis-ci.org with GitHub Workflow for the configurations: Ubuntu 20.04: GCC 9.3, Ubuntu 18.04: GCC 7.5, GCC 8.4, macOS 10.15: Xcode 12.4, Windows 10: Visual Studio 2019
  • Fix for #125, where fs::create_directories on Windows no longer breaks on long filenames.
  • Fix for #124, ghc::filesystem treated mounted folder/volumes erroneously as symlinks, leading fs::canonical to fail on paths containing those.
  • Fix for #122, incrementing the recursive_directory_iterator will not try to enter dead symlinks.
  • Fix for #121, on Windows backend the fs::remove failed when the path pointed to a read-only entry, see also (microsoft/STL#1511) for the corresponding issue in std::fs on windows.
  • Fix for #119, added missing support for char16_t and char32_t and on C++20 char8_t literals.
  • Pull request #118, when running tests as root, disable tests that would not work.
  • Pull request #117, added checks to tests to detect the clang/libstdc++ combination.
  • Fix for #116, internal macro GHC_NO_DIRENT_D_TYPE allows os detection to support systems without the dirent.d_type member, experimental first QNX compile support as initial use case, fixed issue with filesystems returning DT_UNKNOWN (e.g. reiserfs).
  • Pull request #115, added string_view support when clang with libstdc++ is detected.
  • Fix for #114, for macOS the pre-Catalina deployment target detection worked only if <Availability.h> was included before <ghc/fs_std.hpp> or <ghc/fs_std_fwd.hpp>/<ghc/fs_std_impl.hpp>.
  • Fix for #113, the use of standard chapter numbers was misleading since C++17 and C++20 std::filesystem features are supported, and was replaced by the tag-like chapter names that stay (mostly) consistent over the versions.
  • Pull request #112, lots of cleanup work on the readme, thanks!
  • Enhancement for #111, further optimization of directory iteration, performance for recursive_directory_iterator over large trees now somewhere between libc++ and libstdc++.
  • Enhancement for #110, ghc::filesystem now has preliminary support for Cygwin. Changes where made to allow the tests to compile and run successfully (tested with GCC 10.2.0), feedback and additional PRs welcome as it is currently not part of the CI configuration.
  • Pull request #109, various spelling errors in error messages and comments fixed.
  • Pull request #108, old style casts removed.
  • Fix for #107, the error handling for status calls was suppressing errors on symlink targets.
  • Pull request #106, fixed detection of AppleClang for compile options.
  • Pull request #105, added option GHC_FILESYSTEM_BUILD_STD_TESTING to override additional build of std::filesystem versions of the tests for comparison and the possibility to use GHC_FILESYSTEM_TEST_COMPILE_FEATURES to prefill the used compile features defaulting to CMAKE_CXX_COMPILE_FEATURES when not given.
  • Enhancement #104, on POSIX backend: optimized reuse of status information and reduced directory_entry creation leads to about 20%-25% in tests with recursive_directory_iterator over a larger directory tree.
  • Pull request #103, wchar_t was not in the list of supported char types on non-Windows backends.
  • Pull request #102, improved string_view support makes use of <string_view> or <experimental/string_view> when available, and allows use of custom basic_string_view implementation when defining GHC_HAS_CUSTOM_STRING_VIEW and importing the string view into the ghc::filesystem namespace before including filesystem header.
  • Pull request #101, fix for #100, append and concat type of operations on path called redundant conversions.
  • Pull request #98, on older linux variants (GCC 7/8), the comparison std::filesystem tests now link with -lrt to avoid issues.
  • Fix for #97, on BTRFS the test case for fs::hard_link_count failed due to the filesystems behavior, the test case was adapted to take that into account.
  • Pull request #96, the export attribute defines GHC_FS_API and GHC_FS_API_CLASS are now honored when when set from outside to allow override of behavior.
  • Fix for #95, the syntax for disabling the deprecated warning in tests in MSVC was wrong.
  • Pull request #93, now the CMake configuration file is configured and part of the make install files.
  • Fix for #91, the way the CMake build options GHC_FILESYSTEM_BUILD_TESTING, GHC_FILESYSTEM_BUILD_EXAMPLES and GHC_FILESYSTEM_WITH_INSTALL where implemented, prohibited setting them from a parent project when using this via add_subdirectory, this fix allows to set them again.
  • Major refactoring for #90, the way, the Windows version of fs::path was originally created from the POSIX based implementation was, by adaption of the incoming and outgoing strings. This resulted in a mutable cache inside fs::pathon Windows, that was inherently not thread-safe, even for const methods. To not add additional patches to a suboptimal solution, this time I reworked the path code to now store native path-representation. This changed a lot of code, but when combined with wchar_t as value_type helped to avoid lots of conversion for calls to Win-API.
    As interfaces where changed, it had to be released in a new minor version. The set of refactorings resulted in the following changes:
    • fs::path::native() and fs::path::c_str() can now be noexcept as the standard mandates
    • On Windows wchar_t is now the default for fs::path::value_type and std::wstring is the default for fs::path::string_type.
    • This allows the implementation to call Win-API without allocating conversions
    • Thread-safety on const methods of fs::path is no longer an issue
    • Some code could be simplified during this refactoring
    • Automatic prefixing of long path on Windows can now be disabled with defining GHC_WIN_DISABLE_AUTO_PREFIXES, for all other types of prefixes or namespaces the behavior follows that of MSVC std::filesystem::path
    • In case the old char/std::string based approach for Windows is still needed, it can be activated with GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
  • Enhancement for #89, fs::file_status now supports operator== introduced in std::filesystem with C++20.
  • Refactoring for #88, fs::path::parent_path() had a performance issue, as it was still using a loop based approach to recreate the parent from elements. This created lots of temporaries and was too slow especially on long paths.
  • Enhancements for #71, when compiled with C++20:
    • char8_t and std::u8string are supported where Source is the parameter type
    • fs::path::u8string() and fs::path::generic_u8string() now return a std::u8string
    • The spaceship operator <=> is now supported for fs::path
    • With the define GHC_FILESYSTEM_ENFORCE_CPP17_API ghc::filesystem will fall back to the old fs::path::u8string() and fs::path::generic_u8string() API if preferred
  • Bugfix for fs::proximate(p, ec) where the internal call to fs::current_path() was not using the error_code variant, throwing possible exceptions instead of setting ec.
  • Enhancement LWG_2936_BEHAVIOUR is now on by default.
  • Some cleanup work to reduce preprocessor directives for better readability and remove unneeded template specializations.
  • Fix for #81, fixed issues with handling Source parameters that are string views.
  • Fix for #79, the bit operations for filesystem bitmasks that should be are now constexpr.
  • Refactoring for #78, the dynamic switching helper includes are now using __MAC_OS_X_VERSION_MIN_REQUIRED to ensure that std::filesystem is only selected on macOS if the deployment target is at least Catalina.
  • Bugfix for #77, the directory_iterator and the recursive_directory_iterator had an issue with the skip_permission_denied option, that leads to the inability to skip SIP protected folders on macOS.
  • Enhancement for #76,