Add CMakeAddFortranSubdirectory to use MinGW gfortran in VS
This patch adds a new module that allows for easy integration of MinGW gfortran and the Visual Studio compiler. It is done in a function called cmake_add_fortran_subdirectory. The patch also includes a test for this feature.
This commit is contained in:
parent
3c6af5ff33
commit
538c3452ad
|
@ -0,0 +1,167 @@
|
||||||
|
# - Use MinGW gfortran from VS if a fortran compiler is not found.
|
||||||
|
# The 'add_fortran_subdirectory' function adds a subdirectory
|
||||||
|
# to a project that contains a fortran only sub-project. The module
|
||||||
|
# will check the current compiler and see if it can support fortran.
|
||||||
|
# If no fortran compiler is found and the compiler is MSVC, then
|
||||||
|
# this module will find the MinGW gfortran. It will then use
|
||||||
|
# an external project to build with the MinGW tools. It will also
|
||||||
|
# create imported targets for the libraries created. This will only
|
||||||
|
# work if the fortran code is built into a dll, so BUILD_SHARED_LIBS
|
||||||
|
# is turned on in the project. In addition the GNUtoMS option is set
|
||||||
|
# to on, so that the MS .lib files are created.
|
||||||
|
# Usage is as follows:
|
||||||
|
# cmake_add_fortran_subdirectory(
|
||||||
|
# <subdir> # name of subdirectory
|
||||||
|
# PROJECT <project_name> # project name in sbudir toplevel CMakeLists.txt
|
||||||
|
# ARCHIVE_DIR <dir> # .lib location relative to root binary tree (lib)
|
||||||
|
# RUNTIME_DIR <dir> # .dll location relative to root binary tree (bin)
|
||||||
|
# LIBRARIES lib2 lib2 # names of libraries created and exported
|
||||||
|
# LINK_LIBRARIES # link interface libraries for LIBRARIES
|
||||||
|
# LINK_LIBS <lib1> <dep1> <dep2> ... <depN>
|
||||||
|
# LINK_LIBS <lib2> <dep1> <dep2> ... <depN>
|
||||||
|
# CMAKE_COMMAND_LINE # extra command line flags to pass to cmake
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2002-2009 Kitware, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
# see accompanying file Copyright.txt for details.
|
||||||
|
#
|
||||||
|
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the License for more information.
|
||||||
|
#=============================================================================
|
||||||
|
# (To distribute this file outside of CMake, substitute the full
|
||||||
|
# License text for the above reference.)
|
||||||
|
|
||||||
|
|
||||||
|
set(_MS_MINGW_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
include(CheckLanguage)
|
||||||
|
include(ExternalProject)
|
||||||
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
|
function(_setup_mingw_config_and_build source_dir)
|
||||||
|
find_program(MINGW_GFORTRAN NAMES gfortran
|
||||||
|
HINTS
|
||||||
|
c:/MinGW/bin
|
||||||
|
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin" )
|
||||||
|
if(NOT MINGW_GFORTRAN)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"gfortran not found, please install MinGW with the gfortran option."
|
||||||
|
"Or set the cache variable MINGW_GFORTRAN to the full path. "
|
||||||
|
" This is required to build")
|
||||||
|
endif()
|
||||||
|
execute_process(COMMAND ${MINGW_GFORTRAN} -v ERROR_VARIABLE out)
|
||||||
|
if(NOT "${out}" MATCHES "Target:.*mingw32")
|
||||||
|
message(FATAL_ERROR "Non-MinGW gfortran found: ${MINGW_GFORTRAN}\n"
|
||||||
|
"output from -v [${out}]\n"
|
||||||
|
"set MINGW_GFORTRAN to the path to MinGW fortran.")
|
||||||
|
endif()
|
||||||
|
get_filename_component(MINGW_PATH ${MINGW_GFORTRAN} PATH)
|
||||||
|
file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
|
||||||
|
string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
|
||||||
|
configure_file(
|
||||||
|
${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/config_mingw.cmake
|
||||||
|
@ONLY)
|
||||||
|
configure_file(
|
||||||
|
${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/build_mingw.cmake
|
||||||
|
@ONLY)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_add_fortran_library_link_interface library depend_library)
|
||||||
|
set_target_properties(${library} PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG "${depend_library}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
function(cmake_add_fortran_subdirectory subdir)
|
||||||
|
# if we are not using MSVC without fortran support
|
||||||
|
# then just use the usual add_subdirectory to build
|
||||||
|
# the fortran library
|
||||||
|
check_language(Fortran)
|
||||||
|
if(NOT (MSVC AND (NOT CMAKE_Fortran_COMPILER)))
|
||||||
|
add_subdirectory(${subdir})
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# if we have MSVC without Intel fortran then setup
|
||||||
|
# external projects to build with mingw fortran
|
||||||
|
|
||||||
|
# Parse arguments to function
|
||||||
|
set(oneValueArgs PROJECT ARCHIVE_DIR RUNTIME_DIR)
|
||||||
|
set(multiValueArgs LIBRARIES LINK_LIBRARIES CMAKE_COMMAND_LINE)
|
||||||
|
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}")
|
||||||
|
set(project_name "${ARGS_PROJECT}")
|
||||||
|
set(library_dir "${ARGS_ARCHIVE_DIR}")
|
||||||
|
set(binary_dir "${ARGS_RUNTIME_DIR}")
|
||||||
|
set(libraries ${ARGS_LIBRARIES})
|
||||||
|
# use the same directory that add_subdirectory would have used
|
||||||
|
set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
|
||||||
|
# create build and configure wrapper scripts
|
||||||
|
_setup_mingw_config_and_build(${source_dir})
|
||||||
|
# create the external project
|
||||||
|
externalproject_add(${project_name}_build
|
||||||
|
SOURCE_DIR ${source_dir}
|
||||||
|
BINARY_DIR ${build_dir}
|
||||||
|
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||||
|
-P ${CMAKE_CURRENT_BINARY_DIR}/config_mingw.cmake
|
||||||
|
BUILD_COMMAND ${CMAKE_COMMAND}
|
||||||
|
-P ${CMAKE_CURRENT_BINARY_DIR}/build_mingw.cmake
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
)
|
||||||
|
# make the external project always run make with each build
|
||||||
|
externalproject_add_step(${project_name}_build forcebuild
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-E remove
|
||||||
|
${CMAKE_CURRENT_BUILD_DIR}/${project_name}-prefix/src/${project_name}-stamp/${project_name}-build
|
||||||
|
DEPENDEES configure
|
||||||
|
DEPENDERS build
|
||||||
|
ALWAYS 1
|
||||||
|
)
|
||||||
|
# create imported targets for all libraries
|
||||||
|
foreach(lib ${libraries})
|
||||||
|
add_library(${lib} SHARED IMPORTED)
|
||||||
|
set_property(TARGET ${lib} APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
|
||||||
|
set_target_properties(${lib} PROPERTIES
|
||||||
|
IMPORTED_IMPLIB_NOCONFIG
|
||||||
|
"${build_dir}/${library_dir}/lib${lib}.lib"
|
||||||
|
IMPORTED_LOCATION_NOCONFIG
|
||||||
|
"${build_dir}/${binary_dir}/lib${lib}.dll"
|
||||||
|
)
|
||||||
|
add_dependencies(${lib} ${project_name}_build)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# now setup link libraries for targets
|
||||||
|
set(start FALSE)
|
||||||
|
set(target)
|
||||||
|
foreach(lib ${ARGS_LINK_LIBRARIES})
|
||||||
|
if("${lib}" STREQUAL "LINK_LIBS")
|
||||||
|
set(start TRUE)
|
||||||
|
else()
|
||||||
|
if(start)
|
||||||
|
if(DEFINED target)
|
||||||
|
# process current target and target_libs
|
||||||
|
_add_fortran_library_link_interface(${target} "${target_libs}")
|
||||||
|
# zero out target and target_libs
|
||||||
|
set(target)
|
||||||
|
set(target_libs)
|
||||||
|
endif()
|
||||||
|
# save the current target and set start to FALSE
|
||||||
|
set(target ${lib})
|
||||||
|
set(start FALSE)
|
||||||
|
else()
|
||||||
|
# append the lib to target_libs
|
||||||
|
list(APPEND target_libs "${lib}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
# process anything that is left in target and target_libs
|
||||||
|
if(DEFINED target)
|
||||||
|
_add_fortran_library_link_interface(${target} "${target_libs}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
|
@ -0,0 +1,2 @@
|
||||||
|
set(ENV{PATH} "@MINGW_PATH@\;$ENV{PATH}")
|
||||||
|
execute_process(COMMAND "@CMAKE_COMMAND@" --build . )
|
|
@ -0,0 +1,8 @@
|
||||||
|
set(ENV{PATH} "@MINGW_PATH@\;$ENV{PATH}")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "@CMAKE_COMMAND@" "-GMinGW Makefiles"
|
||||||
|
-DCMAKE_Fortran_COMPILER:PATH=@MINGW_GFORTRAN@
|
||||||
|
-DBUILD_SHARED_LIBS=ON
|
||||||
|
-DCMAKE_GNUtoMS=ON
|
||||||
|
@ARGS_CMAKE_COMMAND_LINE@
|
||||||
|
"@source_dir@")
|
|
@ -163,6 +163,15 @@ IF(BUILD_TESTING)
|
||||||
IF(CMAKE_Fortran_COMPILER)
|
IF(CMAKE_Fortran_COMPILER)
|
||||||
ADD_TEST_MACRO(FortranOnly FortranOnly)
|
ADD_TEST_MACRO(FortranOnly FortranOnly)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
# test Visual Studio GNU Fortran mixing with cmake_add_fortran_subdirectory
|
||||||
|
# run this project if we have a working fortran compiler or
|
||||||
|
# the test is enabled with CMAKE_TEST_CMAKE_ADD_FORTRAN cache variable.
|
||||||
|
# If you enable the test, CMake should find the MinGW fortran install,
|
||||||
|
# or in some cases you might need to set the PATH so that cmake can find
|
||||||
|
# the gfortran from mingw.
|
||||||
|
IF(CMAKE_Fortran_COMPILER OR CMAKE_TEST_CMAKE_ADD_FORTRAN)
|
||||||
|
ADD_TEST_MACRO(VSGNUFortran ${CMAKE_COMMAND} -P runtest.cmake)
|
||||||
|
ENDIF()
|
||||||
ADD_TEST_MACRO(COnly COnly)
|
ADD_TEST_MACRO(COnly COnly)
|
||||||
ADD_TEST_MACRO(CxxOnly CxxOnly)
|
ADD_TEST_MACRO(CxxOnly CxxOnly)
|
||||||
ADD_TEST_MACRO(IPO COnly/COnly)
|
ADD_TEST_MACRO(IPO COnly/COnly)
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
project(VSGNUFortran)
|
||||||
|
# force the executable to be put out of Debug/Release dir
|
||||||
|
# because gmake build of fortran will not be in a config
|
||||||
|
# directory, and for easier testing we want the exe and .dll
|
||||||
|
# to be in the same directory.
|
||||||
|
if(CMAKE_CONFIGURATION_TYPES)
|
||||||
|
foreach(config ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
string(TOUPPER "${config}" config)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
|
||||||
|
"${PROJECT_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
|
||||||
|
"${PROJECT_BINARY_DIR}/bin")
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CMakeAddFortranSubdirectory)
|
||||||
|
# add the fortran subdirectory as a fortran project
|
||||||
|
# the subdir is fortran, the project is FortranHello
|
||||||
|
cmake_add_fortran_subdirectory(fortran
|
||||||
|
PROJECT FortranHello # project name in toplevel CMakeLists.txt
|
||||||
|
ARCHIVE_DIR ../bin # .lib location relative to root binary tree
|
||||||
|
RUNTIME_DIR ../bin # .dll location relative to root binary tree
|
||||||
|
LIBRARIES hello world # target libraries created
|
||||||
|
CMAKE_COMMAND_LINE -DEXECUTABLE_OUTPUT_PATH=../bin
|
||||||
|
-DLIBRARY_OUTPUT_PATH=../bin
|
||||||
|
LINK_LIBRARIES # link interface libraries
|
||||||
|
LINK_LIBS hello world # hello needs world to link
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${VSGNUFortran_BINARY_DIR}/fortran)
|
||||||
|
add_subdirectory(c_code)
|
||||||
|
# use a cmake script to run the executable so that PATH
|
||||||
|
# can be set with the MinGW/bin in it, and the fortran
|
||||||
|
# runtime libraries can be found.
|
||||||
|
configure_file(runtest.cmake.in runtest.cmake @ONLY)
|
|
@ -0,0 +1,2 @@
|
||||||
|
add_executable(c_using_fortran main.c)
|
||||||
|
target_link_libraries(c_using_fortran hello)
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <HelloWorldFCMangle.h> // created by FortranCInterface
|
||||||
|
extern void FC_hello(void);
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
FC_hello();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
project(FortranHello Fortran C)
|
||||||
|
|
||||||
|
include(FortranCInterface)
|
||||||
|
FortranCInterface_HEADER(HelloWorldFCMangle.h
|
||||||
|
MACRO_NAMESPACE "FC_"
|
||||||
|
SYMBOL_NAMESPACE "FC_"
|
||||||
|
SYMBOLS hello world)
|
||||||
|
|
||||||
|
add_library(hello SHARED hello.f)
|
||||||
|
add_library(world SHARED world.f)
|
||||||
|
target_link_libraries(hello world)
|
|
@ -0,0 +1,7 @@
|
||||||
|
!DEC$ ATTRIBUTES DLLEXPORT :: HELLO
|
||||||
|
SUBROUTINE HELLO
|
||||||
|
|
||||||
|
PRINT *, 'Hello'
|
||||||
|
CALL WORLD
|
||||||
|
|
||||||
|
END
|
|
@ -0,0 +1,6 @@
|
||||||
|
!DEC$ ATTRIBUTES DLLEXPORT :: WORLD
|
||||||
|
SUBROUTINE WORLD
|
||||||
|
|
||||||
|
PRINT *, 'World!'
|
||||||
|
|
||||||
|
END
|
|
@ -0,0 +1,23 @@
|
||||||
|
get_filename_component(MINGW_PATH "@MINGW_GFORTRAN@" PATH)
|
||||||
|
if(NOT EXISTS "${MINGW_PATH}")
|
||||||
|
set(test_exe
|
||||||
|
"@VSGNUFortran_BINARY_DIR@/bin/c_using_fortran@CMAKE_EXECUTABLE_SUFFIX@")
|
||||||
|
message("run: ${test_exe}")
|
||||||
|
execute_process(COMMAND "${test_exe}"
|
||||||
|
RESULT_VARIABLE res)
|
||||||
|
if(NOT "${res}" EQUAL 0)
|
||||||
|
message(FATAL_ERROR "${test_exe} returned a non 0 value")
|
||||||
|
endif()
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
|
||||||
|
string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
|
||||||
|
message("${MINGW_PATH}")
|
||||||
|
set(test_exe "@VSGNUFortran_BINARY_DIR@/bin/c_using_fortran.exe")
|
||||||
|
set(ENV{PATH} "${MINGW_PATH}";$ENV{PATH})
|
||||||
|
message("run ${test_exe}")
|
||||||
|
execute_process(COMMAND "${test_exe}"
|
||||||
|
RESULT_VARIABLE res)
|
||||||
|
if(NOT "${res}" EQUAL 0)
|
||||||
|
message(FATAL_ERROR "${test_exe} returned a non 0 value")
|
||||||
|
endif()
|
Loading…
Reference in New Issue