SethMMorton / cmake_fortran_template

A template directory structure for a Fortran project using CMake as the build system.
MIT License
126 stars 27 forks source link

pgfortran/CMake issues #1

Open nenanth opened 8 years ago

nenanth commented 8 years ago

I tried the template code as is, and it worked fine for gfortran on Ubuntu 14.04

When i tried it with ifort, things were good too.. $ FC=ifort cmake .. The Makefile was created and everything worked.

However, after flushing the build directory, I tried it with the PGI compiler to get (FC=pgfortran cmake ..)

yosemite@yosemite-xeon:~/Dropbox/cmake_trials/build$ FC=pgfortran cmake ..
-- The Fortran compiler identification is PGI
-- Check for working Fortran compiler: /opt/pgi/linux86-64/15.7/bin/pgfortran
-- Check for working Fortran compiler: /opt/pgi/linux86-64/15.7/bin/pgfortran  -- works
-- Detecting Fortran compiler ABI info
-- Detecting Fortran compiler ABI info - done
-- Checking whether /opt/pgi/linux86-64/15.7/bin/pgfortran supports Fortran 90
-- Checking whether /opt/pgi/linux86-64/15.7/bin/pgfortran supports Fortran 90 -- yes
CMake Error at cmake/Modules/SetCompileFlag.cmake:109 (MESSAGE):
  No compile flags were found
Call Stack (most recent call first):
  cmake/Modules/SetFortranFlags.cmake:99 (SET_COMPILE_FLAG)
  CMakeLists.txt:48 (INCLUDE)

-- Configuring incomplete, errors occurred!

I placed print statements in cmake/Modules/SetFortranFlags.cmake, and it seems like there is an issue with setting the first debug flag with the PGI compiler. Not sure what's going on under the hood, but thought it might be worth digging into.

SethMMorton commented 8 years ago

If the SET_COMPILE_FLAG function is called with the REQUIRED option, then it will fail if no valid flags are found. In the template, the only calls with REQUIRED are the ones that set the optimization level... for DEBUG this would be either -O0 or /Od.

If you remove REQUIRED does it still work?

I just took a look at the pgfortran manual and it seems to support the -O0, so I will need you to supply extra information for further help.

nenanth commented 8 years ago

So I did a little debugging, and it appears that pgfortran is not throwing up one of the recognized error messages on my system for anything starting with "/" (written specifically for windows-ifort). Instead, the compilation messages for the same program fool it into believing that flags like "/O0" (slash oh-zero) are valid options. I fixed it by checking for OS type first, and using it as a branch to test the windows-ifort flags separately from the rest.

Below is my modified version of SetFortranFlags.cmake:

#==============================================================
# Determine and set the Fortran compiler flags we want 
#==============================================================

#==============================================================
# Make sure that the default build type is RELEASE if not specified.
#==============================================================

INCLUDE(${CMAKE_MODULE_PATH}/SetCompileFlag.cmake)

#==============================================================
# Make sure the build type is uppercase
#==============================================================

STRING(TOUPPER "${CMAKE_BUILD_TYPE}" BT)

IF(BT STREQUAL "RELEASE")
    SET(CMAKE_BUILD_TYPE RELEASE CACHE STRING
      "Choose the type of build, options are DEBUG, RELEASE, or TESTING."
      FORCE)
    MESSAGE(STATUS "CMAKE BUILD TYPE RELEASE SELECTED")
ELSEIF(BT STREQUAL "DEBUG")
    SET (CMAKE_BUILD_TYPE DEBUG CACHE STRING
      "Choose the type of build, options are DEBUG, RELEASE, or TESTING."
      FORCE)
    MESSAGE(STATUS "CMAKE BUILD TYE DEBUG SELECTED")
ELSEIF(BT STREQUAL "TESTING")
    SET (CMAKE_BUILD_TYPE TESTING CACHE STRING
      "Choose the type of build, options are DEBUG, RELEASE, or TESTING."
      FORCE)
    MESSAGE(STATUS "CMAKE BUILD TYPE TESTING SELECTED")
ELSEIF(NOT BT)
    SET(CMAKE_BUILD_TYPE RELEASE CACHE STRING
      "Choose the type of build, options are DEBUG, RELEASE, or TESTING."
      FORCE)
    MESSAGE(STATUS "CMAKE_BUILD_TYPE not given, defaulting to RELEASE")
ELSE()
    MESSAGE(FATAL_ERROR "CMAKE_BUILD_TYPE not valid, choices are DEBUG, RELEASE, or TESTING")
ENDIF(BT STREQUAL "RELEASE")

#==============================================================
# If the compiler flags have already been set, return now
#==============================================================

IF(CMAKE_Fortran_FLAGS_RELEASE AND CMAKE_Fortran_FLAGS_TESTING AND CMAKE_Fortran_FLAGS_DEBUG)
    RETURN ()
ENDIF(CMAKE_Fortran_FLAGS_RELEASE AND CMAKE_Fortran_FLAGS_TESTING AND CMAKE_Fortran_FLAGS_DEBUG)

#==============================================================
# Determine the appropriate flags for this compiler for each build type.
# For each option type, a list of possible flags is given that work
# for various compilers.  The first flag that works is chosen.
# If none of the flags work, nothing is added (unless the REQUIRED 
# flag is given in the call).  This way unknown compiles are supported.
#==============================================================

#==============================================================
### GENERAL FLAGS ###
#==============================================================

#==============================================================
# Don't add underscores in symbols for C-compatability
#==============================================================

SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}"
                 Fortran "-fno-underscoring")

#==============================================================
# There is some bug where -march=native doesn't work on Mac
#==============================================================

IF(APPLE)
    SET(GNUNATIVE "-mtune=native")
ELSE()
    SET(GNUNATIVE "-march=native")
ENDIF()

#==============================================================
# Define the operating system
#==============================================================

SET(OS ${CMAKE_SYSTEM_NAME})
SET(FC ${CMAKE_Fortran_COMPILER})

STRING(TOUPPER "${OS}" OS)
STRING(TOUPPER "${FC}" FC)

SET(Wintel FALSE)
IF(${OS} STREQUAL "WINDOWS")
   IF(${FC} MATCHES "INTEL")
      SET(Wintel TRUE)
   ENDIF()
ENDIF()

MESSAGE("The Operating System Type is " ${OS})
MESSAGE("The Fortran Compiler is      " ${FC})

#==============================================================
# Optimize for the host's architecture
#==============================================================

IF(${Wintel}) 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}"
                 Fortran "/QxHost"       # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS}"
                 Fortran "-xHost"        # Intel
                         ${GNUNATIVE}    # GNU
                         "-ta=host"      # Portland Group
                )
ENDIF()

#==============================================================
### DEBUG FLAGS ###
#==============================================================

# NOTE: debugging symbols (-g or /debug:full) are already on by default

#==============================================================
# Disable optimizations
#==============================================================

IF(${Wintel}) 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran REQUIRED "/Od" # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran REQUIRED "-O0" # All compilers not on Windows
                )
ENDIF()

#==============================================================
# Turn on all warnings 
#==============================================================

IF(OS STREQUAL "WINDOWS") 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "/warn:all" # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "-warn all" # Intel
                         "-Wall"     # GNU
                                     # Portland Group (on by default)
                )
ENDIF()

#==============================================================
# Traceback
#==============================================================

IF(${Wintel}) 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "/traceback"   # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "-traceback"   # Intel/Portland Group
                         "-fbacktrace"  # GNU (gfortran)
                         "-ftrace=full" # GNU (g95)
                )
ENDIF()

#==============================================================
# Check array bounds
#==============================================================

IF(${Wintel}) 
SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "/check:bounds"  # Intel Windows
                )
ELSE()
SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG}"
                 Fortran "-check bounds"  # Intel
                         "-fcheck=bounds" # GNU (New style)
                         "-fbounds-check" # GNU (Old style)
                         "-Mbounds"       # Portland Group
                )
ENDIF()

#==============================================================
### TESTING FLAGS ###
#==============================================================

#==============================================================
# Optimizations
#==============================================================

IF(${Wintel}) 
SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_TESTING "${CMAKE_Fortran_FLAGS_TESTING}"
                 Fortran REQUIRED "/O2" # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_TESTING "${CMAKE_Fortran_FLAGS_TESTING}"
                 Fortran REQUIRED "-O2" # All compilers not on Windows
                )
ENDIF()

#==============================================================
### RELEASE FLAGS ###
#==============================================================

# NOTE: agressive optimizations (-O3) are already turned on by default

#==============================================================
# Unroll loops
#==============================================================

IF(${Wintel}) 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "/unroll"        # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "-funroll-loops" # GNU
                         "-unroll"        # Intel
                         "-Munroll"       # Portland Group
                )
ENDIF()

#==============================================================
# Inline functions
#==============================================================

IF(${Wintel}) 
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "/Qinline"           # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "-inline"            # Intel
                         "-finline-functions" # GNU
                         "-Minline"           # Portland Group
                )
ENDIF()

#==============================================================
# Interprocedural (link-time) optimizations
#==============================================================

IF(${Wintel})
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "/Qipo"    # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "-ipo"     # Intel
                         "-flto"    # GNU
                         "-Mipa"   # Portland Group
                )
ENDIF()

#==============================================================
# Single-file optimizations
#==============================================================

IF(${Wintel})
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "/Qip" # Intel Windows
                )
ELSE()
   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
                 Fortran "-ip"  # Intel
                )
ENDIF()

#==============================================================
# Turn off loop vectorization diagnostics; parallelization done by AS
#==============================================================

#IF(${Wintel})
#   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
#                 Fortran "/Qvec-report0" # Intel Windows
#                )
#ELSE()
#   SET_COMPILE_FLAG(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE}"
#                 Fortran "-vec-report0"  # Intel
#                         "-Mvect"        # Portland Group
#                )
#ENDIF()
SethMMorton commented 8 years ago

Is there a reason you didn't modify SetCompileFlag.cmake with the message the pgfortran was giving on compile error? I believe that would have been less work.

nenanth commented 8 years ago

pgfortran seemed to think that the "slash" referred to a path, instead of a compilation option.

I didn't want any (future) compiler versions to throw errors based on OS-specific flags. The solution above feels more robust, because it avoids combinations of flags/OS's that are known to be incompatible (as opposed to triggers based on specific errors).

The exact error is pasted below: pgfortran /Qhost a.f90 a.f90: /usr/bin/ld: cannot find /Qhost: No such file or directory

SethMMorton commented 8 years ago

The string "No such file or directory" could be added to the FAIL_REGEX in SetCompileFlag.cmake to solve the problem.

I am glad you have a solution that works for you. I am not convinced this is the most robust solution because it assumes that Intel Windows is the only compiler the uses the / prefix, which may not be true. I had only checked a limited number of compilers when I made the template.

The try: except: methodology I used mimics what Kitware does in FindOpenMP.cmake, and my assumption was that if this method works for them, it would work for me as well.

Having said all that, if you think your solution is better then make a pull request and I will consider accepting the change.

nenanth commented 8 years ago

makes sense. I'll experiment with it a bit more on all 3 OS's, and see whether the solution covers all cases. After that, I can say with a little more confidence that it's a working solution.

emanspeaks commented 5 years ago

@nenanth Did this issue ever get resolved? I'm thinking of putting together a pull request to modernize this template per #3 and would like to incorporate any findings. Or if you can submit a pull request yourself, that would be great!

nenanth commented 5 years ago

@emanspeaks so far I've gotten by with the code posted above; havent dug into CMake too much because I'm not super familiar with the language.. my patch seems to work OK on a few different machines. Also, I was able make only a few tests on windows before the infamous Fall Creators update screwed up a few things and I moved away from the OS altogether.

SethMMorton commented 5 years ago

@emanspeaks Based on your willingness to tackle #3 I would guess you are actually versed in modern CMake style. I don't think there are many out there that have that knowledge AND Fortran knowledge, so I would say just go for it and add this fix into your PR.

emanspeaks commented 5 years ago

because of the new way that compiler flags are set in modern CMake, this issue would become obsolete after #4 is merged.