як запобігти "директорії вже існує помилка" в makefile при використанні mkdir


109

Мені потрібно генерувати каталог у своєму makefile, і я хотів би не отримувати "каталог вже існує помилка" знову і знову, хоча я легко ігнорую її.

Я в основному використовую mingw / msys, але хотів би те, що працює і в інших оболонках / системах.

Я спробував це, але нічого не вийшло, ідеї?

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

Відповіді:


106

У UNIX просто використовуйте це:

mkdir -p $(OBJDIR)

Опція -p mkdir запобігає повідомленню про помилку, якщо каталог існує.


39
"Як правило, дотримуйтесь широко підтримуваних параметрів та функцій цих програм, які зазвичай підтримуються. Як правило, не використовуйте '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
@Antimin Ви, мабуть, зробили щось не так, якщо ви використовуєте GNU Make поза оболонкою MinGW. Хоча ваш вибір.
yyny

"У 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

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

67

Ви можете скористатися командою тесту:

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

7
Це кращий спосіб, якщо вам потрібні файли для роботи в ОС Windows (з gnuwin32 та PowerShell) та ОС, сумісних з POSIX.
Кріс Харді

6
ЗАБЕЗПЕЧЕНО, у вас є пакет gnuwin32 CoreUtils для надання утиліти 'test'.
davenpcj

2
До цього часу пізно, але я хотів би зазначити, що це рішення може зламатися при побудові паралельно ( make -j) через перегоновий стан між тим, коли тестується каталог на існування, і він створюється. Одне завдання може перевірити, що його там немає, але перш ніж його створити, інше - створить каталог. Тоді, коли перший спробує зробити це, він вийде з ладу, оскільки каталог вже існує. Найкращим рішенням у цьому випадку є використання лише необхідних умов для замовлення, як зазначено у відповіді @ TeKa (яка повинна бути прийнятою відповіддю).
C0deH4cker

@MarcusJ Працює над моєю ОС X El Capitan. Яка версія зламала його?
Франклін Ю

@ 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. Не забувайте "-" перед командою, тому змушуйте не вносити заставу.
Майкл Берр

11

Всередині вашого файлу:

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
Edgard Leal

По-перше, Windows, версія не є атомною, тому вона не працює, коли інший процес (наприклад, правило в паралельному режимі) створює каталог між "не існує" і "mkdir".
Петро Вепрєк

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

Це добре працює для виготовлення mingw, не вимагаючи додаткових інструментів.
davenpcj

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

Якщо ви явно проігноруєте код повернення і скидаєте потік помилок, ваша марка буде ігнорувати помилку, якщо вона виникає:

mkdir 2>/dev/null || true

Це не повинно викликати небезпеку для гонки паралельно, але я не перевіряв її.


0

Трохи простіше, ніж відповідь Ларса:

something_needs_directory_xxx : xxx/..

і загальне правило:

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

Немає сенсорних файлів для очищення чи створення. ПРЕЦІАЛЬНИЙ :-)

Якщо ви хочете побачити ще один невеликий загальний трюк gmake або якщо ви зацікавлені в нерекурсивному виконанні з мінімальними риштуваннями, ви можете поцікавитись ще двома дешевими трюками gmake та іншими публікаціями, пов’язаними з виготовленням у цьому блозі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.