CMake generated makefile does not expand all `make` variables - cmake

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.

Related

Is it possible to refer to an additional .txt-file inside the CMakeLists.txt? [duplicate]

I use some libraries that I don't want built as part of every project that uses them. A very understandable example is LLVM, which has 78 static libraries. Even having the cmake code to find and import these in every cmakefile is excessive.
The obvious solution seems to be to use the "include" command, and to factor out relevant chunks of cmake script into *.cmake files, and set up a CMAKE_MODULE_PATH environment variable.
Except it just plain doesn't work. Cmake doesn't find the file I specify in the include command.
On the off-chance, I even tried specifying the path in the environment variable several ways - once with backslashes, once with forward slashes... - and I restarted the command prompt each time and checked that the environment variable was present and correct.
In the cmake manual, it kind of implies that a "file" is different from a "module" - only a module gets the automatic add-the-extension-and-search-the-path treatment. But there is no explanation of what the difference is. I guessed that the missing extension might be enough (as with standard modules) but clearly it isn't.
Searching the manual for "module" isn't much help as the word seems to be overloaded. For example, a module is also a dynamic library loaded using LoadLibrary/dl_open.
Can anyone explain what the difference is between a file and a module in this context, and how I create my own module so that the cmake include command can find and use it?
I'm using cmake 2.8.1 on Windows.
EDIT
I'm pretty confident that the problem here is not understanding how cmake is supposed to work. I think what I should be doing is writing something that find_package can work with.
As things stand though, I'm still some way from being able to answer my own question.
I believe that a CMake 'module' is simply a file that can be used with the find_package directive. That is, when you run find_package(Module), it searches for a file in the MODULE_PATH that is named FindModule.cmake.
That said, if you include a file without the extension, it too will search through your MODULE_PATH for that file.cmake. In a CMake project I'm working on, I have a very similar directory structure as to what you propose.
+ root/
+ CMakeLists.txt
+ cmake/
| + FindMatlab.cmake
| + TestInline.cmake
| + stdint.cmake
+ src/
+ include/
In CMakeLists.txt I have:
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package (Matlab) # runs FindMatlab.cmake
include(TestInline) # defines a macro:
test_inline (CONFIG_C_INLINE)
include(stdint) # simply executes flat CMake code
Perhaps your issue is that you are trying to define the Module path from the environment. Instead, try to simply append to it within the very CMakeList you try to access the modules/files.
I had this same question after reading the CMake include() command documentation. It states:
Load and run CMake code from the file given. [...snip for brevity...] If a module is specified instead of a file, the file with name .cmake is searched first in CMAKE_MODULE_PATH, then in the CMake module directory.
This leaves a lot of interpretation as to what CMake considers a module vs. a file, because a CMake module is a file on the file system after all. So what's the difference?
The CMake source code is the only place I could find the answer. Basically CMake considers the argument to include() to be a file if it looks like an absolute path. This means:
On Linux/Unix
The argument starts with either '/' or '~'
On Windows
The argument's second character is ':' (as in C:)
The argument starts with '\'
CMake assumes anything else that doesn't meet the above criteria is a Module. In which case it appends '.cmake' to the argument and searches the CMAKE_MODULE_PATH for it.
File is CMake listfile, example is CMakeLists.txt. Use following command to get the list of command used in
cmake --help-command-list
Module is a cmake file (*.cmake) which contain cmake commands.
As Matt B. put, the CMAKE_MODULE_PATH is not environment variable of your shell, but cmake variable.
To append your module path to CMAKE_MODULE_PATH
LIST(APPEND CMAKE_MODULE_PATH ${YourPath})
Or if you perfer your modules to be used first
LIST(INSERT CMAKE_MODULE_PATH 0 ${Yourpath})

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 requires me to manually copy CMAKE_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 .

Display CMake variables without running CMake on a CMakeLists.txt file or manually inspecting config.cmake?

Suppose I have a package called Foo. If I run CMake on a CMakeLists.txt file that contains find_package(Foo), then I can print out the values of variables such as ${Foo_LIBRARIES} and ${Foo_INCLUDES}.
Is there an easy way to display these variables without having to run CMake on a CMakeLists.txt file, and without having to manually inspect the config.cmake file?
You asked: (1) Is there an easy way to display these variables without having to run cmake on a CMakeLists.txt file, and (2) without having to manually inspect the config.cmake file?
I can give you a yes answer to (2) but it does require that you (re)run cmake. But since you can re-run your cmake configure step by simply executing cmake . in the build directory, re-running cmake should not keep you from trying this approach. My answer is given in this SO answer and uses the get_cmake_property command. Here is that code encapsulated into a cmake macro, print_all_variables, so I can use it when debugging my cmake scripts.
macro(print_all_variables)
message(STATUS "print_all_variables------------------------------------------{")
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
message(STATUS "print_all_variables------------------------------------------}")
endmacro()
The macros are invoked with same syntax as cmake functions:
print_all_variables()
To simply print a value, you could do something like this:
message(STATUS "foo include dir: ${foo_INCLUDE}")
where ${foo_INCLUDE} is the value you desire to print.
Note: I'm using cmake > 3.14
Run CMake and have a look at the cache with the ccmake GUI tool. Then you'll get all the variables.
Or run CMake with -LH then you will get all variables printed after configuration.
So I think it is not possible to get the variables without running CMake.
Run cmake in find-package mode. Example to display a package`s include directories:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=COMPILE --find-package
Example to display the libraries:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=LINK --find-package
The NAME must be set to the package name. You can obtain your COMPILER_ID on this page. LANGUAGE can be C, CXX or Fortran.
I am always suspicious of variables changing values throughout a script somewhere so I like to see the value of a variable at a particular point in the running script. Combining the ideas from both Phil and Aaron B. this is what I'm using:
function(PRINT_VAR VARNAME)
message(STATUS "${VARNAME}: ${${VARNAME}}")
endfunction()
PRINT_VAR("CMAKE_CXX_COMPILER")
Then I can just litter PRINT_VAR statements around like I'm debugging code back in 1980
These variables are generally hardcoded into FindFoo.cmake so that it is not possible to extract them without running the function first. Note that sometimes the value of Foo_LIBRARIES depends on the system configuration, which is unknown until find_package(Foo) is run.

Wrap cache variables of a library when configuring my code in CMake

I have to build a library that is using another library, how can I make variables of that another library not showing in the CMake-Gui and use instead values that are configured by my CMake script?
For example when my application is built for Mobile, I already have a flag for OpenGL ES, but the SDL "VIDEO_OPENGLES" variable still shows up in the GUI. The problem is that since my build script already have knowledge of what happens, it can happily pre-configure other scripts instead of polluting the Gui of the users that need to compile my code.
Also I want to know what happens if 2 libraries have by accident 2 variables with same name but used in a different fashion.
The key word is INTERNAL property of the variable. Take a look at this example:
Test/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
Project(Test)
add_subdirectory(Component1)
add_subdirectory(Component2)
Test/Component1/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.11)
project(Component1)
set(SOURCES test1.c)
set(Component1_CONF "SomeValue" CACHE STRING "Component1_CONF description")
message(STATUS "Component1_CONF=${Component1_CONF}")
add_library(Component1 ${SOURCES})
target_include_directories(
Component1 INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
)
Test/Component2/CMakeLists.txt:
project(Component2)
set(SOURCES test1.c)
set(Component2_CONF "/etc/passwd" CACHE FILEPATH "Component2_CONF description")
add_executable(Component2 ${SOURCES})
target_link_libraries(Component2 Component1)
Actually Component2 can be a library static or shared not an executable, it doesn't matter.
Now if you run cmake-gui on the top directory and run Configure you will see both Component1_CONF and Component2_CONF. But if you change the top-level CMakeLists.txt and add the line which forcibly sets Component1_CONF:
cmake_minimum_required(VERSION 2.8)
Project(Test)
set(Component1_CONF "Other value" CACHE INTERNAL "Component1_CONF forced value" FORCE)
add_subdirectory(Component1)
add_subdirectory(Component2)
you will effectively hide Component1_CONF from cmake-gui and even from command-line (-DComponent1_CONF=Boo) configuration.
Also you should note that usually variables are set only in the current scope (unless PARENT keyword is used). Thus variables set in some directory don't affect variables set in peer subirectories and the parent subdirectory. However if variables are set in the very same scope (say, when you run configuration tests from within the parent CMakeLists.txt) then yes, they could interfere one another. To prevent this CMake uses naming convention which explicitly separates variables from different packages. Most of configuration macros receives "prefix" parameter which specifies the name prefix for variables being set in the macros, so macro users (that is applications CMakeLists.txt) can explicitly separate test variables from different dependent packages.