Jak przypisać wynik polecenia do zmiennej Makefile

224

Muszę wykonać niektóre reguły make warunkowo, tylko jeśli zainstalowany Python jest większy niż pewna wersja (powiedzmy 2.5).

Myślałem, że mogę zrobić coś takiego:

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

a następnie używając wyniku („1”, jeśli jest ok, „0” w przeciwnym razie) w ifeqinstrukcji make.

W prostym skrypcie powłoki bash jest to po prostu:

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

ale to nie działa w Makefile.

Jakieś sugestie? Aby to osiągnąć, mógłbym użyć innego rozsądnego obejścia.

fortran
źródło
Dziwne tyknięcia wokół poleceń związanych z wykonywaniem dla mnie innych skryptów w Makefile. To może być coś innego.
Leif Gruenwoldt

Odpowiedzi:

345

Użyj shellwbudowanego Make, jak wMY_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)
Arkaitz Jimenez
źródło
34
shell nie jest standardowym poleceniem Make wbudowanym. To jest wbudowane narzędzie GNU Make.
Dereckson,
12
stackoverflow.com/a/2373111/12916 dodaje ważną uwagę na temat ucieczki $.
Jesse Glick,
6
Ten prosty przykład działa. Działa również z potokiem poleceń powłoki. Konieczne jest jednak użycie $$ do reprezentowania $ w poleceniu powłoki
Sergey P. aka azure
28
Chociaż pytanie jest dość stare, najlepiej zrobić MY_VAR: = $ (shell ...), w przeciwnym razie za każdym razem, gdy MY_VAR jest oceniany, ponownie wykona $ (shell ...).
Russ Schultz,
Miałem spację między powłoką a nawiasami otwierającymi i dopiero po usunięciu spacji tekst wyjściowy mojego makefile
wyszedł
29

Zawinięcie zadania w evaldziała dla mnie.

# 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)
Dave
źródło
6
„Uwaga: @true tutaj uniemożliwia Makeowi myślenie, że nic nie można zrobić.” Po to .PHONY always make these targetsjest.
underscore_d
Dziękuję Ci! Pomaga mi to ominąć „dziwne uderzenie” w makefile
Nam G VU
19

Oto nieco bardziej skomplikowany przykład z orurowaniem i przypisywaniem zmiennych w przepisie:

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)
Francesco Casula
źródło
2
Na wypadek, gdyby kiedykolwiek się przydało, używam nieco podobnego podejścia, aby uzyskać PODNAMEnazwę wdrożenia:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter
Składnia pomieszała mnie przez sekundę, dopóki nie zdałem sobie sprawy, że używasz zarówno eval wbudowanego w powłokę (w linii docker-env), jak i eval funkcji make (w następnej linii).
Brian Gordon
17

Piszę odpowiedź, aby zwiększyć widoczność rzeczywistej składni, która rozwiązuje problem. Niestety to, co ktoś może uznać za trywialne, może stać się bardzo dotkliwym bólem głowy dla kogoś, kto szuka prostej odpowiedzi na rozsądne pytanie.

Umieść następujący plik w pliku „Makefile”.

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

all:
    @echo MY_VAR IS $(MY_VAR)

Zachowanie, które chciałbyś zobaczyć, jest następujące (zakładając, że masz zainstalowany najnowszy Python).

make
MY_VAR IS 1

Czy skopiujesz i wkleisz powyższy tekst do pliku Makefile, czy dostaniesz to? Prawdopodobnie nie. Prawdopodobnie wystąpi błąd podobny do zgłaszanego tutaj:

makefile: 4: *** brak separatora. Zatrzymać

Dlaczego: Ponieważ chociaż osobiście użyłem oryginalnej tabulacji, przepełnienie stosu (próbując być pomocnym) konwertuje moją tabulator na kilka spacji. Ty, sfrustrowany obywatelu internetu, teraz go skopiuj, myśląc, że masz teraz ten sam tekst, którego użyłem. Komenda make odczytuje teraz spacje i stwierdza, że ​​komenda „all” jest niepoprawnie sformatowana. Skopiuj powyższy tekst, wklej go, a następnie przekonwertuj spację przed „@echo” na kartę, a ten przykład powinien wreszcie, mam nadzieję, zadziałać.

AlanSE
źródło
Zależy od tego, w którym edytorze wklejasz. Właśnie skopiowałem i wkleiłem do edytora Eclipse Makefile i dostałem wiodącą kartę (w razie potrzeby).
Technophile
Och, nie myślałem o tym. Atom tutaj.
AlanSE
11

Uważaj na takie przepisy

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

Robi dwie rzeczy źle. Pierwszy wiersz w przepisie jest wykonywany w osobnej instancji powłoki od drugiego wiersza. W międzyczasie zmienna została utracona. Drugą wadą jest to, że $nie uciekł.

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

Oba problemy zostały naprawione i zmienna jest użyteczna. Odwrotny ukośnik łączy oba wiersze, aby działały w jednej powłoce, dlatego działa ustawienie zmiennej i odczytywanie zmiennych po słowie.

Zdaję sobie sprawę, że w pierwotnym poście powiedziano, jak uzyskać wyniki polecenia powłoki do zmiennej MAKE, a ta odpowiedź pokazuje, jak przenieść ją do zmiennej powłoki. Ale inni czytelnicy mogą skorzystać.

Ostatnia poprawka, jeśli konsument spodziewa się ustawienia „zmiennej środowiskowej”, musisz ją wyeksportować.

my_shell_script
    echo $MY_ID

potrzebuje tego w pliku makefile

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

Mam nadzieję, że komuś pomoże. Ogólnie rzecz biorąc, należy unikać wykonywania prawdziwej pracy poza przepisami, ponieważ jeśli ktoś użyje makefile z opcją „--dry-run”, aby ZOBACZYĆ tylko, co zrobi, nie spowoduje to żadnych niepożądanych efektów ubocznych. Każde $(shell)połączenie jest oceniane podczas kompilacji, a niektóre prawdziwe prace mogą zostać przypadkowo wykonane. Lepiej pozostawić prawdziwą pracę, taką jak generowanie identyfikatorów, w przepisach, jeśli to możliwe.

Juraj
źródło
9

Za pomocą GNU Make możesz używać shelli evalprzechowywać, uruchamiać i przypisywać dane wyjściowe z dowolnych wywołań wiersza poleceń. Różnica między poniższym przykładem a tymi, które go wykorzystują, :=polega na tym, że :=przypisanie następuje raz (po napotkaniu) i na zawsze. Rekurencyjnie rozwinięte zmienne ustawione za pomocą =są nieco bardziej „leniwe”; odwołania do innych zmiennych pozostają do momentu odwołania się do samej zmiennej, a kolejne rekurencyjne rozszerzanie ma miejsce za każdym razem, gdy odwołuje się do niej zmienna , co jest pożądane, aby uzyskać „spójne, wywoływalne fragmenty”. Aby uzyskać więcej informacji, zobacz instrukcję ustawiania zmiennych .

# 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)
Mitchell Tracy
źródło
Masz pierwsze miłe wytłumaczenie, z którym się zetknąłem. Dziękuję Ci. Choć jedna rzecz dodać, że jeśli $(SET_ID)życie wewnątrz z ifklauzulą , że jest fałszywa wtedy to nadal nazywa .
Rzym.
Nadal jest wywoływany, ponieważ stmts if są oceniane w czasie kompilacji makefile, a nie w czasie wykonywania. Aby uzyskać szczegółowe instrukcje w czasie wykonywania, umieść je w przepisach. Jeśli są warunkowe, dodaj if stmts, napisane bash / shell, jako część przepisu.
Juraj,
0

W poniższym przykładzie zapisałem ścieżkę folderu Makefile do, LOCAL_PKG_DIRa następnie używam LOCAL_PKG_DIRzmiennej w obiektach docelowych.

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

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

Wyjście końcowe:

$ make print
/home/amrit/folder
Amritpal Singh
źródło