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

109

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

Я в основном использую mingw / msys, но хотел бы, чтобы что-то работало и в других оболочках / системах.

Я пробовал это, но это не сработало, есть идеи?

ifeq (,$(findstring $(OBJDIR),$(wildcard $(OBJDIR) )))
-mkdir $(OBJDIR)
endif
KPexEA
источник

Ответы:

106

В UNIX Просто используйте это:

mkdir -p $(OBJDIR)

Параметр -p для mkdir предотвращает появление сообщения об ошибке, если каталог существует.

tchen
источник
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)
skymt
источник
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
wmad
источник
Этот для Windows.
контрольная сумма
/bin/sh: 1: Syntax error: end of file unexpected (expecting "then")
wotanii
Первая версия предназначена для Windows, второй вариант работает в Linux, как сказал @checksum.
Эдгард Лил,
Первая версия, Windows, не является атомарной, поэтому она не работает, когда другой процесс (например, правило в параллельном режиме) создает каталог между «не существует» и «mkdir».
Петр Вепржек
7
ifeq "$(wildcard $(MY_DIRNAME) )" ""
  -mkdir $(MY_DIRNAME)
endif
Майкл Маккарти
источник
Это хорошо работает с make mingw без дополнительных инструментов.
davenpcj 02
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
lygstate
источник
0

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

mkdir 2>/dev/null || true

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

Андрей
источник
0

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

something_needs_directory_xxx : xxx/..

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

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

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

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

Mischa
источник