# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. # # # The Python build infrastructure in setup.py.mk and pyproject.mk files uses # several Python projects to work properly. Since we cannot use these projects # until they are actually built and installed we need to bootstrap. # # We do have several sequential bootstrap checkpoints during the process: # # (0) Nothing works yet. # # Just core Python runtime is available (with no additional projects). # While here almost nothing works. We cannot do following tasks with # regular Python projects: # - detect their requirements, # - build and publish them, # - test them. # # (1) The bootstrapper is ready. # # The bootstrapper is special tool that requires just core Python with no # dependency on other Python projects and it is able to build and publish # itself and other Python projects. # # For projects using the 'setup.py' build style we do not need any # special bootstrapper because such projects are built using their own # 'setup.py' script. The only issue with the 'setup.py' build style # projects is that their 'setup.py' script usually depends on some other # projects (typically setuptools) to get successfully built. # # For 'pyproject'-style projects we use pyproject_installer as the # bootstrapper. # # To achieve this checkpoint we just need to build pyproject_installer # using pyproject_installer without detecting its requirements (they are # none anyway) and without testing it (since no testing infrastructure is # ready yet). # # (2) The python-requires script works. # # Once the python-requires script works we can start to detect runtime # dependencie of other Python projects automatically. # # To achieve this checkpoint we need to build the packaging project # (directly needed by the python-requires script) and all projects # required by packaging. During this all projects' dependencies needs to # be manually evaluated to make sure they are correct. # # (3) The build infrastructure is fully working. # # Once we are here we can build any Python project, but we cannot test it # yet. # # For projects using the 'setup.py' build style we do not need any # special build infrastructure. See checkpoint (1) above for detialed # discussion about 'setup.py' build style projects. # # For 'pyproject'-style projects we need to build both 'build' and # 'installer' projects and all projects they depends on. # # (4) The testing infrastructure is fully working. # # Once we are here we can finally use all features of the Python build # framework. Including testing. # # To achieve this we need to build tox, tox-current-env, and pytest # projects together with their dependencies. # # All projects needed to achieve checkpoints (1), (2), and (3) should set # PYTHON_BOOTSTRAP to 'yes' in their Makefile to make sure the regular build # infrastructure is not used for them and special set of build rules is applied # instead. # # All projects needed to go from checkpoint (3) to checkpoint (4) should set # PYTHON_TEST_BOOTSTRAP to 'yes' in their Makefile to let the build # infrastructure know that testing for such projects might not work properly. # # The PYTHON_BOOTSTRAP set to 'yes' implies PYTHON_TEST_BOOTSTRAP set to 'yes' # too. # ifeq ($(strip $(PYTHON_BOOTSTRAP)),yes) PYTHON_TEST_BOOTSTRAP = yes endif # # Lists of Python projects needed to achieve particular bootstrap checkpoint. # Indentation shows project dependencies (e.g. packaging requires flit_core). # PYTHON_BOOTSTRAP_CHECKPOINT_1 += pyproject_installer # PYTHON_BOOTSTRAP_CHECKPOINT_2 += $(PYTHON_BOOTSTRAP_CHECKPOINT_1) PYTHON_BOOTSTRAP_CHECKPOINT_2 += packaging PYTHON_BOOTSTRAP_CHECKPOINT_2 += flit_core # Particular python runtime is always required (at least to run setup.py) PYTHON_REQUIRED_PACKAGES += runtime/python define python-rule $(BUILD_DIR)/%-$(1)/.built: PYTHON_VERSION=$(1) $(BUILD_DIR)/%-$(1)/.installed: PYTHON_VERSION=$(1) $(BUILD_DIR)/%-$(1)/.tested: PYTHON_VERSION=$(1) $(BUILD_DIR)/%-$(1)/.tested-and-compared: PYTHON_VERSION=$(1) endef $(foreach pyver, $(PYTHON_VERSIONS), $(eval $(call python-rule,$(pyver)))) $(BUILD_DIR)/$(MACH32)-%/.built: BITS=32 $(BUILD_DIR)/$(MACH64)-%/.built: BITS=64 $(BUILD_DIR)/$(MACH32)-%/.installed: BITS=32 $(BUILD_DIR)/$(MACH64)-%/.installed: BITS=64 $(BUILD_DIR)/$(MACH32)-%/.tested: BITS=32 $(BUILD_DIR)/$(MACH64)-%/.tested: BITS=64 $(BUILD_DIR)/$(MACH32)-%/.tested-and-compared: BITS=32 $(BUILD_DIR)/$(MACH64)-%/.tested-and-compared: BITS=64 PYTHON_32_VERSIONS = $(filter-out $(PYTHON_64_ONLY_VERSIONS), $(PYTHON_VERSIONS)) BUILD_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.built) BUILD_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.built) BUILD_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.built) ifeq ($(filter-out $(PYTHON_64_ONLY_VERSIONS), $(PYTHON_VERSION)),) BUILD_32_and_64 = $(BUILD_64) endif INSTALL_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.installed) INSTALL_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.installed) INSTALL_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.installed) PYTHON_ENV = CC="$(CC)" PYTHON_ENV += CFLAGS="$(CFLAGS)" PYTHON_ENV += CXX="$(CXX)" PYTHON_ENV += CXXFLAGS="$(CXXFLAGS)" PYTHON_ENV += LDFLAGS="$(LDFLAGS)" PYTHON_ENV += PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" COMPONENT_BUILD_ENV += $(PYTHON_ENV) COMPONENT_INSTALL_ENV += $(PYTHON_ENV) COMPONENT_TEST_ENV += $(PYTHON_ENV) # Set CARGO_HOME to make sure projects built using rust (for example via # setuptools-rust) do not pollute user's home directory with cargo bits. COMPONENT_BUILD_ENV += CARGO_HOME=$(@D)/.cargo # Make sure the default Python version is installed last and so is the # canonical version. This is needed for components that keep PYTHON_VERSIONS # set to more than single value, but deliver unversioned binaries in usr/bin or # other overlapping files. define python-order-rule $(BUILD_DIR)/%-$(PYTHON_VERSION)/.installed: $(BUILD_DIR)/%-$(1)/.installed endef $(foreach pyver,$(filter-out $(PYTHON_VERSION),$(PYTHON_VERSIONS)),$(eval $(call python-order-rule,$(pyver)))) # We need to copy the source dir to avoid its modification by install target # where egg-info is re-generated CLONEY_ARGS = CLONEY_MODE="copy" COMPONENT_BUILD_CMD = $(PYTHON) setup.py --no-user-cfg build $(COMPONENT_BUILD_SETUP_PY_ARGS) COMPONENT_INSTALL_CMD = $(PYTHON) setup.py --no-user-cfg install COMPONENT_INSTALL_ARGS += --root $(PROTO_DIR) COMPONENT_INSTALL_ARGS += --install-lib=$(PYTHON_LIB) COMPONENT_INSTALL_ARGS += --install-data=$(PYTHON_DATA) COMPONENT_INSTALL_ARGS += --skip-build COMPONENT_INSTALL_ARGS += --force ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no) # Rename binaries in /usr/bin to contain version number COMPONENT_POST_INSTALL_ACTION += \ for f in $(PROTOUSRBINDIR)/* ; do \ [ -f $$f ] || continue ; \ for v in $(PYTHON_VERSIONS) ; do \ [ "$$f" == "$${f%%$$v}" ] || continue 2 ; \ done ; \ $(MV) $$f $$f-$(PYTHON_VERSION) ; \ done ; endif # Remove any previous dependency files COMPONENT_POST_INSTALL_ACTION += $(RM) $(@D)/.depend-runtime $(@D)/.depend-test ; # Define Python version specific filenames for tests. ifeq ($(strip $(USE_COMMON_TEST_MASTER)),no) COMPONENT_TEST_MASTER = $(COMPONENT_TEST_RESULTS_DIR)/results-$(PYTHON_VERSION).master endif COMPONENT_TEST_BUILD_DIR = $(BUILD_DIR)/test-$(PYTHON_VERSION) COMPONENT_TEST_OUTPUT = $(COMPONENT_TEST_BUILD_DIR)/test-$(PYTHON_VERSION)-results COMPONENT_TEST_DIFFS = $(COMPONENT_TEST_BUILD_DIR)/test-$(PYTHON_VERSION)-diffs COMPONENT_TEST_SNAPSHOT = $(COMPONENT_TEST_BUILD_DIR)/results-$(PYTHON_VERSION).snapshot COMPONENT_TEST_TRANSFORM_CMD = $(COMPONENT_TEST_BUILD_DIR)/transform-$(PYTHON_VERSION)-results # Generic transforms for Python test results. # See below for test style specific transforms. COMPONENT_TEST_TRANSFORMS += "-e 's|$(PYTHON_DIR)|\$$(PYTHON_DIR)|g'" # Testing depends on install target because we want to test installed modules COMPONENT_TEST_DEP += $(BUILD_DIR)/%/.installed # Point Python to the proto area so it is able to find installed modules there COMPONENT_TEST_ENV += PYTHONPATH=$(PROTO_DIR)/$(PYTHON_LIB) # Make sure testing is able to find own installed executables (if any) COMPONENT_TEST_ENV += PATH=$(PROTOUSRBINDIR):$(PATH) # determine the type of tests we want to run. ifeq ($(strip $(wildcard $(COMPONENT_TEST_RESULTS_DIR)/results-*.master)),) TEST_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.tested) TEST_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.tested) TEST_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.tested) else TEST_32 = $(PYTHON_32_VERSIONS:%=$(BUILD_DIR)/$(MACH32)-%/.tested-and-compared) TEST_64 = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH64)-%/.tested-and-compared) TEST_NO_ARCH = $(PYTHON_VERSIONS:%=$(BUILD_DIR)/$(MACH)-%/.tested-and-compared) endif # # Testing in the Python world is complex. Python projects usually do not # support Makefile with common 'check' or 'test' target to get built bits # tested. # # De facto standard way to test Python projects these days is tox which is # designed and used primarily for release testing; to make sure the released # python project runs on all supported Python versions, platforms, etc. tox # does so using virtualenv and creates isolated test environments where the # tested package together with all its dependencies is automatically installed # (using pip) and tested. This is great for Python projects developers but it # is hardly usable for operating system distributions like OpenIndiana. # # We do not need such release testing. Instead we need something closer to # integration testing: we need to test the built component in our real # environment without automatic installation of any dependencies using pip. In # addition, we need to run tests only for Python versions we actually support # and the component is built for. # # To achieve that we do few things. First, to avoid isolated environments # (virtualenv) we run tox with the tox-current-env plugin. Second, to test # only Python versions we are interested in we use -e option for tox to select # single Python version only. Since we run separate test target per Python # version this will make sure we test all needed Python versions. # # The tox tool itself uses some other tools under the hood to run tests, for # example pytest. Some projects could even support pytest testing directly # without support for tox. For such projects we offer separate "pytest"-style # testing. # # For projects that do not support testing using neither tox nor pytest we # offer either unittest or (deprecated) "setup.py test" testing too. # # The TEST_STYLE variable is used to select (or force) particular test style # for Python projects. Valid values are: # # tox - "tox"-style testing # pytest - "pytest"-style testing # unittest - "unittest"-style testing # setup.py - "setup.py test"-style testing # none - no testing is supported (or desired) at all # TEST_STYLE ?= tox ifeq ($(strip $(TEST_STYLE)),tox) # tox needs PATH environment variable - see https://github.com/tox-dev/tox/issues/2538 # We already added it to the test environment - see above COMPONENT_TEST_ENV += PYTEST_ADDOPTS="$(PYTEST_ADDOPTS)" COMPONENT_TEST_ENV += NOSE_VERBOSE=2 COMPONENT_TEST_CMD = $(TOX) COMPONENT_TEST_ARGS = --current-env --no-provision COMPONENT_TEST_ARGS += --recreate COMPONENT_TEST_ARGS += $(TOX_TESTENV) COMPONENT_TEST_TARGETS = $(if $(strip $(TOX_POSARGS)),-- $(TOX_POSARGS)) TOX_TESTENV = -e py$(subst .,,$(PYTHON_VERSION)) # Make sure following tools are called indirectly to properly support tox-current-env TOX_CALL_INDIRECTLY += py.test TOX_CALL_INDIRECTLY += pytest TOX_CALL_INDIRECTLY += coverage TOX_CALL_INDIRECTLY += zope-testrunner TOX_CALL_INDIRECTLY.zope-testrunner = zope.testrunner TOX_CALL_INDIRECTLY += sphinx-build TOX_CALL_INDIRECTLY.sphinx-build = sphinx.cmd.build TOX_CALL_INDIRECTLY += nosetests TOX_CALL_INDIRECTLY.nosetests = nose $(foreach indirectly, $(TOX_CALL_INDIRECTLY), $(eval TOX_CALL_INDIRECTLY.$(indirectly) ?= $(indirectly))) COMPONENT_PRE_TEST_ACTION += COMPONENT_TEST_DIR=$(COMPONENT_TEST_DIR) ; COMPONENT_PRE_TEST_ACTION += \ $(foreach indirectly, $(TOX_CALL_INDIRECTLY), \ [ -f $$COMPONENT_TEST_DIR/tox.ini ] && \ $(GSED) -i -e '/^commands *=/,/^$$/{ \ s/^\(\(commands *=\)\{0,1\}[ \t]*\)'$(indirectly)'\([ \t]\{1,\}.*\)\{0,1\}$$/\1python -m '$(TOX_CALL_INDIRECTLY.$(indirectly))'\3/ \ }' $$COMPONENT_TEST_DIR/tox.ini ; \ ) COMPONENT_PRE_TEST_ACTION += true ; # Normalize tox test results. COMPONENT_TEST_TRANSFORMS += "-e 's/py$(subst .,,$(PYTHON_VERSION))/py\$$(PYV)/g'" # normalize PYV COMPONENT_TEST_TRANSFORMS += "-e '/^py\$$(PYV) installed:/d'" # depends on set of installed packages COMPONENT_TEST_TRANSFORMS += "-e '/PYTHONHASHSEED/d'" # this is random # Normalize zope.testrunner test results COMPONENT_TEST_TRANSFORMS += \ "-e 's/ in \([0-9]\{1,\} minutes \)\{0,1\}[0-9]\{1,\}\.[0-9]\{3\} seconds//'" # timing # Remove timing for tox 4 test results COMPONENT_TEST_TRANSFORMS += "-e 's/^\( py\$$(PYV): OK\) (.* seconds)$$/\1/'" COMPONENT_TEST_TRANSFORMS += "-e 's/^\( congratulations :)\) (.* seconds)$$/\1/'" # Remove useless lines from the "coverage combine" output COMPONENT_TEST_TRANSFORMS += "-e '/^Combined data file .*\.coverage/d'" COMPONENT_TEST_TRANSFORMS += "-e '/^Skipping duplicate data .*\.coverage/d'" # sort list of Sphinx doctest results COMPONENT_TEST_TRANSFORMS += \ "| ( \ $(GSED) -u -e '/^running tests\.\.\.$$/q' ; \ $(GSED) -u -e '/^Doctest summary/Q' \ | $(NAWK) '/^$$/{\$$0=\"\\\\n\"}1' ORS='|' \ | $(GNU_GREP) -v '^|$$' \ | $(SORT) \ | tr -d '\\\\n' | tr '|' '\\\\n' \ | $(NAWK) '{print}END{if(NR>0)printf(\"\\\\nDoctest summary\\\\n\")}' ; \ $(CAT) \ ) | $(COMPONENT_TEST_TRANSFORMER)" # tox package together with the tox-current-env plugin is needed USERLAND_TEST_REQUIRED_PACKAGES += library/python/tox USERLAND_TEST_REQUIRED_PACKAGES += library/python/tox-current-env # Generate raw lists of test dependencies per Python version # Please note we set PATH below four times for $(COMPONENT_TEST_CMD) (aka tox) # to workaround https://github.com/tox-dev/tox/issues/2538 COMPONENT_POST_INSTALL_ACTION += \ if [ -x "$(COMPONENT_TEST_CMD)" ] ; then \ cd $(@D)$(COMPONENT_SUBDIR:%=/%) ; \ echo "Testing dependencies:" ; \ PATH=$(PATH) $(COMPONENT_TEST_CMD) -qq --no-provision --print-deps-to=- $(TOX_TESTENV) || exit 1 ; \ echo "Testing extras:" ; \ PATH=$(PATH) $(COMPONENT_TEST_CMD) -qq --no-provision --print-extras-to=- $(TOX_TESTENV) || exit 1 ; \ ( PATH=$(PATH) $(COMPONENT_TEST_CMD) -qq --no-provision --print-deps-to=- $(TOX_TESTENV) \ | $(WS_TOOLS)/python-resolve-deps \ PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \ $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) \ | $(PYTHON) $(WS_TOOLS)/python-requires - ; \ for e in $$(PATH=$(PATH) $(COMPONENT_TEST_CMD) -qq --no-provision --print-extras-to=- $(TOX_TESTENV)) ; do \ PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \ $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) $$e ; \ done ) | $(GSED) -e '/^tox\(-current-env\)\?$$/d' >> $(@D)/.depend-test ; \ fi ; else ifeq ($(strip $(TEST_STYLE)),pytest) COMPONENT_TEST_CMD = $(PYTHON) -m pytest COMPONENT_TEST_ARGS = $(PYTEST_ADDOPTS) COMPONENT_TEST_TARGETS = USERLAND_TEST_REQUIRED_PACKAGES += library/python/pytest else ifeq ($(strip $(TEST_STYLE)),unittest) COMPONENT_TEST_CMD = $(PYTHON) -m unittest COMPONENT_TEST_ARGS = COMPONENT_TEST_ARGS += --verbose COMPONENT_TEST_TARGETS = else ifeq ($(strip $(TEST_STYLE)),setup.py) # Old and deprecated "setup.py test"-style testing COMPONENT_TEST_CMD = $(PYTHON) setup.py COMPONENT_TEST_ARGS = --no-user-cfg COMPONENT_TEST_TARGETS = test else ifeq ($(strip $(TEST_STYLE)),none) TEST_TARGET = $(NO_TESTS) endif # Run pytest verbose to get separate line per test in results output PYTEST_ADDOPTS += --verbose # Force pytest to not use colored output so the results normalization is unaffected PYTEST_ADDOPTS += --color=no # Create list of required pytest plugins. define pytest-plugin PYTEST_PLUGINS += $$(if $$(filter library/python/$(1)-$$(subst .,,$$(PYTHON_VERSION)), $$(REQUIRED_PACKAGES) $$(TEST_REQUIRED_PACKAGES) $$(COMPONENT_FMRI)-$$(subst .,,$$(PYTHON_VERSION))),$(2)) endef $(eval $(call pytest-plugin,anyio,anyio)) $(eval $(call pytest-plugin,betamax,pytest-betamax)) $(eval $(call pytest-plugin,faker,faker)) $(eval $(call pytest-plugin,flaky,flaky)) $(eval $(call pytest-plugin,hypothesis,hypothesispytest)) $(eval $(call pytest-plugin,jaraco-test,jaraco.test.http)) $(eval $(call pytest-plugin,jaraco-vcs,jaraco.vcs.fixtures)) $(eval $(call pytest-plugin,kgb,kgb)) $(eval $(call pytest-plugin,pyfakefs,pytest_fakefs)) $(eval $(call pytest-plugin,pytest-asyncio,asyncio)) $(eval $(call pytest-plugin,pytest-benchmark,benchmark)) $(eval $(call pytest-plugin,pytest-black,black)) $(eval $(call pytest-plugin,pytest-check,check)) $(eval $(call pytest-plugin,pytest-checkdocs,checkdocs)) $(eval $(call pytest-plugin,pytest-console-scripts,console-scripts)) $(eval $(call pytest-plugin,pytest-cov,pytest_cov)) $(eval $(call pytest-plugin,pytest-custom-exit-code,custom_exit_code)) $(eval $(call pytest-plugin,pytest-datadir,pytest-datadir)) $(eval $(call pytest-plugin,pytest-enabler,enabler)) $(eval $(call pytest-plugin,pytest-env,env)) $(eval $(call pytest-plugin,pytest-expect,pytest_expect)) $(eval $(call pytest-plugin,pytest-flake8,flake8)) $(eval $(call pytest-plugin,pytest-forked,pytest_forked)) $(eval $(call pytest-plugin,pytest-freezer,freezer)) $(eval $(call pytest-plugin,pytest-helpers-namespace,helpers_namespace)) $(eval $(call pytest-plugin,pytest-home,home)) $(eval $(call pytest-plugin,pytest-httpserver,pytest_httpserver)) $(eval $(call pytest-plugin,pytest-ignore-flaky,pytest_ignore_flaky)) $(eval $(call pytest-plugin,pytest-lazy-fixtures,pytest_lazyfixture)) $(eval $(call pytest-plugin,pytest-metadata,metadata)) $(eval $(call pytest-plugin,pytest-mock,pytest_mock)) $(eval $(call pytest-plugin,pytest-mypy,mypy)) $(eval $(call pytest-plugin,pytest-mypy-plugins,pytest-mypy-plugins)) $(eval $(call pytest-plugin,pytest-perf,perf)) $(eval $(call pytest-plugin,pytest-randomly,randomly)) $(eval $(call pytest-plugin,pytest-regressions,regressions)) $(eval $(call pytest-plugin,pytest-relaxed,relaxed)) $(eval $(call pytest-plugin,pytest-reporter,reporter)) $(eval $(call pytest-plugin,pytest-rerunfailures,rerunfailures)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-event-listener)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-factories)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-loader-mock)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-log-server)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-markers)) $(eval $(call pytest-plugin,pytest-salt-factories,salt-factories-sysinfo)) $(eval $(call pytest-plugin,pytest-shell-utilities,shell-utilities)) $(eval $(call pytest-plugin,pytest-skip-markers,skip-markers)) $(eval $(call pytest-plugin,pytest-socket,socket)) $(eval $(call pytest-plugin,pytest-subprocess,pytest-subprocess)) $(eval $(call pytest-plugin,pytest-subtests,subtests)) $(eval $(call pytest-plugin,pytest-system-statistics,system-statistics)) $(eval $(call pytest-plugin,pytest-timeout,timeout)) $(eval $(call pytest-plugin,pytest-travis-fold,travis-fold)) $(eval $(call pytest-plugin,pytest-xdist,xdist)) $(eval $(call pytest-plugin,pytest-xdist,xdist.looponfail)) $(eval $(call pytest-plugin,pytest-xprocess,xprocess)) $(eval $(call pytest-plugin,teamcity-messages,pytest-teamcity)) $(eval $(call pytest-plugin,time-machine,time_machine)) $(eval $(call pytest-plugin,typeguard,typeguard)) # # Transitional (indirect) runtime dependencies of pytest plugins. # # Note: The list is not exhaustive and contians only entries that proved to be # needed or useful. # # pytest-datadir is required by pytest-regressions and pytest-regressions is required by coincidence $(eval $(call pytest-plugin,coincidence,regressions)) $(eval $(call pytest-plugin,coincidence,pytest-datadir)) # By default disable all pytest plugins ... COMPONENT_TEST_ENV += PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 # ... and load those in the PYTEST_PLUGINS list only. # $(sort) is used to avoid duplicates and to strip spaces. COMPONENT_TEST_ENV += PYTEST_PLUGINS="$(subst $(space),$(comma),$(sort $(PYTEST_PLUGINS)))" # By default we are not interested in full list of test failures so exit on # first failure to save time. This could be easily overridden from environment # if needed (for example to debug test failures) or in per-component Makefile. PYTEST_FASTFAIL = -x PYTEST_ADDOPTS += $(PYTEST_FASTFAIL) # By default we are not interested to see the default long tracebacks. # Detailed tracebacks are shown either for failures or xfails. We aim to see # testing passed so there should be no failures. Since xfails are expected # failures we are not interested in detailed tracebacks here at all since they # could contain random data, like pointers, temporary file names, etc. PYTEST_TRACEBACK = --tb=line PYTEST_ADDOPTS += $(PYTEST_TRACEBACK) # Normalize pytest test results. The pytest framework could be used either # directly or via tox or setup.py so add these transforms for all test styles # unconditionally. COMPONENT_TEST_TRANSFORMS += \ "-e 's/^\(platform sunos5 -- Python \)$(shell echo $(PYTHON_VERSION) | $(GSED) -e 's/\./\\./g')\.[0-9]\{1,\}.*\( -- .*\)/\1\$$(PYTHON_VERSION).X\2/'" COMPONENT_TEST_TRANSFORMS += "-e '/^plugins: /d'" # order of listed plugins could vary COMPONENT_TEST_TRANSFORMS += "-e '/^-\{1,\} coverage: /,/^$$/d'" # remove coverage report COMPONENT_TEST_TRANSFORMS += "-e 's/ \{1,\}\[...%\]\$$//'" # drop percentage COMPONENT_TEST_TRANSFORMS += \ "-e 's/^=\{1,\} \(.*\) in [0-9]\{1,\}\.[0-9]\{1,\}s \(([^)]*) \)\?=\{1,\}$$/======== \1 ========/'" # remove timing # Remove slowest durations report for projects that run pytest with --durations option COMPONENT_TEST_TRANSFORMS += "-e '/^=\{1,\} slowest [0-9 ]*durations =\{1,\}$$/,/^=/{/^=/!d}'" # Remove short test summary info for projects that run pytest with -r option COMPONENT_TEST_TRANSFORMS += "-e '/^=\{1,\} short test summary info =\{1,\}$$/,/^=/{/^=/!d}'" # Normalize test results produced by pytest-benchmark COMPONENT_TEST_TRANSFORMS += \ $(if $(filter library/python/pytest-benchmark-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)),"| ( \ $(GSED) -e '/^-\{1,\} benchmark/,/^=/{/^=/!d}' \ ) | $(COMPONENT_TEST_TRANSFORMER) -e ''") # Normalize test results produced by pytest-randomly USE_PYTEST_RANDOMLY = $(filter library/python/pytest-randomly-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)) PYTEST_SORT_TESTS = $(USE_PYTEST_RANDOMLY) COMPONENT_TEST_TRANSFORMS += $(if $(strip $(USE_PYTEST_RANDOMLY)),"-e '/^Using --randomly-seed=[0-9]\{1$(comma)\}\$$/d'") COMPONENT_TEST_TRANSFORMS += \ $(if $(strip $(PYTEST_SORT_TESTS)),"| ( \ $(GSED) -u -e '/^=\{1$(comma)\} test session starts /q' ; \ $(GSED) -u -e '/^\$$/q' ; \ $(GSED) -u -e '/^\$$/Q' | $(SORT) | $(GSED) -e '\$$a\'\$$'\\\n\\\n' ; \ $(CAT) \ ) | $(COMPONENT_TEST_TRANSFORMER) -e ''") # Normalize test results produced by pytest-xdist COMPONENT_TEST_TRANSFORMS += \ $(if $(filter library/python/pytest-xdist-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)),"| ( \ $(GSED) -u \ -e '/^created: .* workers$$/d' \ -e 's/^[0-9]\{1,\}\( workers \[[0-9]\{1,\} items\]\)$$/X\1/' \ -e '/^scheduling tests via /q' ; \ $(GSED) -u -e '/^$$/q' ; \ $(GSED) -u -n -e '/^\[gw/p' -e '/^$$/Q' | ( $(GSED) \ -e 's/^\[gw[0-9]\{1,\}\] \[...%\] //' \ -e 's/ *$$//' \ -e 's/\([^ ]\{1,\}\) \(.*\)$$/\2 \1/' \ | $(SORT) | $(NAWK) '{print}END{if(NR>0)printf(\"\\\\n\")}' ; \ ) ; \ $(CAT) \ ) | $(COMPONENT_TEST_TRANSFORMER) -e ''") # Normalize stestr test results USE_STESTR = $(filter library/python/stestr-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)) COMPONENT_TEST_TRANSFORMS += \ $(if $(strip $(USE_STESTR)),"| ( \ $(GSED) -e '0,/^{[0-9]\{1,\}}/{//i\'\$$'\\\n{0}\\\n}' \ -e 's/^\(Ran: [0-9]\{1,\} tests\{0,1\}\) in .*\$$/\1/' \ -e '/^Sum of execute time for each test/d' \ -e '/^ - Worker /d' \ ) | ( \ $(GSED) -u -e '/^{0}\$$/Q' ; \ $(GSED) -u -e 's/^{[0-9]\{1,\}} //' \ -e 's/\[[.0-9]\{1,\}s\] \.\.\./.../' \ -e '/^\$$/Q' | $(SORT) | $(GSED) -e '\$$a\'\$$'\\\n\\\n' ; \ $(CAT) \ ) | $(COMPONENT_TEST_TRANSFORMER) -e ''") # Remove timestamp produced by coincidence USE_COINCIDENCE = $(filter library/python/coincidence-$(subst .,,$(PYTHON_VERSION)), $(REQUIRED_PACKAGES) $(TEST_REQUIRED_PACKAGES)) COMPONENT_TEST_TRANSFORMS += $(if $(strip $(USE_COINCIDENCE)),"-e '/^Test session started at/d'") # Normalize setup.py test results. The setup.py testing could be used either # directly or via tox so add these transforms for all test styles # unconditionally. COMPONENT_TEST_TRANSFORMS += "-e '/SetuptoolsDeprecationWarning:/,+1d'" # depends on Python version and is useless COMPONENT_TEST_TRANSFORMS += "-e 's/^\(Ran [0-9]\{1,\} tests\{0,1\}\) in .*$$/\1/'" # delete timing from test results COMPONENT_TEST_DIR = $(@D)$(COMPONENT_SUBDIR:%=/%) ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no) # Temporarily create symlinks for renamed binaries COMPONENT_PRE_TEST_ACTION += \ for f in $(PROTOUSRBINDIR)/*-$(PYTHON_VERSION) ; do \ [ -f $$f ] || continue ; \ [ -L $${f%%-$(PYTHON_VERSION)} ] && $(RM) $${f%%-$(PYTHON_VERSION)} ; \ [ -e $${f%%-$(PYTHON_VERSION)} ] && continue ; \ $(SYMLINK) $$(basename $$f) $${f%%-$(PYTHON_VERSION)} ; \ done ; # Cleanup of temporary symlinks COMPONENT_POST_TEST_ACTION += \ for f in $(PROTOUSRBINDIR)/*-$(PYTHON_VERSION) ; do \ [ -f $$f ] || continue ; \ [ ! -L $${f%%-$(PYTHON_VERSION)} ] || $(RM) $${f%%-$(PYTHON_VERSION)} ; \ done ; endif ifeq ($(strip $(SINGLE_PYTHON_VERSION)),no) # We need to add -$(PYV) to package fmri GENERATE_EXTRA_CMD += | \ $(GSED) -e 's/^\(set name=pkg.fmri [^@]*\)\(.*\)$$/\1-$$(PYV)\2/' endif # Add runtime dependencies from project metadata to generated manifest GENERATE_EXTRA_DEPS += $(BUILD_DIR)/META.depend-runtime.res GENERATE_EXTRA_CMD += | \ $(CAT) - <( \ echo "" ; \ echo "\# python modules are unusable without python runtime binary" ; \ echo "depend type=require fmri=__TBD pkg.debug.depend.file=python\$$(PYVER) \\" ; \ echo " pkg.debug.depend.path=usr/bin" ; \ echo "" ; \ echo "\# Automatically generated dependencies based on distribution metadata" ; \ $(CAT) $(BUILD_DIR)/META.depend-runtime.res \ ) # Add runtime dependencies from project metadata to REQUIRED_PACKAGES REQUIRED_PACKAGES_RESOLVED += $(BUILD_DIR)/META.depend-runtime.res # Generate raw lists of runtime dependencies per Python version COMPONENT_POST_INSTALL_ACTION += \ PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \ $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) >> $(@D)/.depend-runtime ; # Convert raw per version lists of runtime dependencies to single resolved # runtime dependency list. The dependency on META.depend-test.required here is # purely to get the file created as a side effect of this target. $(BUILD_DIR)/META.depend-runtime.res: $(INSTALL_$(MK_BITS)) $(BUILD_DIR)/META.depend-test.required $(CAT) $(INSTALL_$(MK_BITS):%.installed=%.depend-runtime) | $(SORT) -u \ | $(GSED) -e 's/.*/depend type=require fmri=pkg:\/library\/python\/&-$$(PYV)/' > $@ # Generate raw lists of test dependencies per Python version COMPONENT_POST_INSTALL_ACTION += \ cd $(@D)$(COMPONENT_SUBDIR:%=/%) ; \ ( for f in $(TEST_REQUIREMENTS) ; do \ $(CAT) $$f | $(DOS2UNIX) -ascii ; \ done ; \ for e in $(TEST_REQUIREMENTS_EXTRAS) ; do \ PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \ $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) $$e ; \ done ) | $(WS_TOOLS)/python-resolve-deps \ PYTHONPATH=$(PROTO_DIR)/$(PYTHON_DIR)/site-packages:$(PROTO_DIR)/$(PYTHON_LIB) \ $(PYTHON) $(WS_TOOLS)/python-requires $(COMPONENT_NAME) \ | $(PYTHON) $(WS_TOOLS)/python-requires - >> $(@D)/.depend-test ; # Convert raw per version lists of test dependencies to single list of # TEST_REQUIRED_PACKAGES entries. Some Python projects lists their own project # as a test dependency so filter this out here too. $(BUILD_DIR)/META.depend-test.required: $(INSTALL_$(MK_BITS)) $(CAT) $(INSTALL_$(MK_BITS):%.installed=%.depend-test) | $(SORT) -u \ | $(GSED) -e 's/.*/TEST_REQUIRED_PACKAGES.python += library\/python\/&/' \ | ( $(GNU_GREP) -v ' $(COMPONENT_FMRI)$$' || true ) \ > $@ # Add META.depend-test.required to the generated list of REQUIRED_PACKAGES REQUIRED_PACKAGES_TRANSFORM += -e '$$r $(BUILD_DIR)/META.depend-test.required' # The python-requires script requires packaging to provide useful output but # packaging might be unavailable during bootstrap until we reach bootstrap # checkpoint 2. So require it conditionally. ifeq ($(filter $(strip $(COMPONENT_NAME)),$(PYTHON_BOOTSTRAP_CHECKPOINT_2)),) USERLAND_REQUIRED_PACKAGES.python += library/python/packaging endif clean:: $(RM) -r $(SOURCE_DIR) $(BUILD_DIR) # Make it easy to construct a URL for a pypi source download. pypi_url_multi = pypi:///$(COMPONENT_NAME_$(1))==$(COMPONENT_VERSION_$(1)) pypi_url_single = pypi:///$(COMPONENT_NAME)==$(COMPONENT_VERSION) pypi_url = $(if $(COMPONENT_NAME_$(1)),$(pypi_url_multi),$(pypi_url_single)) # Use common rules USE_COMMON_RULES = yes