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. Может рассматриваться как хорошее введение в тему