วิธีป้องกัน“ ข้อผิดพลาดไดเรกทอรีมีอยู่แล้ว” ใน makefile เมื่อใช้ mkdir


109

ฉันต้องการสร้างไดเร็กทอรีใน makefile ของฉันและฉันไม่ต้องการให้ "ไดเร็กทอรีมีข้อผิดพลาด" ซ้ำแล้วซ้ำเล่าแม้ว่าฉันจะเพิกเฉยได้ง่ายๆก็ตาม

ฉันใช้ mingw / msys เป็นหลัก แต่ต้องการบางอย่างที่ใช้ได้กับเชลล์ / ระบบอื่น ๆ ด้วย

ฉันลองแล้ว แต่ไม่ได้ผลมีความคิดอะไรไหม?

ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif

คำตอบ:


106

บน UNIX เพียงใช้สิ่งนี้:

mkdir -p $(OBJDIR)

อ็อพชัน -p เพื่อ mkdir ป้องกันข้อความแสดงข้อผิดพลาดหากมีไดเร็กทอรีอยู่


39
"โดยทั่วไปให้ยึดตามตัวเลือกและคุณสมบัติของโปรแกรมเหล่านี้ที่รองรับอย่างกว้างขวาง (โดยปกติจะระบุตำแหน่ง posix) ตัวอย่างเช่นอย่าใช้" mkdir -p "สะดวกเพราะอาจมีบางระบบไม่รองรับ ไม่ปลอดภัยสำหรับการดำเนินการแบบขนาน " gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel

8
-pไม่ทำงานบน Windows ซึ่งน่าจะเป็นคำถามที่ถามเกี่ยวกับ ไม่แน่ใจว่าสิ่งนี้ได้รับการยอมรับอย่างไร
พลวง

2
สำหรับ Windows ให้โหวตสิ่งนี้: connect.microsoft.com/PowerShell/feedback/details/1074131/…
vulcan raven

1
@ พลวงคุณอาจทำอะไรผิดพลาดถ้าคุณใช้ GNU Make นอกเปลือก MinGW ทางเลือกของคุณแม้ว่า

"บน UNIX เพียงแค่ใช้สิ่งนี้ ... " - เป็นmake -pUnix หรือ Linux?
jww

122

ดูเอกสารประกอบอย่างเป็นทางการนี่เป็นวิธีที่ดี:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    mkdir -p $(OBJDIR)

คุณควรดูการใช้งาน | ตัวดำเนินการท่อกำหนดคำสั่งเบื้องต้นเท่านั้น หมายความว่า$(OBJDIR)ควรมีเป้าหมายอยู่ (แทนที่จะเป็นเป้าหมายล่าสุด ) เพื่อสร้างเป้าหมายปัจจุบัน

โปรดทราบว่าฉันใช้mkdir -p. -pธงถูกเพิ่มเข้ามาเมื่อเทียบกับตัวอย่างของเอกสารที่ ดูคำตอบอื่น ๆ สำหรับทางเลือกอื่น


4
นี่เป็นวิธีอย่างเป็นทางการในการแก้ไขปัญหานี้
lcltj

@CraigMcQueen: ผมหมายอย่างแท้จริงว่า " $(OBJDIR)เป้าหมายควรจะเป็นสิ่งที่มีอยู่จริง " ตรวจสอบการมีอยู่ของไฟล์ (และการประทับเวลา) เพื่อตัดสินใจว่าจำเป็นต้องสร้างเป้าหมายหรือไม่ ดังนั้นที่นี่หาก$(OBJDIR)ขาดมันจะสร้างมันขึ้นมาเพื่อให้มีอยู่เพื่อสร้างเป้าหมายปัจจุบัน$(OBJS)ที่จะถูกสร้างขึ้นภายใน
ofavre

ฉันคิดว่าฉันได้ลองใช้ชุดค่าผสมอื่น ๆ ในการแก้ปัญหานี้แล้วก่อนที่จะพบสิ่งนี้ (ฉันกำลังพยายามสร้าง makefiles ที่เป็นแบบทั่วไปที่สุดเท่าที่จะทำได้ระหว่าง windows / linux) - นี่เป็นสิ่งเดียวที่ฉันสามารถใช้งานได้ แต่มัน ใช้งานได้ดี! :)
code_fodder

67

คุณสามารถใช้คำสั่งทดสอบ:

test -d $(OBJDIR) || mkdir $(OBJDIR)

7
นี่เป็นวิธีที่แนะนำหากคุณต้องการให้ makefiles ของคุณทำงานได้ทั้งใน Windows (พร้อม gnuwin32 และ PowerShell) และระบบปฏิบัติการที่รองรับ POSIX
Kris Hardy

6
ให้คุณมีแพ็คเกจ gnuwin32 CoreUtils เพื่อให้ยูทิลิตี้ 'test'
davenpcj

2
มาสายที่นี่ แต่ฉันต้องการชี้ให้เห็นว่าโซลูชันนี้สามารถแตกหักได้เมื่อสร้างแบบขนาน ( make -j) เนื่องจากเงื่อนไขการแข่งขันระหว่างเมื่อไดเร็กทอรีถูกทดสอบว่ามีอยู่และถูกสร้างขึ้น งานหนึ่งสามารถทดสอบว่าไม่ได้อยู่ที่นั่น แต่ก่อนที่จะสร้างงานนั้นงานอื่นจะสร้างไดเร็กทอรี จากนั้นเมื่อพยายามทำครั้งแรกมันจะล้มเหลวเนื่องจากมีไดเรกทอรีอยู่แล้ว ทางออกที่ดีที่สุดในกรณีนี้คือใช้คำสั่งเบื้องต้นเท่านั้นตามที่กล่าวไว้ในคำตอบของ @ TeKa (ซึ่งควรเป็นคำตอบที่ยอมรับได้)
C0deH4cker

@MarcusJ ทำงานบน OS X El Capitan ของฉัน รุ่นไหนทำลายมัน?
Franklin Yu

@ C0deH4cker - ยกโทษให้กับความไม่รู้ของฉัน ... คำตอบของ TeKa คืออะไร? ฉันคิดว่า TeKa เปลี่ยนชื่อของเขา / เธอตั้งแต่ที่คุณแสดงความคิดเห็น
jww

18

นี่คือเคล็ดลับที่ฉันใช้กับ GNU สร้างขึ้นเพื่อสร้างไดเร็กทอรีคอมไพเลอร์เอาต์พุต ก่อนอื่นกำหนดกฎนี้:

  %/.d:
          mkdir -p $(@D)
          touch $@

จากนั้นทำให้ไฟล์ทั้งหมดที่เข้าสู่ไดเร็กทอรีขึ้นอยู่กับไฟล์. d ในไดเร็กทอรีนั้น:

 obj/%.o: %.c obj/.d
    $(CC) $(CFLAGS) -c -o $@ $<

หมายเหตุการใช้ $ <แทน $ ^

สุดท้ายป้องกันไม่ให้ไฟล์. d ถูกลบโดยอัตโนมัติ:

 .PRECIOUS: %/.d

การข้ามไฟล์. d และขึ้นอยู่กับไดเร็กทอรีโดยตรงจะไม่ทำงานเนื่องจากเวลาแก้ไขไดเร็กทอรีจะถูกอัปเดตทุกครั้งที่เขียนไฟล์ในไดเร็กทอรีนั้นซึ่งจะบังคับให้สร้างใหม่ทุกครั้งที่มีการเรียกใช้ make


1
อ่าขอบคุณฉันพยายามทำให้สิ่งนี้ได้ผล ฉันไม่ต้องการ "ทดสอบ -d foo || mkdir foo" ในหลาย ๆ เป้าหมายตั้งแต่นั้นมาการใช้ "make -j2" จะทดสอบพร้อมกันการทดสอบทั้งสองให้เป็นเท็จจากนั้นสองกระบวนการจะพยายาม mkdir สุดท้ายก็ล้มเหลว
unhammer

13

หากการมีไดเร็กทอรีอยู่แล้วไม่ใช่ปัญหาสำหรับคุณคุณสามารถเปลี่ยนเส้นทาง stderr สำหรับคำสั่งนั้นได้โดยกำจัดข้อความแสดงข้อผิดพลาด:

-mkdir $(OBJDIR) 2>/dev/null

สิ่งนี้มีข้อดีของการทำงานบน Windows ด้วย อย่าลืม '-' หน้าคำสั่งเพื่อไม่ให้ประกันตัว
Michael Burr


10

บน Windows

if not exist "$(OBJDIR)" mkdir $(OBJDIR)

บน Unix | ลินุกซ์

if [ ! -d "$(OBJDIR)" ]; then mkdir $(OBJDIR); fi

อันนี้สำหรับ Windows
เช็ค

/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii

รุ่นแรกใช้สำหรับ Windows ตัวเลือกที่สองทำงานบน Linux ตามที่ @checksum กล่าว
Edgard Leal

ประการแรก Windows เวอร์ชันไม่ใช่อะตอมดังนั้นจึงไม่ทำงานเมื่อกระบวนการอื่น (เช่นกฎในโหมดขนาน) สร้างไดเร็กทอรีระหว่าง "ไม่มีอยู่จริง" และ "mkdir"
Petr Vepřek

7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif

ใช้งานได้ดีสำหรับการทำของ mingw โดยไม่ต้องใช้เครื่องมือเพิ่มเติม
davenpcj

6
$(OBJDIR):
    mkdir $@

ซึ่งใช้ได้กับหลายไดเร็กทอรีเช่น.

OBJDIRS := $(sort $(dir $(OBJECTS)))

$(OBJDIRS):
    mkdir $@

การเพิ่ม$(OBJDIR)เป็นเป้าหมายแรกทำได้ดี



0

หากคุณเพิกเฉยต่อรหัสส่งคืนอย่างชัดเจนและถ่ายโอนสตรีมข้อผิดพลาดการทำของคุณจะไม่สนใจข้อผิดพลาดหากเกิดขึ้น:

mkdir 2>/dev/null || true

สิ่งนี้ไม่ควรก่อให้เกิดอันตรายจากการแข่งขันในการทำแบบขนาน - แต่ฉันยังไม่ได้ทดสอบเพื่อให้แน่ใจ


0

ง่ายกว่าคำตอบของ Lars เล็กน้อย:

something_needs_directory_xxx : xxx/..

และกฎทั่วไป:

%/.. : ;@mkdir -p $(@D)

ไม่มีไฟล์สัมผัสเพื่อล้างหรือสร้าง. PRECIOUS :-)

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

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