I am trying to create a command that will copy a data directory from my source directory to my build directory so that it will be grabbed when I package up a build into a zip file. For Windows, my custom command looks like
add_custom_command(TARGET "${target_name}" PRE_BUILD
COMMAND IF NOT EXIST "$<SHELL_PATH:$<TARGET_FILE_DIR:${target_name}>/${dest_relpath}>" mkdir "$<SHELL_PATH:$<TARGET_FILE_DIR:${target_name}>/${dest_relpath}>"
COMMAND cp -auv "${source_dir}/*" "$<TARGET_FILE_DIR:${target_name}>/${dest_relpath}")
And this works well in the MSVC generator, but in the Ninja generator, I get the following in my build.ninja file (edited for length):
But “&&” means “execute the second command if the first command succeeded”, which is not how I thought add_custom_command handled multiple commands. And both IF NOT EXIST and mkdir return errors if the directory does exist, which causes the copy to not happen.
Is this a bug in the Ninja generator for Windows? If not, how should I call add_custom_command in order to get the behavior I want?
Yeah, I would definitely not recommend trying to shoehorn chained commands into an IF statement inside the Ninja file like that. My recommended approach would be to put your logic inside a cmake -P script. For example:
I wrote something that the CMake documentation strongly implies will work one way, and that does in fact work that way in both the “Unix Makefiles” and “Visual Studio 15 2017 Win64” generators.
If the Ninja generator generates something that works another way that you would “definitely not recommend”, isn’t that a problem?
OK, I know what the actual problem is now. On Windows, && binds more tightly than IF does. So the PRE_LINK command in the first post is interpreted as
IF NOT EXIST c:\build-dir\Debug\data
( mkdir c:\build-dir\Debug\data && cp -auv C:/source-dir/data/* C:/build-dir/Debug/data )
which I would have said as one COMMAND if that is what I intended. By using two separate COMMANDs, I was stating my intention to be
( IF NOT EXIST c:\build-dir\Debug\data mkdir c:\build-dir\Debug\data )
&& ( cp -auv C:/source-dir/data/* C:/build-dir/Debug/data )
Would it be possible to get the Ninja generator for Windows to add parentheses to group things so such ambiguity doesn’t happen? Now that I know that the lack of parentheses is the problem, I can work around this myself, but I would like to help other people avoid pitfalls.
I vaguely recall someone trying to modify the generator to add ( and ) but encountering problems. Unfortunately I could not find anything in a quick search just now. If anyone can get that working in a compatible way I’d be open to integrating it into the generator.
add_custom_command's abstraction isn’t really meant to allow arbitrary shell-specific code since it’s supposed to be cross-platform. You could use ${CMAKE_COMMAND} -E make_directory ... to make the directory instead. It doesn’t complain about already-existing directories.