CMake add_custom_command replaces slash with backslash on windows - windows

I'm trying to create a conan-package (hint: the names contain forward-slashes) as a postbuild operation with something like this:
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
WORKING_DIRECTORY ${PROJECT_DIR}
COMMAND conan export-pkg .. "gtest/1.10.0#shared/testing" -f -pr ${CONAN_PROFILE}
COMMENT "Uploading conan package..."
)
On Linux/Ubuntu/WSL this works fine.
conan export-pkg . 'gtest/1.10.0#shared/testing' -f -pr './platforms/platform_linux-x86-clang/conan_profile/linux-x86-clang'
On Windows the executed command looks like this:
conan export-pkg . 'gtest\1.10.0#shared\testing' -f -pr .\platforms\platform_win-x86-clang\conan_profile\win-x86-clang
How do I keep cmake from replacing / with \? Is there a setting for this?

You can use the file(TO_CMAKE_PATH "<path>" <variable>) command. It force the path to have a format with / separator.
In your case you have to put gtest/1.10.0#shared/testing in a variable with the command :
file(TO_CMAKE_PATH "gtest/1.10.0#shared/testing" GTEST_PATH)
file(TO_CMAKE_PATH ${CONAN_PROFILE} CONAN_PROFILE)
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
WORKING_DIRECTORY ${PROJECT_DIR}
COMMAND conan export-pkg .. ${GTEST_PATH} -f -pr ${CONAN_PROFILE}
COMMENT "Uploading conan package..."
)

Related

how do you escape an nmake command line inside cmake?

I am trying to build zlib on windows as a dependency in my cmake project.
I have come up with the following which works fine:
file(WRITE ${CMAKE_BINARY_DIR}/zlib.nmake
AS=ml64\n
LOC="-DASMV -DASMINF -DNDEBUG -I."\n
OBJA="inffasx64.obj gvmat64.obj inffas8664.obj"\n
-f win32/Makefile.msc\n
zlib.lib\n)
ExternalProject_Add(
zlib
GIT_REPOSITORY https://github.com/kyotov/zlib.git
GIT_TAG mt
PREFIX ${CMAKE_BINARY_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMAKE_COMMAND} -E chdir <SOURCE_DIR> nmake #${CMAKE_BINARY_DIR}/zlib.nmake
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/zlib.h <INSTALL_DIR>/install/include/zlib.h
COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/zconf.h <INSTALL_DIR>/install/include/zconf.h
COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/zlib.lib <INSTALL_DIR>/install/lib/zlib.lib
COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/zlib.pdb <INSTALL_DIR>/install/lib/zlib.pdb
)
I would really prefer if I can inline the call to nmake and not have to resort to passing # parameter.
I spent about 15-30 minutes trying to escape the double quotes in various ways but could not get it done.
Google did not teach me anything either...
Anybody has any smart ideas?

How to extract all file in a .tar.gz to specific folder with cmake add_custom_target?

To decrease library size, used cmake -E tar "zcvf" "lib.tar.gz" out.lib in cmake to automatically create .tar.gz then lib.tar.gz is moved to folder f, ie. f/lib.tar.gz.
However, to use this library in another project, it's needed to extract to specific folder. The command without cmake is clear: tar -xzf f/lib.tar.gz -C target-folder.
The problem is that, in cmake, how to combine cmake -E tar "xzf" f/lib.tar.gz with -C option? Commands such as cmake -E tar "xzf" f/lib.tar.gz -C f/ will result error.
Ideal script would be sth. like
add_custom_target(PrepareLib
COMMAND ${CMAKE_COMMAND} -E tar "xzf" command....
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ## note, this line must not be modified
)
Tools version: Visual Studio 2017, Windows 10 and CMake 3.17.1.

Code coverage ( *.cpp.gcda: CANNOT OPEN) is not working

I want to code coverage with gcov & lcov. But I got a few error.I think My CMakeLists is not providing ".gcda" files or is not finding ".gcda" files.
I tried a lot of solutions. Such as;
- I looked at similar projects and rearranged:
For example: https://jhbell.com/using-cmake-and-gcov
- I changed object output direcory:
set(OBJECT_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test/CMakeFiles/*****rpmsgtobetested.dir)
But I didn't solve this problem.
CMAKE_MINIMUM_REQUIRED(VERSION 3.8)
option(COMPILE_TESTS "Compile the tests" OFF)
if(COMPILE_TESTS)
enable_language(C)
enable_language(CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(DEFINED ENV{CPPUTEST_HOME})
message(STATUS "Using CppUTest home: $ENV{CPPUTEST_HOME}")
set(CPPUTEST_INCLUDE_DIRS $ENV{CPPUTEST_HOME}/include)
set(CPPUTEST_LIBRARIES $ENV{CPPUTEST_HOME}/lib)
set(CPPUTEST_LDFLAGS CppUTest CppUTestExt)
else()
find_package(PkgConfig REQUIRED)
pkg_search_module(CPPUTEST REQUIRED cpputest>=3.8)
message(STATUS "Found CppUTest version ${CPPUTEST_VERSION}")
endif()
pkg_check_modules(******rpmsgtest_depends json-c)
add_definitions(-DCPPUTEST_USE_MEM_LEAK_DETECTION=0)
add_definitions("-std=c++11")
# Coverage bilgisi elde etmek için CXX flagları
#
set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE OFF)
set(OBJECT_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test/CMakeFiles/******rpmsgtobetested.dir)
# (1) set sources to be tested
set(TARGET_LIBTOBETESTED ******rpmsgtobetested)
set(libtobetested_sources
../src/rpmsg_device.cpp
../src/rpmsg_sm_messages.cpp
../src/thread_interface.cpp)
add_library(${TARGET_LIBTOBETESTED} ${libtobetested_sources})
# (2) Our unit tests sources
set(TEST_APP_NAME ${TARGET_LIBTOBETESTED}_tests)
set(TEST_SOURCES
test_rpmsg_device.cpp
main.cpp
fake_sdevent.cpp
fake_syscalls.cpp
)
# (3) Take care of include directories
include_directories(${CPPUTEST_INCLUDE_DIRS}
../include
/usr/include/glib-2.0
/usr/include/gio-unix-2.0
${******rpmsgtest_depends_INCLUDE_DIRS})
link_directories(${CPPUTEST_LIBRARIES})
link_libraries(${TARGET_LIBTOBETESTED})
# (4) Build the unit tests objects and link then with the app library
add_executable(${TEST_APP_NAME} ${TEST_SOURCES})
target_compile_options(${TEST_APP_NAME} PUBLIC ${******rpmsgtest_depends_CFLAGS})
target_link_libraries(${TEST_APP_NAME} ${link_libraries}
${CPPUTEST_LDFLAGS}
${******rpmsgtest_depends_LIBRARIES}
)
# (5) Run the test once the build is done
add_custom_command(TARGET ${TEST_APP_NAME} COMMAND ./${TEST_APP_NAME} POST_BUILD)
############################################################################
################################### LCOV ###################################
############################################################################
add_custom_target(lcov
COMMAND mkdir -p coverage
COMMAND mkdir -p coverage/html
COMMAND ${CMAKE_MAKE_PROGRAM}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(TARGET lcov
COMMAND echo "################# LCOV #################"
COMMAND lcov -c -d ${COVERAGE_DATA_DIR} -o ${CMAKE_BINARY_DIR}/coverage.info
COMMAND lcov --remove ${CMAKE_BINARY_DIR}/coverage.info '**/include/**' -o ${CMAKE_BINARY_DIR}/coverage_filtered.info
COMMAND genhtml -o ${CMAKE_BINARY_DIR}/coverage/html ${CMAKE_BINARY_DIR}/coverage_filtered.info --ignore-errors source
COMMAND echo "-- Coverage report has been output to ${CMAKE_BINARY_DIR}/coverage/html"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_dependencies(lcov ${TARGET_LIBTOBETESTED})
############################################################################
################################### GCOV ###################################
############################################################################
add_custom_target(gcov
COMMAND mkdir -p coverage
COMMAND ${CMAKE_MAKE_PROGRAM}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(TARGET gcov
COMMAND echo "################# GCOV #################"
COMMAND gcov -b ${libtobetested_sources} -o ${OBJECT_OUTPUT_DIR} | grep -A 5 ".cpp$" > CoverageSummary.tmp
COMMAND cat CoverageSummary.tmp
COMMAND echo "-- Coverage files have been output to ${CMAKE_BINARY_DIR}/coverage"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/coverage
)
add_dependencies(gcov ${TARGET_LIBTOBETESTED})
else()
message(STATUS "Cross compile environment set")
endif()
Like below the last of my code output.
TEST(RpmsgDevice_test, create_new_object_with_null_param) - 0 ms
TEST(RpmsgDevice_test, create_new_object_cannot_open_device) - 0 ms
TEST(RpmsgDevice_test, create_new_object_with_thread) - 0 ms
OK (10 tests, 10 ran, 20 checks, 0 ignored, 0 filtered out, 0 ms)
profiling:/home/msergenergen/Desktop/*****/IP/*****-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested.dir//src/rpmsg_sm_messages.cpp.gcda:Cannot open
profiling:/home/msergenergen/Desktop/*****/IP/*****-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested.dir//src/rpmsg_device.cpp.gcda:Cannot open
profiling:/home/msergenergen/Desktop/*****/IP/*****-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested_tests.dir/fake_syscalls.cpp.gcda:Cannot open
profiling:/home/msergenergen/Desktop/*****/IP/*****-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested_tests.dir/fake_sdevent.cpp.gcda:Cannot open
profiling:/home/msergenergen/Desktop/*****/IP/******-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested_tests.dir/main.cpp.gcda:Cannot open
profiling:/home/msergenergen/Desktop/*****/IP/*****-lib-rpmsg/build-test/test/CMakeFiles/*****rpmsgtobetested_tests.dir/test_rpmsg_device.cpp.gcda:Cannot open
[100%] Built target *****rpmsgtobetested_tests

How do I properly invoke MSYS2 Bash commands using CMake execute_process()?

Problem Description
I am having trouble setting up a CMake external_process() command that executes a MSYS2 bash command. When I am in the MSYS2 shell if I run the command $ bash -v ./bootstrap.sh the command works correctly. But if I run the CMake script in the MSYS2 shell using $ cmake -P Run_bash_command.cmake the command errors out part way through the process. An important piece of information I found in the CMake Documentation makes me think that I am not invoking bash correctly or missing an environmental variable:
CMake executes the child process using operating system APIs directly. All arguments are passed VERBATIM to the child process. No intermediate shell is used, so shell operators such as > are treated as normal arguments.
I would like to be able to do this command using CMake if possible as this problem is part of a much larger CMake superbuild project. If there is another approach to solving the problem I am open to suggestions as long as I can include it into the automation of the superbuild project. Any help will be appreciated.
Run_bash_command.cmake contents:
SET( ENV{MSYSTEM} MINGW64 )
SET( DIR_CONTAINING_BOOTSTRAP_SH C:/bash_test )
SET( BASH_COMMAND_TO_RUN bash -v ./bootstrap.sh )
EXECUTE_PROCESS( COMMAND ${BASH_COMMAND_TO_RUN}
WORKING_DIRECTORY ${DIR_CONTAINING_BOOTSTRAP_SH} RESULT_VARIABLE command_result )
IF( NOT "${command_result}" STREQUAL "0" )
MESSAGE( FATAL_ERROR "Error: command_result='${command_result}'" )
ENDIF()
Environment Setup
I followed the directions to setup MSYS2 64bit and added the mingw-w64 toolchain as well as cmake using the command pacman -S base-devel git mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain
To run the commands I use the MinGW-w64 Win64 Shell that is installed with MSYS2
The file bootstrap.sh comes from the libusb github repository and the folder c:/bash_test contains a clone of the master branch
Output
$ bash -v ./bootstrap.sh output:
$ bash -v ./bootstrap.sh
#!/bin/sh
if ! test -d m4 ; then
mkdir m4
fi
autoreconf -ivf || exit 1
autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I m4
autoreconf: configure.ac: tracing
autoreconf: running: libtoolize --copy --force
libtoolize: putting auxiliary files in '.'.
libtoolize: copying file './ltmain.sh''
...<clipped output due to length>...
configure.ac:29: installing './install-sh'
configure.ac:29: installing './missing'
examples/Makefile.am: installing './depcomp'
autoreconf: Leaving directory `.'
$ cmake -P Run_bash_command.cmake output:
$ cmake -P Run_bash_command.cmake
#!/bin/sh
if ! test -d m4 ; then
mkdir m4
fi
autoreconf -ivf || exit 1
autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I m4
aclocal-1.15: error: aclocal: file '/msys64/usr/share/aclocal/xsize.m4' does not exist
autoreconf: aclocal failed with exit status: 1
CMake Error at Run_bash_command.cmake:10 (MESSAGE):
Error: command_result='1'
Things I have tried:
Substituting bash -l -c but this causes the shell to default to the home directory and then it is unable to find the file bootstrap.sh
Verified the correct version of bash is found by inspecting my environmental PATH varaiable
Verified MSYS2 and its packages are up to date
Using sh instead of bash
Calling autoreconf -ivf directly, but the same issue occurs
Using Unix style paths instead of Windows style
I was able to fix the issue using the code below.
Run_bash_command.cmake contents:
SET( DIR_CONTAINING_BOOTSTRAP_SH /C/bash_test )
SET( BASH_COMMAND_TO_RUN bash -l -c "cd ${DIR_CONTAINING_BOOTSTRAP_SH} && sh ./bootstrap.sh" )
EXECUTE_PROCESS( COMMAND ${BASH_COMMAND_TO_RUN}
WORKING_DIRECTORY ${DIR_CONTAINING_BOOTSTRAP_SH} RESULT_VARIABLE command_result )
IF( NOT "${command_result}" STREQUAL "0" )
MESSAGE( FATAL_ERROR "Error: command_result='${command_result}'" )
ENDIF()
Important Notes:
Using bash -l causes the shell to default to the home directory, to get around this I added a change directory cd <path> command to get us back to the directory where we want to issue the bash command.
Using -c causes bash to read a command from a string. Since we want to issue two commands, one for change directory and one for running the shell script, we need to use && to chain the commands together as well as use "quotation marks" to make sure the entire command is read properly as a string.

Use shell command as INSTALL_COMMAND to ExternalProject_Add

Is it possible to use any shell command for the INSTALL_COMMAND phase of cmake's ExternalProject_Add? e.g.
ExternalProject_Add(leveldb
GIT_REPOSITORY git#github.com:google/leveldb.git
GIT_TAG v1.18
CONFIGURE_COMMAND ./build_detect_platform build.settings .
BUILD_COMMAND make -j 8
BUILD_IN_SOURCE 1
INSTALL_COMMAND ""
)
# INSTALL_COMMAND "mkdir -p ${CMAKE_BINARY_DIR}/lib/ \
# && find . \( -name \"*${CMAKE_SHARED_LIBRARY_SUFFIX}\" -or -name \"*${CMAKE_STATIC_LIBRARY_SUFFIX}\" \
# -exec cp {} ${CMAKE_BINARY_DIR}/lib/\;\) \
# && cp -r ./include ${CMAKE_BINARY_DIR}")
I commented out the INSTALL_COMMAND I want to use, find and cp don't seem to be allowed, "No such file or directory, error 127" is the result of using this.
Direct Answer:
install(CODE "execute_process(...)")
The SCRIPT and CODE signature:
install([[SCRIPT <file>] [CODE <code>]] [...])
The SCRIPT form will invoke the given CMake script files during installation. If the script file name is a relative path it will be interpreted with respect to the current source directory. The CODE form will invoke the given CMake code during installation. Code is specified as a single argument inside a double-quoted string. For example, the code
install(CODE "MESSAGE(\"Sample install message.\")")
will print a message during installation.
Food for thought:
You might be in a hurry to get something done quickly. Do consider this if you have time or you can get back to it and fix it.
One of the main reasons why people love cmake is because of it's cross platform nature. Any project when properly coded with this aspect in mind will work with either on Linux or Windows or any other operating supported system. The various generators will work happily if the developer had this in mind. My suggestion is to convert the shell commands into cmake in a cross platform way, put them in a separate *.cmake file and execute them using cmake -E option.
Here is an extract from a working project that I had worked on in the past.
project_build_steps.cmake
message(VAR1=${VAR1}) # These variables can be passed from the invocation place
message(VAR2=${VAR2}) # You can use them in the build steps
if("${BUILD_STEP}" STREQUAL "patch")
message("BUILD_STEP: patch")
# Put your patch steps using cmake
endif()
if("${BUILD_STEP}" STREQUAL "configure")
message("BUILD_STEP: configure")
# Put your configure steps using cmake
endif()
if("${BUILD_STEP}" STREQUAL "build")
message("BUILD_STEP: build")
# Put your build steps using cmake
endif()
if("${BUILD_STEP}" STREQUAL "install")
message("BUILD_STEP: install")
# Put your install steps using cmake
endif()
CMakeLists.txt (Option 1)
set(CMAKE_COMMAND /usr/bin/cmake)
set(PROJECT_BUILD_STEPS_FILE project_build_steps.cmake)
ExternalProject_Add(
project_name
SOURCE_DIR /path/to/project/source
PATCH_COMMAND ${CMAKE_COMMAND} -DBUILD_STEP=patch -P ${PROJECT_BUILD_STEPS_FILE}
CONFIGURE_COMMAND ${CMAKE_COMMAND} -DBUILD_STEP=configure -P ${PROJECT_BUILD_STEPS_FILE}
BUILD_COMMAND ${CMAKE_COMMAND} -DBUILD_STEP=build -P ${PROJECT_BUILD_STEPS_FILE}
INSTALL_COMMAND ${CMAKE_COMMAND} -DBUILD_STEP=install -P ${PROJECT_BUILD_STEPS_FILE}
)
If you do not want to use ExternalProject_Add you can use something like following. This will also give you individual build targets like make project_patch, make project_configure, make project_build, make project_install.
CMakeLists.txt (Option 2)
set(CMAKE_COMMAND /usr/bin/cmake)
set(PROJECT_BUILD_STEPS_FILE project_build_steps.cmake)
set(STAMP_FILE_PROJECT_PATCH .project_patch_done)
add_custom_command(
OUTPUT ${STAMP_FILE_PROJECT_PATCH}
COMMAND ${CMAKE_COMMAND} -DVAR1=value1 -DSTEP=patch -P ${PROJECT_BUILD_STEPS_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${STAMP_FILE_PROJECT_PATCH}
)
add_custom_target(project_patch DEPENDS ${STAMP_FILE_PROJECT_PATCH})
set(STAMP_FILE_PROJECT_CONFIGURE .project_configure_done)
add_custom_command(
OUTPUT ${STAMP_FILE_PROJECT_CONFIGURE}
COMMAND ${CMAKE_COMMAND} -DSTEP=configure -P ${PROJECT_BUILD_STEPS_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${STAMP_FILE_PROJECT_CONFIGURE}
)
add_custom_target(project_configure DEPENDS project_patch ${STAMP_FILE_PROJECT_CONFIGURE})
set(STAMP_FILE_PROJECT_BUILD .project_build_done)
add_custom_command(
OUTPUT ${STAMP_FILE_PROJECT_BUILD}
COMMAND ${CMAKE_COMMAND} -DSTEP=build -P ${PROJECT_BUILD_STEPS_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${STAMP_FILE_PROJECT_BUILD}
VERBATIM
)
add_custom_target(project_build DEPENDS project_configure ${STAMP_FILE_PROJECT_BUILD})
set(STAMP_FILE_PROJECT_INSTALL .project_install_done)
add_custom_command(
OUTPUT ${STAMP_FILE_PROJECT_INSTALL}
COMMAND ${CMAKE_COMMAND} -DSTEP=install -P ${PROJECT_INSTALL_STEPS_FILE}
COMMAND ${CMAKE_COMMAND} -E touch ${STAMP_FILE_PROJECT_INSTALL}
VERBATIM
)
add_custom_target(project_install DEPENDS project_build ${STAMP_FILE_PROJECT_INSTALL})

Resources