Makefile в python-проектах

Содержание

Что такое makefile и для чего его обычно используют

Из одной очень умной статьи:

Makefile предоставляет простой способ организации процесса компиляции кода.

Обычно makefile используются при написании программ на C, для автоматизации операций, которые необходимо выполнить перед компиляцией кода. В этих файлах описываются правила (шаги), которые необходимо выполнить, чтобы скомпилировать программу. Самый простой makefile для C-программы выглядит так:

helloword: helloword.c
    gcc -o hellword hellword.c -I.

После этого, выполнив из командной строки

$ make helloword

можно запустить компиляцию C-кода при помощи gcc.

Возникает справедливый вопрос: как все это связано с python-ом? Ведь это интерпретируемый язык программирования, а прекомпиляция происходит невидимо для пользователя в момент первого запуска программы, так что использование makefile кажется бессмысленным. Однако и тут makefile может облегчить жизнь и упростить выполнение рутинных операций.

Специфическое для python проектов использование makefile

Возникала ли у вас когда-либо необходимость очистить проект от временных .pyc файлов или удалить промежуточные результаты сборки python-пакетов? Или, возможно, вам нужно запустить тестирование с учетом покрытия кода тестами? Проверить код при помощи pep8, lint или isort? А как вам запуск приложения в docker-контейнере с необходимостью каждый раз вспоминать команду, которая даже не помещается полностью на экране монитора?

You can have everything kept in one place and use only make clean to clean up unnecessary files or make tests to test your application. Вот тут-то makefile приходит на помощь, предоставляя возможностьб держать все команды в одном месте и упрощая их запуск: make clean для очистки проекта от временных файлов или make tests для запуска тестирования.

Давайте рассмотрим makefile из одного реального python-проекта.

HOST=127.0.0.1
TEST_PATH=./

clean-pyc:
    find . -name '*.pyc' -exec rm --force {} +
    find . -name '*.pyo' -exec rm --force {} +
    find . -name '*~' -exec rm --force  {} +

clean-build:
    rm --force --recursive build/
    rm --force --recursive dist/
    rm --force --recursive *.egg-info

isort:
    sh -c "isort --skip-glob=.tox --recursive . "

lint:
    flake8 --exclude=.tox

test: clean-pyc
    py.test --verbose --color=yes $(TEST_PATH)

run:
    python manage.py runserver

docker-run:
    docker build \
      --file=./Dockerfile \
      --tag=my_project ./
    docker run \
      --detach=false \
      --name=my_project \
      --publish=$(HOST):8080 \
      my_project

В начале файла определяются переменные HOST и TEST PATH, которые доступны для любой из определенных в файле команд. Команда clean-pyc находит все *.pyc, *.pyo или *~ файлы и удаляет их. Знак + в конце команды относится к -exec command {} и означает, что команда rm буде вызвана один раз для всей группы файлов, а не индивидуально для каждого.

Следующее определение, clean-build, используется для удаления промежуточных результатов сборки пакета. В isort выполняется соответствующая консольная команда с заданными атрибутами, -c указывает на необходимость считать данные из строки, а не из стандартного потока ввода. lint работает аналогично. В test добавлено дополнительное правило, которое должно выполняться перед запуском тестов: clean-pyc. И в самом конце, docker-run создает и запускает docker-контейнер.

file called clean-pyc it will try to use it instead of a command. To avoid this use PHONY at the beginning of your makefile: Помимо этого, стоит отметить обращение к PHONY. По-умолчанию, makefile работает с файлами, поэтому, если в рабочей директории будет находиться файл clean-pyc, то он будет использован, вместо выполнения команды с таким же названием. Чтобы избежать такого поведения, укажите PHONY в начале файла:

.PHONY: clean-pyc clean-build

I also like to have help function for my makefile so I put this somewhere inside: Так же, неплохо иметь help-функцию в makefile:

help:
    @echo "    clean-pyc"
    @echo "        Удаляет временные файлы python."
    @echo "    clean-build"
    @echo "        Удаляет промежуточные файлы сборки проекта."
    @echo "    isort"
    @echo "        Сортирует import выражения."
    @echo "    lint"
    @echo "        Проверка кода с flake8."
    @echo "    test"
    @echo "        Запуск py.test"
    @echo "    run"
    @echo "        Запуск сервиса локально."
    @echo "    docker-run"
    @echo "        Создание и запуск сервиса в docker-контейнере."

Вы, наверно, обратили внимание, что перед каждым вызовом echo стоит знак @. Дело в том, что make по-умолчанию печатает каждую строку, перед тем, как выполнить её. Знак @ отключает такое поведение.

А что, если понадобится запустить при помощи команды make приложение используя другой ip-адрес или порт? Все просто, нужно лишь изменить команду запуска сервиса следующим образом:

run:
    python manage.py runserver --host $(HOST) --port $(PORT)

После чего, выполните из командной строки:

$ make run HOST=127.0.0.1 PORT=8000

Ну и в конце, необходимо отметить, что все отступы в makefile должны быть выполнены при помощи tab-ов, а не пробелов.

Преимущества применения

Как можно заметить, использование makefile в python-проектах дает определенные преимущества. Если вам надоело постоянно набирать в командной строке одни и те же команды, создайте для каждой из них отдельное правило в makefile: очистка временных файлов, запуск тестов, запуск сервиса... перечислять можно долго.

Оригинальная статья на английском