как предотвратить ошибку «каталог уже существует» в make-файле при использовании mkdir


109

Мне нужно создать каталог в моем make-файле, и я бы не хотел, чтобы снова и снова появлялась ошибка «каталог уже существует», даже если я легко могу ее игнорировать.

Я в основном использую 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
@Antimony Вы, вероятно, сделали что-то не так, если используете GNU Make вне оболочки MinGW. Но выбор за вами.
yyny

«В UNIX просто используйте это ...» - make -pUnix или Linux?
jww 05

122

Взглянув на официальную документацию make , вот хороший способ сделать это:

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)

Вы должны увидеть здесь использование | оператор pipe, определяющий только предварительное условие заказа. Это означает, что $(OBJDIR)цель должна существовать (а не более поздней ), чтобы создать текущую цель.

Обратите внимание, что я использовал mkdir -p. -pФлаг был добавлен по сравнению с примером Документов. См. Другие ответы для другой альтернативы.


4
Это определенно официальный способ решения этой проблемы.
lcltj

@CraigMcQueen: Я действительно имел в виду, что « $(OBJDIR)цель должна существовать » . Сделайте проверки наличия файла (и отметок времени), чтобы решить, нужно ли создавать цель. Следовательно, здесь, если $(OBJDIR)он отсутствует, он будет строить его, так что он существует , чтобы построить текущую цель $(OBJS), которая будет создана внутри.
ofavre

Я думаю, что буквально пробовал любую другую комбинацию решения этой проблемы, прежде чем найти это (я пытаюсь сгенерировать make-файлы, которые являются настолько общими, насколько это возможно для Windows / Linux) - это почти единственный, с которым я мог работать, но он так хорошо работает! :)
code_fodder

67

Вы можете использовать тестовую команду:

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

7
Это предпочтительный способ, если вам нужно, чтобы ваши make-файлы работали как в Windows (с gnuwin32 и PowerShell), так и в POSIX-совместимых ОС.
Крис Харди

6
ПРЕДОСТАВЛЯЕТСЯ, что у вас есть пакет gnuwin32 CoreUtils для предоставления «тестовой» утилиты.
davenpcj 02

2
Здесь уже поздно, но я хотел бы отметить, что это решение может сломаться при сборке в parallel ( make -j) из-за состояния гонки между проверкой существования каталога и его созданием. Одно задание может проверить, что его нет, но перед его созданием другое задание создает каталог. Затем, когда первая попытается это сделать, это не удастся, потому что каталог уже существует. Лучшее решение в этом случае - использовать предварительные условия только для порядка, как указано в ответе @ TeKa (который должен быть принятым ответом).
C0deH4cker

@MarcusJ Работает на моей OS X El Capitan. Какая версия его сломала?
Франклин Ю

@ C0deH4cker - Простите мое незнание ... Какой ответ TeKa? Я думаю, что TeKa изменил свое имя после того, как вы сделали комментарий.
jww 05

18

Вот трюк, который я использую с GNU make для создания каталогов вывода компилятора. Сначала определите это правило:

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

Затем сделайте все файлы, которые входят в каталог, зависимыми от файла .d в этом каталоге:

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

Обратите внимание на использование $ <вместо $ ^.

Наконец, предотвратите автоматическое удаление файлов .d:

 .PRECIOUS: %/.d

Пропуск файла .d и прямая зависимость от каталога не сработает, поскольку время модификации каталога обновляется каждый раз, когда файл записывается в этот каталог, что приводит к принудительной перестройке при каждом вызове make.


1
Ах, спасибо, я пытался заставить что-то подобное работать. Я не хочу "test -d foo || mkdir foo" для нескольких целей, так как тогда использование "make -j2" будет проверять их одновременно, оба теста дают false, а затем два процесса попытаются выполнить mkdir, последний из них терпит неудачу.
unhammer

13

Если наличие каталога уже не является проблемой для вас, вы можете просто перенаправить stderr для этой команды, избавившись от сообщения об ошибке:

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

Это также имеет то преимущество, что работает в Windows. Не забывайте "-" перед командой, чтобы make не сработала.
Майкл Берр,

11

Внутри вашего make-файла:

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

10

В Windows

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

В Unix | Linux

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

Этот для Windows.
контрольная сумма

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

Первая версия предназначена для Windows, второй вариант работает в Linux, как сказал @checksum.
Эдгард Лил,

Первая версия, Windows, не является атомарной, поэтому она не работает, когда другой процесс (например, правило в параллельном режиме) создает каталог между «не существует» и «mkdir».
Петр Вепржек


6
$(OBJDIR):
    mkdir $@

Что также работает для нескольких каталогов, например.

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

$(OBJDIRS):
    mkdir $@

Добавление $(OBJDIR)в качестве первой цели работает хорошо.


3

Работает под mingw32 / msys / cygwin / linux

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

0

Если вы явно проигнорируете код возврата и сбросите поток ошибок, ваша программа make проигнорирует ошибку, если она возникнет:

mkdir 2>/dev/null || true

Это не должно вызывать опасности гонки в параллельной модели - но я не проверял это, чтобы быть уверенным.


0

Немного проще, чем ответ Ларса:

something_needs_directory_xxx : xxx/..

и общее правило:

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

Нет touch-файлов, которые нужно чистить или делать .PRECIOUS :-)

Если вы хотите увидеть еще один общий трюк с gmake или если вас интересует нерекурсивный make с минимальным набором шаблонов , вы можете попробовать еще два дешевых трюка с gmake и другие сообщения, связанные с make, в этом блоге.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.