GNU Make может больше чем ты думаешь

    Как только исходники проекта надо распространять, то возникает необходимость использовать систему сборке, вместо того что нагенерила любимая IDE. В мире unix (с подачи gnu) традиционно используется autotools, ему есть отличные альтернативы в виде cmake или scons. Но почему-то ядро Linux собирается при помощи GNU Make, а вся FreeBSD включая порты при помощи BSD Make. WTF?

    Однажды намучившись с autotools, я решил провести эксперимент — насколько можно перелопатить Makefile, чтобы обеспечить себе более-менее удобную сборку.



    Т.к. современные make сильно отличаются, я использовал GNU Make, т.к. он родной для моей основной системы — GNU/Linux.
    И так, первую приятную вещь, что я обнаружил, так это отсутствие необходимости писать для каждого файлика правило. Т.е. вместо повторения для каждого файла блоков вроде:
    foo.o: foo.c
    	$(CC) -c foo.c
    

    Можно просто, для конкретного бинарика указать список зависимых объектов и он их соберёт:
    main_executable: foo.o bar.o test.o main.o
        $(CC) $^ -o $@ $(LDFLAGS)
    

    Что за $^ и $@? Это автоматические переменные, здесь $@ раскрывается в имя цели (т.е. main_executable), а $^ в список всех зависимостей. Подробное описание всех автоматических переменных тут.

    Вторая проблема которую я хотел решить это генерация зависимостей. Один файлик может зависеть от другого, а он от системного заголовка и т.д. Если прописывать их руками то отпадает всякий смысл в предыдущем открытии.
    Как выяснилось, gcc умеет разбирать исходник и выдавать список заголовков от которых он зависит, делается это при помощи ключа -M: gcc -M foo.c. Вывод команды в формате Makefile, следовательно он может быть сохранён в файл и подключён в Makefile. Большинство build систем также использую gcc -M. Классически, генерация зависимостей запихивалась куда-нибудь в таргет: depends, но в мануале нашёлся способ обновлять все зависимости автоматом.
    В совете используется один список объектов, но что если целей несколько? Я слишком ленив чтобы руками писать для каждой! По этому я решил организовывать Makefile по такой схеме:
    * Есть список targets, содержащий все цели (бинарики). Правило для его сборки прописывается вручную.
    * Для кажой цели есть список её объектов: цель_obj = foo.o bar.o и т.д.
    * Есть цель all которая проходит по всем целям.
    Исходя из этого, необходимо взять все цели, преобразовать их имена в вид цель_obj, из них уже вытащить объекты, переименовать их суффиксы в .dep (или в .d), и потом только подключить. Звучит сложно? Строчка которая делает это ещё более запутана: deps = $(foreach o,$(targets:=_obj),$($(o):%.o=.deps/%.dep)). Я даже не буду пытаться объяснить ньюансы, скажу лишь что она генерит список зависимостей для каждого объекта, в виде .deps/foo.dep. В директорию .deps только чтобы не гадить в директории с сорцами.

    В заключение пример мэйкфайла:
    CFLAGS=-Wall -pipe -ggdb
    
    compiler_obj = compiler.o string_util.o tokenizer.o btree.o memory.o ast.o
    vm_obj = vm.o
    
    targets = compiler vm
    
    all: $(targets)
    
    .deps/%.dep: %.c
    	@mkdir -p .deps
    	@set -e; rm -f $@; \
    		$(CC) -M $(CFLAGS) $< > $@; \
    		sed -i 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@;
    
    deps = $(foreach o,$(targets:=_obj),$($(o):%.o=.deps/%.dep))
    -include $(deps)
    
    echo-deps:
    	@echo $(deps)
    
    compiler: $(compiler_obj)
    	@$(CC) $^ -o $@ $(LDFLAGS)
    
    vm: $(vm_obj)
    	@$(CC) $^ -o $@ $(LDFLAGS)
    
    clean:
    	rm -f *.o .deps/*.dep $(targets)
    
    ctags:
    	@ctags *.c *.h
    


    P.S. В данном случае есть ещё куча недочётов, но опыт работы с GNU Make, дал мне понять, что при желании, на ней можно сделать полноценную билд систему. Возможно в будущем, напишу и выложу более generic библиотеку мэйкфайлов.

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

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 9 120 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 58

      +3
      Вы в скриптах тоже допускаете грамматические АшЫПки, или просто не любите русский язык?

      ЗЫ «ньюансы» стали уже фишкой Хабра наверное
        –4
        Я не люблю русский язык, в школе любовь отбили.
          +6
          Ну, мне (и Вам, судя по всему) почему-то школьная информатика не отбила интереса к ИТ ;)
            –3
            Наверное по тому, что информатику не вели gramar nazi.
              +5
              Какбе «gramMar nazi».
                –3
                You is one of them!
                  +5
                  Какбы «You ARE one of them!» :)
                    0
                    Да я знаю. Как бы шутка. И, кстати, КАК БЫ.
                      +1
                      Я вас отлично понял (с первого раза) и поддержал.
                      Мне вообще следовало написать «какбе», подражая <~dals>
                        +2
                        Если вам без слова «как бы»,
                        обойтись никак нельзя,
                        то умом вы значит слабы
                        и учились как бы зря.
                        (ц) не помню кто :)
        0
        Хорошая статья. Правда как то не могу понять её практического применения… Вернее я не могу понять зачем это надо? Если спортивный интерес — однозначно вам плюс. А если нет — раскажите мне, так как любопытно =)
          0
          В первую очередь конечно спортивный интерес. Практическая польза: иметь заготовку для небольших программ, где лень возиться в autotools.
            0
            Я для мелочи юзаю qmake. Создает make-файл, которым и пользуюсь :-)
              0
              Если бы я работал с Qt то тоже наверное бы пользовал.
                0
                Qt и не нужно, чтобы использовать qmake standalone.
              0
              А не проще и практичнее чем тратить время на бодание с граблями make делать сборку с помощью scons, а секономленное время потратить на разработку.
              Проблемы make извесны и в рамках его идеологии и архитектуры неизлечимы(иначе порушится обратная совместимость) так может тогда выбрать инструмент который будет помагать, а не ставить подножки.
                0
                Нарисовалась свободная минутка, имею право потратить так как хочу, кто-то в игры играет, кто-то в курилке тусит, а я с gnu make поковырялся.
            0
            Исправьте, пожалуйста:

              +1
              В мире unix (с под*о*чи gnu)

                +1
                Поправил, спасибо.
                0
                Прошу прощения — какие-то непонятные явления у меня с комментариями. Виноват.
                +2
                  0
                  Нет, но спасибо, почитаю.
                  +5
                  Для большинства достаточно крупных проектов используются именно Мэйкфайлы, а не autotools, причём написанные людями Мэйкфайлы. Это хорошо ложится в практику ежедневных билдов.

                  Вы будете смеяться, но в Windows мэйк используется ещё шире, чем юниксах. 95% известным мне проектов собираются в Виндах с помощью микрософтского nmake.

                  Но чем сложнее становится проект, тем сложнее становятся Мэйкфайлы. Всё больше уровней включений (include) становится в Мэйкфайлах.

                  Постепенно, с ростом проекта, поддерка Мэйкфайлов становится всё более и более утомительной, затратной. И тут уже действительно недостатки мэйка становятся видны невооруженным глазом. Но есть другая система, которая написана специально, чтобы преодолеть недостатки мэйка (Особенно зависимости, обслуживание зависимостей). Я говорю об Apache Ant. ant.apache.org/

                  Да, корни Анта из Жавы, но он применим для сборки систем на любых языках программирования, на любых ОС.

                  Тем кто планирует писать немаленькие программы/системы настоятельно рекомендую обратить внимание на Ant.
                    –2
                    Хм, а я думал им только яву собирать. Нодо будит посмотреть.
                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Аналогом Ant в мире .NET является NAnt=)
                        0
                        Вообще-то для промышленного C/C++ под Windows доминирует Visual C++ и у него своя система MSBuild, но под C++ используется VCBuild, его я считаю использовать целесообразнее в Windows чем Ant. В 2010 планируют польностью перейти к MSBuild без использования VCBuild.

                        А для личных маленьких проэктов я использую scons. XML-хорош для машины но для человека он ужасен. Человекочитабельный формат значительно легче в дальнейшем сопровождать, да и после обучения легче логическую ошибку найти.
                          0
                          xml уныл и для машины, s-expressions рулит.
                        0
                        Было бы интересно, если бы кто нибудь написал обзор о том, как можно собирать с помощью make на N ядрах одновременно. Хотя, думаю это не так то и просто, раскидать N исходников на N ядер с учетом зависимостей и очередности сборки.
                          +1
                          make -j4
                            0
                            Это по два трэда на ядро будет. Проблемы встречал только на автотулз, да и то в редких ситуациях.
                              +1
                              Встроенная возможность make, отлично! Спасибо!
                              Протестировал на последней ревизии mplayer-а:
                              make: real 4m12.391s
                              make -j4: real 1m17.964s
                              make -j:real 1m13.463s

                              Это на Intel® Core(TM)2 Quad CPU Q9550 @ 2.83GHz
                                0
                                -j без параметра АФАИР это количество используемых потоков = количество ядер+1
                                  0
                                  что количество потоков вроде как равно количеству ядер, но для чего +1?
                                    0
                                    например для того, чтобы n+1й работал, когда n-й пишет на диск. чисто теоретически.
                                    0
                                    хм, мой make вот что говорит

                                    $ (make -h 2>&1 && make -v) | grep -e "Make\|-j "
                                    -j [N], --jobs[=N] Allow N jobs at once; infinite jobs with no arg.
                                    GNU Make 3.81
                              –5
                              сборке опасносте?
                                –1
                                >можно хакнуть Makefile

                                т.е. теперь чтение документации называется хаком?
                                  +2
                                  Использование чего либо не совсем очевидным образом и есть хак. Может в этом случае и громковато сказано, но человек проявил терпение и смекалку, за что и получил от меня плюс. Осилить документацию — иногда это тот еще труд :)
                                    0
                                    не совсем очевидным? о_О

                                    это именно стандартное поведение make, и я удивился, когда прочел, что автор писал все зависимости руками.
                                      0
                                      А мне например очевидно как использовать find и я про неё статьи не пишу. Если уж задел тебя использованием словом «хак» то извини, обидеть не хотел.
                                        0
                                        да, ты четко подметил разницу — если убрать из статьи фразу про хак, она воспринимается совсем иначе.

                                        а так, словно из журнала хакер…
                                          0
                                          Ок, убрал. Всё-таки в англоизычных интернетах как-то более классически это слово воспринемают :\
                                            0
                                            здорово, что прислушиваешься к мнению других. :)
                                    0
                                    Можешь провести аналогию с «грязный хак» — воткнуть костыль чтобы работало. В данном контексте примерно тоже самое.
                                    +2
                                    Кстати, есть более стандартный способ не гадить в директории с сорцами — VPATH
                                      0
                                      Да, спасибо за совет. Как-то работал над проектом, где была куче сорцов в одной дире и несколько директорий для сборки (собирались разные проекты на одном ядре). Т.е. там нельзя было все .o держать в одном месте и Makefile генерился своим скриптом. Если занесёт на подобную организацию сборки — обязательно VPATH заюзаю.
                                      +1
                                      Когда передо мной встал выбор какую систему сборки использовать — я остановился на scons. К тому моменту я неплохо владел make и в ужасе думал о сложном Makefile под большой проект: слишком много нужно прописать руками, слишком нечеловеческий синтаксис для простых операций.
                                      Я ценю шелл-скрипт, но
                                      %.o: %.c defines.h
                                                   $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
                                      

                                      для большого проекта, среди сотен подобных строчек просто снесёт голову…

                                      А в случае со scons мы имеем полноценное python-окружение, внятные директивы и умный сборщик…

                                        0
                                        scons конечно хорошо но зачем правила то руками писать?
                                        %.o: %.c defines.h
                                        $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
                                        — так не нужно. Зависимость файлов от defines.h сама сгенерится. Также для специфичных целей, можно указать специфичные CFLAGS и т.д. Хотя конечно лучше scons использовать, если есть возможность, я просто в защиту make :)
                                          0
                                          Зависимость файлов от defines.h сама сгенерится. Генерацию зависимостей таки придётся ручками прописывать: $(CC) -M и всё такое… :)

                                          P.S. Я тоже не наезжаю на make, хорошая система. Но стоит использовать более современные аналоги :)
                                            0
                                            -include .config
                                            -include ../mk/common.mk
                                            
                                            all:
                                            
                                            dir  := ls
                                            exe  := program
                                            src  := main.c
                                            misc := ls/stdafx.h.gch
                                            src := $(addprefix $(dir)/, $(src))
                                            
                                            $(eval $(call make-program, $(exe), $(misc), $(src)))
                                            
                                            -include ../mk/build-rules.mk
                                            


                                            Вот примерно такие у меня мэйкфайлы для новых проектов.
                                            Все зависимости файлов итп итд сгенерируются(одновременно с компиляцией сишного файла, а не как в этой статье). Можно так же наделать много make-program/library/archive/итд (обычно я их выношу в отдельные .mk файлики и делаю инклуд в основной). Если нужно будет что-то посложнее — всё легко реализуется.
                                        +1
                                        Можно просто, для конкретного бинарика указать список зависимых объектов и он их соберёт


                                        Не поверите, make это умеел еще тогда, когда ни вас, ни GNU в проекте не было.
                                          –3
                                          «В мире unix (с подачи gnu) традиционно используется autotools..» — «мир юникс» не начинается и не заканчивается гнутым подельем.
                                          «Но почему-то ядро Linux собирается при помощи GNU Make, а вся FreeBSD включая порты при помощи BSD Make.» — еще не хватало чтобы бсд собиралась гнутьем.

                                          вы и все гнутье сообщество будете очень удивлены, когда узнаете что все чему вы радовались последние годы существования тазика торвальца и гнутья существует довольно давно за пределами гну. а еще больше удивитесь тому что там, за рамками гнутья, есть такие вещи которых у гнутья попросту нет.

                                          никогда не забуду как линупсятнеги радовались своему LVM'у и даже собрались портировать во FreeBSD. а бсдятники недоуменно пальцем у виска крутили… в конечном счете гнушники не осилили портануть, а потом обиделись когда узнали что в бсд с незапамятных времен есть vinum который умеет все что лвм и еще немного сверху…
                                            +2
                                            Вас обидел кто-то?
                                              +1
                                              Офтоп: что лучше Windows или Linux? Третьего не дано :)
                                                –1
                                                Бздун такой бздун.
                                                +2
                                                >> опыт работы с GNU Make, дал мне понять, что при желании, на ней можно сделать полноценную билд систему

                                                Мой работы с C++ и Java дал мне понять, что при желании, на них можно полноценно программировать. :-)

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

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