Backward compatible `add_compile_definitions` - cmake

I am working on fixing some CMake files that have been made with a version newer than 3.10, but should have been coded to be compatible with 3.10.
There is a particular line:
add_compile_definitions(SOME_BOOL_VARIABLE)
that I can not figure out how to change to make it work with version 3.10. The add_definitions(SOME_BOOL_VARIABLE) command was the obvious thing that I tried, but produces errors with "no such file of directory".

From the CMake documentation, the functionality of add_definitions() and add_compile_definitions() is essentially the identical:
Adds preprocessor definitions to the compiler command line for targets in the current directory and below (whether added before or after this command is invoked).
The difference is in the syntax accepted for the command arguments. The older add_definitions() command requires the -D flag be prepended to each definition.
add_definitions(-DSOME_BOOL_VARIABLE)
The newer add_compile_definitions() command (available in CMake 3.12 and above) is cleaner, and does not require the -D flag prefix.
If you are refactoring your code, modern CMake encourages a target-centric approach. Whenever possible, you should prefer the target_compile_definitions() command to add preprocessor definitions to only those targets that require them.
target_compile_definitions(MyLibraryTarget PRIVATE SOME_BOOL_VARIABLE)

Related

How to detect using too new features in CMake?

Prepare the following (erroneous) CMakeLists.txt file:
cmake_minimum_required(VERSION 3.10)
project(foo)
add_executable(foo foo.cpp)
add_compile_definitions(BAR=123)
add_compile_definitions is new in CMake 3.12, so processing the above file in CMake 3.10 will result in an error.
CMake Error at CMakeLists.txt:4 (add_compile_definitions):
Unknown CMake command "add_compile_definitions".
However, using CMake 3.12 or later, no errors or warnings are output.
Therefore, as long as you are using CMake 3.12 or later, you will not notice the error.
(In this case, we can use add_compile_options instead of add_compile_definitions, but that is not the main issue.)
You may say, "you shouldn't write cmake_minimum_required(VERSION 3.10) because you are not using CMake 3.10, you should write the version you are actually using".
However, there may be cases where modifications are made to an existing code base.
Is there any way to realize that when you do so, you inadvertently write something that is not usable in the specified version?
For example, is there a tool like lint that can check for features that are not available in a given version?
Currently, is the only way to do this is to install the specified version of CMake?
You have to test with the minimum required version. But even if no error occurs, your test might be incomplete, because you only test these parts of the code, that you are actually running. If your setup does not provide an optional dependency or you did not set a flag, the code executed for this dependency or flag will not be tested.
Depending on your setup, it makes sense to have a continuous testing (GitLab, Jenkins, GitHub actions) that runs your CMake code with CMake in the minimum required version. Then you get early warning that someone added code that is above the required CMake version and you should revert it or increase the requirements.
It is really not a satisfying answer and in general not a satisfying situation.
usr1234567 wrote a good answer, but let me add an additional point:
I think you (and many others; you're in good company) are misunderstanding the guarantee made by cmake_minimum_required(VERSION X). Many people believe it means the following:
This project will build with version X.
That is not the case at all. What it actually promises is:
If this project builds with version X, then it will build on versions Y > X.
That is to say, it is a backwards compatibility guarantee, not a forwards compatibility guarantee. You cannot author a project with a newer version of CMake and expect it to work with older versions without testing it.

How to use cpplint code style checking with CMake?

The only online resources I have found are the CMake documentation on CMAKE_<LANG>_CPPLINT (link here) and this example (link here), but I cannot figure out how to actually use it inside a CMakeLists.txt file.
I tried the example provided, but I can't make it work. FYI, I installed cpplint as explained here.
As of now, I can run the cpplint python script inside CMakeLists.txt using this CMake command:
execute_process(COMMAND cpplint path/To/File/To/Analyse.cpp)
However, I am pretty sure that this is not the right way to do this.
Recommended way to use static analysis tools with CMake was presented in Daniel Pffeifer's "Effective Cmake" (https://www.youtube.com/watch?v=rLopVhns4Zs&amp=&t=77m13s).
You can either define it when calling cmake, eg.:
cmake "-DCMAKE_CXX_CPPLINT=cpplint" ..
or put it into CMakeLists.txt:
set(CMAKE_CXX_CPPLINT "cpplint")
Recommended option is the first one (we shouldn't define in a project what isn't a project requirement).
CMake will call cpplint for each file it compiles. You can pass extra arguments after semicolon (e.g. -DCMAKE_CXX_CPPLINT=cpplint;--linelength=100).
Downsides of this method:
Errors count will not get accumulated (because cpplint is invoked for each file separately).
It will not check header files (as opposed to what D. Pffeifer says in his presentation, include files are not being scanned by cpplint).
Note that you can use other static analysis tools the same way:
Clan Tidy "-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-3.9;-checks=*"
CppCheck "-DCMAKE_CXX_CPPCHECK=/usr/bin/cppcheck;--std=c++11"
IWYU "-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=/usr/bin/iwyu;--transitive_includes_only"
LWYU cmake -DCMAKE_LINK_WHAT_YOU_USE=TRUE
clazy
Some of them will require "compilation database" (set(CMAKE_EXPORT_COMPILE_COMMANDS ON)).
I failed to use CMAKE_<LANG>_CPPLINT to check code style.
I make it by using add_custom_target.
download cpplint.py
then download cpplint.cmake or write yourselt.
Suppose that there is a source code directory named src in your project, code those statements into your CMakeLists.txt.
aux_source_directory(${CMAKE_SOURCE_DIR}/src src)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}) #I put cpplint.cmake in $CMAKE_SOURCE_DIR
include(cpplint)
add_style_check_target(phoenix-cpplint "${src}")
Note:
you should pass the whole list, so use "${src}" instead of ${src}.
By default nothing depends on the custom target, see add_custom_target.
If there's still some problem, debug your CMakeLists.txt.
I have been struggling with the same problem.
I tried it with CMake 3.10.2 and the comment by user2449761 is still true. Using set(CMAKE_CXX_CPPLINT "cpplint") still does not check any header files.
The answer by kgbook does not work anymore, since aux_source_directory does not list the header files. You can, however, use
get_target_property(src staticcodecheck SOURCES)
That will give you all the non-system headers. The rest can be kept the same. As for running cpplint at a specific time, you might try
add_custom_command(TARGET ${TARGET}
PRE_BUILD
...
That will replace add_custom_target(${TARGET_NAME}... in his cpplint.cmake.
Hope this helps.
The following is how I am running cpplint on all files in the src directory for a project.
file(GLOB_RECURSE SRC_FILES "${PROJECT_SOURCE_DIR}/src/**/*")
add_custom_command(TARGET target PRE_BUILD COMMAND cpplint ${SRC_FILES})
This runs every time, it fails the build when there are cpplint issues, and it runs on all files in the src directory. You may also want to consider adding cpplint specific arguments to the command, such as --quiet or --extensions for example.

Is it possible to alter CMAKE_MODULE_PATH from CMake commandline?

Edit: The accepted answer actually shows that it is pretty normally possible to set CMAKE_MODULE_PATH as any other CMake variable e.g. via the -DCMAKE_MODULE_PATH path CLI parameter. It seems that in my case there is some included CMake script that calls set(CMAKE_MODULE_PATH /library_path), which erases all previous paths set to the variable. That's why I couldn't get the variable to do what I wanted it to do. I'll leave the question here in case anybody else faces this kind of situation.
I'm building a (3rd party) project that uses the Protobuf library (but this question is general). My system has a system-wide install of a newer version of Protobuf than the project is compatible with. So I've downloaded and compiled from source an older version of Protobuf.
The project uses CMake, and in its CMakeLists.txt, there is:
find_package(Protobuf REQUIRED)
Which, however, finds the (incompatible) system install. Of course, CMake doesn't know about my custom build of Protobuf. But how do I tell it?
I've created a FindProtobuf.cmake file in, say, ~/usr/share/cmake-3.0/Modules/ and want the build process to use this one for finding Protobuf. But I haven't succeeded forcing CMake to pick up this one and not the system one. I think the reason is quite obvious from the CMake docs of find_package:
The command has two modes by which it searches for packages: “Module” mode and “Config” mode. Module mode is available when the command is invoked with the above reduced signature. CMake searches for a file called Find<package>.cmake in the CMAKE_MODULE_PATH followed by the CMake installation. If the file is found, it is read and processed by CMake. ... If no module is found and the MODULE option is not given the command proceeds to Config mode.
So until I succeed to change CMAKE_MODULE_PATH, CMake will just pick up the FindProtobuf.cmake installed to the default system path and won't ever proceed to the "Config" mode where I could probably make use of CMAKE_PREFIX_PATH.
It's important for me to not edit the CMakeLists.txt since it belongs to a 3rd party project I don't maintain.
What I've tried (all without success):
calling CMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules cmake ... (the env. variable is not "transferred" to the CMake variable with the same name)
calling cmake -DCMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules ... (doesn't work, probably by design?)
calling Protobuf_DIR=path/to/my/protobuf cmake ... (the project doesn't support this kind of override for Protobuf)
It seems to me that, unfortunately, the only way to alter the CMAKE_MODULE_PATH used by find_package is to alter it from within CMakeLists.txt, which is exactly what I want to avoid.
Do you have any ideas/workarounds on how not to touch the CMakeLists.txt and still convince find_package to find my custom Protobuf?
For reference, the CMake part of this project is on github .
As a direct answer to your question, yes, you can set CMAKE_MODULE_PATH at the command line by running cmake -DCMAKE_MODULE_PATH=/some/path -S /path/to/src -B /path/to/build.
But that probably doesn't do what you want it to do; see below.
The Bitbucket link you supplied is dead, but here are a few suggestions that might help.
Avoid writing your own find modules, especially when the upstream supplies CMake config modules.
You can direct CMake to your custom Protobuf installation by setting one of CMAKE_PREFIX_PATH or Protobuf_ROOT (v3.12+) to the Protobuf install root.
You can tell find_package to try CONFIG mode first by setting CMAKE_FIND_PACKAGE_PREFER_CONFIG to true (v3.15+). Then set Protobuf_DIR to the directory containing ProtobufConfig.cmake.
Failing all else, you can manually set the variables documented in CMake's own FindProtobuf module, here: https://cmake.org/cmake/help/latest/module/FindProtobuf.html
All these variables can be set at the configure command line with the -D flag.
There are very few environment variables that populate CMake variables to start and I would avoid relying on them. There is an exhaustive list here: https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html. CMAKE_MODULE_PATH is not among them.

Detect current CMake version using CMake

I am working on a project that uses CMake. The top CMakeLists.txt file contains the following line:
cmake_minimum_required(VERSION 3.7.2) # Kittens will die if you switch to an earlier version of CMake. We suggest using CMake 3.8.0.
I want to force all developers to switch to CMake 3.8.0, but for some reasons, not all developers have administration rights and are not able to switch from 3.7.2 to 3.8.0 immediately. Actually, we do not need any new features of version 3.8.0, but our policy is to use always the newest and greatest tools to prevent "porting up" problems in the future - for instance switching fast from Qt4 to Qt5 was a good decission in the past - I know switching always to the newest libraries and tools has also some drawbacks as discussed here, but we want to do it this way.
Because of this, instead of forcing everyone to use version 3.8.0, I'd like to output a warning message if CMake 3.7.2 is used. Somehow like this:
# not working - just pseudocode
if(CMAKE_VERSION == "3.7.2")
message("Please consider to switch to CMake 3.8.0")
endif()
I tried to read the VERSION variable, but this does not work. Does anyone now how this check can be achieved?
There exist a few variables for that, described here:
CMAKE_MAJOR_VERSION
major version number for CMake, e.g. the "2" in CMake 2.4.3
CMAKE_MINOR_VERSION
minor version number for CMake, e.g. the "4" in CMake 2.4.3
CMAKE_PATCH_VERSION
patch version number for CMake, e.g. the "3" in CMake 2.4.3
Also, the variable CMAKE_VERSION contains the string for the version.
In your case, you would, for instance, use the following:
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
message("Please consider to switch to CMake 3.8.0")
endif()
Other comparison operators are VERSION_EQUAL and VERSION_GREATER.

What is the default search path for find_package in windows using cmake?

I am porting some code over to windows and my cmake checks for the package Libavahi using
find_package(Libavahi)
I have the headers, dll, etc. but I'm not sure where to place these such that cmake will find them.
Where can I put these files to be found by cmake? They're in a folder called usr.
I see that the module path is specified using:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
but I'm wondering if there is a default location that will be searched as well
The CMake manual fully specifies the rather complicated search order for the different find_* commands. Unfortunately, since Windows lacks a default directory structure à la /usr/local/lib, it is hard to come up with reasonable defaults here.
One of the most reliable ways of managing directories is through environment variable hints. You simply add an $ENV{MY_VAR} to the HINTS section of the find command and then document that environment variable in your project's readme. Most users that are capable of compiling a C++ program know how to use environment variables, and it is way more convenient than having to give the path on the command line every time (although it never hurts to leave that as an additional option).
For find_package CMake offers a special mechanism on Windows called the package registry. CMake maintains a list of package information in the Windows registry under HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\. Packages build from source can register there using the export command. Other projects build later on the same machine will then be able to find that package without additional configuration. This is quite powerful if you need to build a lot of interdependent projects from source on the same machine.
Update: Starting with version 3.12, CMake now implicitly considers the <PackageName>_Root environment variable a HINT for every find_package call.
In the newer versions of cmake, you can use the --debug-find option to list the directories that cmake is searching through. Somethin like:
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TOOLS=ON --debug-find .