resource: clean up arguments and typing

- [x] Clean up arguments on the `resource` directive.
- [x] Add type annotations
- [x] Add `resource` to type annotations on `PackageBase`
- [x] Fix up `resource` docstrings

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
This commit is contained in:
Todd Gamblin 2024-11-25 00:02:40 -08:00
parent f54526957a
commit 8b1009a4a0
4 changed files with 41 additions and 39 deletions

View File

@ -693,19 +693,19 @@ def invalid_sha256_digest(fetcher):
return h, True return h, True
return None, False return None, False
error_msg = "Package '{}' does not use sha256 checksum".format(pkg_name) error_msg = f"Package '{pkg_name}' does not use sha256 checksum"
details = [] details = []
for v, args in pkg.versions.items(): for v, args in pkg.versions.items():
fetcher = spack.fetch_strategy.for_package_version(pkg, v) fetcher = spack.fetch_strategy.for_package_version(pkg, v)
digest, is_bad = invalid_sha256_digest(fetcher) digest, is_bad = invalid_sha256_digest(fetcher)
if is_bad: if is_bad:
details.append("{}@{} uses {}".format(pkg_name, v, digest)) details.append(f"{pkg_name}@{v} uses {digest}")
for _, resources in pkg.resources.items(): for _, resources in pkg.resources.items():
for resource in resources: for resource in resources:
digest, is_bad = invalid_sha256_digest(resource.fetcher) digest, is_bad = invalid_sha256_digest(resource.fetcher)
if is_bad: if is_bad:
details.append("Resource in '{}' uses {}".format(pkg_name, digest)) details.append(f"Resource in '{pkg_name}' uses {digest}")
if details: if details:
errors.append(error_cls(error_msg, details)) errors.append(error_cls(error_msg, details))

View File

@ -40,6 +40,7 @@ class OpenMpi(Package):
import llnl.util.tty.color import llnl.util.tty.color
import spack.deptypes as dt import spack.deptypes as dt
import spack.fetch_strategy
import spack.package_base import spack.package_base
import spack.patch import spack.patch
import spack.spec import spack.spec
@ -47,7 +48,6 @@ class OpenMpi(Package):
import spack.variant import spack.variant
from spack.dependency import Dependency from spack.dependency import Dependency
from spack.directives_meta import DirectiveError, DirectiveMeta from spack.directives_meta import DirectiveError, DirectiveMeta
from spack.fetch_strategy import from_kwargs
from spack.resource import Resource from spack.resource import Resource
from spack.version import ( from spack.version import (
GitVersion, GitVersion,
@ -740,58 +740,55 @@ def _execute_variant(pkg):
@directive("resources") @directive("resources")
def resource(**kwargs): def resource(
"""Define an external resource to be fetched and staged when building the *,
package. Based on the keywords present in the dictionary the appropriate name: Optional[str] = None,
FetchStrategy will be used for the resource. Resources are fetched and destination: str = "",
staged in their own folder inside spack stage area, and then moved into placement: Optional[str] = None,
the stage area of the package that needs them. when: WhenType = None,
# additional kwargs are as for `version()`
**kwargs,
):
"""Define an external resource to be fetched and staged when building the package.
Based on the keywords present in the dictionary the appropriate FetchStrategy will
be used for the resource. Resources are fetched and staged in their own folder
inside spack stage area, and then moved into the stage area of the package that
needs them.
List of recognized keywords: Keyword Arguments:
name: name for the resource
when: condition defining when the resource is needed
destination: path, relative to the package stage area, to which resource should be moved
placement: optionally rename the expanded resource inside the destination directory
* 'when' : (optional) represents the condition upon which the resource is
needed
* 'destination' : (optional) path where to move the resource. This path
must be relative to the main package stage area.
* 'placement' : (optional) gives the possibility to fine tune how the
resource is moved into the main package stage area.
""" """
def _execute_resource(pkg): def _execute_resource(pkg):
when = kwargs.get("when")
when_spec = _make_when_spec(when) when_spec = _make_when_spec(when)
if not when_spec: if not when_spec:
return return
destination = kwargs.get("destination", "")
placement = kwargs.get("placement", None)
# Check if the path is relative # Check if the path is relative
if os.path.isabs(destination): if os.path.isabs(destination):
message = ( msg = "The destination keyword of a resource directive can't be an absolute path.\n"
"The destination keyword of a resource directive " "can't be an absolute path.\n" msg += f"\tdestination : '{destination}\n'"
) raise RuntimeError(msg)
message += "\tdestination : '{dest}\n'".format(dest=destination)
raise RuntimeError(message)
# Check if the path falls within the main package stage area # Check if the path falls within the main package stage area
test_path = "stage_folder_root" test_path = "stage_folder_root"
normalized_destination = os.path.normpath(
os.path.join(test_path, destination) # Normalized absolute path
) # Normalized absolute path normalized_destination = os.path.normpath(os.path.join(test_path, destination))
if test_path not in normalized_destination: if test_path not in normalized_destination:
message = ( msg = "Destination of a resource must be within the package stage directory.\n"
"The destination folder of a resource must fall " msg += f"\tdestination : '{destination}'\n"
"within the main package stage directory.\n" raise RuntimeError(msg)
)
message += "\tdestination : '{dest}'\n".format(dest=destination)
raise RuntimeError(message)
resources = pkg.resources.setdefault(when_spec, []) resources = pkg.resources.setdefault(when_spec, [])
name = kwargs.get("name") resources.append(
fetcher = from_kwargs(**kwargs) Resource(name, spack.fetch_strategy.from_kwargs(**kwargs), destination, placement)
resources.append(Resource(name, fetcher, destination, placement)) )
return _execute_resource return _execute_resource

View File

@ -54,6 +54,7 @@
import spack.variant import spack.variant
from spack.error import InstallError, NoURLError, PackageError from spack.error import InstallError, NoURLError, PackageError
from spack.filesystem_view import YamlFilesystemView from spack.filesystem_view import YamlFilesystemView
from spack.resource import Resource
from spack.solver.version_order import concretization_version_order from spack.solver.version_order import concretization_version_order
from spack.stage import DevelopStage, ResourceStage, Stage, StageComposite, compute_stage_name from spack.stage import DevelopStage, ResourceStage, Stage, StageComposite, compute_stage_name
from spack.util.package_hash import package_hash from spack.util.package_hash import package_hash
@ -585,6 +586,7 @@ class PackageBase(WindowsRPath, PackageViewMixin, metaclass=PackageMeta):
# Declare versions dictionary as placeholder for values. # Declare versions dictionary as placeholder for values.
# This allows analysis tools to correctly interpret the class attributes. # This allows analysis tools to correctly interpret the class attributes.
versions: dict versions: dict
resources: Dict[spack.spec.Spec, List[Resource]]
dependencies: Dict[spack.spec.Spec, Dict[str, spack.dependency.Dependency]] dependencies: Dict[spack.spec.Spec, Dict[str, spack.dependency.Dependency]]
conflicts: Dict[spack.spec.Spec, List[Tuple[spack.spec.Spec, Optional[str]]]] conflicts: Dict[spack.spec.Spec, List[Tuple[spack.spec.Spec, Optional[str]]]]
requirements: Dict[ requirements: Dict[

View File

@ -12,7 +12,10 @@
class Resource: class Resource:
"""Represents an optional resource to be fetched by a package. """Represents any resource to be fetched by a package.
This includes the main tarball or source archive, as well as extra archives defined
by the resource() directive.
Aggregates a name, a fetcher, a destination and a placement. Aggregates a name, a fetcher, a destination and a placement.
""" """