"Rosetta" for switching to native CMake CUDA support - cmake

I want to switch a CMake-based project of mine to using CMake 3.8 and up's native support for CUDA. I know that, basically, I enable_language(CUDA), then use the regular add_target and add_library instead of the cuda_xxx variants of the same.
However, I'm not sure how to rewrite my code exactly. How do I...:
Set of global CUDA-related CMake variables , e.g. separable compilation, propagation of host flags?
Invoke the CUDA equivalent of CMAKE_CXX_STANDARDand friends?
Add dependencies on CUDA libraries which are not required by default (e.g. NVTX)
... and everything else?
Is there some uniform method to all this, or do I just have to memorize a bunch of new commands?

CMake's native CUDA support works very much in the same way as it does for C and C++ projects. Using CUDA instead of C++ is a similar process to using C instead of C++.
First of all, you should enable CUDA support in your project by adding:
project(myproject LANGUAGES CUDA)
This way, you shouldn't need to run enable_language, and your CMake cache will not be cluttered with the C and C++ compiler configuration generated by the default implied project(myproject LANGUAGES C CXX) you get when you write just project(myproject).
The equivalent of CMAKE_CXX_STANDARD for CUDA is CMAKE_CUDA_STANDARD, much like how there is also CMAKE_C_STANDARD. Additionally, there is CMAKE_CUDA_EXTENSIONS which can be configured to enable / disable compiler specific extensions (i.e. whether to choose -std=c++14 or -std=gnu++14). It's generally the case that if you see CMake variables of the form CMAKE_CXX_..... or CMAKE_C_....., there is an equivalent CMAKE_CUDA_...... And in general, where you see CXX or C in variables and properties, there may be a CUDA equivalent.
set(CMAKE_CUDA_STANDARD 11)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_EXTENSIONS OFF)
One thing to note however, is that these particular variables have target-specific equivalents in the form of target properties (both for C++ and CUDA):
set_target_properties(
myprojectlibrary PROPERTIES
CUDA_STANDARD 11
CUDA_STANDARD_REQUIRED ON
CUDA_EXTENSIONS OFF)
As for CUDA-specific features like in the old FindCUDA, you should find these as more target properties just like those in the previous example:
set_target_properties(
myprojectlibrary PROPERTIES
CUDA_SEPARABLE_COMPILATION ON
CUDA_RESOLVE_DEVICE_SYMBOLS ON)
Information about these target properties can be found here, searching on the page for "CUDA".
As for linking to third-party libraries, this can be done the same way as it is done for regular C++ projects:
target_link_libraries(myprojectlib nvtx)
Or if your library isn't defined as a first-class CMake target:
target_link_libraries(myprojectlib ${NVTX_LIBRARY})
target_include_directories(myprojectlib PRIVATE ${NVTX_INCLUDE_DIR})
target_compile_definitions(myprojectlib PRIVATE ${NVTX_DEFINES})
Lastly, if there are any specific nvcc flags required to build your project, these can be set as follows:
target_compile_options(myprojectlib PRIVATE --default-stream=per-thread)
Note that the global user-configurable nvcc flags are also available as the variable CMAKE_CUDA_FLAGS.
Also note that you may have to change PRIVATE to PUBLIC in places where you want particular flags / include directories / compile definitions to also be propagated to targets linking to your library, in the case that myprojectlib is a library and not an executable, which may or may not be desired depending on how your project is structured.

Related

How to error out cleanly in CMake if CMAKE_CXX_STANDARD cannot be met?

CMake provides CMAKE_CXX_STANDARD for specifying the required C++ Standard.
set (CMAKE_CXX_STANDARD 17)
However, if your compiler is old... say gcc4... it will still attempt to compile the sources, and it will of course fail with bizarre error messages, due to missing compiler features.
Is there a clean way to make CMake detect this missing support, and fail in a more obvious way?
From https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html :
See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers.
From https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html#manual:cmake-compile-features(7) :
The CMAKE_C_KNOWN_FEATURES, CMAKE_CUDA_KNOWN_FEATURES, and CMAKE_CXX_KNOWN_FEATURES global properties contain all the features known to CMake, regardless of compiler support for the feature. The CMAKE_C_COMPILE_FEATURES, CMAKE_CUDA_COMPILE_FEATURES , and CMAKE_CXX_COMPILE_FEATURES variables contain all features CMake knows are known to the compiler, regardless of language standard or compile flags needed to use them.
From https://cmake.org/cmake/help/latest/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html#prop_gbl:CMAKE_CXX_KNOWN_FEATURES :
cxx_std_17
Compiler mode is at least C++ 17.
Check:
if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
Using target_compile_features for more control
As KamilCuk mentioned in their answer, you can check for cxx_std_17.
Additionally, you can set the requirement on a target with:
target_compile_features(my_example_library PUBLIC cxx_std_17)
Source https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html#requiring-language-standards
This is more verbose, but it allows you to specify that only some targets need certain standards. The same syntax can also be used to require only specific features, full list found in CMAKE_CXX_KNOWN_FEATURES.
Simpler option for CMAKE_CXX_STANDARD, set CXX_STANDARD_REQUIRED
However, if you're using CMAKE_CXX_STANDARD, it's much easier to just set CMAKE_CXX_STANDARD_REQUIRED, e.g.:
# will affect any library/target defined after this
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# also recommended, uses -std=c++17 instead of -std=gnu++17
# if you code is compatible with non-GCC compilers too
# set(CMAKE_CXX_EXTENSIONS OFF)
You'll get an error that looks something like:
CMake Error in CMakeLists.txt:
Target "my_example_library" requires the language dialect "C17" (with
compiler extensions). But the current compiler "GNU" does not support
this, or CMake does not know the flags to enable it.

Setting project wide compiler options [duplicate]

This question already has an answer here:
What is the modern method for setting general compile flags in CMake?
(1 answer)
Closed 1 year ago.
I want to use CMake to create modular embedded C++ software. I separated hal-drivers static library, some common-utils library and top target device depends on those two what is marked using target_link_libraries like this:
target_link_libraries(device
PRIVATE
hal-drivers
common-utils
)
It is easy to propagate compile definitions and option up in "dependency ladder" using commands like this:
target_compile_definitions(hal-drivers
INTERFACE
STM32F415xx
USE_HAL_DRIVER
)
This way any target utilising hal-drivers header files will preprocess those headers correctlym, and I found this CMake scripts feature (propagation of "settings") great, but it is not the point of this question.
The question is how should I propagate common compiler options like for example -fdata-sections or -Wall for every target in my project? I know I can
create dummy (no source and no header files, just compile options) interface target which will be consumed by every other target in project but this looks like a workaround...
specify mentioned compiler options for every target separatly, since I have only about 5 targets, but it will be very problematic to maintain.
In my commercial work project (50 targets) my boss ended up with an ugly compromise: setting common compile options in top CMakeLists.txt as cached variable and then applying this variable in all targets manually, but we dont like it at all.
Bear in mind: I do have solutions that work, I am interested in recomended solutions. Also I am using Professional CMake: A Practical Guide 9th Edition on daily basis (its a great book), but I failed to found answer on my question in this book.
I found an answer.
I guess my whining about lack of elegant solution is due to my attachment to syntactic sugar like target_compile_options, but the thing is CMake evolved in hardship and not every CMake feature is pretty, but it works.
There is an answer: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS.html
The flags in this variable will be passed to the compiler before those in the per-configuration CMAKE_<LANG>_FLAGS_<CONFIG> variant, and before flags added by the add_compile_options() or target_compile_options() commands.
So I have to append my custom options to this special CMake variable like this:
project(Device C CXX ASM)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections")
This way it will flood all targets with fdata-sections.
Leaving this thread as interesting note.

How to detect C++11-... support of a compiler with CMake and react without failing generation

I did read this question: How to detect C++11 support in CMake. It basically tells me that CMake's support for detecting C++ features is very sophisticated when you want to fail generation if a certain compiler does NOT support the feature you are using in your code but also extremely limited when you want to react on this at generation time without failing.
What I would like to be able to do is not to fail but e.g. to define a different set of source files if certain features are not available in order to allow compilation of old c++03 code if needed and use the newer c++11-... ones if possible. Something like this would be great:
set(TARGET MyTarget)
if(CPP_STANDARD_VERSION GREATER_EQUAL 11)
set(SOURCE myModernFile.cpp)
else()
set(SOURCE myLegacyFile.cpp)
endif()
add_executable(${TARGET} ${SOURCE })
if(CPP_STANDARD_VERSION GREATER_EQUAL 11)
# Here failing would be fine if lambdas are not supported!
target_compile_features(${TARGET} PRIVATE cxx_lambdas)
endif()
When following this approach as recommended in the linked question:
foreach(i ${CMAKE_CXX_COMPILE_FEATURES})
message("${i}")
endforeach()
e.g. with Visual Studio 2013 with CMake 3.13.2 I am getting the following output:
cxx_std_98
cxx_std_11
cxx_std_14
cxx_std_17
cxx_std_20
cxx_alias_templates
cxx_auto_type
cxx_contextual_conversions
cxx_decltype
cxx_default_function_template_args
cxx_defaulted_functions
cxx_delegating_constructors
cxx_enum_forward_declarations
cxx_explicit_conversions
cxx_extended_friend_declarations
cxx_extern_templates
cxx_final
cxx_generalized_initializers
cxx_lambdas
cxx_local_type_template_args
cxx_long_long_type
cxx_nullptr
cxx_override
cxx_range_for
cxx_raw_string_literals
cxx_right_angle_brackets
cxx_rvalue_references
cxx_static_assert
cxx_strong_enums
cxx_template_template_parameters
cxx_trailing_return_types
cxx_uniform_initialization
cxx_variadic_macros
cxx_variadic_templates
To me the list makes sense for the specific features, but the general ones with the supported C++ standards apparently cannot be right as VS 2013 does not support C++20. Am I missing something?
Right now I ended up with more or less the same approach as described in the linked question: I check various compiler versions manually and then depending on that I set my CPP_STANDARD_VERSION property, but this seems very fragile to me. Is there really no other way?

cmake function for using a modular library with multiple targets

I develop some library which is mainly some set of interfaces to third-party packages. In my library there are core routines which need to be compiled anyway and a set of optional ones which one need to compile in case of using it in the code.
Let me illustrate it on some example.
Library
|---interfaces
| |---first
| | |---CMakeLists.txt
| | |---...
| |---second
| |---CMakeLists.txt
| |---...
|---core_routines
| |---CMakeLists.txt
| |---...
|---dependencies.cmake
This is a tree of my project. My library is a header-only. I need to write cmake function which one can use to efficiently add required submodules in its project.
Currently I use my own quite ugly solution. Namely, below you can see how my dependencies.cmake file looks like
#first interface dependencies
if(first)
link_directories(...)
include_directories(...)
add_subdirectory(${SOME_PATH}/interfaces/first
"${CMAKE_CURRENT_BINARY_DIR}/first")
endif()
#second interface dependencies
if(second)
...
endif()
#core routines
add_subdirectory(${SOME_PATH}/core_routines/
"${CMAKE_CURRENT_BINARY_DIR}/core_routines")
#function for adding required libs to new target
function(add_new_target new_target)
target_link_libraries(
${new_target} core_lib
)
if(first)
target_link_libraries(
${new_target} first_lib
)
endif()
if(second)
target_link_libraries(
...
)
endif()
endfunction(add_new_target)
Now, when I use my library in some code, the corresponding CMakeLists.txt file looks like
set(first ON)
set(second ON)
include(dependencies.cmake)
add_executable(main.exe main.cpp)
add_new_target(main.exe)
I use this way to compile the example since, as far as I know, one needs to specify link_directories and include_directories before adding new executable and then specify required libraries with target_link_libraries after it. Correct me if I am wrong.
My question is whether it is possible to write something better in this case? I need to keep modular structure of my code because, first, some interfaces require of using libraries which can be not installed on user PC, and, second, the main usage of my library will be in projects with cmake file which includes a lot of different targets, so I need to have some function which can specify required libraries and interfaces depending on user request.
link_directories() and include_directories() are not the modern way. They are too broad. By default, almost everything in modern CMake is target_* based.
Each sub-directory target should already be using target_include_directories() and target_link_libraries() with "PUBLIC" or "INTERFACE" for transitive dependencies, i.e. to carry along what is needed to successfully include and link them.
If these libraries are third party and don't already bundle their transitive dependencies, then you can do it for them using set_target_properties() with the "INTERFACE_INCLUDE_DIRECTORIES" AND "INTERFACE_LINK_LIBRARIES" properties.
Supply a list of all the libraries that client programs need to link. This would be a great deal like the builtin FindBoost module, which takes in a list of components, then prepares variables for each component, plus BOOST_LIBRARIES which is a list of all of the components.
set(my_component_libraries core_lib)
if(first)
list(APPEND my_component_libraries first_lib)
endif()
# etc.
set(my_component_libraries ${my_component_libraries} PARENT_SCOPE)
This way you leave the actual linking up to the users.
If you write your CMake interface to be find_package compatible, you can actually use the COMPONENTS interface to find_package, just like Boost.

What are the possible values for the LANGUAGE variable in CMAKE

I haven't been able to find a list of possible values for the LANGUAGE variable on the CMAKE.org site or anywhere else. Would someone please enumerate the values CMAKE recognises? I specifically need to specify Objective C++.
Just take a look at all the CMakeDetermine<Language>Compiler.cmake scripts CMake ships with.
This would result - in alphabetic order - in the following you could put in the enable_language() call:
ASM
ASM-ATT
ASM-MASM
ASM-NASM
C
CSharp
CUDA
CXX
Fortran
Java
OBJC (Objective C)
OBJCXX (Objective C++)
RC (Windows Resource Compiler)
Swift
Evaluated with CMake Version 3.16
References
enable_language()
Generic rule from makefile to CMake
Update for CMake 3.16 and later: CMake added native support for Objective-C in version 3.16. The corresponding language strings are OBJC and OBJCXX. Thanks to squareskittles for pointing this out.
Original answer: The support for languages varies across platforms.
Currently CMake supports C, CXX and Fortran out of the box on most platforms. There is also support for certain Assemblers on some platforms. For a complete list, check out the contents of the Modules/Platform folder.
The idea is that the language given to the LANGUAGE field of the project command or the enable_language command is just a string, which will then be used by CMake together with the language dependent variables to setup the build system. The Platform scripts shipping with CMake do this configuration for C and C++. In theory, one can add their own language simply by setting the correct variables (although this is quite involved and I do not know of anyone ever successfully doing this).
As for adding support for Objective-C: Since most toolchains use the same compiler for C and Objective-C, you do not need to configure a new language. Simply compile your code as if it was plain C and add the appropriate compiler flags for Objective-C support.
Unfortunately, this is not very comfortable to use and can easily break in corner cases. But until CMake adds explicit support for Objective-C as a first class language, I'm afraid this is as good as it gets.