Created
May 31, 2020 14:49
-
-
Save nehaljwani/29d912b7395701c17866767c6d31c984 to your computer and use it in GitHub Desktop.
CMake: std::filesystem / std::experimental::filesystem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
# file Copyright.txt or https://cmake.org/licensing for details. | |
# This is a modified version of: | |
# https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake | |
#[=======================================================================[.rst: | |
FindStdFilesystem | |
################# | |
This module supports the standard library's (experimental?) filesystem | |
utilities. Use the :imp-target:`std::filesystem` imported target. | |
Imported Targets | |
**************** | |
.. imp-target:: std::filesystem | |
The ``std::filesystem`` imported target is defined when any requested | |
version of the C++ filesystem library has been found, whether it is | |
*Experimental* or *Final*. | |
If no version of the filesystem library is available, this target will not | |
be defined. | |
.. _fs.variables: | |
Variables | |
********* | |
.. variable:: TCM_FS_HEADER | |
Set to either ``filesystem`` or ``experimental/filesystem`` | |
.. variable:: TCM_FS_NAMESPACE | |
Set to either ``std::filesystem`` or ``std::experimental::filesystem`` | |
Examples | |
******** | |
Using `find_package(StdFilesystem)`: | |
.. code-block:: cmake | |
find_package(StdFilesystem REQUIRED) | |
add_executable(my-program main.cpp) | |
target_link_libraries(my-program PRIVATE std::filesystem) | |
# The macros TCM_FS_HEADER and TCM_FS_NAMESPACE will be injected via | |
# target_compile_defintions() for the appropriate header and namespace, to | |
# be consumed by main.cpp | |
#]=======================================================================] | |
if(TARGET std::filesystem) | |
# This module has already been processed. Don't do it again. | |
return() | |
endif() | |
include(CMakePushCheckState) | |
include(CheckCXXSourceCompiles) | |
cmake_push_check_state() | |
set(CMAKE_REQUIRED_QUIET ${StdFilesystem_FIND_QUIETLY}) | |
set(_found FALSE) | |
foreach(_candidate IN ITEMS filesystem;experimental/filesystem) | |
set(_fs_header ${_candidate}) | |
string(REPLACE "/" "::" _fs_namespace ${_fs_header}) | |
set(_fs_namespace std::${_fs_namespace}) | |
string(CONFIGURE [[ | |
#include <@_fs_header@> | |
int main() { | |
auto cwd = @_fs_namespace@::current_path(); | |
return static_cast<int>(cwd.string().size()); | |
} | |
]] code @ONLY) | |
set(_COMBO "") | |
if("${_fs_header}" MATCHES .*experimental.*) | |
string(APPEND _COMBO _EXPT) | |
endif() | |
# Try to compile a simple filesystem program without any linker flags | |
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK${_COMBO}_NEEDED) | |
set(can_link ${CXX_FILESYSTEM_NO_LINK${_COMBO}_NEEDED}) | |
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) | |
if(NOT can_link) | |
# Try linking the libstdc++ library | |
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) | |
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS${_COMBO}_NEEDED) | |
set(can_link ${CXX_FILESYSTEM_STDCPPFS${_COMBO}_NEEDED}) | |
endif() | |
if(NOT can_link) | |
# Try linking the libc++ library | |
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) | |
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS${_COMBO}_NEEDED) | |
set(can_link ${CXX_FILESYSTEM_CPPFS${_COMBO}_NEEDED}) | |
endif() | |
if(NOT can_link) | |
# Try linking the libc++experimental library | |
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++experimental) | |
check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPEXPERIMENTAL${_COMBO}_NEEDED) | |
set(can_link ${CXX_FILESYSTEM_CPPEXPERIMENTAL${_COMBO}_NEEDED}) | |
endif() | |
# Reset CMAKE_REQUIRED_LIBRARIES | |
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries}) | |
unset(prev_libraries) | |
if(can_link) | |
add_library(std::filesystem INTERFACE IMPORTED) | |
set(_found TRUE) | |
if(CXX_FILESYSTEM_NO_LINK${_COMBO}_NEEDED) | |
# Nothing to add... | |
elseif(CXX_FILESYSTEM_STDCPPFS${_COMBO}_NEEDED) | |
target_link_libraries(std::filesystem INTERFACE -lstdc++fs) | |
elseif(CXX_FILESYSTEM_CPPFS${_COMBO}_NEEDED) | |
target_link_libraries(std::filesystem INTERFACE -lc++fs) | |
elseif(CXX_FILESYSTEM_CPPEXPERIMENTAL${_COMBO}_NEEDED) | |
target_link_libraries(std::filesystem INTERFACE -lc++experimental) | |
endif() | |
string(TOLOWER ${CMAKE_BUILD_TYPE} _find_stdfs_cmake_build_type) | |
if("${_find_stdfs_cmake_build_type}" MATCHES "(debug|buildfast)") | |
if(${CMAKE_CXX_COMPILER_ID} STREQUAL Intel) | |
# Without at least -O1, the Intel compiler errors out with: | |
# c++/${cxxversion}/experimental/fs_path.h:822: | |
# undefined reference to | |
# `std::codecvt_utf8<char, 1114111ul, | |
# (std::codecvt_mode)0>::codecvt_utf8(unsigned long)' | |
target_compile_options(std::filesystem BEFORE | |
INTERFACE -mGLOB_opt_level=1) | |
message(STATUS | |
"Injecting -mGLOB_opt_level=1 for std::filesystem to get" | |
" around ${CMAKE_CXX_COMPILER_ID} compiler bug") | |
endif() | |
endif() | |
unset(_find_stdfs_cmake_build_type) | |
break() | |
endif() | |
endforeach() | |
if(_found) | |
target_compile_definitions(std::filesystem INTERFACE | |
TCM_FS_HEADER=<${_fs_header}> | |
TCM_FS_NAMESPACE=${_fs_namespace}) | |
endif() | |
cmake_pop_check_state() | |
set(StdFilesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE) | |
if(StdFilesystem_FIND_REQUIRED AND NOT StdFilesystem_FOUND) | |
message(FATAL_ERROR "Cannot Compile simple program using std::(experimental::)filesystem") | |
endif() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment