- Что такое Makefile и как начать его использовать
- Введение
- Что такое make и Makefile
- Синтаксис Makefile
- Продвинутое использование
- Фальшивая цель
- Последовательный запуск команд и игнорирование ошибок
- Переменные
- Заключение
- Дополнительные материалы
- Makefile для самых маленьких
- Makefile для чайников? Mac OS X
- Автоматизируем работу с контейнерами через Makefile: сборка, тестирование и развёртывание за один вызов make
- Цели и структура Makefile
- Сборка, тестирование и развёртывание
- Дополнительные цели
- Локальный запуск
- Связанные одной целью
- Не только контейнеры
Что такое Makefile и как начать его использовать
Введение
В жизни многих разработчиков найдётся история про первый рабочий день с новым проектом. После клонирования основного репозитория проекта наступает этап, когда приходится вводить множество команд с определёнными флагами и в заданной последовательности. Без описания команд, в большинстве случаев, невозможно понять что происходит, например:
Эти команды являются лишь частью того, что необходимо выполнить при разворачивании проекта. В приведённом примере видно, что команды сами по себе длинные, содержат много флагов, а значит, их трудно не только запомнить, но и вводить вручную. Постоянно вести документацию становится сложнее с ростом проекта, она неизбежно устаревает, а порог входа для новичков становится выше, ведь уже никто не в состоянии вспомнить всех деталей проекта. Некоторые такие команды необходимо использовать каждый день, и даже не один раз в день.
Со временем становится понятно, что нужен инструмент, способный объединить в себе подобные команды, предоставить к ним удобные шорткаты (более короткие и простые команды) и обеспечить самодокументацию проекта. Именно таким инструментом стал Makefile и утилита make . Этот гайд расскажет, как использование этих инструментов позволит свести процесс разворачивания проекта к нескольким коротким и понятным командам:
Что такое make и Makefile
Makefile — это файл, который хранится вместе с кодом в репозитории. Его обычно помещают в корень проекта. Он выступает и как документация, и как исполняемый код. Мейкфайл скрывает за собой детали реализации и раскладывает “по полочкам” команды, а утилита make запускает их из того мейкфайла, который находится в текущей директории.
Изначально make предназначалась для автоматизации сборки исполняемых программ и библиотек из исходного кода. Она поставлялась по умолчанию в большинство *nix дистрибутивов, что и привело к её широкому распространению и повсеместному использованию. Позже оказалось что данный инструмент удобно использовать и при разработке любых других проектов, потому что процесс в большинстве своём сводится к тем же задачам — автоматизация и сборка приложений.
Применение мейка в проектах стало стандартом для многих разработчиков, включая крупные проекты. Примеры мейкфайла можно найти у таких проектов, как Kubernetes, Babel, Ansible и, конечно же, повсеместно на Хекслете.
Синтаксис Makefile
make запускает цели из Makefile, которые состоят из команд:
Но недостаточно просто начать использовать мейкфайл в проекте. Чтобы получить эффект от его внедрения, понадобится поработать над разделением команд на цели, а целям дать семантически подходящие имена. Поначалу, перенос команд в Makefile может привести к свалке всех команд в одну цель с «размытым» названием:
Здесь происходит сразу несколько действий: создание файла с переменными окружения, подготовка базы данных, генерация ключей, установка зависимостей и запуск проекта. Это невозможно понять из комментариев и названия цели, поэтому будет правильно разделить эти независимые команды на разные цели:
Теперь, когда команды разбиты на цели, можно отдельно установить зависимости командой make install или запустить приложение через make start . Но остальные цели нужны только при первом разворачивании проекта и выполнять их нужно в определённой последовательности. Говоря языком мейкфайла, цель имеет пререквизиты:
Задачи будут выполняться только в указанной последовательности и только в случае успеха предыдущей задачи. Значит, можно добавить цель setup , чтобы объединить в себе все необходимые действия:
Теперь развернуть и запустить проект достаточно двумя командами:
Благодаря проделанной работе Makefile, команды проекта вместе с флагами сведены в Makefile. Он обеспечивает правильный порядок выполнения и не важно, какие при этом задействованы языки и технологии.
Продвинутое использование
Фальшивая цель
Использование make в проекте однажды может привести к появлению ошибки make: is up to date. , хотя всё написано правильно. Зачастую, её появление связано с наличием каталога или файла, совпадающего с именем цели. Например:
Как уже говорилось ранее, изначально make предназначалась для сборок из исходного кода. Поэтому она ищет каталог или файл с указанным именем, и пытается собрать из него проект. Чтобы изменить это поведение, необходимо в конце мейкфайла добавить .PHONY указатель на цель:
Последовательный запуск команд и игнорирование ошибок
Запуск команд можно производить по одной: make setup , make start , make test или указывать цепочкой через пробел: make setup start test . Последний способ работает как зависимость между задачами, но без описания её в мейкфайле. Сложности могут возникнуть, если одна из команд возвращает ошибку, которую нужно игнорировать. В примерах ранее такой командой было создание .env-файла при разворачивании проекта:
Самый простой (но не единственный) способ «заглушить» ошибку — это сделать логическое ИЛИ прямо в мейкфайле:
Добавлять такие хаки стоит с осторожностью, чтобы не «выстрелить себе в ногу» в более сложных случаях.
Переменные
Зачастую в команды подставляют параметры для конфигурации, указания путей, переменные окружения и make тоже позволяет этим управлять. Переменные можно прописать прямо в команде внутри мейкфайла и передавать их при вызове:
Переменные могут быть необязательными и содержать значение по умолчанию. Обычно их объявляют в начале мейкфайла.
Некоторые переменные в Makefile имеют названия отличные от системных. Например, $PWD называется $CURDIR в мейкфайле:
Заключение
В рамках данного гайда было рассказано об основных возможностях Makefile и утилиты make . Более плотное знакомство с данным инструментом откроет множество других его полезных возможностей: условия, циклы, подключение файлов. В компаниях, где имеется множество проектов, написанных разными командами в разное время, мейкфайл станет отличным подспорьем в стандартизации типовых команд: setup start test deploy . .
Возможность описывать в мейкфале последовательно многострочные команды позволяет использовать его как «универсальный клей» между менеджерами языков и другими утилитами. Широкая распространённость этого инструмента и общая простота позволяют внедрить его в свой проект достаточно легко, без необходимости доработок. Но мейкфайл может быть по-настоящему большим и сложным, это можно увидеть на примере реальных проектов:
Дополнительные материалы
- Руководство по современному Make — «выжимка» из документации на русском языке;
- Утилита make: полезный универсальный инструмент программиста — видео-версия данного гайда.
Мейкфайлы, использованные при составлении гайда:
Источник
Makefile для самых маленьких
Не очень строгий перевод материала mrbook.org/tutorials/make Мне в свое время очень не хватило подобной методички для понимания базовых вещей о make. Думаю, будет хоть кому-нибудь интересно. Хотя эта технология и отмирает, но все равно используется в очень многих проектах. Кармы на хаб «Переводы» не хватило, как только появится возможность — добавлю и туда. Добавил в Переводы. Если есть ошибки в оформлении, то прошу указать на них. Буду исправлять.
Статья будет интересная прежде всего изучающим программирование на C/C++ в UNIX-подобных системах от самых корней, без использования IDE.
Компилировать проект ручками — занятие весьма утомительное, особенно когда исходных файлов становится больше одного, и для каждого из них надо каждый раз набивать команды компиляции и линковки. Но не все так плохо. Сейчас мы будем учиться создавать и использовать Мейкфайлы. Makefile — это набор инструкций для программы make, которая помогает собирать программный проект буквально в одно касание.
Для практики понадобится создать микроскопический проект а-ля Hello World из четырех файлов в одном каталоге:
Все скопом можно скачать отсюда
Автор использовал язык C++, знать который совсем не обязательно, и компилятор g++ из gcc. Любой другой компилятор скорее всего тоже подойдет. Файлы слегка подправлены, чтобы собирались gcc 4.7.1
Программа make
Если запустить
make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Есть еще множество других параметров, нам пока не нужных. О них можно узнать в ман-странице.
Процесс сборки
Компилятор берет файлы с исходным кодом и получает из них объектные файлы. Затем линковщик берет объектные файлы и получает из них исполняемый файл. Сборка = компиляция + линковка.
Компиляция руками
Самый простой способ собрать программу:
g++ main.cpp hello.cpp factorial.cpp -o hello
Каждый раз набирать такое неудобно, поэтому будем автоматизировать.
Самый простой Мейкфайл
В нем должны быть такие части:
Для нашего примера мейкфайл будет выглядеть так:
Обратите внимание, что строка с командой должна начинаться с табуляции! Сохраните это под именем Makefile-1 в каталоге с проектом и запустите сборку командой make -f Makefile-1
В первом примере цель называется all . Это цель по умолчанию для мейкфайла, которая будет выполняться, если никакая другая цель не указана явно. Также у этой цели в этом примере нет никаких зависимостей, так что make сразу приступает к выполнению нужной команды. А команда в свою очередь запускает компилятор.
Использование зависимостей
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
Это надо сохранить под именем Makefile-2 все в том же каталоге
Теперь у цели all есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель clean . Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: make -f Makefile-2 clean
Использование переменных и комментариев
Переменные широко используются в мейкфайлах. Например, это удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
Это Makefile-3
Переменные — очень удобная штука. Для их использования надо просто присвоить им значение до момента их использования. После этого можно подставлять их значение в нужное место вот таким способом: $(VAR)
Что делать дальше
После этого краткого инструктажа уже можно пробовать создавать простые мейкфайлы самостоятельно. Дальше надо читать серьезные учебники и руководства. Как финальный аккорд можно попробовать самостоятельно разобрать и осознать такой универсальный мейкфайл, который можно в два касания адаптировать под практически любой проект:
Источник
Makefile для чайников? Mac OS X
У меня возникли проблемы с пониманием ответа на предыдущий вопрос, который я задал здесь, в stackoverflow, расположенном по адресу:
Ответ, в частности, от пользователя: las3rjock, который предлагает создать «makefile» и запустить makefile для автоматизации компиляции .Rnw в R (Sweave), а затем в LaTeX. Я не знаю, что такое make файл или как его использовать в терминале (?) В Mac OS X. Мой интернет-поиск вернул вещи выше моего уровня знаний. Думаю, мне нужна рука, чтобы создать и запустить make файл.
Может ли кто-нибудь дать «dummy» инструкции о том, как создать make файл для запуска Sweave/LaTeX, или более того, использовать ответ от las3rjock в предыдущем вопросе? Спасибо!
- В Ross Ihaka есть краткое введение в make файлы, ориентированные на Sweave, R и LaTeX
Чарли Гиббонс написал документ LaTeX, Sweave и Beamer примеры make файлов
Грегор Gorjanc показывает пример Sweave make file, используя свою пользовательскую команду Sweave
Документация на GNU Make
Я также разместил четыре образца Sweave-документов.
Каждый из них имеет полный исходный код, доступный на github.
Каждый использует make файл.
В первых двух учебниках описываются детали make файла:
Файл makefile — это простой текстовый файл, который указывает, как сделать «цели» заданными набором «зависимостей» через кучу действий. Целями являются то, что слева от них: «после», зависимости находятся на остальной части этой линии. Последующие строки с отступом — это действия, которые должны сделать цель.
Так, например, в вашем случае LaTeX,.pdf файл является вашей конечной целью, и это зависит от вашего .tex файла. Итак, есть действие, которое запускает pdflatex. Сам .tex создается из файла .Rnw, поэтому существует другое правило, чтобы foo.tex из foo.Rnw запустил Sweave.
Красота make заключается в том, что он проверяет временные метки на зависимостях. Первоначально он предназначался для компиляции больших программ на C, разделенных на множество файлов. При компиляции окончательной программы нет смысла перекомпилировать все .C, которые вы не изменили, поэтому ваш Makefile просто скомпилировал измененные .C файлы и связал все файлы .o с исполняемым файлом.
Make был заимствован за рамки этой оригинальной схемы, до такой степени, что доступны новые автоматизированные решения сборки, поэтому вы можете увидеть упоминания cmake, ant, maven и множество других вещей. Я не знаю, что из них половина.
Источник
Автоматизируем работу с контейнерами через Makefile: сборка, тестирование и развёртывание за один вызов make
Утилита make позволяет просто управлять контейнерами, объединив команды для сборки, тестирования и развёртывания в одном конфигурационном файле.
Разработчики многие годы используют утилиту make. При запуске утилита читает файл с описанием проекта (Makefile) и, интерпретируя его содержимое, предпринимает необходимые действия. Файл с описанием проекта представляет собой текстовый конфигурационный файл, где описаны зависимости и команды, которые необходимо выполнить. Он похож на Dockerfile или другой файл конфигурации контейнера: там тоже указаны команды, на основе которых формируются образы для развёртывания контейнеров.
В этой статье я расскажу о том, как управлять контейнерами, используя Makefile. Контейнерный конфигурационный файл описывает образ контейнера, а Makefile описывает процесс сборки проекта, тестирование и развёртывание, а также другие полезные команды.
Цели и структура Makefile
Утилита make по умолчанию установлена в большинстве современных Linux-дистрибутивов, поэтому проблем с её использованием обычно не возникает. И чтобы начать её использовать, нужно создать файл с именем Makefile.
Makefile состоит из набора целей (target), зависимостей (dependency) и команд (commands), необходимых для их выполнения:
Цель — это некий желаемый результат, способ достижения которого описан в Makefile. Под целью подразумевают выполнение некого действия либо получение новой версии файла. А dependency — это некие «исходные данные», условия необходимые для достижения указанной цели. Зависимость может быть результатом выполнения другой цели, либо обычным файлом.
Команды вводят с использованием символа табуляции (пробелы не подойдут). Цель может не содержать команд, но при этом содержать зависимости.
Сначала проверяются и выполняются все зависимости по порядку, в случае завершения какой-либо команды из зависимости с ненулевой ошибкой, выполнение команды прерывается. Далее выполняются все команды перечисленные в самой цели, в случае завершения какой-либо команды с ненулевой ошибкой, выполнение цели прерывается.
Все команды выполняются в контексте текущей директории. Можно закомментировать команду поставив перед ней знак решётки (#).
Чтобы отправить цель на выполнение, нужно указать её название при вызове утилиты make:
В этом вся прелесть Makefile. Вы можете создать набор целей для каждой задачи. В контексте управления контейнерами, это создание образа и его отправка в реестр, тестирование и развёртывание контейнера, а также обновление его сервиса.
Я проиллюстрирую использование Makefile на примере своего личного веб-сайта и покажу как просто можно автоматизировать выполнение перечисленных задач.
Сборка, тестирование и развёртывание
Я создал простой веб-сайт с помощью Hugo, генератора статических сайтов. Он позволяет получить статический HTML из файлов YAML. В качестве веб-сервера я использовал Caddy.
Теперь посмотрим, как Makefile упростит сборку и развёртывание этого проекта на проде.
Первой целью в Makefile будет image_build:
Эта цель вызывает Podman для создания контейнера из его конфигурационного файла (Containerfile), включенного в проект. В приведённой выше команде есть несколько переменных. Переменные в Makefile можно использовать примерно так же, как в простых скриптовых языках программирования. В данном случае мне это нужно для создания «ссылки» на образ, который будет отправлен в удалённый реестр:
Используя эти переменные, цель image_build формирует идентификатор вида us.gcr.io/my-project-name/my-image-name:abc1234.
В конце, в качестве тега образа добавлен хэш соответствующей Git-ревизии.
В нашем случае образ будет помечен тегом :latest. Этот тег нам пригодится для очистки контейнера:
Итак, теперь контейнер создан, и его необходимо проверить, чтобы убедиться, что он соответствует некоторым минимальным требованиям. Для моего личного веб-сайта важно ответить на два вопроса:
- «Запускается ли веб-сервер?»
- «Что он возвращает?»
Это можно было сделать, выполняя shell-команды внутри Makefile. Но мне было проще написать на Python скрипт, который запускает контейнер с помощью Podman, отправляет HTTP-запрос в контейнер, проверяет, получает ли он ответ, а затем очищает его. Механизм обработки исключений в Python (try, except, finally) идеально подходит для этой задачи. И реализовать эту логику на Python значительно проще, чем на shell:
Проверку можно усложнить. Например, во время процесса сборки можно потребовать, чтобы в ответе содержался хэш Git-ревизии. И тогда мы сможем проверить, содержится ли ответ заданный хэш.
Если с тестами всё хорошо, образ готов к развёртыванию. Я использую для размещения своего веб-сайта сервис Google Cloud Run: скармливаю ему образ, и он мгновенно выдаёт мне URL. Как и многие подобные сервисы, с ним можно общаться через интерфейс командной строки (CLI). С Cloud Run развёртывание сводится к отправке образов (созданных локально) в удалённый реестр контейнеров и к запуску процесса самого развёртывания с помощью инструмента командной строки gcloud.
Теперь создадим цель push. Я используюPodman (но можно вместо него использовать Skopeo или тот же Docker).
После того, как образ будет отправлен, используйте команду gcloud run deploy, чтобы развернуть новейшую версию образа в проекте и «оживить» его.
Давайте снова создадим цель в Makefile. В этом файле я могу создать переменные для аргументов —platform и —region, чтобы мне не нужно было каждый раз запоминать их. Иначе мне пришлось бы вводить их из головы каждый раз, когда я развёртываю новый образ. А я так редко пишу для своего личного блога, что нет никаких шансов, что я запомню эти переменные.
Дополнительные цели
Локальный запуск
При тестировании CSS или других изменений кода мне хочется проверять работу проекта локально, без развёртывания на удалённом сервере. Для этого в мой Makefile я добавлю цель run_local. Она выбирает контейнер в соответствии с моим текущим коммитом и открывает в браузере URL-адрес страницы с локального веб-сервера.
В традиционных реализациях, у программы make нет надежного способа узнать, чем именно является цель. Ведь она может быть как именем действия, так и именем файла. Утилита make просто ищет на диске файл с именем, которое указано в качестве цели. Если такой файл существует, то цель считается именем файла.
Поэтому приходится явно объявлять цели абстрактными (то есть действиями). Для этого достаточно добавить ключевое слово .PHONY.
Создадим абстрактную цель run_local:
Я также использую переменную для имени браузера, поэтому при желании могу тестировать на разных браузерах. Когда я запускаю make run_local, по умолчанию сайт открывается в Firefox. Чтобы протестировать то же самое в Google Chrome, я должен модифицировать вызов make run_local, добавив BROWSER = ‘google-chrome’.
Очистка старых контейнеров и образов
При работе с контейнерами очистка старых образов — неприятная рутинная работа, особенно при частых итерациях. Поэтому добавим в Makefile цели для выполнения этих задач. Если контейнер не существует, Podman или Docker вернутся из процесса очистки с кодом 125. Но к сожалению, make ожидает, что каждая команда вернёт 0 (если всё ОК) или прекратит работу (если что-то не так). Поэтому придётся написать на bash вот такую обработку:
Для очистки образов требуется реализовать более сложную логику, но всё это можно сделать в Makefile. Чтобы сделать это легко, я добавлю метку (через Containerfile) к образу (на этапе его создания). Это позволяет легко найти все образы с заданными метками. Самые последние из них можно определить по тегу :latest. И теперь все образы, кроме самых последних (с тегом: latest), могут быть удалены:
Связанные одной целью
На данный момент мой Makefile включает команды для создания и маркировки образов, тестирования, отправки образов, развёртывания новых версий, очистки образов и запуска локальной версии. Выполнять каждую из них с помощью make image_build && make image_tag && make test и так далее значительно проще, чем выполнение каждой из команд, находящихся внутри этих вызовов. Но всё можно упростить ещё больше.
Я хочу, чтобы make в моём проекте по умолчанию делала всё — от создания образа до тестирования, развёртывания и очистки:
Makefile может запускать сразу несколько целей. Сгруппируем цели image_build и image_tag внутри одной цели build. Теперь, чтобы запустить их, нужно просто вызвать make build. Более того, цели build, test, deploy и clean ядополнительно сгруппирую в цель all, что позволит мне запускать их все (в указанной последовательности) за один вызов:
Не только контейнеры
C помощью Makefile можно объединить все команды, необходимые для сборки, тестирования и развёртывания проекта. Так можно упростить и автоматизировать огромное количество задач.
Но Makefile можно использовать и для задач, связанных с разработкой: запуск модульных тестов, компиляция двоичных файлов и формирование контрольных сумм.
Жаль, Makefile не может писать код за нас (подмигивает).
Купить VDS-хостинг с быстрыми NVMе-дисками и посуточной оплатой у хостинга Маклауд.
Источник