python/cliff: update to 4.8.0
[oi-userland.git] / make-rules / setup.py.mk
blobcfa0670590904997806ce81bf61d8b6adfa25202
2 # CDDL HEADER START
4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
19 # CDDL HEADER END
21 # Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
25 # The Python build infrastructure in setup.py.mk and pyproject.mk files uses
26 # several Python projects to work properly. Since we cannot use these projects
27 # until they are actually built and installed we need to bootstrap.
29 # We do have several sequential bootstrap checkpoints during the process:
31 # (0) Nothing works yet.
33 # Just core Python runtime is available (with no additional projects).
34 # While here almost nothing works. We cannot do following tasks with
35 # regular Python projects:
36 # - detect their requirements,
37 # - build and publish them,
38 # - test them.
40 # (1) The bootstrapper is ready.
42 # The bootstrapper is special tool that requires just core Python with no
43 # dependency on other Python projects and it is able to build and publish
44 # itself and other Python projects.
46 # For projects using the 'setup.py' build style we do not need any
47 # special bootstrapper because such projects are built using their own
48 # 'setup.py' script. The only issue with the 'setup.py' build style
49 # projects is that their 'setup.py' script usually depends on some other
50 # projects (typically setuptools) to get successfully built.
52 # For 'pyproject'-style projects we use pyproject_installer as the
53 # bootstrapper.
55 # To achieve this checkpoint we just need to build pyproject_installer
56 # using pyproject_installer without detecting its requirements (they are
57 # none anyway) and without testing it (since no testing infrastructure is
58 # ready yet).
60 # (2) The python-requires script works.
62 # Once the python-requires script works we can start to detect runtime
63 # dependencie of other Python projects automatically.
65 # To achieve this checkpoint we need to build the packaging project
66 # (directly needed by the python-requires script) and all projects
67 # required by packaging. During this all projects' dependencies needs to
68 # be manually evaluated to make sure they are correct.
70 # (3) The build infrastructure is fully working.
72 # Once we are here we can build any Python project, but we cannot test it
73 # yet.
75 # For projects using the 'setup.py' build style we do not need any
76 # special build infrastructure. See checkpoint (1) above for detialed
77 # discussion about 'setup.py' build style projects.
79 # For 'pyproject'-style projects we need to build both 'build' and
80 # 'installer' projects and all projects they depends on.
82 # (4) The testing infrastructure is fully working.
84 # Once we are here we can finally use all features of the Python build
85 # framework. Including testing.
87 # To achieve this we need to build tox, tox-current-env, and pytest
88 # projects together with their dependencies.
90 # All projects needed to achieve checkpoints (1), (2), and (3) should set
91 # PYTHON_BOOTSTRAP to 'yes' in their Makefile to make sure the regular build
92 # infrastructure is not used for them and special set of build rules is applied
93 # instead.
95 # All projects needed to go from checkpoint (3) to checkpoint (4) should set
96 # PYTHON_TEST_BOOTSTRAP to 'yes' in their Makefile to let the build
97 # infrastructure know that testing for such projects might not work properly.
99 # The PYTHON_BOOTSTRAP set to 'yes' implies PYTHON_TEST_BOOTSTRAP set to 'yes'
100 # too.
102 ifeq ($(strip $(PYTHON_BOOTSTRAP)),yes)
103 PYTHON_TEST_BOOTSTRAP = yes
104 endif
107 # Lists of Python projects needed to achieve particular bootstrap checkpoint.
108 # Indentation shows project dependencies (e.g. packaging requires flit_core).
110 PYTHON_BOOTSTRAP_CHECKPOINT_1 += pyproject_installer
112 PYTHON_BOOTSTRAP_CHECKPOINT_2 += $(PYTHON_BOOTSTRAP_CHECKPOINT_1)
113 PYTHON_BOOTSTRAP_CHECKPOINT_2 += packaging
114 PYTHON_BOOTSTRAP_CHECKPOINT_2 += flit_core
116 # Particular python runtime is always required (at least to run setup.py)
117 PYTHON_REQUIRED_PACKAGES += runtime/python
119 define python-rule
120 $(BUILD_DIR)/%-$(1)/.built: PYTHON_VERSION=$(1)
121 $(BUILD_DIR)/%-$(1)/.installed: PYTHON_VERSION=$(1)
122 $(BUILD_DIR)/%-$(1)/.tested: PYTHON_VERSION=$(1)
123 $(BUILD_DIR)/%-$(1)/.tested-and-compared: PYTHON_VERSION=$(1)
124 endef
126 $(foreach pyver, $(PYTHON_VERSIONS), $(eval $(call python-rule,$(pyver))))
128 $(BUILD_DIR)/$(MACH32)-%/.built: BITS=32
129 $(BUILD_DIR)/$(MACH64)-%/.built: BITS=64
130 $(BUILD_DIR)/$(MACH32)-%/.installed: BITS=32
131 $(BUILD_DIR)/$(MACH64)-%/.installed: BITS=64
132 $(BUILD_DIR)/$(MACH32)-%/.tested: BITS=32
133 $(BUILD_DIR)/$(MACH64)-%/.tested: BITS=64
134 $(BUILD_DIR)/$(MACH32)-%/.tested-and-compared: BITS=32
135 $(BUILD_DIR)/$(MACH64)-%/.tested-and-compared: BITS=64
137 PYTHON_32_VERSIONS = $(filter-out $(PYTHON_64_ONLY_VERSIONS), $(PYTHON_VERSIONS))
139 BUILD_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.built)
140 BUILD_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.built)
141 BUILD_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.built)
143 ifeq ($(filter-out $(PYTHON_64_ONLY_VERSIONS), $(PYTHON_VERSION)),)
144 BUILD_32_and_64 = $(BUILD_64)
145 endif
147 INSTALL_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.installed)
148 INSTALL_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.installed)
149 INSTALL_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.installed)
151 PYTHON_ENV = CC="$(CC)"
152 PYTHON_ENV += CFLAGS="$(CFLAGS)"
153 PYTHON_ENV += CXX="$(CXX)"
154 PYTHON_ENV += CXXFLAGS="$(CXXFLAGS)"
155 PYTHON_ENV += LDFLAGS="$(LDFLAGS)"
156 PYTHON_ENV += PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)"
158 COMPONENT_BUILD_ENV += $(PYTHON_ENV)
159 COMPONENT_INSTALL_ENV += $(PYTHON_ENV)
160 COMPONENT_TEST_ENV += $(PYTHON_ENV)
162 # Set CARGO_HOME to make sure projects built using rust (for example via
163 # setuptools-rust) do not pollute user's home directory with cargo bits.
164 COMPONENT_BUILD_ENV += CARGO_HOME=$(@D)/.cargo
166 # Make sure the default Python version is installed last and so is the
167 # canonical version. This is needed for components that keep PYTHON_VERSIONS
168 # set to more than single value, but deliver unversioned binaries in usr/bin or
169 # other overlapping files.
170 define python-order-rule
171 $(BUILD_DIR)/%-$(PYTHON_VERSION)/.installed: $(BUILD_DIR)/%-$(1)/.installed
172 endef
173 $(foreach pyver,$(filter-out $(PYTHON_VERSION),$(PYTHON_VERSIONS)),$(eval $(call python-order-rule,$(pyver))))
175 # We need to copy the source dir to avoid its modification by install target
176 # where egg-info is re-generated
177 CLONEY_ARGS = CLONEY_MODE="copy"
179 COMPONENT_BUILD_CMD = $(PYTHON) setup.py --no-user-cfg build $(COMPONENT_BUILD_SETUP_PY_ARGS)
182 COMPONENT_INSTALL_CMD = $(PYTHON) setup.py --no-user-cfg install
184 COMPONENT_INSTALL_ARGS += --root $(PROTO_DIR)
185 COMPONENT_INSTALL_ARGS += --install-lib=$(PYTHON_LIB)
186 COMPONENT_INSTALL_ARGS += --install-data=$(PYTHON_DATA)
187 COMPONENT_INSTALL_ARGS += --skip-build
188 COMPONENT_INSTALL_ARGS += --force
190 ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no)
191 # Rename binaries in /usr/bin to contain version number
192 COMPONENT_POST_INSTALL_ACTION += \
193 for f in $(PROTOUSRBINDIR)/* ; do \
194 [ -f $$f ] || continue ; \
195 for v in $(PYTHON_VERSIONS) ; do \
196 [ "$$f" == "$${f%%$$v}" ] || continue 2 ; \
197 done ; \
198 $(MV) $$f $$f-$(PYTHON_VERSION) ; \
199 done ;
200 endif
202 # Remove any previous dependency files
203 COMPONENT_POST_INSTALL_ACTION += $(RM) $(@D)/.depend-runtime $(@D)/.depend-test ;
205 # Define Python version specific filenames for tests.
206 ifeq ($(strip $(USE_COMMON_TEST_MASTER)),no)
207 COMPONENT_TEST_MASTER = $(COMPONENT_TEST_RESULTS_DIR)/results-$(PYTHON_VERSION).master
208 endif
209 COMPONENT_TEST_BUILD_DIR = $(BUILD_DIR)/test-$(PYTHON_VERSION)
210 COMPONENT_TEST_OUTPUT = $(COMPONENT_TEST_BUILD_DIR)/test-$(PYTHON_VERSION)-results
211 COMPONENT_TEST_DIFFS = $(COMPONENT_TEST_BUILD_DIR)/test-$(PYTHON_VERSION)-diffs
212 COMPONENT_TEST_SNAPSHOT = $(COMPONENT_TEST_BUILD_DIR)/results-$(PYTHON_VERSION).snapshot
213 COMPONENT_TEST_TRANSFORM_CMD = $(COMPONENT_TEST_BUILD_DIR)/transform-$(PYTHON_VERSION)-results
215 # Generic transforms for Python test results.
216 # See below for test style specific transforms.
217 COMPONENT_TEST_TRANSFORMS += "-e 's|$(PYTHON_DIR)|\$$(PYTHON_DIR)|g'"
219 # Testing depends on install target because we want to test installed modules
220 COMPONENT_TEST_DEP += $(BUILD_DIR)/%/.installed
221 # Point Python to the proto area so it is able to find installed modules there
222 COMPONENT_TEST_ENV += PYTHONPATH=$(PROTO_DIR)/$(PYTHON_LIB)
223 # Make sure testing is able to find own installed executables (if any)
224 COMPONENT_TEST_ENV += PATH=$(PROTOUSRBINDIR):$(PATH)
226 # determine the type of tests we want to run.
227 ifeq ($(strip $(wildcard $(COMPONENT_TEST_RESULTS_DIR)/results-*.master)),)
228 TEST_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.tested)
229 TEST_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.tested)
230 TEST_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.tested)
231 else
232 TEST_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.tested-and-compared)
233 TEST_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.tested-and-compared)
234 TEST_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.tested-and-compared)
235 endif
238 # Testing in the Python world is complex. Python projects usually do not
239 # support Makefile with common 'check' or 'test' target to get built bits
240 # tested.
242 # De facto standard way to test Python projects these days is tox which is
243 # designed and used primarily for release testing; to make sure the released
244 # python project runs on all supported Python versions, platforms, etc. tox
245 # does so using virtualenv and creates isolated test environments where the
246 # tested package together with all its dependencies is automatically installed
247 # (using pip) and tested. This is great for Python projects developers but it
248 # is hardly usable for operating system distributions like OpenIndiana.
250 # We do not need such release testing. Instead we need something closer to
251 # integration testing: we need to test the built component in our real
252 # environment without automatic installation of any dependencies using pip. In
253 # addition, we need to run tests only for Python versions we actually support
254 # and the component is built for.
256 # To achieve that we do few things. First, to avoid isolated environments
257 # (virtualenv) we run tox with the tox-current-env plugin. Second, to test
258 # only Python versions we are interested in we use -e option for tox to select
259 # single Python version only. Since we run separate test target per Python
260 # version this will make sure we test all needed Python versions.
262 # The tox tool itself uses some other tools under the hood to run tests, for
263 # example pytest. Some projects could even support pytest testing directly
264 # without support for tox. For such projects we offer separate "pytest"-style
265 # testing.
267 # For projects that do not support testing using neither tox nor pytest we
268 # offer either unittest or (deprecated) "setup.py test" testing too.
270 # The TEST_STYLE variable is used to select (or force) particular test style
271 # for Python projects. Valid values are:
273 # tox - "tox"-style testing
274 # pytest - "pytest"-style testing
275 # unittest - "unittest"-style testing
276 # setup.py - "setup.py test"-style testing
277 # none - no testing is supported (or desired) at all
280 TEST_STYLE ?= tox
281 ifeq ($(strip $(TEST_STYLE)),tox)
282 # tox needs PATH environment variable - see https://github.com/tox-dev/tox/issues/2538
283 # We already added it to the test environment - see above
284 COMPONENT_TEST_ENV += PYTEST_ADDOPTS="$(PYTEST_ADDOPTS)"
285 COMPONENT_TEST_ENV += NOSE_VERBOSE=2
286 COMPONENT_TEST_CMD = $(TOX)
287 COMPONENT_TEST_ARGS = --current-env --no-provision
288 COMPONENT_TEST_ARGS += --recreate
289 COMPONENT_TEST_ARGS += $(TOX_TESTENV)
290 COMPONENT_TEST_TARGETS = $(if $(strip $(TOX_POSARGS)),-- $(TOX_POSARGS))
292 TOX_TESTENV = -e py$(subst .,,$(PYTHON_VERSION))
294 # Make sure following tools are called indirectly to properly support tox-current-env
295 TOX_CALL_INDIRECTLY += py.test
296 TOX_CALL_INDIRECTLY += pytest
297 TOX_CALL_INDIRECTLY += coverage
298 TOX_CALL_INDIRECTLY += zope-testrunner
299 TOX_CALL_INDIRECTLY.zope-testrunner = zope.testrunner
300 TOX_CALL_INDIRECTLY += sphinx-build
301 TOX_CALL_INDIRECTLY.sphinx-build = sphinx.cmd.build
302 TOX_CALL_INDIRECTLY += nosetests
303 TOX_CALL_INDIRECTLY.nosetests = nose
304 $(foreach indirectly, $(TOX_CALL_INDIRECTLY), $(eval TOX_CALL_INDIRECTLY.$(indirectly) ?= $(indirectly)))
305 COMPONENT_PRE_TEST_ACTION += COMPONENT_TEST_DIR=$(COMPONENT_TEST_DIR) ;
306 COMPONENT_PRE_TEST_ACTION += \
307 $(foreach indirectly, $(TOX_CALL_INDIRECTLY), \
308 [ -f $$COMPONENT_TEST_DIR/tox.ini ] && \
309 $(GSED) -i -e '/^commands *=/,/^$$/{ \
310 s/^\(\(commands *=\)\{0,1\}[ \t]*\)'$(indirectly)'\([ \t]\{1,\}.*\)\{0,1\}$$/\1python -m '$(TOX_CALL_INDIRECTLY.$(indirectly))'\3/ \
311 }' $$COMPONENT_TEST_DIR/tox.ini ; \
313 COMPONENT_PRE_TEST_ACTION += true ;
315 # Normalize tox test results.
316 COMPONENT_TEST_TRANSFORMS += "-e 's/py$(subst .,,$(PYTHON_VERSION))/py\$$(PYV)/g'" # normalize PYV
317 COMPONENT_TEST_TRANSFORMS += "-e '/^py\$$(PYV) installed:/d'" # depends on set of installed packages
318 COMPONENT_TEST_TRANSFORMS += "-e '/PYTHONHASHSEED/d'" # this is random
320 # Normalize zope.testrunner test results
321 COMPONENT_TEST_TRANSFORMS += \
322 "-e 's/ in \([0-9]\{1,\} minutes \)\{0,1\}[0-9]\{1,\}\.[0-9]\{3\} seconds//'" # timing
324 # Remove timing for tox 4 test results
325 COMPONENT_TEST_TRANSFORMS += "-e 's/^\( py\$$(PYV): OK\) (.* seconds)$$/\1/'"
326 COMPONENT_TEST_TRANSFORMS += "-e 's/^\( congratulations :)\) (.* seconds)$$/\1/'"
328 # Remove useless lines from the "coverage combine" output
329 COMPONENT_TEST_TRANSFORMS += "-e '/^Combined data file .*\.coverage/d'"
330 COMPONENT_TEST_TRANSFORMS += "-e '/^Skipping duplicate data .*\.coverage/d'"
332 # sort list of Sphinx doctest results
333 COMPONENT_TEST_TRANSFORMS += \
334 "| ( \
335 $(GSED) -u -e '/^running tests\.\.\.$$/q' ; \
336 $(GSED) -u -e '/^Doctest summary/Q' \
337 | $(NAWK) '/^$$/{\$$0=\"\\\\n\"}1' ORS='|' \
338 | $(GNU_GREP) -v '^|$$' \
339 | $(SORT) \
340 | tr -d '\\\\n' | tr '|' '\\\\n' \
341 | $(NAWK) '{print}END{if(NR>0)printf(\"\\\\nDoctest summary\\\\n\")}' ; \
342 $(CAT) \
343 ) | $(COMPONENT_TEST_TRANSFORMER)"
345 # tox package together with the tox-current-env plugin is needed
346 USERLAND_TEST_REQUIRED_PACKAGES += library/python/tox
347 USERLAND_TEST_REQUIRED_PACKAGES += library/python/tox-current-env
349 # Generate raw lists of test dependencies per Python version
350 # Please note we set PATH below five times for tox to workaround
351 # https://github.com/tox-dev/tox/issues/2538
352 COMPONENT_POST_INSTALL_ACTION += \
353 if [ -x "$(TOX)" ] ; then \
354 cd $(@D)$(COMPONENT_SUBDIR:%=/%) ; \
355 echo "Testing dependencies:" ; \
356 PATH=$(PATH) PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
357 $(TOX) -qq --no-provision --print-deps-to=- $(TOX_TESTENV) || exit 1 ; \
358 echo "Testing extras:" ; \
359 PATH=$(PATH) PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
360 $(TOX) -qq --no-provision --print-extras-to=- $(TOX_TESTENV) || exit 1 ; \
361 echo "Testing dependency groups:" ; \
362 PATH=$(PATH) PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
363 $(TOX) -qq --no-provision --print-dependency-groups-to=- $(TOX_TESTENV) || exit 1 ; \
364 ( PATH=$(PATH) PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
365 $(TOX) -qq --no-provision --print-deps-to=- $(TOX_TESTENV) \
366 | $(WS_TOOLS)/python-resolve-deps \
367 PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
368 $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) \
369 | $(PYTHON) $(WS_TOOLS)/python-requires - ; \
370 for e in $$(PATH=$(PATH) PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
371 $(TOX) -qq --no-provision --print-extras-to=- $(TOX_TESTENV)) ; do \
372 PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
373 $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) $$e ; \
374 done \
375 ) | $(GSED) -e '/^tox\(-current-env\)\?$$/d' >> $(@D)/.depend-test ; \
376 fi ;
377 else ifeq ($(strip $(TEST_STYLE)),pytest)
378 COMPONENT_TEST_CMD = $(PYTHON) -m pytest
379 COMPONENT_TEST_ARGS = $(PYTEST_ADDOPTS)
380 COMPONENT_TEST_TARGETS =
382 USERLAND_TEST_REQUIRED_PACKAGES += library/python/pytest
383 else ifeq ($(strip $(TEST_STYLE)),unittest)
384 COMPONENT_TEST_CMD = $(PYTHON) -m unittest
385 COMPONENT_TEST_ARGS =
386 COMPONENT_TEST_ARGS += --verbose
387 COMPONENT_TEST_TARGETS =
388 else ifeq ($(strip $(TEST_STYLE)),setup.py)
389 # Old and deprecated "setup.py test"-style testing
390 COMPONENT_TEST_CMD = $(PYTHON) setup.py
391 COMPONENT_TEST_ARGS = --no-user-cfg
392 COMPONENT_TEST_TARGETS = test
393 else ifeq ($(strip $(TEST_STYLE)),none)
394 TEST_TARGET = $(NO_TESTS)
395 endif
397 # Run pytest verbose to get separate line per test in results output
398 PYTEST_ADDOPTS += --verbose
400 # Force pytest to not use colored output so the results normalization is unaffected
401 PYTEST_ADDOPTS += --color=no
403 # Create list of required pytest plugins.
404 define pytest-plugin
405 PYTEST_PLUGINS += $$(if $$(filter library/python/$(1)-$$(subst .,,$$(PYTHON_VERSION)), $$(REQUIRED_PACKAGES) $$(TEST_REQUIRED_PACKAGES) $$(COMPONENT_FMRI)-$$(subst .,,$$(PYTHON_VERSION))),$(2))
406 endef
407 $(eval $(call pytest-plugin,anyio,anyio))
408 $(eval $(call pytest-plugin,betamax,pytest-betamax))
409 $(eval $(call pytest-plugin,faker,faker))
410 $(eval $(call pytest-plugin,flaky,flaky))
411 $(eval $(call pytest-plugin,hypothesis,hypothesispytest))
412 $(eval $(call pytest-plugin,inline-snapshot,inline_snapshot))
413 $(eval $(call pytest-plugin,jaraco-test,jaraco.test.http))
414 $(eval $(call pytest-plugin,jaraco-vcs,jaraco.vcs.fixtures))
415 $(eval $(call pytest-plugin,kgb,kgb))
416 $(eval $(call pytest-plugin,pyfakefs,pytest_fakefs))
417 $(eval $(call pytest-plugin,pytest-asyncio,asyncio))
418 $(eval $(call pytest-plugin,pytest-benchmark,benchmark))
419 $(eval $(call pytest-plugin,pytest-black,black))
420 $(eval $(call pytest-plugin,pytest-check,check))
421 $(eval $(call pytest-plugin,pytest-checkdocs,checkdocs))
422 $(eval $(call pytest-plugin,pytest-console-scripts,console-scripts))
423 $(eval $(call pytest-plugin,pytest-cov,pytest_cov))
424 $(eval $(call pytest-plugin,pytest-custom-exit-code,custom_exit_code))
425 $(eval $(call pytest-plugin,pytest-datadir,pytest-datadir))
426 $(eval $(call pytest-plugin,pytest-enabler,enabler))
427 $(eval $(call pytest-plugin,pytest-env,env))
428 $(eval $(call pytest-plugin,pytest-expect,pytest_expect))
429 $(eval $(call pytest-plugin,pytest-flake8,flake8))
430 $(eval $(call pytest-plugin,pytest-forked,pytest_forked))
431 $(eval $(call pytest-plugin,pytest-freezer,freezer))
432 $(eval $(call pytest-plugin,pytest-helpers-namespace,helpers_namespace))
433 $(eval $(call pytest-plugin,pytest-home,home))
434 $(eval $(call pytest-plugin,pytest-httpserver,pytest_httpserver))
435 $(eval $(call pytest-plugin,pytest-ignore-flaky,pytest_ignore_flaky))
436 $(eval $(call pytest-plugin,pytest-lazy-fixtures,pytest_lazyfixture))
437 $(eval $(call pytest-plugin,pytest-metadata,metadata))
438 $(eval $(call pytest-plugin,pytest-mock,pytest_mock))
439 $(eval $(call pytest-plugin,pytest-mypy,mypy))
440 $(eval $(call pytest-plugin,pytest-mypy-plugins,pytest-mypy-plugins))
441 $(eval $(call pytest-plugin,pytest-perf,perf))
442 $(eval $(call pytest-plugin,pytest-randomly,randomly))
443 $(eval $(call pytest-plugin,pytest-regressions,regressions))
444 $(eval $(call pytest-plugin,pytest-relaxed,relaxed))
445 $(eval $(call pytest-plugin,pytest-reporter,reporter))
446 $(eval $(call pytest-plugin,pytest-rerunfailures,rerunfailures))
447 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories))
448 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-event-listener))
449 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-factories))
450 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-loader-mock))
451 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-log-server))
452 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-markers))
453 $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-sysinfo))
454 $(eval $(call pytest-plugin,pytest-shell-utilities,shell-utilities))
455 $(eval $(call pytest-plugin,pytest-skip-markers,skip-markers))
456 $(eval $(call pytest-plugin,pytest-socket,socket))
457 $(eval $(call pytest-plugin,pytest-subprocess,pytest-subprocess))
458 $(eval $(call pytest-plugin,pytest-subtests,subtests))
459 $(eval $(call pytest-plugin,pytest-system-statistics,system-statistics))
460 $(eval $(call pytest-plugin,pytest-timeout,timeout))
461 $(eval $(call pytest-plugin,pytest-travis-fold,travis-fold))
462 $(eval $(call pytest-plugin,pytest-xdist,xdist))
463 $(eval $(call pytest-plugin,pytest-xdist,xdist.looponfail))
464 $(eval $(call pytest-plugin,pytest-xprocess,xprocess))
465 $(eval $(call pytest-plugin,teamcity-messages,pytest-teamcity))
466 $(eval $(call pytest-plugin,time-machine,time_machine))
467 $(eval $(call pytest-plugin,typeguard,typeguard))
469 # Transitional (indirect) runtime dependencies of pytest plugins.
471 # Note: The list is not exhaustive and contians only entries that proved to be
472 # needed or useful.
474 # pytest-datadir is required by pytest-regressions and pytest-regressions is required by coincidence
475 $(eval $(call pytest-plugin,coincidence,regressions))
476 $(eval $(call pytest-plugin,coincidence,pytest-datadir))
478 # By default disable all pytest plugins ...
479 COMPONENT_TEST_ENV += PYTEST_DISABLE_PLUGIN_AUTOLOAD=1
480 # ... and load those in the PYTEST_PLUGINS list only.
481 # $(sort) is used to avoid duplicates and to strip spaces.
482 COMPONENT_TEST_ENV += PYTEST_PLUGINS="$(subst $(space),$(comma),$(sort $(PYTEST_PLUGINS)))"
484 # By default we are not interested in full list of test failures so exit on
485 # first failure to save time. This could be easily overridden from environment
486 # if needed (for example to debug test failures) or in per-component Makefile.
487 PYTEST_FASTFAIL = -x
488 PYTEST_ADDOPTS += $(PYTEST_FASTFAIL)
490 # By default we are not interested to see the default long tracebacks.
491 # Detailed tracebacks are shown either for failures or xfails. We aim to see
492 # testing passed so there should be no failures. Since xfails are expected
493 # failures we are not interested in detailed tracebacks here at all since they
494 # could contain random data, like pointers, temporary file names, etc.
495 PYTEST_TRACEBACK = --tb=line
496 PYTEST_ADDOPTS += $(PYTEST_TRACEBACK)
498 # Normalize pytest test results. The pytest framework could be used either
499 # directly or via tox or setup.py so add these transforms for all test styles
500 # unconditionally.
501 COMPONENT_TEST_TRANSFORMS += \
502 "-e 's/^\(platform sunos5 -- Python \)$(shell echo $(PYTHON_VERSION) | $(GSED) -e 's/\./\\./g')\.[0-9]\{1,\}.*\( -- .*\)/\1\$$(PYTHON_VERSION).X\2/'"
503 COMPONENT_TEST_TRANSFORMS += "-e '/^plugins: /d'" # order of listed plugins could vary
504 COMPONENT_TEST_TRANSFORMS += "-e '/^-\{1,\} coverage: /,/^$$/d'" # remove coverage report
505 COMPONENT_TEST_TRANSFORMS += "-e 's/ \{1,\}\[...%\]\$$//'" # drop percentage
506 COMPONENT_TEST_TRANSFORMS += \
507 "-e 's/^=\{1,\} \(.*\) in [0-9]\{1,\}\.[0-9]\{1,\}s \(([^)]*) \)\?=\{1,\}$$/======== \1 ========/'" # remove timing
508 # Remove slowest durations report for projects that run pytest with --durations option
509 COMPONENT_TEST_TRANSFORMS += "-e '/^=\{1,\} slowest [0-9 ]*durations =\{1,\}$$/,/^=/{/^=/!d}'"
510 # Remove short test summary info for projects that run pytest with -r option
511 COMPONENT_TEST_TRANSFORMS += "-e '/^=\{1,\} short test summary info =\{1,\}$$/,/^=/{/^=/!d}'"
513 # Normalize test results produced by pytest-benchmark
514 COMPONENT_TEST_TRANSFORMS += \
515 $(if $(filter library/python/pytest-benchmark-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)),"| ( \
516 $(GSED) -e '/^-\{1,\} benchmark/,/^=/{/^=/!d}' \
517 ) | $(COMPONENT_TEST_TRANSFORMER) -e ''")
519 # Normalize test results produced by pytest-randomly
520 USE_PYTEST_RANDOMLY = $(filter library/python/pytest-randomly-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES))
521 PYTEST_SORT_TESTS = $(USE_PYTEST_RANDOMLY)
522 COMPONENT_TEST_TRANSFORMS += $(if $(strip $(USE_PYTEST_RANDOMLY)),"-e '/^Using --randomly-seed=[0-9]\{1$(comma)\}\$$/d'")
523 COMPONENT_TEST_TRANSFORMS += \
524 $(if $(strip $(PYTEST_SORT_TESTS)),"| ( \
525 $(GSED) -u -e '/^=\{1$(comma)\} test session starts /q' ; \
526 $(GSED) -u -e '/^\$$/q' ; \
527 $(GSED) -u -e '/^\$$/Q' | $(SORT) | $(GSED) -e '\$$a\'\$$'\\\n\\\n' ; \
528 $(CAT) \
529 ) | $(COMPONENT_TEST_TRANSFORMER) -e ''")
531 # Normalize test results produced by pytest-xdist
532 COMPONENT_TEST_TRANSFORMS += \
533 $(if $(filter library/python/pytest-xdist-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)),"| ( \
534 $(GSED) -u \
535 -e '/^created: .* workers$$/d' \
536 -e 's/^[0-9]\{1,\}\( workers \[[0-9]\{1,\} items\]\)$$/X\1/' \
537 -e '/^scheduling tests via /q' ; \
538 $(GSED) -u -e '/^$$/q' ; \
539 $(GSED) -u -n -e '/^\[gw/p' -e '/^$$/Q' | ( $(GSED) \
540 -e 's/^\[gw[0-9]\{1,\}\] \[...%\] //' \
541 -e 's/ *$$//' \
542 -e 's/\([^ ]\{1,\}\) \(.*\)$$/\2 \1/' \
543 | $(SORT) | $(NAWK) '{print}END{if(NR>0)printf(\"\\\\n\")}' ; \
544 ) ; \
545 $(CAT) \
546 ) | $(COMPONENT_TEST_TRANSFORMER) -e ''")
548 # Normalize stestr test results
549 USE_STESTR = $(filter library/python/stestr-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES))
550 COMPONENT_TEST_TRANSFORMS += \
551 $(if $(strip $(USE_STESTR)),"| ( \
552 $(GSED) -e '0,/^{[0-9]\{1,\}}/{//i\'\$$'\\\n{0}\\\n}' \
553 -e 's/^\(Ran: [0-9]\{1,\} tests\{0,1\}\) in .*\$$/\1/' \
554 -e '/^Sum of execute time for each test/d' \
555 -e '/^ - Worker /d' \
556 ) | ( \
557 $(GSED) -u -e '/^{0}\$$/Q' ; \
558 $(GSED) -u -e 's/^{[0-9]\{1,\}} //' \
559 -e 's/\[[.0-9]\{1,\}s\] \.\.\./.../' \
560 -e '/^\$$/Q' | $(SORT) | $(GSED) -e '\$$a\'\$$'\\\n\\\n' ; \
561 $(CAT) \
562 ) | $(COMPONENT_TEST_TRANSFORMER) -e ''")
564 # Remove timestamp produced by coincidence
565 USE_COINCIDENCE = $(filter library/python/coincidence-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES))
566 COMPONENT_TEST_TRANSFORMS += $(if $(strip $(USE_COINCIDENCE)),"-e '/^Test session started at/d'")
568 # Normalize setup.py test results. The setup.py testing could be used either
569 # directly or via tox so add these transforms for all test styles
570 # unconditionally.
571 COMPONENT_TEST_TRANSFORMS += "-e '/SetuptoolsDeprecationWarning:/,+1d'" # depends on Python version and is useless
572 COMPONENT_TEST_TRANSFORMS += "-e 's/^\(Ran [0-9]\{1,\} tests\{0,1\}\) in .*$$/\1/'" # delete timing from test results
574 COMPONENT_TEST_DIR = $(@D)$(COMPONENT_SUBDIR:%=/%)
576 ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no)
577 # Temporarily create symlinks for renamed binaries
578 COMPONENT_PRE_TEST_ACTION += \
579 for f in $(PROTOUSRBINDIR)/*-$(PYTHON_VERSION) ; do \
580 [ -f $$f ] || continue ; \
581 [ -L $${f%%-$(PYTHON_VERSION)} ] && $(RM) $${f%%-$(PYTHON_VERSION)} ; \
582 [ -e $${f%%-$(PYTHON_VERSION)} ] && continue ; \
583 $(SYMLINK) $$(basename $$f) $${f%%-$(PYTHON_VERSION)} ; \
584 done ;
586 # Cleanup of temporary symlinks
587 COMPONENT_POST_TEST_ACTION += \
588 for f in $(PROTOUSRBINDIR)/*-$(PYTHON_VERSION) ; do \
589 [ -f $$f ] || continue ; \
590 [ ! -L $${f%%-$(PYTHON_VERSION)} ] || $(RM) $${f%%-$(PYTHON_VERSION)} ; \
591 done ;
592 endif
595 ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no)
596 # We need to add -$(PYV) to package fmri
597 GENERATE_EXTRA_CMD += | \
598 $(GSED) -e 's/^\(set name=pkg.fmri [^@]*\)\(.*\)$$/\1-$$(PYV)\2/'
599 endif
601 # Add runtime dependencies from project metadata to generated manifest
602 GENERATE_EXTRA_DEPS += $(BUILD_DIR)/META.depend-runtime.res
603 GENERATE_EXTRA_CMD += | \
604 $(CAT) - <( \
605 echo "" ; \
606 echo "\# python modules are unusable without python runtime binary" ; \
607 echo "depend type=require fmri=__TBD pkg.debug.depend.file=python\$$(PYVER) \\" ; \
608 echo " pkg.debug.depend.path=usr/bin" ; \
609 echo "" ; \
610 echo "\# Automatically generated dependencies based on distribution metadata" ; \
611 $(CAT) $(BUILD_DIR)/META.depend-runtime.res \
614 # Add runtime dependencies from project metadata to REQUIRED_PACKAGES
615 REQUIRED_PACKAGES_RESOLVED += $(BUILD_DIR)/META.depend-runtime.res
618 # Generate raw lists of runtime dependencies per Python version
619 COMPONENT_POST_INSTALL_ACTION += \
620 PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
621 $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) >> $(@D)/.depend-runtime ;
623 # Convert raw per version lists of runtime dependencies to single resolved
624 # runtime dependency list. The dependency on META.depend-test.required here is
625 # purely to get the file created as a side effect of this target.
626 $(BUILD_DIR)/META.depend-runtime.res: $(INSTALL_$(MK_BITS)) $(BUILD_DIR)/META.depend-test.required
627 $(CAT) $(INSTALL_$(MK_BITS):%.installed=%.depend-runtime) | $(SORT) -u \
628 | $(GSED) -e 's/.*/depend type=require fmri=pkg:\/library\/python\/&-$$(PYV)/' > $@
630 # Generate raw lists of test dependencies per Python version
631 COMPONENT_POST_INSTALL_ACTION += \
632 cd $(@D)$(COMPONENT_SUBDIR:%=/%) ; \
633 ( for f in $(TEST_REQUIREMENTS) ; do \
634 $(CAT) $$f | $(DOS2UNIX) -ascii ; \
635 done ; \
636 for e in $(TEST_REQUIREMENTS_EXTRAS) ; do \
637 PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
638 $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) $$e ; \
639 done ) | $(WS_TOOLS)/python-resolve-deps \
640 PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \
641 $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) \
642 | $(PYTHON) $(WS_TOOLS)/python-requires - >> $(@D)/.depend-test ;
644 # Convert raw per version lists of test dependencies to single list of
645 # TEST_REQUIRED_PACKAGES entries. Some Python projects lists their own project
646 # as a test dependency so filter this out here too.
647 $(BUILD_DIR)/META.depend-test.required: $(INSTALL_$(MK_BITS))
648 $(CAT) $(INSTALL_$(MK_BITS):%.installed=%.depend-test) | $(SORT) -u \
649 | $(GSED) -e 's/.*/TEST_REQUIRED_PACKAGES.python += library\/python\/&/' \
650 | ( $(GNU_GREP) -v ' $(COMPONENT_FMRI)$$' || true ) \
651 > $@
653 # Add META.depend-test.required to the generated list of REQUIRED_PACKAGES
654 REQUIRED_PACKAGES_TRANSFORM += -e '$$r $(BUILD_DIR)/META.depend-test.required'
656 # The python-requires script requires packaging to provide useful output but
657 # packaging might be unavailable during bootstrap until we reach bootstrap
658 # checkpoint 2. So require it conditionally.
659 ifeq ($(filter $(strip $(COMPONENT_NAME)),$(PYTHON_BOOTSTRAP_CHECKPOINT_2)),)
660 USERLAND_REQUIRED_PACKAGES.python += library/python/packaging
661 endif
664 clean::
665 $(RM) -r $(SOURCE_DIR) $(BUILD_DIR)
667 # Make it easy to construct a URL for a pypi source download.
668 pypi_url_multi = pypi:///$(COMPONENT_NAME_$(1))==$(COMPONENT_VERSION_$(1))
669 pypi_url_single = pypi:///$(COMPONENT_NAME)==$(COMPONENT_VERSION)
670 pypi_url = $(if $(COMPONENT_NAME_$(1)),$(pypi_url_multi),$(pypi_url_single))
672 # Use common rules
673 USE_COMMON_RULES = yes