Как назначить вывод команды в переменную Makefile - PullRequest
181 голосов
/ 07 января 2010

Мне нужно выполнить некоторые правила make условно, только если установленный Python больше определенной версии (скажем, 2.5).

Я думал, что мог бы сделать что-то вроде выполнения:

python -c 'import sys; print int(sys.version_info >= (2,5))'

, а затем с помощью вывода («1», если в порядке, «0» в противном случае) в операторе ifeq make.

В простом сценарии оболочки bash это просто:

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

но это не работает в Makefile.

Есть предложения? Я мог бы использовать любой другой разумный способ обойти это.

Ответы [ 6 ]

288 голосов
/ 07 января 2010

Используйте встроенный Make shell, как в MY_VAR=$(shell echo whatever)

me@Zack:~$make
MY_VAR IS whatever

me@Zack:~$ cat Makefile 
MY_VAR := $(shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)
24 голосов
/ 22 апреля 2016

Завершение назначения в eval работает для меня.

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)
12 голосов
/ 26 января 2018

Вот немного более сложный пример с трубопроводом и назначением переменной внутри рецепта:

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)
11 голосов
/ 23 октября 2017

Я пишу ответ, чтобы улучшить видимость фактического синтаксиса, который решает проблему. К сожалению, то, что кто-то может считать тривиальным, может стать очень серьезной головной болью для человека, который ищет простой ответ на разумный вопрос.

Поместите в файл "Makefile" следующее.

MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

Вы хотели бы увидеть следующее (при условии, что у вас установлен последний Python).

make
MY_VAR IS 1

Если вы скопируете и вставите вышеуказанный текст в Makefile, вы это получите? Возможно нет. Вы, вероятно, получите сообщение об ошибке, например, здесь:

makefile: 4: *** отсутствует разделитель. Стоп

Почему: потому что, хотя я лично использовал настоящую вкладку, Переполнение стека (пытаясь быть полезным) преобразует мою вкладку в несколько пробелов. Вы, разочарованный гражданин Интернета, теперь копируете это, думая, что теперь у вас есть тот же текст, который я использовал. Команда make теперь читает пробелы и обнаруживает, что команда «all» неверно отформатирована. Поэтому скопируйте приведенный выше текст, вставьте его, а затем преобразуйте пробел перед «@echo» во вкладку, и этот пример, наконец, должен, надеюсь, сработать.

3 голосов
/ 20 февраля 2019

С GNU Make вы можете использовать shell и eval для хранения, запуска и назначения вывода из произвольных вызовов командной строки. Разница между примером ниже и теми, которые используют :=, состоит в том, что присвоение := происходит один раз (когда оно встречается) и для всех. Рекурсивно развернутые переменные, установленные с =, немного более "ленивы"; ссылки на другие переменные остаются до тех пор, пока не будет указана ссылка на саму переменную, и последующее рекурсивное расширение происходит каждый раз, когда на ссылку ссылается , что желательно для создания «согласованных, вызываемых фрагментов». См. руководство по настройке переменных для получения дополнительной информации.

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)
2 голосов
/ 28 марта 2019

Остерегайтесь подобных рецептов

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

Он делает две вещи неправильно. Первая строка в рецепте выполняется в отдельном экземпляре оболочки от второй строки. Переменная тем временем теряется. Во-вторых, неправильно, что $ не сбежал.

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

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

Я понимаю, что в оригинальном сообщении говорилось, как получить результаты команды оболочки в переменную MAKE, и этот ответ показывает, как получить ее в переменную оболочки. Но другие читатели могут извлечь выгоду.

Последнее улучшение: если потребитель ожидает, что будет установлена ​​«переменная окружения», вам придется ее экспортировать.

my_shell_script
    echo $MY_ID

понадобится это в make-файле

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_shell_script;

Надеюсь, это кому-нибудь поможет. В общем, следует избегать какой-либо реальной работы вне рецептов, потому что если кто-то использует make-файл с опцией --dry-run, только чтобы УВИДЕТЬ, что он будет делать, у него не будет никаких нежелательных побочных эффектов. Каждый $(shell) вызов оценивается во время компиляции, и некоторая реальная работа может быть случайно выполнена. Лучше по возможности оставлять реальную работу, такую ​​как создание идентификаторов, внутри рецептов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...