Як призначити висновок команди змінній Makefile


225

Мені потрібно виконати деякі правила укладання умовно, лише якщо встановлений Python перевищує певну версію (скажімо, 2.5).

Я думав, що можу зробити щось на кшталт виконання:

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

а потім, використовуючи висновок ('1', якщо добре, '0' в іншому випадку) в ifeqоператорі make.

У простому скрипті bash shell просто:

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

але це не працює в Makefile.

Будь-які пропозиції? Я міг би скористатися будь-яким іншим розумним рішенням, щоб досягти цього.


Дивні зворотні галочки навколо роботи команди для виконання інших сценаріїв для мене в Makefile. Можливо, буде щось інше.
Лейф Грюнвольдт

Відповіді:


346

Використовуйте Make shellmakein як в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)

34
оболонка не є стандартною командою Зробити вбудовану команду. Це вбудований GNU Make.
Дерексон

12
stackoverflow.com/a/2373111/12916 додає важливу примітку про втечу $.
Джессі Глік

6
Цей простий приклад працює. Він також працює з трубопроводом команд оболонки. Але важливо, щоб ви використовували $$, щоб представляти $ в команді оболонки
Сергій П. aka azure

28
У той час як питання м'яко старе, найкраще робити MY_VAR: = $ (оболонка ...), інакше щоразу, коли MY_VAR оцінюється, він буде виконувати $ (оболонку ...) знову.
Russ Schultz

У мене був пробіл між оболонкою та
вхідними

29

Обгортання завдання в 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)

6
"Примітка: @true тут заважає Make думати, що нічого робити не можна". Гм, це для чого .PHONY always make these targets.
підкреслюйте_d

Дякую! Це допомагає мені обійти "дивний баш" у makefile
Nam G VU

19

Ось трохи складніший приклад із трубопроводом та змінним призначенням всередині рецепта:

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)

2
У випадку, якщо це стане в нагоді, я використовую трохи подібний підхід, щоб отримати PODNAMEім'я розгортання:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Ян Ріхтер,

Синтаксис переплутав мене на секунду, поки я не зрозумів, що ти використовуєш оболонку, побудовану eval (у рядку docker-env), і функцію make eval (у наступному рядку).
Брайан Гордон

17

Я пишу відповідь, щоб збільшити видимість власне синтаксису, який вирішує проблему. На жаль, те, що хтось може сприймати як тривіальне, може стати дуже значним головним болем для того, хто шукає просту відповідь на розумне запитання.

Помістіть наступне у файл "Makefile".

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

all:
    @echo MY_VAR IS $(MY_VAR)

Поведінка, яку ви хотіли б бачити, полягає в наступному (якщо припустити, що у вас встановлений останній пітон).

make
MY_VAR IS 1

Якщо ви скопіюєте та вставте вищезазначений текст у Makefile, ви отримаєте це? Напевно, ні. Ви, ймовірно, отримаєте помилку, як, наприклад, повідомлення про це тут:

makefile: 4: *** пропущений роздільник. Стій

Чому: Тому що, хоча я особисто використовував справжню вкладку, переповнення стека (намагаючись бути корисним) перетворює мою вкладку в ряд пробілів. Ви, засмучений громадянин Інтернету, тепер скопіюйте це, думаючи, що у вас зараз той самий текст, що і я. Команда make, тепер зчитує пробіли і виявляє, що команда "all" неправильно відформатована. Тож скопіюйте текст, поданий вище, вставте його, а потім перетворіть пробіл перед "@echo" на вкладку, і цей приклад, нарешті, сподіваємось, спрацює для вас.


Залежить від того, в який редактор ви вставляєте. Я просто скопіював і вставив у редактор Eclipse Makefile і отримав провідну вкладку (як потрібно).
Технофіл

О, я про це не думав. Атом тут.
AlanSE

11

Остерігайтеся таких рецептів

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

Це робить дві речі неправильно. Перший рядок у рецепті виконується в окремому екземплярі оболонки з другого рядка. Змінна тим часом втрачається. Друге, що не так - це те, що $не уникнути.

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

Обидві проблеми були виправлені, і змінна є корисною. Зворотна косою рисою поєднує обидва рядки в одній оболонці, отже, встановлення змінної та зчитування після змін після змінної працює.

Я розумію, що в початковій публікації сказано, як отримати результати команди оболонки в змінну MAKE, і ця відповідь показує, як отримати її в змінну оболонки. Але інші читачі можуть отримати користь.

Останнє покращення, якщо споживач очікує встановлення "змінної середовища", то вам доведеться експортувати його.

my_shell_script
    echo $MY_ID

знадобиться це в makefile

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

Сподіваюся, що хтось допомагає. Взагалі слід уникати будь-якої реальної роботи за межами рецептів, адже якщо хтось використовує makefile з опцією '--dry-run', щоб бачити лише те, що він буде робити, це не матиме небажаних побічних ефектів. Кожен $(shell)дзвінок оцінюється під час компіляції, і справжня робота може бути випадково виконана. Краще залишити справжню роботу, як-от генерування ідентифікаторів, всередині рецептів, коли це можливо.


9

За допомогою 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)

У вас є перше приємне пояснення, з яким я кожного разу стикався. Дякую. Хоча одна річ , щоб додати, що якщо $(SET_ID)життя всередині з ifпункту , що є помилковим , то вона по - , як і раніше називається .
Роман

Він все ще називається тому, що якщо stmts оцінюються під час компіляції makefile, а не під час виконання. Для конкретних інструкцій на час роботи, введіть їх у рецепти. Якщо вони умовні, додайте, якщо stmts, написані bash / shell, як частину рецепту.
Юрай

0

У наведеному нижче прикладі я зберігав шлях до папки Makefile до LOCAL_PKG_DIRта використовуюLOCAL_PKG_DIR змінну в цілях.

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

.PHONY: print
print:
    @echo $(LOCAL_PKG_DIR)

Термінальний вихід:

$ make print
/home/amrit/folder
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.