1

As part of our build process we automatically run unit tests through valgrind during the actual build (ie: it's not a separate target such as make test)

We create a sentinel file when the tests pass, so that subsequent build won't rerun the tests if not necessary.

We also save the command line and test output to a file.

Here I have built the valgrind command line:

set(VALGRIND_BIN  "valgrind")
set(VALGRIND_OPTS "--leak-check=full --track-origins=yes")

set(VALGRIND_CMD "${VALGRIND_BIN} ${VALGRIND_OPTS}")
separate_arguments(VALGRIND_CMD)

These are the "passed" sentinal file, and the test output file.

set(OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${ARG_NAME}.output)
set(PASSED_FILE ${CMAKE_CURRENT_BINARY_DIR}/${ARG_NAME}.passed)

Here I add a custom_command which works in the following way:

  • It echos the command line and saves it to the output file
  • It runs the test through valgrind, saving all output to the output file
  • If the test doesn't pass it will cat the output file and the command fails
  • If the test passes it will touch the passed sentinel file.

Here is the cmake source:

add_custom_command(
    OUTPUT
        ${PASSED_FILE}

    COMMAND
        echo "\"${VALGRIND_BIN} ${VALGRIND_OPTS} $<TARGET_FILE:${TEST_NAME}>\"" > ${OUTPUT_FILE}

    COMMAND
        ${VALGRIND_CMD} $<TARGET_FILE:${TEST_NAME}> >> ${OUTPUT_FILE} 2>&1 || (cat ${OUTPUT_FILE} && false)

    COMMAND
        ${CMAKE_COMMAND} -E touch ${PASSED_FILE}

    COMMENT
        "Running ${ARG_NAME} tests"

    DEPENDS
        ${TEST_NAME}

    USES_TERMINAL
    )

Unfortunately cmake is escaping all the whitespace in my echo of the test command line, so that the first line in the output file looks like this:

valgrind\ --leak-check=full\ --track-origins=yes\ /home/steve/src/test\

I have proven to myself the escapes aren't in the variables, as if I output a message they aren't in there.

message(STATUS "\"${VALGRIND_BIN} ${VALGRIND_OPTS} $<TARGET_FILE:${TEST_NAME}>\"")

The resulting output:

-- "valgrind --leak-check=full --track-origins=yes $<TARGET_FILE:test>"

Question:

How can I unescape the whitespace when echoing to a file?

That is, how can I have the line not be this:

valgrind\ --leak-check=full\ --track-origins=yes\ /home/steve/src/test\

but instead be this:

valgrind --leak-check=full --track-origins=yes /home/steve/src/test
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • https://cmake.org/cmake/help/v3.8/command/add_custom_command.html - maybe the "VERBATIM" flag would work for you? – MuertoExcobito Apr 24 '17 at 02:12
  • @MuertoExcobito whilst this works for the `echo`, it breaks the 2nd part of the test command (the call to `|| (cat ${OUTPUT_FILE} && false)`) as it surrounds the parens with quotes `"("`, resulting in `|| "(" cat OUTPUT_FILE && false ")"` – Steve Lorimer Apr 24 '17 at 16:03

2 Answers2

2

You can put everything into a list, which will be expanded and the spaces will not be escaped.

Because CMake will be escaping spaces if it believes the string to be a single argument. Giving it as a list will take every element as a separate argument:

list(APPEND VALGRIND_CMD "$<TARGET_FILE:${TEST_NAME}>")

add_custom_command(
    OUTPUT
        ${PASSED_FILE}
    COMMAND
        ${CMAKE_COMMAND} -E echo \"${VALGRIND_CMD}\" > ${OUTPUT_FILE}
    COMMAND
        ${VALGRIND_CMD} >> ${OUTPUT_FILE} 2>&1 || (cat ${OUTPUT_FILE} && false)
    COMMAND
        ${CMAKE_COMMAND} -E touch ${PASSED_FILE}
    COMMENT
        "Running ${ARG_NAME} tests"
    USES_TERMINAL
)

References

Florian
  • 39,996
  • 9
  • 133
  • 149
0

As pointed by @MuertoExcobito, option VERBATIM cares about properly escaping parameters, no needs in additional double quotes escaped manually:

COMMAND
    echo "${VALGRIND_BIN} ${VALGRIND_OPTS} $<TARGET_FILE:${TEST_NAME}>" > ${OUTPUT_FILE}
VERBATIM

(Outer double quotes are needed for CMake do not separate echo parameters).

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Whilst this works for the `echo`, it breaks the 2nd part of the test command (the call to `|| (cat ${OUTPUT_FILE} && false)`) as it surrounds the parens with quotes `"("`, resulting in `|| "(" cat OUTPUT_FILE && false ")"` – Steve Lorimer Apr 24 '17 at 16:01
  • So the question should be titled "How to unescape whitespaces without VERBATIM" :D.Well, as for this specific case, why do you ever need to separate arguments for `echo` (outer double quotes)? When output, this command automatically delimits its argument with spaces. – Tsyvarev Apr 24 '17 at 17:56