What is make test linux
Normally, when an error happens in executing a shell command, make gives up immediately, returning a nonzero status. No further recipes are executed for any target. The error implies that the goal cannot be correctly remade, and make reports this as soon as it knows.
When you are compiling a program that you have just changed, this is not what you want. Instead, you would rather that make try compiling every file that can be tried, to show you as many compilation errors as possible.
On these occasions, you should use the ‘ -k ’ or ‘ —keep-going ’ flag. This tells make to continue to consider the other prerequisites of the pending targets, remaking them if necessary, before it gives up and returns nonzero status. For example, after an error in compiling one object file, ‘ make -k ’ will continue compiling other object files even though it already knows that linking them will be impossible. In addition to continuing after failed shell commands, ‘ make -k ’ will continue as much as possible after discovering that it does not know how to make a target or prerequisite file. This will always cause an error message, but without ‘ -k ’, it is a fatal error (see Summary of Options).
The usual behavior of make assumes that your purpose is to get the goals up to date; once make learns that this is impossible, it might as well report the failure immediately. The ‘ -k ’ flag says that the real purpose is to test as much as possible of the changes made in the program, perhaps to find several independent problems so that you can correct them all before the next attempt to compile. This is why Emacs’ M-x compile command passes the ‘ -k ’ flag by default.
Что такое Makefile и как начать его использовать
В жизни многих разработчиков найдётся история про первый рабочий день с новым проектом. После клонирования основного репозитория проекта наступает этап, когда приходится вводить множество команд с определёнными флагами и в заданной последовательности. Без описания команд, в большинстве случаев, невозможно понять что происходит, например:
# Bash touch ~/.bash_history ufw allow 3035/tcp || echo 'cant configure ufw' ufw allow http || echo 'cant configure ufw' docker run \ -v /root/:/root/ \ -v /etc:/etc \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /var/tmp:/var/tmp \ -v /tmp:/tmp \ -v $PWD:/app \ --network host \ -w /app \ --env-file .env \ ansible ansible-playbook ansible/development.yml -i ansible/development --limit =localhost -vv grep -qxF 'fs.inotify.max_user_watches=524288' /etc/sysctl.conf || echo fs.inotify.max_user_watches =524288 | tee -a /etc/sysctl.conf || echo 'cant set max_user_watches' && sysctl -p sudo systemctl daemon-reload && sudo systemctl restart docker
Эти команды являются лишь частью того, что необходимо выполнить при разворачивании проекта. В приведённом примере видно, что команды сами по себе длинные, содержат много флагов, а значит, их трудно не только запомнить, но и вводить вручную. Постоянно вести документацию становится сложнее с ростом проекта, она неизбежно устаревает, а порог входа для новичков становится выше, ведь уже никто не в состоянии вспомнить всех деталей проекта. Некоторые такие команды необходимо использовать каждый день, и даже не один раз в день.
Со временем становится понятно, что нужен инструмент, способный объединить в себе подобные команды, предоставить к ним удобные шорткаты ( более короткие и простые команды) и обеспечить самодокументацию проекта. Именно таким инструментом стал Makefile и утилита make . Этот гайд расскажет, как использование этих инструментов позволит свести процесс разворачивания проекта к нескольким коротким и понятным командам:
# Bash make setup make start make test
Что такое make и Makefile
Makefile — это файл, который хранится вместе с кодом в репозитории. Его обычно помещают в корень проекта. Он выступает и как документация, и как исполняемый код. Мейкфайл скрывает за собой детали реализации и раскладывает «по полочкам» команды, а утилита make запускает их из того мейкфайла, который находится в текущей директории.
Изначально make предназначалась для автоматизации сборки исполняемых программ и библиотек из исходного кода. Она поставлялась по умолчанию в большинство *nix дистрибутивов, что и привело к её широкому распространению и повсеместному использованию. Позже оказалось что данный инструмент удобно использовать и при разработке любых других проектов, потому что процесс в большинстве своём сводится к тем же задачам — автоматизация и сборка приложений.
Применение мейка в проектах стало стандартом для многих разработчиков, включая крупные проекты. Примеры мейкфайла можно найти у таких проектов, как Kubernetes, Babel, Ansible и, конечно же, повсеместно на Хекслете.
Синтаксис Makefile
make запускает цели из Makefile, которые состоят из команд:
# Makefile цель1: # имя цели, поддерживается kebab-case и snake_case команда1 # для отступа используется табуляция, это важная деталь команда2 # команды будут выполняться последовательно и только в случае успеха предыдущей
Но недостаточно просто начать использовать мейкфайл в проекте. Чтобы получить эффект от его внедрения, понадобится поработать над разделением команд на цели, а целям дать семантически подходящие имена. Поначалу, перенос команд в Makefile может привести к свалке всех команд в одну цель с «размытым» названием:
# Makefile up: # разворачивание и запуск cp -n .env.example .env touch database/database.sqlite composer install npm install php artisan key:generate php artisan migrate --seed heroku local -f Procfile.dev # запуск проекта
Здесь происходит сразу несколько действий: создание файла с переменными окружения, подготовка базы данных, генерация ключей, установка зависимостей и запуск проекта. Это невозможно понять из комментариев и названия цели, поэтому будет правильно разделить эти независимые команды на разные цели:
# Makefile env-prepare: # создать .env-файл для секретов cp -n .env.example .env sqlite-prepare: # подготовить локальную БД touch database/database.sqlite install: # установить зависимости composer install npm install key: # сгенерировать ключи php artisan key:generate db-prepare: # загрузить данные в БД php artisan migrate --seed start: # запустить приложение heroku local -f Procfile.dev
Теперь, когда команды разбиты на цели, можно отдельно установить зависимости командой make install или запустить приложение через make start . Но остальные цели нужны только при первом разворачивании проекта и выполнять их нужно в определённой последовательности. Говоря языком мейкфайла, цель имеет пререквизиты:
# Makefile цель1: цель2 # такой синтаксис указывает на зависимость задач — цель1 зависит от цель2 команда2 # команда2 выполнится только в случае успеха команды из цель2 цель2: команда1
Задачи будут выполняться только в указанной последовательности и только в случае успеха предыдущей задачи. Значит, можно добавить цель setup , чтобы объединить в себе все необходимые действия:
# Makefile setup: env-prepare sqlite-prepare install key db-prepare # можно ссылаться на цели, описанные ниже env-prepare: cp -n .env.example .env sqlite-prepare: touch database/database.sqlite install: composer install npm install key: php artisan key:generate db-prepare: php artisan migrate --seed start: heroku local -f Procfile.dev
Теперь развернуть и запустить проект достаточно двумя командами:
# Bash make setup # выполнит последовательно: env-prepare sqlite-prepare install key db-prepare make start
Благодаря проделанной работе Makefile, команды проекта вместе с флагами сведены в Makefile. Он обеспечивает правильный порядок выполнения и не важно, какие при этом задействованы языки и технологии.
Продвинутое использование
Фальшивая цель
Использование make в проекте однажды может привести к появлению ошибки make: is up to date. , хотя всё написано правильно. Зачастую, её появление связано с наличием каталога или файла, совпадающего с именем цели. Например:
# Makefile test: # цель в мейкфайле php artisan test
# Bash $ ls Makefile test # в файловой системе находится каталог с именем, как у цели в мейкфайле $ make test # попытка запустить тесты make: `test` is up to date.
Как уже говорилось ранее, изначально make предназначалась для сборок из исходного кода. Поэтому она ищет каталог или файл с указанным именем, и пытается собрать из него проект. Чтобы изменить это поведение, необходимо в конце мейкфайла добавить .PHONY указатель на цель:
# Makefile test: php artisan test .PHONY: test
# Bash $ make test ✓ All tests passed !
Последовательный запуск команд и игнорирование ошибок
Запуск команд можно производить по одной: make setup , make start , make test или указывать цепочкой через пробел: make setup start test . Последний способ работает как зависимость между задачами, но без описания её в мейкфайле. Сложности могут возникнуть, если одна из команд возвращает ошибку, которую нужно игнорировать. В примерах ранее такой командой было создание .env-файла при разворачивании проекта:
# Makefile env-prepare: cp -n .env.example .env # если файл уже создан, то повторный запуск этой команды вернёт ошибку
Самый простой ( но не единственный) способ «заглушить» ошибку — это сделать логическое ИЛИ прямо в мейкфайле:
# Makefile env-prepare: cp -n .env.example .env || true # теперь любой исход выполнения команды будет считаться успешным
Добавлять такие хаки стоит с осторожностью, чтобы не «выстрелить себе в ногу» в более сложных случаях.
Переменные
Зачастую в команды подставляют параметры для конфигурации, указания путей, переменные окружения и make тоже позволяет этим управлять. Переменные можно прописать прямо в команде внутри мейкфайла и передавать их при вызове:
# Makefile say: echo "Hello, $(HELLO)!"
# Bash $ make say HELLO=World echo "Hello, World!" Hello, World ! $ make say HELLO=Kitty echo "Hello, Kitty!" Hello, Kitty !
Переменные могут быть необязательными и содержать значение по умолчанию. Обычно их объявляют в начале мейкфайла.
# Makefile HELLO ?=World # знак вопроса указывает, что переменная опциональна. Значение после присвоения можно не указывать. say: echo "Hello, $(HELLO)!"
# Bash $ make say echo "Hello, World!" Hello, World ! $ make say HELLO=Kitty echo "Hello, Kitty!" Hello, Kitty !
Некоторые переменные в Makefile имеют названия отличные от системных. Например, $PWD называется $CURDIR в мейкфайле:
# Makefile project-env-generate: docker run --rm -e RUNNER_PLAYBOOK =ansible/development.yml \ -v $(CURDIR)/ansible/development :/runner/inventory \ # $(CURDIR) - то же самое, что $PWD в терминале -v $(CURDIR) :/runner/project \ ansible/ansible-runner
Заключение
В рамках данного гайда было рассказано об основных возможностях Makefile и утилиты make . Более плотное знакомство с данным инструментом откроет множество других его полезных возможностей: условия, циклы, подключение файлов. В компаниях, где имеется множество проектов, написанных разными командами в разное время, мейкфайл станет отличным подспорьем в стандартизации типовых команд: setup start test deploy . .
Возможность описывать в мейкфале последовательно многострочные команды позволяет использовать его как «универсальный клей» между менеджерами языков и другими утилитами. Широкая распространённость этого инструмента и общая простота позволяют внедрить его в свой проект достаточно легко, без необходимости доработок. Но мейкфайл может быть по-настоящему большим и сложным, это можно увидеть на примере реальных проектов:
Дополнительные материалы
Мейкфайлы, использованные при составлении гайда: