Skip to content

Commit

Permalink
[Support] Add a new path style for Windows with forward slashes
Browse files Browse the repository at this point in the history
This behaves just like the regular Windows style, with both separator
forms accepted, but with get_separator() returning forward slashes.

Add a more descriptive name for the existing style, keeping the old
name around as an alias initially.

Add a new function `make_preferred()` (like the C++17
`std::filesystem::path` function with the same name), which converts
windows paths to the preferred separator form (while this one works on
any platform and takes a `path::Style` argument).

Contrary to `native()` (just like `make_preferred()` in `std::filesystem`),
this doesn't do anything at all on Posix, it doesn't try to reinterpret
backslashes into forward slashes there.

Differential Revision: https://reviews.llvm.org/D111879
  • Loading branch information
mstorsjo committed Nov 5, 2021
1 parent f95bd18 commit a8b5483
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
19 changes: 18 additions & 1 deletion llvm/include/llvm/Support/Path.h
Expand Up @@ -25,7 +25,13 @@ namespace llvm {
namespace sys {
namespace path {

enum class Style { windows, posix, native };
enum class Style {
native,
posix,
windows_slash,
windows_backslash,
windows = windows_backslash, // deprecated
};

/// Check if \p S uses POSIX path rules.
constexpr bool is_style_posix(Style S) {
Expand Down Expand Up @@ -257,6 +263,17 @@ void native(const Twine &path, SmallVectorImpl<char> &result,
/// @param path A path that is transformed to native format.
void native(SmallVectorImpl<char> &path, Style style = Style::native);

/// For Windows path styles, convert path to use the preferred path separators.
/// For other styles, do nothing.
///
/// @param path A path that is transformed to preferred format.
inline void make_preferred(SmallVectorImpl<char> &path,
Style style = Style::native) {
if (!is_style_windows(style))
return;
native(path, style);
}

/// Replaces backslashes with slashes if Windows.
///
/// @param path processed path
Expand Down
10 changes: 7 additions & 3 deletions llvm/lib/Support/Path.cpp
Expand Up @@ -37,13 +37,15 @@ namespace {
using llvm::sys::path::Style;

inline Style real_style(Style style) {
if (style != Style::native)
return style;
if (is_style_posix(style))
return Style::posix;
return Style::windows;
}

inline const char *separators(Style style) {
if (real_style(style) == Style::windows)
if (is_style_windows(style))
return "\\/";
return "/";
}
Expand Down Expand Up @@ -547,7 +549,9 @@ void native(SmallVectorImpl<char> &Path, Style style) {
if (Path.empty())
return;
if (is_style_windows(style)) {
std::replace(Path.begin(), Path.end(), '/', '\\');
for (char &Ch : Path)
if (is_separator(Ch, style))
Ch = preferred_separator(style);
if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) {
SmallString<128> PathHome;
home_directory(PathHome);
Expand Down Expand Up @@ -601,7 +605,7 @@ bool is_separator(char value, Style style) {
}

StringRef get_separator(Style style) {
if (is_style_windows(style))
if (real_style(style) == Style::windows)
return "\\";
return "/";
}
Expand Down
35 changes: 33 additions & 2 deletions llvm/unittests/Support/Path.cpp
Expand Up @@ -75,7 +75,9 @@ TEST(is_style_Style, Works) {
// Check platform-independent results.
EXPECT_TRUE(is_style_posix(Style::posix));
EXPECT_TRUE(is_style_windows(Style::windows));
EXPECT_TRUE(is_style_windows(Style::windows_slash));
EXPECT_FALSE(is_style_posix(Style::windows));
EXPECT_FALSE(is_style_posix(Style::windows_slash));
EXPECT_FALSE(is_style_windows(Style::posix));

// Check platform-dependent results.
Expand All @@ -95,12 +97,19 @@ TEST(is_separator, Works) {
EXPECT_FALSE(path::is_separator(' '));

EXPECT_TRUE(path::is_separator('\\', path::Style::windows));
EXPECT_TRUE(path::is_separator('\\', path::Style::windows_slash));
EXPECT_FALSE(path::is_separator('\\', path::Style::posix));

EXPECT_EQ(path::is_style_windows(path::Style::native),
path::is_separator('\\'));
}

TEST(get_separator, Works) {
EXPECT_EQ(path::get_separator(path::Style::posix), "/");
EXPECT_EQ(path::get_separator(path::Style::windows_backslash), "\\");
EXPECT_EQ(path::get_separator(path::Style::windows_slash), "/");
}

TEST(is_absolute_gnu, Works) {
// Test tuple <Path, ExpectedPosixValue, ExpectedWindowsValue>.
const std::tuple<StringRef, bool, bool> Paths[] = {
Expand Down Expand Up @@ -383,6 +392,8 @@ TEST(Support, PathIterator) {
testing::ElementsAre("/", ".c", ".d", "..", "."));
EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),
testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_slash),
testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));
EXPECT_THAT(GetComponents("//net/c/foo.txt"),
testing::ElementsAre("//net", "/", "c", "foo.txt"));
Expand Down Expand Up @@ -1425,10 +1436,25 @@ TEST(Support, NormalizePath) {
for (auto &T : Tests) {
SmallString<64> Win(std::get<0>(T));
SmallString<64> Posix(Win);
SmallString<64> WinSlash(Win);
path::native(Win, path::Style::windows);
path::native(Posix, path::Style::posix);
path::native(WinSlash, path::Style::windows_slash);
EXPECT_EQ(std::get<1>(T), Win);
EXPECT_EQ(std::get<2>(T), Posix);
EXPECT_EQ(std::get<2>(T), WinSlash);
}

for (auto &T : Tests) {
SmallString<64> WinBackslash(std::get<0>(T));
SmallString<64> Posix(WinBackslash);
SmallString<64> WinSlash(WinBackslash);
path::make_preferred(WinBackslash, path::Style::windows_backslash);
path::make_preferred(Posix, path::Style::posix);
path::make_preferred(WinSlash, path::Style::windows_slash);
EXPECT_EQ(std::get<1>(T), WinBackslash);
EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged here
EXPECT_EQ(std::get<2>(T), WinSlash);
}

#if defined(_WIN32)
Expand All @@ -1437,10 +1463,15 @@ TEST(Support, NormalizePath) {

const char *Path7a = "~/aaa";
SmallString<64> Path7(Path7a);
path::native(Path7);
path::native(Path7, path::Style::windows_backslash);
EXPECT_TRUE(Path7.endswith("\\aaa"));
EXPECT_TRUE(Path7.startswith(PathHome));
EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));
Path7 = Path7a;
path::native(Path7, path::Style::windows_slash);
EXPECT_TRUE(Path7.endswith("/aaa"));
EXPECT_TRUE(Path7.startswith(PathHome));
EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));

const char *Path8a = "~";
SmallString<64> Path8(Path8a);
Expand All @@ -1454,7 +1485,7 @@ TEST(Support, NormalizePath) {

const char *Path10a = "aaa/~/b";
SmallString<64> Path10(Path10a);
path::native(Path10);
path::native(Path10, path::Style::windows_backslash);
EXPECT_EQ(Path10, "aaa\\~\\b");
#endif
}
Expand Down

0 comments on commit a8b5483

Please sign in to comment.