Merge topic 'cmake_add_fortran_subdirectory'

1e16406 CMakeAddFortranSubdirectory: Add NO_EXTERNAL_INSTALL option
6f6891b CMakeAddFortranSubdirectory: Always parse arguments
48a09f8 CMakeAddFortranSubdirectory: Make IMPORTED targets GLOBAL
067c1f4 VSGNUFortran: Disable test in special cases
bd69e1c VSGNUFortran: Add special case for SunPro Fortran runtime library
414a780 CMakeAddFortranSubdirectory: Validate gfortran architecture
7e0d9f1 CMakeAddFortranSubdirectory: Find gfortran in PATH
d6b0312 CMakeAddFortranSubdirectory: Fix documentation format and typos
e4ae038 CMakeAddFortranSubdirectory: Allow full paths to directories
538c345 Add CMakeAddFortranSubdirectory to use MinGW gfortran in VS
3c6af5f Merge branch 'add-CheckLanguage-module' into CMakeAddFortranSubdirectory
This commit is contained in:
David Cole 2012-02-14 16:16:15 -05:00 committed by CMake Topic Stage
commit c69a20573e
12 changed files with 376 additions and 0 deletions

View File

@ -0,0 +1,206 @@
# - 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 CMAKE_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 subdir top CMakeLists.txt
# ARCHIVE_DIR <dir> # dir where project places .lib files
# RUNTIME_DIR <dir> # dir where project places .dll files
# LIBRARIES <lib>... # names of library targets to import
# LINK_LIBRARIES # link interface libraries for LIBRARIES
# [LINK_LIBS <lib> <dep>...]...
# CMAKE_COMMAND_LINE ... # extra command line flags to pass to cmake
# NO_EXTERNAL_INSTALL # skip installation of external project
# )
# Relative paths in ARCHIVE_DIR and RUNTIME_DIR are interpreted with respect
# to the build directory corresponding to the source directory in which the
# function is invoked.
#
# Limitations:
#
# NO_EXTERNAL_INSTALL is required for forward compatibility with a
# future version that supports installation of the external project
# binaries during "make install".
#=============================================================================
# Copyright 2011-2012 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)
# Look for a MinGW gfortran.
find_program(MINGW_GFORTRAN
NAMES gfortran
PATHS
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()
# Validate the MinGW gfortran we found.
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_mingw_target "Target:.*64.*mingw")
else()
set(_mingw_target "Target:.*mingw32")
endif()
execute_process(COMMAND "${MINGW_GFORTRAN}" -v
ERROR_VARIABLE out ERROR_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" MATCHES "${_mingw_target}")
string(REPLACE "\n" "\n " out " ${out}")
message(FATAL_ERROR
"MINGW_GFORTRAN is set to\n"
" ${MINGW_GFORTRAN}\n"
"which is not a MinGW gfortran for this architecture. "
"The output from -v does not match \"${_mingw_target}\":\n"
"${out}\n"
"Set MINGW_GFORTRAN to a proper MinGW gfortran for this architecture."
)
endif()
# Configure scripts to run MinGW tools with the proper PATH.
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)
# Parse arguments to function
set(options NO_EXTERNAL_INSTALL)
set(oneValueArgs PROJECT ARCHIVE_DIR RUNTIME_DIR)
set(multiValueArgs LIBRARIES LINK_LIBRARIES CMAKE_COMMAND_LINE)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT ARGS_NO_EXTERNAL_INSTALL)
message(FATAL_ERROR
"Option NO_EXTERNAL_INSTALL is required (for forward compatibility) "
"but was not given."
)
endif()
# 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
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}")
foreach(dir_var library_dir binary_dir)
if(NOT IS_ABSOLUTE "${${dir_var}}")
get_filename_component(${dir_var}
"${CMAKE_CURRENT_BINARY_DIR}/${${dir_var}}" ABSOLUTE)
endif()
endforeach()
# 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 GLOBAL)
set_property(TARGET ${lib} APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(${lib} PROPERTIES
IMPORTED_IMPLIB_NOCONFIG "${library_dir}/lib${lib}.lib"
IMPORTED_LOCATION_NOCONFIG "${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()

View File

@ -0,0 +1,2 @@
set(ENV{PATH} "@MINGW_PATH@\;$ENV{PATH}")
execute_process(COMMAND "@CMAKE_COMMAND@" --build . )

View File

@ -0,0 +1,9 @@
set(ENV{PATH} "@MINGW_PATH@\;$ENV{PATH}")
set(CMAKE_COMMAND_LINE "@ARGS_CMAKE_COMMAND_LINE@")
execute_process(
COMMAND "@CMAKE_COMMAND@" "-GMinGW Makefiles"
-DCMAKE_Fortran_COMPILER:PATH=@MINGW_GFORTRAN@
-DBUILD_SHARED_LIBS=ON
-DCMAKE_GNUtoMS=ON
${CMAKE_COMMAND_LINE}
"@source_dir@")

View File

@ -163,6 +163,32 @@ IF(BUILD_TESTING)
IF(CMAKE_Fortran_COMPILER)
ADD_TEST_MACRO(FortranOnly FortranOnly)
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)
SET(CMAKE_SKIP_VSGNUFortran FALSE)
# disable test for apple builds using ifort if they are building
# more than one architecture, as ifort does not support that.
IF(APPLE AND (CMAKE_Fortran_COMPILER MATCHES ifort))
LIST(LENGTH CMAKE_OSX_ARCHITECTURES len)
IF("${len}" GREATER 1)
MESSAGE(STATUS "Skip VSGNUFortran for ifort dual cpu mac build")
SET(CMAKE_SKIP_VSGNUFortran TRUE)
ENDIF()
ENDIF()
IF((CMAKE_C_COMPILER MATCHES lsb)
AND (CMAKE_Fortran_COMPILER MATCHES ifort))
MESSAGE(STATUS "Skip VSGNUFortran for ifort and lsb compilers")
SET(CMAKE_SKIP_VSGNUFortran TRUE)
ENDIF()
IF(NOT CMAKE_SKIP_VSGNUFortran)
ADD_TEST_MACRO(VSGNUFortran ${CMAKE_COMMAND} -P runtest.cmake)
ENDIF()
ENDIF()
ADD_TEST_MACRO(COnly COnly)
ADD_TEST_MACRO(CxxOnly CxxOnly)
ADD_TEST_MACRO(IPO COnly/COnly)

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.8)
project(VSGNUFortran)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
# 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_RUNTIME_OUTPUT_DIRECTORY_${config}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endforeach()
endif()
add_subdirectory(subdir)
include_directories(${VSGNUFortran_BINARY_DIR}/subdir/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)

View File

@ -0,0 +1,2 @@
add_executable(c_using_fortran main.c)
target_link_libraries(c_using_fortran hello)

View File

@ -0,0 +1,7 @@
#include <HelloWorldFCMangle.h> // created by FortranCInterface
extern void FC_hello(void);
int main()
{
FC_hello();
return 0;
}

View File

@ -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()

View File

@ -0,0 +1,16 @@
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 ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
RUNTIME_DIR bin # ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
LIBRARIES hello world # target libraries created
CMAKE_COMMAND_LINE
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
LINK_LIBRARIES # link interface libraries
LINK_LIBS hello world # hello needs world to link
NO_EXTERNAL_INSTALL
)

View File

@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 2.8)
project(FortranHello Fortran C)
# add a function to test for -lsunquad on sunpro sun systems.
function(test_sunquad result)
set( TEST_DIR "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/sunq")
file(WRITE "${TEST_DIR}/testsunq.f" "
PROGRAM TEST
END
")
file(WRITE ${TEST_DIR}/CMakeLists.txt "
project(sunq Fortran)
add_library(sunq SHARED testsunq.f)
target_link_libraries(sunq sunquad)
")
message(STATUS "looking for -lsunquad")
try_compile(RESULT "${TEST_DIR}" "${TEST_DIR}" sunq OUTPUT_VARIABLE OUT)
if("${RESULT}")
message(STATUS "-lsunquad found")
else()
message(STATUS "-lsunquad not found")
endif()
message(STATUS
"looking for sunquad:\nRESULT=[${RESULT}]\nOUTPUT=[\n${OUT}\n]")
set(${result} "${RESULT}" PARENT_SCOPE)
endfunction()
# check for the fortran c interface mangling
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)
if(CMAKE_Fortran_COMPILER_ID MATCHES SunPro)
target_link_libraries(hello fsu)
if(CMAKE_Fortran_PLATFORM_ID MATCHES SunOS)
target_link_libraries(hello sunmath m)
test_sunquad(CMAKE_HAS_SUNQUAD)
if(CMAKE_HAS_SUNQUAD)
target_link_libraries(hello sunquad)
endif()
endif()
endif()

View File

@ -0,0 +1,7 @@
!DEC$ ATTRIBUTES DLLEXPORT :: HELLO
SUBROUTINE HELLO
PRINT *, 'Hello'
CALL WORLD
END

View File

@ -0,0 +1,6 @@
!DEC$ ATTRIBUTES DLLEXPORT :: WORLD
SUBROUTINE WORLD
PRINT *, 'World!'
END