CMake check if CUDA environment exists without find_package - cmake

As stated here https://cmake.org/cmake/help/latest/module/FindCUDA.html
find_package(CUDA)
is a deprecated way to use CUDA in CXX project. We have to use
project(MY_PROJECT LANGUAGES CUDA CXX)
but how can I detect whether the current platform supports CUDA. The goal is to exclude some targets from build if CUDA is not installed.

Just found a solution thanks to a comment posted on the question referring to this Stack Overflow answer, and in turn this piece of documentation.
Here is a code snippet :
cmake_minimum_required(VERSION 3.8)
include(CheckLanguage)
project(my_project)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
message(STATUS "CUDA is OK")
else()
message(STATUS "No CUDA")
endif()

Related

How can I "tell" CMake 3.9+ I want to use NVIDIA's OpenCL library?

In my CMakeLists.txt I have:
cmake_minimum_required(VERSION 3.9)
# ... etc etc ...
find_package(CUDA 8.0 REQUIRED)
find_package(OpenCL REQUIRED)
and this finds CUDA and OpenCL. But - it prefers my system's non-NVIDIA OpenCL library over the library which comes with the CUDA installation. Actually, it's worse than that, since I get:
//Path to a file.
OpenCL_INCLUDE_DIR:PATH=/usr/local/cuda/include
//Path to a library.
OpenCL_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libOpenCL.so
in my CMakeCache.txt file.
How can I, from within CMakeLists.txt (i.e. not as the user but as the package maintainer), get CMake to switch the order of preference?
Note: Yes, it needs to be CMake 3.9 for compatibility reasons.
Since you're on CMake 3.9, your hands are very much tied.
If you were using CMake 3.17+ then you shouldn't find OpenCL at all. You would just use FindCUDAToolkit and the CUDA::OpenCL target:
cmake_minimum_required(VERSION 3.17)
project(test)
find_package(CUDAToolkit 8.0 REQUIRED)
add_executable(my_target ...)
target_link_libraries(my_target PRIVATE CUDA::OpenCL)
If you were using CMake 3.16, then you could still use FindCUDA and link to the CUDA-found OpenCL using the package variables:
cmake_minimum_required(VERSION 3.16)
project(test)
find_package(CUDA 8.0 REQUIRED)
cuda_add_executable(my_target ...)
target_link_libraries(my_target PRIVATE ${CUDA_OpenCL_LIBRARY})
If you were as far back as 3.12, then you could use the <Pkg>_ROOT variable to guide the search:
cmake_minimum_required(VERSION 3.12)
project(test)
find_package(CUDA 8.0 REQUIRED)
set(OpenCL_ROOT "${CUDA_TOOLKIT_ROOT_DIR}")
find_package(OpenCL REQUIRED)
and I would expect that to find the right OpenCL version.
But on earlier versions, the best you can do is use the CUDA root variable to find OpenCL inside the CUDA installation manually. Then, let FindOpenCL create the imported target:
cmake_minimum_required(VERSION 3.9)
project(test)
find_package(CUDA 8.0 REQUIRED)
find_path(
OpenCL_INCLUDE_DIR CL/opencl.h
HINTS "${CUDA_TOOLKIT_ROOT_DIR}/include"
NO_DEFAULT_PATH
)
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(opencl_lib64 "${CUDA_TOOLKIT_ROOT_DIR}/lib64" )
endif()
find_library(
OpenCL_LIBRARY OpenCL
HINTS ${opencl_lib64} "${CUDA_TOOLKIT_ROOT_DIR}/lib"
PATH_SUFFIXES "${CMAKE_LIBRARY_ARCHITECTURE}"
NO_DEFAULT_PATH
)
find_package(OpenCL REQUIRED)
cuda_add_executable(my_target ...)
target_link_libraries(my_target PRIVATE OpenCL::OpenCL)

CMake does not find MPI for Fortran

CMake does not find MPI for Fortran.
I am on Arch Linux with CMake 3.19.2.
I installed the openmpi (4.0.5-2) package and as stated here I also installed the gcc-fortran (10.2.0-4) package for fortran support.
I my CMake script in the line where MPI should be found:
find_package(MPI REQUIRED)
it tells me that it could not find MPI for Fortran:
-- Found MPI_C: /usr/lib/openmpi/libmpi.so (found version "3.1")
-- Found MPI_CXX: /usr/lib/openmpi/libmpi_cxx.so (found version "3.1")
-- Could NOT find MPI_Fortran (missing: MPI_Fortran_WORKS)
CMake Error at /usr/share/cmake-3.19/Modules/FindPackageHandleStandardArgs.cmake:218 (message):
Could NOT find MPI (missing: MPI_Fortran_FOUND) (found version "3.1")
Call Stack (most recent call first):
/usr/share/cmake-3.19/Modules/FindPackageHandleStandardArgs.cmake:582 (_FPHSA_FAILURE_MESSAGE)
/usr/share/cmake-3.19/Modules/FindMPI.cmake:1721 (find_package_handle_standard_args)
CMakeLists.txt:387 (find_package)
When installing openmpi it tells me that fortran support should be enabled:
(2/2) installing openmpi
Optional dependencies for openmpi
gcc-fortran: fortran support [installed]
There is a file /usr/lib/openmpi/libmpi_mpifh.so. Correct me if I am wrong but I think this should be the correct library file.
Thanks in advance for your help!
EDIT
I just tried to build a minimal example:
project(test)
cmake_minimum_required(VERSION 3.0)
enable_language(Fortran)
find_package(MPI REQUIRED)
With this it finds MPI_Fortran
-- Found MPI_Fortran: /usr/lib/openmpi/libmpi_usempif08.so (found version "3.1")
The CMakeLists.txt is a rather long and legacy one...
Seems that I have to find the solution on my own.
I solved the problem. As said in the edit of the answer this is legacy cmake and not written by myself.
My fortran skills are not too advanced so I do not know why this is used but the following line did break the finding process of the library:
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -heap-arrays 0")
If anyone falls into the same trap try to avoid this!
You need to use the COMPONENTS keyword on the find_package command, e.g.
find_package(MPI REQUIRED COMPONENTS Fortran)
Be sure you have Fortran enabled in your CMakeLists.txt either by enabling it in the project command, i.e.
project(name LANGUAGES Fortran)
or by calling
enable_language(Fortran)
prior to find_package.

Clion IDE C++ false positive on syntax error

I am using Clion as IDE
I just updated my fedora from 24 to 25 in order to use C++17,
But unfortunately now, CLion (v 2017.2.3) display code error that wasn't there before the OS upgrade
Here an "error" shown using template argument as template for std::optional
Here an "error" shown using std::make_shared, saying I am using too many argument (I should need 2 apparently) while the actual constructor takes 3 argument. I remind that the code compile and works
My CMake file (dunno if it could help for this)
Cmake_minimum_required(VERSION 3.3)
project(FreeSouls CXX)
set(CMAKE_CXX_COMPILER /usr/bin/g++)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lprotobuf -lboost_timer -lboost_system -lboost_thread")
set(CMAKE_CXX_STANDARD 17)
if ( UNIX )
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wextra -Wno-deprecated-declarations")
message( STATUS Set Warnings flag : ${CMAKE_CXX_FLAGS})
endif()
enable_testing()
add_subdirectory( Protobuff )
include_directories(
cmake-build-debug/Protobuff
FySMQ/include/queue
FySMQ/include/bus
FySMemoryManager/include
Server/include/gateway
Server/include/network
Server/include/babble
Server/include/game Utils/src)
set(SOURCE_FILES_MM)
set(SOURCE_PROTOBUF
cmake-build-debug/Protobuff/FySBabbleMessage.pb.cc
cmake-build-debug/Protobuff/FySGtwMessage.pb.cc
cmake-build-debug/Protobuff/FySLoginMessage.pb.cc
cmake-build-debug/Protobuff/FySAuthenticationResponse.pb.cc
)
set(SOURCE_FILES_MQ
FySMQ/include/queue/LockFreeQueue.hh
FySMQ/include/queue/QueueContainer.hh
FySMQ/include/queue/LockingQueue.hh
FySMQ/include/bus/BusListener.hh
FySMQ/include/bus/FysBus.hh)
set(SOURCE_FILES_SRV
Server/src/gateway/Gateway.cpp
Server/include/gateway/Gateway.hh
Server/src/gateway/Context.cpp
Server/include/gateway/Context.hh
Server/src/network/SessionManager.cpp
Server/include/network/SessionManager.hh
Server/src/babble/Babble.cpp
Server/include/babble/Babble.hh
Server/src/babble/BabbleChannel.cpp
Server/include/babble/BabbleChannel.hh
Server/src/network/TcpConnection.cpp
Server/include/network/TcpConnection.hh
Server/src/gateway/GameServerInstance.cpp
Server/include/gateway/GameServerInstance.hh
Server/src/gateway/Authenticator.cpp
Server/include/gateway/Authenticator.hh
)
set(SOURCE_FILES_UTILS Utils/src/CheckVars.hh Utils/src/TokenGenerator.cpp Utils/src/TokenGenerator.hh)
set(SOURCE_FILES_ALL
${SOURCE_PROTOBUF}
${SOURCE_FILES_UTILS}
${SOURCE_FILES_MQ}
${SOURCE_FILES_MM}
${SOURCE_FILES_SRV})
set(SOURCE_FILES
${SOURCE_FILES_ALL}
Server/src/main.cpp)
set(SOURCE_FILES_TEST
${SOURCE_FILES_ALL}
FySMQ/test/TestUnitMQ.cpp
Utils/test/TestCheckVar.cpp
FySMQ/test/FysBusTest.hh)
### Server ###
add_executable(FreeSouls ${SOURCE_FILES})
target_link_libraries(FreeSouls proto )
# link against dynamic libraries
add_definitions(-DBOOST_ALL_DYN_LINK)
## Test executable
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
add_executable(UnitTests ${SOURCE_FILES_TEST})
target_link_libraries(UnitTests ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
add_test(UnitTests UnitTests)
Does this error ever happened to you? How can I fix that?
I found a post about a problem similar here
Linux CLion can't resolve namespace member
But it looked like it were a bug of the Clion and a new version fixed it,
knowing I was using the exact same version on fedora 24 and it was working fine before the upgrade, I don't know if it is the same.
As said by Chris, this was a bug from the CLion parser.
The release of the version 2017.3 has solved those issues.

MacOS, CMake and OpenMP

I am using the newest CMake (3.9.3) from Homebrew along with LLVM 5.0.0 also from Brew, because Clang here has OpenMP support.
This worked in CMake 3.8.2 with LLVM 5.
In my CMakeLists.txt I have
find_package( OpenMP )
and later I want to do
if( OpenMP_CXX_FOUND )
However CMake doesn't seem to pick up on the find_package directive.
I run CMake with
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DUSE_WERROR=ON
where I have checked that clang and clang++ points correctly to /usr/local/opt/llvm/bin/clang and /usr/local/opt/llvm/bin/clang++
All I get is these two lines:
-- Could NOT find OpenMP_C (missing: OpenMP_C_FLAGS OpenMP_C_LIB_NAMES) (found version "1.0")
-- Could NOT find OpenMP_CXX (missing: OpenMP_CXX_FLAGS OpenMP_CXX_LIB_NAMES) (found version "1.0")
If I set OpenMP_C_FLAGS myself (with -DOpenMP_C_FLAGS=-fopenmp=libomp) it changes the error to
-- Could NOT find OpenMP_C (missing: OpenMP_C_LIB_NAMES) (found version "3.1")
Notice that it changes the version number, so it must be finding something, right?
What am I missing for this to work properly?
Okay, it seem that inside the FindOpenMP.cmake supplied by CMake we do a try_compile, which fails silently (because we do it a lot of times and most of them will fail, this makes sense). However, with Clang a -Werror flag is supplied, which fails because of an unused command line argument. I can thus add:
if(APPLE)
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(OpenMP_C_FLAG "-fopenmp=libomp -Wno-unused-command-line-argument")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(OpenMP_CXX_FLAG "-fopenmp=libomp -Wno-unused-command-line-argument")
endif()
endif()
to my project because I know that -fopenmp=libomp will work for this Clang.
Is this the right way of doing it?
The message basically tells you that you have to provide the path to the libraries and the names of the libraries. The following example should fix your problem (see also find_package(OpenMP)). Note that I use the brew installation using the command "brew install llvm". The first four lines are just for completeness.
set(CMAKE_C_COMPILER "/usr/local/Cellar/llvm/5.0.1/bin/clang")
set(CMAKE_CXX_COMPILER "/usr/local/Cellar/llvm/5.0.1/bin/clang++")
set(OPENMP_LIBRARIES "/usr/local/Cellar/llvm/5.0.1/lib")
set(OPENMP_INCLUDES "/usr/local/Cellar/llvm/5.0.1/include")
OPTION (USE_OpenMP "Use OpenMP to enamble <omp.h>" ON)
# Find OpenMP
if(APPLE AND USE_OpenMP)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(OpenMP_C "${CMAKE_C_COMPILER}")
set(OpenMP_C_FLAGS "-fopenmp=libomp -Wno-unused-command-line-argument")
set(OpenMP_C_LIB_NAMES "libomp" "libgomp" "libiomp5")
set(OpenMP_libomp_LIBRARY ${OpenMP_C_LIB_NAMES})
set(OpenMP_libgomp_LIBRARY ${OpenMP_C_LIB_NAMES})
set(OpenMP_libiomp5_LIBRARY ${OpenMP_C_LIB_NAMES})
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(OpenMP_CXX "${CMAKE_CXX_COMPILER}")
set(OpenMP_CXX_FLAGS "-fopenmp=libomp -Wno-unused-command-line-argument")
set(OpenMP_CXX_LIB_NAMES "libomp" "libgomp" "libiomp5")
set(OpenMP_libomp_LIBRARY ${OpenMP_CXX_LIB_NAMES})
set(OpenMP_libgomp_LIBRARY ${OpenMP_CXX_LIB_NAMES})
set(OpenMP_libiomp5_LIBRARY ${OpenMP_CXX_LIB_NAMES})
endif()
endif()
if(USE_OpenMP)
find_package(OpenMP REQUIRED)
endif(USE_OpenMP)
if (OPENMP_FOUND)
include_directories("${OPENMP_INCLUDES}")
link_directories("${OPENMP_LIBRARIES}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
# set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif(OPENMP_FOUND)
You might want to set e.g. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread") such that the linker automatically detects the appropriate pthread library (see pthread and wiki).
Apparently, case is important. For an unrelated project I can make it work with
find_package ( OPENMP REQUIRED )
This didn't work:
find_package ( OpenMP REQUIRED )
With that directive, no need for setting all the other flags by hand.
cmake 3.13.2, clang-1000.11.45.5 (High Sierra)
Maybe it's a CMake version thing, I come up with a slightly different solution with Franzi's.
I also use brew install libomp on my machine. It seems like OpenMP_CXX_FLAGS is used for compiling project source code instead of compiling the omp (the flag is stored in the omp target and will be populated by command target_link_libraries).
Besides that, OpenMP_CXX_LIB_NAMES shouldn't have prefix lib because it will cause an error like -llibomp not found, where -lomp should be used instead.
I also noticed that CMAKE_C_COMPILER_ID is AppleClang instead of Clang if I put project(playground) after cmake_minimum_required. In reverse, it's Clang, quite annoying and I don't know why.
Xpreprocessor used here is because apple clang doesn't ship with OpenMP and this flag tells the compiler to look for pragma (preprocessor expansion) elsewhere. In our case, it's the header files in the include path where the libomp is installed.
cmake_minimum_required(VERSION 3.12)
project(playground)
if(APPLE)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
if(CMAKE_C_COMPILER_ID MATCHES "Clang\$")
set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp")
set(OpenMP_C_LIB_NAMES "omp")
set(OpenMP_omp_LIBRARY omp)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang\$")
set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp")
set(OpenMP_CXX_LIB_NAMES "omp")
set(OpenMP_omp_LIBRARY omp)
endif()
endif()
find_package(OpenMP REQUIRED)
add_executable(helloworld helloworld.cxx)
target_link_libraries(helloworld PRIVATE OpenMP::OpenMP_CXX)
Here's my helloworld
#include <cstdio>
#include <thread>
#include <sstream>
int main(void)
{
#pragma omp parallel
{
std::stringstream ss;
ss << std::this_thread::get_id();
printf("%s, Hello, world.\n", ss.str().c_str());
}
return 0;
}
output is,
0x700002dc8000, Hello, world.
0x10a17d5c0, Hello, world.
0x7000045d1000, Hello, world.
0x7000055d7000, Hello, world.
0x700005dda000, Hello, world.
0x7000035cb000, Hello, world.
0x7000065dd000, Hello, world.
0x700003dce000, Hello, world.
0x700007de6000, Hello, world.
0x700004dd4000, Hello, world.
0x7000075e3000, Hello, world.
0x700006de0000, Hello, world.
With recent versions of CMake (3.18, didn't work with 3.14) and a fresh installation of MacOS (with developer CL tools installed, of course), brew install libomp was the only action needed to make things work.
MacOS Ventura
cmake: /usr/local/Cellar/cmake/3.25.1
OpenMP: /usr/local/Cellar/libomp/15.0.7
llvm: /usr/local/Cellar/llvm/15.0.7_1
above solution worked after setting compiler directories and libomp directories to the correct values
with the change:
set(OpenMP_C_FLAGS "-fopenmp=libomp")
for both the clang and clangxx sections

How can I add a minimum compiler version requisite?

I want to create a project in C++11 and I use CMake as my build system.
How can I add a minimum compiler version requisite in the CMake config files?
AFAIK, there is no built-in support for something like this, but you could certainly write it yourself:
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "your.required.gcc.version")
message(FATAL_ERROR "Insufficient gcc version")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "your.required.msvc.version")
message(FATAL_ERROR "Insufficient msvc version")
endif()
elseif(...)
# etc.
endif()
However, I suggest you actually consider a feature-detection approach instead. That is, use try_compile() to verify that the compiler supports the features you need, and FATAL_ERROR if it doesn't. It's more idiomatic in CMake, and has the added benefit you don't have to discover the appropriate minimal version for all compilers out there.
Starting with CMake 2.8.10 the CMAKE_<LANG>_COMPILER_VERSION variables can be accessed by users to get the compiler version. In previous versions those were only reserved for internal purposes and should not be read by user code. They are also not guaranteed to be set for all languages but C and CXX should definitely be available.
CMake also contains operators for version comparison (VERSION_LESS, VERSION_EQUAL, VERSION_GREATER) that you can use to write your version validation code.
Remember that you will need to find out which compiler you have first and then check for the correct version.
Here is a short sample from one of my projects:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# require at least gcc 4.8
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
message(FATAL_ERROR "GCC version must be at least 4.8!")
endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# require at least clang 3.2
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.2)
message(FATAL_ERROR "Clang version must be at least 3.2!")
endif()
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
endif()
You can check the specific gcc version as follows:
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1)
message(FATAL_ERROR "Require at least gcc-5.1")
endif()
There is built-in support nowadays. From the Documentation:
target_compile_features(mylib PUBLIC cxx_std_11)
"In this example, CMake will ensure the compiler is invoked in a mode of at-least C++ 11 (or C++ 14, C++ 17, ...), adding flags such as -std=gnu++11 if necessary. This applies to sources within mylib as well as any dependents (that may include headers from mylib)."