How to trigger CMake reconfiguration from a target? - c++

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.

Related

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.

Can someone expand what is meant by 'configure' and 'build' with CMake files

I am downloading this code from GitHub (subdivision-regression), and am getting stuck following the instructions:
To build doosabin_regression:
Run CMake with an out of source build.
Set COMMON_CPP_INCLUDE_DIR to the full path to rstebbing/common/cpp.
Set DOOSABIN_INCLUDE_DIR to the full path to rstebbing/subdivision/cpp/doosabin/include.
Set Ceres_DIR to the directory containing CeresConfig.cmake.
Set GFLAGS_INCLUDE_DIR, GFLAGS_LIBRARY and RAPID_JSON_INCLUDE_DIR. (Add -std=c++11 to CMAKE_CXX_FLAGS if compiling with gcc.)
Configure.
Build.
I have edited the CMakeLists.txt file to put the correct paths in. I then created a new directory called subdivision-regression-bin and ran:
cmake ../subdivision-regression/src
It completes this and displays:
-- Configuring done
-- Generating done
-- Build files have been written to: /home/hert5584/RStebbing/subdivision-regression-bin
However, when I try and run the example code, it cannot find the files listed in CMakeLists.txt (I know they are the right paths as otherwise CMake does not run).
I have tried running:
sudo make install
But get the following error:
make: *** No rule to make target 'install'. Stop.
Any ideas why this isn't working? Have the above steps Configured and Built the files?
The ordered CMake idiom to understand is:
The Configure step
The Generate step (This is often subsumed in the Configure step, and not mentioned explicitly, as in this case.)
The Build step (in which you actually compile/link your code into libraries/executables)
Take a look at this resource for information about the configure and generate stages.
You didn't appear to perform the steps to set CMake cache variables. For these you have to use CMake command line options (-D specifically). So run CMake as something like this instead to set all six variables:
cmake -DCOMMON_CPP_INCLUDE_DIR=/rstebbing/common/cp -DDOOSABIN_INCLUDE_DIR=...[More CMake Cache variables]... ../subdivision-regression/src
For building, try just running make without sudo or install:
make

How can make CMake install an external project my targets depend on? [duplicate]

It can be a pain to refrence ExternalProjects when their install targets are messed up. So one may want to build and install ExternalProjects once before generating main project files for given project. Is it possible with CMake and how to do it?
You may use cmake call within execute_process for configure and build CMake project, which contains ExternalProject:
other_project/CMakeLists.txt:
project(other_project)
include(ExternalProject)
ExternalProject_Add(<project_name> <options...>)
CMakeLists.txt:
# Configure external project
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/other_project
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/other_project
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/other_project
)
Such a way other_project will be configured and built in directory ${CMAKE_BINARY_DIR}/other_project. If you do not disable installation in ExternalProject_Add call, then it will performed when building other_project.
Normally, you want some options to ExternalProject, like SOURCE_DIR, BINARY_DIR, INSTALL_DIR, to be deduced from variables in the main project. You have two ways for achive that:
Create CMakeLists.txt for other_project with configure_file, called from main project (before execute_process command).
Pass variables from main project as -D parameters to ${CMAKE_COMMAND}.
Having separated execute_process calls for sequential COMMANDS is important. Otherwise, if use single execute_process with several COMMANDS, these commands will be just "piped" (executed concurrently but with output of the first command being treated as input for the second).

Running Google test at build time with CMake generated system

My configuration has CMake 3.6, Visual Studio 2015 and latest Google test from GitHub. I add my unit tests through one of my cmake functions addGtest and do the build. After this I can run the test from my RUN_TESTS target or using ctrl + F5 in VS and works as expected.
The final goal is to run the unit tests at build time using the CMake dependency management. For now, as a first step, I have enhanced my function to create a custom_target (included the entire function, in case there are unforeseen issues in the working part), but not build it:
function (addGtest)
# vvvv this part works as explained vvvv #
set (optBOOLS)
set (optSINGLES EXE)
set (optLISTS DLL_LIST)
cmake_parse_arguments (myARGS
"${optBOOLS}" "${optSINGLES}" "${optLISTS}" ${ARGN})
# addExecutable is a function that adds target executables
set(myARGS_DLL_LIST gtest_main gtest "${myARGS_DLL_LIST}")
addExecutable (EXE ${myARGS_EXE} DLL_LIST ${myARGS_DLL_LIST} ${myARGS_UNPARSED_ARGUMENTS})
add_test (NAME ${myARGS_EXE} COMMAND ${myARGS_EXE} WORKING_DIRECTORY
${CMAKE_INSTALL_PREFIX}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/bin
) # so it can be run using ctest
# ^^^^ this part works as explained ^^^^ #
add_custom_target (${myARGS_EXE}.tgt DEPENDS ${myARGS_EXE}
COMMAND ${myARGS_EXE} --gtest_output="xml:${myARGS_EXE}.xml"
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>/bin
)
endfunction (addGtest)
As expected when I perform the build, a new target, say, utMyTest.tgt is added to VS, but it is not built. Now when I build this new target by hand in VS, I expect that the test will be run. But it doesn't and gives the following error:
1> The filename, directory name, or volume label syntax is incorrect.
I tried providing full path to the COMMAND option, removing double quotes around --gtest_output value, but to no avail. On the other hand when I cd to the working directory in a command line window and invoke the exe, it works fine!!
The first question is how do I fix it to run the test by building this new target? After that, I plan to add_custom_target (${myARGS_EXE}.run) and add_dependencies (${myARGS_EXE}.run ${myARGS_EXE}.tgt). Would this then run the test whenever the exe changes? Or should I do something else? Thank you for your help.
Could not add so much details in the comment, hence this answer.
1. Answer to the original problem
Since I needed the configuration dependent path in the WORKING_DIRECTORY option of the add_custom_target command, but cannot pass generator expressions to it, the idea is to use the CMAKE_CFG_INTDIR variable so:
add_custom_target (${myARGS_EXE}.tgt
DEPENDS ${myARGS_EXE}
COMMAND ${myARGS_EXE} --gtest_output=xml:${myARGS_EXE}.xml
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${CMAKE_CFG_INTDIR}/bin
)
Now, when you build the above target, the unit test is run in the WORKING_DIRECTORY which is not entirely desirable, since that is the install directory for libs and exes. It would be really nice to ...
2. Run the unit test from its build directory
While, at the same time, picking up the DLL paths from within Visual Studio, and storing the Gtest generated .xml file in the build directory. This is the solution:
In CMake version 3.10 CMAKE_MSVCIDE_RUN_PATH property was added. In the project wide CMakeLists.txt, set(CMAKE_MSVCIDE_RUN_PATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_CFG_INTDIR}/bin) - thanks to this solution #3, we can appendPATH to point to our install directory. And then replace the above add_custom_target command with this:
add_custom_command (
TARGET ${myARGS_EXE} POST_BUILD
COMMAND ${myARGS_EXE} --gtest_output=xml:${myARGS_EXE}.xml
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
)
This solution avoids the mess of creating additional targets. Clearly only when myARGS_EXE is built, the unit test is run. Obviously myARGS_EXE's transitive dependency on other DLLs is covered also.
If you have other elegant solutions, please post.

How to build cmake ExternalProject while configurating main one?

It can be a pain to refrence ExternalProjects when their install targets are messed up. So one may want to build and install ExternalProjects once before generating main project files for given project. Is it possible with CMake and how to do it?
You may use cmake call within execute_process for configure and build CMake project, which contains ExternalProject:
other_project/CMakeLists.txt:
project(other_project)
include(ExternalProject)
ExternalProject_Add(<project_name> <options...>)
CMakeLists.txt:
# Configure external project
execute_process(
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/other_project
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/other_project
)
# Build external project
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/other_project
)
Such a way other_project will be configured and built in directory ${CMAKE_BINARY_DIR}/other_project. If you do not disable installation in ExternalProject_Add call, then it will performed when building other_project.
Normally, you want some options to ExternalProject, like SOURCE_DIR, BINARY_DIR, INSTALL_DIR, to be deduced from variables in the main project. You have two ways for achive that:
Create CMakeLists.txt for other_project with configure_file, called from main project (before execute_process command).
Pass variables from main project as -D parameters to ${CMAKE_COMMAND}.
Having separated execute_process calls for sequential COMMANDS is important. Otherwise, if use single execute_process with several COMMANDS, these commands will be just "piped" (executed concurrently but with output of the first command being treated as input for the second).