Blame SOURCES/README.md

838e4d
pyproject RPM macros
838e4d
====================
838e4d
838e4d
These macros allow projects that follow the Python [packaging specifications]
838e4d
to be packaged as RPMs.
838e4d
838e4d
They are still *provisional*: we can make non-backwards-compatible changes to
838e4d
the API.
838e4d
Please subscribe to Fedora's [python-devel list] if you use the macros.
838e4d
838e4d
They work for:
838e4d
838e4d
* traditional Setuptools-based projects that use the `setup.py` file,
838e4d
* newer Setuptools-based projects that have a `setup.cfg` file,
838e4d
* general Python projects that use the [PEP 517] `pyproject.toml` file (which allows using any build system, such as setuptools, flit or poetry).
838e4d
838e4d
These macros replace `%py3_build` and `%py3_install`, which only work with `setup.py`.
838e4d
838e4d
[packaging specifications]: https://packaging.python.org/specifications/
838e4d
[python-devel list]: https://lists.fedoraproject.org/archives/list/python-devel@lists.fedoraproject.org/
838e4d
838e4d
838e4d
Usage
838e4d
-----
838e4d
838e4d
To use these macros, first BuildRequire them:
838e4d
838e4d
    BuildRequires: pyproject-rpm-macros
838e4d
838e4d
Also BuildRequire the devel package for the Python you are building against.
838e4d
In Fedora, that's `python3-devel`.
838e4d
(In the future, we plan to make `python3-devel` itself require
838e4d
`pyproject-rpm-macros`.)
838e4d
838e4d
Next, you need to generate more build dependencies (of your projects and
838e4d
the macros themselves) by running `%pyproject_buildrequires` in the
838e4d
`%generate_buildrequires` section:
838e4d
838e4d
    %generate_buildrequires
838e4d
    %pyproject_buildrequires
838e4d
838e4d
This will add build dependencies according to [PEP 517] and [PEP 518].
838e4d
To also add run-time and test-time dependencies, see the section below.
838e4d
If you need more dependencies, such as non-Python libraries, BuildRequire
838e4d
them manually.
838e4d
838e4d
Note that `%generate_buildrequires` may produce error messages `(exit 11)` in
838e4d
the build log. This is expected behavior of BuildRequires generators; see
838e4d
[the Fedora change] for details.
838e4d
838e4d
[the Fedora change]: https://fedoraproject.org/wiki/Changes/DynamicBuildRequires
838e4d
838e4d
Then, build a wheel in `%build` with `%pyproject_wheel`:
838e4d
838e4d
    %build
838e4d
    %pyproject_wheel
838e4d
838e4d
And install the wheel in `%install` with `%pyproject_install`:
838e4d
838e4d
    %install
838e4d
    %pyproject_install
838e4d
838e4d
`%pyproject_install` installs all wheels in `pyproject-wheeldir/` located in the root of the source tree.
838e4d
838e4d
838e4d
Adding run-time and test-time dependencies
838e4d
------------------------------------------
838e4d
838e4d
To run tests in the `%check` section, the package's runtime dependencies
838e4d
often need to also be included as build requirements.
838e4d
This can be done using the `-r` flag:
838e4d
838e4d
    %generate_buildrequires
838e4d
    %pyproject_buildrequires -r
838e4d
838e4d
For this to work, the project's build system must support the
838e4d
[`prepare-metadata-for-build-wheel` hook](https://www.python.org/dev/peps/pep-0517/#prepare-metadata-for-build-wheel).
838e4d
The popular buildsystems (setuptools, flit, poetry) do support it.
838e4d
838e4d
For projects that specify test requirements using an [`extra`
838e4d
provide](https://packaging.python.org/specifications/core-metadata/#provides-extra-multiple-use),
838e4d
these can be added using the `-x` flag.
838e4d
Multiple extras can be supplied by repeating the flag or as a comma separated list.
838e4d
For example, if upstream suggests installing test dependencies with
838e4d
`pip install mypackage[testing]`, the test deps would be generated by:
838e4d
838e4d
    %generate_buildrequires
838e4d
    %pyproject_buildrequires -x testing
838e4d
838e4d
For projects that specify test requirements in their [tox] configuration,
838e4d
these can be added using the `-t` flag (default tox environment)
838e4d
or the `-e` flag followed by the tox environment.
838e4d
The default tox environment (such as `py37` assuming the Fedora's Python version is 3.7)
838e4d
is available in the `%{toxenv}` macro.
838e4d
For example, if upstream suggests running the tests on Python 3.7 with `tox -e py37`,
838e4d
the test deps would be generated by:
838e4d
838e4d
    %generate_buildrequires
838e4d
    %pyproject_buildrequires -t
838e4d
838e4d
If upstream uses a custom derived environment, such as `py37-unit`, use:
838e4d
838e4d
    %pyproject_buildrequires -e %{toxenv}-unit
838e4d
838e4d
Or specify more environments if needed:
838e4d
838e4d
    %pyproject_buildrequires -e %{toxenv}-unit,%{toxenv}-integration
838e4d
838e4d
The `-e` option redefines `%{toxenv}` for further reuse.
838e4d
Use `%{default_toxenv}` to get the default value.
838e4d
838e4d
The `-t`/`-e` option uses [tox-current-env]'s `--print-deps-to-file` behind the scenes.
838e4d
838e4d
If your package specifies some tox plugins in `tox.requires`,
838e4d
such plugins will be BuildRequired as well.
838e4d
Not all plugins are guaranteed to play well with [tox-current-env],
838e4d
in worst case, patch/sed the requirement out from the tox configuration.
838e4d
838e4d
Note that both `-x` and `-t` imply `-r`,
838e4d
because runtime dependencies are always required for testing.
838e4d
838e4d
[tox]: https://tox.readthedocs.io/
838e4d
[tox-current-env]: https://github.com/fedora-python/tox-current-env/
838e4d
838e4d
Additionally to generated requirements you can supply multiple file names to `%pyproject_buildrequires` macro.
838e4d
Dependencies will be loaded from them:
838e4d
838e4d
    %pyproject_buildrequires -r requirements/tests.in requirements/docs.in requirements/dev.in
838e4d
838e4d
For packages not using build system you can use `-N` to entirely skip automatical
838e4d
generation of requirements and install requirements only from manually specified files.
838e4d
`-N` option cannot be used in combination with other options mentioned above
838e4d
(`-r`, `-e`, `-t`, `-x`).
838e4d
838e4d
Running tox based tests
838e4d
-----------------------
838e4d
838e4d
In case you want to run the tests as specified in [tox] configuration,
838e4d
you must use `%pyproject_buildrequires` with `-t` or `-e` as explained above.
838e4d
Then, use the `%tox` macro in `%check`:
838e4d
838e4d
    %check
838e4d
    %tox
838e4d
838e4d
The macro:
838e4d
838e4d
 - Always prepends `$PATH` with `%{buildroot}%{_bindir}`
838e4d
 - If not defined, sets `$PYTHONPATH` to `%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}`
838e4d
 - If not defined, sets `$TOX_TESTENV_PASSENV` to `*`
838e4d
 - Runs `tox` with `-q` (quiet), `--recreate` and `--current-env` (from [tox-current-env]) flags
838e4d
 - Implicitly uses the tox environment name stored in `%{toxenv}` - as overridden by `%pyproject_buildrequires -e`
838e4d
838e4d
By using the `-e` flag, you can use a different tox environment(s):
838e4d
838e4d
    %check
838e4d
    %tox
838e4d
    %if %{with integration_test}
838e4d
    %tox -e %{default_toxenv}-integration
838e4d
    %endif
838e4d
838e4d
If you wish to provide custom `tox` flags or arguments, add them after `--`:
838e4d
838e4d
    %tox -- --flag-for-tox
838e4d
838e4d
If you wish to pass custom `posargs` to tox, use another `--`:
838e4d
838e4d
    %tox -- --flag-for-tox -- --flag-for-posargs
838e4d
838e4d
Or (note the two sequential `--`s):
838e4d
838e4d
    %tox -- -- --flag-for-posargs
838e4d
838e4d
838e4d
838e4d
Generating the %files section
838e4d
-----------------------------
838e4d
838e4d
To generate the list of files in the `%files` section, you can use `%pyproject_save_files` after the `%pyproject_install` macro.
838e4d
It takes toplevel module names (i.e. the names used with `import` in Python) and stores paths for those modules and metadata for the package (dist-info directory) to a file stored at `%{pyproject_files}`.
838e4d
For example, if a package provides the modules `requests` and `_requests`, write:
838e4d
838e4d
    %install
838e4d
    %pyproject_install
838e4d
    %pyproject_save_files requests _requests
838e4d
838e4d
To add listed files to the `%files` section, use `%files -f %{pyproject_files}`.
838e4d
Note that you still need to add any documentation manually (for now).
838e4d
838e4d
    %files -n python3-requests -f %{pyproject_files}
838e4d
    %doc README.rst
838e4d
838e4d
You can use globs in the module names if listing them explicitly would be too tedious:
838e4d
838e4d
    %install
838e4d
    %pyproject_install
838e4d
    %pyproject_save_files '*requests'
838e4d
838e4d
In fully automated environments, you can use the `*` glob to include all modules (put it in single quotes to prevent Shell from expanding it). In Fedora however, you should always use a more specific glob to avoid accidentally packaging unwanted files (for example, a top level module named `test`).
838e4d
838e4d
Speaking about automated environments, some files cannot be classified with `%pyproject_save_files`, but it is possible to list all unclassified files by adding a special `+auto` argument.
838e4d
838e4d
    %install
838e4d
    %pyproject_install
838e4d
    %pyproject_save_files '*' +auto
838e4d
    
838e4d
    %files -n python3-requests -f %{pyproject_files}
838e4d
838e4d
However, in Fedora packages, always list executables explicitly to avoid unintended collisions with other packages or accidental missing executables:
838e4d
838e4d
    %install
838e4d
    %pyproject_install
838e4d
    %pyproject_save_files requests _requests
838e4d
    
838e4d
    %files -n python3-requests -f %{pyproject_files}
838e4d
    %doc README.rst
838e4d
    %{_bindir}/downloader
838e4d
838e4d
`%pyproject_save_files` can automatically mark license files with `%license` macro
838e4d
and  language (`*.mo`) files with `%lang` macro and appropriate language code.
838e4d
Only license files declared via [PEP 639] `License-Field` field are detected.
838e4d
[PEP 639] is still a draft and can be changed in the future.
838e4d
838e4d
Note that `%pyproject_save_files` uses data from the [RECORD file](https://www.python.org/dev/peps/pep-0627/).
838e4d
If you wish to rename, remove or otherwise change the installed files of a package
838e4d
*after* `%pyproject_install`, `%pyproject_save_files` might break.
838e4d
If possible, remove/rename such files in `%prep`.
838e4d
If not possible, avoid using `%pyproject_save_files` or edit/replace `%{pyproject_files}`.
838e4d
838e4d
Generating Extras subpackages
838e4d
-----------------------------
838e4d
838e4d
The `%pyproject_extras_subpkg` macro generates simple subpackage(s)
838e4d
for Python extras.
838e4d
838e4d
The macro should be placed after the base package's `%description` to avoid
838e4d
issues in building the SRPM.
838e4d
838e4d
For example, if the `requests` project's metadata defines the extras
838e4d
`security` and `socks`, the following invocation will generate the subpackage
838e4d
`python3-requests+security` that provides `python3dist(requests[security])`,
838e4d
and a similar one for `socks`.
838e4d
838e4d
    %pyproject_extras_subpkg -n python3-requests security socks
838e4d
838e4d
The macro works like `%python_extras_subpkg`,
838e4d
except the `-i`/`-f`/`-F` arguments are optional and discouraged.
838e4d
A filelist written by `%pyproject_install` is used by default.
838e4d
For more information on `%python_extras_subpkg`, see the [Fedora change].
838e4d
838e4d
[Fedora change]: https://fedoraproject.org/wiki/Changes/PythonExtras
838e4d
838e4d
These arguments are still required:
838e4d
838e4d
* -n: name of the “base” package (e.g. python3-requests)
838e4d
* Positional arguments: the extra name(s).
838e4d
  Multiple subpackages are generated when multiple names are provided.
838e4d
838e4d
838e4d
Limitations
838e4d
-----------
838e4d
838e4d
`%pyproject_install` changes shebang lines of every Python script in `%{buildroot}%{_bindir}` to `#!%{__python3} %{py3_shbang_opt}` (`#!/usr/bin/python3 -s`).
838e4d
Existing Python flags in shebangs are preserved.
838e4d
For example `#!/usr/bin/python3 -Ru` will be updated to `#!/usr/bin/python3 -sRu`.
838e4d
Sometimes, this can interfere with tests that run such scripts directly by name,
838e4d
because in tests we usually rely on `PYTHONPATH` (and `-s` ignores that).
838e4d
Would this behavior be undesired for any reason,
838e4d
undefine `%{py3_shbang_opt}` to turn it off.
838e4d
838e4d
Some valid Python version specifiers are not supported.
838e4d
838e4d
When a dependency is specified via an URL or local path, for example as:
838e4d
838e4d
    https://github.com/ActiveState/appdirs/archive/8eacfa312d77aba28d483fbfb6f6fc54099622be.zip
838e4d
    /some/path/foo-1.2.3.tar.gz
838e4d
    git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3
838e4d
838e4d
The `%pyproject_buildrequires` macro is unable to convert it to an appropriate RPM requirement and will fail.
838e4d
If the URL contains the `packageName @` prefix as specified in [PEP 508],
838e4d
the requirement will be generated without a version constraint:
838e4d
838e4d
    appdirs@https://github.com/ActiveState/appdirs/archive/8eacfa312d77aba28d483fbfb6f6fc54099622be.zip
838e4d
    foo@file:///some/path/foo-1.2.3.tar.gz
838e4d
838e4d
Will be converted to:
838e4d
838e4d
    python3dist(appdirs)
838e4d
    python3dist(foo)
838e4d
838e4d
Alternatively, when an URL requirement parsed from a text file
838e4d
given as positional argument to `%pyproject_buildrequires`
838e4d
contains the `#egg=packageName` fragment,
838e4d
as documented in [pip's documentation]:
838e4d
838e4d
    git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3#egg=sphinx
838e4d
838e4d
The requirements will be converted to package names without versions, e.g.:
838e4d
838e4d
    python3dist(sphinx)
838e4d
838e4d
However upstreams usually only use direct URLs for their requirements as workarounds,
838e4d
so be prepared for problems.
838e4d
838e4d
[PEP 508]: https://www.python.org/dev/peps/pep-0508/
838e4d
[PEP 517]: https://www.python.org/dev/peps/pep-0517/
838e4d
[PEP 518]: https://www.python.org/dev/peps/pep-0518/
838e4d
[PEP 639]: https://www.python.org/dev/peps/pep-0639/
838e4d
[pip's documentation]: https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support
838e4d
838e4d
838e4d
Testing the macros
838e4d
------------------
838e4d
838e4d
This repository has two kinds of tests.
838e4d
First, there is RPM `%check` section, run when building the `python-rpm-macros`
838e4d
package.
838e4d
838e4d
Then there are CI tests.
838e4d
There is currently [no way to run Fedora CI tests locally][ci-rfe],
838e4d
but you can do what the tests do manually using mock.
838e4d
For each `$PKG.spec` in `tests/`:
838e4d
838e4d
  - clean your mock environment:
838e4d
838e4d
        mock -r fedora-rawhide-x86_64 clean
838e4d
838e4d
  - install the version of `python-rpm-macros` you're testing, e.g.:
838e4d
838e4d
        mock -r fedora-rawhide-x86_64 install .../python-rpm-macros-*.noarch.rpm
838e4d
838e4d
  - download the sources:
838e4d
838e4d
        spectool -g -R $PKG.spec
838e4d
838e4d
  - build a SRPM:
838e4d
838e4d
        rpmbuild -bs $PKG.spec
838e4d
838e4d
  - build in mock, using the path from the command above as `$SRPM`:
838e4d
838e4d
        mock -r fedora-rawhide-x86_64 -n -N $SRPM
838e4d
838e4d
[ci-rfe]: https://pagure.io/fedora-ci/general/issue/4