CMake if statement evaluated in makefile - if-statement

Standard cmake if() else() statement is evaluated during cmake execution. Is it possible to put if/else to makefile by declaring it in CMakeLists.txt ?

CMake can be run in "process script" and "command" modes (see documentation for -P and -E flags). You can utilize this to create custom target, which would call cmake to do desired tasks.
This target would end up in Makefile, so you will be able to execute it without running cmake in the build dir.

Related

CMake command line define macro without value

I have the following line in my CMakeLists.txt
add_compile_definitions(DEBUG=$(DEBUG))
so when I compile my code with Makefile, I can do this
make DEBUG=1
But what I really want is to just define the DEBUG macro without setting any value to it.
Is there a way I can do this on a command line with cmake?
With CMake you can, at configuration time, add some CMake variables. For example you can do this cmake -S <src_folder> -B <build_folder> -DDEBUG=ON. This way you will have access to the variable DEBUG in your CMake.
In your CMake you will have this code
if(DEBUG)
add_compile_definition(DEBUG)
endif()
(Note that instead of add_compile_definitions, it is recommended to use target_compile_definitions which will set your DEBUG macro only for one target and not globally to your project.
Example:
add_executable(my_target main.cpp)
target_compile_definition(my_target PRIVATE DEBUG)
PRIVATE means that this compile_definition will only be used by the target my_target and will not be propagated to others.)
But if you're only concern of the building type, I suggest that you use CMake variables that are already present within CMake. You can use CMAKE_BUILD_TYPE which will contains Debug, Release or whatever depending on what type of build you want. Your code will now be this
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_compile_definition(DEBUG)
endif()
And you can use this command line cmake -S <src_folder> -B <build_folder> -DCMAKE_BUILD_TYPE=Debug
And here the documentation https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
Note that this solution will only works for Mono-config generators like Makefile or Ninja and will not works with Visual Studio or Xcode

cmake add_custom_command pre_build

I am writing cmake example for the first time.
Here is a part of CMakeFiles.txt:
add_custom_command(
OUTPUT ${CODEGEN_SRC}
PRE_BUILD
COMMAND ${CODEGEN_CMD} ${SERVICE_XML} --generate-cpp- code=/home/hello/include/gen/testGenCode
COMMENT "Generate gdbus code"
)
add_custom_target(${CODEGEN_TARGET}
DEPENDS ${CODEGEN_SRC}
)
Generate code using gdbus-codegen-glibmm in command syntax using add_custom_command.
However, contrary to my expectations, when I actually do cmake and make, it looks like this:
cmake ..
CMake Error at Server/CMakeLists.txt:1 (ADD_EXECUTABLE):
Cannot find source file:
#### generate File ####
CMake Error at Client/CMakeLists.txt:36 (ADD_EXECUTABLE):
Cannot find source file:
#### generate File ####
Then, if you proceed with make, the contents of COMMANT in add_custom_command are output, and codes are actually generated.
After checking the generated code, proceed with cmake .. and make again to build normally.
Server/CMakeLists.txt, Client/CMakeLists.txt
I set the dependency of ${CODEGEN_TARGET} using ADD_DEPENDENCIES in , but it works differently than I expected.
How can I get the gdbus-codegen-glibmm command to run first?
add_custom_command will run the command during build phase (when running make). Since it generate the files required by the next target, it will fail if the file have never been generated.
You can configure the file when running cmake too, using execute_process() in addition of add_custom_command(). You can also use configure_file() to create a placeholder for the target before you erase it later with gdbus-codegen-glibmm when running make.

How to trigger CMake reconfiguration from a target?

I have the following setup:
- build/
- conanbuildinfo.cmake (generated)
- conanfile.py
- CMakeLists.txt
A conan command is ran before CMake, generating conanbuildinfo.cmake. This contains include paths etc. to be used during the compilation later. If I change the conanfile, I want to trigger conan again and a re-run of CMake from the generated ninja build. I have a script that can call conan and it updates everything properly. However, this happens after CMake was ran and even though conanbuildinfo was changed, CMake won't run once more. This causes ninja to "not see" the changes in the dependency graph, so it doesn't rebuild everything it has to rebuild. That means I have to run ninja twice to get everything to update. The way I trigger the reconfigure currently is like this:
set(DS_CONFIG_INDICATOR ${CMAKE_BINARY_DIR}/ds_configured)
add_custom_command(
DEPENDS ${PROJECT_SOURCE_DIR}/conanfile.py
OUTPUT ${DS_CONFIG_INDICATOR}
COMMAND ${CMAKE_COMMAND} -E env --unset=PYTHONPATH ${PYTHON_BINARY} ${PROJECT_SOURCE_DIR}/scripts/common/reconfigure.py ${DS_CONFIG_INDICATOR} ${PROJECT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Checking if reconfigure is needed"
USES_TERMINAL
)
add_custom_target(ConanReconfigure
DEPENDS ${DS_CONFIG_INDICATOR}
)
Is there a way to trigger a reconfigure after this script was ran?
I tried using the following without any success:
CMAKE_CONFIGURE_DEPENDS
Running cmake from the reconfigure.py script
Touching the main CMakeLists.txt from the reconfigure.py script
Using execute_process to run the script
Why didn't execute_process work?
It seems to me like this should work if you use execute_process (not add_custom_command) to run conan/reconfigure.py before any of the CMake logic that depends on its output, combined with adding the input files of that command to CMAKE_CONFIGURE_DEPENDS.
Trying to run something before CMake won't work... but you don't need to do that, anyway. I think your problem is that you are trying to solve the wrong question. Instead, look at it as a) you want to run something during CMake's execution, and b) you want to re-run CMake if your "conanfile" changes. Re-running CMake will re-run conan.

How to get current configuration (Release/Debug) in CMake for Visual Studio

I am on Visual Studio 2013, CMake 3.5.1, Windows 10. I am trying to copy some files via CMake like below:
file(COPY ${IMAGES} DESTINATION ${CMAKE_BINARY_DIR}/bin/Release)
Is it possible to replace "Release" with a variable that represents the configuration like:
file(COPY ${IMAGES} DESTINATION ${CMAKE_BINARY_DIR}/bin/${Variable})
I attempted
file(COPY ${IMAGES} DESTINATION ${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE})
but CMAKE_BUILD_TYPE is an empty string when I use message to print it out, I also attempted
file(COPY ${IMAGES} DESTINATION ${CMAKE_BINARY_DIR}/bin/$<CONFIGURATION>)
but for some reason file command cannot decipher $<CONFIGURATION> whereas command like
add_custom_target(run COMMAND ${CMAKE_BINARY_DIR}/bin/$<CONFIGURATION>/Test.exe)
can. What is the right way to extract whether visual studio is currently built in Release or Debug in CMake?
The file command is executed during CMake runtime, not during build time (i.e. VS runtime).
This also means, that the generator expressions (e.g. $<CONFIG>) can not be used, as these are evaluated during build time.
(Hint: As long as there is no explicit reference to the use of generator expressions for a particular command in the CMake docu, they are not supported by that command).
The reason, why ${CMAKE_BUILD_TYPE} is empty, is due to the reason that you probably haven't specified it on the invocation of CMake:
cmake -DCMAKE_BUILD_TYPE=Debug ..
However, using that, would mean that the build files are only generated for the Debug configuration. That's obviously not what you want.
To solve your problem: Using generator expressions is the right way, as you've already figured out with the use of add_custom_target (or add_custom_command).
You can use custom commands as dependencies for other "real" targets and you can specify post-/pre-build and pre-link commands for a specific target via add_custom_command.
As the docu states for the COMMAND argument of add_custom_command:
Arguments to COMMAND may use generator expressions. References to target names in generator expressions imply target-level dependencies, but NOT file-level dependencies. List target names with the DEPENDS option to add file-level dependencies.
To copy a file after a successful build of a target:
add_custom_command(TARGET myTarget POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${IMAGE1}" "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${IMAGE2}" "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/"
)

Generator expressions cmake: copying works in debug but not release mode

I am trying to figure out how to copy some libs depending on the config in cmake.
I tried this:
add_custom_command(TARGET Myapp
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<$<CONFIG:Debug>:${_LIBS_DEBUG}>
$<$<CONFIG:Release>:${_LIBS_RELEASE}>
$<TARGET_FILE_DIR:MyApp>)
It copies libs in Debug but not in release:
Is this supposed to be legal and should work?
If it is not legal (I do not get error), how can I achieve the same effect?
Turning my comments into an answer
What I normally do to debug those case is to add another COMMAND before the actual line in question that just echos the command line. In your case:
COMMAND ${CMAKE_COMMAND} -E echo
$<$<CONFIG:Debug>:${_LIBS_DEBUG}>
$<$<CONFIG:Release>:${_LIBS_RELEASE}>
I've run this a few tests and you will see that the $<1:...> and $<0:...> expressions are not evaluated.
So seeing this I was searching CMake's bug tracker database and this is a known issue and yet (as for CMake 3.5.2) unresolved: 0009974: CMake should support custom commands that can vary by configuration.
There are several ways proposed in this ticket that do work with existing versions of CMake.
In your case - until this issue is resolved and if you want to have it shell independent - I would do it the "old way" and call a CMake script:
CopyLibsByConfig.cmake.in
if (_CONFIG STREQUAL "Debug")
file(COPY #_LIBS_DEBUG# DESTINATION "${_DEST_PATH}")
else()
file(COPY #_LIBS_RELEASE# DESTINATION "${_DEST_PATH}")
endif()
CMakeLists.txt
...
configure_file(CopyLibsByConfig.cmake.in CopyLibsByConfig.cmake #ONLY)
add_custom_command(TARGET MyApp
POST_BUILD
COMMAND ${CMAKE_COMMAND}
-D _CONFIG=$<CONFIG>
-D _DEST_PATH="$<TARGET_FILE_DIR:MyApp>"
-P "${CMAKE_CURRENT_BINARY_DIR}/CopyLibsByConfig.cmake"
)
But the solution can very much depend on the files you want to copy to your binary output folder. And there are a lot of way doing it, like using install():
install(FILES ${_LIBS_DEBUG} CONFIGURATIONS Debug DESTINATION $<TARGET_FILE_DIR:MyApp>)
install(FILES ${_LIBS_RELEASE} CONFIGURATIONS Release DESTINATION $<TARGET_FILE_DIR:MyApp>)
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
Obviously that's not the way install() is meant to be used, so consider using the INSTALL or PACKAGE targets properly to distribute your application and all its dependencies.
And if we are talking about Visual Studio runtime DLLs you most likely want to take a look at the InstallRequiredSystemLibraries CMake module.
Other solution is to use generator expression.
For example I have cppzmq (shared library) and cppzmq-static (static library with static dependencies). I would like to have faster debug builds so I use cppzmq in Debug build and in (other) e.g. release I want one big fat exec.
target_link_libraries(CommunicationCommonLib PUBLIC
$<IF:$<CONFIG:Debug>,cppzmq,cppzmq-static>
Dexode::EventBus
gcpp
protobuf::libprotobuf
)