Merge topic 'cpack-innosetup-linux'
[kiteware-cmake.git] / Modules / CMakeAddFortranSubdirectory.cmake
blob69a8417b1ad175b1aa5e886804af772255b5ee94
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
33    )
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.
39 Limitations:
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
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()
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()
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)
96 endfunction()
98 function(_add_fortran_library_link_interface library depend_library)
99   set_target_properties(${library} PROPERTIES
100     IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "${depend_library}")
101 endfunction()
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)
111     message(FATAL_ERROR
112       "Option NO_EXTERNAL_INSTALL is required (for forward compatibility) "
113       "but was not given."
114       )
115   endif()
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()
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)
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()
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()
194 endfunction()