From d654bf3449a24d412378af6c3aea53fa2899ad7d Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Sun, 21 Jun 2020 14:28:37 +0200 Subject: STL Support: Add cm::filesystem::path in --- Help/dev/source.rst | 3 + Source/Checks/cm_cxx_features.cmake | 5 + Source/Checks/cm_cxx_filesystem.cxx | 10 + Tests/CMakeLib/CMakeLists.txt | 3 + Tests/CMakeLib/testCMFilesystemPath.cxx | 1006 ++++++++++++++++++++++++++ Utilities/std/CMakeLists.txt | 4 +- Utilities/std/cm/bits/fs_path.cxx | 989 ++++++++++++++++++++++++++ Utilities/std/cm/filesystem | 1173 +++++++++++++++++++++++++++++++ Utilities/std/cmSTL.hxx.in | 1 + bootstrap | 4 +- 10 files changed, 3196 insertions(+), 2 deletions(-) create mode 100644 Source/Checks/cm_cxx_filesystem.cxx create mode 100644 Tests/CMakeLib/testCMFilesystemPath.cxx create mode 100644 Utilities/std/cm/bits/fs_path.cxx create mode 100644 Utilities/std/cm/filesystem diff --git a/Help/dev/source.rst b/Help/dev/source.rst index 5fbc9fa..d93e55c 100644 --- a/Help/dev/source.rst +++ b/Help/dev/source.rst @@ -56,6 +56,9 @@ Available features are: * ````: ``cm::clamp`` + * ``cm/filesystem>``: + ``cm::filesystem::path`` + * ````: ``cm::size``, ``cm::empty``, ``cm::data`` diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 50ccc7c..e726fc7 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -63,3 +63,8 @@ if(CMake_HAVE_CXX_MAKE_UNIQUE) set(CMake_HAVE_CXX_UNIQUE_PTR 1) endif() cm_check_cxx_feature(unique_ptr) +if (NOT CMAKE_CXX_STANDARD LESS "17") + cm_check_cxx_feature(filesystem) +else() + set(CMake_HAVE_CXX_FILESYSTEM FALSE) +endif() diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx new file mode 100644 index 0000000..e508d1c --- /dev/null +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -0,0 +1,10 @@ + +#include + +int main() +{ + std::filesystem::path p1("/a/b/c"); + std::filesystem::path p2("/a/b/c"); + + return p1 == p2 ? 0 : 1; +} diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index bb50d76..0b2c8f6 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -29,6 +29,9 @@ set(CMakeLib_TESTS testCMExtMemory.cxx testCMExtAlgorithm.cxx ) +if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) + list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx) +endif() add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx) diff --git a/Tests/CMakeLib/testCMFilesystemPath.cxx b/Tests/CMakeLib/testCMFilesystemPath.cxx new file mode 100644 index 0000000..1e84520 --- /dev/null +++ b/Tests/CMakeLib/testCMFilesystemPath.cxx @@ -0,0 +1,1006 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +namespace fs = cm::filesystem; + +void checkResult(bool success) +{ + if (!success) { + std::cout << " => failed"; + } + std::cout << std::endl; +} + +bool testConstructors() +{ + std::cout << "testConstructors()"; + + bool result = true; + + { + fs::path p; + if (p != fs::path()) { + result = false; + } + } + { + fs::path p1("/a/b/c"); + fs::path p2("/a/b/c"); + if (p1 != p2) { + result = false; + } + if (p1.string() != p2.string()) { + result = false; + } + if (p1.string() != "/a/b/c") { + result = false; + } + } + { + std::string s("/a/b/c"); + fs::path p1(s); + fs::path p2(s.begin(), s.end()); + if (p1 != p2) { + result = false; + } + if (p1.string() != s || p2.string() != s) { + result = false; + } +#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR + std::string s2(s); + s2 += '\0'; + fs::path p3(s2.begin()); + if (p1 != p3 || p3.string() != s) { + result = false; + } +#endif + } + { + std::wstring s(L"/a/b/c"); + fs::path p1(s); + fs::path p2(s.begin(), s.end()); + if (p1 != p2) { + result = false; + } + if (p1.wstring() != s || p2.wstring() != s) { + result = false; + } +#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR + std::wstring s2(s); + s2 += L'\0'; + fs::path p3(s2.begin()); + if (p1 != p3 || p3.wstring() != s) { + result = false; + } +#endif + } + { + std::string s("/a/b/c"); + fs::path::string_type ws; + for (auto c : s) { + ws += fs::path::value_type(c); + } + fs::path p1(ws); + fs::path p2(ws.begin(), ws.end()); + if (p1 != p2) { + result = false; + } + if (p1.native() != ws || p2.native() != ws) { + result = false; + } +#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR + fs::path::string_type ws2(ws); + ws2 += fs::path::value_type('\0'); + fs::path p3(ws2.begin()); + if (p1 != p3 || p3.native() != ws) { + result = false; + } +#endif + } + + checkResult(result); + + return result; +} + +bool testConcatenation() +{ + std::cout << "testConcatenation()"; + + bool result = true; + + { + fs::path p("/a/b"); + p /= "c"; + if (!(p.string() == "/a/b/c" || p.string() == "/a/b\\c")) { + result = false; + } + p += "d"; + if (!(p.string() == "/a/b/cd" || p.string() == "/a/b\\cd")) { + result = false; + } + fs::path p2("x/y"); + p /= p2; + if (!(p.string() == "/a/b/cd/x/y" || p.string() == "/a/b\\cd\\x/y")) { + result = false; + } + p = p / p2; + if (!(p.string() == "/a/b/cd/x/y/x/y" || + p.string() == "/a/b\\cd\\x/y\\x/y")) { + result = false; + } + } + { + fs::path p("a"); + p /= ""; + if (!(p.string() == "a/" || p.string() == "a\\")) { + result = false; + } + p /= "/b"; + if (p.string() != "/b") { + result = false; + } + } +#if defined(_WIN32) + { + fs::path p("a"); + p /= "c:/b"; + if (p.string() != "c:/b") { + result = false; + } + p = fs::path("a") / "c:"; + if (p.string() != "c:") { + result = false; + } + p = fs::path("c:") / ""; + if (p.string() != "c:") { + result = false; + } + p = fs::path("c:a") / "/b"; + if (p.string() != "c:/b") { + result = false; + } + p = fs::path("c:a") / "c:b"; + if (p.string() != "c:a\\b") { + result = false; + } + p = fs::path("//host") / "b"; + if (p.string() != "//host\\b") { + result = false; + } + p = fs::path("//host/") / "b"; + if (p.string() != "//host/b") { + result = false; + } + } +#endif + + checkResult(result); + + return result; +} + +bool testModifiers() +{ + std::cout << "testModifiers()"; + + bool result = true; + + { + std::string s("a///b/"); + fs::path p(s); + std::replace( + s.begin(), s.end(), '/', + static_cast(fs::path::preferred_separator)); + p.make_preferred(); + if (p.string() != s) { + result = false; + } + } + { + fs::path p("a/b/c.e.f"); + p.remove_filename(); + if (p.string() != "a/b/") { + result = false; + } + p.remove_filename(); + if (p.string() != "a/b/") { + result = false; + } + } + { + fs::path p("a/b/c.e.f"); + p.replace_filename("x.y"); + if (p.string() != "a/b/x.y") { + result = false; + } + } + { + fs::path p("a/b/c.e.f"); + p.replace_extension(".x"); + if (p.string() != "a/b/c.e.x") { + result = false; + } + p.replace_extension(".y"); + if (p.string() != "a/b/c.e.y") { + result = false; + } + p.replace_extension(); + if (p.string() != "a/b/c.e") { + result = false; + } + p = "/a/b"; + p.replace_extension(".x"); + if (p.string() != "/a/b.x") { + result = false; + } + p = "/a/b/"; + p.replace_extension(".x"); + if (p.string() != "/a/b/.x") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testObservers() +{ + std::cout << "testObservers()"; + + bool result = true; + + { + std::string s("a/b/c"); + fs::path p(s); + fs::path::string_type st; + for (auto c : s) { + st += static_cast(c); + } + if (p.native() != st || static_cast(p) != st || + p.c_str() != st) { + result = false; + } + } + { + std::string s("a//b//c"); + std::wstring ws(L"a//b//c"); + fs::path p(s); + if (p.string() != s || p.wstring() != ws) { + result = false; + } + } + { + std::string s("a/b/c"); + std::wstring ws; + for (auto c : s) { + ws += static_cast(c); + } + std::string ns(s); + std::replace( + ns.begin(), ns.end(), '/', + static_cast(fs::path::preferred_separator)); + fs::path p(ns); + if (p.generic_string() != s || p.generic_wstring() != ws) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testCompare() +{ + std::cout << "testCompare()"; + + bool result = true; + + { + std::string s("a/b/c"); + fs::path p1(s); + fs::path p2(s); + if (p1.compare(p2) != 0) { + result = false; + } + p2 = "a/b"; + if (p1.compare(p2) <= 0) { + result = false; + } + p2 = "a/d"; + if (p1.compare(p2) >= 0) { + result = false; + } + p2 = "a/b/d"; + if (p1.compare(p2) >= 0) { + result = false; + } + p2 = "a/b/a"; + if (p1.compare(p2) <= 0) { + result = false; + } + p2 = "a/b/c/d"; + if (p1.compare(p2) >= 0) { + result = false; + } + p1 = "a"; + p2 = "b"; + if (p1.compare(p2) == 0) { + result = false; + } + } + { + // LWG 3096 (https://cplusplus.github.io/LWG/issue3096) + // fs::path p1("/a/"); + // fs::path p2("/a/."); + // if (p1.compare(p2) != 0) { + // result = false; + // } + } + + checkResult(result); + + return result; +} + +bool testGeneration() +{ + std::cout << "testGeneration()"; + + bool result = true; + + { + fs::path p("a/./b/.."); + if (p.lexically_normal().generic_string() != "a/") { + result = false; + } + p = "a/.///b/../"; + if (p.lexically_normal().generic_string() != "a/") { + result = false; + } + } +#if defined(_WIN32) + { + fs::path p("//host/./b/.."); + if (p.lexically_normal().string() != "\\\\host\\") { + result = false; + } + p = "//host/.///b/../"; + if (p.lexically_normal().string() != "\\\\host\\") { + result = false; + } + p = "c://a/.///b/../"; + if (p.lexically_normal().string() != "c:\\a\\") { + result = false; + } + } +#endif + + { + if (fs::path("/a//d").lexically_relative("/a/b/c") != "../../d") { + result = false; + } + if (fs::path("/a//b///c").lexically_relative("/a/d") != "../b/c") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a") != "b/c") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a/b/c/x/y") != "../..") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a/b/c") != ".") { + result = false; + } + if (fs::path("a/b").lexically_relative("c/d") != "../../a/b") { + result = false; + } + } + { +#if defined(_WIN32) + if (fs::path("/a/d").lexically_relative("e/d/c") != "/a/d") { + result = false; + } + if (!fs::path("c:/a/d").lexically_relative("e/d/c").empty()) { + result = false; + } +#else + if (!fs::path("/a/d").lexically_relative("e/d/c").empty()) { + result = false; + } +#endif + } + { +#if defined(_WIN32) + if (fs::path("c:/a/d").lexically_proximate("e/d/c") != "c:/a/d") { + result = false; + } +#else + if (fs::path("/a/d").lexically_proximate("e/d/c") != "/a/d") { + result = false; + } +#endif + if (fs::path("/a/d").lexically_proximate("/a/b/c") != "../../d") { + result = false; + } + } + // LWG 3070 + { +#if defined(_WIN32) + if (!fs::path("/a:/b:").lexically_relative("/a:/c:").empty()) { + result = false; + } + if (fs::path("c:/a/b").lexically_relative("c:/a/d") != "../b") { + result = false; + } + if (!fs::path("c:/a/b:").lexically_relative("c:/a/d").empty()) { + result = false; + } + if (!fs::path("c:/a/b").lexically_relative("c:/a/d:").empty()) { + result = false; + } +#else + if (fs::path("/a:/b:").lexically_relative("/a:/c:") != "../b:") { + result = false; + } +#endif + } + // LWG 3096 + { + if (fs::path("/a").lexically_relative("/a/.") != ".") { + result = false; + } + if (fs::path("/a").lexically_relative("/a/") != ".") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a/b/c") != ".") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a/b/c/") != ".") { + result = false; + } + if (fs::path("a/b/c").lexically_relative("a/b/c/.") != ".") { + result = false; + } + if (fs::path("a/b/c/").lexically_relative("a/b/c") != ".") { + result = false; + } + if (fs::path("a/b/c/.").lexically_relative("a/b/c") != ".") { + result = false; + } + if (fs::path("a/b/c/.").lexically_relative("a/b/c/") != ".") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testDecomposition() +{ + std::cout << "testDecomposition()"; + + bool result = true; + + { + if (!fs::path("/a/b").root_name().empty()) { + result = false; + } +#if defined(_WIN32) + if (fs::path("c:/a/b").root_name() != "c:") { + result = false; + } + if (fs::path("c:a/b").root_name() != "c:") { + result = false; + } + if (fs::path("c:").root_name() != "c:") { + result = false; + } + if (fs::path("//host/b").root_name() != "//host") { + result = false; + } + if (fs::path("//host").root_name() != "//host") { + result = false; + } +#endif + } + { + if (!fs::path("a/b").root_directory().empty()) { + result = false; + } + if (fs::path("/a/b").root_directory() != "/") { + result = false; + } +#if defined(_WIN32) + if (!fs::path("c:a/b").root_directory().empty()) { + result = false; + } + if (fs::path("/a/b").root_directory() != "/") { + result = false; + } + if (fs::path("c:/a/b").root_directory() != "/") { + result = false; + } + if (fs::path("//host/b").root_directory() != "/") { + result = false; + } +#endif + } + { + if (!fs::path("a/b").root_path().empty()) { + result = false; + } + if (fs::path("/a/b").root_path() != "/") { + result = false; + } +#if defined(_WIN32) + if (fs::path("c:a/b").root_path() != "c:") { + result = false; + } + if (fs::path("/a/b").root_path() != "/") { + result = false; + } + if (fs::path("c:/a/b").root_path() != "c:/") { + result = false; + } + if (fs::path("//host/b").root_path() != "//host/") { + result = false; + } +#endif + } + { + if (!fs::path("/").relative_path().empty()) { + result = false; + } + if (fs::path("a/b").relative_path() != "a/b") { + result = false; + } + if (fs::path("/a/b").relative_path() != "a/b") { + result = false; + } +#if defined(_WIN32) + if (fs::path("c:a/b").relative_path() != "a/b") { + result = false; + } + if (fs::path("/a/b").relative_path() != "a/b") { + result = false; + } + if (fs::path("c:/a/b").relative_path() != "a/b") { + result = false; + } + if (fs::path("//host/b").relative_path() != "b") { + result = false; + } +#endif + } + { + if (fs::path("/a/b").parent_path() != "/a") { + result = false; + } + if (fs::path("/a/b/").parent_path() != "/a/b") { + result = false; + } + if (fs::path("/a/b/.").parent_path() != "/a/b") { + result = false; + } + if (fs::path("/").parent_path() != "/") { + result = false; + } +#if defined(_WIN32) + if (fs::path("c:/a/b").parent_path() != "c:/a") { + result = false; + } + if (fs::path("c:/").parent_path() != "c:/") { + result = false; + } + if (fs::path("c:").parent_path() != "c:") { + result = false; + } + if (fs::path("//host/").parent_path() != "//host/") { + result = false; + } + if (fs::path("//host").parent_path() != "//host") { + result = false; + } +#endif + } + { + if (fs::path("/a/b.txt").filename() != "b.txt") { + result = false; + } + if (fs::path("/a/.b").filename() != ".b") { + result = false; + } + if (fs::path("/foo/bar/").filename() != "") { + result = false; + } + if (fs::path("/foo/.").filename() != ".") { + result = false; + } + if (fs::path("/foo/..").filename() != "..") { + result = false; + } + if (fs::path(".").filename() != ".") { + result = false; + } + if (fs::path("..").filename() != "..") { + result = false; + } + if (!fs::path("/").filename().empty()) { + result = false; + } +#if defined(_WIN32) + if (fs::path("c:a").filename() != "a") { + result = false; + } + if (fs::path("c:/a").filename() != "a") { + result = false; + } + if (!fs::path("c:").filename().empty()) { + result = false; + } + if (!fs::path("c:/").filename().empty()) { + result = false; + } + if (!fs::path("//host").filename().empty()) { + result = false; + } +#endif + } + { + if (fs::path("/a/b.txt").stem() != "b") { + result = false; + } + if (fs::path("/a/b.c.txt").stem() != "b.c") { + result = false; + } + if (fs::path("/a/.b").stem() != ".b") { + result = false; + } + if (fs::path("/a/b").stem() != "b") { + result = false; + } + if (fs::path("/a/b/.").stem() != ".") { + result = false; + } + if (fs::path("/a/b/..").stem() != "..") { + result = false; + } + if (!fs::path("/a/b/").stem().empty()) { + result = false; + } +#if defined(_WIN32) + if (!fs::path("c:/a/b/").stem().empty()) { + result = false; + } + if (!fs::path("c:/").stem().empty()) { + result = false; + } + if (!fs::path("c:").stem().empty()) { + result = false; + } + if (!fs::path("//host/").stem().empty()) { + result = false; + } + if (!fs::path("//host").stem().empty()) { + result = false; + } +#endif + } + { + if (fs::path("/a/b.txt").extension() != ".txt") { + result = false; + } + if (fs::path("/a/b.").extension() != ".") { + result = false; + } + if (!fs::path("/a/b").extension().empty()) { + result = false; + } + if (fs::path("/a/b.txt/b.cc").extension() != ".cc") { + result = false; + } + if (fs::path("/a/b.txt/b.").extension() != ".") { + result = false; + } + if (!fs::path("/a/b.txt/b").extension().empty()) { + result = false; + } + if (!fs::path("/a/.").extension().empty()) { + result = false; + } + if (!fs::path("/a/..").extension().empty()) { + result = false; + } + if (!fs::path("/a/.hidden").extension().empty()) { + result = false; + } + if (fs::path("/a/..b").extension() != ".b") { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testQueries() +{ + std::cout << "testQueries()"; + + bool result = true; + + { + if (fs::path("/a/b").has_root_name()) { + result = false; + } + fs::path p("/a/b"); + if (!p.has_root_directory() || !p.has_root_path()) { + result = false; + } + if (!fs::path("/a/b").has_root_path() || fs::path("a/b").has_root_path()) { + result = false; + } + if (!fs::path("/a/b").has_relative_path() || + fs::path("/").has_relative_path()) { + result = false; + } + if (!fs::path("/a/b").has_parent_path() || + !fs::path("/").has_parent_path() || fs::path("a").has_parent_path()) { + result = false; + } + if (!fs::path("/a/b").has_filename() || !fs::path("a.b").has_filename() || + fs::path("/a/").has_filename() || fs::path("/").has_filename()) { + result = false; + } + if (!fs::path("/a/b").has_stem() || !fs::path("a.b").has_stem() || + !fs::path("/.a").has_stem() || fs::path("/a/").has_stem() || + fs::path("/").has_stem()) { + result = false; + } + if (!fs::path("/a/b.c").has_extension() || + !fs::path("a.b").has_extension() || fs::path("/.a").has_extension() || + fs::path("/a/").has_extension() || fs::path("/").has_extension()) { + result = false; + } +#if defined(_WIN32) + p = "c:/a/b"; + if (!fs::path("c:/a/b").has_root_name() || !p.has_root_directory() || + !p.has_root_path()) { + result = false; + } + p = "c:a/b"; + if (!p.has_root_name() || p.has_root_directory() || !p.has_root_path()) { + result = false; + } + p = "//host/b"; + if (!p.has_root_name() || !p.has_root_directory() || !p.has_root_path()) { + result = false; + } + p = "//host"; + if (!p.has_root_name() || p.has_root_directory() || !p.has_root_path()) { + result = false; + } + if (!fs::path("c:/a/b").has_relative_path() || + !fs::path("c:a/b").has_relative_path() || + !fs::path("//host/b").has_relative_path()) { + result = false; + } + if (!fs::path("c:/a/b").has_parent_path() || + !fs::path("c:/").has_parent_path() || + !fs::path("c:").has_parent_path() || + !fs::path("//host/").has_parent_path() || + !fs::path("//host").has_parent_path()) { + result = false; + } +#endif + } + { +#if defined(_WIN32) + fs::path p("c:/a"); +#else + fs::path p("/a"); +#endif + if (!p.is_absolute() || p.is_relative()) { + result = false; + } + p = "a/b"; + if (p.is_absolute() || !p.is_relative()) { + result = false; + } +#if defined(_WIN32) + p = "c:/a/b"; + if (!p.is_absolute() || p.is_relative()) { + result = false; + } + p = "//host/b"; + if (!p.is_absolute() || p.is_relative()) { + result = false; + } + p = "/a"; + if (p.is_absolute() || !p.is_relative()) { + result = false; + } + p = "c:a"; + if (p.is_absolute() || !p.is_relative()) { + result = false; + } +#endif + } + + checkResult(result); + + return result; +} + +bool testIterators() +{ + std::cout << "testIterators()"; + + bool result = true; + + { + fs::path p("/a/b/"); +#if defined(_WIN32) + std::vector ref{ L"/", L"a", L"b", L"" }; +#else + std::vector ref{ "/", "a", "b", "" }; +#endif + std::vector res; + for (auto i = p.begin(), e = p.end(); i != e; ++i) { + res.push_back(*i); + } + if (res != ref) { + result = false; + } + res.clear(); + for (const auto& e : p) { + res.push_back(e); + } + if (res != ref) { + result = false; + } + } + { + fs::path p("/a/b/"); +#if defined(_WIN32) + std::vector ref{ L"", L"b", L"a", L"/" }; +#else + std::vector ref{ "", "b", "a", "/" }; +#endif + std::vector res; + auto i = p.end(), b = p.begin(); + do { + res.push_back(*--i); + } while (i != b); + if (res != ref) { + result = false; + } + } + + checkResult(result); + + return result; +} + +bool testNonMemberFunctions() +{ + std::cout << "testNonMemberFunctions()"; + + bool result = true; + + { + fs::path p1("/a/b/"); + fs::path p2("/c/d"); + fs::swap(p1, p2); + if (p1.string() != "/c/d" || p2.string() != "/a/b/") + result = false; + } + { + auto h1 = fs::hash_value(fs::path("/a//b//")); + auto h2 = fs::hash_value(fs::path("/a/b/")); + if (h1 != h2) + result = false; + } + { + fs::path p1("/a/b/"); + fs::path p2("/c/d"); + if (p1 == p2) + result = false; + p1 = "/a//b//"; + p2 = "/a/b/"; + if (p1 != p2) + result = false; + } + { + fs::path p = "/a"; + p = p / "b" / "c"; + if (p.generic_string() != "/a/b/c") { + result = false; + } + fs::path::string_type ref; + ref += fs::path::value_type('/'); + ref += fs::path::value_type('a'); + ref += fs::path::preferred_separator; + ref += fs::path::value_type('b'); + ref += fs::path::preferred_separator; + ref += fs::path::value_type('c'); + if (p.native() != ref) { + result = false; + } + } + { + fs::path p("/a b\\c/"); + std::ostringstream oss; + oss << p; + if (oss.str() != "\"/a b\\\\c/\"") { + result = false; + } + std::istringstream iss(oss.str()); + fs::path p2; + iss >> p2; + if (p2 != p) { + result = false; + } + } + + checkResult(result); + + return result; +} +} + +int testCMFilesystemPath(int /*unused*/, char* /*unused*/ []) +{ + int result = 0; + + if (!testConstructors()) { + result = 1; + } + if (!testConcatenation()) { + result = 1; + } + if (!testModifiers()) { + result = 1; + } + if (!testObservers()) { + result = 1; + } + if (!testCompare()) { + result = 1; + } + if (!testGeneration()) { + result = 1; + } + if (!testDecomposition()) { + result = 1; + } + if (!testQueries()) { + result = 1; + } + if (!testIterators()) { + result = 1; + } + if (!testNonMemberFunctions()) { + result = 1; + } + + return result; +} diff --git a/Utilities/std/CMakeLists.txt b/Utilities/std/CMakeLists.txt index 17a7aaa..23d9104 100644 --- a/Utilities/std/CMakeLists.txt +++ b/Utilities/std/CMakeLists.txt @@ -4,7 +4,9 @@ set(CMAKE_CXX_EXTENSIONS FALSE) # source files for CMake std library -set(SRCS cm/bits/string_view.cxx +set(SRCS cm/bits/fs_path.cxx + cm/bits/string_view.cxx + cm/filesystem cm/memory cm/optional cm/shared_mutex diff --git a/Utilities/std/cm/bits/fs_path.cxx b/Utilities/std/cm/bits/fs_path.cxx new file mode 100644 index 0000000..71386bb --- /dev/null +++ b/Utilities/std/cm/bits/fs_path.cxx @@ -0,0 +1,989 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include // IWYU pragma: associated + +#if !defined(CMake_HAVE_CXX_FILESYSTEM) + +# include +# include +# include +# include +# include +# include +# include +# include +# if defined(_WIN32) +# include +# include +# endif + +# include +# include +# include + +namespace cm { +namespace filesystem { +namespace internals { + +class path_parser +{ +# if defined(__SUNPRO_CC) && defined(__sparc) + // Oracle DeveloperStudio C++ compiler generates wrong code if enum size + // is different than the default. + using enum_size = int; +# else + using enum_size = unsigned char; +# endif + + enum class state : enum_size + { + before_begin, + in_root_name, + in_root_dir, + in_filename, + trailing_separator, + at_end + }; + + using pointer = char const*; + +public: + enum class seek_position : enum_size + { + root_name = static_cast(state::in_root_name), + root_directory = static_cast(state::in_root_dir) + }; + enum class peek_fragment : enum_size + { + remainder, + path + }; + + path_parser(cm::string_view path, bool set_at_end = false) + : State(set_at_end ? state::at_end : state::before_begin) + , Path(path) + { + } + + path_parser(const path_parser&) = default; + + ~path_parser() = default; + + void reset() noexcept { this->set_state(state::before_begin); } + + void increment() noexcept + { + const pointer start = this->next_token(); + const pointer end = this->after_end(); + + if (start == end) { + this->set_state(state::at_end); + return; + } + + switch (this->State) { + case state::before_begin: { + auto pos = this->consume_root_name(start, end); + if (pos) { + this->set_state(state::in_root_name); + } else { + pos = this->consume_separator(start, end); + if (pos) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(start, end); + this->set_state(state::in_filename); + } + } + break; + } + case state::in_root_name: { + auto pos = this->consume_separator(start, end); + if (pos) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(start, end); + this->set_state(state::in_filename); + } + break; + } + case state::in_root_dir: { + this->consume_filename(start, end); + this->set_state(state::in_filename); + break; + } + case state::in_filename: { + auto posSep = this->consume_separator(start, end); + if (posSep != end) { + auto pos = this->consume_filename(posSep, end); + if (pos) { + return; + } + } + set_state(state::trailing_separator); + break; + } + case state::trailing_separator: { + this->set_state(state::at_end); + break; + } + case state::at_end: + // unreachable + std::abort(); + } + } + + void decrement() noexcept + { + const pointer rstart = this->current_token() - 1; + const pointer rend = this->before_start(); + + if (rstart == rend) { + this->set_state(state::before_begin); + return; + } + + switch (this->State) { + case state::at_end: { + auto posSep = this->consume_separator(rstart, rend); + if (posSep) { + if (posSep == rend) { + this->set_state(state::in_root_dir); + } else { + auto pos = this->consume_root_name(posSep, rend, true); + if (pos == rend) { + this->set_state(state::in_root_dir); + } else { + this->set_state(state::trailing_separator); + } + } + } else { + auto pos = this->consume_root_name(rstart, rend); + if (pos == rend) { + this->set_state(state::in_root_name); + } else { + this->consume_filename(rstart, rend); + this->set_state(state::in_filename); + } + } + break; + } + case state::trailing_separator: { + this->consume_filename(rstart, rend); + this->set_state(state::in_filename); + break; + } + case state::in_filename: { + auto posSep = this->consume_separator(rstart, rend); + if (posSep == rend) { + this->set_state(state::in_root_dir); + } else { + auto pos = this->consume_root_name(posSep, rend, true); + if (pos == rend) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(posSep, rend); + this->set_state(state::in_filename); + } + } + break; + } + case state::in_root_dir: { + auto pos = this->consume_root_name(rstart, rend); + if (pos) { + this->set_state(state::in_root_name); + } + break; + } + case state::in_root_name: + case state::before_begin: { + // unreachable + std::abort(); + } + } + } + + path_parser& operator++() noexcept + { + this->increment(); + return *this; + } + + path_parser& operator--() noexcept + { + this->decrement(); + return *this; + } + + cm::string_view operator*() const noexcept + { + switch (this->State) { + case state::before_begin: + case state::at_end: + return cm::string_view(); + case state::trailing_separator: + return ""; + case state::in_root_dir: + case state::in_root_name: + case state::in_filename: + return this->Entry; + default: + // unreachable + std::abort(); + } + } + + void seek(seek_position position) + { + state s = static_cast(static_cast(position)); + + while (this->State <= s) { + this->increment(); + } + } + + cm::string_view peek(peek_fragment fragment) + { + if (fragment == peek_fragment::remainder) { + // peek-up remain part of the initial path + return { this->Entry.data(), + std::size_t(&this->Path.back() - this->Entry.data() + 1) }; + } + if (fragment == peek_fragment::path) { + // peek-up full path until current position + return { this->Path.data(), + std::size_t(&this->Entry.back() - this->Path.data() + 1) }; + } + return {}; + } + + bool in_root_name() const { return this->State == state::in_root_name; } + bool in_root_directory() const { return this->State == state::in_root_dir; } + bool at_end() const { return this->State == state::at_end; } + + bool at_start() const { return this->Entry.data() == this->Path.data(); } + +private: + void set_state(state newState) noexcept + { + this->State = newState; + if (newState == state::before_begin || newState == state::at_end) { + this->Entry = {}; + } + } + + pointer before_start() const noexcept { return this->Path.data() - 1; } + pointer after_end() const noexcept + { + return this->Path.data() + this->Path.size(); + } + + pointer current_token() const noexcept + { + switch (this->State) { + case state::before_begin: + case state::in_root_name: + return &this->Path.front(); + case state::in_root_dir: + case state::in_filename: + case state::trailing_separator: + return &this->Entry.front(); + case state::at_end: + return &this->Path.back() + 1; + default: + // unreachable + std::abort(); + } + } + pointer next_token() const noexcept + { + switch (this->State) { + case state::before_begin: + return this->Path.data(); + case state::in_root_name: + case state::in_root_dir: + case state::in_filename: + return &this->Entry.back() + 1; + case state::trailing_separator: + case state::at_end: + return after_end(); + default: + // unreachable + std::abort(); + } + } + + pointer consume_separator(pointer ptr, pointer end) noexcept + { + if (ptr == end || + (*ptr != '/' +# if defined(_WIN32) + && *ptr != '\\' +# endif + )) { + return nullptr; + } + const auto step = ptr < end ? 1 : -1; + ptr += step; + while (ptr != end && + (*ptr == '/' +# if defined(_WIN32) + || *ptr == ' \\' +# endif + )) { + ptr += step; + } + if (step == 1) { + this->Entry = cm::string_view(ptr - 1, 1); + } else { + this->Entry = cm::string_view(ptr + 1, 1); + } + + return ptr; + } + + pointer consume_filename(pointer ptr, pointer end) noexcept + { + auto start = ptr; + + if (ptr == end || *ptr == '/' +# if defined(_WIN32) + || *ptr == '\\' +# endif + ) { + return nullptr; + } + const auto step = ptr < end ? 1 : -1; + ptr += step; + while (ptr != end && *ptr != '/' +# if defined(_WIN32) + && *ptr != '\\' +# endif + ) { + ptr += step; + } + +# if defined(_WIN32) + if (step == -1 && (start - ptr) >= 2 && ptr == end) { + // rollback drive name consumption, if any + if (this->is_drive_name(ptr + 1)) { + ptr += 2; + } + if (ptr == start) { + return nullptr; + } + } +# endif + + if (step == 1) { + this->Entry = cm::string_view(start, ptr - start); + } else { + this->Entry = cm::string_view(ptr + 1, start - ptr); + } + + return ptr; + } + +# if defined(_WIN32) + bool is_drive_name(pointer ptr) + { + return std::toupper(ptr[0]) >= 'A' && std::toupper(ptr[0]) <= 'Z' && + ptr[1] == ':'; + } +# endif + + pointer consume_root_name(pointer ptr, pointer end, + bool check_only = false) noexcept + { +# if defined(_WIN32) + if (ptr < end) { + if ((end - ptr) >= 2 && this->is_drive_name(ptr)) { + // Drive letter (X:) is a root name + if (!check_only) { + this->Entry = cm::string_view(ptr, 2); + } + return ptr + 2; + } + if ((end - ptr) > 2 && (ptr[0] == '/' || ptr[0] == '\\') && + (ptr[1] == '/' || ptr[1] == '\\') && + (ptr[2] != '/' && ptr[2] != '\\')) { + // server name (//server) is a root name + auto pos = std::find(ptr + 2, end, '/'); + if (!check_only) { + this->Entry = cm::string_view(ptr, pos - ptr); + } + return pos; + } + } else { + if ((ptr - end) >= 2 && this->is_drive_name(ptr - 1)) { + // Drive letter (X:) is a root name + if (!check_only) { + this->Entry = cm::string_view(ptr - 1, 2); + } + return ptr - 2; + } + if ((ptr - end) > 2 && (ptr[0] != '/' && ptr[0] != '\\')) { + std::reverse_iterator start(ptr); + std::reverse_iterator stop(end); + auto res = std::find_if(start, stop, + [](char c) { return c == '/' || c == '\\'; }); + pointer pos = res.base() - 1; + if ((pos - 1) > end && (pos[-1] == '/' || pos[-1] == '\\')) { + // server name (//server) is a root name + if (!check_only) { + this->Entry = cm::string_view(pos - 1, ptr - pos + 2); + } + return pos - 2; + } + } + } +# else + (void)ptr; + (void)end; + (void)check_only; +# endif + return nullptr; + } + + state State; + const cm::string_view Path; + cm::string_view Entry; +}; + +// class unicode_helper +void unicode_helper::append(std::string& str, std::uint32_t codepoint) +{ + if (codepoint <= 0x7f) { + str.push_back(static_cast(codepoint)); + } else if (codepoint >= 0x80 && codepoint <= 0x7ff) { + str.push_back(static_cast((codepoint >> 6) + 192)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) || + (codepoint >= 0xe000 && codepoint <= 0xffff)) { + str.push_back(static_cast((codepoint >> 12) + 224)); + str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) { + str.push_back(static_cast((codepoint >> 18) + 240)); + str.push_back(static_cast(((codepoint & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else { + append(str, 0xfffd); + } +} + +unicode_helper::utf8_state unicode_helper::decode(const utf8_state state, + const std::uint8_t fragment, + std::uint32_t& codepoint) +{ + const std::uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, + 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, + 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, + 0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, + 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, + 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, + 0u, 0u, + }; + std::uint8_t category = fragment < 128 + ? 0 + : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) + : (0xffu >> category) & fragment); + return state == s_reject + ? s_reject + : static_cast( + (utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +} // internals + +// Class path +path& path::operator/=(const path& p) +{ + if (p.is_absolute() || + (p.has_root_name() && p.get_root_name() != this->get_root_name())) { + this->path_ = p.path_; + return *this; + } + if (p.has_root_directory()) { + this->path_ = static_cast(this->get_root_name()); + this->path_ += static_cast(p.get_root_directory()); + } else if (this->has_filename()) { + this->path_ += this->preferred_separator; +# if defined(_WIN32) + // special case: "//host" / "b" => "//host/b" + } else if (this->has_root_name() && !this->has_root_directory()) { + if (this->path_.length() >= 3 && + (this->path_[0] == '/' || this->path_[0] == '\\') && + (this->path_[1] == '/' || this->path_[1] == '\\') && + (this->path_[2] != '/' || this->path_[2] != '\\')) { + this->path_ += this->preferred_separator; + } +# endif + } + + this->path_ += p.get_relative_path(); + return *this; +} + +path path::lexically_normal() const +{ + if (this->path_.empty()) { + return *this; + } + + const cm::string_view dot = "."_s; + const cm::string_view dotdot = ".."_s; + + std::vector root_parts; + std::vector parts; + bool root_directory_defined = false; + bool need_final_separator = false; + std::size_t path_size = 0; + + internals::path_parser parser(this->path_); + ++parser; + while (!parser.at_end()) { + auto part = *parser; + + if (parser.in_root_name() || parser.in_root_directory()) { + if (parser.in_root_directory()) { + root_directory_defined = true; + } + root_parts.push_back(part); + path_size += part.size(); + } else if (part == dotdot) { + if (!parts.empty() && parts.back() != dotdot) { + need_final_separator = true; + path_size -= parts.back().size(); + parts.pop_back(); + } else if ((parts.empty() || parts.back() == dotdot) && + !root_directory_defined) { + parts.push_back(dotdot); + path_size += 2; + } + + } else if (part == dot || part.empty()) { + need_final_separator = true; + if (part.empty()) { + parts.push_back(part); + } + } else { + // filename + need_final_separator = false; + parts.push_back(part); + path_size += part.size(); + } + ++parser; + } + + // no final separator if last element of path is ".." + need_final_separator = + need_final_separator && !parts.empty() && parts.back() != dotdot; + + // build final path + //// compute final size of path + path_size += parts.size() + (need_final_separator ? 1 : 0); + + std::string np; + np.reserve(path_size); + for (const auto& p : root_parts) { + np += p; + } + // convert any slash to the preferred_separator + if (static_cast(this->preferred_separator) != '/') { + std::replace( + np.begin(), np.end(), '/', + static_cast(this->preferred_separator)); + } + for (const auto& p : parts) { + if (!p.empty()) { + np += p; + np += static_cast(this->preferred_separator); + } + } + if (!parts.empty() && !need_final_separator) { + // remove extra separator + np.pop_back(); + } + if (np.empty()) { + np.assign(1, '.'); + } + + return path(std::move(np)); +} + +path path::lexically_relative(const path& base) const +{ + internals::path_parser parser(this->path_); + ++parser; + internals::path_parser parserbase(base.path_); + ++parserbase; + cm::string_view this_root_name, base_root_name; + cm::string_view this_root_dir, base_root_dir; + + if (parser.in_root_name()) { + this_root_name = *parser; + ++parser; + } + if (parser.in_root_directory()) { + this_root_dir = *parser; + ++parser; + } + if (parserbase.in_root_name()) { + base_root_name = *parserbase; + ++parserbase; + } + if (parserbase.in_root_directory()) { + base_root_dir = *parserbase; + ++parserbase; + } + + auto is_path_absolute = [](cm::string_view rn, cm::string_view rd) -> bool { +# if defined(_WIN32) + return !rn.empty() && !rd.empty(); +# else + (void)rn; + return !rd.empty(); +# endif + }; + + if (this_root_name != base_root_name || + is_path_absolute(this_root_name, this_root_dir) != + is_path_absolute(base_root_name, base_root_dir) || + (this_root_dir.empty() && !base_root_dir.empty())) { + return path(); + } + +# if defined(_WIN32) + // LWG3070 handle special case: filename can also be a root-name + auto is_drive_name = [](cm::string_view item) -> bool { + return item.length() == 2 && item[1] == ':'; + }; + parser.reset(); + parser.seek(internals::path_parser::seek_position::root_directory); + while (!parser.at_end()) { + if (is_drive_name(*parser)) { + return path(); + } + ++parser; + } + parserbase.reset(); + parserbase.seek(internals::path_parser::seek_position::root_directory); + while (!parserbase.at_end()) { + if (is_drive_name(*parserbase)) { + return path(); + } + ++parserbase; + } +# endif + + const cm::string_view dot = "."_s; + const cm::string_view dotdot = ".."_s; + + auto a = this->begin(), aend = this->end(); + auto b = base.begin(), bend = base.end(); + while (a != aend && b != bend && a->string() == b->string()) { + ++a; + ++b; + } + + int count = 0; + for (; b != bend; ++b) { + auto part = *b; + if (part == dotdot) { + --count; + } else if (part.string() != dot && !part.empty()) { + ++count; + } + } + + if (count == 0 && (a == this->end() || a->empty())) { + return path(dot); + } + if (count >= 0) { + path result; + path p_dotdot(dotdot); + for (int i = 0; i < count; ++i) { + result /= p_dotdot; + } + for (; a != aend; ++a) { + result /= *a; + } + return result; + } + // count < 0 + return path(); +} + +path::path_type path::get_generic() const +{ + auto gen_path = this->path_; + auto start = gen_path.begin(); +# if defined(_WIN32) + std::replace(gen_path.begin(), gen_path.end(), '\\', '/'); + // preserve special syntax for root_name ('//server' or '//?') + if (gen_path.length() > 2 && gen_path[2] != '/') { + start += 2; + } +# endif + // remove duplicate separators + auto new_end = std::unique(start, gen_path.end(), [](char lhs, char rhs) { + return lhs == rhs && lhs == '/'; + }); + gen_path.erase(new_end, gen_path.end()); + return gen_path; +} + +cm::string_view path::get_root_name() const +{ + internals::path_parser parser(this->path_); + ++parser; + if (parser.in_root_name()) { + return *parser; + } + return {}; +} + +cm::string_view path::get_root_directory() const +{ + internals::path_parser parser(this->path_); + ++parser; + if (parser.in_root_name()) { + ++parser; + } + if (parser.in_root_directory()) { + return *parser; + } + return {}; +} + +cm::string_view path::get_relative_path() const +{ + internals::path_parser parser(this->path_); + parser.seek(internals::path_parser::seek_position::root_directory); + if (parser.at_end()) { + return {}; + } + return parser.peek(internals::path_parser::peek_fragment::remainder); +} + +cm::string_view path::get_parent_path() const +{ + if (!this->has_relative_path()) { + return this->path_; + } + + // peek-up full path minus last element + internals::path_parser parser(this->path_, true); + --parser; + if (parser.at_start()) { + return {}; + } + --parser; + return parser.peek(internals::path_parser::peek_fragment::path); +} + +cm::string_view path::get_filename() const +{ + { + internals::path_parser parser(this->path_); + parser.seek(internals::path_parser::seek_position::root_directory); + if (parser.at_end()) { + return {}; + } + } + { + internals::path_parser parser(this->path_, true); + return *(--parser); + } +} + +cm::string_view path::get_filename_fragment(filename_fragment fragment) const +{ + auto file = this->get_filename(); + + if (file == "." || file == ".." || file.empty()) { + return fragment == filename_fragment::stem ? file : cm::string_view{}; + } + + auto pos = file.find_last_of('.'); + if (pos == cm::string_view::npos || pos == 0) { + return fragment == filename_fragment::stem ? file : cm::string_view{}; + } + return fragment == filename_fragment::stem ? file.substr(0, pos) + : file.substr(pos); +} + +int path::compare_path(cm::string_view str) const +{ + internals::path_parser this_pp(this->path_); + ++this_pp; + internals::path_parser other_pp(str); + ++other_pp; + + // compare root_name part + { + bool compare_root_names = false; + cm::string_view this_root_name, other_root_name; + int res; + + if (this_pp.in_root_name()) { + compare_root_names = true; + this_root_name = *this_pp; + ++this_pp; + } + if (other_pp.in_root_name()) { + compare_root_names = true; + other_root_name = *other_pp; + ++other_pp; + } + if (compare_root_names && + (res = this_root_name.compare(other_root_name) != 0)) { + return res; + } + } + + // compare root_directory part + { + if (!this_pp.in_root_directory() && other_pp.in_root_directory()) { + return -1; + } else if (this_pp.in_root_directory() && !other_pp.in_root_directory()) { + return 1; + } + if (this_pp.in_root_directory()) { + ++this_pp; + } + if (other_pp.in_root_directory()) { + ++other_pp; + } + } + + // compare various parts of the paths + while (!this_pp.at_end() && !other_pp.at_end()) { + int res; + if ((res = (*this_pp).compare(*other_pp)) != 0) { + return res; + } + ++this_pp; + ++other_pp; + } + + // final step + if (this_pp.at_end() && !other_pp.at_end()) { + return -1; + } else if (!this_pp.at_end() && other_pp.at_end()) { + return 1; + } + + return 0; +} + +// Class path::iterator +path::iterator::iterator() + : path_(nullptr) +{ +} +path::iterator::iterator(const iterator& other) +{ + this->path_ = other.path_; + if (other.parser_) { + this->parser_ = cm::make_unique(*other.parser_); + this->path_element_ = path(**this->parser_); + } +} +path::iterator::iterator(const path* p, bool at_end) + : path_(p) + , parser_(cm::make_unique(p->path_, at_end)) +{ + if (!at_end) { + ++(*this->parser_); + this->path_element_ = path(**this->parser_); + } +} + +path::iterator::~iterator() = default; + +path::iterator& path::iterator::operator=(const iterator& other) +{ + this->path_ = other.path_; + if (other.parser_) { + this->parser_ = cm::make_unique(*other.parser_); + this->path_element_ = path(**this->parser_); + } + + return *this; +} + +path::iterator& path::iterator::operator++() +{ + assert(this->parser_); + + if (this->parser_) { + assert(!this->parser_->at_end()); + + if (!this->parser_->at_end()) { + ++(*this->parser_); + if (this->parser_->at_end()) { + this->path_element_ = path(); + } else { + this->path_element_ = path(**this->parser_); + } + } + } + + return *this; +} + +path::iterator& path::iterator::operator--() +{ + assert(this->parser_); + + if (this->parser_) { + assert(!this->parser_->at_start()); + + if (!this->parser_->at_start()) { + --(*this->parser_); + this->path_element_ = path(**this->parser_); + } + } + + return *this; +} + +bool operator==(const path::iterator& lhs, const path::iterator& rhs) +{ + return lhs.path_ == rhs.path_ && lhs.parser_ != nullptr && + ((lhs.parser_->at_end() && rhs.parser_->at_end()) || + (lhs.parser_->at_start() && rhs.parser_->at_start()) || + ((**lhs.parser_).data() == (**rhs.parser_).data())); +} + +std::size_t hash_value(const path& p) noexcept +{ + internals::path_parser parser(p.path_); + std::hash hasher; + std::size_t value = 0; + + while (!parser.at_end()) { + value = hasher(*parser) + 0x9e3779b9 + (value << 6) + (value >> 2); + ++parser; + } + + return value; +} +} // filesystem +} // cm + +#else + +// Avoid empty translation unit. +void cm_filesystem_path_cxx() +{ +} + +#endif diff --git a/Utilities/std/cm/filesystem b/Utilities/std/cm/filesystem new file mode 100644 index 0000000..d7ade34 --- /dev/null +++ b/Utilities/std/cm/filesystem @@ -0,0 +1,1173 @@ +// -*-c++-*- +// vim: set ft=cpp: + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cm_filesystem +#define cm_filesystem + +#include "cmSTL.hxx" // IWYU pragma: keep + +#if defined(CMake_HAVE_CXX_FILESYSTEM) + +# include // IWYU pragma: export + +#else + +# include +# include +# include +# include +# include +# include +# include + +# include +# include +# include +# include + +# if defined(_WIN32) +# include +# endif + +#endif + +namespace cm { +namespace filesystem { + +#if defined(CMake_HAVE_CXX_FILESYSTEM) + +using std::filesystem::path; +using std::filesystem::swap; +using std::filesystem::hash_value; + +#else + +# if !defined(CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR) +// Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile +// the source_traits for iterator check. So disable it for now. +# define CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR 0 +# endif + +namespace internals { + +class path_parser; + +class unicode_helper +{ +protected: + using utf8_state = unsigned char; + static const utf8_state s_start = 0; + static const utf8_state s_reject = 8; + + static inline bool in_range(std::uint32_t c, std::uint32_t lo, + std::uint32_t hi) + { + return (static_cast(c - lo) < (hi - lo + 1)); + } + + static inline bool is_surrogate(std::uint32_t c) + { + return in_range(c, 0xd800, 0xdfff); + } + + static inline bool is_high_surrogate(std::uint32_t c) + { + return (c & 0xfffffc00) == 0xd800; + } + + static inline bool is_low_surrogate(std::uint32_t c) + { + return (c & 0xfffffc00) == 0xdc00; + } + + static void append(std::string& str, std::uint32_t codepoint); + + static utf8_state decode(const utf8_state state, const std::uint8_t fragment, + std::uint32_t& codepoint); +}; + +template +class unicode +{ +}; + +template +class unicode::type> + : public unicode_helper +{ +public: + // UTF32 -> UTF8 + static std::string to_utf8(const std::wstring& str) + { + std::string result; + for (auto c : str) { + append(result, c); + } + return result; + } + static std::string to_utf8(Char c) + { + std::string result; + append(result, c); + return result; + } + + // UTF8 -> UTF32 + static std::wstring from_utf8(const std::string& str) + { + std::wstring result; + result.reserve(str.length()); + auto iter = str.begin(); + utf8_state state = s_start; + std::uint32_t codepoint = 0; + while (iter < str.end()) { + if ((state = decode(state, static_cast(*iter++), + codepoint)) == s_start) { + result += static_cast(codepoint); + codepoint = 0; + } else if (state == s_reject) { + result += static_cast(0xfffd); + state = s_start; + codepoint = 0; + } + } + if (state) { + result += static_cast(0xfffd); + } + return result; + } + static std::wstring from_utf8(char c) + { + std::wstring result; + utf8_state state = s_start; + std::uint32_t codepoint = 0; + if ((state = decode(state, static_cast(c), codepoint)) == + s_start) { + result += static_cast(codepoint); + } else { + result += static_cast(0xfffd); + } + + return result; + } +}; + +template +class unicode::type> + : public unicode_helper +{ +public: + // UTF16 -> UTF8 + static std::string to_utf8(const std::wstring& str) + { + std::string result; + for (auto iter = str.begin(); iter != str.end(); ++iter) { + std::uint32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != str.end() && is_high_surrogate(c) && + is_low_surrogate(*iter)) { + append(result, (std::uint32_t(c) << 10) + *iter - 0x35fdc00); + } else { + append(result, 0xfffd); + if (iter == str.end()) { + break; + } + } + } else { + append(result, c); + } + } + return result; + } + static std::string to_utf8(Char c) + { + std::string result; + if (is_surrogate(c)) { + append(result, 0xfffd); + } else { + append(result, c); + } + return result; + } + + // UTF8 -> UTF16 + static std::wstring from_utf8(const std::string& str) + { + std::wstring result; + result.reserve(str.length()); + auto iter = str.begin(); + utf8_state state = s_start; + std::uint32_t codepoint = 0; + while (iter < str.end()) { + if ((state = decode(state, static_cast(*iter++), + codepoint)) == s_start) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } else { + codepoint -= 0x10000; + result += + static_cast((codepoint >> 10) + 0xd800); + result += static_cast((codepoint & 0x3ff) + + 0xdc00); + } + codepoint = 0; + } else if (state == s_reject) { + result += static_cast(0xfffd); + state = s_start; + codepoint = 0; + } + } + if (state) { + result += static_cast(0xfffd); + } + return result; + } + static std::wstring from_utf8(char c) + { + std::wstring result; + utf8_state state = s_start; + std::uint32_t codepoint = 0; + if ((state = decode(state, static_cast(c), codepoint)) == + s_start) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } else { + codepoint -= 0x10000; + result += + static_cast((codepoint >> 10) + 0xd800); + result += + static_cast((codepoint & 0x3ff) + 0xdc00); + } + } else { + result += static_cast(0xfffd); + } + return result; + } +}; + +template +class unicode_converter; + +template <> +class unicode_converter +{ +public: + std::wstring operator()(const std::string& in) + { + return unicode::from_utf8(in); + } + std::wstring operator()(const char* in) + { + return unicode::from_utf8(in); + } + std::wstring operator()(char in) { return unicode::from_utf8(in); } +}; +template <> +class unicode_converter +{ +public: + std::string operator()(const std::wstring& in) + { + return unicode::to_utf8(in); + } + std::string operator()(const wchar_t* in) + { + return unicode::to_utf8(in); + } + std::string operator()(wchar_t in) { return unicode::to_utf8(in); } +}; +template <> +class unicode_converter +{ +public: + std::string operator()(const std::string& in) { return in; } + std::string operator()(const char* in) { return std::string(in); } + std::string operator()(char in) { return std::string(1, in); } +}; +template <> +class unicode_converter +{ +public: + std::wstring operator()(const std::wstring& in) { return in; } + std::wstring operator()(const wchar_t* in) { return std::wstring(in); } + std::wstring operator()(wchar_t in) { return std::wstring(1, in); } +}; + +template +struct string_converter +{ +}; + +template <> +struct string_converter +{ + // some compilers, like gcc 4.8 does not implement the following C++11 + // signature: + // std::string::string(const string&, const Allocator&) + // As workaround, use char* pointer. + template + static std::basic_string to(const std::string& in, + const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(const char* in, + const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(char in, const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + + template + static std::basic_string to(const std::string& in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(const char* in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(char in) + { + return std::basic_string(unicode_converter()(in)); + } +}; +template <> +struct string_converter +{ + // some compilers, like gcc 4.8 does not implement the following C++11 + // signature: + // std::string::string(const string&, const Allocator&) + // As workaround, use char* pointer. + template + static std::basic_string to(const std::wstring& in, + const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(const wchar_t* in, + const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(wchar_t in, const Alloc& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + + template + static std::basic_string to(const std::wstring& in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(const wchar_t* in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(wchar_t in) + { + return std::basic_string(unicode_converter()(in)); + } +}; + +template +struct source_traits +{ +}; + +template +struct source_traits +{ + using value_type = T; +}; + +template +struct source_traits> +{ + using value_type = + typename std::basic_string::value_type; +}; + +template <> +struct source_traits +{ + using value_type = cm::string_view::value_type; +}; + +# if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR +template +struct source_traits::value, void>> +{ + using value_type = + typename std::iterator_traits::type>::value_type; +}; +# endif + +template +struct source_converter +{ +}; + +template <> +struct source_converter +{ + template + static void append_range(std::string& p, Iterator b, Iterator e) + { + if (b == e) { + return; + } + p.append(b, e); + } + template + static void append_range(std::string& p, Iterator b) + { + char e = '\0'; + + if (*b == e) { + return; + } + for (; *b != e; ++b) { + p.push_back(*b); + } + } + + static void append_source(std::string& p, const cm::string_view s) + { + append_range(p, s.begin(), s.end()); + } + template + static void append_source(std::string& p, + const std::basic_string& s) + { + append_range(p, s.begin(), s.end()); + } + template + static void append_source(std::string& p, const Source& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::string&& s) { p = std::move(s); } +}; + +template <> +struct source_converter +{ + template + static void append_range(std::string& p, Iterator b, Iterator e) + { + if (b == e) { + return; + } + + std::wstring tmp(b, e); + std::string dest = string_converter::to(tmp); + p.append(dest.begin(), dest.end()); + } + template + static void append_range(std::string& p, Iterator b) + { + wchar_t e = '\0'; + + if (*b == e) { + return; + } + std::wstring tmp; + for (; *b != e; ++b) { + tmp.push_back(*b); + } + + std::string dest = string_converter::to(tmp); + p.append(dest.begin(), dest.end()); + } + + template + static void append_source(std::string& p, + const std::basic_string& s) + { + append_range(p, s.begin(), s.end()); + } + template + static void append_source(std::string& p, const Source& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::wstring&& s) + { + p = string_converter::to(s); + } +}; + +template +struct is_pathable_string : std::false_type +{ +}; +template +struct is_pathable_string> + : std::true_type +{ +}; +template +struct is_pathable_string> + : std::true_type +{ +}; +template <> +struct is_pathable_string : std::true_type +{ +}; + +template +struct is_pathable_char_array : std::false_type +{ +}; +template +struct is_pathable_char_array< + T, + cm::enable_if_t< + std::is_same::type>::value || + std::is_same::type>::value, + void>> + : bool_constant::type>::value || + std::is_same::type>::value> +{ +}; + +template +struct is_pathable_iterator : std::false_type +{ +}; +template +struct is_pathable_iterator< + T, + cm::enable_if_t< + is_input_iterator::value && + (std::is_same::type>::value_type>::value || + std::is_same::type>::value_type>::value), + void>> + : bool_constant< + std::is_same::type>::value_type>::value || + std::is_same::type>::value_type>::value> +{ +}; + +# if defined(__SUNPRO_CC) && defined(__sparc) +// Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile +// the full 'is_pathable' check. We use it only to improve error messages +// via 'enable_if' when calling methods with incorrect types. Just +// pretend all types are allowed so we can at least compile valid code. +template +struct is_pathable : std::true_type +{ +}; +# else +template +struct is_pathable + : bool_constant::value || + is_pathable_char_array::value || + is_pathable_iterator::value> +{ +}; +# endif +} + +class path +{ + using path_type = std::string; + + template + using enable_if_pathable = + enable_if_t::value, path&>; + + enum class filename_fragment : unsigned char + { + stem, + extension + }; + +public: +# if defined(_WIN32) + using value_type = wchar_t; +# else + using value_type = char; +# endif + using string_type = std::basic_string; + + class iterator; + using const_iterator = iterator; + + enum format : unsigned char + { + auto_format, + native_format, + generic_format + }; + +# if defined(_WIN32) + static constexpr value_type preferred_separator = L'\\'; +# else + static constexpr value_type preferred_separator = '/'; +# endif + + // Constructors + // ============ + path() noexcept {} + path(const path& p) + : path_(p.path_) + { + } + path(path&& p) noexcept + : path_(std::move(p.path_)) + { + } + path(string_type&& source, format fmt = auto_format) + { + (void)fmt; + internals::source_converter::set_source( + this->path_, std::move(source)); + } + template > + path(const Source& source, format fmt = auto_format) + { + (void)fmt; + internals::source_converter< + typename internals::source_traits::value_type, + path_type::value_type>::append_source(this->path_, source); + } + template > + path(const Iterator first, Iterator last, format fmt = auto_format) + { + (void)fmt; + internals::source_converter< + typename std::iterator_traits::value_type, + path_type::value_type>::append_range(this->path_, first, last); + } + + ~path() = default; + + // Assignments + // =========== + path& operator=(const path& p) + { + if (this != &p) { + this->path_ = p.path_; + } + return *this; + } + path& operator=(path&& p) noexcept + { + if (this != &p) { + this->path_ = std::move(p.path_); + } + return *this; + } + path& operator=(string_type&& source) { return this->assign(source); } + template > + path& operator=(const Source& source) + { + return this->assign(source); + } + + path& assign(string_type&& source) + { + internals::source_converter::set_source( + this->path_, std::move(source)); + return *this; + } + template > + path& assign(const Source& source) + { + this->path_.clear(); + internals::source_converter< + typename internals::source_traits::value_type, + path_type::value_type>::append_source(this->path_, source); + return *this; + } + template > + path& assign(Iterator first, Iterator last) + { + this->path_.clear(); + internals::source_converter< + typename std::iterator_traits::value_type, + path_type::value_type>::append_range(this->path_, first, last); + return *this; + } + + // Concatenation + // ============= + path& operator/=(const path& p); + + template > + path& append(const Source& source) + { + return this->operator/=(path(source)); + } + template + path& operator/=(const Source& source) + { + return this->append(source); + } + + template > + path& append(Iterator first, Iterator last) + { + return this->operator/=(path(first, last)); + } + + path& operator+=(const path& p) + { + this->path_ += p.path_; + return *this; + } + path& operator+=(const string_type& str) + { + this->path_ += + internals::string_converter::to(str); + return *this; + } + path& operator+=(cm::string_view str) + { + this->path_.append(str.begin(), str.end()); + return *this; + } + path& operator+=(const value_type* str) + { + this->path_ += + internals::string_converter::to(str); + return *this; + } + path& operator+=(const value_type c) + { + this->path_ += + internals::string_converter::to(c); + return *this; + } + template > + path& concat(const Source& source) + { + internals::source_converter< + typename internals::source_traits::value_type, + path_type::value_type>::append_source(this->path_, source); + return *this; + } + template + path& operator+=(const Source& source) + { + return this->concat(source); + } + template > + path& concat(Iterator first, Iterator last) + { + internals::source_converter< + typename std::iterator_traits::value_type, + path_type::value_type>::append_range(this->path_, first, last); + return *this; + } + + // Modifiers + // ========= + void clear() noexcept { this->path_.clear(); } + + path& make_preferred() + { +# if defined(_WIN32) + std::replace( + this->path_.begin(), this->path_.end(), '/', + static_cast(this->preferred_separator)); +# endif + return *this; + } + + path& remove_filename() + { + auto fname = this->get_filename(); + if (!fname.empty()) { + this->path_.erase(fname.data() - this->path_.data()); + } + return *this; + } + + path& replace_filename(const path& replacement) + { + this->remove_filename(); + this->operator/=(replacement); + return *this; + } + + path& replace_extension(const path& replacement = path()) + { + auto ext = this->get_filename_fragment(filename_fragment::extension); + if (!ext.empty()) { + this->path_.erase(ext.data() - this->path_.data()); + } + if (!replacement.path_.empty()) { + if (replacement.path_[0] != '.') { + this->path_ += '.'; + } + this->path_.append(replacement.path_); + } + return *this; + } + + void swap(path& other) noexcept { this->path_.swap(other.path_); } + + // Format observers + // ================ + const string_type& native() const noexcept + { +# if defined(_WIN32) + this->native_path_ = internals::string_converter< + path_type::value_type>::to(this->path_); + return this->native_path_; +# else + return this->path_; +# endif + } + const value_type* c_str() const noexcept { return this->native().c_str(); } + operator string_type() const { return this->native(); } + + template < + typename Char, typename Traits = std::char_traits, + typename Alloc = std::allocator, + cm::enable_if_t<(std::is_same::value && + std::is_same>::value) || + (std::is_same::value && + std::is_same>::value), + int> = 1> + std::basic_string string(const Alloc& a = Alloc()) const + { + return internals::string_converter::to( + this->path_, a); + } + const std::string string() const { return this->path_; } + std::wstring wstring() const + { + std::string out = this->string(); + return internals::string_converter::to< + std::wstring::value_type>(out); + } + + template < + typename Char, typename Traits = std::char_traits, + typename Alloc = std::allocator, + cm::enable_if_t<(std::is_same::value && + std::is_same>::value) || + (std::is_same::value && + std::is_same>::value), + int> = 1> + std::basic_string generic_string( + const Alloc& a = Alloc()) const + { + return internals::string_converter::to( + this->get_generic(), a); + } + std::string generic_string() const { return this->get_generic(); } + std::wstring generic_wstring() const + { + auto dest = this->generic_string(); + return internals::string_converter::to< + std::wstring::value_type>(dest); + } + + // Compare + // ======= + int compare(const path& p) const noexcept + { + return this->compare_path(p.path_); + } + int compare(const string_type& str) const + { + return this->compare_path( + internals::string_converter::to(str)); + } + int compare(const value_type* str) const + { + return this->compare_path( + internals::string_converter::to(str)); + } + int compare(cm::string_view str) const { return this->compare_path(str); } + + // Generation + // ========== + path lexically_normal() const; + + path lexically_relative(const path& base) const; + + path lexically_proximate(const path& base) const + { + path result = this->lexically_relative(base); + return result.empty() ? *this : result; + } + + // Decomposition + // ============= + path root_name() const { return get_root_name(); } + + path root_directory() const { return this->get_root_directory(); } + + path root_path() const + { + return this->root_name().append(this->get_root_directory()); + } + + path relative_path() const { return this->get_relative_path(); } + + path parent_path() const { return this->get_parent_path(); } + + path filename() const { return this->get_filename(); } + + path stem() const + { + return this->get_filename_fragment(filename_fragment::stem); + } + path extension() const + { + return this->get_filename_fragment(filename_fragment::extension); + } + + // Queries + // ======= + bool empty() const noexcept { return this->path_.empty(); } + + bool has_root_name() const { return !this->get_root_name().empty(); } + + bool has_root_directory() const + { + return !this->get_root_directory().empty(); + } + + bool has_root_path() const + { + return this->has_root_name() || this->has_root_directory(); + } + + bool has_relative_path() const { return !this->get_relative_path().empty(); } + + bool has_parent_path() const { return !this->get_parent_path().empty(); } + + bool has_filename() const { return !this->get_filename().empty(); } + + bool has_stem() const + { + return !this->get_filename_fragment(filename_fragment::stem).empty(); + } + bool has_extension() const + { + return !this->get_filename_fragment(filename_fragment::extension).empty(); + } + + bool is_absolute() const + { +# if defined(_WIN32) + return this->has_root_name() && this->has_root_directory(); +# else + return this->has_root_directory(); +# endif + } + + bool is_relative() const { return !this->is_absolute(); } + + // Iterators + // ========= + inline iterator begin() const; + inline iterator end() const; + + // Non-members + // =========== + friend inline bool operator==(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + friend inline bool operator!=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + friend inline bool operator<(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + friend inline bool operator<=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + friend inline bool operator>(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + friend inline bool operator>=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + friend inline path operator/(const path& lhs, const path& rhs) + { + path result(lhs); + result /= rhs; + + return result; + } + + template + friend inline cm::enable_if_t< + (std::is_same::value && + std::is_same>::value) || + (std::is_same::value && + std::is_same>::value), + std::basic_ostream&> + operator<<(std::basic_ostream& os, const path& p) + { + os << cm::quoted(p.string()); + return os; + } + + template + friend inline cm::enable_if_t< + (std::is_same::value && + std::is_same>::value) || + (std::is_same::value && + std::is_same>::value), + std::basic_istream&> + operator>>(std::basic_istream& is, path& p) + { + std::basic_string tmp; + is >> cm::quoted(tmp); + p = tmp; + return is; + } + +private: + friend class iterator; + friend std::size_t hash_value(const path& p) noexcept; + + path_type get_generic() const; + + cm::string_view get_root_name() const; + cm::string_view get_root_directory() const; + cm::string_view get_relative_path() const; + cm::string_view get_parent_path() const; + cm::string_view get_filename() const; + cm::string_view get_filename_fragment(filename_fragment fragment) const; + + int compare_path(cm::string_view str) const; + + path_type path_; +# if defined(_WIN32) + mutable string_type native_path_; +# endif +}; + +class path::iterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + + using value_type = path; + using difference_type = std::ptrdiff_t; + using pointer = const path*; + using reference = const path&; + + iterator(); + iterator(const iterator& other); + + ~iterator(); + + iterator& operator=(const iterator& other); + + reference operator*() const { return this->path_element_; } + + pointer operator->() const { return &this->path_element_; } + + iterator& operator++(); + + iterator operator++(int) + { + iterator it(*this); + this->operator++(); + return it; + } + + iterator& operator--(); + + iterator operator--(int) + { + iterator it(*this); + this->operator--(); + return it; + } + +private: + friend class path; + friend bool operator==(const iterator&, const iterator&); + + iterator(const path* p, bool at_end = false); + + const path* path_; + std::unique_ptr parser_; + path path_element_; +}; + +inline path::iterator path::begin() const +{ + return iterator(this); +} +inline path::iterator path::end() const +{ + return iterator(this, true); +} + +bool operator==(const path::iterator& lhs, const path::iterator& rhs); + +inline bool operator!=(const path::iterator& lhs, const path::iterator& rhs) +{ + return !(lhs == rhs); +} + +// Non-member functions +// ==================== +inline void swap(path& lhs, path& rhs) noexcept +{ + lhs.swap(rhs); +} + +std::size_t hash_value(const path& p) noexcept; + +#endif + +} // namespace filesystem +} // namespace cm + +#endif diff --git a/Utilities/std/cmSTL.hxx.in b/Utilities/std/cmSTL.hxx.in index 28fe226..9c8605c 100644 --- a/Utilities/std/cmSTL.hxx.in +++ b/Utilities/std/cmSTL.hxx.in @@ -5,5 +5,6 @@ /* Whether CMake is using its own STL implementation. */ #cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE +#cmakedefine CMake_HAVE_CXX_FILESYSTEM #endif diff --git a/bootstrap b/bootstrap index 6b37164..2adb5d9 100755 --- a/bootstrap +++ b/bootstrap @@ -479,6 +479,7 @@ if ${cmake_system_mingw}; then fi CMAKE_STD_CXX_HEADERS="\ + filesystem \ memory \ optional \ shared_mutex \ @@ -486,6 +487,7 @@ CMAKE_STD_CXX_HEADERS="\ utility \ " CMAKE_STD_CXX_SOURCES="\ + fs_path \ string_view \ " @@ -1252,7 +1254,7 @@ echo "C++ compiler on this system is: ${cmake_cxx_compiler} ${cmake_cxx_flags}" #----------------------------------------------------------------------------- # Test CXX features -cmake_cxx_features="make_unique" +cmake_cxx_features="make_unique filesystem" for feature in ${cmake_cxx_features}; do eval "cmake_have_cxx_${feature}=0" -- cgit v0.12