CMake: Get environment variable with escaped characters - cmake

Suppose one wants to use the environment variable MY_VAR in CMake. This may be accomplished simply by using set(myVar $ENV{MY_VAR}).
But what if MY_VAR contains escape characters, say MY_VAR="/path/with/escaped\ chars"? CMake treats \ followed by a white-space as two individual characters, instead of a single character (white-space). In other words,
# CMakeLists.txt
set(myVar $ENV{MY_VAR})
message(${myVar})
prints /path/with/escaped\ chars, not /path/with/escaped chars. How does one get CMake to recognize escape characters in environment variables? Are there any best practices regarding this problem? I'm running CMake on macOS, but hope that there is a platform independent solution...
Context: Cmake is used to configure a C++ framework before installation. The MY_VAR contains a path provided by the user that is used (a) in a Makefile to set the CMAKE_PREFIX_PATH and (b) by Cmake to configure_file a configuration file for a python script that is required by the framework.

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 load additional parameter in CMAKE using ${CMAKE_CURRENT_SOURCE_DIR}?

I want to pass an additional parameter to cmake relative to where it is.
My libraries are in:
C:/bla/imgui
C:/bla/imgui-integration
From
C:/bla/imgui-integration/build folder I want to refer to C:/bla/imgui in a parameter named IMGUI_DIR :
cmake .. -DIMGUI_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../imgui"
The problem is that I tried every combination of :
-DIMGUI_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../../imgui"
-DIMGUI_DIR="${CMAKE_CURRENT_SOURCE_DIR}/imgui"
And none of them works.
Only works if I directly use:
-DIMGUI_DIR="C:/bla/imgui"
What am I doing wrong exactly?
As Tsyvarev said in a comment:
CMAKE_CURRENT_SOURCE_DIR is a CMake variable, which is set by CMake
when it interprets your project. But when running cmake <..> you use
shell scripting. You need to use abilities of the shell for prepare
needed parameters
I can't use cmake variables in the shell that will be interpreted inside cmake.

using lists in generator expressions with "target_link_libraries" command (unwanted -l flag)

I'm trying to use generator expressions to link imported libraries for my downstream builds. (using cmake 3.11.1 btw)
Since the generator command in TARGET_LINK_LIBRARIES doesn't accept whitespace, I discovered I need to use the 'JOIN' command. This seems to be adding an unwanted -l flag to my generated ninja file, however, and breaking the build.
# link dependencies
TARGET_LINK_LIBRARIES(TGT1
"$<JOIN:$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT1}>, >")
TARGET_LINK_LIBRARIES(TGT2
"$<JOIN:$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT2}>, >" $<TARGET_FILE:TGT1>)
the result in my Ninja file is:
LINK_LIBRARIES = -llibModuleA libModuleB libModuleC ...
What's going on here?
Thanks #StephenNewell for the hint. Since the list had no spaces, I can use the BOOL generator expression. I was just having problems with the quotes being in the wrong place. The below works:
# link dependencies
TARGET_LINK_LIBRARIES(TGT1
"$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT1}>")
TARGET_LINK_LIBRARIES(TGT2
"$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT2}>" $<TARGET_FILE:TGT1>)

Command: `cmake . .`

I use cmake (https://cmake.org) to compile a program. However, I don't understand this command:
cmake . .
The command line reference shows that the positional arguments to cmake are either <path-to-source> or <path-to-existing-build>. However, the use of two positional arguments (as in your case) is not documented. It causes the first position argument to be the <path-to-source> (assuming it contains a CMakeList.txt), and the second to be the project binary directory.
Using two current directory (.) position arguments is equivalent to using only one, as the project binary directory defaults to the current directory if none is provided on the command line. Usually when using CMake and creating an out-of-source build, CMake would be run from the project binary directory, and the path to the source directory (only) is provided. This is the recommended use case.

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