How to load additional parameter in CMAKE using ${CMAKE_CURRENT_SOURCE_DIR}? - cmake

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.

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

Obtaining Vcpkg paths within a CMake script

Is there a simple way to find either the root, or the "installed" subdirectory of Microsoft's Vcpkg, from within a CMake build script? Let's assume the CMAKE_TOOLCHAIN_FILE has already been set to the standard vcpkg.cmake file.
You most likely need to set an environment variable to tell CMake where to find the toolchain file.
Possible answer: Call cmake with -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake

Forcing a particular MPI compiler with CMake

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.

Get C/CXX FLAGS set by commands add_definitions() and add_compile_options()

In my CMakeLists.txt, global C and CXX flags are set using commands add_definitions()
and
add_compile_options.
Many attempts to retrieve the C/CXX flags:
${CMAKE_C_FLAGS} and ${CMAKE_CXX_FLAGS} are empty
${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}} is the original default CMake value
(e.g. -g for CMAKE_C_FLAGS_DEBUG)
${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}} same as above
get_cmake_property(def COMPILE_DEFINITIONS) is NOTFOUND
get_cmake_property(opt COMPILE_OPTIONS) is also NOTFOUND
I want to pass my custom C/CXX flags to an external project based on ./configure:
ExternalProject_Add( my_external_lib
DEPENDS Threads::Threads
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
BUILD_IN_SOURCE 1
UPDATE_COMMAND echo "Full clean" && make distclean
CONFIGURE_COMMAND ${CMAKE_CURRENT_LIST_DIR}/configure
--prefix=${CMAKE_BINARY_DIR}
--cc=${CMAKE_C_COMPILER}
--cxx=${CMAKE_CXX_COMPILER}
--CFLAGS=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}}
--CXXFLAGS=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}
--LDFLAGS=${CMAKE_STATIC_LINKER_FLAGS_${CMAKE_BUILD_TYPE}}
--ARFLAGS=${CMAKE_STATIC_LINKER_FLAGS_${CMAKE_BUILD_TYPE}}
--enable-static
)
(1) How to get compilation flags set by add_definitions() and add_compile_options?
Note: I use CMake version 3.3.2
While writing the question, I realize I am wondering another question:
I use ccache but the ./configure script does not see my environment variables CC and CXX when I set them like that:
get_cmake_property(launcher RULE_LAUNCH_COMPILE)
set(ENV{CC} ${launcher} ${CMAKE_C_COMPILER})
set(ENV{CXX} ${launcher} ${CMAKE_CXX_COMPILER})
(2) Are these above CMake statements correct?
Oops I have again another question:
I retrieve the LDFLAGS and ARFLAGS from the same variable ${CMAKE_STATIC_LINKER_FLAGS}.
(3) Is there another CMake variable to set ARFLAGS?
Several questions in one post, but all of them are simple:
Documentation link for add_definitions() explicitely refers to directory and target COMPILE_DEFINITION properties. The first of them is extracted by using get_directory_property(), the second by using get_target_property(). Any of them can also be extracted by using get_property(). get_cmake_property you tried to use is inapplicable here (it is used for other type of properties).
You set environment variables CC and CXX at configuration step, but all commands for ExternalProject_Add are executed at build step.
Currently ExternalProject_Add doesn't support simple setting environment variables. As adviced in this bugreport, for set variable's value without spaces you may prepend command with "VAR=VAL" clauses. But for your case this wouldn't work because of escaping problems.
See also CMake FAQ: How can I get or set environment variables?
[...]
environment variables SET in the CMakeLists.txt only
take effect for cmake itself (configure-time),
so you cannot use this method to set an environment variable
that a custom command might need (build-time).
Barring environment variable support by various CMake commands
(e.g. add_custom_command(), currently not supported yet),
an acceptable workaround may be to invoke shell scripts instead
which wrap the commands to be executed.
Simple wrapper script to set environment variables and run actual command:
wrapper.sh.in:
export "CC=ccache #CMAKE_C_COMPILER#"
export "CXX=ccache #CMAKE_XX_COMPILER#"
eval "$*"
CMakeLists.txt:
configure_file(wrapper.sh.in wrapper.sh #ONLY)
ExternalProject_Add(my_external_lib
...
CONFIGURE_COMMAND /bin/sh ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh
${CMAKE_CURRENT_LIST_DIR}/configure ...
BUILD_COMMAND /bin/sh ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh make
)
Note, that you should use wrapper for every command which requires this variables. In you case, these are CONFIGURE_COMMAND and BUILD_COMMAND.
Documentation for CMAKE_STATIC_LINKER_FLAGS says that these flags are used for linker, so you may probably use them as LDFLAGS. As for ARFLAGS, they are hardcoded directrly to CMAKE_C_ARCHIVE_CREATE variable, contained full command line for ar. By default this command simply uses linker flags, so your approach seems to be correct too.

How to get CMake to build a Fortran program with MPI support?

I was trying to parallelize a Fortran program using MPI. I use CMake to do the build of my program. It was difficult to find support on getting CMake to create a working makefile for Fortran with MPI support on google, but from what I could gather, I added the following commands to my CMakeLists.txt script:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_DIRS})
link_directories(${MPI_FortranLIBRARY_DIRS})
This will locate MPI on my system and then set the variables found in the following three commands. In my linking line, I added the MPI libraries variable to the list of the other libraries that my program needed to build.
target_link_libraries(${exe_name} otherlibs ${MPI_FortranLIBRARY_DIRS})
Doing cmake and then make worked to build the program and the program ran; however, when I tried to add more to the source which required me to include the mpif.h include file, my compilation failed due to not being able to find this header file. I also could not use mpi because the compiler cannot find the mpi.mod file in the path.
I inserted "message" commands into my CMakeLists.txt file and printed out the values of the variables that I was using for including and linking. It turns out that the variables, MPI_Fortran_INCLUDE_DIRS and MPI_FortranLIBRARY_DIRS weren't set to anything. A check of the module that CMake is actually using to set these variables (FindMPI.cmake) showed these variables to be non-existent. I changed my CMakeLists.txt file to use the correct variables:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_PATH})
link_directories(${MPI_Fortran_LIBRARIES})
target_link_libraries(${exe_name} otherlibs ${MPI_Fortran_LIBRARIES})
Now when I execute make, the compiler could find both mpif.h as well as mpi.mod.
UPDATE:
I want to mention that this solution worked for cmake version 2.8.10.1. When I moved my CMakeLists.txt scripts to a different machine that has cmake version 2.8.4, I get the same error about mpi.mod missing during the link stage. I checked the FindMPI.cmake module and, sure enough, there are no variables that specify the language (i.e. there is no MPI_Fortran_LIBRARIES variable, just a MPI_LIBRARIES variable, and this variable is not getting set to the correct location of the mpi library on that system. So this solution will be dependent on cmake version.
Sounds like you are not using the mpi compiler. That is fine, but you have to add a lot of flags then. There is not really an mpi compiler but a wrapper that sets the flags to be able to use mpi. With cmake I was able to do this by defining the fortran compiler I was going to use BEFORE the call to cmake. It's not a nice solution since you loose portability, but it works. I'm trying to find a better solution and define inside cmake what compiler to use, but have not been able to do so. In other words, this works for me:
FC=mpif90 cmake ../.
make
I was having the same problem as you. Hope this solves the issue. If anybody finds how to define the fortran compiler INSIDE cmake please post it!
as you've already noticed, you misspelled the name of two variables, namely MPI_Fortran_LIBRARIES and MPI_Fortran_LIBRARIES
It is useful also to add:
cmake_minimum_required(VERSION 2.8.10)
at the very beginning of your CMake to be sure that these variables will be defined.