Search code examples
cmakeclangllvmllvm-cov

CMake custom command formatting issue


I'm trying to write a custom CMake command that will execute llvm-cov as a part of the coverage analysis. What I have is a string variable tests that holds the list of test executables and was defined as:

set(tests "\"path/to/test1\" -object \"path/to/test2\" -object \"path/to/test3\"")

Note that -object specifier precedes every test except the first one, since that's the format that llvm-cov expects. When I print that variable, I get:

"path/to/test1" -object "path/to/test2" -object "path/to/test3"

which is what I wanted to achieve.

However, when I create a custom CMake command like this:

add_custom_command(
        TARGET coverage
        COMMAND llvm-cov show -instr-profile ${profdata_file} ${tests} -format html -o coverage)

The command is not correctly executed since it expands to:

llvm-cov show -instr-profile path/to/profdata "/path/to/test1"\ -object\  "path/to/test2"\ -object\  "path/to/test3" -format html -o coverage

The problem is that backslashes are added for some reason before space characters. I've tried using VERBATIM and COMMAND_EXPAND_LISTS options of add_custom_command, but neither worked. How can I solve this formatting issue?


Solution

  • What you've done is define the arguments all in one string together. And CMake faithfully passes it all as a single commandline argument (contributes +1 to "argc" and one single element to "argv" of the build tool that runs the command). That might not be a problem if the program parsing the command is written to deal with that, but it seems that in this case, the specific program isn't.

    What you're observing the backslashes before the spaces is just the way that spaces are escaped so that it's not used as a separator between multiple commandline arguments and instead is treated as part of a continuous commandline argument in whatever tool is being used in your context.

    I'm pretty sure what you want is to set tests as a list variable. Multiple value arguments to set get turned into elements of a list variable being set, and unquoted arguments are divided into elements in the same way lists are.

    set(tests "path/to/test1" -object "path/to/test2" -object "path/to/test3")
    

    And pass VERBATIM to your call to add_custom_command so that quoting things get passed properly to the build tool.

    Note that (I think) technically you could pass tests as a quoted argument if you also pass COMMAND_EXPAND_LISTS to the add_custom_command invocation.