Melewati argumen untuk "menjalankan"

354

Saya menggunakan Makefiles.

Saya memiliki target yang dipanggil runyang menjalankan target build. Sederhana, seperti berikut ini:

prog: ....
  ...

run: prog
  ./prog

Apakah ada cara untuk menyampaikan argumen? Maka

make run asdf --> ./prog asdf
make run the dog kicked the cat --> ./prog the dog kicked the cat

Terima kasih!

segera
sumber

Jawaban:

264

Saya tidak tahu cara untuk melakukan persis apa yang Anda inginkan, tetapi solusinya mungkin:

run: ./prog
    ./prog $(ARGS)

Kemudian:

make ARGS="asdf" run
# or
make run ARGS="asdf"
Jakob Borg
sumber
27
@Rob: $ () lebih portabel, ia bekerja di Nmake dan juga membuatnya.
John Knoeller
7
@Rob: Nmake tidak pernah mendukung $ {} untuk ekspansi makro, dan tampaknya merupakan bentuk kuno yang sekarang dibuat. $ () direkomendasikan oleh setiap tutorial online yang saya lihat. $ () juga lebih konsisten dengan alat lain seperti bash.
John Knoeller
10
Mungkin itu kuno. Saya selalu menggunakan $ {}, tetapi manual untuk GNU Make menyatakan "Untuk mengganti nilai variabel, tulis tanda dolar diikuti dengan nama variabel dalam kurung atau kurung kurawal: $(foo)' or $ $ foo} 'adalah referensi yang valid untuk variabel `foo '." dan hasil untuk memberikan contoh di mana hanya $ () digunakan. Baiklah
Jakob Borg
7
bersorak John dan tenang, saya kembali dan melihat bahwa saran datang dari salinan saya edisi pertama buku OReilly "Mengelola Proyek dengan Make". Penulis menyatakan aturan tentang substitusi arsip menggunakan () dan makro dapat melakukan keduanya tetapi menyarankan menggunakan {} untuk membedakan. Tapi .... Edisi baru sekarang berjudul "Mengelola Proyek dengan GNU Make" menggunakan () di seluruh. Go figure .... Tebak saya harus memodernisasi! (-: Saya masih kagum bahwa MS NMake muntah di {} s meskipun.
Rob Wells
1
@xealits Pasti - ada contoh di pertanyaan di sini
helvete
198

Pertanyaan ini sudah hampir tiga tahun, tetapi bagaimanapun juga ...

Jika Anda menggunakan GNU make, ini mudah dilakukan. Satu-satunya masalah adalah yang makeakan menafsirkan argumen non-opsi di baris perintah sebagai target. Solusinya adalah mengubah mereka menjadi target yang tidak melakukan apa-apa, jadi maketidak akan mengeluh:

# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "run"
  RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(RUN_ARGS):;@:)
endif

prog: # ...
    # ...

.PHONY: run
run : prog
    @echo prog $(RUN_ARGS)

Menjalankan ini memberi:

$ make run foo bar baz
prog foo bar baz
Idelic
sumber
2
Ini bagus kecuali tampaknya tidak berfungsi untuk argumen yang dimulai dengan tanda hubung:prog foo bar --baz
ingydotnet
22
Tidak bekerja dalam kasus itu juga, tapi Anda harus memberitahu makeuntuk tidak menafsirkan --bazsebagai opsi baris perintah: make -- prog foo bar --baz. The --berarti "semuanya setelah ini argumen, bukan pilihan".
Idelic
Bagaimana saya menentukan nilai default untuk RUN_ARGSmenggunakan ini?
Bouke
Mungkin menambahkan elsecabang ke ifeqdan mengatur di RUN_ARGSsana?
Idelic
1
Poin bagus kebiru-biruan! Tetapi ada solusi untuk itu: ganti baris 'eval' dengan $(eval $(RUN_ARGS):dummy;@:), dengan tidak ada target dummy yang ditentukan.
Lucas Cimon
52

untuk standar make Anda bisa memberikan argumen dengan mendefinisikan makro seperti ini

make run arg1=asdf

kemudian gunakan seperti ini

run: ./prog $(arg1)
   etc

Referensi untuk membuat NMake Microsoft

John Knoeller
sumber
34

Anda dapat meneruskan variabel ke Makefile seperti di bawah ini:

run:
    @echo ./prog $$FOO

Pemakaian:

$ make run FOO="the dog kicked the cat"
./prog the dog kicked the cat

atau:

$ FOO="the dog kicked the cat" make run
./prog the dog kicked the cat

Atau gunakan solusi yang disediakan oleh Beta :

run:
    @echo ./prog $(filter-out $@,$(MAKECMDGOALS))
%:
    @:

%:- aturan yang cocok dengan nama tugas apa pun; @:- resep kosong = tidak melakukan apa-apa

Pemakaian:

$ make run the dog kicked the cat
./prog the dog kicked the cat
kenorb
sumber
22

TL; DR jangan mencoba melakukan ini

$ make run arg

alih-alih membuat skrip:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

dan lakukan ini:

$ ./buildandrunprog.sh arg

menjawab pertanyaan yang disebutkan:

Anda dapat menggunakan variabel dalam resep

run: prog
    ./prog $(var)

kemudian lulus tugas variabel sebagai argumen untuk dibuat

$ make run var=arg

ini akan mengeksekusi ./prog arg.

tapi waspadalah terhadap jebakan. Saya akan menguraikan tentang perangkap metode ini dan metode lain lebih jauh ke bawah.


jawaban atas anggapan niat di balik pertanyaan:

asumsi: Anda ingin menjalankan progdengan beberapa argumen tetapi minta dibangun kembali sebelum dijalankan jika perlu.

jawabannya: buat skrip yang membangun kembali jika perlu kemudian jalankan prog dengan args

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

naskah ini memperjelas niatnya. ia menggunakan make untuk melakukan apa yang baik untuk: membangun. menggunakan skrip shell untuk melakukan apa yang baik untuk: pemrosesan batch.

ditambah Anda dapat melakukan apa pun yang mungkin Anda butuhkan dengan fleksibilitas penuh dan ekspresif dari skrip shell tanpa semua peringatan dari makefile.

juga sintaks panggilan sekarang praktis identik:

$ ./buildandrunprog.sh foo "bar baz"

dibandingkan dengan:

$ ./prog foo "bar baz"

berbeda dengan

$ make run var="foo bar\ baz"

Latar Belakang:

make tidak dirancang untuk meneruskan argumen ke target. semua argumen pada baris perintah ditafsirkan sebagai tujuan (alias target), sebagai opsi, atau sebagai penugasan variabel.

jadi jika Anda menjalankan ini:

$ make run foo --wat var=arg

membuat akan menafsirkan rundan foosebagai tujuan (target) untuk memperbarui sesuai dengan resep mereka. --watsebagai opsi untuk make. dan var=argsebagai tugas variabel.

untuk lebih jelasnya lihat: https://www.gnu.org/software/make/manual/html_node/Goals.html#Goals

untuk terminologi lihat: https://www.gnu.org/software/make/manual/html_node/Rule-Introduction.html#Rule-Introduction


tentang metode penugasan variabel dan mengapa saya merekomendasikannya

$ make run var=arg

dan variabel dalam resep

run: prog
    ./prog $(var)

ini adalah cara yang paling "benar" dan langsung untuk menyampaikan argumen ke resep. tetapi sementara itu dapat digunakan untuk menjalankan program dengan argumen itu tentu tidak dirancang untuk digunakan seperti itu. lihat https://www.gnu.org/software/make/manual/html_node/Overriding.html#Overriding

menurut saya ini memiliki satu kelemahan besar: apa yang ingin Anda lakukan adalah menjalankan progargumen arg. tetapi bukannya menulis:

$ ./prog arg

kamu sedang menulis:

$ make run var=arg

ini semakin canggung ketika mencoba mengoper beberapa argumen atau argumen yang mengandung spasi:

$ make run var="foo bar\ baz"
./prog foo bar\ baz
argcount: 2
arg: foo
arg: bar baz

dibandingkan dengan:

$ ./prog foo "bar baz"
argcount: 2
arg: foo
arg: bar baz

Sebagai catatan, seperti inilah progpenampilan saya :

#! /bin/sh
echo "argcount: $#"
for arg in "$@"; do
  echo "arg: $arg"
done

juga perhatikan bahwa Anda tidak harus memberi $(var)tanda kutip di makefile:

run: prog
    ./prog "$(var)"

karena dengan begitu progakan selalu mendapatkan hanya satu argumen:

$ make run var="foo bar\ baz"
./prog "foo bar\ baz"
argcount: 1
arg: foo bar\ baz

semua ini adalah mengapa saya merekomendasikan rute ini.


untuk kelengkapan berikut adalah beberapa metode lain untuk "meneruskan argumen untuk menjalankan".

metode 1:

run: prog
    ./prog $(filter-out $@, $(MAKECMDGOALS))

%:
    @true

Penjelasan super singkat: menyaring tujuan saat ini dari daftar tujuan. buat catch all target ( %) yang tidak melakukan apa pun untuk mengabaikan tujuan lain secara diam-diam.

metode 2:

ifeq (run, $(firstword $(MAKECMDGOALS)))
  runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
  $(eval $(runargs):;@true)
endif

run:
    ./prog $(runargs)

Penjelasan super singkat: jika target tersebut runkemudian menghapus tujuan pertama dan membuat lakukan target apa-apa untuk tujuan yang tersisa menggunakan eval.

keduanya akan memungkinkan Anda untuk menulis sesuatu seperti ini

$ make run arg1 arg2

untuk penjelasan lebih lanjut, pelajari manual make: https://www.gnu.org/software/make/manual/html_node/index.html

masalah metode 1:

  • argumen yang dimulai dengan tanda hubung akan ditafsirkan dengan membuat dan tidak diteruskan sebagai tujuan.

    $ make run --foo --bar
    

    solusi

    $ make run -- --foo --bar
    
  • argumen dengan tanda sama akan ditafsirkan dengan membuat dan tidak lulus

    $ make run foo=bar
    

    tidak ada solusi

  • argumen dengan spasi canggung

    $ make run foo "bar\ baz"
    

    tidak ada solusi

  • jika sebuah argumen kebetulan run(sama dengan target) itu juga akan dihapus

    $ make run foo bar run
    

    akan berjalan ./prog foo barsebagai gantinya./prog foo bar run

    solusi mungkin dengan metode 2

  • jika sebuah argumen adalah target yang sah, maka itu juga akan dijalankan.

    $ make run foo bar clean
    

    akan berjalan ./prog foo bar cleantetapi juga resep untuk target clean(dengan asumsi itu ada).

    solusi mungkin dengan metode 2

  • ketika Anda salah mengetik target yang sah itu akan diam-diam diabaikan karena menangkap semua target.

    $ make celan
    

    hanya akan diam-diam mengabaikan celan .

    solusi adalah untuk membuat semuanya bertele-tele. jadi Anda lihat apa yang terjadi. tapi itu menciptakan banyak suara untuk keluaran yang sah.

masalah metode 2:

  • jika suatu argumen memiliki nama yang sama dengan target yang ada maka make akan mencetak peringatan bahwa itu sedang ditimpa.

    tidak ada solusi yang saya tahu

  • argumen dengan tanda sama dengan masih akan ditafsirkan dengan membuat dan tidak lulus

    tidak ada solusi

  • argumen dengan spasi masih canggung

    tidak ada solusi

  • argumen dengan ruang istirahat evalmencoba membuat target melakukan apa-apa.

    solusi: membuat tangkapan global semua target tidak melakukan apa pun seperti di atas. dengan masalah seperti di atas bahwa ia akan lagi diam-diam mengabaikan target yang sah salah ketik.

  • ini digunakan evaluntuk memodifikasi makefile saat runtime. seberapa buruk Anda dalam hal keterbacaan dan debugabilitas dan Prinsip yang paling mengejutkan .

    solusi: jangan lakukan ini !! 1 sebagai gantinya tulis skrip shell yang menjalankan make lalu jalankan prog.

saya hanya diuji menggunakan gnu make. merek lain mungkin memiliki perilaku yang berbeda.


TL; DR jangan mencoba melakukan ini

$ make run arg

alih-alih membuat skrip:

#! /bin/sh
# rebuild prog if necessary
make prog
# run prog with some arguments
./prog "$@"

dan lakukan ini:

$ ./buildandrunprog.sh arg
lesmana
sumber
12

Berikut solusi lain yang dapat membantu dengan beberapa kasus penggunaan ini:

test-%:
    $(PYTHON) run-tests.py $@

Dengan kata lain, pilih beberapa awalan ( test-dalam hal ini), dan kemudian berikan nama target langsung ke program / pelari. Saya kira ini sebagian besar berguna jika ada beberapa skrip pelari yang terlibat yang dapat membuka nama target menjadi sesuatu yang berguna untuk program yang mendasarinya.

djc
sumber
2
Anda juga dapat menggunakan $*untuk melewati hanya bagian dari target yang cocok dengan %.
Malvineous
9

Tidak. Melihat sintaks dari halaman manual untuk GNU make

make [-f makefile] [options] ... [target] ...

Anda dapat menentukan beberapa target, karenanya 'tidak' (setidaknya tidak dengan cara yang Anda tentukan).

ziya
sumber
4

Anda dapat secara eksplisit mengekstraksi setiap argumen ke-n di baris perintah. Untuk melakukan ini, Anda dapat menggunakan variabel MAKECMDGOALS, itu memegang daftar argumen baris perintah yang diberikan untuk 'make', yang ditafsirkan sebagai daftar target. Jika Anda ingin mengekstraksi argumen ke-n, Anda dapat menggunakan variabel itu dikombinasikan dengan fungsi "kata", misalnya, jika Anda ingin argumen kedua, Anda dapat menyimpannya dalam variabel sebagai berikut:

second_argument := $(word 2, $(MAKECMDGOALS) )
pengguna5122888
sumber
Ini juga menjalankan perintah make untuk argumen itu. make: *** No rule to make target 'arg'. Stop.
ThomasReggi
2

segera , run: ./progterlihat agak aneh, karena bagian kanan harus menjadi target, jadi run: progterlihat lebih baik.

Saya hanya menyarankan:

.PHONY: run

run:
        prog $(arg1)

dan saya ingin menambahkan, bahwa argumen dapat diajukan:

  1. sebagai argumen: make arg1="asdf" run
  2. atau didefinisikan sebagai lingkungan: arg1="asdf" make run
dma_k
sumber
2

Ini contoh saya. Perhatikan bahwa saya menulis di bawah Windows 7, menggunakan mingw32-make.exe yang datang dengan Dev-Cpp. (Saya punya c: \ Windows \ System32 \ make.bat, jadi perintahnya masih disebut "make".)

clean:
    $(RM) $(OBJ) $(BIN) 
    @echo off
    if "${backup}" NEQ "" ( mkdir ${backup} 2> nul && copy * ${backup} )

Penggunaan untuk pembersihan rutin:

make clean

Penggunaan untuk membersihkan dan membuat cadangan di mydir /:

make clean backup=mydir
osa
sumber
2

Tidak terlalu bangga dengan ini, tetapi saya tidak ingin meneruskan variabel lingkungan jadi saya membalikkan cara untuk menjalankan perintah kaleng:

run:
    @echo command-you-want

ini akan mencetak perintah yang ingin Anda jalankan, jadi evaluasi saja dalam sebuah subkulit:

$(make run) args to my command
Conrad.Dean
sumber
4
melihat kembali jawaban ini dua tahun kemudian - mengapa saya begitu keras kepala sehingga saya tidak ingin menggunakan variabel lingkungan dan mengapa saya pikir inlining generasi perintah lain lebih baik?
Conrad.Dean
0

Saya menemukan cara untuk mendapatkan argumen dengan tanda sama dengan (=)! Jawabannya terutama merupakan tambahan untuk jawaban @lesmana (karena ini adalah yang paling lengkap dan dijelaskan di sini), tetapi akan terlalu besar untuk menuliskannya sebagai komentar. Sekali lagi, saya ulangi pesannya: TL; DR jangan mencoba melakukan ini!

Saya membutuhkan cara untuk menangani argumen saya --xyz-enabled=false(karena defaultnya benar), yang kita semua tahu sekarang bahwa ini bukan target make dan karenanya bukan bagian dari$(MAKECMDGOALS) .

Sambil melihat ke semua variabel make dengan menggema $(.VARIABLES)saya mendapat output menarik ini:

[...] -*-command-variables-*- --xyz-enabled [...]

Ini memungkinkan kita untuk melakukan dua cara: memulai semuanya dengan variabel --(jika itu berlaku untuk kasus Anda), atau melihat ke dalam GNU membuat spesifik (mungkin tidak dimaksudkan untuk kita gunakan) variabel -*-command-variables-*-. ** Lihat catatan kaki untuk opsi tambahan ** Dalam kasus saya, variabel ini berlaku:

--xyz-enabled=false

Dengan variabel ini kita dapat menggabungkannya dengan solusi yang sudah ada dengan $(MAKECMDGOALS)dan dengan mendefinisikan:

# the other technique to invalidate other targets is still required, see linked post
run:
    @echo ./prog $(-*-command-variables-*-) $(filter-out $@,$(MAKECMDGOALS))`

dan menggunakannya dengan (secara eksplisit mencampur urutan argumen):

make run -- config --xyz-enabled=false over=9000 --foo=bar show  isit=alwaysreversed? --help

dikembalikan:

./prog isit=alwaysreversed? --foo=bar over=9000 --xyz-enabled=false config show --help

Seperti yang Anda lihat, kami kehilangan urutan total argumen. Bagian dengan "penugasan" -args tampaknya telah terbalik, urutan "target" -args disimpan. Saya menempatkan "penugasan" di awal, semoga program Anda tidak peduli di mana argumen itu ditempatkan.


Pembaruan: berikut membuat variabel terlihat menjanjikan juga:

MAKEFLAGS =  -- isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
MAKEOVERRIDES = isit=alwaysreverse? --foo=bar over=9000 --xyz-enabled=false
Dionysius
sumber
-2

Trik lain yang saya gunakan adalah -nbendera, yang memberitahu makeuntuk melakukan lari kering. Sebagai contoh,

$ make install -n 
# Outputs the string: helm install stable/airflow --name airflow -f values.yaml
$ eval $(make install -n) --dry-run --debug
# Runs: helm install stable/airflow --name airflow -f values.yaml --dry-run --debug
santon
sumber