กำหนดตัวแปรในเวลาดำเนินการของกฎ


209

ใน GNUmakefile ของฉันฉันต้องการมีกฎที่ใช้ไดเรกทอรีชั่วคราว ตัวอย่างเช่น:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

เขียนเป็นกฎข้างต้นสร้างไดเรกทอรีชั่วคราวในเวลาที่กฎจะถูกแยกวิเคราะห์ ซึ่งหมายความว่าแม้ว่าฉันจะไม่สร้าง out.tar ตลอดเวลาก็มีการสร้างไดเรกทอรีชั่วคราวจำนวนมาก ฉันต้องการหลีกเลี่ยง / tmp ของฉันที่เกลื่อนไปด้วยไดเรกทอรีชั่วคราวที่ไม่ได้ใช้

มีวิธีที่จะทำให้ตัวแปรถูกกำหนดเมื่อกฎถูกไล่ออกซึ่งตรงข้ามกับเมื่อใดก็ตามที่มันถูกกำหนด?

ความคิดหลักของฉันคือการทิ้ง mktemp และ tar ลงในเชลล์สคริปต์ แต่มันก็ดูไม่น่าดู

คำตอบ:


322

ในตัวอย่างของคุณTMPตัวแปรจะถูกตั้งค่า (และสร้างไดเร็กทอรีชั่วคราว) เมื่อใดก็ตามที่กฎสำหรับout.tarถูกประเมินผล เพื่อสร้างไดเรกทอรีเฉพาะเมื่อout.tarถูกไล่ออกจริงคุณต้องย้ายการสร้างไดเรกทอรีลงในขั้นตอน:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

EVALฟังก์ชั่นการประเมินสตริงราวกับว่ามันได้รับการพิมพ์ลงใน Makefile ด้วยตนเอง ในกรณีนี้มันตั้งค่าTMPตัวแปรเป็นผลลัพธ์ของการshellเรียกใช้ฟังก์ชัน

แก้ไข (เพื่อตอบสนองต่อความคิดเห็น):

ในการสร้างตัวแปรเฉพาะคุณสามารถทำสิ่งต่อไปนี้:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

นี้จะ prepend ชื่อของเป้าหมาย (out.tar ในกรณีนี้) out.tar_TMPให้กับตัวแปรการผลิตตัวแปรที่มีชื่อ หวังว่าจะเพียงพอที่จะป้องกันความขัดแย้ง


2
เจ๋งชี้แจงบางอย่าง ... นี้จะไม่ขอบเขต TMP กับเป้าหมายนี้มันจะ? ดังนั้นหากมีกฎอื่น ๆ ที่มีการใช้ $ (TMP) ของตัวเอง (อาจขนานกับ -j) อาจมีข้อขัดแย้งหรือไม่ @echo ยังจำเป็นด้วยหรือไม่ ดูเหมือนว่าคุณจะทิ้งมันไว้
Emil Sit

3
ดูเหมือนว่าจะทำเคล็ดลับ (แม้ว่าเล็กน้อยทึบแสงเพื่อกูรูที่ไม่ทำให้ค่าเฉลี่ย :-) ขอบคุณ!
Emil Sit

29
ระวังด้วยวิธีนี้! $(eval $@_TMP := $(shell mktemp -d))จะเกิดขึ้นเมื่อ Makefile ถูกประเมินครั้งแรกไม่ใช่ตามลำดับขั้นตอนของกฎ ในคำอื่น ๆ$(eval ...)เกิดขึ้นเร็วกว่าที่คุณคิด ในขณะที่อาจไม่เป็นไรสำหรับตัวอย่างนี้วิธีนี้จะทำให้เกิดปัญหาสำหรับการดำเนินการตามลำดับบางอย่าง
JamesThomasMoon1979

1
@ JamesThomasMoon1979 คุณรู้วิธีที่จะเอาชนะข้อ จำกัด เหล่านั้นเช่นประเมินตัวแปรบางอย่างที่ควรเป็นผลมาจากขั้นตอนที่ดำเนินการก่อนหน้านี้ในกฎหรือไม่
Vadim Kotov

2
ตอบคำถามของฉันเอง: วิธีแก้ปัญหาสำหรับฉันคือการสร้างไฟล์ระหว่างการดำเนินการกฎข้อที่ 1 และพยายามที่จะประเมินผลพวกเขาในขั้นตอนแรกของกฎข้อที่ 2
Vadim Kotov

63

วิธีที่ง่ายในการทำเช่นนี้คือการเขียนลำดับทั้งหมดเป็นเชลล์สคริปต์

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\

ฉันได้รวบรวมเคล็ดลับที่เกี่ยวข้องไว้ที่นี่: https://stackoverflow.com/a/29085684/86967


3
นี้แน่นอนคำตอบที่ง่ายที่สุดและทำให้ดีที่สุด (หลีกเลี่ยง@และevalและการทำงานเดียวกัน) โปรดทราบว่าในผลลัพธ์ของคุณคุณเห็น$TMP(เช่นtar -C $TMP ...) แม้ว่าค่าจะถูกส่งผ่านไปยังคำสั่งที่ถูกต้อง
Karl Richter

นี่คือวิธีปกติ ฉันหวังว่าผู้คนจะเห็นคำตอบนี้เพราะในทางปฏิบัติไม่มีใครผ่านความพยายามทั้งหมดของคำตอบที่ยอมรับ
pmos

ถ้าคุณใช้คำสั่งเฉพาะเชลล์? ในกรณีนั้นคำสั่งเชลล์จะต้องเป็นภาษาเชลล์เดียวกับที่ใช้ในการเรียก ฉันเคยเห็น makefile กับ shebang บ้าง
ptitpion

@ptitpion: คุณอาจต้องการSHELL := /bin/bashmakefile เพื่อเปิดใช้งานคุณสมบัติเฉพาะของ BASH
สูงศักดิ์

31

ความเป็นไปได้อีกอย่างหนึ่งคือการใช้บรรทัดแยกเพื่อตั้งค่าสร้างตัวแปรเมื่อกฎเริ่มทำงาน

ตัวอย่างเช่นนี่คือ makefile ที่มีสองกฎ หากกฎเริ่มทำงานจะสร้าง temp dir และตั้งค่า TMP เป็นชื่อ temp dir

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

การรันโค้ดจะสร้างผลลัพธ์ที่คาดหวัง:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow

เช่นเดียวกับสิ่งเตือนความจำ GNU make มีไวยากรณ์สำหรับสิ่งที่จำเป็นต้องมีเฉพาะคำสั่งซื้อซึ่งอาจจำเป็นต้องเสริมวิธีการนี้ให้สมบูรณ์
anol

11
ระวังด้วยวิธีนี้! ruleA: TMP = $(shell mktemp -d testruleA_XXXX)และruleB: TMP = $(shell mktemp -d testruleB_XXXX)จะเกิดขึ้นเมื่อ Makefile ถูกประเมินครั้งแรก ในคำอื่น ๆruleA: TMP = $(shell ...เกิดขึ้นเร็วกว่าที่คุณคิด ขณะนี้อาจใช้ได้กับกรณีนี้วิธีนี้จะทำให้เกิดปัญหาสำหรับการดำเนินการตามลำดับบางอย่าง
JamesThomasMoon1979

1

ฉันไม่ชอบคำตอบ "ไม่" แต่ ... ไม่

makeตัวแปรของทั่วโลกและควรได้รับการประเมินในระหว่างขั้นตอน "การแยกวิเคราะห์" ของ makefile ไม่ใช่ในระหว่างขั้นตอนการดำเนินการ

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

ตัวแปรเป้าหมายที่เฉพาะเจาะจงเกินไปจะถือว่าเป็นอันตรายจากการใช้งานที่ทำให้คนอื่น ๆ : kati , Mozilla pymake เนื่องจากเป้าหมายเหล่านั้นสามารถสร้างได้แตกต่างกันไปขึ้นอยู่กับว่ามันสร้างขึ้นแบบสแตนด์อโลนหรือเป็นการพึ่งพาของเป้าหมายหลักที่มีตัวแปรเฉพาะเป้าหมาย และคุณจะไม่รู้ว่ามันเป็นอย่างไรเพราะคุณไม่รู้ว่าอะไรถูกสร้างขึ้นแล้ว

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