ฉันจะเขียนคำสั่ง 'cd' ใน makefile ได้อย่างไร


354

ตัวอย่างเช่นฉันมีสิ่งนี้ใน makefile ของฉัน:

all:
     cd some_directory

แต่เมื่อฉันพิมพ์makeฉันเห็นเฉพาะ 'cd some_directory' เท่านั้นเช่นในechoคำสั่ง


5
ไม่ชัดเจนว่าคุณต้องการทำอะไร แต่จากประสบการณ์ของmakeฉันฉันไม่เคยต้องการเปลี่ยนไดเรกทอรีเช่นนี้ บางทีคุณควรลองวิธีอื่นในการแก้ปัญหาของคุณ?
P Shved

2
มันเป็นความผิดพลาดทั่วไปที่มือใหม่ที่เชื่อว่าสารบบของคุณมีความสำคัญ สำหรับสิ่งที่มันไม่ได้; สามารถเกือบตลอดเวลาจะแสดงออกมากขึ้นเป็นประโยชน์cd dir; cmd file cmd dir/file
tripleee

34
เป็นความผิดพลาดทั่วไปที่มือใหม่ที่จะเชื่อว่าไดเรกทอรีงานปัจจุบันของคุณไม่สำคัญ หลายโปรแกรมโดยเฉพาะเชลล์สคริปต์จะเขียนด้วยค่าเฉพาะ.ในใจ เป็นความจริงที่เครื่องมือส่วนใหญ่ได้รับการออกแบบในแบบที่คุณไม่จำเป็นต้องเปลี่ยน pwd ของมัน แต่นี่ไม่เป็นความจริงเสมอไปและฉันไม่คิดว่าเป็นความคิดที่ดีที่จะเรียกมันว่า "ผิดพลาด" เพราะเชื่อว่าสารบบของคุณอาจมีความสำคัญ
Evelyn Kokemoor

เคล็ดลับ: ถ้าคำสั่ง cd ของคุณบอกว่า "ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว" แม้เมื่อ (ญาติ) ไดเรกทอรีไม่อยู่ตรวจสอบว่าตัวแปรสภาพแวดล้อม CDPATH ของคุณจะว่างหรือรวมถึง "" ทำให้คำสั่งประมวลผลด้วย "sh" ซึ่งจะค้นหาเส้นทางสัมพัทธ์ผ่าน CDPATH เท่านั้นหากตั้งค่าไว้ ตรงกันข้ามกับทุบตีซึ่งจะลอง ก่อนปรึกษา CDPATH
เดนิสฮาว

คำตอบ:


620

จริงๆแล้วมันกำลังดำเนินการคำสั่งเปลี่ยนไดเรกทอรีเป็นsome_directoryอย่างไรก็ตามสิ่งนี้จะดำเนินการในเปลือกกระบวนการย่อยและส่งผลกระทบต่อทั้งไม่สร้างหรือเปลือกที่คุณกำลังทำงานจาก

หากคุณต้องการทำงานภายในเพิ่มเติมsome_directoryคุณต้องเพิ่มเซมิโคลอนและต่อท้ายคำสั่งอื่นเช่นกัน โปรดทราบว่าคุณไม่สามารถใช้การขึ้นบรรทัดใหม่ได้เนื่องจากถูกตีความโดย make เป็นจุดสิ้นสุดของกฎดังนั้นการขึ้นบรรทัดใหม่ใด ๆ ที่คุณใช้เพื่อความชัดเจนจะต้องถูก backslash

ตัวอย่างเช่น:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

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

all:
        cd some_dir && echo "I'm in some_dir" && \
          gcc -Wall -o myTest myTest.c

สิ่งนี้มีความสำคัญอย่างยิ่งเมื่อทำการทำลายเช่นล้างข้อมูลในขณะที่คุณจะทำลายสิ่งที่ไม่ถูกต้องหากcdล้มเหลวด้วยเหตุผลใดก็ตาม

การใช้งานทั่วไปที่เรียกว่าสร้างในไดเรกทอรีย่อยซึ่งคุณอาจต้องการตรวจสอบ มีตัวเลือกบรรทัดคำสั่งสำหรับสิ่งนี้เพื่อให้คุณไม่ต้องเรียกcdตัวเองดังนั้นกฎของคุณจะมีลักษณะเช่นนี้

all:
        $(MAKE) -C some_dir all

ซึ่งจะเปลี่ยนเป็นsome_dirและดำเนินการMakefileกับเป้าหมาย "ทั้งหมด" ตามแนวทางปฏิบัติที่ดีที่สุดให้ใช้$(MAKE)แทนการโทรmakeโดยตรงเนื่องจากมันจะโทรหาอินสแตนซ์ที่ถูกต้อง (เช่นคุณใช้เวอร์ชัน make พิเศษสำหรับสภาพแวดล้อมการสร้างของคุณ) รวมถึงพฤติกรรมที่แตกต่างกันเล็กน้อยเมื่อทำงาน -tโดยใช้สวิทช์บางอย่างเช่น

สำหรับบันทึกที่ทำให้เสมอสะท้อนคำสั่งจะรัน (ยกเว้นกรณีการปราบปรามอย่างชัดเจน) แม้ว่าจะมีการส่งออกไม่ซึ่งคือสิ่งที่คุณเห็นกำลัง


8
ก็ไม่เสมอไป ในการระงับเสียงก้องเพียงแค่ใส่ @ ที่จุดเริ่มต้นของบรรทัด
เบต้า

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

46
สอง nits: 1. คำสั่งควรเข้าร่วมจริง ๆ&&เพราะ;ถ้าไม่มีไดเรกทอรีอยู่และcdล้มเหลวเชลล์จะยังคงเรียกใช้คำสั่งที่เหลือในไดเรกทอรีปัจจุบันซึ่งอาจทำให้ไฟล์ "ลึกลับไม่" พบ” ข้อความสำหรับคอมไพล์, clean:: cd dir; rm -rf *ลูปไม่มีที่สิ้นสุดเมื่ออัญเชิญแต่งหน้าหรือภัยพิบัติสำหรับกฎเช่น 2. เมื่ออัญเชิญย่อยทำให้โทร$(MAKE)แทนmakeเพื่อให้ตัวเลือกจะถูกส่งผ่านได้อย่างถูกต้อง
andrewdotn

2
@ จริงฉันมักจะกำหนดกฎรูปแบบเช่น: %-recursive:กับร่างกาย: @T="$@";$(MAKE) -C some_dir $${T%-*}(ฉันมักจะมีห่วงสำหรับเกินไปในการวนรอบรายการย่อยที่$${T%-*}เป็นการขยายตัวทุบตีซึ่งเอา-recursiveส่วนหนึ่งของชื่อเป้าหมาย) แล้ว กำหนดอย่างชัดเจนสั้นมือ (และ .PHONY) เป้าหมายสำหรับแต่ละเช่นall: all-recursive, ,check: check-recursive clean: clean-recursive
falstro

1
@ChristianStewart จริงตามที่ได้กล่าวไว้ในความคิดเห็นที่ 2 และ 3
falstro

94

เริ่มต้นจากGNU สร้าง 3.82 (กรกฎาคม 2010) คุณสามารถใช้.ONESHELLเป้าหมายพิเศษเพื่อเรียกใช้สูตรอาหารทั้งหมดในการเริ่มต้นเชลล์ (การเน้นตัวหนา):

  • เป้าหมายพิเศษใหม่: .ONESHELLแนะนำให้ทำการเรียกใช้อินสแตนซ์เดียวของเชลล์และจัดทำสูตรทั้งหมดโดยไม่คำนึงถึงจำนวนบรรทัดที่มี [ ... ]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
เพิ่งทราบว่าpwdมันใช้งานได้ดีเช่นเดียวกับ`pwd`(กับ backticks) แต่$(shell pwd)และ$(PWD)จะยังคงส่งคืนไดเรกทอรีก่อนที่จะทำcdคำสั่งดังนั้นคุณจึงไม่สามารถใช้พวกเขาโดยตรง
anol

3
ใช่เพราะการขยายตัวแปรและฟังก์ชั่นเสร็จสิ้นก่อนดำเนินการคำสั่งโดย make ในขณะที่pwdและ`pwd`ถูกดำเนินการโดยเชลล์เอง
Chnossos

2
ไม่ใช่ทุกคนที่ทำงานเกี่ยวกับ makefiles แบบดั้งเดิมและแม้กระทั่งคำตอบนี้ก็คือการรู้ว่าความเป็นไปได้นี้มีอยู่จริง
Chnossos

1
นี่อาจเป็นตัวเลือกที่น่ารำคาญ / อันตรายเนื่องจากคำสั่งสุดท้ายสำหรับเป้าหมายเท่านั้นที่สามารถทำให้เกิดความล้มเหลว (ความล้มเหลวใด ๆ ของคำสั่งก่อนหน้านี้จะถูกละเว้น) และอาจไม่มีใครทำงานกับคุณได้
tobii

1
ตั้งSHELLFLAGSเป็น-e -cและเชลล์จะออกจากคำสั่งแรกที่ล้มเหลว
ทิโมธีบอลด์วิน

21

นี่คือเคล็ดลับน่ารักที่จะจัดการกับไดเรกทอรีและทำ แทนที่จะใช้สตริงหลายบรรทัดหรือ "cd;" ในแต่ละคำสั่งให้นิยามฟังก์ชัน chdir อย่างง่าย ๆ ดังนี้:

CHDIR_SHELL := $(SHELL)
define chdir
   $(eval _D=$(firstword $(1) $(@D)))
   $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef

จากนั้นสิ่งที่คุณต้องทำคือเรียกมันในกฎของคุณดังนี้:

all:
          $(call chdir,some_dir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

คุณสามารถทำสิ่งต่อไปนี้:

some_dir/myTest:
          $(call chdir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

41
"น่ารัก"? เหมือนเชือกมากพอที่จะยิงตัวเองที่เท้า
tripleee

1
ไดเร็กทอรีปัจจุบันนี้ถูกตั้งค่าไว้สำหรับคำสั่งเฉพาะในกฎนั้นหรือสำหรับกฎที่เรียกใช้ทั้งหมดในภายหลังหรือไม่? นอกจากนี้รูปแบบของงานนี้ภายใต้ Windows จะเป็นอย่างไร
user117529

8
แน่นอนว่านี่คือการหยุดการดำเนินการแบบขนาน ( -jn) ซึ่งเป็นจุดรวมของการทำจริงๆ
bobbogo

5
นี่เป็นแฮ็คที่ไม่ดี หากคุณต้องหันไปใช้สิ่งนี้คุณไม่ได้ใช้ Makefiles สำหรับสิ่งที่พวกเขาทำ
gatopeich

4
ฉันจะไม่เห็นด้วยว่าแฮ็คที่ไม่ดีนั้นแน่นอน แต่แสดงให้เห็นถึงสิ่งชั่วร้ายที่คุณสามารถทำได้
JoeS

9

คุณต้องการให้มันทำอะไรเมื่อไปถึงที่นั่น? แต่ละคำสั่งจะถูกดำเนินการใน subshell ดังนั้น subshell จะเปลี่ยนไดเรกทอรี แต่ผลลัพธ์สุดท้ายคือคำสั่งถัดไปยังคงอยู่ในไดเรกทอรีปัจจุบัน

ด้วย GNU make คุณสามารถทำสิ่งต่อไปนี้

BIN=/bin
foo:
    $(shell cd $(BIN); ls)

5
ทำไม$(shell ...)เวลาcd $(BIN); lsหรือcd $(BIN) && ls(ตามที่ @ andrewdotn ชี้ให้เห็น) น่าจะเพียงพอ
vapace


-6

แบบนี้:

target:
    $(shell cd ....); \
    # ... commands execution in this directory
    # ... no need to go back (using "cd -" or so)
    # ... next target will be automatically in prev dir

โชคดี!


1
ไม่ผิด โดยเฉพาะอย่างยิ่ง$(shell cd ....)จะถูกดำเนินการเมื่อ Makefile จะถูกแยกวิเคราะห์ในตอนแรกไม่ได้เมื่อสูตรนี้เฉพาะจะทำงาน
tripleee

@triplee ไม่มาก - The $(shell)ขยายเฉพาะเมื่อทำให้ตัดสินใจที่จะสร้างเป้าหมาย ถ้าทำให้ไม่เคยต้องการสูตรมันจะไม่ขยาย
bobbogo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.