From 5513c410bf7d1d6baa001d9eda84e8aaf742d0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 8 Dec 2021 11:07:48 +0100 Subject: [PATCH] Define provisional %pyproject_build_lib Related: rhbz#1950291 --- README.md | 65 +++++++++++++++++++++++++++++++++++++ macros.pyproject | 19 +++++++++++ pyproject-rpm-macros.spec | 6 +++- tests/double-install.spec | 67 +++++++++++++++++++++++++++++++++++++++ tests/python-ldap.spec | 10 ++++++ tests/tests.yml | 3 ++ tests/tldr.spec | 7 ++++ 7 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 tests/double-install.spec diff --git a/README.md b/README.md index c484b79..ac8c5cc 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,71 @@ These arguments are still required: Multiple subpackages are generated when multiple names are provided. +PROVISIONAL: Importing just-built (extension) modules in %build +--------------------------------------------------------------- + +Sometimes, it is desired to be able to import the *just-built* extension modules +in the `%build` section, e.g. to build the documentation with Sphinx. + + %build + %pyproject_wheel + ... build the docs here ... + +With pure Python packages, it might be possible to set `PYTHONPATH=${PWD}` or `PYTHONPATH=${PWD}/src`. +However, it is a bit more complicated with extension modules. + +The location of just-built modules might differ depending on Python version, architecture, pip version. +Hence, the macro `%{pyproject_build_lib}` exists to be used like this: + + %build + %pyproject_wheel + PYTHONPATH=%{pyproject_build_lib} ... build the docs here ... + +This macro is currently **provisional** and the behavior might change. + +The `%{pyproject_build_lib}` macro expands to an Shell `$(...)` expression and does not work when put into single quotes (`'`). + +Depending on the pip version, the expanded value will differ: + + +### New pip 21.3+ with in-tree-build (Fedora 36+) + +Always use the macro from the same directory where you called `%pyproject_wheel` from. +The value will expand to something like: + +* `/builddir/build/BUILD/%{name}-%{version}/build/lib.linux-x86_64-3.10` for wheels with extension modules +* `/builddir/build/BUILD/%{name}-%{version}/build/lib` for pure Python wheels + +If multiple wheels were built from the same directory, +some pure Python and some with extension modules, +the expanded value will be combined with `:`: + +* `/builddir/build/BUILD/%{name}-%{version}/build/lib.linux-x86_64-3.10:/builddir/build/BUILD/%{name}-%{version}/build/lib` + +If multiple wheels were built from different directories, +the value will differ depending on the current directory. + + +### Older pip with out-of-tree-build (Fedora 34, 35, and EL 9) + +The value will expand to something like: + +* `/builddir/build/BUILD/%{name}-%{version}/.pyproject-builddir/pip-req-build-xxxxxxxx/build/lib.linux-x86_64-3.10` for wheels with extension modules +* `/builddir/build/BUILD/%{name}-%{version}/.pyproject-builddir/pip-req-build-xxxxxxxx/build/lib` for pure Python wheels + +Note that the exact value is **not stable** between builds +(the `xxxxxxxx` part is randomly generated, +neither you should consider the `.pyproject-builddir` directory to remain stable). + +If multiple wheels are built, +the expanded value will always be combined with `:` regardless of the current directory, e.g.: + +* `/builddir/build/BUILD/%{name}-%{version}/.pyproject-builddir/pip-req-build-xxxxxxxx/build/lib.linux-x86_64-3.10:/builddir/build/BUILD/%{name}-%{version}/.pyproject-builddir/pip-req-build-yyyyyyyy/build/lib.linux-x86_64-3.10:/builddir/build/BUILD/%{name}-%{version}/.pyproject-builddir/pip-req-build-zzzzzzzz/build/lib` + +**Note:** If you manage to build some wheels with in-tree-build and some with out-of-tree-build option, +the expanded value will contain all relevant directories. + + Limitations ----------- diff --git a/macros.pyproject b/macros.pyproject index 11fddc7..a3e3da3 100644 --- a/macros.pyproject +++ b/macros.pyproject @@ -28,6 +28,25 @@ CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}" TMPDI } +%pyproject_build_lib %{expand:\\\ +$( +pyproject_build_lib=() +if [ -d build/lib.%{python3_platform}-%{python3_version} ]; then + pyproject_build_lib+=( "${PWD}/build/lib.%{python3_platform}-%{python3_version}" ) +fi +if [ -d build/lib ]; then + pyproject_build_lib+=( "${PWD}/build/lib" ) +fi +for directory in $(find "%{_pyproject_builddir}" -type d -wholename "%{_pyproject_builddir}/pip-req-build-*/build/lib.%{python3_platform}-%{python3_version}" 2>/dev/null); do + pyproject_build_lib+=( "${directory}" ) +done +for directory in $(find "%{_pyproject_builddir}" -type d -wholename "%{_pyproject_builddir}/pip-req-build-*/build/lib" 2>/dev/null); do + pyproject_build_lib+=( "${directory}" ) +done +echo $(IFS=:; echo "${pyproject_build_lib[*]}") +)} + + %pyproject_install() %{expand:\\\ specifier=$(ls %{_pyproject_wheeldir}/*.whl | xargs basename --multiple | sed -E 's/([^-]+)-([^-]+)-.+\\\.whl/\\\1==\\\2/') TMPDIR="%{_pyproject_builddir}" %{__python3} -m pip install --root %{buildroot} --no-deps --disable-pip-version-check --progress-bar off --verbose --ignore-installed --no-warn-script-location --no-index --no-cache-dir --find-links %{_pyproject_wheeldir} $specifier diff --git a/pyproject-rpm-macros.spec b/pyproject-rpm-macros.spec index 7bbbf40..7d7ef30 100644 --- a/pyproject-rpm-macros.spec +++ b/pyproject-rpm-macros.spec @@ -12,7 +12,7 @@ License: MIT # In other cases, such as backports, increment the point # release. Version: 0 -Release: 50%{?dist} +Release: 51%{?dist} # Macro files Source001: macros.pyproject @@ -60,6 +60,7 @@ Requires: python-srpm-macros Requires: python3-rpm-macros # We use the following tools outside of coreutils +Requires: /usr/bin/find Requires: /usr/bin/sed %description @@ -121,6 +122,9 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856 %license LICENSE %changelog +* Wed Dec 08 2021 Miro HronĨok - 0-51 +- Define provisional %%pyproject_build_lib + * Mon Nov 1 2021 Gordon Messmer - 0-50 - Improve handling of > operator, preventing post-release from satisfying most rpm requirements - Improve handling of < operator, preventing pre-release from satisfying rpm requirement diff --git a/tests/double-install.spec b/tests/double-install.spec new file mode 100644 index 0000000..93a9778 --- /dev/null +++ b/tests/double-install.spec @@ -0,0 +1,67 @@ +Name: double-install +Version: 0 +Release: 0%{?dist} +Summary: Install 2 wheels +License: BSD and MIT +%global markupsafe_version 2.0.1 +%global tldr_version 0.4.4 +Source1: https://github.com/pallets/markupsafe/archive/%{markupsafe_version}/MarkupSafe-%{markupsafe_version}.tar.gz +Source2: %{pypi_source tldr %{tldr_version}} + +BuildRequires: gcc +BuildRequires: python3-devel + +%description +This package tests that we can build and install 2 wheels at once. +One of them is "noarch" and one has an extension module. + + +%prep +%setup -Tc +tar xf %{SOURCE1} +tar xf %{SOURCE2} + + +%generate_buildrequires +cd markupsafe-%{markupsafe_version} +%pyproject_buildrequires +cd ../tldr-%{tldr_version} +%pyproject_buildrequires +cd .. + + +%build +cd markupsafe-%{markupsafe_version} +%pyproject_wheel +cd ../tldr-%{tldr_version} +%pyproject_wheel +cd .. + + +%install +# This should install both the wheels: +%pyproject_install +#pyproject_save_files is not possible with 2 dist-infos + + +%check +# Internal check for the value of %%{pyproject_build_lib} +%if 0%{?fedora} >= 36 || 0%{?rhel} >= 10 +cd markupsafe-%{markupsafe_version} +test "%{pyproject_build_lib}" == "%{_builddir}/%{buildsubdir}/markupsafe-%{markupsafe_version}/build/lib.%{python3_platform}-%{python3_version}" +cd ../tldr-%{tldr_version} +test "%{pyproject_build_lib}" == "%{_builddir}/%{buildsubdir}/tldr-%{tldr_version}/build/lib" +cd .. +%else +for dir in . markupsafe-%{markupsafe_version} tldr-%{tldr_version}; do + (cd $dir && test "%{pyproject_build_lib}" == "$(echo %{_pyproject_builddir}/pip-req-build-*/build/lib.%{python3_platform}-%{python3_version}):$(echo %{_pyproject_builddir}/pip-req-build-*/build/lib)") +done +%endif + + +%files +%{_bindir}/tldr* +%pycached %{python3_sitelib}/tldr.py +%{python3_sitelib}/tldr-%{tldr_version}.dist-info/ +%{python3_sitearch}/MarkupSafe-%{markupsafe_version}.dist-info/ +%{python3_sitearch}/markupsafe/ diff --git a/tests/python-ldap.spec b/tests/python-ldap.spec index 7937560..1647523 100644 --- a/tests/python-ldap.spec +++ b/tests/python-ldap.spec @@ -42,6 +42,9 @@ Summary: %{summary} %build %pyproject_wheel +# Internal check that we can import the built extension modules from %%{pyproject_build_lib} +! %{python3} -c 'import _ldap' +PYTHONPATH=%{pyproject_build_lib} %{python3} -c 'import _ldap' %install @@ -73,6 +76,13 @@ test -f %{buildroot}%{python3_sitearch}/_ldap.cpython-*.so ! grep -E '/site-packages/__pycache__$' %{pyproject_files} ! grep -E '/site-packages/__pycache__/$' %{pyproject_files} +# Internal check for the value of %%{pyproject_build_lib} in an archful package +%if 0%{?fedora} >= 36 || 0%{?rhel} >= 10 +test "%{pyproject_build_lib}" == "%{_builddir}/%{buildsubdir}/build/lib.%{python3_platform}-%{python3_version}" +%else +test "%{pyproject_build_lib}" == "$(echo %{_pyproject_builddir}/pip-req-build-*/build/lib.%{python3_platform}-%{python3_version})" +%endif + %files -n python3-ldap -f %{pyproject_files} %license LICENCE diff --git a/tests/tests.yml b/tests/tests.yml index 90060df..60e3576 100644 --- a/tests/tests.yml +++ b/tests/tests.yml @@ -76,6 +76,9 @@ - markupsafe: dir: . run: ./mocktest.sh python-markupsafe + - double_install: + dir: . + run: ./mocktest.sh double-install - fake_requirements: dir: . run: ./mocktest.sh fake-requirements diff --git a/tests/tldr.spec b/tests/tldr.spec index f1371fc..2924485 100644 --- a/tests/tldr.spec +++ b/tests/tldr.spec @@ -41,6 +41,13 @@ head -n1 %{buildroot}%{_bindir}/%{name}.py | grep -E '#!\s*%{python3}\s+%{py3_sh # Internal check for our macros: tests that direct_url.json file wasn't created test ! -e %{buildroot}%{python3_sitelib}/*.dist-info/direct_url.json +# Internal check for the value of %%{pyproject_build_lib} in a noarch package +%if 0%{?fedora} >= 36 || 0%{?rhel} >= 10 +test "%{pyproject_build_lib}" == "${PWD}/build/lib" +%else +test "%{pyproject_build_lib}" == "$(echo %{_pyproject_builddir}/pip-req-build-*/build/lib)" +%endif + %files -f %pyproject_files %license LICENSE %doc README.md