CMake requires me to manually copy CMAKE_INCLUDE_PATH - cmake

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 .

Related

Add to cmake search path [duplicate]

In CMake, is there a way to make built-in Find scripts to prioritize a custom directory that we specify? Because especially in windows, module finding scripts usually can't detect the module in, for example visual studio directories. Therefore I usually have to manually set the paths for the external libraries which is pretty tiring. Instead, I want those scripts to look in a custom directory, let's say 'dependencies' folder in the main project first so that I can directly put those externals in that folder which is much easier than putting them into the VC folder or manually setting the paths.
Setting CMAKE_PREFIX_PATH variable serves exactly these purposes: hinting find_* function about location of searched item.
While description of this variable doesn't note about find_package function, the variable affects it indirectly: the most of Find<name>.cmake scripts use find_library and find_path functions. Note, that all find_* functions have precise algorithm for search items, and paths constructed with CMAKE_PREFIX_PATH are checked before system ones.
Moreover, CMAKE_PREFIX_PATH affects some other search procedures. E.g., if 3rd party package provides <name>Config.cmake script instead of Find<name>.cmake one, this script is also searched for using this variable. pkg_check_modules also uses CMAKE_PREFIX_PATH to search for .pc files describing the package.
CMAKE_PREFIX_PATH variable can be set as environment one (in platform-depending and usage-specific way), as parameter to cmake call:
cmake -DCMAKE_PREFIX_PATH=<additional-path> <other-parameters>
or within CMakeLists.txt file. In the last case it is better to append search directories, so user of you package is able to set the variable too for search packages not shipped with your project:
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/dependencies")
Note, that variable CMAKE_PREFIX_PATH doesn't affect searching for FindXXX.cmake script itself. For specifying the directory where the script is located, use CMAKE_MODULE_PATH variable.

What's the effect of export CMAKE_PREFIX_PATH=...?

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.

How to pass variable to cpack?

I have a cmake project which one of the install targets is a collection of files. This files change depending on the configuration (Release, Debug...).
I would like to be able to install the files like so:
install(DIRECTORY $<TARGET_FILE_DIR:tgt>
DESTINATION bin
COMPONENT files)
But cmake does not support that. Generator variables do not apply to DIRECTORY. So I was wondering if there is a way to either save the directory somewhere. Either the cache or a file and then load it into cpack.
So I guess the question is how to pass a variable from cmake to cpack?
This is a rather late answer, but I happened upon this question trying to solve a somewhat different problem that could also be summarized as: "How do I pass a variable to CPack?" In my case, I was making this call from a customized version of CPackDeb.cmake copied to my workspace:
find_program(OPKG_CMD NAMES opkg-build HINTS "${OPKG_HINT}")
# ^^^^^^^^^^^^
# This is what I wanted to pass to CPack
I was setting OPKG_HINT in a file included from my top-level CMakeLists.txt, but it was not getting passed through to cpack; the above find_program() invocation was seeing an empty string for OPKG_HINT.
The solution turned out to be stupid simple: just prepend CPACK_ to the variable name!
If I do this in CMakeLists.txt:
set(CPACK_OPKG_HINT "${_sysroot_top}/aarch64-poky-linux/usr/bin")
then I can put this in my CPackDeb.cmake file and it works fine:
find_program(OPKG_CMD NAMES opkg-build HINTS "${CPACK_OPKG_HINT}")
Anyway, this wound up being a bit of an X-Y problem for the OP, but... if you really need to set a variable at CMake time in such a way that it's accessible to cpack, prefixing the variable name with CPACK_ seems to do the trick nicely...
The following setup work if you use a "single-configuration generators (such as make and Ninja)" and call CMake with
cmake -DCMAKE_BUILD_TYPE=Release <source_dir>
https://cmake.org/cmake/help/v3.0/variable/CMAKE_BUILD_TYPE.html
You can define the ${dir} variable in another way if you like.
IF (CMAKE_BUILD_TYPE STREQUAL "Release")
SET(dir release_dir)
ELSE()
SET(dir debug_dir)
ENDIF()
INSTALL(DIRECTORY ${dir} DESTINATION bin COMPONENT files)
Until now this seems to be the best answer (from someone on the cmake mail list)
install(DIRECTORY path/to/Debug/dir
DESTINATION bin
CONFIGURATIONS Debug
COMPONENT files
)
install(DIRECTORY path/to/Release/dir
DESTINATION bin
CONFIGURATIONS Release
COMPONENT files
)
CMake 3.5 supports generator expressions for the DIRECTORY arguments. See installing directories.

CMake generated makefile does not expand all `make` variables

I have a projects that consists of modules that are built separately. Some modules use make only, others use cmake.
The top project has a makefile, which is used to trigger the build of the submodules. The makefile at the top defines make variables for pathes to libs, includes, and binaries. The submodules then use these variables.
My problem is that I cannot pass all these make variables to the projects that use cmake.
For the include directory, this work-around in CMakeLists.txt works:
INCLUDE_DIRECTORIES("/$(INCLUDE_DIR)")
The string is passed as is all the way to the generated makefile, and is then expanded by make upon building. The leading slash is needed. Without it, the content of INCLUDE_DIR gets prefixed by the cmake project root (it is then considered a relative path).
This solution does not work for linking:
LINK_DIRECTORIES("/$(LIB_DIR)")
The link command that I see upon building has the literal string -L/$(LIB_DIR). Removing the leading slash gives a warning about relative paths, and the link command has still the unexpanded $LIB_DIR.
How do I make sure that the expansion works for the make variable LIB_DIR passed though cmake to the generated makefile? Since it is make that runs the linker (I think?) I don't see why it wouldn't expand.
Is there another, less ugly solution, solution for fixing it for INCLUDE_DIR as well?
A backup solution is to let the top makefile define CMAKE variables. It should work fine, and is probably what I'll do if I can't make it work otherwise. But it would be nice to just define these variables once and make everything else take them into account.
One option is to use an environment variable to set a CMake variable. For example:
set (INCLUDE_DIR $ENV{INCLUDE_DIR})
then use the CMake variable like other CMake variables:
INCLUDE_DIRECTORIES(${INCLUDE_DIR})
If you want the variable to show up in cmake-gui or ccmake you can put it in the cache using the following:
set(INCLUDE_DIR $ENV{INCLUDE_DIR} CACHE PATH "An environment variable containing a path" FORCE)
for my projects I have wrapped the concept in a cmake function:
function( define_from_environment VariableName PackageName)
if (NOT DEFINED ${VariableName})
message( STATUS "${VariableName}=$ENV{${VariableName}}" )
if (NOT "$ENV{${VariableName}}" STREQUAL "")
set(${VariableName} $ENV{${VariableName}} CACHE PATH "Set the path variable ${VariableName} for ${PackageName}" FORCE)
endif (NOT "$ENV{${VariableName}}" STREQUAL "")
endif(NOT DEFINED ${VariableName})
endfunction( define_from_environment)
An example usage of this CMake function is
define_from_environment(GDCM_DIR GDCM)
which sets the CMake variable GDCM_DIR to a CMake cache variable if the environment variable GDCM_DIR is set.

Install path for cmake modules

So if I'm making a package with cmake/cpack, there must be a variable already set in cmake that tells it where to put the *.cmake files that will be used by find_package for my module...right?
What's that variable?
According to the documentation of find_package, it is either CMAKE_MODULE_PATH, but I would not recommend that, otherwise you can put your <name>-config.cmake files anywhere, as long as you specify the path in your find_package call.
Otherwise system-wide paths might be set, being CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_FRAMEWORK_PATH andCMAKE_SYSTEM_APPBUNDLE_PATH.
from the documentation (removed unnecessary information):
find_package(<package> ...
[CONFIG]
...
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
...)
It will search for <NAMES>-config.cmake in all PATHS and all HINTS (in that order), as well as predefines CMAKE Paths.
The exact search order (from the documentation cited above):
Search paths specified in cmake-specific cache variables. These are intended to be used on the command line with a -DVAR=value. This can be skipped if NO_CMAKE_PATH is passed
Search paths specified in cmake-specific environment variables. These are intended to be set in the user’s shell configuration. This can be skipped if NO_CMAKE_ENVIRONMENT_PATH is passed
Search paths specified by the HINTS option. These should be paths computed by system introspection, such as a hint provided by the location of another item already found. Hard-coded guesses should be specified with the PATHS option.
Search the standard system environment variables. This can be skipped if NO_SYSTEM_ENVIRONMENT_PATH is passed. Path entries ending in /bin or /sbin are automatically converted to their parent directories
The Variable
I've found the variable named CMAKE_ROOT to include the path to the default cmake modules:
CMAKE_ROOT=/usr/share/cmake-3.10
This path is what it is set to under Ubuntu 18.04 which is correct compared to the cmake version on that platform:
$ cmake --version
cmake version 3.10.2
CMake suite maintained and supported by Kitware (kitware.com/cmake).
How do you find such hidden variables?
A while back, I stole a piece of code I found on stackoverflow which I can use to display all the variables at a given point in the processing of my CMakeLists.txt files. I create a command I called DumpCMakeVariables.
To use it (assuming the Modules directory is somehow accessible):
find_package(DumpCMakeVariables)
DumpCMakeVariables()
If you have an idea of the variable name, then you can include a valid regular expression as the parameter of that function. To only list variables that start with "CMAKE_" write:
DumpCMakeVariables("^CMAKE_")
Then re-run your configuration process, something like this:
cmake <path to source> | less -S
and search for a specific variable or what your think the value should be. In out case here:
/usr.share.cmake
That will eventually point us to CMAKE_ROOT.
Other Installation Path
Note that I also install files directly under:
/usr/share/cmake/<name>/<name>Config.cmake
And if you want to include a version:
/usr/share/cmake/<name>/<name>ConfigVersion.cmake
where <name> is generally Camel Case.
Having such a directory allows you to have many sub-.cmake files that your main configuration uses to find this or that. So it is a way to make it much cleaner if you need many files instead of just one.
That being, that /usr/share/cmake directory works great. However, certain types of modules should certainly be installed under CMAKE_ROOT to 100% work as expected.
Catch2 also uses that scheme:
# This variable is used in some subdirectories, so we need it here, rather
# than later in the install block
set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2")