FindFilesystem.cmake 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. # Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. # file Copyright.txt or https://cmake.org/licensing for details.
  3. #[=======================================================================[.rst:
  4. FindFilesystem
  5. ##############
  6. This module supports the C++17 standard library's filesystem utilities. Use the
  7. :imp-target:`std::filesystem` imported target to
  8. Options
  9. *******
  10. The ``COMPONENTS`` argument to this module supports the following values:
  11. .. find-component:: Experimental
  12. :name: fs.Experimental
  13. Allows the module to find the "experimental" Filesystem TS version of the
  14. Filesystem library. This is the library that should be used with the
  15. ``std::experimental::filesystem`` namespace.
  16. .. find-component:: Final
  17. :name: fs.Final
  18. Finds the final C++17 standard version of the filesystem library.
  19. If no components are provided, behaves as if the
  20. :find-component:`fs.Final` component was specified.
  21. If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are
  22. provided, first looks for ``Final``, and falls back to ``Experimental`` in case
  23. of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all
  24. :ref:`variables <fs.variables>` will refer to the ``Final`` version.
  25. Imported Targets
  26. ****************
  27. .. imp-target:: std::filesystem
  28. The ``std::filesystem`` imported target is defined when any requested
  29. version of the C++ filesystem library has been found, whether it is
  30. *Experimental* or *Final*.
  31. If no version of the filesystem library is available, this target will not
  32. be defined.
  33. .. note::
  34. This target has ``cxx_std_17`` as an ``INTERFACE``
  35. :ref:`compile language standard feature <req-lang-standards>`. Linking
  36. to this target will automatically enable C++17 if no later standard
  37. version is already required on the linking target.
  38. .. _fs.variables:
  39. Variables
  40. *********
  41. .. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL
  42. Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++
  43. filesystem library was found, otherwise ``FALSE``.
  44. .. variable:: CXX_FILESYSTEM_HAVE_FS
  45. Set to ``TRUE`` when a filesystem header was found.
  46. .. variable:: CXX_FILESYSTEM_HEADER
  47. Set to either ``filesystem`` or ``experimental/filesystem`` depending on
  48. whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was
  49. found.
  50. .. variable:: CXX_FILESYSTEM_NAMESPACE
  51. Set to either ``std::filesystem`` or ``std::experimental::filesystem``
  52. depending on whether :find-component:`fs.Final` or
  53. :find-component:`fs.Experimental` was found.
  54. Examples
  55. ********
  56. Using `find_package(Filesystem)` with no component arguments:
  57. .. code-block:: cmake
  58. find_package(Filesystem REQUIRED)
  59. add_executable(my-program main.cpp)
  60. target_link_libraries(my-program PRIVATE std::filesystem)
  61. #]=======================================================================]
  62. if(TARGET std::filesystem)
  63. # This module has already been processed. Don't do it again.
  64. return()
  65. endif()
  66. # Ignore fileystem check if version too low
  67. if(CMAKE_VERSION VERSION_LESS 3.10)
  68. set(CXX_FILESYSTEM_HAVE_FS FALSE CACHE BOOL "TRUE if we have the C++ filesystem headers")
  69. set(Filesystem_FOUND FALSE CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
  70. return()
  71. endif()
  72. cmake_minimum_required(VERSION 3.10)
  73. include(CMakePushCheckState)
  74. include(CheckIncludeFileCXX)
  75. # If we're not cross-compiling, try to run test executables.
  76. # Otherwise, assume that compile + link is a sufficient check.
  77. if(CMAKE_CROSSCOMPILING)
  78. include(CheckCXXSourceCompiles)
  79. macro(_cmcm_check_cxx_source code var)
  80. check_cxx_source_compiles("${code}" ${var})
  81. endmacro()
  82. else()
  83. include(CheckCXXSourceRuns)
  84. macro(_cmcm_check_cxx_source code var)
  85. check_cxx_source_runs("${code}" ${var})
  86. endmacro()
  87. endif()
  88. cmake_push_check_state()
  89. set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
  90. # All of our tests required C++17 or later
  91. set(BACKUP_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
  92. set(CMAKE_CXX_STANDARD 17)
  93. # Normalize and check the component list we were given
  94. set(want_components ${Filesystem_FIND_COMPONENTS})
  95. if(Filesystem_FIND_COMPONENTS STREQUAL "")
  96. set(want_components Final)
  97. endif()
  98. # Warn on any unrecognized components
  99. set(extra_components ${want_components})
  100. list(REMOVE_ITEM extra_components Final Experimental)
  101. foreach(component IN LISTS extra_components)
  102. message(WARNING "Extraneous find_package component for Filesystem: ${component}")
  103. endforeach()
  104. # Detect which of Experimental and Final we should look for
  105. set(find_experimental TRUE)
  106. set(find_final TRUE)
  107. if(NOT "Final" IN_LIST want_components)
  108. set(find_final FALSE)
  109. endif()
  110. if(NOT "Experimental" IN_LIST want_components)
  111. set(find_experimental FALSE)
  112. endif()
  113. if(find_final)
  114. check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
  115. mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
  116. if(_CXX_FILESYSTEM_HAVE_HEADER)
  117. # We found the non-experimental header. Don't bother looking for the
  118. # experimental one.
  119. set(find_experimental FALSE)
  120. endif()
  121. else()
  122. set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
  123. endif()
  124. if(find_experimental)
  125. check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
  126. mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
  127. else()
  128. set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
  129. endif()
  130. if(_CXX_FILESYSTEM_HAVE_HEADER)
  131. set(_have_fs TRUE)
  132. set(_fs_header filesystem)
  133. set(_fs_namespace std::filesystem)
  134. set(_is_experimental FALSE)
  135. elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
  136. set(_have_fs TRUE)
  137. set(_fs_header experimental/filesystem)
  138. set(_fs_namespace std::experimental::filesystem)
  139. set(_is_experimental TRUE)
  140. else()
  141. set(_have_fs FALSE)
  142. endif()
  143. set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
  144. set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
  145. set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")
  146. set(CXX_FILESYSTEM_IS_EXPERIMENTAL ${_is_experimental} CACHE BOOL "TRUE if the C++ filesystem library is the experimental version")
  147. set(_found FALSE)
  148. if(CXX_FILESYSTEM_HAVE_FS)
  149. # We have some filesystem library available. Do link checks
  150. string(CONFIGURE [[
  151. #include <cstdio>
  152. #include <@CXX_FILESYSTEM_HEADER@>
  153. int main() {
  154. auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
  155. printf("%s", cwd.generic_string().c_str());
  156. return EXIT_SUCCESS;
  157. }
  158. ]] code @ONLY)
  159. # HACK: Needed to compile correctly on Yocto Linux
  160. if(CMAKE_CXX_COMPILER_ID STREQUAL "GCC" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
  161. OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
  162. set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
  163. endif ()
  164. # Check a simple filesystem program without any linker flags
  165. _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
  166. set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
  167. if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)
  168. set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
  169. # Add the libstdc++ flag
  170. set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
  171. _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
  172. set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
  173. if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
  174. # Try the libc++ flag
  175. set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
  176. _cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
  177. set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
  178. endif()
  179. endif()
  180. if(can_link)
  181. add_library(std::filesystem INTERFACE IMPORTED)
  182. set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)
  183. set(_found TRUE)
  184. if(CXX_FILESYSTEM_NO_LINK_NEEDED)
  185. # Nothing to add...
  186. elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
  187. set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lstdc++fs)
  188. elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
  189. set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lc++fs)
  190. endif()
  191. endif()
  192. endif()
  193. cmake_pop_check_state()
  194. set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
  195. if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
  196. message(FATAL_ERROR "Cannot run simple program using std::filesystem")
  197. endif()
  198. set(CMAKE_CXX_STANDARD "${BACKUP_CXX_STANDARD}")