Buat daftar tujuan / target di GNU make yang mengandung variabel dalam definisinya


100

Saya memiliki makefile yang cukup besar yang membuat sejumlah target dengan cepat dengan menghitung nama dari variabel. (mis. foo $ (VAR): $ (PREREQS)). Adakah cara agar gnu make dapat diyakinkan untuk mengeluarkan daftar target setelah variabel-variabel ini diperluas?

Saya ingin mendapatkan target untuk makefile yang sembarangan. Saya mencoba menulis fungsi penyelesaian untuk shell saya.

Jawaban:


79

Dapatkah Anda mengurai keluaran dari make -pn(yaitu make --print-data-base --dry-run)? Ini mencetak semua variabel, aturan, aturan implisit dan perintah mana yang akan dijalankan dengan detail yang melelahkan.


Jawaban ini terlihat jauh lebih bagus dari yang lain.
Matt Joiner

Saya setuju bahwa ini jauh lebih baik. Masih lebih banyak pekerjaan dari yang saya harapkan, tetapi saya akan menandainya sebagai jawaban karena tidak ada hal lain yang masuk akal dan GNU make tidak memiliki flag yang sesuai untuk apa yang saya inginkan.
BitShifter

6
Ketahuilah bahwa hasilnya mungkin bergantung pada target yang Anda tentukan, jika Makefile Anda menghasilkan target secara dinamis. Outputnya juga dapat bergantung pada nilai variabel lingkungan atau membuat variabel ditentukan dengan perintah make.
reinierpost

6
Saya suka singkatnya. +1 Menambahkan putaran saya sendiri (dijelaskan dalam jawaban di bawah):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

Sarankan make -pnr. The -rmenghilangkan aturan implisit.
sjbx

114
make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'     

Diambil dari penyelesaian make arg, yang berfungsi seperti pesona.


2
saya mencoba membuat alias untuk ini, meletakkannya dalam tanda kutip ganda, tetapi itu hanya menghasilkan awk yang memiliki kesalahan sintaks pada koma pertama ... ada pemikiran tentang cara melarikan diri dengan benar ini untuk alias shell (bash)?
drfrogsplat

3
Saya pikir itu ada hubungannya dengan di mana itu didefinisikan. Saya pikir jika itu didefinisikan sebagai alias, direktori kerjanya menjadi direktori tempat ia ditentukan, dan tampaknya bagian awk tidak menyukai titik di direktori tersembunyi. Mendefinisikan fungsi bekerja.
Ibrahim

Saya hanya memasukkannya ke dalam skrip shell ~/bindaripada menggunakan alias. Bekerja dengan baik; Terima kasih!
mpontillo

3
Alias alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"yang akan menghemat waktu 2 menit mengutip permainan :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
| sort -uterlihat lebih berguna :)
sherpya

14

Saya tidak yakin apakah ini hanya gnu make, tapi ini bekerja dengan baik:

make help


Ini cukup berguna, jauh lebih mudah diingat daripada alias khusus untuk salah satu metode lainnya.
Ibrahim

8
Tidak benar-benar bekerja pada GNU Make 3.81, mungkin target di makefiles Anda: "make: *** Tidak ada aturan untuk membuat target 'membantu'. Berhenti"
a1an

8
Ini hanya memanggil target "bantuan" di Makefile. Jika ada, itu akan mencetak sesuatu (bukan daftar lengkap target), jika tidak ada (situasi tipikal), itu akan ditalangi.
pfalcon

2
Kabar baiknya adalah cmake tampaknya menghasilkan target "bantuan" yang bagus dan mudah dibaca.
Stéphane

Ini luar biasa! :) Pergi cmake
Jeef

10

Beberapa responden telah menyarankan penggunaan make -pn, yang akan mencetak database aturan tetapi tidak mengeksekusi apa pun - lebih atau kurang. Masalah dengan pendekatan ini adalah bahwa -nmasih memanggil semua rekursif membuat, dan masih melakukan lebih banyak pekerjaan daripada yang diperlukan, karena mencetak setiap perintah yang akan dipanggil dalam build biasa. Solusi yang lebih efisien adalah membuat makefile sepele, dummy.mk, dengan konten berikut:

__all_targets__: ; #no-op

Sekarang aktifkan make as make -p -f Makefile -f dummy.mk __all_targets__. Pada bangunan substansial apa pun, perbedaan dalam jumlah output yang dihasilkan oleh merek adalah signifikan. Sebagai contoh:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

Waktu eksekusi juga jauh lebih baik - 2.063 detik untuk versi pertama, 0.059 detik untuk versi kedua.


1
Ide bagus, semua orang yang bekerja dengan sistem build besar akan menghargainya. Statistik untuk Android Gingerbread tree (tidak menggunakan merek rekursif, jadi penghematan tidak besar, hanya bagus): " time make -pn | wc -l": 1338738 real 0m47.754s." time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l": 938621 real 0m40.219s.
pfalcon

Poin yang bagus. Halaman manual untuk maketampaknya menyarankan sesuatu yang serupa tetapi bersahabat, yaitumake -p -f/dev/null
Davide

@Davide itu tidak akan berfungsi: menentukan -f /dev/nullakan mencegah makepenguraian makefile biasa, jadi Anda hanya akan mendapatkan cetakan dari aturan bawaan. Itulah mengapa solusi saya menentukan -f Makefile -f dummy.mk. Tetapi itu juga tidak cukup, karena with -n, makeakan melanjutkan dan benar-benar mulai menjalankan aturan di makefile juga. Sayangnya saya tidak mengetahui adanya modifikasi yang dapat menyederhanakan solusi seperti yang disajikan sebelumnya.
Eric Melski

Tentu saja Anda benar. Namun halaman manual make memiliki bug, karena menyatakan: "Untuk mencetak basis data tanpa mencoba membuat ulang file apa pun, gunakan make -p -f / dev / null"
Davide

Sedikit koreksi pada komentar saya sebelumnya: seharusnya berbunyi "... karena tanpa -n ..."
Eric Melski


7

Sunting: Untuk diketahui, repositori git penyelesaian bash debian dalam jawaban lain sekarang menggabungkan versi yang disempurnakan dari skrip ini yang disesuaikan dengan kasus penggunaan penyelesaian bash.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

Ini adalah skrip yang jauh lebih lengkap daripada skrip penyelesaian bash debian karena ini memberikan hasil untuk aturan yang dihasilkan yang merupakan pertanyaan yang diminta dan jawaban pilihan teratas (skrip penyelesaian debian bash di server debian git) tidak cukup.

Ini bukan skrip asli yang saya tautkan, tetapi lebih sederhana dan lebih cepat.


BTW, mods, jika jawaban ini tidak sepenuhnya sejalan dengan semua kebijakan karena nuansa teksnya, silakan beri komentar sebelum menghapus. Saya akan membuat penyesuaian yang sesuai hukum yang diperlukan agar pertanyaan ini benar-benar mendapat jawaban yang lengkap dan valid.
codeshot

Sebagai pengujian cepat, saya mengambil kode sumber gcc dan menjalankan ./configure && time ~ / makecomp.sh | wc -l dimana ~ / makecomp.sh adalah jawaban saya. ... konfigurasikan output ... config.status: membuat Makefile 3312 real 0m0.401s pengguna 0m0.412s sys 0m0.028s
codeshot

Sebagai tes lain saya menggunakan demo makefile di codeshot.blogspot.co.uk/2012/08/… yang menghasilkan aturan semu untuk membangun dan memeriksa lulus uji unit. Contoh ini menggunakan lebih banyak fitur make daripada proyek autoconf lintas platform biasa sehingga skrip dalam jawaban ini mengembalikan 14 target nyata sementara penyelesaian bash untuk make di ubuntu hanya mengembalikan dua aturan yang berguna dan kemudian 5 file sumber.
codeshot

4

Ini adalah kode untuk alias berdasarkan solusi Todd Hodes

alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split(\$1,A,/ /);for(i in A)print A[i]}"'

3

Ini akan memberikan hasil yang bagus:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort


2

Sepertinya saya sedikit terlambat ke pesta ini, tetapi jika Anda mencari perintah khusus, Anda dapat mencoba

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

Ini sebagian besar berfungsi, meskipun mungkin masih menyertakan beberapa hal non-target seperti vpatharahan. Jika Anda tidak bergantung pada makeaturan bawaan, Anda dapat menggunakan make -qpRsebagai perintah pertama dalam pipeline.


Saya mencobanya dengan freetz (.org). Mencantumkan banyak makefile yang disertakan dan tidak mencantumkan semua target.
Robert Siemer

1

Solusi Ruby:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}

1

Saya sedang mengerjakan solaris 10 dan turbo C shell. Solusi yang diberikan tidak berfungsi untuk proyek makefile saya. bahkan setelah mengubah baris perintah di atas menjadi sintaks tcsh. Namun, saya menemukan Anda bisa mendapatkan solusi mudah menggunakan

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

versi remake:

remake --version

adalah

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

dan beberapa data versi tidak penting lainnya


0

Saya mencari pertanyaan yang sama dan muncul dengan putaran ini:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

Ini menghapus semua baris komentar, aturan pola (baris yang dimulai dengan tab), semua intrinsik (contoh .c.odan %.o: %.cpola).


tidak membaca komentar dari jawaban yang diterima: +1 untuk jawaban Jack ... gist.github.com/777954 - pvandenberk 13 Jan jam 14:47
Jamie

0

Menemukan solusi ini di utas lain:

sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\""

Anda juga dapat menambahkannya ke Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

Dan jalankan make list.


-1

Tentu, tapi kapan Anda ingin dia meludahkannya?

Untuk melaporkan nama target saat menjalankan aturan, letakkan baris di aturan:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

Untuk melontarkan semuanya sekaligus, Anda dapat membuat target PHONY terpisah:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

Dan ini dapat dijadikan prasyarat dari target default Anda:

all: show_vars
    ...

EDIT:
Anda menginginkan cara untuk menampilkan semua kemungkinan target dari makefile sewenang-wenang, yang menurut saya berarti tidak mengganggu. Baik...

Untuk melakukannya dengan tepat, dan dapat menangani makefile yang canggih, misalnya melibatkan aturan yang dibuat oleh evalpernyataan, Anda harus menulis sesuatu yang mirip dengan emulator Make. Tidak praktis.

Untuk melihat target dari aturan sederhana, Anda dapat menulis makefile yang akan bertindak sebagai pemindai makefile, yang beroperasi pada makefile sewenang-wenang:

  1. Dapatkan semua nama target dari makefile menggunakan sed.
  2. `include` makefile untuk menggunakannya untuk memperluas variabel.
  3. Gunakan `show_%:; echo $$ * `untuk mencetak semua target

Ini akan menjadi karya yang mengesankan. Apakah Anda yakin tujuannya sepadan dengan usaha?


Tidak seperti yang kuinginkan. Saya ingin dapat membuat daftar target untuk makefile sewenang-wenang yang mungkin saya miliki. Saya mencoba menulis fungsi penyelesaian argumen untuk shell saya dan saya tidak dapat mengandalkan makefiles yang memiliki bantuan yang masuk akal.
BitShifter

-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g

1
-1: Pendekatan ini menghasilkan positif palsu bahkan untuk tugas variabel sederhana ( :=).
besok

Anda harus mengujinya terlebih dahulu. Namun, ada baiknya Anda mencobanya.
Konrad Viltersten
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.