Repository: scikit-build/scikit-build-core (python)

Issue:
Set `License` metadata as well
I am not sure about what magic fedora uses with [their macro](https://src.fedoraproject.org/rpms/pyproject-rpm-macros/blob/f31/f/pyproject_save_files.py), but apparently it is able to auto-detect and mark python files as license files, e.g.:
```console
$ rpm -qpL python3-packit-0.75.0-1.fc38.noarch.rpm
/usr/lib/python3.11/site-packages/packitos-0.75.0.dist-info/licenses/LICENSE
```
But this does not work here:
```console
$ rpm -qpL python3-spglib-2.1.0.rc1-1.20230518123112228603.pr272.50.ge38c0dc.x86_64.rpm 
warning: python3-spglib-2.1.0.rc1-1.20230518123112228603.pr272.50.ge38c0dc.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID bcf19e6e: NOKEY
```

My suspicion is because of the difference in `METADATA` file:
```
# packit
License-Expression: MIT
License-File: LICENSE
# spglib (where it doesn't detect)
License: BSD-3-Clause
```
Maybe the guys who showed me this feature can know more about it ([discussion](https://github.com/packit/packit/pull/1913#discussion_r1199132232))

Edit: Confirmed it is `License-File`.

Pull request:
fix: add License-file metadata entry
Fix #328. Follows the draft of PEP 639 more closely.

Patch:
diff --git a/README.md b/README.md
index 475a4f4..abde42e 100644
--- a/README.md
+++ b/README.md
@@ -169,7 +169,7 @@ wheel.expand-macos-universal-tags = false
 wheel.install-dir = "."
 
 # The licence file(s) to include in the wheel metadata directory.
-wheel.license-files = ["LICENSE*", "COPYING*", "COPYRIGHT*"]
+wheel.license-files = ["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"]
 
 # This will backport an internal copy of FindPython if CMake is less than this
 # value. Set to 0 or the empty string to disable. The default will be kept in
diff --git a/docs/configuration.md b/docs/configuration.md
index 1158627..95cc279 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -177,10 +177,10 @@ for now.
 
 :::
 
-By default, any `LICENSE*`, `COPYING*`, or `COPYRIGHT*` file in the root of the
-build directory will be picked up. You can specify an exact list of files if you
-prefer, or if you your license file is in a different directory. The files must
-have unique names. Globbing patterns are supported.
+By default, any `LICEN[CS]E*`, `COPYING*`, `NOTICE*`, or `AUTHORS*` file in the
+root of the build directory will be picked up. You can specify an exact list of
+files if you prefer, or if your license file is in a different directory.
+Globbing patterns are supported.
 
 ```toml
 [tool.scikit-build]
diff --git a/src/scikit_build_core/build/_wheelfile.py b/src/scikit_build_core/build/_wheelfile.py
index f6b2c09..547caaa 100644
--- a/src/scikit_build_core/build/_wheelfile.py
+++ b/src/scikit_build_core/build/_wheelfile.py
@@ -10,7 +10,7 @@ import os
 import stat
 import time
 import zipfile
-from collections.abc import Set
+from collections.abc import Mapping, Set
 from email.message import Message
 from email.policy import EmailPolicy
 from pathlib import Path, PurePosixPath
@@ -73,6 +73,7 @@ class WheelWriter:
     wheel_metadata = WheelMetadata(root_is_purelib=False)
     buildver: str = ""
     zipfile: zipfile.ZipFile | None = None
+    license_files: Mapping[Path, bytes] = dataclasses.field(default_factory=dict)
 
     @property
     def name_ver(self) -> str:
@@ -117,12 +118,22 @@ class WheelWriter:
                 entry_points.write("\n")
 
         self.wheel_metadata.tags = self.tags
+
         # Using deepcopy here because of a bug in pyproject-metadata
         # https://github.com/FFY00/python-pyproject-metadata/pull/49
+        rfc822 = copy.deepcopy(self.metadata).as_rfc822()
+        for fp in self.license_files:
+            rfc822["License-File"] = f"{fp}"
+
+        license_entries = {
+            f"licenses/{fp}": data for fp, data in self.license_files.items()
+        }
+
         return {
-            "METADATA": bytes(copy.deepcopy(self.metadata).as_rfc822()),
+            "METADATA": bytes(rfc822),
             "WHEEL": self.wheel_metadata.as_bytes(),
             "entry_points.txt": entry_points.getvalue().encode("utf-8"),
+            **license_entries,
         }
 
     def build(self, wheel_dirs: dict[str, Path]) -> None:
diff --git a/src/scikit_build_core/build/wheel.py b/src/scikit_build_core/build/wheel.py
index 6eb18fa..5981072 100644
--- a/src/scikit_build_core/build/wheel.py
+++ b/src/scikit_build_core/build/wheel.py
@@ -1,7 +1,6 @@
 from __future__ import annotations
 
 import dataclasses
-import glob
 import os
 import shutil
 import sys
@@ -146,6 +145,16 @@ def _build_wheel_impl(
         else:
             install_dir = wheel_dirs["platlib"] / settings.wheel.install_dir
 
+        license_files = {
+            x: x.read_bytes()
+            for y in settings.wheel.license_files
+            for x in Path().glob(y)
+        }
+        if settings.wheel.license_files and not license_files:
+            logger.warning(
+                "No license files found, set wheel.license-files to [] to suppress this warning"
+            )
+
         config = CMaker(
             cmake,
             source_dir=Path(settings.cmake.source_dir or "."),
@@ -162,12 +171,19 @@ def _build_wheel_impl(
             if metadata_directory is None:
                 msg = "metadata_directory must be specified if wheel_directory is None"
                 raise AssertionError(msg)
-            wheel = WheelWriter(metadata, Path(metadata_directory), tags.as_tags_set())
+            wheel = WheelWriter(
+                metadata,
+                Path(metadata_directory),
+                tags.as_tags_set(),
+                license_files=license_files,
+            )
             dist_info_contents = wheel.dist_info_contents()
             dist_info = Path(metadata_directory) / f"{wheel.name_ver}.dist-info"
             dist_info.mkdir(parents=True)
             for key, data in dist_info_contents.items():
                 path = dist_info / key
+                if not path.parent.is_dir():
+                    path.parent.mkdir(exist_ok=True, parents=True)
                 path.write_bytes(data)
             return WheelImplReturn(wheel_filename=dist_info.name)
 
@@ -215,22 +231,13 @@ def _build_wheel_impl(
 
             process_script_dir(wheel_dirs["scripts"])
 
-        license_files = [
-            Path(x)
-            for y in settings.wheel.license_files
-            for x in glob.glob(y, recursive=True)
-        ]
-        if settings.wheel.license_files and not license_files:
-            logger.warning(
-                "No license files found, set wheel.license-files to [] to suppress this warning"
-            )
-
-        with WheelWriter(metadata, Path(wheel_directory), tags.as_tags_set()) as wheel:
+        with WheelWriter(
+            metadata,
+            Path(wheel_directory),
+            tags.as_tags_set(),
+            license_files=license_files,
+        ) as wheel:
             wheel.build(wheel_dirs)
-            for license_file in license_files:
-                wheel.write(
-                    str(license_file), f"{wheel.dist_info}/licenses/{license_file.name}"
-                )
 
             if editable:
                 modules = {
diff --git a/src/scikit_build_core/settings/skbuild_model.py b/src/scikit_build_core/settings/skbuild_model.py
index 8440fcf..9039155 100644
--- a/src/scikit_build_core/settings/skbuild_model.py
+++ b/src/scikit_build_core/settings/skbuild_model.py
@@ -100,7 +100,7 @@ class WheelSettings:
 
     #: A list of license files to include in the wheel. Supports glob patterns.
     license_files: List[str] = dataclasses.field(
-        default_factory=lambda: ["LICENSE*", "COPYING*", "COPYRIGHT*"]
+        default_factory=lambda: ["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"]
     )
 
 


Test patch:
diff --git a/tests/test_pyproject_pep517.py b/tests/test_pyproject_pep517.py
index 4eb50f4..475d126 100644
--- a/tests/test_pyproject_pep517.py
+++ b/tests/test_pyproject_pep517.py
@@ -273,6 +273,7 @@ def test_prepare_metdata_for_build_wheel():
         "Requires-Python": ">=3.7",
         "Provides-Extra": "test",
         "Requires-Dist": 'pytest>=6.0; extra == "test"',
+        "License-File": "LICENSE",
     }
 
     for k, b in answer.items():
diff --git a/tests/test_skbuild_settings.py b/tests/test_skbuild_settings.py
index c156311..aefc4cd 100644
--- a/tests/test_skbuild_settings.py
+++ b/tests/test_skbuild_settings.py
@@ -37,7 +37,12 @@ def test_skbuild_settings_default(tmp_path):
     assert settings.wheel.packages is None
     assert settings.wheel.py_api == ""
     assert not settings.wheel.expand_macos_universal_tags
-    assert settings.wheel.license_files == ["LICENSE*", "COPYING*", "COPYRIGHT*"]
+    assert settings.wheel.license_files == [
+        "LICEN[CS]E*",
+        "COPYING*",
+        "NOTICE*",
+        "AUTHORS*",
+    ]
     assert settings.backport.find_python == "3.26.1"
     assert settings.strict_config
     assert not settings.experimental