Skip to content

Commit

Permalink
Wrap all steps in FindFilesystem.cmake in a function to avoid local v…
Browse files Browse the repository at this point in the history
…ariables leaking out. This should fix #16
  • Loading branch information
DavidAce committed Sep 11, 2022
1 parent f292750 commit 41c720e
Showing 1 changed file with 123 additions and 119 deletions.
242 changes: 123 additions & 119 deletions cmake/modules/FindFilesystem.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -104,146 +104,150 @@ if(TARGET std::filesystem)
# This module has already been processed. Don't do it again.
return()
endif()
function(find_filesystem)
cmake_policy(PUSH)
if(POLICY CMP0067)
# pass CMAKE_CXX_STANDARD to check_cxx_source_compiles()
# has to appear before including CheckCXXSourceCompiles module
cmake_policy(SET CMP0067 NEW)
endif()

cmake_policy(PUSH)
if(POLICY CMP0067)
# pass CMAKE_CXX_STANDARD to check_cxx_source_compiles()
# has to appear before including CheckCXXSourceCompiles module
cmake_policy(SET CMP0067 NEW)
endif()

include(CMakePushCheckState)
include(CheckIncludeFileCXX)
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
include(CheckIncludeFileCXX)
include(CheckCXXSourceCompiles)

cmake_push_check_state()
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
cmake_push_check_state()
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})

# All of our tests required C++17 or later
set(CMAKE_CXX_STANDARD 17)
# All of our tests required C++17 or later
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Normalize and check the component list we were given
set(want_components ${Filesystem_FIND_COMPONENTS})
if(Filesystem_FIND_COMPONENTS STREQUAL "")
set(want_components Final)
endif()

# Warn on any unrecognized components
set(extra_components ${want_components})
list(REMOVE_ITEM extra_components Final Experimental)
foreach(component IN LISTS extra_components)
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
endforeach()

# Detect which of Experimental and Final we should look for
set(find_experimental TRUE)
set(find_final TRUE)
if(NOT "Final" IN_LIST want_components)
set(find_final FALSE)
endif()
if(NOT "Experimental" IN_LIST want_components)
set(find_experimental FALSE)
endif()
# Normalize and check the component list we were given
set(want_components ${Filesystem_FIND_COMPONENTS})
if(Filesystem_FIND_COMPONENTS STREQUAL "")
set(want_components Final)
endif()

if(find_final)
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
if(_CXX_FILESYSTEM_HAVE_HEADER)
# We found the non-experimental header. Don't bother looking for the
# experimental one.
# Warn on any unrecognized components
set(extra_components ${want_components})
list(REMOVE_ITEM extra_components Final Experimental)
foreach(component IN LISTS extra_components)
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
endforeach()

# Detect which of Experimental and Final we should look for
set(find_experimental TRUE)
set(find_final TRUE)
if(NOT "Final" IN_LIST want_components)
set(find_final FALSE)
endif()
if(NOT "Experimental" IN_LIST want_components)
set(find_experimental FALSE)
endif()
else()
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
endif()

if(find_experimental)
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
else()
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
endif()

if(_CXX_FILESYSTEM_HAVE_HEADER)
set(_have_fs TRUE)
set(_fs_header filesystem)
set(_fs_namespace std::filesystem)
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
set(_have_fs TRUE)
set(_fs_header experimental/filesystem)
set(_fs_namespace std::experimental::filesystem)
else()
set(_have_fs FALSE)
endif()

set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")

set(_found FALSE)

if(CXX_FILESYSTEM_HAVE_FS)
# We have some filesystem library available. Do link checks
## HERE STARTS MODIFICATION 1
string(CONFIGURE [[
#include <@CXX_FILESYSTEM_HEADER@>

int main() {
@CXX_FILESYSTEM_NAMESPACE@::path testpath = \"../\";
@CXX_FILESYSTEM_NAMESPACE@::exists(testpath);
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
return static_cast<int>(cwd.string().size());
}
]] code @ONLY)

if (NOT can_link)
# Try to compile a simple filesystem program without any linker flags
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
if(find_final)
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
if(_CXX_FILESYSTEM_HAVE_HEADER)
# We found the non-experimental header. Don't bother looking for the
# experimental one.
set(find_experimental FALSE)
endif()
else()
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
endif()

if(NOT can_link)
# Try to compile a simple filesystem program with the libstdc++ flag
set(CMAKE_REQUIRED_LIBRARIES stdc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
if(find_experimental)
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
else()
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
endif()

if(NOT can_link)
# Try to compile a simple filesystem program with the libc++ flag
set(CMAKE_REQUIRED_LIBRARIES -lc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
if(_CXX_FILESYSTEM_HAVE_HEADER)
set(_have_fs TRUE)
set(_fs_header filesystem)
set(_fs_namespace std::filesystem)
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
set(_have_fs TRUE)
set(_fs_header experimental/filesystem)
set(_fs_namespace std::experimental::filesystem)
else()
set(_have_fs FALSE)
endif()
## HERE ENDS MODIFICATION 1

if(can_link)
add_library(std::filesystem INTERFACE IMPORTED)
target_compile_features(std::filesystem INTERFACE cxx_std_17)
set(_found TRUE)

if(CXX_FILESYSTEM_NO_LINK_NEEDED)
# on certain linux distros we have a version of libstdc++ which has the final code for c++17 fs in the
# libstdc++.so.*. BUT when compiling with g++ < 9, we MUST still link with libstdc++fs.a
# libc++ should not suffer from this issue, so, in theory we should be fine with only checking for
# GCC's libstdc++
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0"))

set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")

set(_found FALSE)

if(CXX_FILESYSTEM_HAVE_FS)
# We have some filesystem library available. Do link checks
## HERE STARTS MODIFICATION 1
string(CONFIGURE [[
#include <@CXX_FILESYSTEM_HEADER@>

int main() {
@CXX_FILESYSTEM_NAMESPACE@::path testpath = \"../\";
@CXX_FILESYSTEM_NAMESPACE@::exists(testpath);
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
return static_cast<int>(cwd.string().size());
}
]] code @ONLY)

if (NOT can_link)
# Try to compile a simple filesystem program without any linker flags
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
endif()

if(NOT can_link)
# Try to compile a simple filesystem program with the libstdc++ flag
set(CMAKE_REQUIRED_LIBRARIES stdc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
endif()

if(NOT can_link)
# Try to compile a simple filesystem program with the libc++ flag
set(CMAKE_REQUIRED_LIBRARIES -lc++fs)
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
endif()
## HERE ENDS MODIFICATION 1

if(can_link)
add_library(std::filesystem INTERFACE IMPORTED)
target_compile_features(std::filesystem INTERFACE cxx_std_17)
set(_found TRUE)

if(CXX_FILESYSTEM_NO_LINK_NEEDED)
# on certain linux distros we have a version of libstdc++ which has the final code for c++17 fs in the
# libstdc++.so.*. BUT when compiling with g++ < 9, we MUST still link with libstdc++fs.a
# libc++ should not suffer from this issue, so, in theory we should be fine with only checking for
# GCC's libstdc++
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0"))
target_link_libraries(std::filesystem INTERFACE stdc++fs)
endif()
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
target_link_libraries(std::filesystem INTERFACE stdc++fs)
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
target_link_libraries(std::filesystem INTERFACE c++fs)
endif()
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
target_link_libraries(std::filesystem INTERFACE stdc++fs)
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
target_link_libraries(std::filesystem INTERFACE c++fs)
endif()
endif()
endif()

cmake_pop_check_state()
cmake_pop_check_state()

set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE)
cmake_policy(POP)
endfunction()

set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE)
find_filesystem()

if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
message(FATAL_ERROR "Cannot Compile simple program using std::filesystem")
endif()

cmake_policy(POP)

0 comments on commit 41c720e

Please sign in to comment.