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