1 # Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 # file Copyright.txt or https://cmake.org/licensing for details.
4 #[=======================================================================[.rst:
5 CMakeAddFortranSubdirectory
6 ---------------------------
8 Add a fortran-only subdirectory, find a fortran compiler, and build.
10 The ``cmake_add_fortran_subdirectory`` function adds a subdirectory
11 to a project that contains a fortran-only subproject. The module will
12 check the current compiler and see if it can support fortran. If no
13 fortran compiler is found and the compiler is MSVC, then this module
14 will find the MinGW gfortran. It will then use an external project to
15 build with the MinGW tools. It will also create imported targets for
16 the libraries created. This will only work if the fortran code is
17 built into a dll, so :variable:`BUILD_SHARED_LIBS` is turned on in
18 the project. In addition the :variable:`CMAKE_GNUtoMS` option is set
19 to on, so that Microsoft ``.lib`` files are created. Usage is as follows:
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
35 Relative paths in ``ARCHIVE_DIR`` and ``RUNTIME_DIR`` are interpreted with
36 respect to the build directory corresponding to the source directory
37 in which the function is invoked.
41 ``NO_EXTERNAL_INSTALL`` is required for forward compatibility with a
42 future version that supports installation of the external project
43 binaries during ``make install``.
44 #]=======================================================================]
46 include(CheckLanguage)
47 include(ExternalProject)
49 function(_setup_mingw_config_and_build source_dir build_dir)
50 # Look for a MinGW gfortran.
51 find_program(MINGW_GFORTRAN
55 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin"
57 if(NOT MINGW_GFORTRAN)
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")
64 # Validate the MinGW gfortran we found.
65 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
66 set(_mingw_target "Target:.*64.*mingw")
68 set(_mingw_target "Target:.*mingw32")
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}")
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"
80 "Set MINGW_GFORTRAN to a proper MinGW gfortran for this architecture."
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}")
89 ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
90 ${build_dir}/config_mingw.cmake
93 ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
94 ${build_dir}/build_mingw.cmake
98 function(_add_fortran_library_link_interface library depend_library)
99 set_target_properties(${library} PROPERTIES
100 IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "${depend_library}")
104 function(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)
112 "Option NO_EXTERNAL_INSTALL is required (for forward compatibility) "
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})
126 # if we have MSVC without Intel fortran then setup
127 # external projects to build with mingw fortran
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)
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
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"
163 add_dependencies(${lib} ${project_name}_build)
166 # now setup link libraries for targets
169 foreach(lib ${ARGS_LINK_LIBRARIES})
170 if("${lib}" STREQUAL "LINK_LIBS")
175 # process current target and target_libs
176 _add_fortran_library_link_interface(${target} "${target_libs}")
177 # zero out target and target_libs
181 # save the current target and set start to FALSE
185 # append the lib to target_libs
186 list(APPEND target_libs "${lib}")
190 # process anything that is left in target and target_libs
192 _add_fortran_library_link_interface(${target} "${target_libs}")