Surrounding Paths with Spaces in Quotes: Where Does the Magic Happen?

On Windows, the following CMake code correctly wraps the path containing spaces in quotes:

set( cmdargs "-options" "${CMAKE_CURRENT_BINARY_DIR}/path with spaces" )

add_custom_command( OUTPUT MyGeneratedHeader.h
    COMMAND MyCompiler.exe ARGS ${cmdargs}
    USES_TERMINAL VERBATIM )

In Visual Studio, this generates the following command line:

MyCompiler.exe -options "D:/Project/_build/path with spaces"

So clearly there is some magic happening that puts quotes around the path, but not around -options.

However, that magic does not work for VS_DEBUGGER_COMMAND_ARGUMENTS because

set_target_properties( "MyCompiler" PROPERTIES 
    VS_DEBUGGER_COMMAND_ARGUMENTS "${cmdargs}" )

results in

-options D:/Project/_build/path with spaces

in the Debugging → Command Line field of the MyCompiler project.

  • When and where does CMake surround the path containing spaces with quotes?
  • Can that same logic be applied to VS_DEBUGGER_COMMAND_ARGUMENTS as well?
1 Like

Did you notice you quoted on of them and not the other?

The quoting is right in the CMake code here. set_target_properties takes a list of pairs, so not quoting ${cmdargs} there would try to set a property named "${CMAKE_CURRENT_BINARY_DIR}/path with spaces" without a value (causing an error) and leave VS_DEBUGGER_COMMAND_ARGUMENTS with just -options.

Quoting is weird and inconsistent with CMake. I would recommend adding \" to cmdargs around arguments you want quoted for the target properties.

Oh, there’s another difference here: VERBATIM in add_custom_command. I suspect that if you get it to work with set_target_properties, you can drop the VERBATIM and use the same value for both.

FWIW, I never use VERBATIM because I can’t remember all of the rules CMake applies and I find it easier to do some safety in the command specification myself, but YMMV.

I would recommend adding \" to cmdargs around arguments you want quoted for the target properties. […] I suspect that if you get it to work with set_target_properties , you can drop the VERBATIM and use the same value for both.

Yes, the workaround I came up with was surrounding the path with \" and not using VERBATIM.

VERBATIM and \" passes the escaped quote in the argument to my compiler, which causes issues with parsing the file path because Windows file I/O doesn’t handle quotes in file names. This forced me to write C++ code that strips leading and trailing quotes from paths.

Without VERBATIM and with \" I got what I wanted: the path is correctly quoted in both cases, but the quotes are not part of the argument.

I wanted to keep VERBATIM because the CMake documentation is recommending using it to guarantee that the arguments will be identical across platforms.

But I am still curious what causes the path to be quoted. It clearly is not part of the set() function, so my guess is that add_custom_command() does it. What I can’t figure out is how it decides to quote the second entry in my cmdArgs list, but not the first … Is it going by the presence of spaces in the entry?

It sees a single CMake list item and wants to pass it as a single argument (with VERBATIM). This triggers adding the quotes.

That is fine, but I just cannot keep CMake and shell quoting in mind when looking at these things and I vastly prefer the conformity that not using VERBATIM affords with the “quote all variable expansions that are not part of if (and friends) arguments that care or are intended to be used as a list of arguments” rule I go by.

the documentation states The optional ARGS argument is for backward compatibility and will be ignored. so it seems a version somewhere prior to CMake 3.0 is needed for the first case.

Huh? It is ignored. It doesn’t change anything. So…why would an older CMake be required?

oh, i was just trying to recreate the examples, but i don’t see the same results and i don’t have VS installed.

While the quoting is correct i try to avoid unnecessary quoting. Quoting the target name is confusing and -options does not need quoting but YMMV.

the documentation states The optional ARGS argument is for backward compatibility and will be ignored. so it seems a version somewhere prior to CMake 3.0 is needed for the first case.

Huh, I am running that code example with CMake 3.20.5 and the ARGS arguments are being used. Thanks for the heads up, I’ll change the code to

add_custom_command( OUTPUT MyGeneratedHeader.h
    COMMAND MyCompiler.exe ${cmdargs}
    USES_TERMINAL VERBATIM )

as to not run the risk of it unexpectedly breaking with a new CMake version.

It sees a single CMake list item and wants to pass it as a single argument (with VERBATIM ). This triggers adding the quotes.

If I understand it correctly, the logic is something like this:

  1. The COMMAND property of add_custom_command() expects a list.
  2. While parsing said list, if an entry contains spaces the entry’s content will be surrounded by quotes.

I have tested that this behavior is identical with and without VERBATIM. VERBATIM matters only if I explicitly add escaped quotes, which get passed to my executable with VERBATIM and are ignored without VERBATIM.

To bring this all back to what prompted my original post: Would it make sense to add similar logic to VS_DEBUGGER_COMMAND_ARGUMENTS?

My argument is that VS_DEBUGGER_COMMAND_ARGUMENTS explicitly expects command arguments which should be surrounded by quotes if they contain spaces. Currently, if you pass the same list of arguments to add_custom_command() and to VS_DEBUGGER_COMMAND_ARGUMENTS you get two different argument lists.

The main question for someone familiar with the CMake source code is whether set_property(TARGET) already has special-case handling for different target properties. If all target properties are treated the same in set_property() then trying to implement a special case for VS_DEBUGGER_COMMAND_ARGUMENTS makes no sense …