1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-18 17:31:57 +08:00
Files
CMake/Tests/CMakeLib/testPathResolver.cxx
Brad King 8dfc725cdb PathResolver: Add mode to collapse paths naively and look up on-disk case
In CMake 3.31 and below, `CollapseFullPath` did this on Windows.  KWSys
has since stopped looking up the on-disk case in `CollapseFullPath` to
avoid disk access when most callers only need an in-memory operation.
We currently call `GetActualCaseForPath` explicitly when needed.

Add a mode to `cm::PathResolver` to combine these operations and cache
disk access behind the `System::ReadName` callback.  We will use this to
restore the way CMake 3.31 and below normalized input paths on Windows.

Issue: #26750
Issue: #20214
2025-03-07 18:22:46 -05:00

600 lines
18 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include <cmConfigure.h> // IWYU pragma: keep
#include <cerrno>
#include <map>
#include <string>
#include <utility>
#ifdef _WIN32
# include <cctype>
#endif
#include <cmsys/Status.hxx>
#include "cmPathResolver.h"
#if defined(_WIN32) || defined(__APPLE__)
# include "cmSystemTools.h"
#endif
#include "testCommon.h"
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::LogicalPath
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::NaivePath
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::CasePath
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath
namespace {
class MockSystem : public cm::PathResolver::System
{
public:
~MockSystem() override = default;
struct Path
{
std::string Name;
std::string Link;
};
std::map<std::string, Path> Paths;
void SetPaths(std::map<std::string, Path> paths)
{
this->Paths = std::move(paths);
}
static std::string AdjustCase(std::string const& path)
{
#if defined(_WIN32) || defined(__APPLE__)
return cmSystemTools::LowerCase(path);
#else
return path;
#endif
}
cmsys::Status ReadSymlink(std::string const& path,
std::string& link) override
{
auto i = this->Paths.find(AdjustCase(path));
if (i == this->Paths.end()) {
return cmsys::Status::POSIX(ENOENT);
}
if (i->second.Link.empty()) {
return cmsys::Status::POSIX(EINVAL);
}
link = i->second.Link;
return cmsys::Status::Success();
}
bool PathExists(std::string const& path) override
{
return this->Paths.find(AdjustCase(path)) != this->Paths.end();
}
std::string WorkDir;
void SetWorkDir(std::string wd) { this->WorkDir = std::move(wd); }
std::string GetWorkingDirectory() override { return this->WorkDir; }
#ifdef _WIN32
std::map<char, std::string> WorkDirOnDrive;
void SetWorkDirOnDrive(std::map<char, std::string> wd)
{
this->WorkDirOnDrive = std::move(wd);
}
std::string GetWorkingDirectoryOnDrive(char letter) override
{
std::string result;
auto i = this->WorkDirOnDrive.find(std::tolower(letter));
if (i != this->WorkDirOnDrive.end()) {
result = i->second;
}
return result;
}
#endif
#if defined(_WIN32) || defined(__APPLE__)
cmsys::Status ReadName(std::string const& path, std::string& name) override
{
auto i = this->Paths.find(AdjustCase(path));
if (i == this->Paths.end()) {
return cmsys::Status::POSIX(ENOENT);
}
name = i->second.Name;
return cmsys::Status::Success();
}
#endif
};
#define EXPECT_RESOLVE(_in, _expect) \
do { \
std::string out; \
ASSERT_TRUE(r.Resolve(_in, out)); \
ASSERT_EQUAL(out, _expect); \
} while (false)
#define EXPECT_ENOENT(_in, _expect) \
do { \
std::string out; \
ASSERT_EQUAL(r.Resolve(_in, out).GetPOSIX(), ENOENT); \
ASSERT_EQUAL(out, _expect); \
} while (false)
using namespace cm::PathResolver;
bool posixRoot()
{
std::cout << "posixRoot()\n";
MockSystem os;
os.SetPaths({
{ "/", { {}, {} } },
});
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("/", "/");
EXPECT_RESOLVE("//", "/");
EXPECT_RESOLVE("/.", "/");
EXPECT_RESOLVE("/./", "/");
EXPECT_RESOLVE("/..", "/");
EXPECT_RESOLVE("/../", "/");
return true;
}
bool posixAbsolutePath()
{
std::cout << "posixAbsolutePath()\n";
MockSystem os;
os.SetPaths({
{ "/", { {}, {} } },
{ "/a", { {}, {} } },
});
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("/a", "/a");
EXPECT_RESOLVE("/a/", "/a");
EXPECT_RESOLVE("/a//", "/a");
EXPECT_RESOLVE("/a/.", "/a");
EXPECT_RESOLVE("/a/./", "/a");
EXPECT_RESOLVE("/a/..", "/");
EXPECT_RESOLVE("/a/../", "/");
EXPECT_RESOLVE("/a/../..", "/");
#ifndef _WIN32
EXPECT_RESOLVE("//a", "/a");
#endif
return true;
}
bool posixWorkingDirectory()
{
std::cout << "posixWorkingDirectory()\n";
MockSystem os;
os.SetPaths({
{ "/", { {}, {} } },
{ "/a", { {}, {} } },
{ "/cwd", { {}, {} } },
{ "/cwd/a", { {}, {} } },
});
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("", "/");
EXPECT_RESOLVE(".", "/");
EXPECT_RESOLVE("..", "/");
EXPECT_RESOLVE("a", "/a");
os.SetWorkDir("/cwd");
EXPECT_RESOLVE("", "/cwd");
EXPECT_RESOLVE(".", "/cwd");
EXPECT_RESOLVE("..", "/");
EXPECT_RESOLVE("a", "/cwd/a");
return true;
}
bool posixSymlink()
{
std::cout << "posixSymlink()\n";
MockSystem os;
os.SetPaths({
{ "/", { {}, {} } },
{ "/link-a", { {}, "a" } },
{ "/link-a-excess", { {}, "a//." } },
{ "/link-broken", { {}, "link-broken-dest" } },
{ "/a", { {}, {} } },
{ "/a/b", { {}, {} } },
{ "/a/link-b", { {}, "b" } },
{ "/a/b/link-c", { {}, "c" } },
{ "/a/b/c", { {}, {} } },
{ "/a/b/c/link-..|..", { {}, "../.." } },
{ "/a/link-|1|2", { {}, "/1/2" } },
{ "/1", { {}, {} } },
{ "/1/2", { {}, {} } },
{ "/1/2/3", { {}, {} } },
});
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("/link-a", "/link-a");
EXPECT_RESOLVE("/link-a-excess", "/link-a-excess");
EXPECT_RESOLVE("/link-a-excess/b", "/link-a-excess/b");
EXPECT_RESOLVE("/link-broken", "/link-broken");
EXPECT_RESOLVE("/link-a/../missing", "/missing");
EXPECT_RESOLVE("/a/b/link-c", "/a/b/link-c");
EXPECT_RESOLVE("/a/link-b/c", "/a/link-b/c");
EXPECT_RESOLVE("/a/link-b/link-c/..", "/a/link-b");
EXPECT_RESOLVE("/a/b/c/link-..|..", "/a/b/c/link-..|..");
EXPECT_RESOLVE("/a/b/c/link-..|../link-b", "/a/b/c/link-..|../link-b");
EXPECT_RESOLVE("/a/link-|1|2/3", "/a/link-|1|2/3");
EXPECT_RESOLVE("/a/link-|1|2/../2/3", "/a/2/3");
}
{
Resolver<Policies::LogicalPath> const r(os);
EXPECT_RESOLVE("/link-a", "/link-a");
EXPECT_RESOLVE("/link-a-excess", "/link-a-excess");
EXPECT_RESOLVE("/link-a-excess/b", "/link-a-excess/b");
EXPECT_RESOLVE("/link-broken", "/link-broken");
EXPECT_RESOLVE("/link-a/../missing", "/missing");
EXPECT_RESOLVE("/a/b/link-c", "/a/b/link-c");
EXPECT_RESOLVE("/a/link-b/c", "/a/link-b/c");
EXPECT_RESOLVE("/a/link-b/link-c/..", "/a/link-b");
EXPECT_RESOLVE("/a/b/c/link-..|..", "/a/b/c/link-..|..");
EXPECT_RESOLVE("/a/b/c/link-..|../link-b", "/a/b/c/link-..|../link-b");
EXPECT_RESOLVE("/a/link-|1|2/3", "/a/link-|1|2/3");
EXPECT_RESOLVE("/a/link-|1|2/../2/3", "/1/2/3");
}
{
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("/link-a", "/a");
EXPECT_RESOLVE("/link-a-excess", "/a");
EXPECT_RESOLVE("/link-a-excess/b", "/a/b");
EXPECT_ENOENT("/link-broken", "/link-broken-dest");
EXPECT_ENOENT("/link-a/../missing", "/missing");
EXPECT_RESOLVE("/a/b/link-c", "/a/b/c");
EXPECT_RESOLVE("/a/link-b/c", "/a/b/c");
EXPECT_RESOLVE("/a/link-b/link-c/..", "/a/b");
EXPECT_RESOLVE("/a/b/c/link-..|..", "/a");
EXPECT_RESOLVE("/a/b/c/link-..|../link-b", "/a/b");
EXPECT_RESOLVE("/a/link-|1|2/3", "/1/2/3");
}
return true;
}
#ifdef __APPLE__
bool macosActualCase()
{
std::cout << "macosActualCase()\n";
MockSystem os;
os.SetPaths({
{ "/", { {}, {} } },
{ "/mixed", { "MiXeD", {} } },
{ "/mixed/link-mixed", { "LiNk-MiXeD", "mixed" } },
{ "/mixed/mixed", { "MiXeD", {} } },
{ "/mixed/link-c-mixed", { "LiNk-C-MiXeD", "/mIxEd" } },
{ "/upper", { "UPPER", {} } },
{ "/upper/link-upper", { "LINK-UPPER", "upper" } },
{ "/upper/upper", { "UPPER", {} } },
{ "/upper/link-c-upper", { "LINK-C-UPPER", "/upper" } },
});
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("/mIxEd/MiSsInG", "/MiXeD/MiSsInG");
EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/LiNk-MiXeD");
EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD/LiNk-C-MiXeD");
EXPECT_RESOLVE("/upper/mIsSiNg", "/UPPER/mIsSiNg");
EXPECT_RESOLVE("/upper/link-upper", "/UPPER/LINK-UPPER");
EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER/LINK-C-UPPER");
}
{
Resolver<Policies::LogicalPath> const r(os);
EXPECT_RESOLVE("/mIxEd/MiSsInG", "/MiXeD/MiSsInG");
EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/LiNk-MiXeD");
EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD/LiNk-C-MiXeD");
EXPECT_RESOLVE("/upper/mIsSiNg", "/UPPER/mIsSiNg");
EXPECT_RESOLVE("/upper/link-upper", "/UPPER/LINK-UPPER");
EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER/LINK-C-UPPER");
}
{
Resolver<Policies::RealPath> const r(os);
EXPECT_ENOENT("/mIxEd/MiSsInG", "/MiXeD/MiSsInG");
EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/MiXeD");
EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD");
EXPECT_ENOENT("/upper/mIsSiNg", "/UPPER/mIsSiNg");
EXPECT_RESOLVE("/upper/link-upper", "/UPPER/UPPER");
EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER");
}
return true;
}
#endif
#ifdef _WIN32
bool windowsRoot()
{
std::cout << "windowsRoot()\n";
MockSystem os;
{
Resolver<Policies::NaivePath> const r(os);
EXPECT_RESOLVE("c:/", "c:/");
EXPECT_RESOLVE("C:/", "C:/");
EXPECT_RESOLVE("c://", "c:/");
EXPECT_RESOLVE("C:/.", "C:/");
EXPECT_RESOLVE("c:/./", "c:/");
EXPECT_RESOLVE("C:/..", "C:/");
EXPECT_RESOLVE("c:/../", "c:/");
}
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("c:/", "C:/");
EXPECT_RESOLVE("C:/", "C:/");
EXPECT_RESOLVE("c://", "C:/");
EXPECT_RESOLVE("C:/.", "C:/");
EXPECT_RESOLVE("c:/./", "C:/");
EXPECT_RESOLVE("C:/..", "C:/");
EXPECT_RESOLVE("c:/../", "C:/");
}
os.SetPaths({
{ "c:/", { {}, {} } },
{ "//host/", { {}, {} } },
});
{
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("c:/", "C:/");
EXPECT_RESOLVE("C:/", "C:/");
EXPECT_RESOLVE("c://", "C:/");
EXPECT_RESOLVE("C:/.", "C:/");
EXPECT_RESOLVE("c:/./", "C:/");
EXPECT_RESOLVE("C:/..", "C:/");
EXPECT_RESOLVE("c:/../", "C:/");
EXPECT_RESOLVE("//host", "//host/");
EXPECT_RESOLVE("//host/.", "//host/");
EXPECT_RESOLVE("//host/./", "//host/");
EXPECT_RESOLVE("//host/..", "//host/");
EXPECT_RESOLVE("//host/../", "//host/");
}
return true;
}
bool windowsAbsolutePath()
{
std::cout << "windowsAbsolutePath()\n";
MockSystem os;
os.SetPaths({
{ "c:/", { {}, {} } },
{ "c:/a", { {}, {} } },
});
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("c:/a", "C:/a");
EXPECT_RESOLVE("c:/a/", "C:/a");
EXPECT_RESOLVE("c:/a//", "C:/a");
EXPECT_RESOLVE("c:/a/.", "C:/a");
EXPECT_RESOLVE("c:/a/./", "C:/a");
EXPECT_RESOLVE("c:/a/..", "C:/");
EXPECT_RESOLVE("c:/a/../", "C:/");
EXPECT_RESOLVE("c:/a/../..", "C:/");
return true;
}
bool windowsActualCase()
{
std::cout << "windowsActualCase()\n";
MockSystem os;
os.SetPaths({
{ "c:/", { {}, {} } },
{ "c:/mixed", { "MiXeD", {} } },
{ "c:/mixed/link-mixed", { "LiNk-MiXeD", "mixed" } },
{ "c:/mixed/mixed", { "MiXeD", {} } },
{ "c:/mixed/link-c-mixed", { "LiNk-C-MiXeD", "C:/mIxEd" } },
{ "c:/upper", { "UPPER", {} } },
{ "c:/upper/link-upper", { "LINK-UPPER", "upper" } },
{ "c:/upper/upper", { "UPPER", {} } },
{ "c:/upper/link-c-upper", { "LINK-C-UPPER", "c:/upper" } },
});
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("c:/mIxEd/MiSsInG", "C:/MiXeD/MiSsInG");
EXPECT_RESOLVE("c:/mIxEd/link-MiXeD", "C:/MiXeD/LiNk-MiXeD");
EXPECT_RESOLVE("c:/mIxEd/link-c-MiXeD", "C:/MiXeD/LiNk-C-MiXeD");
EXPECT_RESOLVE("c:/upper/mIsSiNg", "C:/UPPER/mIsSiNg");
EXPECT_RESOLVE("c:/upper/link-upper", "C:/UPPER/LINK-UPPER");
EXPECT_RESOLVE("c:/upper/link-c-upper", "C:/UPPER/LINK-C-UPPER");
}
{
Resolver<Policies::LogicalPath> const r(os);
EXPECT_RESOLVE("c:/mIxEd/MiSsInG", "C:/MiXeD/MiSsInG");
EXPECT_RESOLVE("c:/mIxEd/link-MiXeD", "C:/MiXeD/LiNk-MiXeD");
EXPECT_RESOLVE("c:/mIxEd/link-c-MiXeD", "C:/MiXeD/LiNk-C-MiXeD");
EXPECT_RESOLVE("c:/upper/mIsSiNg", "C:/UPPER/mIsSiNg");
EXPECT_RESOLVE("c:/upper/link-upper", "C:/UPPER/LINK-UPPER");
EXPECT_RESOLVE("c:/upper/link-c-upper", "C:/UPPER/LINK-C-UPPER");
}
{
Resolver<Policies::RealPath> const r(os);
EXPECT_ENOENT("c:/mIxEd/MiSsInG", "C:/MiXeD/MiSsInG");
EXPECT_RESOLVE("c:/mIxEd/link-MiXeD", "C:/MiXeD/MiXeD");
EXPECT_RESOLVE("c:/mIxEd/link-c-MiXeD", "C:/MiXeD");
EXPECT_ENOENT("c:/upper/mIsSiNg", "C:/UPPER/mIsSiNg");
EXPECT_RESOLVE("c:/upper/link-upper", "C:/UPPER/UPPER");
EXPECT_RESOLVE("c:/upper/link-c-upper", "C:/UPPER");
}
return true;
}
bool windowsWorkingDirectory()
{
std::cout << "windowsWorkingDirectory()\n";
MockSystem os;
os.SetPaths({
{ "c:/", { {}, {} } },
{ "c:/a", { {}, {} } },
{ "c:/cwd", { {}, {} } },
{ "c:/cwd/a", { {}, {} } },
});
{
Resolver<Policies::LogicalPath> const r(os);
EXPECT_RESOLVE("", "/");
EXPECT_RESOLVE(".", "/");
EXPECT_RESOLVE("..", "/");
EXPECT_RESOLVE("a", "/a");
}
{
Resolver<Policies::RealPath> const r(os);
os.SetWorkDir("c:/cwd");
EXPECT_RESOLVE("", "C:/cwd");
EXPECT_RESOLVE(".", "C:/cwd");
EXPECT_RESOLVE("..", "C:/");
EXPECT_RESOLVE("a", "C:/cwd/a");
EXPECT_ENOENT("missing", "C:/cwd/missing");
}
return true;
}
bool windowsWorkingDirectoryOnDrive()
{
std::cout << "windowsWorkingDirectoryOnDrive()\n";
MockSystem os;
os.SetWorkDir("c:/cwd");
os.SetWorkDirOnDrive({
{ 'd', "d:/cwd-d" },
});
{
Resolver<Policies::NaivePath> const r(os);
EXPECT_RESOLVE("c:", "c:/cwd");
EXPECT_RESOLVE("c:.", "c:/cwd");
EXPECT_RESOLVE("c:..", "c:/");
EXPECT_RESOLVE("C:", "C:/cwd");
EXPECT_RESOLVE("C:.", "C:/cwd");
EXPECT_RESOLVE("C:..", "C:/");
EXPECT_RESOLVE("d:", "d:/cwd-d");
EXPECT_RESOLVE("d:.", "d:/cwd-d");
EXPECT_RESOLVE("d:..", "d:/");
EXPECT_RESOLVE("D:", "D:/cwd-d");
EXPECT_RESOLVE("D:.", "D:/cwd-d");
EXPECT_RESOLVE("D:..", "D:/");
EXPECT_RESOLVE("e:", "e:/");
EXPECT_RESOLVE("e:.", "e:/");
EXPECT_RESOLVE("e:..", "e:/");
EXPECT_RESOLVE("E:", "E:/");
EXPECT_RESOLVE("E:.", "E:/");
EXPECT_RESOLVE("E:..", "E:/");
}
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("c:", "C:/cwd");
EXPECT_RESOLVE("c:.", "C:/cwd");
EXPECT_RESOLVE("c:..", "C:/");
EXPECT_RESOLVE("C:", "C:/cwd");
EXPECT_RESOLVE("C:.", "C:/cwd");
EXPECT_RESOLVE("C:..", "C:/");
EXPECT_RESOLVE("d:", "D:/cwd-d");
EXPECT_RESOLVE("d:.", "D:/cwd-d");
EXPECT_RESOLVE("d:..", "D:/");
EXPECT_RESOLVE("D:", "D:/cwd-d");
EXPECT_RESOLVE("D:.", "D:/cwd-d");
EXPECT_RESOLVE("D:..", "D:/");
EXPECT_RESOLVE("e:", "E:/");
EXPECT_RESOLVE("e:.", "E:/");
EXPECT_RESOLVE("e:..", "E:/");
EXPECT_RESOLVE("E:", "E:/");
EXPECT_RESOLVE("E:.", "E:/");
EXPECT_RESOLVE("E:..", "E:/");
}
os.SetPaths({
{ "c:/", { {}, {} } },
{ "c:/cwd", { {}, {} } },
{ "c:/cwd/existing", { {}, {} } },
{ "d:/", { {}, {} } },
{ "d:/cwd-d", { {}, {} } },
{ "d:/cwd-d/existing", { {}, {} } },
{ "e:/", { {}, {} } },
});
{
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("c:existing", "C:/cwd/existing");
EXPECT_ENOENT("c:missing", "C:/cwd/missing");
EXPECT_RESOLVE("C:existing", "C:/cwd/existing");
EXPECT_ENOENT("C:missing", "C:/cwd/missing");
EXPECT_RESOLVE("d:existing", "D:/cwd-d/existing");
EXPECT_ENOENT("d:missing", "D:/cwd-d/missing");
EXPECT_ENOENT("e:missing", "E:/missing");
EXPECT_ENOENT("f:", "F:/");
}
return true;
}
bool windowsNetworkShare()
{
std::cout << "windowsNetworkShare()\n";
MockSystem os;
os.SetPaths({
{ "c:/", { {}, {} } },
{ "c:/cwd", { {}, {} } },
{ "c:/cwd/link-to-host-share", { {}, "//host/share" } },
{ "//host/", { {}, {} } },
{ "//host/share", { {}, {} } },
});
os.SetWorkDir("c:/cwd");
{
Resolver<Policies::RealPath> const r(os);
EXPECT_RESOLVE("//host/share", "//host/share");
EXPECT_RESOLVE("//host/share/", "//host/share");
EXPECT_RESOLVE("//host/share/.", "//host/share");
EXPECT_RESOLVE("//host/share/./", "//host/share");
EXPECT_RESOLVE("//host/share/..", "//host/");
EXPECT_RESOLVE("//host/share/../", "//host/");
EXPECT_RESOLVE("//host/share/../..", "//host/");
EXPECT_RESOLVE("link-to-host-share", "//host/share");
EXPECT_RESOLVE("link-to-host-share/..", "//host/");
EXPECT_ENOENT("link-to-host-share/../missing", "//host/missing");
}
{
Resolver<Policies::LogicalPath> const r(os);
EXPECT_RESOLVE("link-to-host-share", "C:/cwd/link-to-host-share");
EXPECT_RESOLVE("link-to-host-share/..", "//host/");
EXPECT_RESOLVE("link-to-host-share/../missing", "//host/missing");
}
{
Resolver<Policies::CasePath> const r(os);
EXPECT_RESOLVE("link-to-host-share", "C:/cwd/link-to-host-share");
EXPECT_RESOLVE("link-to-host-share/..", "C:/cwd");
EXPECT_RESOLVE("link-to-host-share/../missing", "C:/cwd/missing");
}
return true;
}
#endif
}
int testPathResolver(int /*unused*/, char* /*unused*/[])
{
return runTests({
posixRoot,
posixAbsolutePath,
posixWorkingDirectory,
posixSymlink,
#ifdef __APPLE__
macosActualCase,
#endif
#ifdef _WIN32
windowsRoot,
windowsAbsolutePath,
windowsActualCase,
windowsWorkingDirectory,
windowsWorkingDirectoryOnDrive,
windowsNetworkShare,
#endif
});
}