22#
33# SPDX-License-Identifier: BSL-1.0
44
5+ set (_FILESYSTEM_UTILS_DIR "${PROJECT_SOURCE_DIR} /src/common" )
6+
57if (MSVC AND MSVC_VERSION GREATER 1890)
68 set (HAVE_FILESYSTEM_WITHOUT_LIB
79 ON
@@ -18,87 +20,107 @@ if(MSVC AND MSVC_VERSION GREATER 1890)
1820else ()
1921 include (CheckCXXSourceCompiles)
2022
21- # This is just example code that is known to not compile if std::filesystem isn't working right
23+ ###
24+ # Test Sources
25+ ###
26+
27+ # This is just example code that is known to not compile if std::filesystem isn't working right.
28+ # It depends on having the proper includes and `using namespace` so it can use the `is_regular_file`
29+ # function unqualified.
30+ # It is at the end of every test file below.
2231 set (_stdfs_test_source
2332 "int main() {
2433 (void)is_regular_file(\" /\" );
2534 return 0;
2635 }
2736 "
2837 )
29- set (_stdfs_conditions
30- "// If the C++ macro is set to the version containing C++17, it must support
31- // the final C++17 package
32- #if __cplusplus >= 201703L
33- #define USE_FINAL_FS 1
34-
35- #elif defined(_MSC_VER) && _MSC_VER >= 1900
36-
37- #if defined(_HAS_CXX17) && _HAS_CXX17
38- // When MSC supports c++17 use <filesystem> package.
39- #define USE_FINAL_FS 1
40- #endif // !_HAS_CXX17
41-
42- // Right now, GCC still only supports the experimental filesystem items starting in GCC 6
43- #elif (__GNUC__ >= 6)
44- #define USE_EXPERIMENTAL_FS 1
45-
46- // If Clang, check for feature support
47- #elif defined(__clang__) && (__cpp_lib_filesystem || __cpp_lib_experimental_filesystem)
48- #if __cpp_lib_filesystem
49- #define USE_FINAL_FS 1
50- #else
51- #define USE_EXPERIMENTAL_FS 1
52- #endif
53-
54- #endif
55- "
56- )
38+
39+ # This is preprocessor code included in all test compiles, which pulls in the conditions
40+ # originally found in filesystem_utils.cpp.
41+ #
42+ # It defines:
43+ # USE_FINAL_FS = 1 if it thinks we have the full std::filesystem in <filesystem> as in C++17
44+ # USE_EXPERIMENTAL_FS = 1 if it thinks we don't have the full c++17 filesystem, but should have
45+ # std::experimental::filesystem and <experimental/filesystem>
46+ #
47+ # Ideally the first condition (__cplusplus >= 201703L) would handle most cases,
48+ # however you're not supposed to report that unless you're fully conformant with all
49+ # of c++17, so you might have a c++17 build flag and the final filesystem library but
50+ # a lower __cplusplus value if some other part of the standard is missing.
51+ set (_stdfs_conditions "#include <stdfs_conditions.h>
52+ " )
53+
54+ # This should only compile if our common detection code decides on the
55+ # **final** (non-experimental) filesystem library.
5756 set (_stdfs_source
5857 "${_stdfs_conditions}
59- #ifdef USE_FINAL_FS
58+ #if defined(USE_FINAL_FS) && USE_FINAL_FS
6059 #include <filesystem>
6160 using namespace std::filesystem;
6261 #endif
6362 ${_stdfs_test_source}
6463 "
6564 )
65+
66+ # This should only compile if our common detection code decides on the
67+ # **experimental** filesystem library.
6668 set (_stdfs_experimental_source
6769 "${_stdfs_conditions}
68- #ifdef USE_EXPERIMENTAL_FS
70+ #if defined(USE_EXPERIMENTAL_FS) && USE_EXPERIMENTAL_FS
6971 #include <experimental/filesystem>
7072 using namespace std::experimental::filesystem;
7173 #endif
7274 ${_stdfs_test_source}
7375 "
7476 )
77+
78+ # This should compile if the common detection code decided that either
79+ # the experimental or final filesystem library is available.
80+ # We use this when trying to detect what library to link, if any:
81+ # earlier checks are the ones that care about how we include the headers.
7582 set (_stdfs_needlib_source
7683 "${_stdfs_conditions}
77- #ifdef USE_FINAL_FS
84+ #if defined(USE_FINAL_FS) && USE_FINAL_FS
7885 #include <filesystem>
7986 using namespace std::filesystem;
8087 #endif
81- #ifdef USE_EXPERIMENTAL_FS
88+ #if defined(USE_EXPERIMENTAL_FS) && USE_EXPERIMENTAL_FS
8289 #include <experimental/filesystem>
8390 using namespace std::experimental::filesystem;
8491 #endif
8592 ${_stdfs_test_source}
8693 "
8794 )
8895
96+ ###
97+ # Identifying header/namespace and standards flags
98+ ###
99+
89100 # First, just look for the include.
90101 # We're checking if it compiles, not if the include exists,
91- # because the source code uses similar conditionals to decide.
102+ # because the source code uses the same conditionals to decide.
103+ # (Static libraries are just object files, they don't get linked)
92104 set (CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
105+ set (CMAKE_REQUIRED_INCLUDES "${_FILESYSTEM_UTILS_DIR} " )
93106 unset (CMAKE_REQUIRED_LIBRARIES)
94- unset (CMAKE_REQUIRED_FLAGS)
95- check_cxx_source_compiles("${_stdfs_source} " HAVE_FILESYSTEM_IN_STD)
96- check_cxx_source_compiles("${_stdfs_experimental_source} " HAVE_FILESYSTEM_IN_STDEXPERIMENTAL)
107+ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0)
108+ # GCC 11+ defaults to C++17 mode and acts badly in these tests if we tell it to use a different version.
109+ set (HAVE_FILESYSTEM_IN_STD_17 ON )
110+ set (HAVE_FILESYSTEM_IN_STD OFF )
111+ else ()
112+ set (CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=14 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE" )
113+ check_cxx_source_compiles("${_stdfs_source} " HAVE_FILESYSTEM_IN_STD)
114+ check_cxx_source_compiles("${_stdfs_experimental_source} " HAVE_FILESYSTEM_IN_STDEXPERIMENTAL)
97115
98- set (CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE" )
99- check_cxx_source_compiles("${_stdfs_source} " HAVE_FILESYSTEM_IN_STD_17)
100- unset (CMAKE_REQUIRED_FLAGS)
116+ # See if the "final" version builds if we try to specify C++17 explicitly
117+ set (CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE" )
118+ check_cxx_source_compiles("${_stdfs_source} " HAVE_FILESYSTEM_IN_STD_17)
119+ unset (CMAKE_REQUIRED_FLAGS)
120+ endif ()
101121
122+ # If we found the final version of filesystem when specifying C++17 explicitly,
123+ # but found it no other way, then we record that we must use C++17 flags.
102124 if (HAVE_FILESYSTEM_IN_STD_17 AND NOT HAVE_FILESYSTEM_IN_STD)
103125 set (HAVE_FILESYSTEM_NEEDS_17
104126 ON
@@ -112,21 +134,37 @@ else()
112134 )
113135 endif ()
114136
115- # Now, see if we need libstdc++fs
137+ ###
138+ # Identifying library to link
139+ ###
140+
141+ # Now, see if we need to link against libstdc++fs, and what it's called
142+ # If we needed C++17 standard flags to find it, they've already been set above.
116143 set (CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
144+
145+ # Try with no lib specified
117146 check_cxx_source_compiles("${_stdfs_needlib_source} " HAVE_FILESYSTEM_WITHOUT_LIB)
147+
148+ # Try with stdc++fs
118149 set (CMAKE_REQUIRED_LIBRARIES stdc++fs)
119150 check_cxx_source_compiles("${_stdfs_needlib_source} " HAVE_FILESYSTEM_NEEDING_LIBSTDCXXFS)
151+
152+ # Try with c++fs (from clang's libc++)
120153 set (CMAKE_REQUIRED_LIBRARIES c++fs)
121154 check_cxx_source_compiles("${_stdfs_needlib_source} " HAVE_FILESYSTEM_NEEDING_LIBCXXFS)
155+
156+ # Clean up these variables before the next user.
122157 unset (CMAKE_REQUIRED_LIBRARIES)
123158 unset (CMAKE_TRY_COMPILE_TARGET_TYPE)
124-
159+ unset (CMAKE_REQUIRED_INCLUDES)
125160endif ()
126161
162+ # Use the observations of the code above to add the filesystem_utils.cpp
163+ # file to a target, along with any required compiler settings and libraries.
164+ # Also handles our BUILD_WITH_STD_FILESYSTEM option.
127165function (openxr_add_filesystem_utils TARGET_NAME)
128- target_sources (${TARGET_NAME} PRIVATE ${PROJECT_SOURCE_DIR} /src/common /filesystem_utils.cpp)
129- target_include_directories (${TARGET_NAME} PRIVATE ${PROJECT_SOURCE_DIR} /src/common )
166+ target_sources (${TARGET_NAME} PRIVATE ${_FILESYSTEM_UTILS_DIR} /filesystem_utils.cpp)
167+ target_include_directories (${TARGET_NAME} PRIVATE ${_FILESYSTEM_UTILS_DIR} )
130168 if (NOT BUILD_WITH_STD_FILESYSTEM)
131169 target_compile_definitions (${TARGET_NAME} PRIVATE DISABLE_STD_FILESYSTEM)
132170 else ()
0 commit comments