cách ngăn chặn "lỗi thư mục đã tồn tại" trong makefile khi sử dụng mkdir


109

Tôi cần tạo một thư mục trong makefile của mình và tôi không muốn lặp đi lặp lại "lỗi thư mục đã tồn tại" mặc dù tôi có thể dễ dàng bỏ qua nó.

Tôi chủ yếu sử dụng mingw / msys nhưng cũng muốn thứ gì đó hoạt động trên các shell / hệ thống khác.

Tôi đã thử điều này nhưng nó không hoạt động, bất kỳ ý tưởng?

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

Câu trả lời:


106

Trên UNIX Chỉ cần sử dụng cái này:

mkdir -p $(OBJDIR)

Tùy chọn -p thành mkdir ngăn thông báo lỗi nếu thư mục tồn tại.


39
"Nói chung, hãy tuân thủ các tùy chọn và tính năng được hỗ trợ rộng rãi (thường là do posix chỉ định) của các chương trình này. Ví dụ: không sử dụng 'mkdir -p', rất tiện lợi vì một số hệ thống không hỗ trợ nó ở tất cả và với người khác, nó không phải là an toàn để thực hiện song song ". gnu.org/s/hello/manual/make/Utilities-in-Makefiles.html
greg.kindel

8
-pkhông hoạt động trên Windows, đây có lẽ là điều mà câu hỏi đang đặt ra. Không chắc chắn làm thế nào điều này bao giờ được chấp nhận.
Antimony

2
Đối với Windows, bỏ phiếu bầu chọn này: connect.microsoft.com/PowerShell/feedback/details/1074131/…
vulcan raven

1
@Antimony Có thể bạn đã làm sai điều gì đó nếu bạn đang sử dụng GNU Make bên ngoài vỏ MinGW. Sự lựa chọn của bạn, mặc dù.
yyny

"Trên UNIX Chỉ sử dụng cái này ..." - Là make -pUnix hay Linux?
jww

122

Xem tài liệu thực hiện chính thức , đây là một cách tốt để làm điều đó:

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)

Bạn sẽ thấy ở đây cách sử dụng của | nhà điều hành đường ống, xác định điều kiện tiên quyết duy nhất. Có nghĩa là $(OBJDIR)mục tiêu phải tồn tại (thay vì gần đây hơn ) để xây dựng mục tiêu hiện tại.

Lưu ý rằng tôi đã sử dụng mkdir -p. Các -plá cờ đã được bổ sung so với ví dụ về các tài liệu. Xem các câu trả lời khác cho một thay thế khác.


4
Đây chắc chắn là cách chính thức cho vấn đề này.
lcltj

@CraigMcQueen: Ý tôi thực sự là " $(OBJDIR)mục tiêu phải tồn tại " . Kiểm tra sự hiện diện của tệp (và dấu thời gian) để quyết định xem nó có cần xây dựng mục tiêu hay không. Do đó ở đây, nếu $(OBJDIR)không có nó sẽ xây dựng nó, vì vậy nó tồn tại để xây dựng mục tiêu hiện tại $(OBJS), mục tiêu đó sẽ được tạo bên trong.
ofavre

Tôi nghĩ rằng tôi đã thực sự thử mọi cách kết hợp khác để giải quyết vấn đề này trước khi tìm ra điều này (tôi đang cố gắng tạo các tệp trang điểm càng chung chung càng tốt giữa các cửa sổ / linux) - đây là cách duy nhất tôi có thể làm việc, nhưng nó hoạt động rất tốt! :)
code_fodder

67

Bạn có thể sử dụng lệnh kiểm tra:

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

7
Đây là cách ưu tiên nếu bạn cần các tệp trang điểm của mình hoạt động trong cả Windows (với gnuwin32 và PowerShell) và hệ điều hành tương thích POSIX.
Kris Hardy

6
CUNG CẤP rằng bạn có gói CoreUtils gnuwin32 để cung cấp tiện ích 'kiểm tra'.
davenpcj

2
Ở đây hơi muộn, nhưng tôi muốn chỉ ra rằng giải pháp này có thể bị hỏng khi xây dựng song song ( make -j) do điều kiện chạy đua giữa khi thư mục được kiểm tra sự tồn tại và nó được tạo. Một công việc có thể kiểm tra rằng nó không có ở đó nhưng trước khi nó tạo nó, một công việc khác sẽ tạo ra thư mục. Sau đó, khi lần đầu tiên cố gắng thực hiện nó, nó sẽ thất bại vì thư mục đã tồn tại. Giải pháp tốt nhất trong trường hợp này là chỉ sử dụng các điều kiện tiên quyết về đơn đặt hàng như đã đề cập trong câu trả lời của @ TeKa (phải là câu trả lời được chấp nhận).
C0deH4cker

@MarcusJ Hoạt động trên OS X El Capitan của tôi. Phiên bản nào đã phá vỡ nó?
Franklin Yu

@ C0deH4cker - Thứ lỗi cho sự thiếu hiểu biết của tôi ... Câu trả lời nào là TeKa? Tôi nghĩ rằng TeKa đã thay đổi tên của anh ấy / cô ấy kể từ khi bạn đưa ra nhận xét.
jww

18

Đây là một mẹo tôi sử dụng với GNU để tạo các thư mục đầu ra của trình biên dịch. Đầu tiên xác định quy tắc này:

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

Sau đó, tạo tất cả các tệp đi vào thư mục phụ thuộc vào tệp .d trong thư mục đó:

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

Lưu ý sử dụng $ <thay vì $ ^.

Cuối cùng, ngăn các tệp .d tự động bị xóa:

 .PRECIOUS: %/.d

Bỏ qua tệp .d và tùy thuộc trực tiếp vào thư mục, sẽ không hoạt động, vì thời gian sửa đổi thư mục được cập nhật mỗi khi tệp được ghi trong thư mục đó, điều này sẽ buộc phải xây dựng lại ở mỗi lần gọi thực hiện.


1
Ah, cảm ơn, tôi đang cố gắng làm cho một cái gì đó như thế này hoạt động. Tôi không muốn "kiểm tra -d foo || mkdir foo" trên một số mục tiêu, vì khi đó việc sử dụng "make -j2" sẽ kiểm tra chúng cùng một lúc, cả hai kiểm tra đều cho sai và sau đó hai quy trình sẽ cố gắng mkdir, cuối cùng trong số họ thất bại.
vui

13

Nếu thư mục đã tồn tại không phải là vấn đề đối với bạn, bạn chỉ có thể chuyển hướng stderr cho lệnh đó, loại bỏ thông báo lỗi:

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

Điều này cũng có lợi thế khi làm việc trên Windows. Đừng quên dấu '-' ở phía trước lệnh để make không bảo chứng.
Michael Burr 19-08

11

Bên trong makefile của bạn:

target:
    if test -d dir; then echo "hello world!"; else mkdir dir; fi

10

Trên Windows

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

Trên Unix | Linux

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

Cái này dành cho Windows.
tổng kiểm tra

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

Phiên bản đầu tiên dành cho Windows, tùy chọn thứ hai hoạt động trên Linux như @checksum đã nói
Edgard Leal

Phiên bản đầu tiên, Windows, không phải là nguyên tử nên nó không hoạt động khi một quy trình khác (ví dụ: quy tắc ở chế độ song song) tạo thư mục giữa "không tồn tại" và "mkdir".
Petr Vepřek

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

Điều này hoạt động tốt để chế tạo mingw mà không cần thêm công cụ.
davenpcj

6
$(OBJDIR):
    mkdir $@

Điều này cũng hoạt động cho nhiều thư mục, ví dụ.

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

$(OBJDIRS):
    mkdir $@

Việc thêm $(OBJDIR)làm mục tiêu đầu tiên hoạt động tốt.


3

Nó hoạt động dưới mingw32 / msys / cygwin / linux

ifeq "$(wildcard .dep)" ""
-include $(shell mkdir .dep) $(wildcard .dep/*)
endif

0

Nếu bạn rõ ràng bỏ qua mã trả lại và kết xuất luồng lỗi thì lệnh của bạn sẽ bỏ qua lỗi nếu nó xảy ra:

mkdir 2>/dev/null || true

Điều này sẽ không gây ra nguy hiểm cho cuộc đua khi thực hiện song song - nhưng tôi chưa thử nghiệm nó để chắc chắn.


0

Đơn giản hơn câu trả lời của Lars một chút:

something_needs_directory_xxx : xxx/..

và quy tắc chung:

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

Không có tệp cảm ứng nào để xóa hoặc tạo .PRECIOUS :-)

Nếu bạn muốn xem một thủ thuật gmake nhỏ chung chung khác hoặc nếu bạn quan tâm đến việc tạo không đệ quy với giàn giáo tối thiểu, bạn có thể quan tâm xem thêm Hai thủ thuật gmake rẻ tiền và các bài đăng khác liên quan đến tạo trong blog đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.