Summary
I would like to make a (boolean) property unset in CMake such that get_property(... SET) returns 0 although the property was set in the first place. Settings the property to 0, FALSE, or OFF is not sufficient. How to achieve that?
Background
I have some custom commands which generate some source files. CMake automatically assigns the GENERATED source file property to those. This seems to make the GNU Makefile generator to add a rule to remove these files with make clean.
However, I do not want those files to be cleaned up, because once they exist the user may edit them. The custom command should only make sure they become generated if they do not exist. Generating them at configure-time with execute_process instead of the custom command is not feasible.
Setting GENERATED to 0, FALSE, or OFF did not exclude the sources from clean.
Versions
OS: Ubuntu 16.04
CMake: 3.5.1 (from Ubuntu 16.04 repository)
I'm currently bound to Ubuntu 16.04 and getting all users to use a more recent CMake version should be avoided if possible.
You need to set it to nothing:
# test unsetting a property
set_property(TARGET Properties PROPERTY TARGETTEST)
get_property(TARGETRESULT TARGET Properties PROPERTY TARGETTEST SET)
if (TARGETRESULT)
message(SEND_ERROR "Error: target prop not unset, "
"result is TARGETRESULT=${TARGETRESULT}")
endif ()
(From https://github.com/Kitware/CMake/blob/master/Tests/Properties/CMakeLists.txt)
Related
When google-ing export CMAKE_PREFIX_PATH, you will often find the suggestion to cure the problem that CMake doesn't find this or that package by setting an environment variable:
export CMAKE_PREFIX_PATH=/path/to/package
I've convinced myself that setting this environment variable has the effect of appending /path/to/package to the list of paths specified in the CMake call using
cmake -DCMAKE_PREFIX_PATH="/path/to/package1;..." .
(I've tried this on macOS Catalina and Ubuntu 18.04 using CMake 3.15.5 and 3.16.0, respectively.)
The documentation doesn't mention any of this. In fact, it simply states:
By default this [CMAKE_PREFIX_PATH] is empty. It is intended to be set by the project.
(see CMake documentation). There's no mention of the effect of setting the environment variable.
This raises two questions:
Is this effect of setting the environment variable intended? Where is it documented? Is this the canonical way of adding prefix paths to all projects build in the environment?
As the documentation says, "It [CMAKE_PREFIX_PATH] is intended to be set by the project". Is there any reason why there shouldn't be a way to set a default CMAKE_PREFIX_PATH?
The effect of setting it as an environment variable is, by default, no effect. CMake defines it's own variables (those set with the set() command, or the -D command line argument) in a file called CMakeCache.txt in the root of your CMake project. Those are the variables which will affect your cmake script.
In order to access and environment variable in CMake, you need to specify the ENV Syntax
$ENV{VAR}
Hence, even if you set a CMAKE_PREFIX_PATH environment variable, it will have no effect unless you explicitly use it in you CMakeLists.txt.
(Edit: I have only verified this behavior on Windows 10 with CMake 3.16.3)
Setting the CMAKE_PREFIX_PATH environment variable works in finding things with function find_file, find_path, etc.
Just because by default these functions use the CMAKE_PREFIX_PATH environment variable as a path hint as well as the CMAKE_PREFIX_PATH variable set via -DCMAKE_PREFIX_PATH=xx or set function.
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.
I want to set a particular MPI compiler (mpiifort) with CMake. Well, not the compiler, but get the libraries and include directories from it. But there is also mpif90 in the path, which uses gfortran under the hood, and has a different set of include dirs and libraries. It seems the FindMPI module in CMake insists on locating mpif90 first and therefore sets the wrong paths.
I've tried setting MPI_Fortran_COMPILER=mpiifort in the command line, or setting FC=mpiifort, but none works. So far the only workaround I've found is creating a symlink mpif90 -> mpiifort in the current directory and adding _MPI_PREFIX_PATH=.. Any other ideas?
EDIT: I had tried the environment variable MPI_Fortran_COMPILER, but I had to set the CMake variable instead. So this worked:
FC=ifort CC=icc cmake -D MPI_Fortran_COMPILER=mpiifort ...
According to the source here, if setting MPI_Fortran_COMPILER does not work, then you could simply set MPI_Fortran_LIBRARIES and MPI_Fortran_INCLUDE_PATH.
I'm trying to test a project on a cluster where I can't install some libraries in the default locations, so I'm trying to override the default CMake search path with the CMAKE_INCLUDE_PATH environment variable.
Unfortunately it doesn't seem to be picked up. I'm having to set the path explicitly with
include_directories("." $ENV{CMAKE_INCLUDE_PATH})
but this seems like a bit of a hack. So I have two questions:
Is this expected behavior?
Is there some cleaner way to add a directory to CMake's include path via an environment variable?
First of all, there is a predefined cmake variable CMAKE_INCLUDE_PATH variable that is a ";-list of directories specifying a search path for the find_file() and find_path() commands." This is not meant to specify the compiler include path.
Secondly, good use of cmake should not involve environment variables. To the extent you can, you should use the conventional cmake find_package to configure your build paths. When you need to explicitly add a path to the compiler include search path, then, yes, include_directories is what you need. But you should a cmake cache variable rather than environment variable. For details on setting a cache variable, see this page, which says
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
For your example this becomes:
set(MYINCLUDE /usr/local/foo/include CACHE PATH "path to the foo include directory")
include_directories(${MYINCLUDE})
Then if you need to override the default /usr/local/foo/include, you may specify it with the command line used when invoking cmake; e.g., cmake -DMYINCLUDE=/home/foo/include .
In my CMake project, I provide a default path to Boost editable by the user.
set(PATH_BOOST_DEFAULT "/softs/boost/${BOOST_VER}/${ARCH}/gcc/${GCCVER}")
set(PATH_BOOST "${PATH_BOOST_DEFAULT}" CACHE PATH "Default path to Boost")
After that, I try to find the libs with:
set(BOOST_ROOT "${PATH_BOOST}")
set(Boost_USE_MULTITHREAD ON)
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost 1.53.0 REQUIRED COMPONENTS thread system)
This works fine and a lot of cache variables like Boost_INCLUDE_DIR, Boost_LIBRARY_DIRS or Boost_THREAD_LIBRARY_DEBUG are generated.
My problem comes when I try to modify the cache variable PATH_BOOST: the cache variables generated by FindBoost.cmake aren't updated. The script FindBoost.cmake seems to be called again (it print messages about found components). I think the variables like Boost_INCLUDE_DIR are not updated because they are in cache.
Is there a way to say to CMake "if the path is modified by the user, re-find the package by forcing the cache variables"?
Also, is there a nicer way to detect a cache variable was just modified than the following ugly idea?
set(MY_VAR ${MY_VAR_DEFAULT} CACHE TYPE "")
if(NOT DEFINED MY_VAR_copy)
set(MY_VAR_copy ${MY_VAR} CACHE INTERNAL "")
mark_as_advanced(FORCE MY_VAR_copy)
endif()
if(NOT "${MY_VAR}" STREQUAL "${MY_VAR_copy}")
# my_var is modified : do something
set(MY_VAR_copy ${MY_VAR} CACHE INTERNAL "")
endif()
I think I've got the same problem as you. My setup tries to find a specific version of a package:
set (MYPACK_REQUIRED_VERSION 1.2.3)
find_package (mypack ${MYPACK_REQUIRED_VERSION} EXACT)
The package config script sets the cached variable MYPACK_LIBRARIES which then is used at a later stage. However when I change the MYPACK_REQUIRED_VERSION variable cmake still use the old MYPACK_LIBRARIES instead of trying to look for the new version.
I think I've solved the problem now by unsetting this cache variable:
set (MYPACK_REQUIRED_VERSION 1.2.3)
unset (MYPACK_LIBRARIES CACHE)
find_package (mypack ${MYPACK_REQUIRED_VERSION} EXACT)
This seems to trigger the find_package procedure again in my case. There are some finer details of the find_package procedure that I don't completely understand so this might not work in your case, but it might be worth a try.
I've got the same problem as you.My project is a cross-compiled project that develops debugging on x86 machines and deploys and runs on aarch64 machines, depending on the x86 and aarch64 versions of the same package, but cmake doesn't seem to re-search for packages when switching architectures.
I noticed that cmake's documentation mentions:
Once one of the calls succeeds the result variable will be set and stored in the cache so that no call will search again.
By default the value stored in the result variable will be the path at which the file is found.
So I find the cached value in CMakeCache.txt, value is just link packageName_DIR.
Solution is:
unset(packageName_DIR CACHE)
find_package(packageName)
if(packageName_FOUND)
#do something
endif()
then each time you reload the CMakeLists, it will re-find the package.
Or you can set a flag in cache, if the flag changed, unset packageName_DIR.
Note that if some cache variables are set in the package, you also need to add unset, You may need to look at the package's FindpackageName.cmake or CmakeCache.txt to see if there are extra cache variables. For example,
packageName_LIBRARIES contain LIB1,LIB2,LIB3, you need
unset(LIB1_LIBRARY CACHE)
unset(LIB2_LIBRARY CACHE)
unset(LIB3_LIBRARY CACHE)
unset(LIB1_INCLUDE_DIR CACHE)
unset(LIB2_INCLUDE_DIR CACHE)
unset(LIB3_INCLUDE_DIR CACHE)
If none of the above works, there's a simplest and crudest way is to delete the CMakeCache.txt file (which is usually in the cmake build root directory) and reload CMakeLists.txt.