Search code examples
cmakefortranintel-fortran

CMake 3.0+Fortran+CUDA requires -fPIC even when building executables


This is a CMake question. I cannot compile Fortran executables with CUDA support when using the Intel Fortran compiler, unless I include the -fPIC flag. The problem is that -fPIC shouldn't be necessary unless I'm building a library.

The following is minimal example:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(cuda LANGUAGES Fortran CXX)
find_package(CUDA)
cuda_add_executable(main main.f90)

and

# main.f90
end

When I try to build and run,

cmake -D CMAKE_Fortran_COMPILER=ifort .. && make VERBOSE=1

I get the following:

-- The Fortran compiler identification is Intel 18.0.0.20170811
-- The CXX compiler identification is GNU 7.3.0
-- Check for working Fortran compiler: /opt/intel/compilers_and_libraries_2018.0.128/linux/bin/intel64/ifort
-- Check for working Fortran compiler: /opt/intel/compilers_and_libraries_2018.0.128/linux/bin/intel64/ifort  -- works
-- Detecting Fortran compiler ABI info
-- Detecting Fortran compiler ABI info - done
-- Checking whether /opt/intel/compilers_and_libraries_2018.0.128/linux/bin/intel64/ifort supports Fortran 90
-- Checking whether /opt/intel/compilers_and_libraries_2018.0.128/linux/bin/intel64/ifort supports Fortran 90 -- yes
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Looking for C++ include pthread.h
-- Looking for C++ include pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Found CUDA: /opt/cuda (found version "9.1")
-- Configuring done
-- Generating done
-- Build files have been written to: /home/raul/tmp/cuda/build
/usr/bin/cmake -H/home/raul/tmp/cuda -B/home/raul/tmp/cuda/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/raul/tmp/cuda/build/CMakeFiles /home/raul/tmp/cuda/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/raul/tmp/cuda/build'
make -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/depend
make[2]: Entering directory '/home/raul/tmp/cuda/build'
cd /home/raul/tmp/cuda/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/raul/tmp/cuda /home/raul/tmp/cuda /home/raul/tmp/cuda/build /home/raul/tmp/cuda/build /home/raul/tmp/cuda/build/CMakeFiles/main.dir/DependInfo.cmake --color=
Dependee "/home/raul/tmp/cuda/build/CMakeFiles/main.dir/DependInfo.cmake" is newer than depender "/home/raul/tmp/cuda/build/CMakeFiles/main.dir/depend.internal".
Dependee "/home/raul/tmp/cuda/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/raul/tmp/cuda/build/CMakeFiles/main.dir/depend.internal".
Scanning dependencies of target main
make[2]: Leaving directory '/home/raul/tmp/cuda/build'
make -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/requires
make[2]: Entering directory '/home/raul/tmp/cuda/build'
make[2]: Nothing to be done for 'CMakeFiles/main.dir/requires'.
make[2]: Leaving directory '/home/raul/tmp/cuda/build'
make -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/build
make[2]: Entering directory '/home/raul/tmp/cuda/build'
[ 50%] Building Fortran object CMakeFiles/main.dir/main.f90.o
/opt/intel/compilers_and_libraries_2018.0.128/linux/bin/intel64/ifort  -I/opt/cuda/include   -c /home/raul/tmp/cuda/main.f90 -o CMakeFiles/main.dir/main.f90.o
[100%] Linking CXX executable main
/usr/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1
/usr/bin/c++    -rdynamic CMakeFiles/main.dir/main.f90.o  -o main /opt/cuda/lib64/libcudart_static.a -lpthread -ldl -lrt -lifport -lifcoremt -limf -lsvml -lipgo -lirc -lpthread -lsvml -lirc_s -ldl
/usr/bin/ld: CMakeFiles/main.dir/main.f90.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/../../../../lib/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/bin/ld: final link failed: Invalid operation
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/main.dir/build.make:97: main] Error 1
make[2]: Leaving directory '/home/raul/tmp/cuda/build'
make[1]: *** [CMakeFiles/Makefile2:69: CMakeFiles/main.dir/all] Error 2
make[1]: Leaving directory '/home/raul/tmp/cuda/build'
make: *** [Makefile:84: all] Error 2

I do not get the build error if I use the newer CUDA features of CMake, e.g.

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(cuda LANGUAGES Fortran CUDA)
add_executable(main main.f90)

However, our current policy is to support CMake 3.0 and thus I cannot the simpler version. Also, I do not get any error if instead of ifort I use gfortran. I'm currently looking into FindCUDA.cmake that ships with CMake, but I haven't found a solution yet. Any thoughts?


Solution

  • I found a solution that works for now, but it's not trivial at all. First, one should use icpc with ifort (not a requirement for CMake >=3.8). It turns out that out of the many libraries that icpc links against under the hood (you won't see them without running make VERBOSE=1), one of them (but not others) should be linked against statically. Thus, by specifying

    target_link_libraries(main ifcoremt.a)
    

    in CMakeLists.txt seems to fix the problem, at least for me.