Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/reference/configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,15 @@ print(mk_skbuild_docs())
Must not be set if ``project.license-files`` is set.
```

```{eval-rst}
.. confval:: wheel.sbom-files
:type: ``list[str]``

A list of Software Bill of Materials files to include in the wheel.

Files are copied into the ``*.dist-info/sboms`` directory.
```

```{eval-rst}
.. confval:: wheel.packages
:type: ``list[str]``
Expand Down
13 changes: 13 additions & 0 deletions src/scikit_build_core/build/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,19 @@ def _build_wheel_impl_impl(
"No license files found, set wheel.license-files to [] to suppress this warning"
)

sbom_paths: list[Path] = []
for sbom_file in settings.wheel.sbom_files or []:
sbom_path = Path(sbom_file)
if not sbom_path.is_file():
msg = f"SBOM file not found: {sbom_file}"
raise FileNotFoundError(msg)
sbom_paths.append(sbom_path)

for sbom_path in sbom_paths:
path = wheel_dirs["metadata"] / "sboms" / sbom_path.name
path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(sbom_path, path)

for gen in settings.generate:
if gen.location == "source":
contents = generate_file_contents(gen, metadata)
Expand Down
10 changes: 10 additions & 0 deletions src/scikit_build_core/resources/scikit-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,13 @@
},
"description": "A list of license files to include in the wheel. Supports glob patterns."
},
"sbom-files": {
"type": "array",
"items": {
"type": "string"
},
"description": "A list of Software Bill of Materials files to include in the wheel."
},
"cmake": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -585,6 +592,9 @@
"license-files": {
"$ref": "#/$defs/inherit"
},
"sbom-files": {
"$ref": "#/$defs/inherit"
},
"exclude": {
"$ref": "#/$defs/inherit"
},
Expand Down
7 changes: 7 additions & 0 deletions src/scikit_build_core/settings/skbuild_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ class WheelSettings:
Must not be set if ``project.license-files`` is set.
"""

sbom_files: Optional[List[str]] = None
"""
A list of Software Bill of Materials files to include in the wheel.

Files are copied into the ``*.dist-info/sboms`` directory.
"""

cmake: bool = True
"""
Run CMake as part of building the wheel.
Expand Down
46 changes: 46 additions & 0 deletions tests/test_pyproject_pep770.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import zipfile
from pathlib import Path

import pytest

from scikit_build_core.build import build_wheel

SPDX_CONTENT = '{"spdxVersion": "SPDX-2.3", "SPDXID": "SPDXRef-DOCUMENT"}'
CYCLONEDX_CONTENT = '{"bomFormat": "CycloneDX", "specVersion": "1.5"}'


@pytest.mark.parametrize(
("filename", "content"),
[
("project.spdx.json", SPDX_CONTENT),
("bom.json", CYCLONEDX_CONTENT),
],
ids=["spdx", "cyclonedx"],
)
@pytest.mark.parametrize("package", ["simple_purelib_package"], indirect=True)
@pytest.mark.usefixtures("package")
def test_pep770_wheel_sbom_files(tmp_path: Path, filename: str, content: str):
sbom_dir = Path("sbom_inputs")
sbom_dir.mkdir()
sbom_file = sbom_dir / filename
sbom_file.write_text(content, encoding="utf-8")

dist = tmp_path / "dist"
build_wheel(str(dist), {"wheel.sbom-files": [str(sbom_file)]})

(wheel,) = dist.glob("purelib_example-0.0.1-*.whl")
wheel = wheel.resolve()
with zipfile.ZipFile(wheel) as zf:
sbom_path = f"purelib_example-0.0.1.dist-info/sboms/{filename}"
assert sbom_path in set(zf.namelist())
assert zf.read(sbom_path).decode("utf-8") == content


@pytest.mark.parametrize("package", ["simple_purelib_package"], indirect=True)
@pytest.mark.usefixtures("package")
def test_pep770_wheel_sbom_file_missing(tmp_path: Path):
dist = tmp_path / "dist"
with pytest.raises(
FileNotFoundError, match=r"SBOM file not found: missing_sbom\.json"
):
build_wheel(str(dist), {"wheel.sbom-files": ["missing_sbom.json"]})
16 changes: 16 additions & 0 deletions tests/test_settings_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path):
cmake.targets = ["a", "b"]
wheel.packages = ["a", "b"]
wheel.license-files = ["a.txt", "b.txt"]
wheel.sbom-files = ["a.spdx.json", "b.cdx.json"]
wheel.exclude = ["x", "y"]
install.components = ["a", "b"]
cmake.define = {{a="A", b="B"}}
Expand All @@ -606,13 +607,15 @@ def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path):
inherit.cmake.targets = "{inherit}"
inherit.wheel.packages = "{inherit}"
inherit.wheel.license-files = "{inherit}"
inherit.wheel.sbom-files = "{inherit}"
inherit.wheel.exclude = "{inherit}"
inherit.install.components = "{inherit}"
inherit.cmake.define = "{inherit}"
cmake.args = ["c", "d"]
cmake.targets = ["c", "d"]
wheel.packages = ["c", "d"]
wheel.license-files = ["c.txt", "d.txt"]
wheel.sbom-files = ["c.spdx.json", "d.cdx.json"]
wheel.exclude = ["xx", "yy"]
install.components = ["c", "d"]
cmake.define = {{b="X", c="C"}}
Expand All @@ -629,6 +632,7 @@ def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path):
assert settings.cmake.targets == ["c", "d"]
assert settings.wheel.packages == ["c", "d"]
assert settings.wheel.license_files == ["c.txt", "d.txt"]
assert settings.wheel.sbom_files == ["c.spdx.json", "d.cdx.json"]
assert settings.wheel.exclude == ["xx", "yy"]
assert settings.install.components == ["c", "d"]
assert settings.cmake.define == {"b": "X", "c": "C"}
Expand All @@ -637,6 +641,12 @@ def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path):
assert settings.cmake.targets == ["a", "b", "c", "d"]
assert settings.wheel.packages == ["a", "b", "c", "d"]
assert settings.wheel.license_files == ["a.txt", "b.txt", "c.txt", "d.txt"]
assert settings.wheel.sbom_files == [
"a.spdx.json",
"b.cdx.json",
"c.spdx.json",
"d.cdx.json",
]
assert settings.wheel.exclude == ["x", "y", "xx", "yy"]
assert settings.install.components == ["a", "b", "c", "d"]
assert settings.cmake.define == {"a": "A", "b": "X", "c": "C"}
Expand All @@ -645,6 +655,12 @@ def test_skbuild_overrides_inherit(inherit: str, tmp_path: Path):
assert settings.cmake.targets == ["c", "d", "a", "b"]
assert settings.wheel.packages == ["c", "d", "a", "b"]
assert settings.wheel.license_files == ["c.txt", "d.txt", "a.txt", "b.txt"]
assert settings.wheel.sbom_files == [
"c.spdx.json",
"d.cdx.json",
"a.spdx.json",
"b.cdx.json",
]
assert settings.wheel.exclude == ["xx", "yy", "x", "y"]
assert settings.install.components == ["c", "d", "a", "b"]
assert settings.cmake.define == {"a": "A", "b": "B", "c": "C"}
Expand Down
2 changes: 2 additions & 0 deletions tests/test_skbuild_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def test_skbuild_settings_config_settings(
"wheel.py-api": "cp39",
"wheel.expand-macos-universal-tags": "True",
"wheel.license-files": ["a", "b", "c"],
"wheel.sbom-files": ["sbom1.spdx.json", "sbom2.cdx.json"],
"wheel.exclude": ["b", "y", "e"],
"wheel.build-tag": "1foo",
"backport.find-python": "0",
Expand Down Expand Up @@ -231,6 +232,7 @@ def test_skbuild_settings_config_settings(
assert settings.wheel.py_api == "cp39"
assert settings.wheel.expand_macos_universal_tags
assert settings.wheel.license_files == ["a", "b", "c"]
assert settings.wheel.sbom_files == ["sbom1.spdx.json", "sbom2.cdx.json"]
assert settings.wheel.exclude == ["b", "y", "e"]
assert settings.wheel.build_tag == "1foo"
assert settings.backport.find_python == Version("0")
Expand Down
Loading