Cannot set CMAKE_WARN_DEPRECATED inside the CMakeLists.txt - cmake

I'm building Googletest as a dependency of my code. Unfortunately Googletest requires very old versions of CMake, which triggers a lot of deprecation warning. I'd like to get rid of them.
I can successfully hide the warnings through command line flags like these:
cmake -Wno-deprecated ..
cmake -DCMAKE_WARN_DEPRECATED=OFF ..
However, I'd prefer to have that set inside the CMakeLists.txt. But I'm not sure how to specify that. The following two commands didn't help:
set(CMAKE_WARN_DEPRECATED OFF)
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL)
Any ideas? Also, is there maybe a way to have that setting for a limited scope, like e.g. one subdirectory?

Found it out. A little extra flag did the trick:
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE)
Not sure though if that's the recommended way to go.

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).

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

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 ()

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 and clang_complete

I'm wanting to setup my CMakeLists.txt file so that it can generate the .clang_complete file required by the vim plugin clang_complete.
Ordinarily, you would do this by passing a parameter to the python script it supplies with the compiler and all of the parameters for compilation. Note that I am omitting the actual directory cc_args.py is in to save on space.
cc_args.py gcc test.c -o test -I~/IncludeDirs/
You can also do this during the make phase...
make CC='cc_args.py gcc' CXX='cc_args.py g++'
However, I am unsure of how to (if it is possible to) set this up within a CMakeLists.txt file. It's really annoying to have to type this in every time I want to setup clang_complete. The reason why I want to do it this way, is because I have multiple projects that I use a custom script to build the CMakeLists.txt file, so having to write a script for each one or manually place a generic one is a step I'd like to avoid.
I've tried a couple of things that have so far have come up with errors.
I've tried setting CMAKE_CC_COMPILER and CMAKE_CXX_COMPILER to lines similar to the first i.e. "cc_args.py g++". The errors that come up here say that It can't find the compiler (which is understandable).
The next thing I tried was setting the Compiler variables just to the cc_args.py and adding a flag for the actual compiler: suffice to say, that failed horribly. CMake said that it couldn't compile a test program (considering the script isn't a compiler, and the tests don't use the flags I set, I'm not surprised at this).
So without writing any other external scripts that require moving around, is there anyone that can think of a way that can do this?
The solution is to set the CXX environment variable before executing cmake. Something like that:
CXX="$HOME/.vim/bin/cc_args.py clang++" cmake ..
make
See http://www.guyrutenberg.com/2013/01/29/vim-creating-clang_complete-using-cmake/ for more details.
I know you said "without writing any other external scripts," but it seems like you just need a one-liner:
exec cc_args.py g++
And then set that file as your CMAKE_CXX_COMPILER. You could even use CMake's file() function to write the one-liner at build time if you don't want to have to distribute it.