Source code for craft_platforms.snap._build

# This file is part of craft-platforms.
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
"""Snapcraft-specific platforms information."""

import re
import typing
from typing import Optional, Sequence, Union

from craft_platforms import _architectures, _buildinfo, _distro, _errors, _platforms

CORE16_18_DEFAULT_ARCHITECTURES = (
    _architectures.DebianArchitecture.AMD64,
    _architectures.DebianArchitecture.ARM64,
    _architectures.DebianArchitecture.ARMHF,
    _architectures.DebianArchitecture.I386,
    _architectures.DebianArchitecture.PPC64EL,
    _architectures.DebianArchitecture.S390X,
)

CORE20_DEFAULT_ARCHITECTURES = (
    _architectures.DebianArchitecture.AMD64,
    _architectures.DebianArchitecture.ARM64,
    _architectures.DebianArchitecture.ARMHF,
    _architectures.DebianArchitecture.PPC64EL,
    _architectures.DebianArchitecture.S390X,
)

DEFAULT_ARCHITECTURES_BY_BASE = {
    "core": CORE16_18_DEFAULT_ARCHITECTURES,
    "core16": CORE16_18_DEFAULT_ARCHITECTURES,
    "core18": CORE16_18_DEFAULT_ARCHITECTURES,
    "core20": CORE20_DEFAULT_ARCHITECTURES,
}

DEFAULT_ARCHITECTURES = (
    _architectures.DebianArchitecture.AMD64,
    _architectures.DebianArchitecture.ARM64,
    _architectures.DebianArchitecture.ARMHF,
    _architectures.DebianArchitecture.PPC64EL,
    _architectures.DebianArchitecture.RISCV64,
    _architectures.DebianArchitecture.S390X,
)

CORE_BASE_REGEX = re.compile("^core(?P<version>16|18|[2-9][02468])?$")

SNAP_TYPES_WITHOUT_BASE = ("base", "kernel", "snapd")

BASE_SNAPS_DOC_URL = (
    "https://canonical-snapcraft.readthedocs-hosted.com/en/stable/reference/bases/"
)


[docs]def get_default_architectures(base: str) -> Sequence[_architectures.DebianArchitecture]: """Get the default architectures for a given snap base. :param base: the snap base as a string (e.g. ``core24``) :returns: A sequence of of DebianArchitecture objects containing the default set of architectures to use if none are defined for this base. """ if base in DEFAULT_ARCHITECTURES_BY_BASE: return DEFAULT_ARCHITECTURES_BY_BASE[base] return DEFAULT_ARCHITECTURES
[docs]def get_distro_base_from_core_base(base: str) -> _distro.DistroBase: """Get a DistroBase from a 'coreXX' base number. :param base: a string containing the base value :returns: A generic base object for the given snap base. """ if base == "core": return _distro.DistroBase("ubuntu", "16.04") if match := CORE_BASE_REGEX.match(base): version = match.group("version") return _distro.DistroBase("ubuntu", f"{version}.04") return _distro.DistroBase.from_str(base)
def get_snap_base( *, base: Optional[str], build_base: Optional[str], snap_type: Optional[str] ) -> _distro.DistroBase: """Get the DistroBase for a snap based on its type, base and build_base. The rules here are defined in ST119, but this only implements "timeless" rules. """ if not base: if snap_type not in SNAP_TYPES_WITHOUT_BASE: raise _errors.RequiresBaseError( f"snaps of type {snap_type!r} require a 'base'", resolution="Declare a 'base' in 'snapcraft.yaml'", docs_url=BASE_SNAPS_DOC_URL, ) if build_base == "devel" and snap_type != "base": raise _errors.RequiresBaseError( "non-base snaps require a 'base' if 'build-base' is 'devel", resolution="Declare a 'base' in 'snapcraft.yaml'", docs_url=BASE_SNAPS_DOC_URL, ) if not build_base: raise _errors.RequiresBaseError( f"{snap_type!r} snaps require a 'build-base' if no 'base' is declared", resolution="Declare a 'build-base' in 'snapcraft.yaml'", docs_url=BASE_SNAPS_DOC_URL, ) try: return get_distro_base_from_core_base(build_base) except ValueError: raise _errors.InvalidBaseError( build_base, build_base=True, resolution="Provide a valid 'build-base'", docs_url=BASE_SNAPS_DOC_URL, ) if CORE_BASE_REGEX.match(base): if not build_base: return get_distro_base_from_core_base(base) if build_base == "devel": return _distro.DistroBase("ubuntu", "devel") if CORE_BASE_REGEX.match(build_base): raise _errors.InvalidBaseError( build_base, build_base=True, message="cannot specify a core 'build-base' alongside a 'base'", docs_url=BASE_SNAPS_DOC_URL, ) if snap_type != "kernel": raise _errors.InvalidBaseError( build_base, build_base=True, message="non-kernel snaps cannot use 'base: coreXY' and arbitrary build-bases", docs_url=BASE_SNAPS_DOC_URL, ) return _distro.DistroBase.from_str(build_base) if not build_base: raise _errors.InvalidBaseError( base, message="must declare a 'build-base' if 'base' does not match 'coreXY'", resolution="Provide a 'build-base'.", docs_url=BASE_SNAPS_DOC_URL, ) try: return get_distro_base_from_core_base(build_base) except ValueError: raise _errors.InvalidBaseError( build_base, build_base=True, resolution="Ensure the build-base is supported.", docs_url=BASE_SNAPS_DOC_URL, ) @typing.overload def get_platforms_snap_build_plan( base: None, *, build_base: Optional[str] = None, snap_type: typing.Literal["base", "kernel", "snapd"], platforms: Union[_platforms.Platforms, None], ) -> Sequence[_buildinfo.BuildInfo]: ... @typing.overload def get_platforms_snap_build_plan( base: str, *, build_base: Optional[str] = None, snap_type: Optional[str] = None, platforms: Union[_platforms.Platforms, None], ) -> Sequence[_buildinfo.BuildInfo]: ...
[docs]def get_platforms_snap_build_plan( base: Optional[str], *, build_base: Optional[str] = None, snap_type: Optional[str] = None, platforms: Union[_platforms.Platforms, None], ) -> Sequence[_buildinfo.BuildInfo]: """Generate the build plan for a platforms-based snap. :param base: The ``base`` string in ``snapcraft.yaml`` (or ``None`` if not given) :param build_base: The ``build-base`` string in ``snapcraft.yaml`` (or ``None`` if not given) :param snap_type: One of "base", "kernel", "snapd" """ distro_base = get_snap_base(base=base, build_base=build_base, snap_type=snap_type) if not platforms: platforms = dict.fromkeys( get_default_architectures(base or build_base or "default") ) return _platforms.get_platforms_build_plan(distro_base, platforms)