What should I do in CMakeLists.txt w.r.t. the build type? - cmake

I'm the author of some library which gets built using CMake.
If a user specifies a build type they run cmake - I can oblige that, no problem.
But what is the best practice when a user doesn't specify a build type?
Should I just ignore it?
Should I choose a build type as a fallback/default myself? If so, which?
I've read this Kitware blog entry which suggests a certain approach to the matter and places it in a library dependency. The approach is encapsulated into this module. Should I use that?
So far I've been forcing some specific build type and it's been suggested to me perhaps I shouldn't be doing that.

I think there are two good options:
Warn the user if CMAKE_BUILD_TYPE is unset and the active generator is single-config.
Do nothing. The warning is only helpful to novice users, anyway. It is possible (however unlikely) that an advanced user intended to do this. In this case, the warning is annoying.
To implement (1) correctly, the following code snippet (placed early, ideally after project()) will work:
cmake_minimum_required(VERSION 3.9)
# ...
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT is_multi_config AND NOT DEFINED CMAKE_BUILD_TYPE)
message(WARNING "Must set CMAKE_BUILD_TYPE for single-config generators.")
endif ()

Related

Is CMake "set(XXXX CACHE ...)" a bug when used in a library?

I'm using CMake in my project, as does one of my project's third-party library (included with add_subdirectory() for convenience). I have been having strange build issues, and I think I've tracked them down to the following line in the third-party library's top-level CMakeLists.txt:
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "")
This sets CMAKE_DEBUG_POSTFIX for my entire project (except for any subtrees where it's explicitly set), which breaks my build. Worse, the build behavior is order- and time-dependent, with the value being changed in the middle of my build after a clean build, and not after a rebuild. (Lots of fun to track down.)
The library sets lots of cache variables whose name begins with "XXXX_", where "XXXX" is the name of the library. That's fine by me, as it's unlikely these variables will be used by others' code. But, it seems antisocial to set commonly-used variables globally when your code is meant to be a component of someone else's project. It also is fragile; if I use set(XXXX <aValue>) in my top-level CMakeLists.txt then the library's set(XXXX CACHE...) statements will be ignored.
Instead, the library should just use set(CMAKE_DEBUG_POSTFIX "d"), which sets the variable for all of the library's code tree, and nobody else's.
Is this a bug in the library's build code? Should libraries that aim to be good CMake citizens avoid CACHE variables except for their clearly-named private variables?
For libraries that intend to be usable as a subproject (via add_subdirectory or FetchContent), I would say it is a bug to set such cache variables without a check that the project is top-level. On the other hand, a project that does not intend to be usable this way should explicitly check and issue a fatal error (or maybe an author warning) in this case. So either way, I would argue there's a bug and you should notify the maintainers.
In CMake 3.21+ the variable PROJECT_IS_TOP_LEVEL works. In earlier versions, you can write:
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL)
to get the same variable. Then check:
if (PROJECT_IS_TOP_LEVEL)
# Either this: (AUTHOR_WARNING acceptable, too)
message(FATAL_ERROR "Subproject inclusion not supported")
# or:
set(CMAKE_* ... CACHE ...)
endif ()

Change toolchain in a subfolder with cmake [duplicate]

It seems like CMake is fairly entrenched in its view that there should be one, and only one, CMAKE_CXX_COMPILER for all C++ source files. I can't find a way to override this on a per-target basis. This makes a mix of host-and-cross compiling in a single CMakeLists.txt very difficult with the built-in CMake facilities.
So, my question is: what's the best way to use multiple compilers for the same language (i.e. C++)?
It's impossible to do this with CMake.
CMake only keeps one set of compiler properties which is shared by all targets in a CMakeLists.txt file. If you want to use two compilers, you need to run CMake twice. This is even true for e.g. building 32bit and 64bit binaries from the same compiler toolchain.
The quick-and-dirty way around this is using custom commands. But then you end up with what are basically glorified shell-scripts, which is probably not what you want.
The clean solution is: Don't put them in the same CMakeLists.txt! You can't link between different architectures anyway, so there is no need for them to be in the same file. You may reduce redundancies by refactoring common parts of the CMake scripts into separate files and include() them.
The main disadvantage here is that you lose the ability to build with a single command, but you can solve that by writing a wrapper in your favorite scripting language that takes care of calling the different CMake-makefiles.
You might want to look at ExternalProject:
http://www.kitware.com/media/html/BuildingExternalProjectsWithCMake2.8.html
Not impossible as the top answer suggests. I have the same problem as OP. I have some sources for cross compiling for a raspberry pi pico, and then some unit tests that I am running on my host system.
To make this work, I'm using the very shameful "set" to override the compiler in the CMakeLists.txt for my test folder. Works great.
if(DEFINED ENV{HOST_CXX_COMPILER})
set(CMAKE_CXX_COMPILER $ENV{HOST_CXX_COMPILER})
else()
set(CMAKE_CXX_COMPILER "g++")
endif()
set(CMAKE_CXX_FLAGS "")
The cmake devs/community seems very against using set to change the compiler since for some reason. They assume that you need to use one compiler for the entire project which is an incorrect assumption for embedded systems projects.
My solution above works, and fits the philosophy I think. Users can still change their chosen compiler via environment variables, if it's not set then I do assume g++. set only changes variables for the current scope, so this doesn't affect the rest of the project.
To extend #Bill Hoffman's answer:
Build your project as a super-build, by using some kind of template like the one here https://github.com/Sarcasm/cmake-superbuild
which will configure both the dependencies and your project as an ExternalProject (standalone cmake configure/build/install environment).

How to remove definitions per target in cmake 2.8.12

I am using cmake 2.8.12.
I am using multiple targets with the same definitions by calling add_definitions().
I want to find out how to remove those definitions for single target and replace them by some other definitions or by default definitions like I have not called add_definitions().
I found a solution there:
https://gitlab.kitware.com/cmake/cmake/issues/19796
This is a way to remove the NOMINMAX define:
get_target_property(defs someTarget COMPILE_DEFINITIONS)
list(FILTER defs EXCLUDE REGEX [[^NOMINMAX$]])
set_property(TARGET someTarget PROPERTY COMPILE_DEFINITIONS ${defs})
The better approach here is to add the definitions for single targets rather than globally.
To do so, you should use target_compile_definitions instead of add_definitions
For instance, instead of
add_definitions(-DSOMEMACRO)
add_executable(myExecutable main.cpp)
you would use
add_executable(myExecutable main.cpp)
target_compile_definitions(myExecutable PUBLIC -DSOMEMACRO)
This allows you for a fine-grain control of the definitions, and is usually the preferred way of setting them.
I would argue that adding definitions for every target is easier than removing definitions from some of the targets.
This is a very important thing to be able to do when building large projects and using other 3rd party (e.g. from Github) projects in your own, as they often screw up the compile definitions and end up creating build-breaking problems that require you to selectively remove a definition, without editing the original project itself.
I haven't found a modern, per target way to do this sadly.
However, remove_definitiions is an old way to do it. It works in the sloppy old style of applying to the current subdirectory and below. This causes leakages to your other targets in the same directory frustratingly enough. But sometimes that might be exactly what you want.
One ugly solution is to break out part of your CMakeLists.txt into a separate CMakeLists.txt for the offending targets, and place remove_definitions only in that secondary CMakelists.txt in a subdirectory that quarantines those targets.
Hopefully CMake will add a target_remove_definitions() function.
Here is an example of where I needed this and how I solved it:
# main_directory/CMakeLists.txt
# Add subdir of the offending targets
add_subdirectory(evil_dependencies)
....
# main_directory/evil_dependencies/CMakeLists.txt
# This is required or else, there are headers in the repo
# that #define NOMINMAX which then causes a warning:
# macro redefinition, and warnings as errors = stop the build.
if(MSVC)
remove_definitions(/DNOMINMAX)
endif()
add_subdirectory(some_evil_dependency1)
add_subdirectory(some_evil_dependency2)
For GCC style problems it will look more like remove_definitions(-DSOMEDEFINE). Note, for this old style call, the /D or -D are required, unfortunately. This sadly requires you to write a bunch of extra if-then branches to see if you're in MSVC et. al. or GCC/Clang.
EDIT: See Remi's answer below. It's probably? better than mine, and how I think I'd do it now, mainly. My answer is still useful potentially for some situations where you actually want to remove definitions from a bunch of children folders as well, but Remi's answer is likely to be more elegant most of the time, esp. because of the regex that can make it easier to scan for -D and /D options.
As this is an open issue for CMake, it will likely be fixed any time, obsoleting these answers for newer versions of CMake. That hasn't happened yet (as of 2020-07-16). https://gitlab.kitware.com/cmake/cmake/-/issues/19796

Cmake not setting RPATH when adding link_library with -L

When setting link libraries in the following manner
target_link_libraries (SOME_TARGET -L/somedir -lfoo)
cmake doesn't handle RPATHs. Is using '-L' and '-l' not best practice, or actually plain wrong? When creating my own Find*.cmake I usually use find_library() but the find script I got doesn't do this and resorts to the above form using '-L' and '-l'.
The documentation doesn't really explain how RPATHs are gathered, also the documentation isn't really clear how it handles "-l" and "-L" the only pointer you get is
"Item names starting with -, but not -l or -framework, are treated as
linker flags"
Specifying toolchain-dependent flags like -l and -L is generally not recommended, as it breaks portability and might have different effects than you expect.
The correct way to set the linker path would be the link_directories command.
The idiomatic solution in CMake is to use find_library for locating the library and then pass the full path to the linker, so you do not need to worry about link directories at all.
Now, the RPATH is a different beast, as it also determines where dynamic libraries can be located at runtime. Usually, the default settings work reasonably fine here. If you ever find yourself in the unfortunate situation where it does not, there is a number of target properties and CMake variables influencing this:
There are a few properties used to specify RPATH rules. INSTALL_RPATH
is a semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). INSTALL_RPATH_USE_LINK_PATH
is a boolean that if set to true will append directories in the linker
search path and outside the project to the INSTALL_RPATH.
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic
generation of an rpath allowing the target to run from the build tree.
BUILD_WITH_INSTALL_RPATH is a boolean specifying whether to link the
target in the build tree with the INSTALL_RPATH. This takes precedence
over SKIP_BUILD_RPATH and avoids the need for relinking before
installation. INSTALL_NAME_DIR is a string specifying the directory
portion of the “install_name” field of shared libraries on Mac OSX to
use in the installed targets. When the target is created the values of
the variables CMAKE_INSTALL_RPATH, CMAKE_INSTALL_RPATH_USE_LINK_PATH,
CMAKE_SKIP_BUILD_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH, and
CMAKE_INSTALL_NAME_DIR are used to initialize these properties.
(From the set_target_properties docs)
Also, you might want to have a look at the CMake Wiki page for RPATH handling.
The whole RPATH business is unfortunately rather complex and a thorough explanation would require far more space than is appropriate for a StackOverflow answer, but I hope this is enough to get you started.
Basically, You're using target_link_libraries() wrong. According to documentation, You should provide target, libraries and maybe some CMake specific linkage flags.
For example something like that:
target_link_libraries(my_build_target somedir/foo.so)
If You're using Your own crafted Find*.cmake solutions, it's usualy being done like this:
find_library(foo)
//build main target somewhere here
//now link it:
target_link_libraries(my_build_target ${FOO_LIBRARIES})
NOTE: I assume Your crafted Find*.cmake files follows these guidelines and fills CMake variables like SOMELIB_LIBRARIES, and/or SOMELIB_INCLUDE_DIRS, etc.
NOTE2: for my personal opinion, target_link_directories() is pain in a butt and You should avoid using it if not really needed. It's difficult to maintain and uses paths relative to current source directory.

CMake : how to link a library WITHOUT automatic search function FIND_PACKAGE?

I wonder how to find/link a library without any FIND_PACKAGE.
Assume that we have a "personal" library called testlib :
/perso/testlib/include/testlib1.h
/perso/testlib/include/testlib2.h
/perso/testlib/lib/testlib1.a
/perso/testlib/lib/testlib2.a
How to link it with CMake ?
1) What are the functions to link it directly in the code of the CMakeLists.txt ?
2) How to allow the user to select where are the files ?
3) I have difficulties to understand what is interpreted and what it's not by CMake. For example if you define a variable ${MYVARIABLE_INCLUDE_DIR} or ${MYVARIABLE_LIBRARIES} is "INCLUDE_DIR" or "LIBRARIES" an extension interpreted by CMake or there is no difference if I call this variable ${MYVARIABLE_INCDIR} ?
4) How to do the same procedures (including a "personal" library) if you have a library that contains ten library files or more in the lib directory ?
5) And finally, when you type TARGET_LINK_LIBRARIES(myexecutable gmp), how do you know that the name of the library is "gmp". Why not "Gmp" or "GMP" ? Is the name of the library to put in this function just equal to the .a file minus "lib" and ".a" ? For example libgmp.a -> gmp ? If I want to link a library called libtestlolexample.a, do I have to type TARGET_LINK_LIBRARIES(myexecutable testlolexample) ?
Thank you very much.
You can use target_link_libraries(myexecutable mylib) to link to the library "mylib". The compiler will use its default way to find the specified library (e.g. it will look for libmylib.a on Linux). The compiler will only look in the link_directories(directory1 directory2 ...), so you could try that command to add the required directories to the search path.
When "mylib" is also compiled with CMake this will be recognized and everything should work automatically.
When you want the user to specify a directory you can use a cached CMake variable. set(MYPATH "NOT-DEFINED" CACHE PATH "docstring").
For more complex stuff it is very advisable to write a CMake find module that can be used with find_package. I suggest you take a look at the FindALSA.cmake which can be used as a good starting point.
The interesting part is at the end:
if(ALSA_FOUND)
set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
endif()
mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)
The ALSA_LIBRARY and ALSA_INCLUDE_DIR variables are user configurable and stored in the cache, while ALSA_LIBRARIES and ALSA_INCLUDE_DIRS as well as ALSA_FOUND get computed and are the ones that the user of the find module is supposed to use.
Typically one would use the find module like this:
find_package(ALSA REQUIRED)
include_directories(${ALSA_INCLUDE_DIRS})
target_link_libraries(myexe ${ALSA_LIBRARIES})
I'm sure you can adapt this for your personal library.
Usually when you want to link against a library that doesn't have a find_package module (e.g. it's an uncommon library, or it's your own library), then you can use the basic commands (the find_X commands) to set variables with the paths you need. Then you use those variables as with find_package (include_directories, target_link_libraries).
If you're going to be using this library from multiple packages, you may want to create a find_package module; basically it's using the same commands with certain conventions.
Either of these allow you to specify paths (in the CMake module) to look in, and they allow the user to override the paths (the variables show up as options in ccmake/cmake-gui).
I'd be glad to add an example of one or both of these methods, just let me know what you're looking for.
If you just want a quick-and-dirty solution, you could do this, but I wouldn't recommend it:
include_directories(/perso/testlib/include)
add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable
/perso/testlib/lib/testlib1.a
/perso/testlib/lib/testlib2.a)
Regarding your question about target_link_libraries (#5), you can do it several ways. If you want you can provide the full name (e.g. target_link_libraries(myexe libfoo.a)), but I think it's better (more portable I suppose) to use the short name (e.g. target_link_libraries(myexe foo). You can also include linker flags; I'm not sure where I read it, but I think it may translate the -L and -l flags for different linkers.
For example, if I have a bunch of libraries in the same directory, and I know the names, I might find the directory, store it in a variable, and then do this:
# First, find and set TESTLIB_LIBRARY_DIR, e.g. with find_path
# ...
# This assumes the libraries are e.g. 'libtestlib1.a' and 'libtestlib2.a'
set(TESTLIB_LIBRARIES
-L${TESTLIB_LIBRARY_DIR)
-l testlib1
-l testlib2)
add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable ${TESTLIB_LIBRARIES})
If you want to make your own find_package module (like trenki mentioned, FindALSA.cmake seems to be a good starting point), you can use it by adding the directory to the CMAKE_MODULE_PATH; for example, if you put your module(s) in a cmake/modules/ subdirectory:
# Look for extra CMake modules in a subdirectory of this project
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH})
One possible issue with FindALSA.cmake: I'm not sure CMAKE_CURRENT_LIST_DIR will work. So I think you should make this change (the second work for me in a module I wrote):
# Change this line
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
# To this (with no path/extension it will search the CMake modules path):
include(FindPackageHandleStandardArgs)
And to get the usage of FIND_PACKAGE_HANDLE_STANDARD_ARGS, look at FindPackageHandleStandardArgs.cmake in the CMake Modules directory.
CMake has a good documentation.
Static linkage (if i understand you correct) is archived by passing the STATIC keyword to add_library
I would suggest to not do that (I'm not a CMake expert) but it sounds like the expense would be to large.
There is no difference, ${MYVARIABLE_INCLUDE_DIR} ist just a variable name it whatever you want. But i would suggest that you follow the naming convention.
One libary is always one .lib/.a file so there should be no problem just use the add_library& target_link_libraries& add_dependencies function.
The library name is always the name that you pass to add_library. However Gmp or gMP would be the same as CMake is case intensitive