Cara menetapkan output dari perintah ke variabel Makefile

225

Saya perlu menjalankan beberapa aturan make secara kondisional, hanya jika Python yang diinstal lebih besar dari versi tertentu (katakanlah 2.5).

Saya pikir saya bisa melakukan sesuatu seperti mengeksekusi:

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

dan kemudian menggunakan output ('1' jika ok, '0' jika tidak) dalam ifeqpernyataan make.

Dalam skrip bash shell sederhana, itu hanya:

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

tapi itu tidak berhasil di Makefile.

Ada saran? Saya bisa menggunakan solusi yang masuk akal lainnya untuk mencapai ini.

fortran
sumber
Kutu balik aneh di sekitar pekerjaan perintah untuk mengeksekusi skrip lain untuk saya di Makefile. Mungkin ada sesuatu yang lain.
Leif Gruenwoldt

Jawaban:

346

Gunakan Make shellbuiltin seperti diMY_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
sumber
34
shell bukan perintah Make builtin standar. Ini adalah built-in GNU Make.
Dereckson
12
stackoverflow.com/a/2373111/12916 menambahkan catatan penting tentang melarikan diri $.
Jesse Glick
6
Contoh sederhana ini berfungsi. Ini juga bekerja dengan pipeline perintah shell. Tetapi penting bahwa Anda harus menggunakan $$ untuk mewakili $ dalam perintah shell
Sergey P. aka azure
28
Meskipun pertanyaan agak lama, sebaiknya MY_VAR: = $ (shell ...), jika tidak setiap kali MY_VAR dievaluasi, ia akan mengeksekusi $ (shell ...) lagi.
Russ Schultz
Saya memiliki ruang antara shell dan tanda kurung pembuka dan hanya setelah menghapus spasi melakukan teks keluaran makefile saya
peterchaula
29

Membungkus penugasan dalam evaladalah untuk saya.

# 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
sumber
6
"Catatan: @true di sini mencegah Make dari berpikir tidak ada yang bisa dilakukan." Um, itu untuk apa .PHONY always make these targets.
underscore_d
Terima kasih! Ini membantu saya melewati "bash aneh" di makefile
Nam G VU
19

Berikut ini contoh yang sedikit lebih rumit dengan perpipaan dan penetapan variabel di dalam resep:

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
sumber
2
Dalam hal ini pernah berguna saya menggunakan pendekatan yang agak mirip untuk mendapatkan PODNAMEdengan nama penyebaran:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter
Sintaksnya membingungkan saya sejenak sampai saya menyadari Anda menggunakan kedua shell builtin eval (pada docker-env line) dan make function eval (pada baris berikutnya).
Brian Gordon
17

Saya menulis jawaban untuk meningkatkan visibilitas ke sintaks aktual yang memecahkan masalah. Sayangnya, apa yang seseorang mungkin anggap sepele bisa menjadi sakit kepala yang sangat signifikan bagi seseorang yang mencari jawaban sederhana untuk pertanyaan yang masuk akal.

Masukkan yang berikut ke dalam file "Makefile".

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

all:
    @echo MY_VAR IS $(MY_VAR)

Perilaku yang ingin Anda lihat adalah sebagai berikut (dengan asumsi Anda memiliki python baru diinstal).

make
MY_VAR IS 1

Jika Anda menyalin dan menempelkan teks di atas ke dalam Makefile, apakah Anda akan mendapatkannya? Mungkin tidak. Anda mungkin akan mendapatkan kesalahan seperti yang dilaporkan di sini:

makefile: 4: *** pemisah yang hilang. Berhenti

Mengapa: Karena meskipun saya secara pribadi menggunakan tab asli, Stack Overflow (berusaha membantu) mengubah tab saya menjadi sejumlah ruang. Anda, warga internet yang frustrasi, sekarang salin ini, berpikir bahwa Anda sekarang memiliki teks yang sama yang saya gunakan. Perintah make, sekarang membaca spasi dan menemukan bahwa perintah "semua" salah diformat. Jadi salin teks di atas, tempel, lalu konversikan spasi putih sebelum "@echo" menjadi sebuah tab, dan contoh ini seharusnya, akhirnya, semoga berhasil untuk Anda.

AlanSE
sumber
Tergantung pada editor yang Anda tempelkan. Saya baru saja menyalin dan menempel ke editor Eclipse Makefile, dan mendapat tab terkemuka (sesuai kebutuhan).
Technophile
Oh, aku tidak memikirkan itu. Atom di sini.
AlanSE
11

Waspadai resep seperti ini

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

Itu melakukan dua hal yang salah. Baris pertama dalam resep dijalankan dalam instance shell terpisah dari baris kedua. Sementara itu variabel hilang. Hal kedua yang salah adalah bahwa hal $itu tidak luput.

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

Kedua masalah telah diperbaiki dan variabel dapat digunakan. Garis miring terbalik menggabungkan kedua baris untuk berjalan dalam satu shell tunggal, maka pengaturan variabel dan pembacaan kata penutup variabel, bekerja.

Saya menyadari posting asli mengatakan bagaimana cara mendapatkan hasil dari perintah shell ke dalam variabel MAKE, dan jawaban ini menunjukkan bagaimana memasukkannya ke dalam variabel shell. Tetapi pembaca lain mungkin mendapat manfaat.

Satu perbaikan terakhir, jika konsumen mengharapkan "variabel lingkungan" untuk ditetapkan, maka Anda harus mengekspornya.

my_shell_script
    echo $MY_ID

akan membutuhkan ini di makefile

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

Semoga itu bisa membantu seseorang. Secara umum, seseorang harus menghindari melakukan pekerjaan nyata di luar resep, karena jika seseorang menggunakan makefile dengan opsi '--dry-run', untuk hanya MELIHAT apa yang akan dilakukan, itu tidak akan memiliki efek samping yang tidak diinginkan. Setiap $(shell)panggilan dievaluasi pada waktu kompilasi dan beberapa pekerjaan nyata dapat dilakukan tanpa sengaja. Lebih baik meninggalkan pekerjaan nyata, seperti membuat id, ke bagian dalam resep bila memungkinkan.

Juraj
sumber
9

Dengan GNU Make, Anda dapat menggunakan shelldan evaluntuk menyimpan, menjalankan, dan menetapkan output dari pemanggilan baris perintah sewenang-wenang. Perbedaan antara contoh di bawah ini dan yang digunakan :=adalah :=penugasan terjadi sekali (ketika ditemui) dan untuk semua. Variabel yang diperluas secara rekursif diatur dengan =sedikit lebih "malas"; referensi ke variabel lain tetap sampai variabel itu sendiri direferensikan, dan ekspansi rekursif berikutnya terjadi setiap kali variabel direferensikan , yang diinginkan untuk membuat "konsisten, dapat dipanggil, snippet". Lihat manual tentang pengaturan variabel untuk info lebih lanjut.

# 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
sumber
Anda memiliki penjelasan bagus pertama yang saya jumpai. Terima kasih. Meskipun satu hal untuk ditambahkan adalah bahwa jika $(SET_ID)hidup di dalam ifklausa yang salah maka itu masih disebut .
Roman
Itu masih dipanggil karena jika stmts dievaluasi pada makefile compile-time bukan pada run-time. Untuk instruksi spesifik run-time, letakkan dalam resep. Jika mereka bersyarat, tambahkan jika stmts, ditulis dalam bash / shell, sebagai bagian dari resep.
Juraj
0

Dalam contoh di bawah ini, saya telah menyimpan path folder Makefile ke LOCAL_PKG_DIRdan kemudian menggunakan LOCAL_PKG_DIRvariabel dalam target.

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

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

Output terminal:

$ make print
/home/amrit/folder
Amritpal Singh
sumber