CMakeAddFortranSubdirectory.cmake 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. CMakeAddFortranSubdirectory
  5. ---------------------------
  6. Add a fortran-only subdirectory, find a fortran compiler, and build.
  7. The ``cmake_add_fortran_subdirectory`` function adds a subdirectory
  8. to a project that contains a fortran-only subproject. The module will
  9. check the current compiler and see if it can support fortran. If no
  10. fortran compiler is found and the compiler is MSVC, then this module
  11. will find the MinGW gfortran. It will then use an external project to
  12. build with the MinGW tools. It will also create imported targets for
  13. the libraries created. This will only work if the fortran code is
  14. built into a dll, so :variable:`BUILD_SHARED_LIBS` is turned on in
  15. the project. In addition the :variable:`CMAKE_GNUtoMS` option is set
  16. to on, so that Microsoft ``.lib`` files are created. Usage is as follows:
  17. ::
  18. cmake_add_fortran_subdirectory(
  19. <subdir> # name of subdirectory
  20. PROJECT <project_name> # project name in subdir top CMakeLists.txt
  21. ARCHIVE_DIR <dir> # dir where project places .lib files
  22. RUNTIME_DIR <dir> # dir where project places .dll files
  23. LIBRARIES <lib>... # names of library targets to import
  24. LINK_LIBRARIES # link interface libraries for LIBRARIES
  25. [LINK_LIBS <lib> <dep>...]...
  26. CMAKE_COMMAND_LINE ... # extra command line flags to pass to cmake
  27. NO_EXTERNAL_INSTALL # skip installation of external project
  28. )
  29. Relative paths in ``ARCHIVE_DIR`` and ``RUNTIME_DIR`` are interpreted with
  30. respect to the build directory corresponding to the source directory
  31. in which the function is invoked.
  32. Limitations:
  33. ``NO_EXTERNAL_INSTALL`` is required for forward compatibility with a
  34. future version that supports installation of the external project
  35. binaries during ``make install``.
  36. #]=======================================================================]
  37. set(_MS_MINGW_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
  38. include(CheckLanguage)
  39. include(ExternalProject)
  40. function(_setup_mingw_config_and_build source_dir build_dir)
  41. # Look for a MinGW gfortran.
  42. find_program(MINGW_GFORTRAN
  43. NAMES gfortran
  44. PATHS
  45. c:/MinGW/bin
  46. "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin"
  47. )
  48. if(NOT MINGW_GFORTRAN)
  49. message(FATAL_ERROR
  50. "gfortran not found, please install MinGW with the gfortran option."
  51. "Or set the cache variable MINGW_GFORTRAN to the full path. "
  52. " This is required to build")
  53. endif()
  54. # Validate the MinGW gfortran we found.
  55. if(CMAKE_SIZEOF_VOID_P EQUAL 8)
  56. set(_mingw_target "Target:.*64.*mingw")
  57. else()
  58. set(_mingw_target "Target:.*mingw32")
  59. endif()
  60. execute_process(COMMAND "${MINGW_GFORTRAN}" -v
  61. ERROR_VARIABLE out ERROR_STRIP_TRAILING_WHITESPACE)
  62. if(NOT "${out}" MATCHES "${_mingw_target}")
  63. string(REPLACE "\n" "\n " out " ${out}")
  64. message(FATAL_ERROR
  65. "MINGW_GFORTRAN is set to\n"
  66. " ${MINGW_GFORTRAN}\n"
  67. "which is not a MinGW gfortran for this architecture. "
  68. "The output from -v does not match \"${_mingw_target}\":\n"
  69. "${out}\n"
  70. "Set MINGW_GFORTRAN to a proper MinGW gfortran for this architecture."
  71. )
  72. endif()
  73. # Configure scripts to run MinGW tools with the proper PATH.
  74. get_filename_component(MINGW_PATH ${MINGW_GFORTRAN} PATH)
  75. file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
  76. string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
  77. configure_file(
  78. ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
  79. ${build_dir}/config_mingw.cmake
  80. @ONLY)
  81. configure_file(
  82. ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
  83. ${build_dir}/build_mingw.cmake
  84. @ONLY)
  85. endfunction()
  86. function(_add_fortran_library_link_interface library depend_library)
  87. set_target_properties(${library} PROPERTIES
  88. IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "${depend_library}")
  89. endfunction()
  90. function(cmake_add_fortran_subdirectory subdir)
  91. # Parse arguments to function
  92. set(options NO_EXTERNAL_INSTALL)
  93. set(oneValueArgs PROJECT ARCHIVE_DIR RUNTIME_DIR)
  94. set(multiValueArgs LIBRARIES LINK_LIBRARIES CMAKE_COMMAND_LINE)
  95. cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  96. if(NOT ARGS_NO_EXTERNAL_INSTALL)
  97. message(FATAL_ERROR
  98. "Option NO_EXTERNAL_INSTALL is required (for forward compatibility) "
  99. "but was not given."
  100. )
  101. endif()
  102. # if we are not using MSVC without fortran support
  103. # then just use the usual add_subdirectory to build
  104. # the fortran library
  105. check_language(Fortran)
  106. if(NOT (MSVC AND (NOT CMAKE_Fortran_COMPILER)))
  107. add_subdirectory(${subdir})
  108. return()
  109. endif()
  110. # if we have MSVC without Intel fortran then setup
  111. # external projects to build with mingw fortran
  112. set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}")
  113. set(project_name "${ARGS_PROJECT}")
  114. set(library_dir "${ARGS_ARCHIVE_DIR}")
  115. set(binary_dir "${ARGS_RUNTIME_DIR}")
  116. set(libraries ${ARGS_LIBRARIES})
  117. # use the same directory that add_subdirectory would have used
  118. set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
  119. foreach(dir_var library_dir binary_dir)
  120. if(NOT IS_ABSOLUTE "${${dir_var}}")
  121. get_filename_component(${dir_var}
  122. "${CMAKE_CURRENT_BINARY_DIR}/${${dir_var}}" ABSOLUTE)
  123. endif()
  124. endforeach()
  125. # create build and configure wrapper scripts
  126. _setup_mingw_config_and_build("${source_dir}" "${build_dir}")
  127. # create the external project
  128. externalproject_add(${project_name}_build
  129. SOURCE_DIR ${source_dir}
  130. BINARY_DIR ${build_dir}
  131. CONFIGURE_COMMAND ${CMAKE_COMMAND}
  132. -P ${build_dir}/config_mingw.cmake
  133. BUILD_COMMAND ${CMAKE_COMMAND}
  134. -P ${build_dir}/build_mingw.cmake
  135. INSTALL_COMMAND ""
  136. )
  137. # make the external project always run make with each build
  138. externalproject_add_step(${project_name}_build forcebuild
  139. COMMAND ${CMAKE_COMMAND}
  140. -E remove
  141. ${CMAKE_CURRENT_BUILD_DIR}/${project_name}-prefix/src/${project_name}-stamp/${project_name}-build
  142. DEPENDEES configure
  143. DEPENDERS build
  144. ALWAYS 1
  145. )
  146. # create imported targets for all libraries
  147. foreach(lib ${libraries})
  148. add_library(${lib} SHARED IMPORTED GLOBAL)
  149. set_property(TARGET ${lib} APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
  150. set_target_properties(${lib} PROPERTIES
  151. IMPORTED_IMPLIB_NOCONFIG "${library_dir}/lib${lib}.lib"
  152. IMPORTED_LOCATION_NOCONFIG "${binary_dir}/lib${lib}.dll"
  153. )
  154. add_dependencies(${lib} ${project_name}_build)
  155. endforeach()
  156. # now setup link libraries for targets
  157. set(start FALSE)
  158. set(target)
  159. foreach(lib ${ARGS_LINK_LIBRARIES})
  160. if("${lib}" STREQUAL "LINK_LIBS")
  161. set(start TRUE)
  162. else()
  163. if(start)
  164. if(DEFINED target)
  165. # process current target and target_libs
  166. _add_fortran_library_link_interface(${target} "${target_libs}")
  167. # zero out target and target_libs
  168. set(target)
  169. set(target_libs)
  170. endif()
  171. # save the current target and set start to FALSE
  172. set(target ${lib})
  173. set(start FALSE)
  174. else()
  175. # append the lib to target_libs
  176. list(APPEND target_libs "${lib}")
  177. endif()
  178. endif()
  179. endforeach()
  180. # process anything that is left in target and target_libs
  181. if(DEFINED target)
  182. _add_fortran_library_link_interface(${target} "${target_libs}")
  183. endif()
  184. endfunction()