CMakeAddFortranSubdirectory.cmake 7.7 KB

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