Skip to content
Draft
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
4 changes: 2 additions & 2 deletions src/macaron/build_spec_generator/build_spec_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ def gen_build_spec_for_purl(
case BuildSpecFormat.DOCKERFILE:
try:
build_spec_content = gen_dockerfile(build_spec)
except ValueError as error:
logger.error("Error while serializing the build spec: %s.", error)
except GenerateBuildSpecError as error:
logger.error("Error while generating the build spec: %s.", error)
return os.EX_DATAERR
build_spec_file_path = os.path.join(build_spec_dir_path, "dockerfile.buildspec")

Expand Down
5 changes: 4 additions & 1 deletion src/macaron/build_spec_generator/common_spec/base_spec.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2025 - 2026, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.

"""This module includes base build specification and helper classes."""
Expand Down Expand Up @@ -81,6 +81,9 @@ class BaseBuildSpecDict(TypedDict, total=False):
#: be a list of these that were used in building the wheel alongside their version.
build_backends: NotRequired[list[str]]

#: The artifacts that were analyzed in generating the build specification.
upstream_artifacts: dict[str, str]


class BaseBuildSpec(ABC):
"""Abstract base class for build specification behavior and field resolution."""
Expand Down
2 changes: 1 addition & 1 deletion src/macaron/build_spec_generator/common_spec/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def gen_generic_build_spec(
"purl": str(purl),
"language": target_language,
"build_tools": build_tool_names,
"build_commands": [selected_build_command],
"build_commands": [selected_build_command] if selected_build_command else [],
}
)
ECOSYSTEMS[purl.type.upper()].value(base_build_spec_dict).resolve_fields(purl)
Expand Down
53 changes: 38 additions & 15 deletions src/macaron/build_spec_generator/common_spec/pypi_spec.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2025 - 2026, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.

"""This module includes build specification and helper classes for PyPI packages."""
Expand Down Expand Up @@ -112,6 +112,7 @@ def resolve_fields(self, purl: PackageURL) -> None:
metadata=[],
)

artifacts: dict[str, str] = {}
pypi_package_json = pypi_registry.find_or_create_pypi_asset(purl.name, purl.version, registry_info)
patched_build_commands: list[list[str]] = []
build_requires_set: set[str] = set()
Expand All @@ -120,6 +121,7 @@ def resolve_fields(self, purl: PackageURL) -> None:
python_version_set: set[str] = set()
wheel_name_python_version_list: list[str] = []
wheel_name_platforms: set[str] = set()
version_constraint_set: set[str] = set()
# Precautionary fallback to default version
chronologically_likeliest_version: str = defaults.get("heuristic.pypi", "default_setuptools")

Expand All @@ -136,6 +138,7 @@ def resolve_fields(self, purl: PackageURL) -> None:

try:
with pypi_package_json.wheel():
artifacts["wheel"] = pypi_package_json.wheel_url
logger.debug("Wheel at %s", pypi_package_json.wheel_path)
# Should only have .dist-info directory.
logger.debug("It has directories %s", ",".join(os.listdir(pypi_package_json.wheel_path)))
Expand All @@ -155,6 +158,16 @@ def resolve_fields(self, purl: PackageURL) -> None:
chronologically_likeliest_version = (
pypi_package_json.get_chronologically_suitable_setuptools_version()
)
try:
# Get information from the wheel file name.
logger.debug(pypi_package_json.wheel_filename)
_, _, _, tags = parse_wheel_filename(pypi_package_json.wheel_filename)
for tag in tags:
wheel_name_python_version_list.append(tag.interpreter)
wheel_name_platforms.add(tag.platform)
logger.debug(python_version_set)
except InvalidWheelFilename:
logger.debug("Could not parse wheel file name to extract version")
except SourceCodeError:
logger.debug("Could not find pure wheel matching this PURL")

Expand All @@ -163,6 +176,8 @@ def resolve_fields(self, purl: PackageURL) -> None:

try:
with pypi_package_json.sourcecode():
artifacts["sdist"] = pypi_package_json.sdist_url
logger.debug("sdist url at %s", artifacts["sdist"])
try:
# Get the build time requirements from ["build-system", "requires"]
pyproject_content = pypi_package_json.get_sourcecode_file_contents("pyproject.toml")
Expand Down Expand Up @@ -214,17 +229,6 @@ def resolve_fields(self, purl: PackageURL) -> None:
except (InvalidRequirement, InvalidSpecifier) as error:
logger.debug("Malformed requirement encountered %s : %s", requirement, error)

try:
# Get information from the wheel file name.
logger.debug(pypi_package_json.wheel_filename)
_, _, _, tags = parse_wheel_filename(pypi_package_json.wheel_filename)
for tag in tags:
wheel_name_python_version_list.append(tag.interpreter)
wheel_name_platforms.add(tag.platform)
logger.debug(python_version_set)
except InvalidWheelFilename:
logger.debug("Could not parse wheel file name to extract version")

self.data["language_version"] = list(python_version_set) or wheel_name_python_version_list

# Use the default build command for pure Python packages.
Expand All @@ -238,14 +242,33 @@ def resolve_fields(self, purl: PackageURL) -> None:
build_backends_set.add("setuptools.build_meta")

logger.debug("Combined build-requires: %s", parsed_build_requires)

for package, constraint in parsed_build_requires.items():
package_requirement = package + constraint
python_version_constraints = registry.get_python_requires_for_package_requirement(package_requirement)
if python_version_constraints:
version_constraint_set.add(python_version_constraints)

self.data["language_version"] = sorted(version_constraint_set)

self.data["build_requires"] = parsed_build_requires
self.data["build_backends"] = list(build_backends_set)
self.data["upstream_artifacts"] = artifacts

if not patched_build_commands:
# Resolve and patch build commands.
selected_build_commands = self.data["build_commands"] or self.get_default_build_commands(
self.data["build_tools"]
)

# To ensure that selected_build_commands is never empty, we seed with the fallback
# command of python -m build --wheel -n
if self.data["build_commands"]:
selected_build_commands = self.data["build_commands"]
else:
self.data["build_commands"] = ["python -m build --wheel -n".split()]
selected_build_commands = (
self.get_default_build_commands(self.data["build_tools"]) or self.data["build_commands"]
)

logger.debug(selected_build_commands)

patched_build_commands = (
patch_commands(
Expand Down
Loading
Loading