# Copyright 2020, Collabora, Ltd. # # SPDX-License-Identifier: BSL-1.0 set(_FILESYSTEM_UTILS_DIR "${PROJECT_SOURCE_DIR}/src/common") if(MSVC AND MSVC_VERSION GREATER 1890) set(HAVE_FILESYSTEM_WITHOUT_LIB ON CACHE INTERNAL "" FORCE ) if(MSVC_VERSION GREATER 1910) # Visual Studio 2017 Update 3 added new filesystem impl, # which only works in C++17 mode. set(HAVE_FILESYSTEM_NEEDS_17 ON CACHE INTERNAL "" FORCE ) endif() else() include(CheckCXXSourceCompiles) ### # Test Sources ### # This is just example code that is known to not compile if std::filesystem isn't working right. # It depends on having the proper includes and `using namespace` so it can use the `is_regular_file` # function unqualified. # It is at the end of every test file below. set(_stdfs_test_source "int main() { (void)is_regular_file(\"/\"); return 0; } " ) # This is preprocessor code included in all test compiles, which pulls in the conditions # originally found in filesystem_utils.cpp. # # It defines: # USE_FINAL_FS = 1 if it thinks we have the full std::filesystem in as in C++17 # USE_EXPERIMENTAL_FS = 1 if it thinks we don't have the full c++17 filesystem, but should have # std::experimental::filesystem and # # Ideally the first condition (__cplusplus >= 201703L) would handle most cases, # however you're not supposed to report that unless you're fully conformant with all # of c++17, so you might have a c++17 build flag and the final filesystem library but # a lower __cplusplus value if some other part of the standard is missing. set(_stdfs_conditions "#include ") # This should only compile if our common detection code decides on the # **final** (non-experimental) filesystem library. set(_stdfs_source "${_stdfs_conditions} #if defined(USE_FINAL_FS) && USE_FINAL_FS #include using namespace std::filesystem; #endif ${_stdfs_test_source} " ) # This should only compile if our common detection code decides on the # **experimental** filesystem library. set(_stdfs_experimental_source "${_stdfs_conditions} #if defined(USE_EXPERIMENTAL_FS) && USE_EXPERIMENTAL_FS #include using namespace std::experimental::filesystem; #endif ${_stdfs_test_source} " ) # This should compile if the common detection code decided that either # the experimental or final filesystem library is available. # We use this when trying to detect what library to link, if any: # earlier checks are the ones that care about how we include the headers. set(_stdfs_needlib_source "${_stdfs_conditions} #if defined(USE_FINAL_FS) && USE_FINAL_FS #include using namespace std::filesystem; #endif #if defined(USE_EXPERIMENTAL_FS) && USE_EXPERIMENTAL_FS #include using namespace std::experimental::filesystem; #endif ${_stdfs_test_source} " ) ### # Identifying header/namespace and standards flags ### # First, just look for the include. # We're checking if it compiles, not if the include exists, # because the source code uses the same conditionals to decide. # (Static libraries are just object files, they don't get linked) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set(CMAKE_REQUIRED_INCLUDES "${_FILESYSTEM_UTILS_DIR}") unset(CMAKE_REQUIRED_LIBRARIES) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0) # GCC 11+ defaults to C++17 mode and acts badly in these tests if we tell it to use a different version. set(HAVE_FILESYSTEM_IN_STD_17 ON) set(HAVE_FILESYSTEM_IN_STD OFF) else() set(CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=14 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE") check_cxx_source_compiles("${_stdfs_source}" HAVE_FILESYSTEM_IN_STD) check_cxx_source_compiles("${_stdfs_experimental_source}" HAVE_FILESYSTEM_IN_STDEXPERIMENTAL) # See if the "final" version builds if we try to specify C++17 explicitly set(CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE") check_cxx_source_compiles("${_stdfs_source}" HAVE_FILESYSTEM_IN_STD_17) unset(CMAKE_REQUIRED_FLAGS) endif() # If we found the final version of filesystem when specifying C++17 explicitly, # but found it no other way, then we record that we must use C++17 flags. if(HAVE_FILESYSTEM_IN_STD_17 AND NOT HAVE_FILESYSTEM_IN_STD) set(HAVE_FILESYSTEM_NEEDS_17 ON CACHE INTERNAL "" ) set(CMAKE_REQUIRED_FLAGS "-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=TRUE") else() set(HAVE_FILESYSTEM_NEEDS_17 OFF CACHE INTERNAL "" ) endif() ### # Identifying library to link ### # Now, see if we need to link against libstdc++fs, and what it's called # If we needed C++17 standard flags to find it, they've already been set above. set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) # Try with no lib specified check_cxx_source_compiles("${_stdfs_needlib_source}" HAVE_FILESYSTEM_WITHOUT_LIB) # Try with stdc++fs set(CMAKE_REQUIRED_LIBRARIES stdc++fs) check_cxx_source_compiles("${_stdfs_needlib_source}" HAVE_FILESYSTEM_NEEDING_LIBSTDCXXFS) # Try with c++fs (from clang's libc++) set(CMAKE_REQUIRED_LIBRARIES c++fs) check_cxx_source_compiles("${_stdfs_needlib_source}" HAVE_FILESYSTEM_NEEDING_LIBCXXFS) # Clean up these variables before the next user. unset(CMAKE_REQUIRED_LIBRARIES) unset(CMAKE_TRY_COMPILE_TARGET_TYPE) unset(CMAKE_REQUIRED_INCLUDES) endif() # Use the observations of the code above to add the filesystem_utils.cpp # file to a target, along with any required compiler settings and libraries. # Also handles our BUILD_WITH_STD_FILESYSTEM option. function(openxr_add_filesystem_utils TARGET_NAME) target_sources(${TARGET_NAME} PRIVATE ${_FILESYSTEM_UTILS_DIR}/filesystem_utils.cpp) target_include_directories(${TARGET_NAME} PRIVATE ${_FILESYSTEM_UTILS_DIR}) if(NOT BUILD_WITH_STD_FILESYSTEM) target_compile_definitions(${TARGET_NAME} PRIVATE DISABLE_STD_FILESYSTEM) else() if(HAVE_FILESYSTEM_NEEDS_17) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 17) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD_REQUIRED TRUE) endif() if(NOT HAVE_FILESYSTEM_WITHOUT_LIB) if(HAVE_FILESYSTEM_NEEDING_LIBSTDCXXFS) target_link_libraries(${TARGET_NAME} PRIVATE stdc++fs) elseif(HAVE_FILESYSTEM_NEEDING_LIBCXXFS) target_link_libraries(${TARGET_NAME} PRIVATE c++fs) endif() endif() endif() endfunction()