#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
Как вы можете видеть, даже ДОСЛОВНО не все ситуации обрабатываются правильно.