make
Материал из Xgu.ru
make — программа, которая используется для автоматизации сборки проекта. Отслеживает взаимосвязь между зависимостями в проекте, и принимает решение о пересборке, на основе связей между зависимостями и состояние зависимостей.
[править] Вопросы и ответы
[править] Я хочу немного разобраться в том, что такое make/gmake/bmake, зачем они нужны и в чём разница между ними
- на вопрос отвечает Алексей Чеусов
>> > А что ты мне порекомендуешь прочитать,
>> > чтобы вообще въехать в проблему как таковую?
>> Да просто посмотри примеры, прикинь, справляется ли инструмент с задачами,
>> которые возникают у тебя лично. Что ты там используешь? autoconf? cmake?
>> голые Makefile-ы?
> Я использую голые Makefil'ы в тех небольших проектах,
> где мне они вообще нужны. А поскольку больше всего,
> что я пишу, я пишу на perl/python, то я с autoconf вообще
> не сталкиваюсь.
Как я уже говорил на LVEE, стрелял бы писателей "самобытных" Makefile-в :-)
Столько проблем от них... В общем, если не использовать никакой
"стандартный" инструмент для сборки и установки, и писать все с нуля самому,
существует очень большой риск нарушить правила написания Makefile-ов,
которые вроде как существуют десятки лет, но которым люди в эпоху всеобщей
линуксоидизации перестают следовать почему-то.
Почему -- не понятно, видимо просто от незнания.
Ну, например, банальные факты. Переменные Makefile-ов принимают значения
по умолчания из окружения. Это раз. Два - Все присваивания переменной
VAR в Makefile-е отменяются при вызове
'make -f Makefile VAR=myvalue'.
Три - нет ничего хуже, чем заставлять пользователя изменять
предоставленный ему Makefile. Должна быть возможность установить все,
что угодно, снаружи.
Не всегда очевидные, но тем не менее, выводы:
- нельзя писать
CFLAGS = -my -local -opts -and -optimization -flags
Необходимо разнести флаги оптимизации и опции, локальные для сборки
проекта, например -I. -Isubdir. Лучше было бы написать
EXTRA_CFLAGS = -O2 -Wall -Werror
CFLAGS += ${EXTRA_CFLAGS} -I. -Isubdir
В этом случае пользователь может легко снести gcc-specific флаги
запустив make all EXTRA_CFLAGS='-O0 -g'
- Желательно, предоставить возможность установить параметры сборки через
окружение, а не через "make target VAR=value" (hint: ?= вместо =)
EXTRA_CFLAGS ?= -O2 -Wall -Werror
CFLAGS += ${EXTRA_CFLAGS} -I. -Isubdir
Сейчас можно запустить
EXTRA_CFLAGS='-O0 -g' make all
- Категорически нежелательно писать
LDFLAGS += -Lreadline-subdir -Lanotherdir
LIBS += -lreadline -lanother-lib
...
Гораздо лучше предоставить возможность указать другие, отличные от
апстримовских, каталоги с библиотеками и опции для их линковки, скажем
LIBDIR_READLINE ?= -Lreadline-subdir
LIB_READLINE ?= -lreadline
LIBDIR_ANOTHER ?= -Lanotherdir
LIB_ANOTHER ?= -lanother-lib
LDFLAGS += ${LIBDIR_READLINE} ${LIBDIR_AMOTHER}
LIBS += ${LIB_READLINE} ${LIB_ANOTHER}
Это полезно, например, для сборки проекта с системными библиотеками
типа readline и всякими iconv, которые пионеры любят совать в дерево
проектов (bash и readline).
- Значения переменным типа LDFLAGS, LIBS, CFLAGS и т.п. лучше вообще никогда
не присваивать значения с помощью =. Всегда лучше использовать +=.
Это дает возможность сборки с пользовательскими настройками по
умолчанию, например на Interix-е можно прописать
export CPPFLAGS=-D_ALL_SOURCE
прямо в профайле пользователя.
- Не придумывайте свои переменные. Используйте "стандартные"
CFLAGS, CPPFLAGS, LDFLAGS, LDADD/LIBS/LDLIBS, COPTS etc.
- Нельзя писать
test: test.c
gcc -o test test.c
Такой способ
a) работает только с gcc и не позволяет его легко заменить на
нужный пользователю компилятор.
b) не допускает указания доп. -I для компилятора
c) не допускает указания опций оптимизации
d) не допускает указания опций включения предупреждений компилятора
и проч.
e) Не позволяет добавить доп. библиотеку/каталог/опции для линковки.
Есть разные "смешные" платформы типа Solaris-а,
на которых стандартные функции
находятся не в libc, а в libsocket. В то же время dlopen(3)
под Линупсом находится в libdl, а в BSD - в libc. И туча других
примеров несовместимостей, например c libcompat из NetBSD
и прочими.
Всегда лучше писать
CC ?= cc
test: test.c
${CC} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $< ${LDADD}
или (POSIX make)
CC ?= cc
.c:
${CC} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $< ${LDADD}
или (GNU make, smake, ...)
CC ?= cc
%: %.c
${CC} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $< ${LDADD}
Ну и так далее. Много выводов можно сделать.
Уже на этом этапе куче писателей нужно было бы поставить единицу, даже
не два балла. Ну а про всякие там "стандартные" DESTDIR, PREFIX и
прочее я уж молчу. Это вообще запредельные знания.
В общем, этим всем призван заниматься automake.
Но уж больно через ... (через кодогенерацию) он это делает...
> Я на Си практически ничего не пишу, за исключением маленьких
> маленьких кусочков, которые мне нужно сделать для
> повышения производительности, и там я их собираю
> через обычный make, без всяких наворотов.
См. выше. Или покажи, я раскритикую :-)
Может, что полезное извлечешь. Поругаться я люблю :-)
> Я понимаю, что вообще надо разобраться с autoconf/autotools
> и вообще сборочным процессом.
Не дури себе голову этим [злом]. Лучше поищи альтернативу.
Нет, не обязательно mk-configure :-) И они есть в наличии.
Хотя адекватную замену libtool найти непросто,
autoconf и automake однозначно в топку.
> Я занимаюсь в фоновом режиме этим, сам видишь, с какой
> периодичностью отвечаю, но у меня ещё тогда летом появилась идея
> (своя погремушка), в которой очень классно было бы использовать
> make, и вот теперь я думаю, что надо с этим разобраться получше.
> Вот первая отправная точка, с которой можно было бы начать:
> http://catb.org/~esr/writings/taoup/html/ch15s04.html#id2987644
Неплохая точка, но autoconf+automake - морально устаревшая давно
прогнившая связка. То же относится и к imake.
makedepend (или mkdep) - это одна из тех вещей, которые обычно делаются
автоматом. По крайней мере в "современных" make-о-заменителях.
в BSD - 'make depend' либо автоматом в зависимости от
использующихся mk-files.
[править] У меня в Makefile несколько раз повторно используется один и тот же очень похожий код. Куда копать?
Для GNU Make копать в сторону eval/call. Например, так [1]:
PROGRAMS = server client
server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol
client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol
# Everything after this is generic
.PHONY: all
all: $(PROGRAMS)
define PROGRAM_template
$(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
ALL_OBJS += $$($(1)_OBJS)
endef
$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
$(PROGRAMS):
$(LINK.o) $^ $(LDLIBS) -o $@
clean:
rm -f $(ALL_OBJS) $(PROGRAMS)
<<GNU make-овская конструкция foreach/eval/call -- настоящее уродство, если ты о примере из 8.8 (-- а я как раз о нём, он выше, -- прим. автора), то в bmake это делается на порядок проще, нагляднее и короче>>(С)Чеусов. Аналогичная функциональность в bmake (как и в других BSD make-ах) реализуется следующим образом.
PROGRAMS = server client
server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol
client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol
# Everything after this is generic
.PHONY: all
all: ${PROGRAMS}
.for p in ${PROGRAMS}
ALL_OBJS += ${${p}_OBJS}
${p}:
$(LD) ${${p}_OBJS} ${${p}_LIBS:S/^/-l/} -o $@
.endfor # p
clean:
rm -f $(ALL_OBJS) $(PROGRAMS)
Хотя на bmake так никто не пишет, для подобных задач есть гораздо более адекватные средства.
[править] Как разделить вывод разных ветвей исполнения при параллельном исполнение (-j) в GNU Make?
опция командной строки "--output-sync" в GNU Make 4.0
[править] Почему make это плохо?
make это не всегда плохо, но у него есть несколько качеств, которые могут сильно испортить жизнь.
Подробнее:
- What’s Wrong With GNU make? (англ.)
[править] Дополнительная информация
- make: Automating Your Recipes(англ.) — глава из книги The Art of Unix Programming, посвящённая make. Может рассматриваться как хорошее введение в тему