Просто о make

Меня всегда привлекал минимализм. Идея о том, что одна вещь должна выполнять одну функцию, но при этом выполнять ее как можно лучше, вылилась в создание UNIX. И хотя UNIX давно уже нельзя назвать простой системой, да и минимализм в ней узреть не так то просто, ее можно считать наглядным примером количество- качественной трансформации множества простых и понятных вещей в одну весьма непростую и не прозрачную. В своем развитии make прошел примерно такой же путь: простота и ясность, с ростом масштабов, превратилась в жуткого монстра (вспомните свои ощущения, когда впервые открыли мэйкфайл).

Мое упорное игнорирование make в течении долгого времени, было обусловлено удобством используемых IDE, и нежеланием разбираться в этом 'пережитке прошлого' (по сути — ленью). Однако, все эти надоедливые кнопочки, менюшки ит.п. атрибуты всевозможных студий, заставили меня искать альтернативу тому методу работы, который я практиковал до сих пор. Нет, я не стал гуру make, но полученных мною знаний вполне достаточно для моих небольших проектов. Данная статья предназначена для тех, кто так же как и я еще совсем недавно, желают вырваться из уютного оконного рабства в аскетичный, но свободный мир шелла.

Make- основные сведения


make — утилита предназначенная для автоматизации преобразования файлов из одной формы в другую. Правила преобразования задаются в скрипте с именем Makefile, который должен находиться в корне рабочей директории проекта. Сам скрипт состоит из набора правил, которые в свою очередь описываются:

1) целями (то, что данное правило делает);
2) реквизитами (то, что необходимо для выполнения правила и получения целей);
3) командами (выполняющими данные преобразования).

В общем виде синтаксис makefile можно представить так:

# Индентация осуществляется исключительно при помощи символов табуляции,
# каждой команде должен предшествовать отступ
<цели>: <реквизиты>
	<команда #1>
	...
	<команда #n>



То есть, правило make это ответы на три вопроса:

			{Из чего делаем? (реквизиты)} ---> [Как делаем? (команды)] ---> {Что делаем? (цели)}

Несложно заметить что процессы трансляции и компиляции очень красиво ложатся на эту схему:

							  {исходные файлы} ---> [трансляция] ---> {объектные файлы}

							  {объектные файлы} ---> [линковка] ---> {исполнимые файлы}


Простейший Makefile


Предположим, у нас имеется программа, состоящая всего из одного файла:

/*
 * main.c
 */
#include <stdio.h>
int main()
{
	printf("Hello World!\n");
	return 0;
}

Для его компиляции достаточно очень простого мэйкфайла:

hello: main.c
	gcc -o hello main.c

Данный Makefile состоит из одного правила, которое в свою очередь состоит из цели — «hello», реквизита — «main.c», и команды — «gcc -o hello main.c». Теперь, для компиляции достаточно дать команду make в рабочем каталоге. По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:

	$ make <цель>


Компиляция из множества исходников


Предположим, что у нас имеется программа, состоящая из 2 файлов:
main.c
/*
 * main.c
 */
int main()
{
	hello();
	return 0;
}

и hello.c
/*
 * hello.c
 */
#include <stdio.h>
void hello()
{
	printf("Hello World!\n");
}

Makefile, выполняющий компиляцию этой программы может выглядеть так:

hello: main.c hello.c
        gcc -o hello main.c hello.c

Он вполне работоспособен, однако имеет один значительный недостаток: какой — раскроем далее.

Инкрементная компиляция


Представим, что наша программа состоит из десятка- другого исходных файлов. Мы вносим изменения в один из них, и хотим ее пересобрать. Использование подхода описанного в предыдущем примере приведет к тому, что все без исключения исходные файлы будут снова скомпилированы, что негативно скажется на времени перекомпиляции. Решение — разделить компиляцию на два этапа: этап трансляции и этап линковки.

Теперь, после изменения одного из исходных файлов, достаточно произвести его трансляцию и линковку всех объектных файлов. При этом мы пропускаем этап трансляции не затронутых изменениями реквизитов, что сокращает время компиляции в целом. Такой подход называется инкрементной компиляцией. Для ее поддержки make сопоставляет время изменения целей и их реквизитов (используя данные файловой системы), благодаря чему самостоятельно решает какие правила следует выполнить, а какие можно просто проигнорировать:

main.o: main.c
        gcc -c -o main.o main.c
hello.o: hello.c
        gcc -c -o hello.o hello.c
hello: main.o hello.o
        gcc -o hello main.o hello.o

Попробуйте собрать этот проект. Для его сборки необходимо явно указать цель, т.е. дать команду make hello.
После- измените любой из исходных файлов и соберите его снова. Обратите внимание на то, что во время второй компиляции, транслироваться будет только измененный файл.

После запуска make попытается сразу получить цель hello, но для ее создания необходимы файлы main.o и hello.o, которых пока еще нет. Поэтому выполнение правила будет отложено и make станет искать правила, описывающие получение недостающих реквизитов. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. Отсюда следует, что make выполняет правила рекурсивно.

Фиктивные цели


На самом деле, в качестве make целей могут выступать не только реальные файлы. Все, кому приходилось собирать программы из исходных кодов должны быть знакомы с двумя стандартными в мире UNIX командами:

	$ make
	$ make install

Командой make производят компиляцию программы, командой make install — установку. Такой подход весьма удобен, поскольку все необходимое для сборки и развертывания приложения в целевой системе включено в один файл (забудем на время о скрипте configure). Обратите внимание на то, что в первом случае мы не указываем цель, а во втором целью является вовсе не создание файла install, а процесс установки приложения в систему. Проделывать такие фокусы нам позволяют так называемые фиктивные (phony) цели. Вот краткий список стандартных целей:

  • all — является стандартной целью по умолчанию. При вызове make ее можно явно не указывать.
  • clean — очистить каталог от всех файлов полученных в результате компиляции.
  • install — произвести инсталляцию
  • uninstall — и деинсталляцию соответственно.

Для того чтобы make не искал файлы с такими именами, их следует определить в Makefile, при помощи директивы .PHONY. Далее показан пример Makefile с целями all, clean, install и uninstall:

.PHONY: all clean install uninstall
	
all: hello
	
clean:
			rm -rf hello *.o
main.o: main.c
			gcc -c -o main.o main.c
hello.o: hello.c
			gcc -c -o hello.o hello.c
hello: main.o hello.o
			gcc -o hello main.o hello.o
install:
			install ./hello /usr/local/bin
uninstall:
			rm -rf /usr/local/bin/hello

Теперь мы можем собрать нашу программу, произвести ее инсталлцию/деинсталляцию, а так же очистить рабочий каталог, используя для этого стандартные make цели.

Обратите внимание на то, что в цели all не указаны команды; все что ей нужно — получить реквизит hello. Зная о рекурсивной природе make, не сложно предположить как будет работать этот скрипт. Так же следует обратить особое внимание на то, что если файл hello уже имеется (остался после предыдущей компиляции) и его реквизиты не были изменены, то команда make ничего не станет пересобирать. Это классические грабли make. Так например, изменив заголовочный файл, случайно не включенный в список реквизитов, можно получить долгие часы головной боли. Поэтому, чтобы гарантированно полностью пересобрать проект, нужно предварительно очистить рабочий каталог:

	$ make clean
	$ make

Для выполнения целей install/uninstall вам потребуются использовать sudo.

Переменные


Все те, кто знакомы с правилом DRY (Don't repeat yourself), наверняка уже заметили неладное, а именно — наш Makefile содержит большое число повторяющихся фрагментов, что может привести к путанице при последующих попытках его расширить или изменить. В императивных языках для этих целей у нас имеются переменные и константы; make тоже располагает подобными средствами. Переменные в make представляют собой именованные строки и определяются очень просто:

	<VAR_NAME> = <value string>

Существует негласное правило, согласно которому следует именовать переменные в верхнем регистре, например:

	SRC = main.c hello.c

Так мы определили список исходных файлов. Для использования значения переменной ее следует разименовать при помощи конструкции $(<VAR_NAME>); например так:

	gcc -o hello $(SRC)

Ниже представлен мэйкфайл, использующий две переменные: TARGET — для определения имени целевой программы и PREFIX — для определения пути установки программы в систему.

TARGET = hello
PREFIX = /usr/local/bin

.PHONY: all clean install uninstall

all: $(TARGET)
	
clean:
			rm -rf $(TARGET) *.o
main.o: main.c
			gcc -c -o main.o main.c
hello.o: hello.c
			gcc -c -o hello.o hello.c
$(TARGET): main.o hello.o
			gcc -o $(TARGET) main.o hello.o
install:
			install $(TARGET) $(PREFIX)
uninstall:
			rm -rf $(PREFIX)/$(TARGET)

Это уже посимпатичней. Думаю, теперь вышеприведенный пример для вас в особых комментариях не нуждается.

Автоматические переменные


Автоматические переменные предназначены для упрощения мейкфайлов, но на мой взгляд негативно сказываются на их читабельности. Как бы то ни было, я приведу здесь несколько наиболее часто используемых переменных, а что с ними делать (и делать ли вообще) решать вам:

  • $@ Имя цели обрабатываемого правила
  • $< Имя первой зависимости обрабатываемого правила
  • $^ Список всех зависимостей обрабатываемого правила

Если кто либо хочет произвести полную обфускацию своих скриптов — черпать вдохновение можете здесь:
Автоматические переменные

Заключение


В этой статье я попытался подробно объяснить основы написания и работы мэйкфайлов. Надеюсь, что она поможет вам приобрести понимание сути make и в кратчайшие сроки освоить этот провереный временем инструмент.



Все примеры на GitHub

Тем, кто вошел во вкус:
Makefile mini HOWTO на OpenNET
GNU Make Richard M. Stallman и Roland McGrath, перевод Владимир Игнатов, 2000
Эффективное использование GNU Make
Поделиться публикацией

Похожие публикации

Комментарии 107
    +16
    Я бы все же добавил, что сила make в универсальности. И последний пример у меня выглядел бы примерно так:

    TARGET = hello
    PREFIX = /usr/local/bin
    SRCS = main.c hello.c
    OBJS = $(SRCS:.c=.o)
    
    .PHONY: all clean install uninstall
    
    all: $(TARGET)
    $(TARGET): $(OBJS)
                $(CC) -o $(TARGET) $(OBJS) $(CFLAGS)
     
    .c.o:
                $(CC) $(CFLAGS)  -c $< -o $@
    
    clean:
                rm -rf $(TARGET) $(OBJS)
    install:
                install $(TARGET) $(PREFIX)
    uninstall:
                rm -rf $(PREFIX)/$(TARGET)
    
      +6
      А расскажите, пожалуйста, что делает следующий код? Где определены $(CC), $(CFLAGS), что такое %< и $@? Что делает и в каком месте вызывается таргет .c.o?
      .c.o:
                  $(CC) $(CFLAGS)  -c $< -o $@
      

      Заранее спасибо.
        +12
        Советую поглядеть вывод make -p, там очень много полезного. Переменная CC по умолчанию содержит имя C компилятора (т.е. на Linux это gcc, также есть и CXX и другие). CFLAGS по умолчанию пустой, но используется вот так:

        $ CFLAGS=-g make
        $ CFLAGS=-O2 make
        


        Т.е. переменными окружения выбираем, что собирать — релизную или отладочную версию.

        Шаблон .c.o указывает, каким образом из .c файлов получить .o файлы.

        %< и $@ — это переменные, значение которых содержит имя цели или зависимости.

        PS: где-то у меня был более универсальный Makefile с генерацией зависимостей и поиском исходников. Если найду, могу показать.
          +2
          Года два назад проникся духом Make'а и решил запилить сборку проекта с юнит тестированием на нём (и генерацией зависимостей, есс-но).
          По сути, Make — язык со своей парадигмой, на основе зависимостей, можно горы свернуть. Но в процессе написания чего-то сложного постоянно натыкаешься на нереализованные мелочи, отчего получаются хаки, вкрапления sed'а и прочие не очень понятные вещи.

          pastebin.com/BVvCpWJV
          pastebin.com/9FakyqTk

          Избранные простые моменты:

          SOMETHING_TEST_ROOT := $(dir $(lastword $(MAKEFILE_LIST)))


          empty = 
          space = $(empty) $(empty)
            +1
            Как же так, CC — это C Compiler, а для C++ Compiler есть свои собственные CXX и CXXFLAGS. Нередко нереализованные мелочи — недопрочтённые мануалы:)

            Что-то не помню я, чтобы приходилось для deps шаманства sed прикручивать, но к сожалению не могу найти исходник.

            Для тестов есть make test и make check, что прекрасно поддерживается дистрибутивами.
              +1
              Недопрочитанные мануалы, говорите? А ларчик просто открывался, да…

              PS: тоже в процессе написания относительно универсального common.mk для собственного пользования. :)
            0
            В переменных окружения имеет смысл задать значения флагов по умолчанию, а для конкретного вызова make значения переменных можно передавать как параметр, это смотрится более аккуратно, что ли.

            make CFLAGS=-g
            make CFLAGS=-O2
            
              0
              Я точно не ручаюсь сейчас, но вроде бы make CFLAGS=-O2 и CFLAGS=-O2 make — будет по-разному интерпретироваться. Т.е. в первом случае CFLAGS будет принудительно перезаписан, вне зависимости от того что прописано в самом makefile, а во втором — только если разработчик разрешил.
          +1
          Есть ещё неплохая свободная книга Managing Projects with GNU Make. Там описано довольно много интересных рецептов для сборки проектов на C, C++, Java, отладки make-файлов и просто хаков.
          0
          .c.o, по-моему, не обязательно — можно обойтись .o и встроенными неявными правилами сборки .c. Ну и замена у вас по суффиксу (.c -> .o), .c.o: не будет работать.
            0
            Ответ не на тот комментарий
              +1
              Очень дельное замечание, но я бы добавил ещё два улучшения:

              • Удаление bin из PREFIX, что возвращает ему стандартный смысл (не путь к директории с исполняемыми файлами, а префикс дерева директорий с определённой структурой, содержащего в том числе и поддиректорию bin)
              • Использование ?= для определения PREFIX, что позволит менять его через окружение

              Итого:

              TARGET = hello
              PREFIX ?= /usr/local
              SRCS = main.c hello.c
              OBJS = $(SRCS:.c=.o)
              
              .PHONY: all clean install uninstall
              
              all: $(TARGET)
              $(TARGET): $(OBJS)
                          $(CC) -o $(TARGET) $(OBJS) $(CFLAGS)
               
              .c.o:
                          $(CC) $(CFLAGS)  -c $< -o $@
              
              clean:
                          rm -rf $(TARGET) $(OBJS)
              install:
                          install $(TARGET) $(PREFIX)/bin
              uninstall:
                          rm -rf $(PREFIX)/bin/$(TARGET)
              

              Это нужно чтобы проект мог правильно и не требуя лишних телодвижений (как-то изменение Makefile или явное указание аргументов make) собраться и установиться у человека с настроенными в окружении компилятором, флагами и путями для установки (например, CC=clang CFLAGS=-g -Wall PREFIX=$HOME/custom_apps). То же самое нужно для сборки проекта через source-based пакетные системы, например порты FreeBSD (а скорее всего верно и для portage/aur/slackbuilds/brew/...).
              +7
              make — это конечно хорошо… было на каком-то этапе эволюции. Советую обратить внимание на cmake — универсальность2.
                +5
                Для сборки программ — конечно следует выбрать что-то вроде cmake/qmake/scons и т.д. Однако make создан не только для компиляции, это просто удобная программа, умеющая отслеживать зависимости. Например вы хотите сайт на markdown писать, make в помощь:

                SRCS = index.md page1.md page2.md
                HTMLS = $(SRCS:.md=.html)
                
                .PHONY: all clean install uninstall
                
                all: $(HTMLS)
                
                %.html: %.md
                        markdown < $^ > $@
                


                Ну и любые подобные задачи. Так что make не перестает быть удобным и полезным инструментом. Хотя да, использую я его все реже и реже… :-)
                  0
                  Я с помощью make набранные в лилипонде ноты компилирую, смотрю, слушаю и архивирую:
                  SRC = a4.ly tenor1.ly tenor2.ly bass1.ly bass2.ly lyrics.ly header.ly ../lib/paper.ly
                  RESULT = a4.pdf all.midi
                  DIRNAME = $(realpath .)
                  
                  .PHONY: preview publish archive dist play view help
                  preview pre view $(RESULT): $(SRC)
                  	lilypond a4.ly
                  	mv -f a4.midi all.midi
                  	see a4.pdf &
                  publish pub a4.publish.pdf: $(SRC)
                  	lilypond -dno-point-and-click -o a4.publish a4.ly
                  	mv -f a4.publish.midi all.midi
                  dist: $(SRC) a4.publish.pdf
                  	7z a -tzip -mx=9 $(DIRNAME).zip $(SRC) a4.publish.pdf all.midi Makefile
                  clean:
                  	rm -f *~ $(RESULT) a4.publish.pdf a4.ps a4.publish.ps
                  play: all.midi
                  	timidity all.midi
                  help:
                  	@echo Available goals:
                  	@echo '  preview - create and show PDF preview with debug info, make MIDI file'
                  	@echo '  publish - create final PDF file without debug info, make MIDI file'
                  	@echo '  play    - play MIDI file'
                  	@echo '  dist    - create archive with sources and result'
                  	@echo '  clean   - delete result and backup files'
                  	@echo '  help    - show this message'
                  
                  +7
                  Cmake — это не замена make. Cmake — это замена autotools.
                    0
                    Даже не столько замена.
                    Autotools создают configure. Который создаёт Makefile. На целевой системе созданный configure самодостаточен (т.е. чтобы сконфигурировать сборку при наличии configure, никаких автотулзов уже не нужно).
                    А вот cmake сразу создаёт Makefile. И в нём уже ничего без cmake не подтюнишь.
                    0
                    Надо бы знать что CMake занимается ничем иным как генерацией Makefile'ов, поэтому надобность в make не пропала особенно при использовании CMake. Кроме того, для простых проектов (несколько исходных файлов, без внешних зависимостей — как в примере из статьи) CMake, пожалуй, будет overkill'ом и make останется лучшим выбором.
                      0
                      Начнем с того, что cmake занимается генерацией build-систем, не только для make, но для VS c XCode. Соответственно, надобность писать под какую-то конкретную build-систему исчезает, абстрактное описание build-процесса эффективнее конкретных реализаций.

                      Написание cmake скриптов легче, сами скрипты проще для понимания (естественно есть какой-то порог вхождения для любой технологии).

                      Ну и по поводу простых/сложных проектов проектов.

                      Для простого проекта в статье 19 строк makefile можно заменить 3-мя строками cmake:
                      cmake_minimum_required(VERSION 2.8)
                      set(TARGET "hello")
                      add_executable(${TARGET} hello.c main.c)
                      


                      Для сложных проектов одно добавление внешних зависимостей 1 командой чего стоит!
                        0
                        Спасибо, я прекрасно осведомлён о преимуществах CMake.

                        Но для
                        binary: binary.c
                            ${CC} ${CFLAGS} binary.c -o binary
                        

                        CMake на самом деле нафиг не сдался.
                          +1
                          Все всякого сомнения не рационально использовать cmake в случаях, когда достаточно одной шелл команды:
                          gcc -O2 binary.c -o binary
                            –1
                            Не надо передёргивать.
                    +4
                    Действительно просто, но не настраиваемо. Компилятор одной строкой не сменить, для добавления одного файла исходника нужно добавить две строки и одну изменить, подключение библиотек совсем не предусматривается.

                    Не упомянуты стандартные цели сборки, с ними всё может быть проще. К примеру:
                    Для его компиляции достаточно очень простого мэйкфайла:

                    hello: main.c
                        gcc -o hello main.c
                    


                    Если не так уж критично имя бинарника hello, можно сделать ещё более простой Makefile:

                    all: main
                    


                    Рекомендую статью Эффективное использование GNU Make. И, конечно же, не останавливаться на простейшем. Читать документацию, смотреть на альтернативы — возможно вам CMake больше подойдёт.
                      0
                      Ах да, есть ещё один неплохой источник — Learn C The Hard Way. Во многом предполагает наличие самостоятельного чтения документации, по Make там два или три урока всего, но вещь полезная.
                        0
                        Полностью согласен.
                        Несколько основных целей я все же упомянул.
                        Вообще я задумывал статью для тех кто никак не соберется с силами начать освоение. В рунете инфы пруд- пруди.
                        Моей целью было как можно более просто приподнести базовые идеи. Главное начать.
                          0
                          Чтобы у тех, кто никак не соберётся силами, эти силы появились, думаю стоит в конце статьи выделить пару хороших ссылок из этого «пруд-пруди».
                            +1
                            Сделано )
                            –1
                            Где же вы были, автор, часов в десять по москве сегодня?! На работе было спокойно и я с утра думал, о чем бы почитать и какой бы инструмент изучить (или продолжить изучать). Начал с xmobar и закончил cmus, и даже в голову не пришло, что я в свое время недоучил make! Спасибо за статью, сейчас буду есть макарошки и чтить Столмана )
                              0
                              Спасибо :) Приятного прочтения!
                            +2
                            Посмотрите на комментарий выше. Вот там хороший пример толкового Makefile, в котором новый исходник добавляется одной строкой.

                            С библиотеками немного более хитро, но сила в минимализме — построить можно что угодно.

                            Компилятор меняется одной строкой, например, так:

                            CC=clang make
                            0
                            У make есть один большой недостаток — непригодность для отладки мейкфайлов. И если в файле из 10 строк можно всё отдебажить методом пристального взгляда, то в мейкфайлы в больших и старых проектах содержат столько всего… кхм… удивительного, что бедные средства make оказываются вообще бесполезными. Единственное решение, которое я для себя нашёл — remake.
                              0
                              Тогда уж лучше сразу automake или cmake.
                                +1
                                Вы не поняли. remake — это тот же самый мейк (сам мейкфал менять не надо), просто к нему прикрутили полезные дебажные возможности.

                                В проектах на несколько миллионов строк и возрастом под 20 лет нет возможности переехать на cmake, а дебажить как-то надо.

                                Хотя, если честно, в cmake кроме кроссплатформенности не увидел особых плюшек. Хотя может смотрел не достаточно хорошо.
                                  0
                                  Ну чтобы не писать миллионы строк в make народ и придумал automake. Для разных систем компилить без чего-то автоматизированного это просто не реально. Даже ключи у компиляторов разные, не говоря уже о путях библиотек.
                                    0
                                    Для этого есть всякие разные библиотеки для мейка.
                                  0
                                  Тогда лучше уж сразу configure.py
                                  0
                                  make --trace помог выудить крайне неуловимый баг в системе сборки (дело оказалось не в самом Make, а в среде). Для сложных проектов с мегатоннами унаследованного кода конечно он не подойдёт, но он серьёзно выручает для генерации всяких graphviz, tex и т. п., для которых не всегда IDE-то существует, а автоматизировать процесс надо. Даже собственный парсер так удобнее запускать.

                                  cmake и autotools преимущественно решают проблему унаследованного ПО (зависимости и совместимость), проект, где из сторонних библиотек только STL, собирать командой make одно удовольствие.
                                    0
                                    Что такое make --trace? У мейка как раз нет --trace, он есть в remake, он этим и ценен.
                                      0
                                      А как cmake помагает с унаследованным ПО? Я не представляю как легко и просто перевести монструозный проект с огромным количеством включаемых друг в друга под разными условиями и разных последовательностях мейкфалов на cmake.

                                      Кстати об STL. Есть libstdc++, есть libc++, они могут собираться с разными abi и иметь разные версии и разную степерь поддержки С++11. А ещё на макоси с 10.9 хидера STL теперь лежат не в системе, а в XCode, что добавляет приятных моментов. Так что даже с одним STL при необходимости написать хорошо портируемый кросс-платформенный мейк возникает куча внезапных нюансов, будь они не ладны.
                                        +1
                                        > Что такое make --trace
                                        Возможно, потому что у меня Linux и немного другой Mac Make?:) В GNU Make --trace есть и здорово помогает отладочными сообщениями в случае чего.

                                        > Я не представляю как легко и просто перевести монструозный проект
                                        Никак. Если сразу выбрали cmake — хорошо. Но в мире OpenSource не принято таскать за собой thirdparty, а как раз разруливать зависимости, уже имеющиеся в операционной системе, с чем cmake/autotools и справляется. Из крупных проектов, которые тащат за собой груз из унаследованного ПО: Qt, Chromium. При этом у них ещё и собственный велосипед — своя система сборки.

                                        > они могут собираться с разными abi
                                        Эти нюансы тоже должны прятаться внутри cmake/autotools. Перед сборкой проекта они проверяют возможности компиляторов, наличие библиотек и их возможности и в соответствии с этим генерируют Makefile.
                                          0
                                          GNU Make 3.82 из Fedora 18. Самый что ни на есть обычный мейк.

                                          Подозреваю, что у вас remake вместо make подложен (и это, кстати, хорошо!). Вот что говорит о себе remake:
                                          > remake --version
                                          GNU Make 3.82+dbg0.9
                                            0
                                            На счёт autoconf — это тоже ещё те грабли. То есть это решает многие проблемы, но далеко не панацея. Плюс, на винде он бесполезен практически (при условии, что проект собирается в некотором шеле — или cygwin, или самописном/самопортированном). Сейчас ответ более или менее понятен — cmake, но лет 15-20-25 назад было всё иначе, а проекты бывают по стольку живут и здравсвуют.

                                            Кстати, в LLVM (типичный большой проект, работающий на всём зоопарке архитектур и ОС) есть и autoconf, и cmake. Собираются постепенно отказаться от autoconf. Но оба решения несколько недопилены (ибо там чёрт ногу сломит местами).
                                        0
                                        make -dp --warn-undefined-variables | less

                                        Также, в GMSL есть точки останова, срабатывающие в рецептах целей.
                                          0
                                          На практике этого мало. Особенно когда хочется понять где переменная получила значение и распечатать то что реально исполнялось (-n не помогает, так как логика мейкфайла тогда меняется).
                                            0
                                            Тут уже вступают в силу обычные рекомендации относительно программирования: по возможности не переиспользуйте переменные.

                                            Насчёт распечатки не понял — Make по умолчанию печататает всё, что делает.
                                              0
                                              Если проект небольшой и свой, проблемы нет. Если проект большой или очень большой, да ещё и старый, там возникают нюансы. Во-первых, рекомендация про хороший стиль перестают работать. Во-вторых, там объективная необходимость в переменных, ибо кроссплатформенность и различные настройки и прочее. В-третьих, в большом проекте может быть много подпроектов, в которых свои мейкфайлы, которые включаются в главный. В-четвёртых, в большом проекте вся выдача подавлена — ибо по дефолту выдача нужна только если какие-то ворнинги/ошибки (и это правильно), поэтому отсутствие ключика --всё-равно-всё-печатать-и-не-волнует расстраивает при необходимости копнуть глубже.
                                                0
                                                Во-первых, рекомендация про хороший стиль перестают работать.


                                                Очень интересно. Чтобы рекомендации не переставали работать, нужно применять code review.

                                                Во-вторых, там объективная необходимость в переменных, ибо кроссплатформенность и различные настройки и прочее.


                                                Я не предложил отказаться от переменных. Я говорю, что одна переменная — одно определение. Тогда нет необходимости понимать, где же ей было присвоено значение.

                                                В-третьих, в большом проекте может быть много подпроектов, в которых свои мейкфайлы, которые включаются в главный.


                                                You're doing it wrong. В частности, по причине того, что становится сложно отследить определение переменных. Советую почитать эту хорошую статью. Ну и эту, до кучи.

                                                В-четвёртых, в большом проекте вся выдача подавлена — ибо по дефолту выдача нужна только если какие-то ворнинги/ошибки (и это правильно), поэтому отсутствие ключика --всё-равно-всё-печатать-и-не-волнует расстраивает при необходимости копнуть глубже.

                                                Ключик реализуется своими силами элементарно:
                                                define RUN
                                                ifeq ($(VERBOSE),y)
                                                  echo $(2);
                                                  $(2);
                                                else
                                                  echo $(1);
                                                  $(2);
                                                endif
                                                
                                                $(TARGET):
                                                  $(call RUN, "CC $@", $(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS))
                                                
                                                  0
                                                  Прошу не принимать это как оскорбление, но вы идеалист-теоретик :)

                                                  Смысла в переменных, когда у них одно определение, нет никакого, ибо это не переменные, а константы. Например, при сборке на определённой ОС, определённой версии с определённой библиотекой, добавлять к ключам компиляции такой-то флажок, а с другой библиотекой, другой флажёк и так стопицот раз. Предлагаете каждый флажёк в отдельную переменную пихать, а потом всё слить воедино? А как же тогда стиль? Там не на один экран таких переменных набежит плотным текстом. Поэтому «CFLAGS +=», а вот откуда что пришло уже не так понятно.

                                                  Я, кстати, не про рекурсивный мейк говорил, а про включение в главный мейкфайл (include'ом). И это хорошая практика.

                                                  Про вербоз своими силами — в вашем случае и --dry-run отлично подойдёт. В реальности там будет ещё несколько генераторов генераторов, запуск этих генераторов, несколько кастомных правил, которые делают разные манипуляуции с выдачей генераторов. Генерация и обратобка зависимостей опять же. И разные файлы с разными ключами собираются, конечно же. Ну, то есть всё под одну гребёнку крайне сложно.

                                                  И, заметьте, это всё при нормальном код ревью и поставленных процессах. Ибо без них вообще адъ, мрак и средневековье. Я по долгу службы на всякое насмотрелся. Действительно большие проекты всегда живут своей жизнью.

                                                  Поэтому вместо изобретения велосипедов проще взять remake.
                                                    +1
                                                    Прошу не принимать это как оскорбление, но вы идеалист-теоретик :)

                                                    Это вы — любитель грязных решений. Я переписал сборку проекта из миллиона строк и тоже знаю, о чём речь.
                                                    Смысла в переменных, когда у них одно определение, нет никакого, ибо это не переменные, а константы. Например, при сборке на определённой ОС, определённой версии с определённой библиотекой, добавлять к ключам компиляции такой-то флажок, а с другой библиотекой, другой флажёк и так стопицот раз. Предлагаете каждый флажёк в отдельную переменную пихать, а потом всё слить воедино? А как же тогда стиль? Там не на один экран таких переменных набежит плотным текстом. Поэтому «CFLAGS +=», а вот откуда что пришло уже не так понятно.

                                                    Функциональные языки программирования (и, в частности, Haskell) передают привет.

                                                    Иммутабельность решает, особенно когда нет областей видимости переменных.

                                                    Да, идеальной ситуации «одно определение» не получится всё равно. Но так проще, поэтому надо стремиться к этому.
                                                    Про вербоз своими силами — в вашем случае и --dry-run отлично подойдёт. В реальности там будет ещё несколько генераторов генераторов, запуск этих генераторов, несколько кастомных правил, которые делают разные манипуляуции с выдачей генераторов. Генерация и обратобка зависимостей опять же. И разные файлы с разными ключами собираются, конечно же. Ну, то есть всё под одну гребёнку крайне сложно.

                                                    --dry-run не подойдёт, потому что он меняет логику. И мы же говорим о журналировании, а не отдельном специальном запуске make. Наличие генераторов генераторов генераторов просто не лучшим образом говорит об архитектуре системы сборки.
                                                    И, заметьте, это всё при нормальном код ревью и поставленных процессах. Ибо без них вообще адъ, мрак и средневековье. Я по долгу службы на всякое насмотрелся. Действительно большие проекты всегда живут своей жизнью.

                                                    Ну я не спорю, дерьма всегда можно понаписать. Особенно в больших проектах. С Make сделать это несложно.
                                                    Поэтому вместо изобретения велосипедов проще взять remake.

                                                    Вывод не следует из предпосылок, нет?

                                                    В remake всё, что добавлено — отладчик. Это, конечно, позволит попроще жить в уже существующей дерьмовой инфраструктуре, но первопричина в том, что инфраструктура дерьмовая, а не в том, что отлаживаться тяжело. Писать надо так, чтобы отлаживаться не приходилось.

                                                    Да, если легаси и всё уже плохо, может и remake поможет. Но вообще это полумера и костыль.
                                                      0
                                                      Это вы — любитель грязных решений. Я переписал сборку проекта из миллиона строк и тоже знаю, о чём речь.

                                                      Совсем нет, но переписывание ради переписывания обычно смысла не имеет. Особенно когда трудозатраты на переписывание заметные. Причём в большом проекте всегда есть некоторое количество ошибок, которые не дают о себе знать, пока не начнёшь копать. Их исправление представляет обычно интересную инженерную задачу, но с точки зрения конечного результата иногда даже вредно (ибо на поверхность начинает лезть всякое разное). Вообще, я как правило отстаиваю точку зрения вычищения авгиевых конюшн, но это имеет смысл и возможно далеко не всегда.

                                                      Наличие генераторов генераторов генераторов просто не лучшим образом говорит об архитектуре системы сборки.

                                                      Нет, это говорит о том, что использование этих техник очень даже оправдано, когда они к месту. lex, bison, иногда что-то своё. За не использование этих инструментов, когда они к месту, надо расстреливать.
                                                      Иногда бываю другие, специфичные для разных областей забавные тулы.

                                                      В remake всё, что добавлено — отладчик. Это, конечно, позволит попроще жить в уже существующей дерьмовой инфраструктуре, но первопричина в том, что инфраструктура дерьмовая, а не в том, что отлаживаться тяжело. Писать надо так, чтобы отлаживаться не приходилось.

                                                      Чтобы отлаживаться не приходилось, говорите? Хм. Такого даже в идеальном мире не бывает ;-)
                                        0
                                        Советую глянуть на mk-configure. Он, правда, работает с bmake, а не GNU make, но bmake есть практически везде на данный момент.
                                        С ним этот мейкфайл будет выглядеть так:
                                        PROG = hello
                                        
                                        SRCS = main.c hello.c
                                        
                                        .include <mkc.mk>
                                          0
                                          Впервые слышу о подобном.
                                          Предлагаю альтернативой CMake: кросплатформенный, способный собрать проект в любой директории.

                                          Без install:
                                          set(sources main.c hello.c)
                                          add_executable(hello ${sources})
                                          

                                          Для install на пару строк больше выйдет.
                                            0
                                            CMake не альтернатива. У него уродский входной синтаксис, а мейкфайлы он генерит нечитабельные и неотлаживаемые.

                                            Кстати, приведённый выше мейкфайл уже с install и прочими весёлыми штуками.
                                          0
                                          Хех, вот так и подсаживаешься.

                                          Поставил Arch Linux на Paspberi Pi чтобы переключить ASIC с основного компьютера. Кое как в том числе тупо повторяя написанное с найденной страницы с инструкциями все это завел. В том числе и make cgminer.

                                          Сейчас глядя на пример с hello.c осознал, что от написания софта меня по сути ничего не отделяет. Никаких навороченых комплексов. В редакторе написал, make и любуешься на результат. И это завораживает.
                                            +1
                                            Вашу простейшую программу main.c можно собрать вообще без Makefile:

                                            make main

                                            Попробуйте.
                                              0
                                              Работает!
                                              Я с трудом воспринимаю «раздутые» примеры и писал с учетом того, что бы людям вроде меня было проще усвоить материал.
                                              Можно было сразу выкатить сложный пример и далее подробно разобрать его, но думаю это ударило бы по усвояемости материала.
                                                0
                                                Но тем не менее.

                                                Вопрос неявных правил (Implicit Rules) и, в частности, встроенных правил, обойдён, по-моему, незаслуженно. Это распространённый источник недопонимания и желания писать в своих Makefile то, что уже и так встроено в Make.
                                                +1
                                                и даже без make
                                                gcc main.c
                                                  0
                                                  А если дописать в исходнике первой строкой #!/usr/local/bin/tcc -run, то можно и без gcc:

                                                  ./main.c
                                                    0
                                                    может уже совсем не компилировать?
                                                      0
                                                      ну, в случае helloworld можно и не компилировать :)
                                                      А в случае прототипирования простейшей утилитки, состоящей из единственного файла бывает гораздо быстрее без make. Голый gcc или g++, а потом запускать ./a.out
                                                      Если прототипирование растягивается на десяток-другой запусков, то в определённый момент уже можно добавить Makefile (исключительно ради того, чтобы компилировать прямо из Vim командой :make
                                                        0
                                                        Не спорю, сам так иногда делаю :) но с чего то надо было начать, а по поводу простоты — хотелось чтобы статью можно было не напрягаясь усвоить за раз, а не добавлять в закладки чтобы в итоге никогда не прочесть
                                                    +1
                                                    Семантика отличается.
                                                    make, при наличии в окружении переменных вроде CC и CFLAGS, применит их в процессе компиляции. И файл получится с тем же именем, что и исходник — это проще, когда в одной папке несколько исходников. Как раз при прототипировании.

                                                    Ну и мощь — во встроенных правилах:

                                                    ➜ ls
                                                    rpn.y
                                                    ➜ make rpn
                                                    yacc rpn.y
                                                    mv -f y.tab.c rpn.c
                                                    cc -c -o rpn.o rpn.c
                                                    cc rpn.o -o rpn
                                                    rm rpn.o rpn.c
                                                  0
                                                  В качестве отличного источника также рекомендую CMCrossroads (например, Painless non-recursive make) и в частности статьи John Graham-Cumming. Он же автор GNU Make Standard Library — в ней немало хороших решений задач, для которых хотелось бы нагородить своих хаков. И отладчик.
                                                    +1
                                                    А вот скажите, а для чего в список исходников указывают вручную все С файлы, а не просто
                                                    SRC=$(wildcard *.c)
                                                    

                                                    Разумеется, если не нужна какая-то индивидуальная сборка отдельных файлов
                                                      0
                                                      Затем, что если кто-то положит в ту же папку свой исходник (возможно, случайно), сборка может сломаться с странными ошибками.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        +1
                                                        В makefile есть функции wildcard и shell.
                                                        С помощью них можно построить дерево файлов по маске.
                                                        Ну, а дальше компилить.
                                                        0
                                                        Что, даже изящнее чем с помощью cmake?

                                                        cmake_minimum_required(VERSION 2.8)
                                                        file(GLOB_RECURSE sample_SRC ./ "*.cpp")
                                                        add_executable(helloworld ${sample_SRC})
                                                        

                                                          –1
                                                          Конечно же, мейк изящнее.
                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                              +1
                                                              Изящество в том, что:
                                                              — данный вспомогатильный код пишется быстрее, больше времени на payload
                                                              — не нужно ломать голову что происходит, человекопонятное описание
                                                              — эти бездушные «огромные» makefile достаточного качества, чтобы их даже не открывать :-)
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  0
                                                                  Видимо есть маленькое отличие между маленьким хобби проектом и промышленным программированием. Я не могу проверить на своих последних проектах данный makefile потому как:

                                                                  — кросскомпиляция
                                                                  — внешние зависимости
                                                                  — внутренние зависимости
                                                                  — различные цели: so, bin
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                      0
                                                                      Это делается одной командой include_directories.
                                                                      Ваш тоже не работает, у вас при изменении заголовочных файлов не пересоберутся объектники зависящие от них, и одной строкой вы это никак не исправите.
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                +2
                                                                Достаточно того что в CMake вы описываете суть проекта на высоком уровне — «взять все исходные файлы» и «собрать из них бинарник», а не цепочку трансформаций списка файлов, суть которой нельзя понять не вчитываясь и которую придётся переписывать целиком если структура изменится (скажем, добавится ещё один уровень каталогов) — две гибкие и понятные строки всяко изящнее десятка строк лапши.

                                                                А так, среди всего прочего, с CMake вы получаете из коробки контроль за зависимостями по include'ам (без этого вообще нельзя), поддержку переопределения компилятора/флагов/префикса/destdir и т.д. что необходимо для переносимой сборки, генерацию solution'ов для IDE и много чего ещё, это даже не затрагивая того что может потенциально понадобиться позже при развитии проекта и что на make двумя строчками сделать может и не получиться (поиск зависимостей, установка, интегрированное тестирование, кросс-компиляция), причём переносимо и без необходимости помнить об особенностях каждой целевой системы.
                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                +1
                                                                У вас полная перекомпиляция всего и вся на каждый вызов make происходит. Вам make вообще не нужен, простого shell-скрипта достаточно.
                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                    0
                                                                    Если изменить константу в заголовочном файле, приведёт ли это к пересборке проекта?
                                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                                        0
                                                                        НЕ происходит

                                                                        Да, я не сразу заметил зависимость all от объектников. К слову, я занимался переводом Managing Projects with GNU make Just For Fun (до сих пор валяется на гитхабе). А билд систем перепробовал ну наверное штук 15, для всех мейнстримовых (и не очень) языков.

                                                                        P.S. Я не минусовал. Но ваш мейкфайл очень легко может привести к некорректной сборке, которая засегфолтится в самый неподходящий момент. Для домашнего проекта который не меняется после первых экспериментов это может быть ок. Для серьёзного проекта этот мейкфайл очень, очень далёк от приемлимого.
                                                                        Дойду до работы — покажу вам скрипт.
                                                                      0
                                                                      пример простого шелл-скрипта можно привести?

                                                                      Вот аналог:
                                                                      #!/bin/bash
                                                                      project=hobo
                                                                      outdir=build
                                                                      sources=`find -name '*.cpp'`
                                                                      incdirs=`ls -d */ | grep -v "${outdir}/" | sed 's/^/-I/'`
                                                                      
                                                                      mkdir -p ${outdir}
                                                                       
                                                                      for src in $sources; do
                                                                          obj=${outdir}/${src}.o
                                                                          mkdir -p `dirname $obj`
                                                                          if [ ! -f "$obj" -o "$src" -nt "$obj" ]; then
                                                                              echo Compiling $src into $obj
                                                                              g++ ${incdirs} -c "$src" -o "$obj"
                                                                          fi
                                                                      done
                                                                      
                                                                      echo Linking ${project}
                                                                      g++ ${incdirs} `find ${outdir} -name '*.o'` -o ${outdir}/${project}
                                                                      

                                                                      На bash пишу раз в год, поэтому возможны ошибки и улучшения. Есть небольшое отличие от вышеупомянутого мейкфайла — объектники складываются в отдельный каталог, структура которого повторяет структуру дерева исходников, что на практике гораздо удобнее.
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          0
                                                                          знаете, как в моем makefile-е обработать изменения в заголовочных файлах

                                                                          Знаю, но доводить это до ума у меня нет никакого желания. Это написано везде миллион раз, всё делается через хаки с седом.

                                                                          В этом проблема Make — из коробки он подходит для чего угодно кроме сборки С/С++ проектов. Чтобы оно хоть как-то работало, люди либо пишут собственные решения, в которых в 99% невозможно разобраться, либо бездумно копипастят иероглифы из проверенных источников.

                                                                          В реальной жизни нужен трекинг зависимостей, трегинг ключей сборки, конфигурации сборки (Debug/Release/...), отделение бинарников от исходников, удобная линковка библиотек, определение доступных внешних библиотек и многое другое. Всего этого в make из коробки нет и достигается лишь хаками. Пусть лучше GYP или CMake генерят эти хаки за меня, у меня есть дела поважнее. Очередной makefile имеет смысл писать только если у вас поистине уникальный проект вроде ядра linux.

                                                                          я его просто удаляю сейчас, когда меняю что-то в header-е

                                                                          Нужно удалять ВСЕ объектники, в которые инклюдят хедер, а не один.о файл. Единственный способ _корректной_ сборки без знания детальных зависимостей — каждый раз пересобирать всё с нуля. Если вам хочется держать зависимости в голове — пожалуйста.

                                                                          Вдобавок, у Вас отсутствует target clean

                                                                          rm -rf build всего на 2 символа длиннее чем make clean. Если сделать алиас на rm -rf, будет даже короче.
                                                                            0
                                                                            Разрешение и использование зависимостей конкретного файла исходного кода это НЕ задача GNU make.

                                                                            Если у Вас инкрементная сборка проекта на C/C++, то Вы можете покурить опции запуска Вашего любимого тулчейна. Я использую GCC, посему у меня хватает для сборки каждого объектника "-MMD -MF {template}" воткнуть и подцепить директивой include. Хаки с sed-ом хороши ровно до тех пор, пока это Ваш внутритумбочный проект — в дальнейшем рекомендую отказаться от них в пользу by-design решений.

                                                                            Тот факт, что в так любимых Вами генераторах генераторов генераторов Makefile есть всяческие вкусные плюшки из коробки, ровным счётом тоже не проблема GNU make. Тот же самый out-of-tree build, на который молятся программисты (и я в том числе), вполне себе достигается GNU make без implicit rules, и отнюдь не хаками — просто надо читать документацию.

                                                                            PS: впрочем да, от головотяпных зависимостей GNU make не спасёт отца русской демократии компиляции.
                                                                              +1
                                                                              использование зависимостей конкретного файла исходного кода это НЕ задача GNU make

                                                                              А чья?

                                                                              в пользу by-design решений

                                                                              Можно поподробнее?

                                                                              отнюдь не хаками — просто надо читать документацию

                                                                              Надо просто запускать make из target-каталога, и всё сразу становится очень просто. Но ведь все привыкли делать это из каталога с makefile-ом…

                                                                              в так любимых Вами генераторах генераторов генераторов Makefile

                                                                              Честно говоря, CMake мне противен. GYP концептуально приятней, но он очень сырой. На работе для сборки используем чистый мейк с кучей собственных макросов и правил, которых 95% разработчиков никогда не видели. Всё благодаря талантливым билд-инженерам, спасибо им большое.

                                                                              Для личных проектов я не могу уделять мейкфайлам много времени, инфраструктурные вопросы и так сейчас требуют больше времени, нежели непосредственное написание кода. CMake плох, но он решает мои задачи с адекватным качеством за минимальное уделённое ему время, его многие используют и поддерживают, поэтому я с ним мирюсь.
                                                                                0
                                                                                Некорректно выразился, спасибо за замечание. Разумеется, использование целиком и полностью на совести make, а вот генерация — тулчейном.

                                                                                Хаки на sed зачастую неплохо заменяются на встроенные функции; зачастую, но не всегда. Скиньте в ЛС реальный пример такого использования sed, которое на порядки лучше built-in вызовов, мне просто интересно.

                                                                                Запускать make из build directory актуально только для собственно конечных целей, а промежуточные итоги (объектники, прекомпилированные заголовочники, временные файлы от link-time optimization, et cetera) и так хорошо собираются.

                                                                                По двум последним абзацам Вашего комментария я могу судить о том, что у Вас разработка ПО — собственно работа, посему затраты на сторонние задачи (обслуживание билд-системы) редко оправданы или же вне компетенций. У меня это хобби, поскольку работаю в другой сфере IT. Сейчас сижу и пилю Makefile только потому, что GYP не хочет сделать мне красиво в одном хорошем опенсорсном проекте. Сыроват он, согласен.

                                                                                Спасибо за беседу, желаю Вам успехов!
                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                                0
                                                                                Если Вы претендуете на профессионализм, то хотя-бы удосужьтесь проверить самостоятельно то, что пишете

                                                                                Какое именно из процитированных вами утверждений неверно? Не могу найти никаких неточностей или противоречий.

                                                                                Если вас устраивает помойка в каталоге с исходниками — прекрасно. Меня не устраивает. Хотя бы потому, что в текстовом редакторе нужно набирать больше букв, чтобы он понял, какой именно файл я хочу открыть. Когда появляется желание помещать сгенерированные файлы в отдельный каталог, файлы зависимостей нужно немного преобразовать. И обычно в дело пускают иероглифы вроде
                                                                                sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                                                                                    -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P;
                                                                                

                                                                                Источник: make.paulandlesley.org/autodep.html

                                                                                У решения выше есть ещё один недостаток: в случае переноса или удаления заголовочных файлов мейк может начать жаловаться на неразрешимые зависимости, и придётся делать полный ребилд.

                                                                                Когда захочется контролировать уровень детальности вывода билд-системы, собирать модули в отдельные библиотеки (например, для юнит-тестирования), использовать ключи компиляции в качестве реквизитов и прочих радостей, нужно будет набрать определённое кол-во букв, набирать которые раз за разом утомляет. Примеры реализации некоторых из описанных радостей можете найти в мейкфайле ядра.
                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                    +1
                                                                                    Единственный способ корректной сборки без знания детальных зависимостей — каждый раз пересобирать всё с нуля.

                                                                                    .d файлы, которые генерятся компилятором, и есть те самые детальные зависимости. Вы утверждали, что удаляли объектный файл при изменении соответствующего заголовочного файла, а именно
                                                                                    мне на практике более удобно иметь объектник в той-же директории — я его просто удаляю сейчас, когда меняю что-то в header-е

                                                                                    Я сказал, что удалять один объектный файл недостаточно, нужно удалять ВСЕ объектные файлы, в модули которых включается изменённый объектный файл. Если этого не делать, сборка будет некорректной и есть вероятность сегфолта или ещё хуче — незаметной некорректной работы программы. Если вы считаете это утверждение неверным и самонадеянным, я умываю руки.

                                                                                    Смешно, что никто не смог ответить на мой, достаточно элементарный вопрос

                                                                                    Ваши вопросы больше походят на топики SO, а их тон лично мне не приятен. Начали с «а вам слабо», показали мейкфайл, абсолютно непригодный для какого-либо production, начали оскорблять аудиторию и ждёте адекватной реакции на вашу викторину? Ок.
                                                                                      0
                                                                                      в модули которых включается изменённый объектныйзаголовочный файл. Если

                                                                                      ещё хучже
                                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                                          0
                                                                                          приведите «хаки с седом» в моей последней версии?

                                                                                          Их нет, потому что все .d файлы высераются в дерево с исходниками. Для меня это неприемлимо.

                                                                                          топики SO

                                                                                          SO = StackOverflow.com

                                                                                          народ просто _не умеет_ им пользоваться

                                                                                          Повторюсь, ваш мейкфайл тривиален потому, что он непригоден для production. Ну либо у меня слишком высокие запросы, но опыт говорит об обратном.

                                                                                          Правильный ответ был-бы — «не знаю»

                                                                                          Ух… смело. Я могу в деталях объяснить, как именно работает ваш код, завязанный на весьма специфическое поведение make при нахождении отсутствующих инклюд-файлов. Я знал о нём ещё 5 лет назад, когда начал переводить Managing Project with GNU Make (Глава 8).
                                                                                          К слову, весь материал из статьи покрывается в первой главе этой книги.
                                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                                              +1
                                                                                              Потрудитесь объяснить, в чем заключается «весьма специфическое поведение make»?

                                                                                              GNU make рассматривает все файлы, которые в него инклюдят, как неявные цели. Когда он прочитывает основной мейкфайл, он пытается обновить все включаемые файлы с помощью явных или неявных правил и рестартует, если получилось.

                                                                                              Но если ещё немного подумать, то это поведение здесь действительно не при чём. Оно нужно только для решения, описанного в руководстве www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html.

                                                                                              И без sed-а можно обойтись, если компилятор — gcc или поддерживает все его флажки, как clang. Сам переводил, а уже не помню. Ок, тут вы правы.
                                                                      +5
                                                                      make не особо кстати и минималистичен, в нем просто сотнями встроенных правил срабатывающих по умолчанию. причем даже с этими встроенными правилами пользоватся makeом достаточно муторно: сделать простейшую вещь — отделить бинарники от исходников (out of source tree build) еще то занятие.
                                                                      Если и смотреть на минималистичные системы сборки — то пожалуй это ninja (http://martine.github.io/ninja/), мало того что он проще так он еще и работает ощутимо быстрее (на инкрементальных сборках более-менее больших проектов).
                                                                      Причем в последних CMake есть возможность использовать ninja для генерации собственно билд скриптов и получается девелопер может получить все лутшее от обоих миров — высокий уровень CMake плюс скорость сборки ninja. Чем сейчас и пользуюсь. )
                                                                        0
                                                                        Спасибо за наводку, попробую. Встречал в выводе cmake, но не обратил внимания на эту ninja.
                                                                        Одно время думал перейти на tup, но как-то не сложилось. Да и проект загибается, видимо.
                                                                          0
                                                                          tup не быстрее ninja но при этом сильно ограничен в том что может делать. tup не позволяет (покрайней мере раньше не позволял и автор не собирался это исправлять хоть я и предлагал модель как это можно сделать) запускать команды которые правят несколько файлов в разных папках, как по мне абсолютно фатальное ограничение для данной билд тулзы.
                                                                          Одно время я пытался использовать tup — даже засабмитил 3 или 4 фикса чтобы он нормально заработал под win32 но:
                                                                          а) выше описанное ограничение по выходам команд (нельзя запустить патч сорцов во время билда, или генерацию сложных ресурсов)
                                                                          б) win32 порт сильно отстает от *nix (на win32 даже нет поддержки out of source build)
                                                                          в) сложная имплеметнация как на *nix так и на win32 которая часто глючит — tup через хуки мониторит процессы запущенные их под него чтобы обнаружить все зависимости и входы-выходы
                                                                          г) сильно ограниченные возможности скриптов, язык tup даже не прячет особенности win32 vs *nix
                                                                          в результате после прототипа на tup и обраружения всех этих недостатков перевел проекты над которыми работал на cmake + ninja.
                                                                        0
                                                                        очень понятно, спасибо
                                                                          +1
                                                                          Извините, если есть выше, но я не увидел.
                                                                          Здесь же на хабре есть хорошая статья Чем плох GNU make
                                                                          Для ее поддержки make сопоставляет время изменения целей и их реквизитов (используя данные файловой системы), благодаря чему самостоятельно решает какие правила следует выполнить, а какие можно просто проигнорировать:

                                                                          В частности одна из причин указанных там, эта неудачная зависимость от точного времени, что затруднительно на серверах.
                                                                          Посмотрите по ссылке, там есть ещё довольно много аргументов.
                                                                            0
                                                                            Спасибо за простую, написанную доступным языком и, главное, последовательную статью.

                                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                            Самое читаемое