python.mk 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # The following python-related tools and processes are supported:
  2. # * black
  3. # * coverage (requires ".coveragerc")
  4. # * flake8
  5. # * pypi upload (requires "~/.pypirc")
  6. # * tox / unittests (requires "tox.ini")
  7. #
  8. PYTHON_BIN ?= python3
  9. DIR_PYTHON_BUILD ?= $(DIR_BUILD)/python-lib
  10. PYTHON_BDIST_ARGS = --dist-dir $(DIR_BUILD)/python-packages
  11. PYTHON_BUILD_ARGS = --build-base "$(DIR_PYTHON_BUILD)"
  12. PYTHON_INSTALL_ARGS = --root "$(abspath $(DESTDIR))"
  13. DISABLE_PYTHON_TESTS ?= 0
  14. DISABLE_PYTHON_LINT_FLAKE8 ?= 0
  15. ENABLE_PYTHON_LINT_BLACK ?= 0
  16. PYPI_UPLOAD_TARGET = https://pypi.python.org/pypi
  17. DIR_PYTHON_COVERAGE_HTML = $(DIR_BUILD)/coverage-html
  18. # detect if flake8 is available as an executable (e.g. Arch) or as a python module (e.g. Debian)
  19. FLAKE8_BIN ?= $(shell hash flake8 2>/dev/null && echo "flake8" || echo "$(PYTHON_BIN) -m flake8")
  20. FLAKE8_FILES ?= $(DIR_PYTHON_SETUP)
  21. RUN_PYTHON = $(PYTHON_BIN)
  22. BLACK_BIN ?= black
  23. # projects may want to override this with something like "--target-version=py39"
  24. BLACK_ARGS ?=
  25. BLACK_FILES ?= $(DIR_PYTHON_SETUP)
  26. PYTHON_TOX_FILE ?= $(DIR_PYTHON_SETUP)/tox.ini
  27. PYTHON_TEST_POETRY_COMMAND ?= pytest
  28. PYTHON_BUILD_SYSTEM ?= $(shell \
  29. if grep -q "^build-backend.*hatch" "$(PYTHON_PYPROJECT_FILE)"; then echo "hatchling"; \
  30. elif grep -q "^build-backend.*poetry" "$(PYTHON_PYPROJECT_FILE)"; then echo "poetry"; \
  31. elif [ -e "$(PYTHON_SETUP_FILE)" ]; then echo "setuptools"; \
  32. fi)
  33. PYTHON_TEST_RUNNER ?= $(shell \
  34. if [ -e "$(PYTHON_TOX_FILE)" ]; then echo "tox"; \
  35. elif [ -e manage.py ]; then echo "django"; \
  36. elif [ "$(PYTHON_BUILD_SYSTEM)" = "hatchling" ]; then echo "hatch"; \
  37. elif [ "$(PYTHON_BUILD_SYSTEM)" = "poetry" ]; then echo "poetry"; \
  38. else echo "generic"; \
  39. fi)
  40. PYTHON_LINT_RUNNER ?= $(shell \
  41. if [ "$(PYTHON_BUILD_SYSTEM)" = "hatchling" ]; then echo "hatch"; \
  42. else echo "generic"; \
  43. fi)
  44. PYTHON_STYLE_RUNNER ?= $(shell \
  45. if [ "$(PYTHON_BUILD_SYSTEM)" = "hatchling" ]; then echo "hatch"; \
  46. else echo "generic"; \
  47. fi)
  48. .PHONY: help
  49. help: help-python
  50. .PHONY: help-python
  51. help-python:
  52. @echo "Python-specific packaging targets:"
  53. @echo " clean-python"
  54. @echo " dist-python"
  55. @echo " lint-python"
  56. @echo " report-python"
  57. @echo " style-python"
  58. @echo " test-python"
  59. @echo " upload-python"
  60. @echo
  61. .PHONY: dist
  62. dist: dist-python
  63. .PHONY: install
  64. install: install-python
  65. .PHONY: lint
  66. lint: lint-python
  67. .PHONY: style
  68. style: style-python
  69. ifneq ($(DISABLE_PYTHON_TESTS),1)
  70. .PHONY: test
  71. test: test-python
  72. endif
  73. .PHONY: install-python
  74. install-python:
  75. cd "$(DIR_PYTHON_SETUP)" && $(RUN_PYTHON) setup.py install $(PYTHON_INSTALL_ARGS)
  76. .PHONY: dist-python
  77. dist-python: clean-python
  78. cd "$(DIR_PYTHON_SETUP)" \
  79. && $(RUN_PYTHON) setup.py build $(PYTHON_BUILD_ARGS) bdist $(PYTHON_BDIST_ARGS)
  80. .PHONY: check-pypi-config
  81. check-pypi-config:
  82. @# verify that the section for our internal target is not missing
  83. @if grep -q "^\[$(PYPI_UPLOAD_TARGET)\]$$" ~/.pypirc; then true; else \
  84. echo "ERROR: missing '$(PYPI_UPLOAD_TARGET)' section in ~/.pypirc: "\
  85. "take a look at pypirc.sample" >&2; \
  86. exit 1; \
  87. fi
  88. .PHONY: upload
  89. upload: upload-python
  90. .PHONY: upload-python
  91. upload-python: check-pypi-config
  92. cd "$(DIR_PYTHON_SETUP)" && $(RUN_PYTHON) setup.py sdist upload -r "$(PYPI_UPLOAD_TARGET)"
  93. .PHONY: lint-python
  94. ifeq ($(PYTHON_LINT_RUNNER),hatch)
  95. lint-python: lint-python-hatch
  96. else ifeq ($(PYTHON_LINT_RUNNER),generic)
  97. ifeq ($(ENABLE_PYTHON_LINT_BLACK),1)
  98. lint-python: lint-python-black
  99. endif
  100. ifneq ($(DISABLE_PYTHON_LINT_FLAKE8),1)
  101. lint-python: lint-python-flake8
  102. endif
  103. endif
  104. .PHONY: lint-python-black
  105. lint-python-black:
  106. $(BLACK_BIN) --check $(BLACK_ARGS) $(BLACK_FILES)
  107. .PHONY: lint-python-flake8
  108. lint-python-flake8:
  109. $(FLAKE8_BIN) $(FLAKE8_FILES)
  110. .PHONY: lint-python-hatch
  111. lint-python-hatch:
  112. hatch run lint:all
  113. .PHONY: style-python
  114. ifeq ($(PYTHON_STYLE_RUNNER),hatch)
  115. style-python: style-python-hatch
  116. else ifeq ($(PYTHON_STYLE_RUNNER),generic)
  117. style-python: style-python-black
  118. endif
  119. .PHONY: style-python-black
  120. style-python-black:
  121. $(BLACK_BIN) $(BLACK_ARGS) $(BLACK_FILES)
  122. .PHONY: style-python-hatch
  123. style-python-hatch:
  124. hatch run lint:fmt
  125. .PHONY: test-python
  126. ifeq ($(PYTHON_TEST_RUNNER),tox)
  127. test-python: test-python-tox
  128. else ifeq ($(PYTHON_TEST_RUNNER),django)
  129. test-python: test-python-django
  130. else ifeq ($(PYTHON_TEST_RUNNER),hatch)
  131. test-python: test-python-hatch
  132. else ifeq ($(PYTHON_TEST_RUNNER),poetry)
  133. test-python: test-python-poetry
  134. else ifeq ($(PYTHON_TEST_RUNNER),generic)
  135. test-python: test-python-generic
  136. endif
  137. .PHONY: test-python-tox
  138. test-python-tox:
  139. tox
  140. .PHONY: test-python-django
  141. test-python-django:
  142. $(RUN_PYTHON) manage.py test
  143. .PHONY: test-python-hatch
  144. test-python-hatch:
  145. hatch run test
  146. .PHONY: test-python-poetry
  147. test-python-poetry:
  148. poetry run $(PYTHON_TEST_POETRY_COMMAND)
  149. .PHONY: test-python-generic
  150. test-python-generic:
  151. $(RUN_PYTHON) $(PYTHON_TEST_ARGS)
  152. .PHONY: report
  153. report: report-python
  154. .PHONY: report-python
  155. report-python:
  156. if [ -e .coveragerc ]; then $(MAKE) report-python-coverage; fi
  157. .PHONY: report-python-coverage
  158. report-python-coverage:
  159. @# run tests for coverage analysis
  160. $(RUN_PYTHON) -m coverage run $(PYTHON_TEST_ARGS)
  161. @# provide textual report
  162. $(RUN_PYTHON) -m coverage report
  163. $(RUN_PYTHON) -m coverage html --directory "$(DIR_PYTHON_COVERAGE_HTML)"
  164. @printf "Browse the coverage report:\n\tfile://%s/index.html\n" \
  165. "$(abspath $(DIR_PYTHON_COVERAGE_HTML))"
  166. @# run a visual browser if the DISPLAY variable is set
  167. @if [ -n "$$DISPLAY" ] && [ -n "$(COVERAGE_BROWSER_BIN)" ]; then \
  168. "$(COVERAGE_BROWSER_BIN)" "$(DIR_PYTHON_COVERAGE_HTML)/index.html"; fi
  169. .PHONY: clean-python
  170. clean-python:
  171. cd "$(DIR_PYTHON_SETUP)" && python3 setup.py clean || true
  172. @# datafile for python3-coverage
  173. $(RM) .coverage
  174. @# workaround for https://github.com/pypa/setuptools/issues/436
  175. $(RM) "$(DIR_PYTHON_SETUP)"/*.egg-info/SOURCES.txt
  176. clean: clean-python