Escaping $ dollar sign in CMake - cmake

I'm trying to run a post build command in CMake 3.1.1 via:
ADD_CUSTOM_COMMAND(
TARGET mytarget
POST_BUILD
COMMAND for i in `ls *` \; do echo \$i \; done \;
However, the $i variable is evaluated to nothing although I escape the dollar sign. According to logs the command is evaluated to:
for i in `ls *` ; do echo ; done ;
I tried without escaping the dollar sign, but it led to the same problem. Double slash didn't work either. Now I'm puzzled...
Can you suggest a way to run a command that uses dollar signs?
P.S. This was just an example. My actual command is slightly more complicated and I don't think I can work it out without using dollar signs.

You should use 'make' style escape with double dollar sign:
ADD_CUSTOM_COMMAND(
TARGET mytarget
POST_BUILD
COMMAND for i in `ls *` \; do echo $$i \; done \;
)
Related links:
https://www.gnu.org/software/make/manual/html_node/Variables-in-Recipes.html
https://www.mail-archive.com/cmake#cmake.org/msg11302.html

Use a bracket quote:
ADD_CUSTOM_COMMAND(
TARGET mytarget
POST_BUILD
COMMAND [=[for i in `ls *`; do echo $i; done]=]
)

Related

CMake add_custom_command: How to write a "quoted string" to a file?

I have spent way too much time trying to add a custom command that writes a "quoted string" to a file:
add_custom_command(
OUTPUT file
COMMAND ${CMAKE_COMMAND} -E echo "\"quoted string\"" > file
...
DEPENDS something
VERBATIM (?)
)
I have tried various ways to escape the quotes \", \\", \\\", quoting the whole command, putting the command in variable, but none of them worked. How can this be achieved?
The following works on both Windows(Ninja) and WSL(unix makefiles):
set(my_output what_it_is.txt)
add_custom_command(
OUTPUT ${my_output}
COMMAND ${CMAKE_COMMAND} -E echo \"quoted string\" > ${my_output}
VERBATIM
)
And the output:
$ cat some_bin/what_it_is.txt
"quoted string"
In my limited experience, VERBATIM is usually the key if you're fighting escaping things in custom commands.
Note: I believe that the redirect is platform specific, so you might want to consider doing something like the file command in a CMake script and invoking that script in the custom command, COMMAND ${CMAKE_COMMAND} -P some_script.cmake.

CMake: unescape whitespace when echoing to a file

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
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
cmake: How to include literal double-quote in custom command?
cmake: when to quote variables?
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).

custom_command ECHO with special character

I am trying to add a custom_command with CMake and call COMMAND echo "$" > file.txt
as long as I put $ in it, the config file will generate but failed to build.
I have also tried echo "\$" and doesn't seems to work.
add_custom_command( TARGET ${TARGET_NAME}
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out
COMMAND echo "-keep class com.android.**\$* { ; }" >> ./proguard.txt
)
The cmake command works but as long as I call ninja, I got the following error:
error: 'src', needed by 'all', missing and no known rule to make it
Seems like cmake is unable to generate the build step. My intention is to print that **$ into a file.
Both
COMMAND echo "$$" > file.txt
and
COMMAND echo "$" > file.txt VERBATIM
output $ sign into given file.
EDIT: This works on makefile generators, and only when make is run from the terminal. Generally redirection sign ">" is not worked as expected in COMMAND expression.

CMake: How to output semicolon (;) as command options in ADD_CUSTOM_TARGET

Suppose I have the following CMake snippet:
MACRO(ADD_CUSTOM_TARGET_COMMAND tag OUTPUT file)
ADD_CUSTOM_TARGET(tag
${ARGN}
)
ADD_CUSTOM_TARGET(OUTPUT file
${ARGN}
)
ENDMACRO()
ADD_CUSTOM_TARGET_COMMAND(tag
OUTPUT file
COMMAND git tag -a -m "${msg}" 1.0.0 HEAD
VERBATIM
)
If msg contains semicolon such as "msg1;msg2", then the actual command is expanded to
git -a -m "msg1" "msg2" 1.0.0. HEAD
which leads to a syntax error.
I have tried to use \ to escape the ; but to no avail.
What should I do?
There is a special token since 2.8.11 version: $<SEMICOLON> (http://www.cmake.org/cmake/help/v2.8.11/cmake.html#command:add_custom_command).
I use it for example for such find command:
find /path/to/search -name some\*name \! -path excluded\*Pattern -exec ln -sf "{}" \;
the following way:
set(
FIND_ARGUMENTS
"${SEARCH_PATH} -name some\\*name \\! -path exclued\\*Pattern -exec ln -sf {} \\$<SEMICOLON>"
)
separate_arguments(FIND_ARGUMENTS)
add_custom_command(TARGET ${PROJECT}
POST_BUILD
COMMAND "find" ${FIND_ARGUMENTS}
WORKING_DIRECTORY ${WORKING_PATH}
)
Note that with separate_arguments VERBATIM parameter for add_custom_command is not needed.
CMake manages list using semi-colon, so I see no better way than just writing the message to file and git tag -F file

cmake parse arguments incorrectly in add_custom_target command

I wrote a cmake command like this:
add_custom_target(testar
COMMAND clearmake -C gnu ${CMD_ARGS})
the CMD_ARGS is defined on the command line like:
cmake -DCMD_ARGS="-d -w"
But in the generated makefile, the -d -w is changed into -d\ -w; it added a slash before all spaces, resulting in:
clearmake -C gnu -d\ -w
If I use VERBATIM option in add_custom_target, cmake doesn't add a slash, but it quotes the argument like
clearmake -C gnu "-d -w"
which is incorrect, I would like:
clearmake -C gnu -d -w
What is the syntax needed to generate the above target?
The arguments are expected to be a list, which "-d -w" is not (it's just a string). You can do two things:
Pass in the arguments as -DCMD_ARGS="-d;-w" (the space is a semicolon)
Use the separate_arguments command on CMD_ARGS before you pass it into add_custom_target (which makes spaces semi-colons to generate a proper list).
Nothing in the add_custom_target command needs to change, the input to CMake is incorrect which can be fixed with 1 or handled by 2.