Skip to content

Instantly share code, notes, and snippets.

@scivision
Last active December 3, 2023 19:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scivision/b13f69c13ae533bd884095b4f7dbafed to your computer and use it in GitHub Desktop.
Save scivision/b13f69c13ae533bd884095b4f7dbafed to your computer and use it in GitHub Desktop.
MinGW C++ filesystem glitches

C++ UNC path filesystem bug with MinGW

MinGW currently has bugs with UNC paths in C++ filesystem library. MSVC, Clang-CL, oneAPI work correctly.

A convenience script runs the tests from the oneAPI command prompt (or MSVC if oneAPI not available).

cmake -P main.cmake

MSVC

cl /std:c++17 test_unc.cpp && test_unc.exe
PASSED: path: "\\\\server\\share\\path\\" == "\\\\server\\share\\path\\"
PASSED: path: "\\\\host\\a\\c" == "\\\\host\\a\\c"
PASSED: string: \\host\a\c == \\host\a\c
PASSED: generic string: //host/a/c == //host/a/c
OK: UNC tests passed

MSYS2 G++

g++ --version && g++ test_unc.cpp && ./a.exe
g++.exe (Rev3, Built by MSYS2 project) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

PASSED: path: "\\server\\share\\path\\" == "\\server\\share\\path\\"
PASSED: path: "\\host\\a\\c" == "\\\\host\\a\\c"
FAILED: string: \host\a\c != \\host\a\c
FAILED: generic string: /host/a/c != //host/a/c
FAILED: UNC tests

MSYS2 Clang

clang++ --version && clang++ test_unc.cpp && ./a.exe
clang version 17.0.6
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:/msys64/mingw64/bin
PASSED: path: "\\server\\share\\path\\" == "\\server\\share\\path\\"
PASSED: path: "\\host\\a\\c" == "\\\\host\\a\\c"
FAILED: string: \host\a\c != \\host\a\c
FAILED: generic string: /host/a/c != //host/a/c
FAILED: UNC tests

Intel oneAPI

ICX (MSVC-like) driver

icx test_unc.cpp && test_unc.exe
Intel(R) oneAPI DPC++/C++ Compiler 2024.0.0 (2024.0.0.20231017)
Target: x86_64-pc-windows-msvc
Thread model: posix

PASSED: path: "\\\\server\\share\\path\\" == "\\\\server\\share\\path\\"
PASSED: path: "\\\\host\\a\\c" == "\\\\host\\a\\c"
PASSED: string: \\host\a\c == \\host\a\c
PASSED: generic string: //host/a/c == //host/a/c
OK: UNC tests passed

ICPX (GNU-like) driver

icpx --version && icpx test_unc.cpp && a.exe
Intel(R) oneAPI DPC++/C++ Compiler 2024.0.0 (2024.0.0.20231017)
Target: x86_64-pc-windows-msvc
Thread model: posix

PASSED: path: "\\\\server\\share\\path\\" == "\\\\server\\share\\path\\"
PASSED: path: "\\\\host\\a\\c" == "\\\\host\\a\\c"
PASSED: string: \\host\a\c == \\host\a\c
PASSED: generic string: //host/a/c == //host/a/c
OK: UNC tests passed
cmake_minimum_required(VERSION 3.10...3.28)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed.
cmake -Bbuild")
endif()
project(fs_unc LANGUAGES CXX)
enable_testing()
set(CMAKE_CXX_STANDARD 17)
add_executable(test_unc test_unc.cpp)
add_test(NAME ${CMAKE_CXX_COMPILER_ID}_unc COMMAND test_unc)
file(GENERATE OUTPUT .gitignore CONTENT "*")
cmake_minimum_required(VERSION 3.21)
# run this script from Windows Intel oneAPI command prompt,
# or MSVC prompt if you don't have oneAPI.
set(compilers cl clang-cl clang++ g++ icx)
# icpx not yet enabled on Windows in CMake
function(run_compiler exe)
find_program(cexe NAMES ${exe} NO_CACHE)
if(NOT cexe)
message(STATUS "Compiler ${exe} not found")
return()
endif()
set(bindir ${CMAKE_CURRENT_LIST_DIR}/build_${exe})
execute_process(COMMAND ${CMAKE_COMMAND}
-B${bindir} -S${CMAKE_CURRENT_LIST_DIR}
-DCMAKE_CXX_COMPILER=${cexe}
RESULT_VARIABLE ret
)
if(NOT ret EQUAL 0)
message(WARNING "Compiler ${c} failed to configure")
return()
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build ${bindir}
RESULT_VARIABLE ret
)
if(NOT ret EQUAL 0)
message(WARNING "Compiler ${c} build failed")
return()
endif()
execute_process(COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${bindir} --verbose
RESULT_VARIABLE ret
)
if(NOT ret EQUAL 0)
message(WARNING "Compiler ${c} tests failed")
return()
endif()
endfunction(run_compiler)
foreach(c IN LISTS compilers)
run_compiler(${c})
endforeach()
#include <filesystem>
#include <cstdlib>
#include <iostream>
static_assert(__cpp_lib_filesystem, "C++17 std::filesystem is not available.");
namespace fs = std::filesystem;
int main(){
int err = 0;
fs::path bserv("\\\\server\\share\\\\path\\");
fs::path rserv("//server/share//path/");
fs::path n_bserv = bserv.lexically_normal();
fs::path n_rserv = rserv.lexically_normal();
if (n_bserv == n_rserv){
std::cout << "PASSED: path: " << n_bserv << " == " << n_rserv << "\n";
}
else {
err++;
std::cerr << "FAILED: path: " << n_bserv << " != " << n_rserv << "\n";
}
fs::path p3("//host/a/b/../c");
fs::path p3n = p3.lexically_normal();
fs::path p3n_r("\\\\host\\a\\c");
if (p3n == p3n_r){
std::cout << "PASSED: path: " << p3n << " == " << p3n_r << "\n";
}
else {
err++;
std::cerr << "FAILED: path: " << p3n << " != " << p3n_r << "\n";
}
std::string p3s = p3n.string();
if (p3s == "\\\\host\\a\\c"){
std::cout << "PASSED: string: " << p3s << " == \\\\host\\a\\c\n";
}
else {
err++;
std::cerr << "FAILED: string: " << p3s << " != \\\\host\\a\\c\n";
}
std::string p3gs = p3n.generic_string();
if (p3gs == "//host/a/c"){
std::cout << "PASSED: generic string: " << p3gs << " == //host/a/c\n";
}
else {
err++;
std::cerr << "FAILED: generic string: " << p3gs << " != //host/a/c\n";
}
fs::path dlocal("\\\\?\\C:\\path\\x\\..");
fs::path blocal("C:\\path\\y\\..\\");
fs::path n_dlocal = fs::weakly_canonical(dlocal);
fs::path n_blocal = blocal.lexically_normal();
if (n_dlocal == n_blocal){
std::cout << "PASSED: long_path: " << n_dlocal << " == " << n_blocal << "\n";
}
else {
err++;
std::cerr << "FAILED: long_path: " << n_dlocal << " != " << n_blocal << "\n";
}
if (err != 0){
std::cerr << "FAILED: UNC tests\n";
return EXIT_FAILURE;
}
std::cout << "OK: UNC tests passed\n";
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment