Создание подкаталогов в Makefile без mkdir -p

В стандарте GNU говорится, что не рекомендуется использовать команду -p для mkdir:

Например, не используйте ‘mkdir -p’, как бы удобно это ни было, потому что некоторые системы вообще не поддерживают его, а в других это небезопасно для параллельного выполнения.

Я хотел бы придерживаться этого стандарта, и поэтому я столкнулся с проблемой в моем Makefile.

Я рекурсивно компилирую все исходники C из всех каталогов в каталоге src и хотел бы поместить их в каталог obj. Та же самая структура каталогов отражается внутри каталога obj, однако эти каталоги изначально не существуют.

Я мог бы очень легко сделать mkdir -p $(@D) для создания своих каталогов, но, следуя стандарту GNU, я не могу этого сделать, что подводит меня к моему вопросу: как я могу обойти это в Makefile?

Мой Makefile ниже:

SRCDIR=src
OBJDIR=obj

CC=cc

CFLAGS=
CFLAGS=-g -O2 -pedantic -Wall
ALL_CFLAGS=-std=c89 $(CFLAGS)

# Copied from https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make/18258352#18258352
rwildcard=$(foreach d, $(wildcard $1/*), $(call rwildcard, $d, $2) $(filter $(subst *, %, $2), $d))

SOURCES=$(call rwildcard, $(SRCDIR), *.c)
OBJECTS=$(subst $(SRCDIR),$(OBJDIR),$(SOURCES:.c=.o))

all: $(OBJECTS)

$(OBJDIR)/%.o: $(SRCDIR)/%.c
    $(CC) $(ALL_CFLAGS) -c -o $@ $<

person Community    schedule 05.10.2019    source источник


Ответы (2)


Стандарты GNU во многих местах устарели. Кажется, это одно из таких мест.

В настоящее время mkdir -p является частью POSIX. Это даже не недавнее изобретение, потому что оно пришло из System V.

Поскольку ваш makefile предполагает, что компилятор C является GCC (или, по крайней мере, имеет синтаксис командной строки, совместимый с GCC), вы также можете предположить, что mkdir -p работает. Если вы хотите, чтобы пользователи могли легко переопределить это, если это не для них, вы можете добавить это:

MKDIR_P = mkdir -p

и используйте $(MKDIR_P) в рецептах вместо mkdir -p.

Но все это действительно не должно быть необходимо. Даже предположительно переносимый скрипт GNU mkinstalldirs использует mkdir -p долгое-долгое время.

person Florian Weimer    schedule 05.10.2019
comment
Я думал, что стандарт актуален (последнее обновление в этом году), но это явно не так. Спасибо за разъяснение. - person ; 05.10.2019
comment
Есть очень старые системы, которые не поддерживают вещи должным образом, которые все еще используются в некоторых пыльных углах. Если вам не нужно быть переносимым на эти старые и малоизвестные системы, вам не нужно о них беспокоиться. В общем, вероятно, не стоит пытаться быть настолько переносимым, если вы также не используете все автоинструменты GNU, такие как autoconf и automake, которые имеют свои собственные средства для работы с этими старыми системами. - person MadScientist; 05.10.2019

Вы можете просто использовать зависимости make. Перетащите файл, .created скажем, в каждую папку. Создайте файлы .created с правилом шаблона, которое естественным образом создает папку в качестве побочного эффекта.

SOURCES=$(call rwildcard, $(SRCDIR), *.c)
OBJECTS=$(subst $(SRCDIR),$(OBJDIR),$(SOURCES:.c=.o))

.PHONY: all
all: $(OBJECTS)

%/.created:
    mkdir -p ${@D}
    touch $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c | ${OBJDIR}/.created
    $(CC) $(ALL_CFLAGS) -c -o $@ $<

Это означает, что папки создаются один раз командой make и только тогда, когда они необходимы. Параллельный сейф тоже.

[Это всего лишь набросок. Ваша замена мне не кажется правильной. Я предполагаю, что у вас нет папки src/ в другой папке src/ (???), и ваш шаблон %.o не совсем правильный.]

person bobbogo    schedule 08.10.2019
comment
На самом деле вам не нужен файл флага. Подойдет и такое правило, как $(OBJDIR)/.. - person raspy; 17.10.2019