คุณจะรับรายชื่อเป้าหมายใน makefile ได้อย่างไร


202

ฉันใช้เรคบิต (โปรแกรมทำ Ruby) และมีตัวเลือกเพื่อรับรายการเป้าหมายที่มีอยู่ทั้งหมดเช่น

> rake --tasks
rake db:charset      # retrieve the charset for your data...
rake db:collation    # retrieve the collation for your da...
rake db:create       # Creates the databases defined in y...
rake db:drop         # Drops the database for your curren...
...

แต่ดูเหมือนจะไม่มีตัวเลือกในการทำเช่นนี้ใน GNU ทำ

เห็นได้ชัดว่ารหัสเกือบจะมีมันเป็นของปี 2007 - http://www.mail-archive.com/help-make@gnu.org/msg06434.html

อย่างไรก็ตามฉันได้แฮ็คเล็กน้อยเพื่อแยกเป้าหมายออกจาก makefile ซึ่งคุณสามารถรวมไว้ใน makefile ได้

list:
    @grep '^[^#[:space:]].*:' Makefile

มันจะให้รายการของเป้าหมายที่กำหนดไว้ มันเป็นเพียงการเริ่มต้นเท่านั้น - มันไม่ได้กรองการพึ่งพาเช่นกัน

> make list
list:
copy:
run:
plot:
turnin:

2
ฉันอ่านคำตอบอย่างรวดเร็วซึ่งน่าสนใจ แต่สำหรับตัวฉันเองฉันต้องการให้มันเรียบง่ายและพกพาโดยใช้นามแฝง (ใน. bashrc ของฉัน): alias makefile-targets='grep "^[^#[:space:]].*:" Makefile' บ่อยครั้งที่ฉันต้องตรวจสอบ makefile ปัจจุบันและการขยาย bash เสร็จสมบูรณ์ นามแฝงของฉัน
— RockyRoad


4
มันเล็กน้อยเกินไปgrep : Makefileใช่ไหม:
— cibercitizen1

คำตอบ:


130

นี่เป็นความพยายามที่จะปรับปรุงวิธีการที่ยอดเยี่ยมของ @ nobarดังต่อไปนี้:

  • ใช้คำสั่งที่มีประสิทธิภาพมากขึ้นเพื่อแยกชื่อเป้าหมายซึ่งหวังว่าจะป้องกันผลบวกปลอมใด ๆ (และกำจัดออกไปโดยไม่จำเป็นsh -c)
  • ไม่กำหนดเป้าหมาย makefile ในไดเร็กทอรีปัจจุบันอย่างสม่ำเสมอ ด้วยความเคารพ makefiles ระบุไว้อย่างชัดเจนด้วย-f <file>
  • ยกเว้นเป้าหมายที่ซ่อนอยู่ - โดยการประชุมนี่เป็นเป้าหมายที่ชื่อเริ่มต้นด้วยตัวอักษรหรือตัวเลข
  • ทำให้ทำกับเป้าหมายปลอมเดียว
  • นำหน้าคำสั่งด้วย@เพื่อป้องกันไม่ให้ถูก echoed ก่อนการเรียกใช้งาน

อยากรู้อยากเห็น GNU makeไม่มีคุณสมบัติสำหรับการแสดงรายการเพียงชื่อของเป้าหมายที่กำหนดไว้ใน makefile -pตัวเลือกที่ผลิตออกที่มีเป้าหมายทั้งหมด แต่ฝังไว้ในข้อมูลจำนวนมากอื่น ๆ

วางกฎต่อไปนี้ใน makefile สำหรับ GNU makeเพื่อใช้งานเป้าหมายที่มีชื่อlistซึ่งแสดงรายชื่อเป้าหมายทั้งหมดตามลำดับตัวอักษร - เช่น: เรียกใช้เป็นmake list :

.PHONY: list
list:
    @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'

สำคัญ : ในการวางสิ่งนี้ตรวจสอบให้แน่ใจว่าบรรทัดสุดท้ายมีการย่อหน้าโดยแท็บที่แท้จริง 1 แท็บ (พื้นที่ไม่ได้ทำงาน)

โปรดทราบว่าการเรียงลำดับรายการที่เกิดจากเป้าหมายเป็นตัวเลือกที่ดีที่สุดเนื่องจากไม่ได้เรียงลำดับไม่ก่อให้เกิดประโยชน์ในการสั่งซื้อที่ลำดับที่เป้าหมายปรากฏใน Makefile จะไม่ได้เก็บรักษาไว้
นอกจากนี้เป้าหมายย่อยของกฎที่ประกอบด้วยหลายเป้าหมายจะถูกส่งออกแยกกันอย่างสม่ำเสมอดังนั้นจะเกิดจากการเรียงลำดับซึ่งมักจะไม่ปรากฏถัดจากกันและกัน เช่นกฎที่ขึ้นต้นด้วยa z:จะไม่มีเป้าหมายaและzแสดงไว้ข้างๆกันในผลลัพธ์หากมีเป้าหมายเพิ่มเติม

คำอธิบายของกฎ :

  • .PHONY: list
    • ประกาศรายชื่อเป้าหมายเป้าหมายปลอมเช่นที่ไม่ได้อ้างถึงไฟล์ซึ่งควรมีสูตรที่เรียกใช้โดยไม่มีเงื่อนไข
  • $(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null

    • เรียกใช้makeอีกครั้งเพื่อพิมพ์และแยกฐานข้อมูลที่ได้มาจาก makefile:
      • -p พิมพ์ฐานข้อมูล
      • -Rr ไม่รวมกฎและตัวแปรในตัว
      • -qทดสอบเฉพาะสถานะล่าสุดของเป้าหมาย (โดยไม่ต้องทำอะไรใหม่) แต่ด้วยตัวเองไม่ได้ป้องกันการเรียกใช้คำสั่งสูตรในทุกกรณี ด้วยเหตุนี้:
      • -f $(lastword $(MAKEFILE_LIST))เพื่อให้แน่ใจว่า Makefile -f ...เดียวกันมีการกำหนดเป้าหมายในขณะที่ภาวนาเดิมไม่ว่าจะเป็นเป้าหมายโดยปริยายหรืออย่างชัดเจนด้วย
        Caveat : สิ่งนี้จะพังถ้า makefile ของคุณมีincludeคำสั่ง; เพื่อระบุสิ่งนี้ให้นิยามตัวแปรTHIS_FILE := $(lastword $(MAKEFILE_LIST)) ก่อนincludeคำสั่งใด ๆและใช้-f $(THIS_FILE)แทน
      • :เป็นเป้าหมายที่ไม่ถูกต้องจงใจที่มีขึ้นเพื่อให้แน่ใจว่าไม่มีคำสั่งจะถูกดำเนินการ ; 2>/dev/nullไม่แสดงข้อความแสดงข้อผิดพลาด หมายเหตุ: สิ่งนี้อาศัย-pการพิมพ์ฐานข้อมูลอย่างไรก็ตามซึ่งเป็นกรณีที่ GNU สร้าง 3.82 เศร้า GNU ทำให้มีตัวเลือกไม่ตรงไปเพียงแค่พิมพ์ฐานข้อมูลโดยไม่ต้องนอกจากนี้ยังมีการดำเนินการเริ่มต้น (หรือให้) งาน; หากคุณไม่ต้องการกำหนดเป้าหมาย Makefile เฉพาะคุณอาจใช้make -p -f/dev/nullตามที่แนะนำในmanหน้า
  • -v RS=

    • นี่เป็นสำนวน awk ที่แบ่งอินพุตเป็นบล็อกของบรรทัดที่ไม่ต่อเนื่องกัน
  • /^# File/,/^# Finished Make data base/
    • จับคู่ช่วงของบรรทัดในเอาต์พุตที่มีเป้าหมายทั้งหมด (เป็นจริงของ GNU ทำ 3.82) - โดย จำกัด การแยกวิเคราะห์ในช่วงนี้ไม่จำเป็นต้องจัดการกับผลบวกปลอมจากส่วนเอาต์พุตอื่น ๆ
  • if ($$1 !~ "^[#.]")
    • เลือกละเว้นบล็อก:
      • # ... ละเว้นกลุ่มที่ไม่ใช่เป้าหมายซึ่งบล็อกเริ่มต้นด้วย # Not a target:
      • . ... ละเว้นเป้าหมายพิเศษ
    • บล็อกอื่น ๆ ทั้งหมดควรเริ่มต้นด้วยบรรทัดที่มีเฉพาะชื่อของเป้าหมายที่กำหนดไว้อย่างชัดเจนตามด้วย :
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' ลบเป้าหมายที่ไม่ต้องการออกจากผลลัพธ์:
    • '^[^[:alnum:]]'... ไม่รวมเป้าหมายที่ซ่อนอยู่ซึ่งโดยการประชุมจะเป็นเป้าหมายที่เริ่มต้นด้วยตัวอักษรหรือตัวเลข
    • '^$@$$'... ไม่รวมlistเป้าหมาย

วิ่งmake listจากนั้นพิมพ์เป้าหมายทั้งหมดโดยแต่ละเส้นจะอยู่ในแนวเดียวกัน คุณสามารถไปที่xargsเพื่อสร้างรายการที่คั่นด้วยช่องว่างแทน


นี่เป็นคำตอบที่ดี แต่ฉันจะขอคำอธิบายสำหรับแต่ละงานได้อย่างไร ขณะนี้ฉันกำลังจัดเก็บเป็นความคิดเห็นด้านบนคำจำกัดความงาน ไม่แน่ใจว่าถ้าเป็นไปได้ที่จะรวมไว้ในรายการในลักษณะที่คล้ายกันเป็นrake --tasksไม่ได้หรือไม่
— Piotr Kuczynski

1
@PiotrKuczynski: คำถามที่น่าสนใจ แต่ฉันขอแนะนำให้คุณโพสต์เป็นคำถามใหม่ (ชี้ไปที่การอ้างอิงนี้)
— mklement0

"น่าเศร้าที่ GNU ทำข้อเสนอไม่มีตัวเลือกโดยตรงเพียงพิมพ์ฐานข้อมูล" มี-nตัวเลือกคือ
— Bulletmagnet

@Bulletmagnet: วัตถุประสงค์ของ-nตัวเลือกนี้คือ "พิมพ์คำสั่งที่จะถูกดำเนินการ ... " - อีกนัยหนึ่งคือคุณลักษณะแบบเรียกใช้แบบแห้ง ไม่ใช่ว่าคำพูดของคุณไม่มีตัวเอียงของคำเพียง : make -pโดยตัวมันเองจะพิมพ์ฐานข้อมูล แต่ยังคงทำงานเริ่มต้นเสมอ ; คู่มือแนะนำ clunky make -p -f/dev/nullเพื่อหลีกเลี่ยงปัญหานั้น
— mklement0

1
จะแสดงรายการเป้าหมาย. PHONY ใด ๆ ดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณไม่มี.PHONY: *
— tekumara

189

ภายใต้ Bash (อย่างน้อย) สิ่งนี้สามารถทำได้โดยอัตโนมัติด้วยการเติมแท็บ:

make spacetabtab


1
ทดสอบแล้ว: bash --version = 4.2.45 (1) - ปล่อย make --version = 3.81
— nobar

53
+1 แต่ทราบว่านี้ไม่ได้เป็นทุบตีคุณลักษณะ แต่ขึ้นอยู่กับคุณแพลตฟอร์ม แพลตฟอร์มแบ่งออกเป็นสามประเภท: ผู้ที่ติดตั้งความสำเร็จนี้เป็นค่าเริ่มต้น (เช่น Debian 7, Fedora 20), ที่ที่คุณสามารถเลือกติดตั้งได้ (เช่น Ubuntu 14, พร้อม. /etc/bash_completion) และผู้ที่ไม่สนับสนุนเลย (เช่น OSX 10.9)
— mklement0

8
มันทำงานบน OS X 10.10 (ไม่ใช่กับ bash แต่ใช้ zsh)
— Florian Oswald

4
เคล็ดลับที่ดี แต่ยังต้องการbash-completionแพคเกจ พบได้ใน Fedora, RHEL, Gentoo, ect
— NoelProf

4
สิ่งนี้ใช้ไม่ได้กับ AFAICT ที่สร้างโดยทางโปรแกรมเช่น$(RUN_TARGETS): run-%: ...
— Matthias

31

สิ่งนี้เห็นได้ชัดว่าใช้งานไม่ได้ในหลายกรณี แต่ถ้าคุณMakefileสร้างโดย CMake คุณอาจสามารถเรียกใช้งานmake helpได้

$ make help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... install
etc

คุณมีตัวเลือกใดบ้างที่จะเปิดใน CMakeList ของคุณเพื่อรับสิ่งนั้น ฉันได้รับ makefile ที่สร้างโดย CMake และฉันไม่มีเป้าหมายช่วยเหลือหลอกที่มีอยู่
— Xavier T.

Nope อย่างน้อยใน OSX ด้วย CMake ล่าสุด (ฉันใช้ nightlies จริง ๆ แต่ฉันเชื่อว่าเมื่อฉันเขียนสิ่งนี้ฉันใช้ 3.3) เพียงแค่เรียกใช้touch CMakeLists.txt && cmake . && make helpและควรใช้งานได้ ฉันเดาได้แค่ว่าคุณใช้ cmake โบราณหรือไม่ใช้เครื่องกำเนิด Unix Makefiles?
— Timmmm

ฉันใช้ CMake 3.6.2 กับตัวสร้าง unix บน Windows ฉันจะขุดไปหาคำอธิบายเพราะมันดูเป็นประโยชน์
— ซาเวียร์ต.

26

ฉันรวมคำตอบสองคำนี้: https://stackoverflow.com/a/9524878/86967และhttps://stackoverflow.com/a/7390874/86967 และได้หลบหนีบ้างเพื่อให้สามารถใช้จากภายใน makefile ได้

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | sort"

.

$ make -s list
build
clean
default
distclean
doc
fresh
install
list
makefile ## this is kind of extraneous, but whatever...
run

10
ฉันได้ค้นพบว่าการทำแท็บเสร็จสมบูรณ์ภายใต้ Bash จะแสดงรายการเป้าหมายที่มี
— nobar

1
+1 สำหรับแนวทางที่ยอดเยี่ยม แต่อาจมีการปรับปรุงเล็กน้อย: (ก) การระบุอย่างชัดเจนsh -c "…"นั้นไม่จำเป็นเนื่องจากเป็นmakeค่าเริ่มต้นที่ใช้เรียกคำสั่งสูตร (b) คำสั่งที่คุณใช้นั้นง่ายเกินไปทำให้เกิดผลบวกปลอม (ดังที่คุณทราบ) (c) หากคุณนำหน้าคำสั่งด้วยคำสั่ง@นั้นจะไม่ถูกสะท้อนไปยัง stdout ก่อนที่จะดำเนินการโดยไม่จำเป็นต้องใช้-sในการเรียกใช้
— mklement0

1
มีเป้าหมายทำให้ง่ายขึ้นมากที่คุณสามารถเพิ่ม: list: cat Makefile | grep "^ [Az]" | awk '{พิมพ์ $$ 1}' | sed "s /: // g | sort"
— thamster

2
วิธีการ cat / sed ล้มเหลวในหลายระดับ มันไม่ได้ขยายตัวแปรและยังไม่สามารถระบุเป้าหมายที่เป็นผลมาจากการรวมไฟล์อื่น ๆ
— ทรอยแดเนียลส์

1
@ mklement0: ฉันชอบที่จะใช้make -s(ผ่านนามแฝงทุบตี) มากกว่า prefixing คำสั่งใน Makefile @ด้วย มีบางกรณีที่@อาจมีประโยชน์โดยเฉพาะอย่างยิ่งในฐานะเป็นคำนำหน้าสำหรับechoคำสั่ง (ที่การแสดงคำสั่งจะซ้ำซ้อน) แต่โดยทั่วไปฉันไม่ต้องการใช้ ด้วยวิธีนี้ฉันสามารถเห็น "สูตรอาหาร" เมื่อฉันต้องการ (โดยไม่ได้ใช้-s) แต่โดยทั่วไปไม่ได้ นี่คือเอกสารที่เกี่ยวข้อง: GNU ทำให้ Manual, 5.2 สูตรสะท้อน
— สูงศักดิ์

14

หากคุณมีเสร็จสิ้นการทุบตีสำหรับการติดตั้งสคริปต์เสร็จสิ้นจะกำหนดฟังก์ชั่นmake _make_target_extract_scriptฟังก์ชั่นนี้มีวัตถุประสงค์เพื่อสร้างsedสคริปต์ที่สามารถใช้เพื่อให้ได้เป้าหมายเป็นรายการ

ใช้แบบนี้:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile

3
มีประโยชน์ แต่โปรดทราบว่าไม่ใช่ทุกแพลตฟอร์มที่มีสคริปต์/etc/bash_completionและ / หรือฟังก์ชั่น_make_target_extract_script- ดูเหมือนว่าคุณจะอยู่บน Ubuntu> 12 หากคุณกำหนดเป้าหมาย/usr/share/bash-completion/completions/makeแทนแนวทางของคุณจะทำงานบนแพลตฟอร์มเพิ่มเติมเช่น Fedora 20 มีแพลตฟอร์มที่เก่ากว่า ไฟล์นี้ แต่ไม่ใช่ฟังก์ชั่น (เช่น Debian 7 และ Ubuntu 12); แพลตฟอร์มอื่น ๆ เช่น OSX ไม่ได้มาด้วยความสมบูรณ์แท็บmakeที่ทั้งหมด บางทีการโพสต์นิยามฟังก์ชันที่แท้จริงอาจเป็นประโยชน์
— mklement0

1
ขอบคุณสำหรับความคิดเห็นของคุณ นี่คือความนิยมมาก ใช่ฉันได้ทดสอบสิ่งนี้บน Ubuntu 14.04
— hek2mgl

11

เมื่อmklement0 ชี้ให้เห็นคุณลักษณะสำหรับการแสดงรายการเป้าหมาย Makefile ทั้งหมดหายไปจาก GNU-make และคำตอบของเขาและอื่น ๆ มีวิธีในการทำเช่นนี้

อย่างไรก็ตามโพสต์ดั้งเดิมยังกล่าวถึงการเสาะหาซึ่งงานสลับทำอะไรบางอย่างที่แตกต่างกว่าเพียงแค่แสดงรายการงานทั้งหมดใน rakefile เรคจะให้รายการงานที่มีคำอธิบายที่เกี่ยวข้องเท่านั้น งานโดยไม่ต้องรายละเอียดจะไม่ได้รับการจดทะเบียน สิ่งนี้จะช่วยให้ผู้เขียนสามารถให้คำอธิบายความช่วยเหลือแบบกำหนดเองและละเว้นความช่วยเหลือสำหรับเป้าหมายบางอย่าง

หากคุณต้องการเลียนแบบพฤติกรรมของ rake ที่คุณให้คำอธิบายสำหรับแต่ละเป้าหมายมีเทคนิคง่าย ๆ สำหรับการทำสิ่งนี้: ฝังคำอธิบายในข้อคิดเห็นสำหรับแต่ละเป้าหมายที่คุณต้องการแสดง

คุณสามารถใส่คำอธิบายถัดจากเป้าหมายหรือตามที่ฉันทำบ่อยๆถัดจากสเปค PHONY เหนือเป้าหมายเช่นนี้:

.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.PHONY: help # Generate list of targets with descriptions                                                                
help:                                                                                                                    
    @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20

ซึ่งจะให้ผลผลิต

$ make help
target1             Target 1 help text
target2             Target 2 help text

...
help                Generate list of targets with descriptions

คุณสามารถค้นหาตัวอย่างรหัสย่อในส่วนสำคัญนี้และที่นี่ด้วย

อีกครั้งนี้ไม่ได้แก้ปัญหาการแสดงรายการเป้าหมายทั้งหมดใน Makefile ตัวอย่างเช่นหากคุณมี Makefile ขนาดใหญ่ที่อาจสร้างขึ้นหรือมีคนอื่นเขียนและคุณต้องการวิธีที่รวดเร็วในการแสดงรายการเป้าหมายโดยไม่ต้องขุดผ่านมันจะไม่ช่วยได้

อย่างไรก็ตามหากคุณกำลังเขียน Makefile และคุณต้องการวิธีสร้างข้อความช่วยเหลือด้วยวิธีการจัดทำเอกสารด้วยตนเองที่สอดคล้องกันเทคนิคนี้อาจมีประโยชน์


นี่เป็นเทคนิคที่ยอดเยี่ยม - ดีและเรียบง่าย แต่ให้คำอธิบายด้วย!
— Brian Burns

เทคนิคที่ยอดเยี่ยม ฉันแก้ไขบิตนี้เพื่อใช้echoคำสั่งเป็นคำอธิบายคำสั่ง ( stackoverflow.com/a/52059487/814145 )
— Penghe Geng

@PengheGeng ขอบคุณสำหรับการโพสต์ ฉันต้องการทำให้ชัดเจน@echo "Target 1"เป็นเพียงตัวยึดตำแหน่งและไม่ใช่ "คำอธิบาย echo คำสั่ง" ข้อความช่วยเหลือที่สร้างขึ้นไม่ได้มาจาก@echoของ ใน Makefile จริงสิ่งเหล่านี้echoจะถูกแทนที่ด้วยคำสั่ง build หรือบางอย่าง ฉันจะแก้ไขโพสต์เพื่อให้ชัดเจนยิ่งขึ้น
— jsp

4

@ nobar ของคำตอบที่แสดงให้เห็นว่าเป็นประโยชน์กับวิธีการใช้แท็บเสร็จสิ้นการลงรายการเป้าหมาย Makefile ของ

  • สิ่งนี้ใช้งานได้ดีสำหรับแพลตฟอร์มที่ให้ฟังก์ชันการทำงานนี้เป็นค่าเริ่มต้น (เช่นDebian, Fedora )

  • บนแพลตฟอร์มอื่น ๆ (เช่นUbuntu ) คุณต้องโหลดการทำงานนี้อย่างชัดเจนตามที่ระบุโดยคำตอบของ @ hek2mgl :

    • . /etc/bash_completion ติดตั้งฟังก์ชั่นการเติมแท็บหลายรายการรวมถึงฟังก์ชันสำหรับ make
    • อีกวิธีหนึ่งในการติดตั้งเฉพาะแท็บเสร็จสมบูรณ์สำหรับmake:
      • . /usr/share/bash-completion/completions/make
  • สำหรับแพลตฟอร์มที่ไม่มีฟังก์ชั่นนี้เลยเช่นOSXคุณสามารถระบุคำสั่งต่อไปนี้ (ปรับจากที่นี่ ) เพื่อนำไปใช้งาน:
_complete_make() { COMPREPLY=($(compgen -W "$(make -pRrq : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | egrep -v '^[^[:alnum:]]' | sort | xargs)" -- "${COMP_WORDS[$COMP_CWORD]}")); }
complete -F _complete_make make
  • หมายเหตุ: นี่คือไม่ซับซ้อนเช่นการทำงานของแท็บเสร็จที่มาพร้อมกับลินุกซ์: สะดุดตาที่สุดก็คงเส้นคงวาเป้าหมาย Makefile ในที่ปัจจุบันไดเรกทอรีแม้ว่าคำสั่งเป้าหมายบรรทัด Makefile -f <file>แตกต่างกันด้วย

4

คำตอบที่ฉันชอบมากที่สุดคือโพสต์โดยChris Downที่ Unix & Linux Stack Exchange ฉันจะพูด

นี่คือวิธีที่โมดูล bash เสร็จสิ้นสำหรับmakeรับรายการ:

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

มันพิมพ์รายการเป้าหมายที่คั่นด้วย newline โดยไม่ต้องเพจจิ้ง

ผู้ใช้Brainstoneแนะนำให้ไปที่ piping sort -uเพื่อลบรายการที่ซ้ำกัน:

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

ที่มา: วิธีแสดงรายการเป้าหมายทั้งหมดในการสร้าง (Unix & Linux SE)


4

มุ่งเน้นไปที่ไวยากรณ์ที่ง่ายสำหรับการอธิบายเป้าหมายการผลิตและการมีผลลัพธ์ที่สะอาดฉันเลือกวิธีนี้:

help:
    @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^\=]|$$)" Makefile \
     | grep -v -- -- \
     | sed 'N;s/\n/###/' \
     | sed -n 's/^#: \(.*\)###\(.*\):.*/\2###\1/p' \
     | column -t  -s '###'


#: Starts the container stack
up: a b
  command

#: Pulls in new container images
pull: c d 
    another command

make-target-not-shown:

# this does not count as a description, so leaving
# your implementation comments alone, e.g TODOs
also-not-shown:

ดังนั้นการปฏิบัติตามข้างต้นในฐานะ Makefile และการเรียกใช้มันให้สิ่งที่คุณต้องการ

> make help
up          Starts the container stack
pull        Pulls in new container images

คำอธิบายสำหรับ chain of command:

  • ครั้งแรก grep เป้าหมายทั้งหมดและสาย preceeding ของพวกเขาดูhttps://unix.stackexchange.com/a/320709/223029
  • จากนั้นได้รับการกำจัดแยกกลุ่มให้ดูhttps://stackoverflow.com/a/2168139/1242922
  • จากนั้นเราจะยุบคู่ของแต่ละบรรทัดที่จะแยกได้ในภายหลังดูhttps://stackoverflow.com/a/9605559/1242922
  • จากนั้นเราแยกวิเคราะห์บรรทัดที่ถูกต้องและลบบรรทัดที่ไม่ตรงกันดูhttps://stackoverflow.com/a/8255627/1242922และให้ผลลัพธ์ตามลำดับที่เราต้องการ: คำสั่งจากนั้นอธิบาย
  • สุดท้ายเราจัดการผลลัพธ์เช่นตาราง

ทางออกที่ดีมาก ฉันใช้สิ่งนี้มาสองสามปีแล้ว: awk -F ':|##' '/^[^\t].+:.*##/ { printf "\033[36mmake %-28s\033[0m -%s\n", $$1, $$NF }' $(MAKEFILE_LIST) | sort กับ##ความคิดเห็นก่อนหน้าในบรรทัดเดียวกับเป้าหมาย แต่ฉันคิดว่าโซลูชันของคุณจะช่วยให้สามารถอ่านได้ก่อน
— thoroc

makefile ประกอบด้วยรายงานสองบรรทัดแรกgrep: parentheses not balancedบนเครื่อง OSX 10.15 ของฉัน
— Malcolm Crum

@ Crummy ขออภัยความผิดพลาดของฉัน ขอบคุณที่ชี้นำ ซ่อมมัน. make จำเป็นต้องหลีกเลี่ยงสัญญาณดอลลาร์ที่ไม่ควรเริ่มชื่อตัวแปร
— Richard Kiefer

ระวัง StackOverflow แปลงแท็บเป็นช่องว่างเมื่อแสดงส่วนของรหัสด้านบน แต่ Makefiles จำเป็นต้องใช้แท็บ คุณสามารถแก้ไขโพสต์แล้วคัดลอกส่วนต่างๆได้ดังนั้นแท็บจะยังคงอยู่
— Richard Kiefer

2

โซลูชันที่ใช้การได้มากมายที่นี่ แต่อย่างที่ฉันชอบพูดว่า "ถ้ามันคุ้มค่าที่จะทำสักครั้งมันก็คุ้มที่จะทำอีกครั้ง" ฉันลงคะแนนสูงถึงความต้องการใช้ (แท็บ) (แท็บ) แต่ตามที่บางคนระบุไว้คุณอาจไม่ได้รับการสนับสนุนที่สมบูรณ์หรือถ้าคุณมีไฟล์จำนวนมากคุณอาจต้องการวิธีที่ง่ายกว่าในการรู้ว่าเป้าหมายถูกกำหนดไว้อย่างไร

ฉันไม่ได้ทดสอบด้านล่างด้วย sub-make ... ฉันคิดว่ามันจะไม่ทำงาน อย่างที่เราทราบกันว่าการเรียกซ้ำทำให้เกิดอันตราย

.PHONY: list ls
ls list :
    @# search all include files for targets.
    @# ... excluding special targets, and output dynamic rule definitions unresolved.
    @for inc in $(MAKEFILE_LIST); do \
    echo ' =' $$inc '= '; \
    grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
    cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
    tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
; done

# to get around escaping limitations:
open_paren := \(
close_paren := \)

ซึ่งฉันชอบเพราะ:

  • รายการเป้าหมายโดยรวมไฟล์
  • เอาต์พุตนิยามเป้าหมายแบบไดนามิกดิบ (แทนที่ตัวคั่นตัวแปรด้วยโมดูโล)
  • เอาต์พุตแต่ละเป้าหมายบนบรรทัดใหม่
  • ดูเหมือนชัดเจน (ความคิดเห็นส่วนตัว)

คำอธิบาย:

  • ไฟล์ foreach ใน MAKEFILE_LIST
  • เอาท์พุทชื่อของไฟล์
  • บรรทัด grep ที่มีเครื่องหมายโคลอนซึ่งไม่ได้เยื้องไม่ใช่ความคิดเห็นและไม่ขึ้นต้นด้วยจุด
  • ไม่รวมการแสดงออกที่ได้รับมอบหมายทันที (: =)
  • ตัดเรียงลำดับเยื้องและสับการพึ่งพากฎ (หลังโคลอน)
  • ตัวคั่นตัวแปร munge เพื่อป้องกันการขยายตัว

ตัวอย่างผลลัพธ์:

 = Makefile = 
  includes
  ls list
 = util/kiss/snapshots.mk = 
  rotate-db-snapshots
  rotate-file-snapshots
  snap-db
  snap-files
  snapshot
 = util/kiss/main.mk = 
  dirs
  install
   %MK_DIR_PREFIX%env-config.php
   %MK_DIR_PREFIX%../srdb

1

อันนี้มีประโยชน์กับฉันเพราะฉันต้องการเห็นเป้าหมายการสร้างที่ต้องการ (และการอ้างอิง) โดยเป้าหมายทำให้ ฉันรู้ว่าการสร้างเป้าหมายไม่สามารถเริ่มต้นด้วย "" ตัวละคร ฉันไม่รู้ว่ารองรับภาษาใดฉันจึงไปกับการแสดงออกวงเล็บของ egrep

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

1

สิ่งนี้อยู่ไกลจากความสะอาด แต่ทำเพื่อฉัน

make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1

ฉันใช้make -pที่ทิ้งฐานข้อมูลภายในคู stderr ใช้อย่างรวดเร็วและสกปรกgrep -A 100000เพื่อให้ด้านล่างของผลลัพธ์ จากนั้นฉันทำความสะอาดผลลัพธ์ด้วยสองสามgrep -vครั้งและในที่สุดก็ใช้cutเพื่อรับสิ่งที่อยู่ข้างหน้าลำไส้ใหญ่นั่นคือเป้าหมาย

นี่ก็เพียงพอแล้วสำหรับสคริปต์ผู้ช่วยของฉันใน Makefiles ส่วนใหญ่ของฉัน

แก้ไข: เพิ่มgrep -v Makefileที่เป็นกฎภายใน


1

นี่คือการแก้ไขคำตอบที่เป็นประโยชน์ของjsp ( https://stackoverflow.com/a/45843594/814145 ) ฉันชอบความคิดในการได้รับไม่เพียง แต่รายการเป้าหมาย แต่ยังรวมถึงคำอธิบายของพวกเขา Makefile ของjspทำให้คำอธิบายเป็นความคิดเห็นซึ่งฉันพบบ่อยจะซ้ำในคำสั่ง echo คำอธิบายของเป้าหมาย ดังนั้นฉันจะแยกคำอธิบายจากคำสั่ง echo สำหรับแต่ละเป้าหมาย

ตัวอย่าง Makefile:

.PHONY: all
all: build
        : "same as 'make build'"

.PHONY: build
build:
        @echo "Build the project"

.PHONY: clean
clean:
        @echo "Clean the project"

.PHONY: help
help:
        @echo -n "Common make targets"
        @echo ":"
        @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20

ผลลัพธ์ของmake help:

$ make help
Common make targets:
    make all        same as 'make build'
    make build      Build the project
    make clean      Clean the project
    make help       Common make targets

หมายเหตุ:

  • เช่นเดียวกับคำตอบของjspเฉพาะเป้าหมายของ PHONY เท่านั้นที่จะแสดงรายการซึ่งอาจหรืออาจไม่เหมาะกับกรณีของคุณ
  • นอกจากนี้จะแสดงรายการเป้าหมาย PHONY เหล่านั้นที่มีechoหรือ:คำสั่งเป็นคำสั่งแรกของสูตรเท่านั้น :แปลว่า "ไม่ทำอะไรเลย" ฉันใช้ที่นี่สำหรับเป้าหมายที่ไม่จำเป็นต้องใช้เสียงสะท้อนเช่นallเป้าหมายด้านบน
  • มีเคล็ดลับเพิ่มเติมสำหรับhelpเป้าหมายในการเพิ่ม ":" ในmake helpเอาต์พุต

0

อีกคำตอบเพิ่มเติมข้างต้น

ทดสอบบน MacOSX โดยใช้ cat และ awk บนเทอร์มินัลเท่านั้น

cat Makefile | awk '!/SHELL/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'

จะส่งออกของไฟล์ทำเหมือนด้านล่าง:

target1

target2

target3

ใน Makefile ควรเป็นคำสั่งเดียวกันตรวจสอบให้แน่ใจว่าคุณได้หลีกเลี่ยงตัวแปรโดยใช้ตัวแปร $$ แทนที่จะเป็น $ variable

คำอธิบาย

แมว - คายเนื้อหา

| - ไปป์แยกวิเคราะห์เอาต์พุตไปที่ awk ถัดไป

awk - รัน regex ยกเว้น "shell" และยอมรับเฉพาะบรรทัด "Az" จากนั้นพิมพ์คอลัมน์แรก $ 1

awk - ลบอักขระตัวสุดท้าย ":" ออกจากรายการอีกครั้ง

นี่เป็นผลลัพธ์คร่าวๆและคุณสามารถทำสิ่งที่ขี้ขลาดมากขึ้นด้วย AWK พยายามหลีกเลี่ยงการใช้งานเนื่องจากไม่สอดคล้องกันในสายพันธุ์ BSD เช่นทำงานบน * ระวัง แต่ล้มเหลวใน BSD เช่น MacOSX

มากกว่า

คุณควรจะสามารถเพิ่มสิ่งนี้ (ด้วยการแก้ไข) ลงในไฟล์สำหรับสร้างลงในโฟลเดอร์ทุบตีเริ่มต้น /usr/local/etc/bash-completion.d/ ความหมายเมื่อคุณ "สร้างแท็บแท็บ " .. มันจะเสร็จสมบูรณ์ เป้าหมายขึ้นอยู่กับสคริปต์ liner หนึ่งรายการ


คุณยังสามารถใช้ make -qp แทนที่จะเป็น cat ดังนั้นมันจะกลายเป็น make -qp | awk '/: / &&! /: = / && / ^ [Az] / &&! / PATH / &&! / DISPLAY / &&! / TERM_SESSION / &&! / USER_TEXT_ENCODING / &&&! / Makefile / {พิมพ์ย่อย ($ 0, 1 ความยาว ($ 0) -1) "sort -u"} '
— จิมมี่ MG Lim

0

ฉันมักจะทำ:

grep install_targets Makefile

มันจะกลับมาพร้อมกับสิ่งที่ชอบ:

install_targets = install-xxx1 install-xxx2 ... etc

ฉันหวังว่านี่จะช่วยได้


0

สำหรับ Bash Script

นี่เป็นวิธีที่ง่ายมากในการทำbashตามความคิดเห็นโดย @ cibercitizen1 ด้านบน :

grep : Makefile | awk -F: '/^[^.]/ {print $1;}'

ดูคำตอบที่เชื่อถือได้มากขึ้นโดย @ Marc.2377 ด้วยเช่นกันซึ่งระบุว่าโมดูล Bash completion makeทำอย่างไร


-4

ไม่แน่ใจว่าทำไมคำตอบก่อนหน้านี้จึงซับซ้อน:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g" 

8
คำตอบก่อนหน้ามีความซับซ้อนเช่นนั้นเพราะมันครอบคลุมทุกสถานการณ์ว่าคำตอบของคุณไม่ครอบคลุมถึง: (ก) การรวมของเป้าหมายที่กำหนดไว้ผ่านทางตัวแปร (เช่น$(SHELLS): ...), (ข) การรวมของเป้าหมายที่เริ่มต้นด้วยตัวเลข (ค) ไม่ใช่ รวมของคำจำกัดความตัวแปร (d) กำหนดเป้าหมาย makefile ที่เหมาะสมหากmakeระบุด้วยพา ธ makefile อย่างชัดเจน (a) เป็นข้อบกพร่องที่สำคัญ: แม้ว่าคุณจะกำหนดเป้าหมายไฟล์ที่ถูกต้องได้อย่างแม่นยำแต่การแยกไฟล์ดิบจะไม่ทำงานในกรณีทั่วไปเนื่องจากขาดการขยายตัวของตัวแปร
— mklement0

6
เช่นกันอีกครั้งซับซ้อน: คำสั่งของคุณได้ง่ายต่อไปนี้ - แต่ประเด็นหลักก็คือว่ามันพอที่จะไม่แข็งแกร่งawk -F: '/^[A-z]/ {print $$1}' Makefile : สุดท้ายเป็นจุดที่นักวิชาการส่วนใหญ่: การใช้[A-z]มากกว่าหมายความว่าคุณจะพลาดเป้าหมายที่ขึ้นต้นด้วยตัวอักษรต่างประเทศเช่น[:alpha:] á
— mklement0

@ mklement0 คำสั่ง awk แรกของคุณทำงานได้ดีและเป็นวิธีที่ง่ายที่สุดแม้ว่าการใช้ [: alpha:] ดูเหมือนว่าจะทำลายด้วยเหตุผลบางประการ - มันพลาดเป้าหมายบางอย่างในไฟล์ทดสอบ
— Brian Burns

@ bburns.km: ไม่ดีของฉัน: ฉันควรจะพูด[[:alpha:]]มากกว่า[:alpha:]( [:alpha:]หมายถึงชุดของตัวอักษรที่ตระหนักถึงสถานที่ภายในคลาสตัวอักษร ( [...]) ดังนั้นความต้องการที่มีประสิทธิภาพสำหรับคู่ [[ / ]].
— mklement0

4
ทำไมคำตอบนี้จึงซับซ้อน? คุณสามารถรวบรวมบาปที่ไร้ประโยชน์หลายอย่างในตัวอย่างสั้น ๆ นี้ awk -F: '/^[A-Za-z]/ { print $$1 }' Makefileใช้กระบวนการเดียวและหวังว่านิพจน์ปกติที่ถูกต้องมากขึ้น (แน่นอนว่าคุณสามารถมีเป้าหมายที่มีเครื่องหมายขีดล่างตัวเลข ฯลฯ เช่นเดียวกับที่ระบุไว้ในความคิดเห็นก่อนหน้านี้)
— tripleee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.