Многострочные команды bash в make файле

У меня очень удобный способ скомпилировать мой проект через несколько строк команд bash. Но теперь мне нужно скомпилировать его через make файл. Учитывая, что каждая команда запускается в своей собственной оболочке, мой вопрос: , что лучший способ запускать многострочную команду bash, зависящую друг от друга, в make файле? Например, например:

for i in `find`
do
    all="$all $i"
done
gcc $all

Кроме того, может ли кто-нибудь объяснить, почему даже однострочная команда bash -c 'a=3; echo $a > file' работает корректно в терминале, но создает пустой файл в случае с файлом?

Ответ 1

Вы можете использовать обратную косую черту для продолжения строки. Однако обратите внимание, что оболочка получает всю команду, объединенную в одну строку, поэтому вам также необходимо завершить некоторые строки точкой с запятой:

foo:
    for i in 'find';     \
    do                   \
        all="$$all $$i"; \
    done;                \
    gcc $$all

UPD.

Но если вы просто хотите взять весь список, возвращаемый вызовом find и передать его в gcc, вам на самом деле не обязательно нужна многострочная команда:

foo:
    gcc 'find'

Или, используя более привычный для оболочки подход $(command) (обратите внимание на $ escaping, хотя):

foo:
    gcc $$(find)

Ответ 2

Как указано в вопросе, каждая подкоманда запускается в своей собственной оболочке. Это делает написание нетривиальных сценариев оболочки немного беспорядочным - но это возможно! Решение состоит в объединении вашего script в то, что make рассмотрит одну подкоманду (одну строку).

Советы по написанию сценариев оболочки в make файлах:

  • Побегите script с помощью $, заменив $$
  • Преобразуйте script, чтобы работать как одна строка, вставив ; между командами
  • Если вы хотите написать script на нескольких строках, выйдите из конца строки с помощью \
  • По желанию начинайте с set -e, чтобы выполнить make make, чтобы прервать сбой в подкоманде
  • Это совершенно необязательно, но вы можете скопировать script с помощью () или {}, чтобы подчеркнуть сплоченность последовательности с несколькими строками - это не типичная последовательность команд makefile

Вот пример, вдохновленный OP:

mytarget:
    { \
    set -e ;\
    msg="header:" ;\
    for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
    msg="$$msg :footer" ;\
    echo msg=$$msg ;\
    }

Ответ 3

Конечно, правильный способ написать Makefile - фактически документировать, какие цели зависят от каких источников. В тривиальном случае предлагаемое решение сделает foo зависимым от себя, но, конечно, make достаточно умен, чтобы отказаться от круговой зависимости. Но если вы добавите временный файл в свой каталог, он "волшебным образом" станет частью цепочки зависимостей. Лучше создать явный список зависимостей раз и навсегда, возможно, через script.

GNU make знает, как запустить gcc, чтобы создать исполняемый файл из набора файлов .c и .h, поэтому, возможно, все, что вам действительно нужно, составляет

foo: $(wildcard *.h) $(wildcard *.c)

Ответ 4

Что не так просто при вызове команд?

foo:
       echo line1
       echo line2
       ....

И для вашего второго вопроса вам нужно избежать $, используя $$ вместо, т.е. bash -c '... echo $$a ...'.

EDIT: ваш пример можно переписать в одну строку script следующим образом:

gcc $(for i in `find`; do echo $i; done)