0

Using CMake and GCC to build a very basic Fortran project with an iso_c_binding fails at linking stage. Compiling and linking manually just works fine:

$ gcc6 -c getkey.c
$ gfortran6 -Wl,-rpath=/usr/local/lib/gcc6/ -o key key.f08 getkey.o

The minimal CMakeLists.txt:

cmake_minimum_required(VERSION 3.9)
project(GetKey)
set(VERSION 1.0)

set(CMAKE_Fortran_COMPILER "gfortran6")
set(GCC_LIB "-Wl,-rpath=/usr/local/lib/gcc6")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${GCC_LIB}")
enable_language(Fortran)

add_library(getkey STATIC getkey.c)
add_executable(key key.f08)

set_target_properties(key PROPERTIES LINKER_LANGUAGE Fortran)
target_link_libraries(key getkey)

Building the project leads to the following linker error:

[...]
[100%] Linking Fortran executable key
/usr/lib/crt1.o: In function `_start':
/usr/src/lib/csu/amd64/crt1.c:(.text+0x17b): undefined reference to `main'
collect2: error: ld returned 1 exit status

The linker seems to search for a main() function, that Fortran does not feature. Trying to force the Fortran linker makes no difference:

set(CMAKE_Fortran_LINKER_PREFERENCE 100)
set(CMAKE_C_LINKER_PREFERENCE_PROPAGATES False)

Help is appreciated.

Example Code

The linker error does not depend on the actual implementation. Anyway, a basic example would be:

key.f08:

program main
    implicit none

    interface
        function get_key() bind(c, name='get_key')
            use iso_c_binding
            integer(kind=c_int) :: get_key
        end function get_key
    end interface

    write(*, '(i0)') get_key()
end program main

And the getkey.c:

int get_key()
{
    return 10000;
}

Verbose Make

Running make VERBOSE=1:

/usr/local/bin/cmake -H"/home/user/fortran" -B"/home/user/fortran" --check-build-system CMakeFiles/Makefile.cmake 0
/usr/local/bin/cmake -E cmake_progress_start "/home/user/fortran/CMakeFiles" "/home/user/fortran/CMakeFiles/progress.marks"
make -f CMakeFiles/Makefile2 all
make -f CMakeFiles/key.dir/build.make CMakeFiles/key.dir/depend
cd "/home/user/fortran" && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" "/home/user/fortran" "/home/user/fortran" "/home/user/fortran" "/home/user/fortran" "/home/user/fortran/CMakeFiles/key.dir/DependInfo.cmake" --color=
make -f CMakeFiles/key.dir/build.make CMakeFiles/key.dir/build
[ 50%] Linking Fortran executable key
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/key.dir/link.txt --verbose=1
/usr/local/bin/gfortran6    CMakeFiles/key.dir/getkey.c.o  -o key
/usr/lib/crt1.o: In function `_start':
/usr/src/lib/csu/amd64/crt1.c:(.text+0x17b): undefined reference to `main'
collect2: error: ld returned 1 exit status
*** Error code 1

Stop.
make[2]: stopped in /usr/home/user/fortran
*** Error code 1

Stop.
make[1]: stopped in /usr/home/user/fortran
*** Error code 1

Stop.
make: stopped in /usr/home/user/fortran
laserbrain
  • 985
  • 3
  • 10
  • 20
  • Your Fortran project has a main program? (Can you provide minimal code as an example?) – francescalus Dec 24 '17 at 01:51
  • Contrary to C, there is no such thing like a main function in Fortran, that is why I guess the C linker is called instead of the Fortran one to link the project. Forcing the use of the Fortran linker did not work. – laserbrain Dec 24 '17 at 02:02
  • 2
    Fortran absolutely has the concept of a main program. – francescalus Dec 24 '17 at 02:59
  • Yes, but there is no `int main()` in Fortran. The linker error occurs on every project where Fortran code is mixed with another language (e.g., C). The specific implementation is therefore of marginal importance. – laserbrain Dec 24 '17 at 10:32
  • Run `make VERBOSE=1` and show us what commands CMake is executing. Also, have you tried just putting the two source files directly into `add_executable()` and removing `add_library()`? – John Zwinck Dec 24 '17 at 10:44
  • Yes, I tried that too, with the same result. – laserbrain Dec 24 '17 at 10:52

1 Answers1

2

As you can see from the verbose make output:

/usr/local/bin/gfortran6    CMakeFiles/key.dir/getkey.c.o  -o key

You are not building with the Fortran code at all! This is because CMake doesn't recognize .f08 as a code extension. See here: cmake, fortran 2008, and .f08 file extension

You may simply rename your .f08 file to .f (or .f90 as described in comments below).

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Renaming to `.f` is a very bad idea! All compilers I know treat .f as a fixed form source by default whereas .f08 is likely to be a free form source. The correct extension for free form files is .f90. https://stackoverflow.com/questions/20269076/correct-suffix-for-fortran-2003-source-file-intel-fortran-compiler https://stackoverflow.com/questions/10884260/how-can-gfortran-tell-if-i-am-compiling-f90-or-f95-code – Vladimir F Героям слава Dec 24 '17 at 12:47
  • @VladimirF: Compiler options can also be used to indicate which form the source file has (I assume you know this). – John Zwinck Dec 24 '17 at 13:15
  • Yes, but it is an unnecessary complication. Especially when you have a mixture (my case, can happen easily) and have to put different options to different files. Naming it .f is confusing at best. A very bad idea IMHO. – Vladimir F Героям слава Dec 24 '17 at 13:20
  • @VladimirF: While I do not agree that using `.f` is "a very bad idea," I do think using `.f90` makes sense on some systems including GCC. I've updated my answer to mention it. – John Zwinck Dec 24 '17 at 13:25