Команды оболочки с использованием Cmake add_custom_command в Linux

#linux #shell #cmake #post-build

Вопрос:

Я не могу понять правильный синтаксис для запуска команды оболочки на этапе после сборки в Cmake в Linux. Я могу выполнить простую echo работу, но когда я хочу, например, повторить все файлы и echo те, я получаю ошибку.

Следующие работы:

 add_custom_command(TARGET ${MY_LIBRARY_NAME}
                   POST_BUILD
                   COMMAND echo Hello world!
                   USES_TERMINAL)
 

Это правильно печатает Hello world! .

Но теперь я хотел бы просмотреть все .obj файлы и распечатать их. Я подумал, что должен сделать:

 add_custom_command(TARGET ${MY_LIBRARY_NAME} 
                   POST_BUILD
                   COMMAND for file in *.obj; do echo @file ; done 
                   VERBATIM
                   USES_TERMINAL)
 

Но это приводит к следующей ошибке:

 /bin/sh: 1: Syntax error: end of file unexpected
 

Я пробовал всевозможные комбинации с кавычками или начиная с sh , но, похоже, ничего из этого не работает. Как я могу сделать это правильно?

Комментарии:

1. $file или bash -c "$(for file in *.obj; do echo $file ; done)"

Ответ №1:

add_custom_command (и все другие функции CMake, которые выполняют a COMMAND ) не запускают сценарии оболочки, а разрешают выполнение только одной команды. Это USES_TERMINAL не приводит к тому, что команда выполняется в реальном терминале или позволяет использовать встроенные оболочки, такие как for цикл.

Из документации:

Чтобы запустить полный сценарий, используйте команду configure_file() или команду file(GENERATE) для его создания, а затем укажите КОМАНДУ для его запуска.

Или, в качестве альтернативы, для очень простых сценариев вы можете сделать то, что предложил @lojza в комментарии к вашему вопросу, и запустить bash команду с фактическим содержимым сценария в качестве аргумента.

 add_custom_command(
  TARGET ${MY_LIBRARY_NAME}
  POST_BUILD 
  COMMAND bash -c [[for file in *.obj; do echo ${file}; done]]
  VERBATIM
)
 

Обратите внимание, что я намеренно использовал здесь необработанный строковый литерал CMake, чтобы ${file} он не расширялся как переменная CMake. Вы также можете использовать bash -c "for file in *obj; do echo $file; done" обычный строковый литерал, в этом случае $file он также не расширяется из-за отсутствия фигурных скобок. Скопировав и вставив код bash из других источников в CMake, прежде чем я узнаю, насколько сложно отследить ошибки, вызванные неожиданным расширением переменных CMake в таких сценариях, поэтому я всегда рекомендую использовать [[ и ]] если вы действительно не хотите расширять переменную CMake.

Однако для вашего конкретного примера выполнения чего-либо со всеми файлами, которые соответствуют шаблону, есть еще более простая альтернатива: используйте find команду вместо цикла bash:

 add_custom_command(
  TARGET ${MY_LIBRARY_NAME}
  POST_BUILD 
  COMMAND find
    -maxdepth 1 
    -name *.obj 
    -printf "%P\n"
  VERBATIM
)
 

Ответ №2:

Иногда правильное написание КОМАНДЫ может быть выполнено с помощью отладки. Под сообщением об ошибке есть точка на строке, которая ее вызывает. Что-то вроде

 /bin/sh: 1: Syntax error: end of file unexpected
make[2]: *** [CMakeFiles/my_target.dir/build.make:57: CMakeFiles/my_target] Error 2
 

Итак, вы можете заглянуть в строку 57 файла CMakeFiles/my_target.dir/build.make и узнать команду, которая на самом деле помещена в файл Makefile:

 CMakeFiles/my_target:
    for file in "*.obj" do echo @file done
 

Как вы можете видеть, CMake удаляет все точки с запятой ( ; ): это связано с тем, что этот символ является разделителем для CMake.

Кавычки с запятой не помогают в дословном режиме:

Коммандант

 COMMAND for file in *.obj ";" do echo @file ";" done
 

преобразуется

 CMakeFiles/my_target:
    for file in "*.obj" ";" do echo @file ";" done
 

Но оболочка не рассматривает точку с запятой в кавычках ( ";" ) как разделитель команд!

Опущение ДОСЛОВНО дает лучшую трансформацию:

 CMakeFiles/my_target:
    for file in *.obj ; do echo @file ; done
 

Следующий шаг-правильно ссылаться на file переменную в скрипте ( @ это определенно неправильный способ). Сценарий должен видеть или $file или ${file} , но и CMake, и Make имеют особый способ обработки доллара ( $ ). ( VERBATIM может автоматически избегать вещей для создания, но мы не можем использовать его из-за точки с запятой.)

Результирующая команда может быть либо

 COMMAND for file in *.obj ";" do echo $file ";" done
 

или

 COMMAND for file in "CMake*" ";" do echo ${file} ";" done
 

Как вы можете видеть, даже ДОСЛОВНО не все ситуации обрабатываются правильно.