Automatically detect LICENSE files and mark them with %license macro
This commit is contained in:
parent
5729f18ddb
commit
5169e0e340
10
README.md
10
README.md
@ -184,11 +184,10 @@ For example, if a package provides the modules `requests` and `_requests`, write
|
|||||||
%pyproject_save_files requests _requests
|
%pyproject_save_files requests _requests
|
||||||
|
|
||||||
To add listed files to the `%files` section, use `%files -f %{pyproject_files}`.
|
To add listed files to the `%files` section, use `%files -f %{pyproject_files}`.
|
||||||
Note that you still need to add any documentation and license manually (for now).
|
Note that you still need to add any documentation manually (for now).
|
||||||
|
|
||||||
%files -n python3-requests -f %{pyproject_files}
|
%files -n python3-requests -f %{pyproject_files}
|
||||||
%doc README.rst
|
%doc README.rst
|
||||||
%license LICENSE
|
|
||||||
|
|
||||||
You can use globs in the module names if listing them explicitly would be too tedious:
|
You can use globs in the module names if listing them explicitly would be too tedious:
|
||||||
|
|
||||||
@ -214,10 +213,12 @@ However, in Fedora packages, always list executables explicitly to avoid uninten
|
|||||||
|
|
||||||
%files -n python3-requests -f %{pyproject_files}
|
%files -n python3-requests -f %{pyproject_files}
|
||||||
%doc README.rst
|
%doc README.rst
|
||||||
%license LICENSE
|
|
||||||
%{_bindir}/downloader
|
%{_bindir}/downloader
|
||||||
|
|
||||||
`%pyproject_save_files` also automatically recognizes language (`*.mo`) files and marks them with `%lang` macro and appropriate language code.
|
`%pyproject_save_files` can automatically mark license files with `%license` macro
|
||||||
|
and language (`*.mo`) files with `%lang` macro and appropriate language code.
|
||||||
|
Only license files declared via [PEP 639] `License-Field` field are detected.
|
||||||
|
[PEP 639] is still a draft and can be changed in the future.
|
||||||
|
|
||||||
Note that `%pyproject_save_files` uses data from the [RECORD file](https://www.python.org/dev/peps/pep-0627/).
|
Note that `%pyproject_save_files` uses data from the [RECORD file](https://www.python.org/dev/peps/pep-0627/).
|
||||||
If you wish to rename, remove or otherwise change the installed files of a package
|
If you wish to rename, remove or otherwise change the installed files of a package
|
||||||
@ -303,6 +304,7 @@ so be prepared for problems.
|
|||||||
[PEP 508]: https://www.python.org/dev/peps/pep-0508/
|
[PEP 508]: https://www.python.org/dev/peps/pep-0508/
|
||||||
[PEP 517]: https://www.python.org/dev/peps/pep-0517/
|
[PEP 517]: https://www.python.org/dev/peps/pep-0517/
|
||||||
[PEP 518]: https://www.python.org/dev/peps/pep-0518/
|
[PEP 518]: https://www.python.org/dev/peps/pep-0518/
|
||||||
|
[PEP 639]: https://www.python.org/dev/peps/pep-0639/
|
||||||
[pip's documentation]: https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support
|
[pip's documentation]: https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856
|
|||||||
- %%pyproject_buildrequires now fails when it encounters an invalid requirement
|
- %%pyproject_buildrequires now fails when it encounters an invalid requirement
|
||||||
- Fixes: rhbz#1983053
|
- Fixes: rhbz#1983053
|
||||||
- Rename %%_pyproject_ghost_distinfo and %%_pyproject_record to indicate they are private
|
- Rename %%_pyproject_ghost_distinfo and %%_pyproject_record to indicate they are private
|
||||||
|
- Automatically detect LICENSE files and mark them with %%license macro
|
||||||
|
|
||||||
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0-45
|
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0-45
|
||||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
|
||||||
|
@ -5,6 +5,7 @@ import os
|
|||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import PosixPath, PurePosixPath
|
from pathlib import PosixPath, PurePosixPath
|
||||||
|
from importlib.metadata import Distribution
|
||||||
|
|
||||||
|
|
||||||
# From RPM's build/files.c strtokWithQuotes delim argument
|
# From RPM's build/files.c strtokWithQuotes delim argument
|
||||||
@ -144,7 +145,7 @@ def add_lang_to_module(paths, module_name, path):
|
|||||||
|
|
||||||
|
|
||||||
def classify_paths(
|
def classify_paths(
|
||||||
record_path, parsed_record_content, sitedirs, python_version
|
record_path, parsed_record_content, metadata, sitedirs, python_version
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
For each BuildrootPath in parsed_record_content classify it to a dict structure
|
For each BuildrootPath in parsed_record_content classify it to a dict structure
|
||||||
@ -160,7 +161,7 @@ def classify_paths(
|
|||||||
"files": [], # regular %file entries with dist-info content
|
"files": [], # regular %file entries with dist-info content
|
||||||
"dirs": [distinfo], # %dir %file entries with dist-info directory
|
"dirs": [distinfo], # %dir %file entries with dist-info directory
|
||||||
"docs": [], # to be used once there is upstream way to recognize READMEs
|
"docs": [], # to be used once there is upstream way to recognize READMEs
|
||||||
"licenses": [], # to be used once there is upstream way to recognize LICENSEs
|
"licenses": [], # %license entries parsed from dist-info METADATA file
|
||||||
},
|
},
|
||||||
"lang": {}, # %lang entries: [module_name or None][language_code] lists of .mo files
|
"lang": {}, # %lang entries: [module_name or None][language_code] lists of .mo files
|
||||||
"modules": defaultdict(list), # each importable module (directory, .py, .so)
|
"modules": defaultdict(list), # each importable module (directory, .py, .so)
|
||||||
@ -170,6 +171,7 @@ def classify_paths(
|
|||||||
# In RECORDs generated by pip, there are no directories, only files.
|
# In RECORDs generated by pip, there are no directories, only files.
|
||||||
# The example RECORD from PEP 376 does not contain directories either.
|
# The example RECORD from PEP 376 does not contain directories either.
|
||||||
# Hence, we'll only assume files, but TODO get it officially documented.
|
# Hence, we'll only assume files, but TODO get it officially documented.
|
||||||
|
license_files = metadata.get_all('License-File')
|
||||||
for path in parsed_record_content:
|
for path in parsed_record_content:
|
||||||
if path.suffix == ".pyc":
|
if path.suffix == ".pyc":
|
||||||
# we handle bytecode separately
|
# we handle bytecode separately
|
||||||
@ -180,7 +182,9 @@ def classify_paths(
|
|||||||
# RECORD and REQUESTED files are removed in %pyproject_install
|
# RECORD and REQUESTED files are removed in %pyproject_install
|
||||||
# See PEP 627
|
# See PEP 627
|
||||||
continue
|
continue
|
||||||
# TODO is this a license/documentation?
|
if license_files and path.name in license_files:
|
||||||
|
paths["metadata"]["licenses"].append(path)
|
||||||
|
else:
|
||||||
paths["metadata"]["files"].append(path)
|
paths["metadata"]["files"].append(path)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -423,6 +427,14 @@ def load_parsed_record(pyproject_record):
|
|||||||
return parsed_record
|
return parsed_record
|
||||||
|
|
||||||
|
|
||||||
|
def dist_metadata(buildroot, record_path):
|
||||||
|
"""
|
||||||
|
Returns distribution metadata (email.message.EmailMessage), possibly empty
|
||||||
|
"""
|
||||||
|
real_dist_path = record_path.parent.to_real(buildroot)
|
||||||
|
dist = Distribution.at(real_dist_path)
|
||||||
|
return dist.metadata
|
||||||
|
|
||||||
def pyproject_save_files(buildroot, sitelib, sitearch, python_version, pyproject_record, varargs):
|
def pyproject_save_files(buildroot, sitelib, sitearch, python_version, pyproject_record, varargs):
|
||||||
"""
|
"""
|
||||||
Takes arguments from the %{pyproject_save_files} macro
|
Takes arguments from the %{pyproject_save_files} macro
|
||||||
@ -439,8 +451,9 @@ def pyproject_save_files(buildroot, sitelib, sitearch, python_version, pyproject
|
|||||||
final_file_list = []
|
final_file_list = []
|
||||||
|
|
||||||
for record_path, files in parsed_records.items():
|
for record_path, files in parsed_records.items():
|
||||||
|
metadata = dist_metadata(buildroot, record_path)
|
||||||
paths_dict = classify_paths(
|
paths_dict = classify_paths(
|
||||||
record_path, files, sitedirs, python_version
|
record_path, files, metadata, sitedirs, python_version
|
||||||
)
|
)
|
||||||
|
|
||||||
final_file_list.extend(
|
final_file_list.extend(
|
||||||
|
@ -50,11 +50,11 @@ classified:
|
|||||||
docs: []
|
docs: []
|
||||||
files:
|
files:
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/INSTALLER
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/INSTALLER
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/LICENSE
|
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/METADATA
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/METADATA
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/WHEEL
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/WHEEL
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/top_level.txt
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/top_level.txt
|
||||||
licenses: []
|
licenses:
|
||||||
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/LICENSE
|
||||||
modules:
|
modules:
|
||||||
requests:
|
requests:
|
||||||
- files:
|
- files:
|
||||||
@ -415,13 +415,13 @@ classified:
|
|||||||
files:
|
files:
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/AUTHORS
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/AUTHORS
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/INSTALLER
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/INSTALLER
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE
|
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE.python
|
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/METADATA
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/METADATA
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/WHEEL
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/WHEEL
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/entry_points.txt
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/entry_points.txt
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/top_level.txt
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/top_level.txt
|
||||||
licenses: []
|
licenses:
|
||||||
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE
|
||||||
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE.python
|
||||||
lang:
|
lang:
|
||||||
django:
|
django:
|
||||||
af:
|
af:
|
||||||
@ -7434,8 +7434,8 @@ dumped:
|
|||||||
- - '%dir /usr/lib/python3.7/site-packages/requests'
|
- - '%dir /usr/lib/python3.7/site-packages/requests'
|
||||||
- '%dir /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info'
|
- '%dir /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info'
|
||||||
- '%dir /usr/lib/python3.7/site-packages/requests/__pycache__'
|
- '%dir /usr/lib/python3.7/site-packages/requests/__pycache__'
|
||||||
|
- '%license /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/LICENSE'
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/INSTALLER
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/INSTALLER
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/LICENSE
|
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/METADATA
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/METADATA
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/WHEEL
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/WHEEL
|
||||||
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/top_level.txt
|
- /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/top_level.txt
|
||||||
@ -11252,12 +11252,12 @@ dumped:
|
|||||||
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sessions/locale/zh_Hant/LC_MESSAGES/django.mo'
|
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sessions/locale/zh_Hant/LC_MESSAGES/django.mo'
|
||||||
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sites/locale/zh_Hans/LC_MESSAGES/django.mo'
|
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sites/locale/zh_Hans/LC_MESSAGES/django.mo'
|
||||||
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sites/locale/zh_Hant/LC_MESSAGES/django.mo'
|
- '%lang(zh) /usr/lib/python3.7/site-packages/django/contrib/sites/locale/zh_Hant/LC_MESSAGES/django.mo'
|
||||||
|
- '%license /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE'
|
||||||
|
- '%license /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE.python'
|
||||||
- /usr/bin/django-admin
|
- /usr/bin/django-admin
|
||||||
- /usr/bin/django-admin.py
|
- /usr/bin/django-admin.py
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/AUTHORS
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/AUTHORS
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/INSTALLER
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/INSTALLER
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE
|
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/LICENSE.python
|
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/METADATA
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/METADATA
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/WHEEL
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/WHEEL
|
||||||
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/entry_points.txt
|
- /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/entry_points.txt
|
||||||
@ -14487,6 +14487,23 @@ dumped:
|
|||||||
- /usr/share/pronterface/zoom_in.png
|
- /usr/share/pronterface/zoom_in.png
|
||||||
- /usr/share/pronterface/zoom_out.png
|
- /usr/share/pronterface/zoom_out.png
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
requests:
|
||||||
|
path: /usr/lib/python3.7/site-packages/requests-2.22.0.dist-info/METADATA
|
||||||
|
content: |
|
||||||
|
Name: requests
|
||||||
|
Version: 2.22.0
|
||||||
|
License-File: LICENSE
|
||||||
|
Whatever: False data
|
||||||
|
django:
|
||||||
|
path: /usr/lib/python3.7/site-packages/Django-3.0.7.dist-info/METADATA
|
||||||
|
content: |
|
||||||
|
Name: Django
|
||||||
|
Version: 3.0.7
|
||||||
|
License-File: LICENSE
|
||||||
|
License-File: LICENSE.python
|
||||||
|
Whatever: False data
|
||||||
|
|
||||||
records:
|
records:
|
||||||
kerberos:
|
kerberos:
|
||||||
path: /usr/lib64/python3.7/site-packages/kerberos-1.3.0.dist-info/RECORD
|
path: /usr/lib64/python3.7/site-packages/kerberos-1.3.0.dist-info/RECORD
|
||||||
|
@ -20,6 +20,7 @@ yaml_data = yaml.safe_load(yaml_file.read_text())
|
|||||||
EXPECTED_DICT = yaml_data["classified"]
|
EXPECTED_DICT = yaml_data["classified"]
|
||||||
EXPECTED_FILES = yaml_data["dumped"]
|
EXPECTED_FILES = yaml_data["dumped"]
|
||||||
TEST_RECORDS = yaml_data["records"]
|
TEST_RECORDS = yaml_data["records"]
|
||||||
|
TEST_METADATAS = yaml_data["metadata"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -47,6 +48,10 @@ def prepare_pyproject_record(tmp_path, package=None, content=None):
|
|||||||
# Get test data and write dist-info/RECORD file
|
# Get test data and write dist-info/RECORD file
|
||||||
record_path = BuildrootPath(TEST_RECORDS[package]["path"])
|
record_path = BuildrootPath(TEST_RECORDS[package]["path"])
|
||||||
record_file.write_text(TEST_RECORDS[package]["content"])
|
record_file.write_text(TEST_RECORDS[package]["content"])
|
||||||
|
if package in TEST_METADATAS:
|
||||||
|
metadata_path = BuildrootPath(TEST_METADATAS[package]["path"]).to_real(tmp_path)
|
||||||
|
metadata_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
metadata_path.write_text(TEST_METADATAS[package]["content"])
|
||||||
# Parse RECORD file
|
# Parse RECORD file
|
||||||
parsed_record = parse_record(record_path, read_record(record_file))
|
parsed_record = parse_record(record_path, read_record(record_file))
|
||||||
# Save JSON content to pyproject-record
|
# Save JSON content to pyproject-record
|
||||||
|
@ -67,8 +67,12 @@ rm pyproject.toml
|
|||||||
# We only run a subset of tests to speed things up and be less fragile
|
# We only run a subset of tests to speed things up and be less fragile
|
||||||
PYTHONPATH=$(pwd) %pytest --ignore=pavement.py -k "sdist"
|
PYTHONPATH=$(pwd) %pytest --ignore=pavement.py -k "sdist"
|
||||||
|
|
||||||
|
# Internal check that license file was recognized correctly
|
||||||
|
grep '^%%license' %{pyproject_files} > tested.license
|
||||||
|
echo '%%license %{python3_sitelib}/setuptools-%{version}.dist-info/LICENSE' > expected.license
|
||||||
|
diff tested.license expected.license
|
||||||
|
|
||||||
|
|
||||||
%files -n python3-setuptools -f %{pyproject_files}
|
%files -n python3-setuptools -f %{pyproject_files}
|
||||||
%license LICENSE
|
|
||||||
%doc docs/* CHANGES.rst README.rst
|
%doc docs/* CHANGES.rst README.rst
|
||||||
%{python3_sitelib}/distutils-precedence.pth
|
%{python3_sitelib}/distutils-precedence.pth
|
||||||
|
Loading…
Reference in New Issue
Block a user