Starting with base: core20
, plugins shall be explicitly versioned. This shall
ensure that plugins can evolve independently and that no breakages occur when
other bases are used.
This document defines the core20 plugin interfaces and the high level changes
required in Snapcraft, such as those in the PluginHandler
and plugin loading
logic. Each plugin will have its own specification relevant to the plugin.
Existing plugins using core
or core18
as a base
shall continue using their
original logic for loading, that is
When the base
in snapcraft is set different one than the previously mentioned
bases, then snapcraft will use the following path to search for plugins:
snapcraft.plugins.<base>
and search for <plugin-name>.py
.
In-tree plugins shall be centrally mapped to the plugin class implementation. The plugin help shall be implemented as the doctring for the plugin module.
Custom plugins shall be declared on snapcraft.yaml
with a prefix of x-
and
the plugin name. On the file system they shall be found on
snap/plugins/x_<plugin.name.py>
.
The class name must be PluginImpl
for the plugin to be easily found.
A new plugin shall be implemented, PluginV2
(inheriting from
abc.ABC
)) as a base for all plugins used that target base core20
.
A specific map shall exist to select these plugins when the
build-base
is set to core20
.
The v2
base plugin and the in-tree
plugins implementing it must reside in the snapcraft.plugins.v2
package, the base plugin shall be implemented with a class name of
PluginV2
in a module with and import path of
snapcraft.plugins.v2._plugin
, for convenience,
snapcraft.plugins.v2.__init__.py
will import PluginV2
such that a
plugin author could simply import
snapcraft.plugins.v2.PluginV2
. This plugin defines the following:
import abc
from typing import Any, Dict, List, Set
class PluginV2(abc.ABC):
@classmethod
@abc.abstractmethod
def get_schema(cls) -> Dict[str, Any]:
"""Return a jsonschema compatible dictionary for the plugin properties."""
def __init__(self, part_name: str, part_properties: Dict[str, str]) -> None:
"""
:param str name: part names
:param dict properties: part defined properties.
"""
@abc.abstractmethod
def get_build_snaps(self) -> Set[str]:
"""
Return a set of required packages to install in the build environment.
"""
@abc.abstractmethod
def get_build_packages(self) -> Set[str]:
"""
Return a set of required packages to install in the build environment.
"""
@abc.abstractmethod
def get_build_environment(self) -> Dict[str, str]:
"""
Return a dictionary with the environment to use in the build step.
For consistency, the keys should be defined as SNAPCRAFT_<PLUGIN>_<KEY>.
This method is called by the PluginHandler during the "build" step.
"""
@abc.abstractmethod
def get_build_commands(self) -> List[str]:
"""
Return a list of commands to run during the build step.
This method is called by the PluginHandler during the "build" step.
These commands are run in a single shell instance. This means
that commands run before do affect the commands that follow.
snapcraftctl can be used in the script to call out to snapcraft
specific functionality.
"""
A common difference from previous implementations is that only build needs to be implemented. This leaves source handling through the pull step a pure Snapcraft core concern.
This new plugin design does not encourage importing any of the Snapcraft
internals for a plugin to actually work, this means that most, if not **all**
plugins, aside from the need of importing the base plugin will lack an import
snapcraft
or from snapcraft import
statement for the plugin to be functional.
The responsibility of running through the build step is done by
PluginHandler.build
, to do so, during .build
what will happen to construct
the build script at a high level is:
- A single environment dictionary is created.
- Snapcraft’s internal
get_build_environment
method is called to update the environment dictionary. - The plugin’s
get_build_environment
method is called to updated the environment dictionary. - The Part’s
build-environment
key is retrieved and used to update the dictionary.
This is a powerful mechanism to give the Snapcraft Snap author the necessary control to override any undesired behavior or tune specific ones as well.
As an example, this would be a simplified implementation of the samurai plugin (a fake ninja based build tool that uses make syntax):
"""
Plugin help
"""
from typing import Any, Dict, List
import snapcraft
class SamuraiPlugin(snapcraft.plugins.v2.PluginV2):
def get_schema(cls) -> Dict[str, Any]:
return dict()
def get_build_environment(self) -> Dict[str, str]:
environment = super().get_environment()
environment.update(
{
"SNAPCRAFT_SAMURAI_ARGS": "--seppuku",
}
)
return environment
def get_build_commands(self) -> List[str]:
commands = super().get_build_commands()
commands.extend(
[
"samurai -j $SNAPCRAFT_SAMURAI_ARGS $SNAPCRAFT_PARALLEL_BUILD_COUNT",
"samurai install DESTDIR=$SNAPCRAFT_PART_INSTALL",
]
)
return commands
Wherever relevant, the Snapcraft commands shall default to the base
declared
in snapcraft.yaml
, otherwise to the latest supported base.
In all cases, Snapcraft shall inform the base that is being used to present information.
A mechanism must exist to be able to invoke Snapcraft commands for a plugin that would apply to different base than the one from the current project or default.
The --base
option is the preferred way to specify output targeting a specific
base.
$ snapcraft help --base=core python Displaying help for the 'python' plugin for 'core20' <plugin-help>
$ snapcraft list-plugins --base=core Displaying plugins available for core <plugin-list>
This is a new command, meant to expose the plugins behavior in an easy way to
the snapcraft.yaml
author. Given the plugin described in Example, with the
following snapcraft.yaml
name: project
base: core20
summary: use of the samurai plugin
description: an example meant to expand the samurai plugin
parts:
samurai-part:
source: .
plugin: samurai
It can be expanded (or explained), by running:
$ snapcraft expand name: project base: core20 summary: use of the samurai plugin description: an example meant to expand the samurai plugin parts: samurai-part: source: . plugin: samurai build-environment: SNAPCRAFT_SAMURAI_ARGS: "--seppuku" override-build: | samurai -j $SNAPCRAFT_PARALLEL_BUILD_COUNT samurai $SNAPCRAFT_SAMURAI_INSTALL_TARGET DESTDIR=$SNAPCRAFT_PART_INSTALL