CMakeAddFortranSubdirectory.cmake 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. function(_setup_mingw_config_and_build source_dir build_dir)
  47. # Look for a MinGW gfortran.
  48. find_program(MINGW_GFORTRAN
  49. NAMES gfortran
  50. PATHS
  51. c:/MinGW/bin
  52. "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin"
  53. )
  54. if(NOT MINGW_GFORTRAN)
  55. message(FATAL_ERROR
  56. "gfortran not found, please install MinGW with the gfortran option."
  57. "Or set the cache variable MINGW_GFORTRAN to the full path. "
  58. " This is required to build")
  59. endif()
  60. # Validate the MinGW gfortran we found.
  61. if(CMAKE_SIZEOF_VOID_P EQUAL 8)
  62. set(_mingw_target "Target:.*64.*mingw")
  63. else()
  64. set(_mingw_target "Target:.*mingw32")
  65. endif()
  66. execute_process(COMMAND "${MINGW_GFORTRAN}" -v
  67. ERROR_VARIABLE out ERROR_STRIP_TRAILING_WHITESPACE)
  68. if(NOT "${out}" MATCHES "${_mingw_target}")
  69. string(REPLACE "\n" "\n " out " ${out}")
  70. message(FATAL_ERROR
  71. "MINGW_GFORTRAN is set to\n"
  72. " ${MINGW_GFORTRAN}\n"
  73. "which is not a MinGW gfortran for this architecture. "
  74. "The output from -v does not match \"${_mingw_target}\":\n"
  75. "${out}\n"
  76. "Set MINGW_GFORTRAN to a proper MinGW gfortran for this architecture."
  77. )
  78. endif()
  79. # Configure scripts to run MinGW tools with the proper PATH.
  80. get_filename_component(MINGW_PATH ${MINGW_GFORTRAN} PATH)
  81. file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
  82. string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
  83. configure_file(
  84. ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
  85. ${build_dir}/config_mingw.cmake
  86. @ONLY)
  87. configure_file(
  88. ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
  89. ${build_dir}/build_mingw.cmake
  90. @ONLY)
  91. endfunction()
  92. function(_add_fortran_library_link_interface library depend_library)
  93. set_target_properties(${library} PROPERTIES
  94. IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "${depend_library}")
  95. endfunction()
  96. function(cmake_add_fortran_subdirectory subdir)
  97. # Parse arguments to function
  98. set(options NO_EXTERNAL_INSTALL)
  99. set(oneValueArgs PROJECT ARCHIVE_DIR RUNTIME_DIR)
  100. set(multiValueArgs LIBRARIES LINK_LIBRARIES CMAKE_COMMAND_LINE)
  101. cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  102. if(NOT ARGS_NO_EXTERNAL_INSTALL)
  103. message(FATAL_ERROR
  104. "Option NO_EXTERNAL_INSTALL is required (for forward compatibility) "
  105. "but was not given."
  106. )
  107. endif()
  108. # if we are not using MSVC without fortran support
  109. # then just use the usual add_subdirectory to build
  110. # the fortran library
  111. check_language(Fortran)
  112. if(NOT (MSVC AND (NOT CMAKE_Fortran_COMPILER)))
  113. add_subdirectory(${subdir})
  114. return()
  115. endif()
  116. # if we have MSVC without Intel fortran then setup
  117. # external projects to build with mingw fortran
  118. set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}")
  119. set(project_name "${ARGS_PROJECT}")
  120. set(library_dir "${ARGS_ARCHIVE_DIR}")
  121. set(binary_dir "${ARGS_RUNTIME_DIR}")
  122. set(libraries ${ARGS_LIBRARIES})
  123. # use the same directory that add_subdirectory would have used
  124. set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
  125. foreach(dir_var library_dir binary_dir)
  126. if(NOT IS_ABSOLUTE "${${dir_var}}")
  127. get_filename_component(${dir_var}
  128. "${CMAKE_CURRENT_BINARY_DIR}/${${dir_var}}" ABSOLUTE)
  129. endif()
  130. endforeach()
  131. # create build and configure wrapper scripts
  132. _setup_mingw_config_and_build("${source_dir}" "${build_dir}")
  133. # create the external project
  134. externalproject_add(${project_name}_build
  135. SOURCE_DIR ${source_dir}
  136. BINARY_DIR ${build_dir}
  137. CONFIGURE_COMMAND ${CMAKE_COMMAND}
  138. -P ${build_dir}/config_mingw.cmake
  139. BUILD_COMMAND ${CMAKE_COMMAND}
  140. -P ${build_dir}/build_mingw.cmake
  141. INSTALL_COMMAND ""
  142. )
  143. # make the external project always run make with each build
  144. externalproject_add_step(${project_name}_build forcebuild
  145. COMMAND ${CMAKE_COMMAND}
  146. -E remove
  147. ${CMAKE_CURRENT_BUILD_DIR}/${project_name}-prefix/src/${project_name}-stamp/${project_name}-build
  148. DEPENDEES configure
  149. DEPENDERS build
  150. ALWAYS 1
  151. )
  152. # create imported targets for all libraries
  153. foreach(lib ${libraries})
  154. add_library(${lib} SHARED IMPORTED GLOBAL)
  155. set_property(TARGET ${lib} APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
  156. set_target_properties(${lib} PROPERTIES
  157. IMPORTED_IMPLIB_NOCONFIG "${library_dir}/lib${lib}.lib"
  158. IMPORTED_LOCATION_NOCONFIG "${binary_dir}/lib${lib}.dll"
  159. )
  160. add_dependencies(${lib} ${project_name}_build)
  161. endforeach()
  162. # now setup link libraries for targets
  163. set(start FALSE)
  164. set(target)
  165. foreach(lib ${ARGS_LINK_LIBRARIES})
  166. if("${lib}" STREQUAL "LINK_LIBS")
  167. set(start TRUE)
  168. else()
  169. if(start)
  170. if(DEFINED target)
  171. # process current target and target_libs
  172. _add_fortran_library_link_interface(${target} "${target_libs}")
  173. # zero out target and target_libs
  174. set(target)
  175. set(target_libs)
  176. endif()
  177. # save the current target and set start to FALSE
  178. set(target ${lib})
  179. set(start FALSE)
  180. else()
  181. # append the lib to target_libs
  182. list(APPEND target_libs "${lib}")
  183. endif()
  184. endif()
  185. endforeach()
  186. # process anything that is left in target and target_libs
  187. if(DEFINED target)
  188. _add_fortran_library_link_interface(${target} "${target_libs}")
  189. endif()
  190. endfunction()