วิธีกำหนดเอาต์พุตของคำสั่งให้กับตัวแปร Makefile


225

ฉันจำเป็นต้องดำเนินการบางอย่างให้กฎตามเงื่อนไขเฉพาะในกรณีที่ติดตั้ง Python มากกว่ารุ่นที่แน่นอน (พูด 2.5)

ฉันคิดว่าฉันสามารถทำอะไรบางอย่างเช่นการดำเนินการ:

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

จากนั้นใช้เอาต์พุต ('1' ถ้าตกลง '0' เป็นอย่างอื่น) ในifeqคำสั่ง make

ในเชลล์สคริปอย่างง่าย ๆ มันแค่:

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

แต่มันไม่ทำงานใน Makefile

ข้อเสนอแนะใด ๆ ฉันสามารถใช้วิธีแก้ไขปัญหาอื่น ๆ ที่สมเหตุสมผลเพื่อบรรลุเป้าหมายนี้


ทำเครื่องหมายย้อนกลับแปลก ๆ รอบ ๆ คำสั่งทำงานเพื่อเรียกใช้สคริปต์อื่น ๆ สำหรับฉันใน Makefile อาจเป็นอย่างอื่น
Leif Gruenwoldt

คำตอบ:


346

ใช้ Make shellbuiltin เหมือนในMY_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)

34
shell ไม่ใช่คำสั่ง Make builtin มาตรฐาน นี่คือ GNU Make builtin
Dereckson

12
stackoverflow.com/a/2373111/12916$เพิ่มหมายเหตุสำคัญเกี่ยวกับการหลบหนี
เจสซีกลิก

6
ตัวอย่างง่ายๆนี้ใช้งานได้ มันยังทำงานได้กับไปป์ไลน์คำสั่งเชลล์ แต่มันเป็นสิ่งสำคัญที่คุณควรใช้ $$ เพื่อแทน $ ในคำสั่งเชลล์
Sergey P. aka azure

28
ในขณะที่คำถามมีปัญหาเล็กน้อยควรทำ MY_VAR: = $ (เชลล์ ... ) มิฉะนั้นทุกครั้งที่มีการประเมิน MY_VAR จะดำเนินการ $ (เชลล์ ... ) อีกครั้ง
Russ Schultz

ฉันมีช่องว่างระหว่างเชลล์และเครื่องหมายวงเล็บเปิดและหลังจากลบพื้นที่ออกแล้วข้อความสำหรับส่งออก makefile ของฉันก็ทำได้
peterchaula

29

การตัดการมอบหมายให้evalทำงานกับฉัน

# 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)

6
"หมายเหตุ: @true ที่นี่ป้องกันไม่ให้ Make คิดว่าไม่มีอะไรที่ต้องทำ" อืมนั่นคือสิ่งที่.PHONY always make these targetsมีไว้เพื่อ
underscore_d

ขอบคุณ! สิ่งนี้ช่วยให้ฉันหลีกเลี่ยง "weird bash" ใน makefile
Nam G VU

19

นี่คือตัวอย่างที่ซับซ้อนมากขึ้นเล็กน้อยกับการวางท่อและการกำหนดตัวแปรภายในสูตร:

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)

2
ในกรณีที่มันมีประโยชน์ฉันกำลังใช้วิธีการที่คล้ายกันเล็กน้อยเพื่อรับPODNAMEโดยชื่อการปรับใช้:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter

ไวยากรณ์สับสนฉันหนึ่งวินาทีจนกระทั่งฉันรู้ว่าคุณใช้ทั้งเชลล์ builtin eval (บน docker-env line) และmake function eval (ในบรรทัดถัดไป)
Brian Gordon

17

ฉันกำลังเขียนคำตอบเพื่อเพิ่มการมองเห็นไวยากรณ์จริงที่แก้ปัญหาได้ แต่น่าเสียดายที่สิ่งที่บางคนอาจเห็นว่าเป็นเรื่องไม่สำคัญอาจกลายเป็นปวดหัวอย่างมีนัยสำคัญกับคนที่กำลังมองหาคำตอบง่ายๆสำหรับคำถามที่เหมาะสม

ใส่สิ่งต่อไปนี้ลงในไฟล์ "Makefile"

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

all:
    @echo MY_VAR IS $(MY_VAR)

พฤติกรรมที่คุณต้องการดูคือสิ่งต่อไปนี้ (สมมติว่าคุณติดตั้งงูหลามแล้ว)

make
MY_VAR IS 1

หากคุณคัดลอกและวางข้อความด้านบนลงใน Makefile คุณจะได้สิ่งนี้หรือไม่ อาจจะไม่. คุณอาจจะได้รับข้อผิดพลาดเหมือนกับที่รายงานไว้ที่นี่:

makefile: 4: *** ตัวคั่นขาดหายไป หยุด

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


ขึ้นอยู่กับว่าคุณกำลังวางเอดิเตอร์อะไรอยู่ ฉันเพิ่งคัดลอกและวางลงในโปรแกรมแก้ไข Eclipse Makefile และมีแท็บนำ (ตามต้องการ)
Technophile

โอ้ฉันไม่ได้คิดอย่างนั้น อะตอมที่นี่
AlanSE

11

ระวังสูตรอาหารเช่นนี้

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

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

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

ปัญหาทั้งสองได้รับการแก้ไขแล้วและสามารถใช้ตัวแปรได้ แบ็กสแลชรวมทั้งสองบรรทัดเพื่อทำงานในหนึ่งเชลล์ดังนั้นการตั้งค่าของตัวแปรและการอ่านตัวแปร afterwords จึงทำงานได้

ฉันรู้ว่าโพสต์ต้นฉบับบอกว่าจะได้ผลลัพธ์ของคำสั่ง shell ไปยังตัวแปร MAKE ได้อย่างไรและคำตอบนี้แสดงวิธีนำมันมาเป็นตัวแปร shell แต่ผู้อ่านคนอื่นอาจได้รับประโยชน์

การปรับปรุงขั้นสุดท้ายหนึ่งหากผู้บริโภคคาดว่าจะมีการตั้งค่า "ตัวแปรสภาพแวดล้อม" คุณต้องส่งออก

my_shell_script
    echo $MY_ID

จะต้องใช้สิ่งนี้ใน makefile

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

หวังว่าจะช่วยใครซักคน โดยทั่วไปแล้วเราควรหลีกเลี่ยงการทำงานจริง ๆ นอกสูตรเพราะถ้ามีคนใช้ makefile พร้อมตัวเลือก '--dry-run' เพื่อดูว่ามันจะทำอะไรเท่านั้นมันจะไม่มีผลข้างเคียงใด ๆ การ$(shell)โทรทุกครั้งจะถูกประเมินในเวลารวบรวมและงานจริงบางอย่างอาจเกิดขึ้นโดยไม่ตั้งใจ ดีกว่าที่จะออกจากงานจริงเช่นการสร้างรหัสไปยังด้านในของสูตรเมื่อเป็นไปได้


9

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

# 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)

คุณมีคำอธิบายที่ดีครั้งแรกที่ฉันได้เจอทุกคน ขอบคุณ. แม้ว่าสิ่งหนึ่งที่จะเพิ่มคือว่าถ้า$(SET_ID)ชีวิตภายในของifประโยคที่เป็นเท็จแล้วมันก็ยังคงเรียกว่า
Roman

มันยังคงถูกเรียกใช้เพราะหาก stmts ถูกประเมินที่ makefile เวลาคอมไพล์ไม่ใช่เวลารันไทม์ สำหรับคำแนะนำเฉพาะเวลาทำงานให้วางไว้ในสูตรอาหาร หากมีเงื่อนไขให้เพิ่มถ้า stmts เขียนใน bash / shell เป็นส่วนหนึ่งของสูตร
Juraj

0

ในตัวอย่างด้านล่างฉันได้จัดเก็บพา ธ ของโฟลเดอร์ Makefile LOCAL_PKG_DIRแล้วใช้LOCAL_PKG_DIRตัวแปรในเป้าหมาย

Makefile:

LOCAL_PKG_DIR := $(shell eval pwd)

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

ขั้วเอาท์พุท:

$ make print
/home/amrit/folder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.