diff --git a/Help/dev/source.rst b/Help/dev/source.rst
index 5fbc9fa70c2ddb0d425d4261679dd3735652c534..d93e55cfb0553125ace8a4cc69700377f358052d 100644
--- a/Help/dev/source.rst
+++ b/Help/dev/source.rst
@@ -56,6 +56,9 @@ Available features are:
   * ``<cm/algorithm>``:
     ``cm::clamp``
 
+  * ``cm/filesystem>``:
+    ``cm::filesystem::path``
+
   * ``<cm/iterator>``:
     ``cm::size``, ``cm::empty``, ``cm::data``
 
diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake
index 50ccc7c86962db9711775a5bcd53d06de7fb3b10..e726fc72b46045c58c8215ec124e6d0b386579ec 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 0000000000000000000000000000000000000000..e508d1c969a3c2fab27aaec8907c7eb4fa4a1d4e
--- /dev/null
+++ b/Source/Checks/cm_cxx_filesystem.cxx
@@ -0,0 +1,10 @@
+
+#include <filesystem>
+
+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 bb50d76f045c898da490b2070507a1b84e7c4ac7..0b2c8f6c2a71b08f94213bf623970192e0e3809c 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 0000000000000000000000000000000000000000..1e8452043af4cb84675ce81521c82799aabc6203
--- /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 <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cm/filesystem>
+#include <cm/iomanip>
+
+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<std::string::value_type>(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<fs::path::value_type>(c);
+    }
+    if (p.native() != st || static_cast<fs::path::string_type>(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<std::wstring::value_type>(c);
+    }
+    std::string ns(s);
+    std::replace(
+      ns.begin(), ns.end(), '/',
+      static_cast<std::string::value_type>(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<fs::path::string_type> ref{ L"/", L"a", L"b", L"" };
+#else
+    std::vector<fs::path::string_type> ref{ "/", "a", "b", "" };
+#endif
+    std::vector<fs::path::string_type> 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<fs::path::string_type> ref{ L"", L"b", L"a", L"/" };
+#else
+    std::vector<fs::path::string_type> ref{ "", "b", "a", "/" };
+#endif
+    std::vector<fs::path::string_type> 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 17a7aaa4eccf2daf48d0f51d0bbef49df5f05c14..23d9104644e2c8b1c43f8a6d522ee70a96df5ac1 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 0000000000000000000000000000000000000000..71386bb1f85548cae55a0168f6a4554e1403dbfb
--- /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 <cm/filesystem> // IWYU pragma: associated
+
+#if !defined(CMake_HAVE_CXX_FILESYSTEM)
+
+#  include <algorithm>
+#  include <cassert>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <functional>
+#  include <string>
+#  include <utility>
+#  include <vector>
+#  if defined(_WIN32)
+#    include <cctype>
+#    include <iterator>
+#  endif
+
+#  include <cm/memory>
+#  include <cm/string_view>
+#  include <cmext/string_view>
+
+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<enum_size>(state::in_root_name),
+    root_directory = static_cast<enum_size>(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<state>(static_cast<int>(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<pointer> start(ptr);
+        std::reverse_iterator<pointer> 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<char>(codepoint));
+  } else if (codepoint >= 0x80 && codepoint <= 0x7ff) {
+    str.push_back(static_cast<char>((codepoint >> 6) + 192));
+    str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
+  } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) ||
+             (codepoint >= 0xe000 && codepoint <= 0xffff)) {
+    str.push_back(static_cast<char>((codepoint >> 12) + 224));
+    str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
+    str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
+  } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) {
+    str.push_back(static_cast<char>((codepoint >> 18) + 240));
+    str.push_back(static_cast<char>(((codepoint & 0x3ffff) >> 12) + 128));
+    str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
+    str.push_back(static_cast<char>((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>(
+        (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<std::string>(this->get_root_name());
+    this->path_ += static_cast<std::string>(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<cm::string_view> root_parts;
+  std::vector<cm::string_view> 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<std::string::value_type>(this->preferred_separator) != '/') {
+    std::replace(
+      np.begin(), np.end(), '/',
+      static_cast<std::string::value_type>(this->preferred_separator));
+  }
+  for (const auto& p : parts) {
+    if (!p.empty()) {
+      np += p;
+      np += static_cast<std::string::value_type>(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<internals::path_parser>(*other.parser_);
+    this->path_element_ = path(**this->parser_);
+  }
+}
+path::iterator::iterator(const path* p, bool at_end)
+  : path_(p)
+  , parser_(cm::make_unique<internals::path_parser>(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<internals::path_parser>(*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<cm::string_view> 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 0000000000000000000000000000000000000000..d7ade344c2623cba043630e9ca881a8d8e97e9aa
--- /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 <filesystem> // IWYU pragma: export
+
+#else
+
+#  include <cstddef>
+#  include <cstdint>
+#  include <iostream>
+#  include <iterator>
+#  include <memory>
+#  include <string>
+#  include <utility>
+
+#  include <cm/iomanip>
+#  include <cm/string_view>
+#  include <cm/type_traits>
+#  include <cmext/iterator>
+
+#  if defined(_WIN32)
+#    include <algorithm>
+#  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<std::uint32_t>(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 <typename Char, typename = void>
+class unicode
+{
+};
+
+template <typename Char>
+class unicode<Char, typename std::enable_if<(sizeof(Char) == 4)>::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<std::uint8_t>(*iter++),
+                          codepoint)) == s_start) {
+        result += static_cast<std::wstring::value_type>(codepoint);
+        codepoint = 0;
+      } else if (state == s_reject) {
+        result += static_cast<std::wstring::value_type>(0xfffd);
+        state = s_start;
+        codepoint = 0;
+      }
+    }
+    if (state) {
+      result += static_cast<std::wstring::value_type>(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<std::uint8_t>(c), codepoint)) ==
+        s_start) {
+      result += static_cast<std::wstring::value_type>(codepoint);
+    } else {
+      result += static_cast<std::wstring::value_type>(0xfffd);
+    }
+
+    return result;
+  }
+};
+
+template <typename Char>
+class unicode<Char, typename std::enable_if<(sizeof(Char) == 2)>::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<std::uint8_t>(*iter++),
+                          codepoint)) == s_start) {
+        if (codepoint <= 0xffff) {
+          result += static_cast<std::wstring::value_type>(codepoint);
+        } else {
+          codepoint -= 0x10000;
+          result +=
+            static_cast<std::wstring::value_type>((codepoint >> 10) + 0xd800);
+          result += static_cast<std::wstring::value_type>((codepoint & 0x3ff) +
+                                                          0xdc00);
+        }
+        codepoint = 0;
+      } else if (state == s_reject) {
+        result += static_cast<std::wstring::value_type>(0xfffd);
+        state = s_start;
+        codepoint = 0;
+      }
+    }
+    if (state) {
+      result += static_cast<std::wstring::value_type>(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<std::uint8_t>(c), codepoint)) ==
+        s_start) {
+      if (codepoint <= 0xffff) {
+        result += static_cast<std::wstring::value_type>(codepoint);
+      } else {
+        codepoint -= 0x10000;
+        result +=
+          static_cast<std::wstring::value_type>((codepoint >> 10) + 0xd800);
+        result +=
+          static_cast<std::wstring::value_type>((codepoint & 0x3ff) + 0xdc00);
+      }
+    } else {
+      result += static_cast<std::wstring::value_type>(0xfffd);
+    }
+    return result;
+  }
+};
+
+template <typename In, typename Out>
+class unicode_converter;
+
+template <>
+class unicode_converter<char, wchar_t>
+{
+public:
+  std::wstring operator()(const std::string& in)
+  {
+    return unicode<wchar_t>::from_utf8(in);
+  }
+  std::wstring operator()(const char* in)
+  {
+    return unicode<wchar_t>::from_utf8(in);
+  }
+  std::wstring operator()(char in) { return unicode<wchar_t>::from_utf8(in); }
+};
+template <>
+class unicode_converter<wchar_t, char>
+{
+public:
+  std::string operator()(const std::wstring& in)
+  {
+    return unicode<wchar_t>::to_utf8(in);
+  }
+  std::string operator()(const wchar_t* in)
+  {
+    return unicode<wchar_t>::to_utf8(in);
+  }
+  std::string operator()(wchar_t in) { return unicode<wchar_t>::to_utf8(in); }
+};
+template <>
+class unicode_converter<char, char>
+{
+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<wchar_t, wchar_t>
+{
+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 <typename In>
+struct string_converter
+{
+};
+
+template <>
+struct string_converter<char>
+{
+  // 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 <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(const std::string& in,
+                                                   const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<char, Char>()(in).c_str(), a);
+  }
+  template <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(const char* in,
+                                                   const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<char, Char>()(in).c_str(), a);
+  }
+  template <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(char in, const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<char, Char>()(in).c_str(), a);
+  }
+
+  template <typename Char>
+  static std::basic_string<Char> to(const std::string& in)
+  {
+    return std::basic_string<Char>(unicode_converter<char, Char>()(in));
+  }
+  template <typename Char>
+  static std::basic_string<Char> to(const char* in)
+  {
+    return std::basic_string<Char>(unicode_converter<char, Char>()(in));
+  }
+  template <typename Char>
+  static std::basic_string<Char> to(char in)
+  {
+    return std::basic_string<Char>(unicode_converter<char, Char>()(in));
+  }
+};
+template <>
+struct string_converter<wchar_t>
+{
+  // 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 <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(const std::wstring& in,
+                                                   const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<wchar_t, Char>()(in).c_str(), a);
+  }
+  template <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(const wchar_t* in,
+                                                   const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<wchar_t, Char>()(in).c_str(), a);
+  }
+  template <typename Char, typename Traits, typename Alloc>
+  static std::basic_string<Char, Traits, Alloc> to(wchar_t in, const Alloc& a)
+  {
+    return std::basic_string<Char, Traits, Alloc>(
+      unicode_converter<wchar_t, Char>()(in).c_str(), a);
+  }
+
+  template <typename Char>
+  static std::basic_string<Char> to(const std::wstring& in)
+  {
+    return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in));
+  }
+  template <typename Char>
+  static std::basic_string<Char> to(const wchar_t* in)
+  {
+    return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in));
+  }
+  template <typename Char>
+  static std::basic_string<Char> to(wchar_t in)
+  {
+    return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in));
+  }
+};
+
+template <typename T, typename = void>
+struct source_traits
+{
+};
+
+template <typename T, std::size_t N>
+struct source_traits<T[N]>
+{
+  using value_type = T;
+};
+
+template <typename Char, typename Traits, typename Alloc>
+struct source_traits<std::basic_string<Char, Traits, Alloc>>
+{
+  using value_type =
+    typename std::basic_string<Char, Traits, Alloc>::value_type;
+};
+
+template <>
+struct source_traits<cm::string_view>
+{
+  using value_type = cm::string_view::value_type;
+};
+
+#  if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR
+template <typename T>
+struct source_traits<T, cm::enable_if_t<cm::is_iterator<T>::value, void>>
+{
+  using value_type =
+    typename std::iterator_traits<typename std::decay<T>::type>::value_type;
+};
+#  endif
+
+template <typename In, typename Out>
+struct source_converter
+{
+};
+
+template <>
+struct source_converter<char, char>
+{
+  template <typename Iterator>
+  static void append_range(std::string& p, Iterator b, Iterator e)
+  {
+    if (b == e) {
+      return;
+    }
+    p.append(b, e);
+  }
+  template <typename Iterator>
+  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 <typename Traits, typename Alloc>
+  static void append_source(std::string& p,
+                            const std::basic_string<char, Traits, Alloc>& s)
+  {
+    append_range(p, s.begin(), s.end());
+  }
+  template <typename Source>
+  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<wchar_t, char>
+{
+  template <typename Iterator>
+  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<wchar_t>::to<char>(tmp);
+    p.append(dest.begin(), dest.end());
+  }
+  template <typename Iterator>
+  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<wchar_t>::to<char>(tmp);
+    p.append(dest.begin(), dest.end());
+  }
+
+  template <typename Traits, typename Alloc>
+  static void append_source(std::string& p,
+                            const std::basic_string<wchar_t, Traits, Alloc>& s)
+  {
+    append_range(p, s.begin(), s.end());
+  }
+  template <typename Source>
+  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<wchar_t>::to<char>(s);
+  }
+};
+
+template <typename T>
+struct is_pathable_string : std::false_type
+{
+};
+template <typename Traits, typename Alloc>
+struct is_pathable_string<std::basic_string<char, Traits, Alloc>>
+  : std::true_type
+{
+};
+template <typename Traits, typename Alloc>
+struct is_pathable_string<std::basic_string<wchar_t, Traits, Alloc>>
+  : std::true_type
+{
+};
+template <>
+struct is_pathable_string<cm::string_view> : std::true_type
+{
+};
+
+template <typename T, typename = void>
+struct is_pathable_char_array : std::false_type
+{
+};
+template <typename T>
+struct is_pathable_char_array<
+  T,
+  cm::enable_if_t<
+    std::is_same<char*, typename std::decay<T>::type>::value ||
+      std::is_same<wchar_t*, typename std::decay<T>::type>::value,
+    void>>
+  : bool_constant<std::is_same<char*, typename std::decay<T>::type>::value ||
+                  std::is_same<wchar_t*, typename std::decay<T>::type>::value>
+{
+};
+
+template <typename T, typename = void>
+struct is_pathable_iterator : std::false_type
+{
+};
+template <typename T>
+struct is_pathable_iterator<
+  T,
+  cm::enable_if_t<
+    is_input_iterator<T>::value &&
+      (std::is_same<char,
+                    typename std::iterator_traits<
+                      typename std::decay<T>::type>::value_type>::value ||
+       std::is_same<wchar_t,
+                    typename std::iterator_traits<
+                      typename std::decay<T>::type>::value_type>::value),
+    void>>
+  : bool_constant<
+      std::is_same<char,
+                   typename std::iterator_traits<
+                     typename std::decay<T>::type>::value_type>::value ||
+      std::is_same<wchar_t,
+                   typename std::iterator_traits<
+                     typename std::decay<T>::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 <typename T>
+struct is_pathable : std::true_type
+{
+};
+#  else
+template <typename T>
+struct is_pathable
+  : bool_constant<is_pathable_string<T>::value ||
+                  is_pathable_char_array<T>::value ||
+                  is_pathable_iterator<T>::value>
+{
+};
+#  endif
+}
+
+class path
+{
+  using path_type = std::string;
+
+  template <typename Source>
+  using enable_if_pathable =
+    enable_if_t<internals::is_pathable<Source>::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<value_type>;
+
+  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<value_type, path_type::value_type>::set_source(
+      this->path_, std::move(source));
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  path(const Source& source, format fmt = auto_format)
+  {
+    (void)fmt;
+    internals::source_converter<
+      typename internals::source_traits<Source>::value_type,
+      path_type::value_type>::append_source(this->path_, source);
+  }
+  template <typename Iterator, typename = enable_if_pathable<Iterator>>
+  path(const Iterator first, Iterator last, format fmt = auto_format)
+  {
+    (void)fmt;
+    internals::source_converter<
+      typename std::iterator_traits<Iterator>::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 <typename Source, typename = enable_if_pathable<Source>>
+  path& operator=(const Source& source)
+  {
+    return this->assign(source);
+  }
+
+  path& assign(string_type&& source)
+  {
+    internals::source_converter<value_type, path_type::value_type>::set_source(
+      this->path_, std::move(source));
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  path& assign(const Source& source)
+  {
+    this->path_.clear();
+    internals::source_converter<
+      typename internals::source_traits<Source>::value_type,
+      path_type::value_type>::append_source(this->path_, source);
+    return *this;
+  }
+  template <typename Iterator, typename = enable_if_pathable<Iterator>>
+  path& assign(Iterator first, Iterator last)
+  {
+    this->path_.clear();
+    internals::source_converter<
+      typename std::iterator_traits<Iterator>::value_type,
+      path_type::value_type>::append_range(this->path_, first, last);
+    return *this;
+  }
+
+  // Concatenation
+  // =============
+  path& operator/=(const path& p);
+
+  template <typename Source, typename = enable_if_pathable<Source>>
+  path& append(const Source& source)
+  {
+    return this->operator/=(path(source));
+  }
+  template <typename Source>
+  path& operator/=(const Source& source)
+  {
+    return this->append(source);
+  }
+
+  template <typename Iterator, typename = enable_if_pathable<Iterator>>
+  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<value_type>::to<path_type::value_type>(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<value_type>::to<path_type::value_type>(str);
+    return *this;
+  }
+  path& operator+=(const value_type c)
+  {
+    this->path_ +=
+      internals::string_converter<value_type>::to<path_type::value_type>(c);
+    return *this;
+  }
+  template <typename Source, typename = enable_if_pathable<Source>>
+  path& concat(const Source& source)
+  {
+    internals::source_converter<
+      typename internals::source_traits<Source>::value_type,
+      path_type::value_type>::append_source(this->path_, source);
+    return *this;
+  }
+  template <typename Source>
+  path& operator+=(const Source& source)
+  {
+    return this->concat(source);
+  }
+  template <typename Iterator, typename = enable_if_pathable<Iterator>>
+  path& concat(Iterator first, Iterator last)
+  {
+    internals::source_converter<
+      typename std::iterator_traits<Iterator>::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<path_type::value_type>(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<string_type::value_type>(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<Char>,
+    typename Alloc = std::allocator<Char>,
+    cm::enable_if_t<(std::is_same<Char, char>::value &&
+                     std::is_same<Traits, std::char_traits<char>>::value) ||
+                      (std::is_same<Char, wchar_t>::value &&
+                       std::is_same<Traits, std::char_traits<wchar_t>>::value),
+                    int> = 1>
+  std::basic_string<Char, Traits, Alloc> string(const Alloc& a = Alloc()) const
+  {
+    return internals::string_converter<path_type::value_type>::to<Char, Traits,
+                                                                  Alloc>(
+      this->path_, a);
+  }
+  const std::string string() const { return this->path_; }
+  std::wstring wstring() const
+  {
+    std::string out = this->string();
+    return internals::string_converter<path_type::value_type>::to<
+      std::wstring::value_type>(out);
+  }
+
+  template <
+    typename Char, typename Traits = std::char_traits<Char>,
+    typename Alloc = std::allocator<Char>,
+    cm::enable_if_t<(std::is_same<Char, char>::value &&
+                     std::is_same<Traits, std::char_traits<char>>::value) ||
+                      (std::is_same<Char, wchar_t>::value &&
+                       std::is_same<Traits, std::char_traits<wchar_t>>::value),
+                    int> = 1>
+  std::basic_string<Char, Traits, Alloc> generic_string(
+    const Alloc& a = Alloc()) const
+  {
+    return internals::string_converter<path_type::value_type>::to<Char, Traits,
+                                                                  Alloc>(
+      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<path_type::value_type>::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<value_type>::to<path_type::value_type>(str));
+  }
+  int compare(const value_type* str) const
+  {
+    return this->compare_path(
+      internals::string_converter<value_type>::to<path_type::value_type>(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 <typename Char, typename Traits>
+  friend inline cm::enable_if_t<
+    (std::is_same<Char, path::value_type>::value &&
+     std::is_same<Traits, std::char_traits<path::value_type>>::value) ||
+      (std::is_same<Char, path::path_type::value_type>::value &&
+       std::is_same<Traits,
+                    std::char_traits<path::path_type::value_type>>::value),
+    std::basic_ostream<Char, Traits>&>
+  operator<<(std::basic_ostream<Char, Traits>& os, const path& p)
+  {
+    os << cm::quoted(p.string<Char, Traits>());
+    return os;
+  }
+
+  template <typename Char, typename Traits>
+  friend inline cm::enable_if_t<
+    (std::is_same<Char, path::value_type>::value &&
+     std::is_same<Traits, std::char_traits<path::value_type>>::value) ||
+      (std::is_same<Char, path::path_type::value_type>::value &&
+       std::is_same<Traits,
+                    std::char_traits<path::path_type::value_type>>::value),
+    std::basic_istream<Char, Traits>&>
+  operator>>(std::basic_istream<Char, Traits>& is, path& p)
+  {
+    std::basic_string<Char, Traits> 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<internals::path_parser> 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 28fe226aaa7aab94e924f393858541e4f3f53a9b..9c8605c83b76eefd12d1c4b828be1514219159c8 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 6b3716415616bf3cf9e4b53b8aa39702dc31cb8d..2adb5d9a2613d496b5a740e9cf6b7d3e79d88d1a 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"