How to know whether the cmake project generation ended with success - cmake

The last few weeks I'm playing with build automation. With the help of Cmake, I'm able to generate Visual Studio solution and MinGW makefile for Windows and also GCC makefile for Linux. The Cmake task is executed through a batch file on Windows respectively through a shell script on Linux. Everything looks correct and works as expected. My plan is to setup different test servers where the whole build and test process will be automated and the results will be reported somewhere.
One thing I was not able to figure out yet is how to obtain the result of the cmake command. I would like to know whether the cmake command ended successfully or not, so in case of error the fail will be reported. At this moment I'm able to parse the result and look for "Build files have been written to: ..." sentence, but I think it is not really robust solution.
Is there a way to determine whether the cmake command was successful or not? I don't want to stick necessarily to batch files, Python (or other) scripts are also welcome. Thanks.

Just make sure your scripts do exit with the error levels reported by the programs you're calling.
Let me explain this with showing an example:
vs2015_x86_build.cmd
#ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION
:: usage:
:: vs2015_x86_build.cmd <target> <config>
:: <target> - target to be built (default: ALL_BUILD)
:: <config> - configuration to be used for build (default: Debug)
if NOT "%1" == "" (SET CMAKE_TARGET=%1) else (SET CMAKE_TARGET=ALL_BUILD)
if NOT "%2" == "" (set CMAKE_BUILD_TYPE=%2) else (set CMAKE_BUILD_TYPE=Debug)
SET CMAKE_BINARY_DIR=vs2015_x86
IF NOT EXIST "%CMAKE_BINARY_DIR%\*.sln" (
cmake -H"." -B"%CMAKE_BINARY_DIR%" -G"Visual Studio 14 2015"
SET GENERATE_ERRORLEVEL=!ERRORLEVEL!
IF NOT "!GENERATE_ERRORLEVEL!"=="0" (
DEL /F /Q "%CMAKE_BINARY_DIR%\*.sln"
EXIT /B !GENERATE_ERRORLEVEL!
)
)
cmake --build "%CMAKE_BINARY_DIR%" --target "%CMAKE_TARGET%" --config "%CMAKE_BUILD_TYPE%"
SET BUILD_ERRORLEVEL=!ERRORLEVEL!
IF NOT "!BUILD_ERRORLEVEL!"=="0" (
EXIT /B !BUILD_ERRORLEVEL!
)
ENDLOCAL
References
Does CMake always generate configurations for all possible project configurations?

Have an additional action associated with the ultimate target, like the creation of a file, or the appending of a time-stamped log entry to a given file.

Related

Have CMake execute a file which is not CMakeLists.txt

Is that true that you can't customize the name of your CMakeLists.txt file? I read in a few places that make suffers from the same problem, but that's completely not true, you sure can:
~$ make -f whatever_name_you_feel_like
Can't you do this with CMake?
My situation is as follows: The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing. I thought it would be possible to just keep 2 CMake files and tell cmake which one to execute.
It's not possible to use file with a name different to CMakeLists.txt, but I'm almost certain that's not actually what you want to do anyways.
I assume the cl version and the development version are mostly similar and only some details change. In this case you should not duplicate the logic. Instead add one or multiple options to your cmake project that can set when you set up the build dir and can even be changed without reconfiguring the whole project from scratch. Basically you add a cache variable to CMakeLists.txt which allows the user to overwrite the default value via -D command line option. The value can also be modified after the initial configuration using cmake-gui.
cmake_mimimum_required(VERSION 3.0.2)
project(MyProject)
# option set to true by default
set(MY_PROJECT_COMMAND_LINE_BUILD 1 CACHE BOOL "Use the command line configuration for MyProject")
#logic common to both configurations
add_executable(MyProg foo.cpp bar.cpp)
if(MY_PROJECT_COMMAND_LINE_BUILD)
#logic only for command line build
target_compile_definitions(MyProg PRIVATE COMMAND_LINE_BUILD)
else()
# logic only for non-command line build
target_compile_definitions(MyProg PRIVATE DEVELOPMENT_BUILD)
endif()
Ironically you could set up both from the command line:
Command line build
cmake -S sourceDir -B buildDir
Development build
cmake -D MY_PROJECT_COMMAND_LINE_BUILD:BOOL=0 -S sourceDir -B buildDir
If you don't want to enter the cache values in the command line every time you set up the project, you could also use a cmake script file to initialize the cache values using the -C command line option.
cmake -C developmentVersion.cmake -S sourceDir -B buildDir
developmentVersion.cmake:
set(MY_PROJECT_COMMAND_LINE_BUILD 0 CACHE BOOL "Use the command line configuration for MyProject")
Theoretically you could the whole CMakeLists.txt file in an if else endif structure and use include in one of the alternatives to competely replace the standard logic in the CMakeLists.txt file, but imho this is not a good idea.
Can't you do this with CMake?
No, it's not possible.
The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing.
One way: copy or symlink proper CMakeLists.txt before executing cmake.
Preferably one would use cmake scripting language:
# CMakeLists.txt
if (MODE STREUQAL "CI_WORKFLOW")
include(CMakeLists-ci-workflow.txt)
elseif (MODE STREQUAL "DEVELOPING")
include(CMakeLists-developing.txt)
else()
message("SUPER ERROR")
fi()
and then separate CMakeLists-ci-workflow.txt and separate CMakeLists-developing.txt and do cmake -D MODE=DEVELOPING or -D MODE=CI_WORKFLOW.
But overall, the idea of "separate CMakeLists.txt" sounds bad to me. Instead use CMAKE_BUILD_TYPE=Debug for developing and CMAKE_BUILD_TYPE=Release for release builds, and use other cmake variables to differentiate settings, instead of duplicating configuration.

Waiting for targets in a CMake build

I think I do have a CMake order/parallelism problem.
In my build process, I need to build a tool which is then later used by some other target. This tool is a separate project with a CMakeLists.txt file like this:
project (package-tool LANGUAGES CXX)
set (SOURCES package_tool.cpp)
...
Later in the build, this top level target is referenced by some other target:
...
add_custom_command (OUTPUT "${DST_FILE}"
COMMAND ${PACKAGE_COMMAND} "${DST_FILE}"
COMMAND package-tool.exe -e "${DST_FILE}"
DEPENDS ${PACKAGE_DEPENDENCIES} package-tool)
...
I use ninja for building and the dependencies (ninja -t depends) are looking correctly. Also, the build commands (ninja -t commands) are making sense. But: From time to time, the build fails. The message does not make sense, it reads:
This version of package-tool.exe is not compatible with the version
of Windows you're running.
Because the build runs in parallel (32 processes) I suspect that the package-tool target is not completed when the generated exe is being used in the second target, which might lead to this confusing error message. Again, most of the time the build succeeds but every 10th or 20th run, it fails with that message.
So now my question is:
Is there a way to wait for a tool/target having been finished building in a parallel build in CMake/Ninja ?
Or how do I handle the task of building build tools in the same build process correctly ?
Thank you in advance !
Actually depend on the executable file and run the executable file, not the target. Don't concern yourself with any .exe suffix. Also better don't assume add_custom_command will be run in CMAKE_RUNTIME_OUTPUT_DIRECTORY - if you depend on specific directory explicitly set it with WORKING_DIRECTORY.
add_custom_command (
OUTPUT "${DST_FILE}"
COMMAND ${PACKAGE_COMMAND} "${DST_FILE}"
COMMAND $<TARGET_FILE:package-tool> -e "${DST_FILE}"
DEPENDS ${PACKAGE_DEPENDENCIES} $<TARGET_FILE:package-tool>
)

Can I setup default CMake cache variables to apply for all projects?

There are several settings I need to provide to CMake which are the same on every project. It gets annoying having to specify these on the command line every time I blow away my build area and start again.
For example:
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${HOME}/local
Is there a way to provide values for common settings like CMAKE_BUILD_TYPE and CMAKE_INSTALL_PREFIX so that they are applied to all projects by default?
You may create "initial-cache" script
~/default.cmake:
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type")
set(CMAKE_INSTALL_PREFIX $ENV{HOME}/local CACHE PATH "Installation prefix")
and pass it to cmake as with -C option:
cmake -C ~/default.cmake ..
More info about -C option in cmake(1) documentation.
As for using these setting by default (that is, without any additional options to cmake), I don't know a clear way for doing this.
You may create wrapper script like default-cmake, which calls cmake with original plus additional parameters.
Another way is to create an initial CMakeCache.txt file that exactly contains those two variables:
$ cat CMakeCache.txt
CMAKE_BUILD_TYPE:STRING=Debug
CMAKE_INSTALL_PREFIX:PATH=~/local
Upon the next cmake run, the CMakeCache.txt will contains the remaining of the cmake run. You do not need to pass any additional flags to cmake, but it will scratch your inital CMakeCache.txt (thus you need to add it to .gitignore).

DLL project : migrating from Qt + MinGW to Visual Studio

I'm making a DLL, written in C++ with Qt Creator (but no Qt stuff inside, pure homemade C++), and using a standard qmake + MinGW/g++ build process. My friend is doing the framework/main soft, and he's using Visual Studio Express 2015, and he's supposed to use my DLL inside of that. Because of that I want to use the same visual studio compiler for my project in Qt Creator.
So I noticed that Qt Creator had auto detected the Visual Studio C++ 14 compiler, which I believe is the one attached to Visual Studio Express 2015. When I create a kit with that compiler and set my project to compile with that kit, I get a suggested "Make" step which calls jom in the bin folder of my Qt Creator installation. I have no idea what this is and I get the following output (see below).
I don't know what to do from here. I also tried to create a compiler directly from the nmake executable in my Visual Studio installation, and then using it - but I get a very similar error about '-' not being recognized as an option. Any hints are greatly appreciated !
12:56:27: Starting: "C:\Qt\qtcreator-3.1.1\bin\jom.exe"
Usage: jom #commandfile
jom [options] [/f makefile] [macro definitions] [targets]
nmake compatible options:
/A build all targets
/D display build information
/E override environment variable macros
/F <filename> use the specified makefile
/G display included makefiles
/H show help
/I ignore all exit codes
/K keep going - build unrelated targets on error
/N dry run - just print commands
/NOLOGO do not print logo
/P print makefile info
/R ignore predefined rules and macros
/S silent mode
/U print content of inline files
/L same as /NOLOGO
/W print the working directory before and after other processing
/X <filename> write stderr to file.
/Y disable batch mode inference rules
jom only options:
/DUMPGRAPH show the generated dependency graph
/DUMPGRAPHDOT dump dependency graph in dot format
/J <n> use up to n processes in parallel
/KEEPTEMPFILES keep all temporary files
/VERSION print version and exit
Error: unknown command line option '-' in arguments: '/L-j4'
12:56:27: The process "C:\Qt\qtcreator-3.1.1\bin\jom.exe" exited with code 128.
Error while building/deploying project Ford_DAT_framework_DLL_as_plugin (kit: MVS Ford)
When executing step 'Make'
12:56:27: Elapsed time: 00:04.
So I used qmake -tpvc to generate a VCproj file which compiled almost immediately in Visual Studio Express 2015 which my friend uses. I guess that's a valid solution to my problem then !

Is there a way to get errors when a CMake command fails?

I am writing a script and started working with the install command (for copying files) and it is not working. CMake configure/generate does not show any errors (i.e. it does not stop and no warnings/errors show related to this command) and the command does not seem to be working, because I don't see any files being copied.
Since I am new, I am wondering:
How can I tell that install failed (perhaps the source directory was wrong, or the destination directory was wrong)? It appears to be failing silently.
Are there error codes I can check to see what went wrong?
When is install called? When I click configure? Or when the project is built?
I am on Windows.
To the general question, there are a number of ways to get more verbose output from CMake - I just learned a third for gnarly errors:
to debug CMake recipes, I like using the message command and you can even iterate over directories and issue messages*
e.g. message( STATUS "SQLITE3_LIB: ${SQLITE3_LIB} SQLITE3_PATH: ${SQLITE3_PATH}") # prints SQLITE3_LIB and SQLITE3_PATH variables
perform verbose builds to troubleshoot your build itself
run make VERBOSE=1 (with make, ninja -v with ninja, etc.) to help you troubleshoot the process, such as cmake -DYOUR_OPTION="insert values" ~/path/to/files/ && make VERBOSE=1
if you ever find an inscrutable error, I just learned that we can run strace on the failing command - this can be a bit overwhelming, but can help when you have exhausted #1 and #2
I just used strace /usr/bin/cmake -E copy_directory $MY_SOURCE_PATH $MY_DEST_PATH to try to understand why a copy was failing
*I have used DLRdave's answer to a different question to print out the INCLUDE_DIRS:
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
message(STATUS "dir='${dir}'")
endforeach()
When you add an install command to your CMakeLists.txt, you get a new target created called "install".
In order to actually install the chosen files/targets, you need to build this install target. It's not automatically built as part of the "ALL" target.
For example, if you're using Visual Studio, the "INSTALL" target should appear in the "CMakePredefinedTargets" folder of the Solution Explorer. Just selecting this target and building it will cause the solution to be built and the selected items installed.
If any part of the build or install process fails, the notifications should then be apparent.