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