ExternalProject_Add commands with wildcard

I have a little problem with ExternalProject_Add when I try to use a command with a wildcard ‘*’. The command statement is generated with quotes around the wildcard statement which causes the command to fail.
For example:

    INSTALL_COMMAND
        ${CMAKE_COMMAND} -E make_directory
            ${CMAKE_CURRENT_BINARY_DIR}/install/usr/include
    COMMAND
        ${CMAKE_COMMAND} -E copy
            libexfat/*.h
            ${CMAKE_CURRENT_BINARY_DIR}/install/usr/include

The generated command is:

build exfat/src/exfat-stamp/exfat-install | ${cmake_ninja_workdir}exfat/src/exfat-stamp/exfat-install: CUSTOM_COMMAND exfat/src/exfat-stamp/exfat-build
  COMMAND = cd /home/users/dlind/packages/build-armv5te/exfat/src/exfat && /usr/bin/cmake -E make_directory /home/users/dlind/packages/build-armv5te/exfat/install/usr/include && /usr/bin/cmake -E copy "libexfat/*.h"/home/users/dlind/packages/build-armv5te/exfat/install/usr/include && /usr/bin/cmake -E touch /home/users/dlind/packages/build-armv5te/exfat/src/exfat-stamp/exfat-install
  DESC = Performing install step for 'exfat'
  pool = console
  restat = 1

Notice

/usr/bin/cmake -E copy "libexfat/*.h" /home/dlind/packages/build-armv5te/exfat/install/usr/include

This causes the copy command to fail. If I edit the ninja project file and remove the quotes the command works.
This behavior happens with any part of ExternalProject_Add when a asterisk is used. The command gets quoted.
Is there another way to specify an asterisk? There isn’t an $<ASTERISK> equivalent of $<SEMICOLON>.

This is really annoying. I can use add_custom_target and the output is generated without quotes.

add_custom_target(test-target
    COMMAND
        ${CMAKE_COMMAND} -E copy
            libexfat/*.h
            ${CMAKE_CURRENT_BINARY_DIR}/install/usr/include
)
build exfat/CMakeFiles/test-target | ${cmake_ninja_workdir}exfat/CMakeFiles/test-target: CUSTOM_COMMAND
  COMMAND = cd /home/users/dlind/packages/build-armv5te/exfat && /usr/bin/cmake -E copy libexfat/*.h /fast/users/dlind/pss/packages/build-armv5te/exfat/install/usr/include

Wildcard expansion is a shell feature. You are assuming that ExternalProject_Add() and add_custom_target() use a shell to execute their command(s), but this is not guaranteed to be the case. The add_custom_target() command’s documentation alludes to this, but doesn’t make this as clear as it could. The fact that add_custom_target() appears to expand your wildcard correctly should be seen as an implementation detail that you should not rely on.

If you want shell expansion, you need to invoke a shell explicitly as your COMMAND. Note that your command is platform-specific if it relies on wildcard expansion.

For the particular example you’ve given, you can create a CMake script that contains a file(COPY) command with appropriate arguments to do the copying. This would replace the shell wildcard expansion with pattern matching performed by the file() command. This would be platform independent and would avoid the problem you’ve highlighted.

There are two phases to CMake.

  1. The configure phase: When CMake is executed and the -G<generator> creates the build phase files (ninja project files, makefiles, etc.)
  2. The build phase: When the generator is executed (ninja, make, etc.)

In this case, there is a CMake bug with the configure phase when using ExternalProject_Add(). When an asterisk is used in any step, the generator files are created by CMake with quotes around the command with the asterisk. This causes the build phase to fail because the shell doesn’t know how to handle a command with quotes.

Doing a manual execution of this command:
/usr/bin/cmake -E copy "libexfat/*.h" /home/users/dlind/packages/build-armv5te/exfat/install/usr/include

Produces:
Error copying file "libexfat/*.h" to "/home/users/dlind/packages/build-armv5te/exfat/install/usr/include"

If I replace using “cmake -E copy” with the shell "cp’:
cp "libexfat/*.h" /home/users/dlind/packages/build-armv5te/exfat/install/usr/include

Produces:
cp: cannot stat 'libexfat/*.h': No such file or directory

In both cases the failure is because of the quotes. Remove the quotes and the command at least attempts to copy the files specified.

BTW, you can’t use file(COPY) with either ExternalProject_Add() COMMANDs or add_custom_target() COMMANDs. These commands are executed during the build phase.