Вызов команд bash в make файле

109
16

Внутри makefile я пытаюсь проверить, был ли файлA изменен совсем недавно, чем fileB. Используя несколько предыдущих сообщений (это и это) в качестве ссылок, я придумал это как попытку сохранить время с момента последней модификации файла в виде переменной:

(Я предпочел бы, чтобы это было функцией вне рецепта make, но по одному шагу за раз).

    .PHONY: all clean

all : (stuff happens here)

radio :
BASE_MOD_TIME="$( expr $(date +%s) - $(date +%s -r src/radio_interface/profile_init.c) )"
@echo "$(BASE_MOD_TIME)"

Я думал, что я буду назначать вывод команды expr переменной BASE_MOD_TIME, но вывод:

    bash-4.1$
BASE_MOD_TIME=""
echo ""

Что я здесь делаю неправильно? Простые попытки сохранить вывод ls -l также не сработали.

спросил(а) 2016-09-14T00:46:00+03:00 4 года, 1 месяц назад
1
Решение
59

Сделать переменные обычно глобальными, и вы обычно не устанавливаете переменные make в рецепте. Рецепт - это просто список команд, которые будут выполняться оболочкой, и поэтому то, что выглядит как назначение переменной в рецепте, - это назначение переменной оболочки.

Однако каждая строка в рецепте make запускается в своем собственном подпроцессе оболочки. Поэтому переменная, установленная в одной строке, не будет видна в другой строке; они не настойчивы. Это делает установку переменных оболочки в рецептах менее полезной. [Примечание 1]

Но вы можете объединить несколько строк рецепта в одну команду оболочки, используя escape-обратную косую черту в конце строки, и помнить о завершении отдельных команд точкой с запятой (или, лучше, связать их с &&), поскольку обратная косая черта Новая строка не будет передана оболочке. Кроме того, не забудьте избежать символов $ чтобы они были переданы оболочке, а не интерпретированы make.

Таким образом, вы можете сделать следующее:

radio:
@BASE_MOD_TIME="$$( expr $$(date +%s) - $$(date +%s -r src/radio_interface/profile_init.c) )"; \
echo "$$BASE_MOD_TIME"; \
# More commands in the same subprocess

Но это становится довольно неудобным, если есть несколько команд, и лучше всего, как правило, писать сценарий оболочки и вызывать его из рецепта (хотя это означает, что Makefile больше не является самодостаточным).

Gnu make предоставляет два способа установки переменных make в рецепте:

1. Целевые переменные.

Вы можете создать целевую переменную (которая не совсем локальна для цели), добавив строку, например:

target: var := value

Чтобы установить переменную из команды оболочки, используйте функцию shell:

target: var := $(shell ....)

Эта переменная будет доступна в целевом рецепте и всех зависимостях, вызванных целевой. Следует отметить, что зависимость вычисляется только один раз, поэтому, если она может быть вызвана другой цели, целевой конкретной переменной может быть или не быть доступны в зависимости, в зависимости от того, в котором make удовлетворял зависимости.

2. Используя функцию eval

Поскольку разложение рецептов всегда отложено, вы можете использовать функцию eval внутри рецепта, чтобы отложить назначение переменной make. Функция eval может быть размещена довольно хорошо в любом месте рецепта, потому что его значение - пустая строка. Однако eval переменной присваивает глобальное назначение переменной; он будет виден во всем make файле, но его значение в других рецептах будет зависеть, опять же, от порядка, в котором производится оценка рецептов, что не обязательно предсказуемо.

Например:

radio:
$(eval var = $(shell ...))

Заметки:

Вы можете изменить это поведение с помощью .ONESHELL: псевдо-target, но это применимо ко всему Makefile; нет никакого способа пометить один рецепт как выполненный в одном подпроцессе. Поскольку изменение поведения может неожиданно нарушать рецепты, я обычно не рекомендую эту функцию.

ответил(а) 2016-09-14T01:37:00+03:00 4 года, 1 месяц назад
59

Что с этим не так?


fileB: fileA
@echo $< was modified more recently than $@

ответил(а) 2016-09-14T02:24:00+03:00 4 года, 1 месяц назад
41

Вместо того, чтобы заставить makefile выполнять весь тяжелый подъем через некоторые команды bash, я просто назвал отдельный сценарий bash. Это дало намного больше ясности для новичка, чтобы bash скрипты, как я, так как мне не нужно было беспокоиться о том, чтобы избежать текущей оболочки, используемой make.

Окончательное решение:

    .PHONY: all clean

all : (stuff happens here)

radio :
./radio_init_check.sh
$(MKDIR_P) $(OBJDIR)
make $(radio_10)

с radio_init_check.sh - мой скрипт для скриптов.

ответил(а) 2016-09-14T03:32:00+03:00 4 года, 1 месяц назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема