diff options
author | bst-marge-bot <marge-bot@buildstream.build> | 2020-04-29 09:06:05 +0000 |
---|---|---|
committer | bst-marge-bot <marge-bot@buildstream.build> | 2020-04-29 09:06:05 +0000 |
commit | e70d77f997b7e94b4f7b8901fdec0973e2e9328c (patch) | |
tree | 5ebf22492da221cf0c606a56036cec39eb8ce450 | |
parent | 82eb1d4271bb634f248bc9e1770119d2815d7cd6 (diff) | |
parent | 1339b0e56c5a685d5da0f617a0b6f205c03d1251 (diff) | |
download | buildstream-e70d77f997b7e94b4f7b8901fdec0973e2e9328c.tar.gz |
Merge branch 'tristan/plugin-versions-refactor' into 'master'
Plugin loading refactor, implement BST_MIN_VERSION
See merge request BuildStream/buildstream!1885
114 files changed, 729 insertions, 692 deletions
diff --git a/doc/examples/flatpak-autotools/project.conf b/doc/examples/flatpak-autotools/project.conf index 08dc6ee32..d7a0a396d 100644 --- a/doc/examples/flatpak-autotools/project.conf +++ b/doc/examples/flatpak-autotools/project.conf @@ -19,4 +19,4 @@ plugins: - origin: pip package-name: bst-plugins-experimental sources: - ostree: 0 + - ostree @@ -9,7 +9,7 @@ parentdir_prefix = BuildStream- [tool:pytest] addopts = --verbose --basetemp ./tmp --durations=20 --timeout=1800 -norecursedirs = src tests/integration/project integration-cache tmp __pycache__ .eggs +norecursedirs = src tests/integration/project tests/plugins/loading integration-cache tmp __pycache__ .eggs python_files = tests/*/*.py env = D:BST_TEST_SUITE=True diff --git a/src/buildstream/_exceptions.py b/src/buildstream/_exceptions.py index f92ab822b..30fc26e71 100644 --- a/src/buildstream/_exceptions.py +++ b/src/buildstream/_exceptions.py @@ -124,8 +124,8 @@ class BstError(Exception): # or by the base :class:`.Plugin` element itself. # class PluginError(BstError): - def __init__(self, message, reason=None, temporary=False): - super().__init__(message, domain=ErrorDomain.PLUGIN, reason=reason, temporary=temporary) + def __init__(self, message, *, reason=None, detail=None, temporary=False): + super().__init__(message, domain=ErrorDomain.PLUGIN, detail=detail, reason=reason, temporary=temporary) # LoadError diff --git a/src/buildstream/_pluginfactory/__init__.py b/src/buildstream/_pluginfactory/__init__.py new file mode 100644 index 000000000..fe69b6e77 --- /dev/null +++ b/src/buildstream/_pluginfactory/__init__.py @@ -0,0 +1,20 @@ +# +# Copyright (C) 2020 Codethink Limited +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 library. If not, see <http://www.gnu.org/licenses/>. +# + +from .pluginorigin import PluginOrigin, PluginOriginType +from .sourcefactory import SourceFactory +from .elementfactory import ElementFactory diff --git a/src/buildstream/_elementfactory.py b/src/buildstream/_pluginfactory/elementfactory.py index 89e01a885..8879a4173 100644 --- a/src/buildstream/_elementfactory.py +++ b/src/buildstream/_pluginfactory/elementfactory.py @@ -17,9 +17,10 @@ # Authors: # Tristan Van Berkom <tristan.vanberkom@codethink.co.uk> -from . import _site -from ._plugincontext import PluginContext -from .element import Element +from .. import _site +from ..element import Element + +from .pluginfactory import PluginFactory # A ElementFactory creates Element instances @@ -27,18 +28,12 @@ from .element import Element # # Args: # plugin_base (PluginBase): The main PluginBase object to work with -# plugin_origins (list): Data used to search for external Element plugins # -class ElementFactory(PluginContext): - def __init__(self, plugin_base, *, format_versions={}, plugin_origins=None): +class ElementFactory(PluginFactory): + def __init__(self, plugin_base): super().__init__( - plugin_base, - Element, - [_site.element_plugins], - "buildstream.plugins.elements", - plugin_origins=plugin_origins, - format_versions=format_versions, + plugin_base, Element, [_site.element_plugins], "buildstream.plugins.elements", ) # create(): @@ -60,6 +55,4 @@ class ElementFactory(PluginContext): def create(self, context, project, meta): element_type, default_config = self.lookup(meta.kind) element = element_type(context, project, meta, default_config) - version = self._format_versions.get(meta.kind, 0) - self._assert_plugin_format(element, version) return element diff --git a/src/buildstream/_plugincontext.py b/src/buildstream/_pluginfactory/pluginfactory.py index 3a195e239..27321c62f 100644 --- a/src/buildstream/_plugincontext.py +++ b/src/buildstream/_pluginfactory/pluginfactory.py @@ -20,9 +20,11 @@ import os import inspect -from ._exceptions import PluginError, LoadError -from .exceptions import LoadErrorReason -from . import utils +from .. import utils +from ..utils import UtilError +from .._exceptions import PluginError + +from .pluginorigin import PluginOrigin, PluginOriginType # A Context for loading plugin types @@ -32,9 +34,6 @@ from . import utils # base_type (type): A base object type for this context # site_plugin_path (str): Path to where buildstream keeps plugins # entrypoint_group (str): Name of the entry point group that provides plugins -# plugin_origins (list): Data used to search for plugins -# format_versions (dict): A dict of meta.kind to the integer minimum -# version number for each plugin to be loaded # # Since multiple pipelines can be processed recursively # within the same interpretor, it's important that we have @@ -43,16 +42,8 @@ from . import utils # a given BuildStream project are isolated to their respective # Pipelines. # -class PluginContext: - def __init__( - self, plugin_base, base_type, site_plugin_path, entrypoint_group, *, plugin_origins=None, format_versions={} - ): - - # For pickling across processes, make sure this context has a unique - # identifier, which we prepend to the identifier of each PluginSource. - # This keeps plugins loaded during the first and second pass distinct - # from eachother. - self._identifier = str(id(self)) +class PluginFactory: + def __init__(self, plugin_base, base_type, site_plugin_path, entrypoint_group): # The plugin kinds which were loaded self.loaded_dependencies = [] @@ -60,16 +51,22 @@ class PluginContext: # # Private members # + + # For pickling across processes, make sure this context has a unique + # identifier, which we prepend to the identifier of each PluginSource. + # This keeps plugins loaded during the first and second pass distinct + # from eachother. + self._identifier = str(id(self)) + self._base_type = base_type # The base class plugins derive from self._types = {} # Plugin type lookup table by kind - self._plugin_origins = plugin_origins or [] + self._origins = {} # PluginOrigin lookup table by kind # The PluginSource object self._plugin_base = plugin_base self._site_plugin_path = site_plugin_path self._entrypoint_group = entrypoint_group self._alternate_sources = {} - self._format_versions = format_versions self._init_site_source() @@ -127,6 +124,23 @@ class PluginContext: def lookup(self, kind): return self._ensure_plugin(kind) + # register_plugin_origin(): + # + # Registers the PluginOrigin to use for the given plugin kind + # + # Args: + # kind (str): The kind identifier of the Plugin + # origin (PluginOrigin): The PluginOrigin providing the plugin + # + def register_plugin_origin(self, kind: str, origin: PluginOrigin): + if kind in self._origins: + raise PluginError( + "More than one {} plugin registered as kind '{}'".format(self._base_type.__name__, kind), + reason="duplicate-plugin", + ) + + self._origins[kind] = origin + # all_loaded_plugins(): # # Returns: an iterable over all the loaded plugins. @@ -186,39 +200,27 @@ class PluginContext: def _ensure_plugin(self, kind): if kind not in self._types: - # Check whether the plugin is specified in plugins source = None defaults = None - loaded_dependency = False - - for origin in self._plugin_origins: - if kind not in origin.get_str_list("plugins"): - continue - - if origin.get_str("origin") == "local": - local_path = origin.get_str("path") - source = self._get_local_plugin_source(local_path) - elif origin.get_str("origin") == "pip": - package_name = origin.get_str("package-name") - source, defaults = self._get_pip_plugin_source(package_name, kind) + + origin = self._origins.get(kind, None) + if origin: + # Try getting the plugin source from a registered origin + if origin.origin_type == PluginOriginType.LOCAL: + source = self._get_local_plugin_source(origin.path) + elif origin.origin_type == PluginOriginType.PIP: + source, defaults = self._get_pip_plugin_source(origin.package_name, kind) else: - raise PluginError( - "Failed to load plugin '{}': " - "Unexpected plugin origin '{}'".format(kind, origin.get_str("origin")) - ) - loaded_dependency = True - break - - # Fall back to getting the source from site - if not source: + assert False, "Encountered invalid plugin origin type" + else: + # Try getting it from the core plugins if kind not in self._site_source.list_plugins(): raise PluginError("No {} type registered for kind '{}'".format(self._base_type.__name__, kind)) source = self._site_source self._types[kind] = self._load_plugin(source, kind, defaults) - if loaded_dependency: - self.loaded_dependencies.append(kind) + self.loaded_dependencies.append(kind) return self._types[kind] @@ -240,15 +242,18 @@ class PluginContext: plugin_type = plugin.setup() except AttributeError as e: raise PluginError( - "{} plugin '{}' did not provide a setup() function".format(self._base_type.__name__, kind) + "{} plugin '{}' did not provide a setup() function".format(self._base_type.__name__, kind), + reason="missing-setup-function", ) from e except TypeError as e: raise PluginError( - "setup symbol in {} plugin '{}' is not a function".format(self._base_type.__name__, kind) + "setup symbol in {} plugin '{}' is not a function".format(self._base_type.__name__, kind), + reason="setup-is-not-function", ) from e self._assert_plugin(kind, plugin_type) - self._assert_version(kind, plugin_type) + self._assert_min_version(kind, plugin_type) + return (plugin_type, defaults) def _assert_plugin(self, kind, plugin_type): @@ -262,45 +267,54 @@ class PluginContext: raise PluginError( "{} plugin '{}' returned type '{}', which is not a subclass of {}".format( self._base_type.__name__, kind, plugin_type.__name__, self._base_type.__name__ - ) + ), + reason="setup-returns-bad-type", ) except TypeError as e: raise PluginError( "{} plugin '{}' returned something that is not a type (expected subclass of {})".format( self._base_type.__name__, kind, self._base_type.__name__ - ) + ), + reason="setup-returns-not-type", ) from e - def _assert_version(self, kind, plugin_type): + def _assert_min_version(self, kind, plugin_type): - # Now assert BuildStream version - bst_major, bst_minor = utils.get_bst_version() + if plugin_type.BST_MIN_VERSION is None: + raise PluginError( + "{} plugin '{}' did not specify BST_MIN_VERSION".format(self._base_type.__name__, kind), + reason="missing-min-version", + detail="Are you trying to use a BuildStream 1 plugin with a BuildStream 2 project ?", + ) - req_major = plugin_type.BST_REQUIRED_VERSION_MAJOR - req_minor = plugin_type.BST_REQUIRED_VERSION_MINOR + try: + min_version_major, min_version_minor = utils._parse_version(plugin_type.BST_MIN_VERSION) + except UtilError as e: + raise PluginError( + "{} plugin '{}' specified malformed BST_MIN_VERSION: {}".format( + self._base_type.__name__, kind, plugin_type.BST_MIN_VERSION + ), + reason="malformed-min-version", + detail="BST_MIN_VERSION must be specified as 'MAJOR.MINOR' with " + + "numeric major and minor minimum required version numbers", + ) from e - if (bst_major, bst_minor) < (req_major, req_minor): + bst_major, bst_minor = utils._get_bst_api_version() + + if min_version_major != bst_major: raise PluginError( - "BuildStream {}.{} is too old for {} plugin '{}' (requires {}.{})".format( - bst_major, - bst_minor, - self._base_type.__name__, - kind, - plugin_type.BST_REQUIRED_VERSION_MAJOR, - plugin_type.BST_REQUIRED_VERSION_MINOR, - ) + "{} plugin '{}' requires BuildStream {}, but is being loaded with BuildStream {}".format( + self._base_type.__name__, kind, min_version_major, bst_major + ), + reason="incompatible-major-version", + detail="You will need to find the correct version of this plugin for your project.", ) - # _assert_plugin_format() - # - # Helper to raise a PluginError if the loaded plugin is of a lesser version then - # the required version for this plugin - # - def _assert_plugin_format(self, plugin, version): - if plugin.BST_FORMAT_VERSION < version: - raise LoadError( - "{}: Format version {} is too old for requested version {}".format( - plugin, plugin.BST_FORMAT_VERSION, version + if min_version_minor > bst_minor: + raise PluginError( + "{} plugin '{}' requires BuildStream {}, but is being loaded with BuildStream {}.{}".format( + self._base_type.__name__, kind, plugin_type.BST_MIN_VERSION, bst_major, bst_minor ), - LoadErrorReason.UNSUPPORTED_PLUGIN, + reason="incompatible-minor-version", + detail="Please upgrade to BuildStream {}".format(plugin_type.BST_MIN_VERSION), ) diff --git a/src/buildstream/_pluginfactory/pluginorigin.py b/src/buildstream/_pluginfactory/pluginorigin.py new file mode 100644 index 000000000..50852711b --- /dev/null +++ b/src/buildstream/_pluginfactory/pluginorigin.py @@ -0,0 +1,130 @@ +# +# Copyright (C) 2020 Codethink Limited +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY 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 library. If not, see <http://www.gnu.org/licenses/>. +# + +import os + +from ..types import FastEnum + + +# PluginOriginType: +# +# An enumeration depicting the type of plugin origin +# +class PluginOriginType(FastEnum): + LOCAL = "local" + PIP = "pip" + + +# PluginOrigin +# +# Base class holding common properties of all origins. +# +class PluginOrigin: + + # Common fields valid for all plugin origins + _COMMON_CONFIG_KEYS = ["origin", "sources", "elements"] + + def __init__(self, origin_type): + + # Public + self.origin_type = origin_type + self.elements = [] + self.sources = [] + + # Private + self._project = None + self._kinds = {} + + # new_from_node() + # + # Load a PluginOrigin from the YAML in project.conf + # + # Args: + # project (Project): The project from whence this origin is loaded + # origin_node (MappingNode): The node defining this origin + # + # Returns: + # (PluginOrigin): The newly created PluginOrigin + # + @classmethod + def new_from_node(cls, project, origin_node): + + origin_type = origin_node.get_enum("origin", PluginOriginType) + + if origin_type == PluginOriginType.LOCAL: + origin = PluginOriginLocal() + elif origin_type == PluginOriginType.PIP: + origin = PluginOriginPip() + + origin._project = project + origin._load(origin_node) + + origin.elements = origin_node.get_str_list("elements", []) + origin.sources = origin_node.get_str_list("sources", []) + + return origin + + # _load() + # + # Abstract method for loading data from the origin node, this + # method should not load the source and element lists. + # + # Args: + # origin_node (MappingNode): The node defining this origin + # + def _load(self, origin_node): + pass + + +# PluginOriginLocal +# +# PluginOrigin for local plugins +# +class PluginOriginLocal(PluginOrigin): + def __init__(self): + super().__init__(PluginOriginType.LOCAL) + + # An absolute path to where the plugin can be found + # + self.path = None + + def _load(self, origin_node): + + origin_node.validate_keys(["path", *PluginOrigin._COMMON_CONFIG_KEYS]) + + path_node = origin_node.get_scalar("path") + path = self._project.get_path_from_node(path_node, check_is_dir=True) + + self.path = os.path.join(self._project.directory, path) + + +# PluginOriginPip +# +# PluginOrigin for pip plugins +# +class PluginOriginPip(PluginOrigin): + def __init__(self): + super().__init__(PluginOriginType.PIP) + + # The pip package name to extract plugins from + # + self.package_name = None + + def _load(self, origin_node): + + origin_node.validate_keys(["package-name", *PluginOrigin._COMMON_CONFIG_KEYS]) + self.package_name = origin_node.get_str("package-name") diff --git a/src/buildstream/_sourcefactory.py b/src/buildstream/_pluginfactory/sourcefactory.py index 254c482d6..9f6a09784 100644 --- a/src/buildstream/_sourcefactory.py +++ b/src/buildstream/_pluginfactory/sourcefactory.py @@ -17,9 +17,10 @@ # Authors: # Tristan Van Berkom <tristan.vanberkom@codethink.co.uk> -from . import _site -from ._plugincontext import PluginContext -from .source import Source +from .. import _site +from ..source import Source + +from .pluginfactory import PluginFactory # A SourceFactory creates Source instances @@ -27,18 +28,12 @@ from .source import Source # # Args: # plugin_base (PluginBase): The main PluginBase object to work with -# plugin_origins (list): Data used to search for external Source plugins # -class SourceFactory(PluginContext): - def __init__(self, plugin_base, *, format_versions={}, plugin_origins=None): +class SourceFactory(PluginFactory): + def __init__(self, plugin_base): super().__init__( - plugin_base, - Source, - [_site.source_plugins], - "buildstream.plugins.sources", - format_versions=format_versions, - plugin_origins=plugin_origins, + plugin_base, Source, [_site.source_plugins], "buildstream.plugins.sources", ) # create(): @@ -61,6 +56,4 @@ class SourceFactory(PluginContext): def create(self, context, project, meta): source_type, _ = self.lookup(meta.kind) source = source_type(context, project, meta) - version = self._format_versions.get(meta.kind, 0) - self._assert_plugin_format(source, version) return source diff --git a/src/buildstream/_project.py b/src/buildstream/_project.py index 3527d211f..40524d7ad 100644 --- a/src/buildstream/_project.py +++ b/src/buildstream/_project.py @@ -36,13 +36,11 @@ from ._artifactcache import ArtifactCache from ._sourcecache import SourceCache from .node import ScalarNode, SequenceNode, _assert_symbol_name from .sandbox import SandboxRemote -from ._elementfactory import ElementFactory -from ._sourcefactory import SourceFactory +from ._pluginfactory import ElementFactory, SourceFactory, PluginOrigin from .types import CoreWarnings from ._projectrefs import ProjectRefs, ProjectRefStorage from ._loader import Loader from .element import Element -from .types import FastEnum from ._message import Message, MessageType from ._includes import Includes from ._workspaces import WORKSPACE_PROJECT_FILE @@ -52,13 +50,6 @@ from ._workspaces import WORKSPACE_PROJECT_FILE _PROJECT_CONF_FILE = "project.conf" -# List of all places plugins can come from -class PluginOrigins(FastEnum): - CORE = "core" - LOCAL = "local" - PIP = "pip" - - # HostMount() # # A simple object describing the behavior of @@ -951,87 +942,19 @@ class Project: return project_directory, workspace_element def _load_plugin_factories(self, config, output): - plugin_source_origins = [] # Origins of custom sources - plugin_element_origins = [] # Origins of custom elements - - # Plugin origins and versions - origins = config.get_sequence("plugins", default=[]) - source_format_versions = {} - element_format_versions = {} - for origin in origins: - allowed_origin_fields = [ - "origin", - "sources", - "elements", - "package-name", - "path", - ] - origin.validate_keys(allowed_origin_fields) - - # Store source versions for checking later - source_versions = origin.get_mapping("sources", default={}) - for key in source_versions.keys(): - if key in source_format_versions: - raise LoadError("Duplicate listing of source '{}'".format(key), LoadErrorReason.INVALID_YAML) - source_format_versions[key] = source_versions.get_int(key) - - # Store element versions for checking later - element_versions = origin.get_mapping("elements", default={}) - for key in element_versions.keys(): - if key in element_format_versions: - raise LoadError("Duplicate listing of element '{}'".format(key), LoadErrorReason.INVALID_YAML) - element_format_versions[key] = element_versions.get_int(key) - - # Store the origins if they're not 'core'. - # core elements are loaded by default, so storing is unnecessary. - origin_value = origin.get_enum("origin", PluginOrigins) - - if origin_value != PluginOrigins.CORE: - self._store_origin(origin, "sources", plugin_source_origins) - self._store_origin(origin, "elements", plugin_element_origins) - + # Create the factories pluginbase = PluginBase(package="buildstream.plugins") - output.element_factory = ElementFactory( - pluginbase, plugin_origins=plugin_element_origins, format_versions=element_format_versions - ) - output.source_factory = SourceFactory( - pluginbase, plugin_origins=plugin_source_origins, format_versions=source_format_versions - ) + output.element_factory = ElementFactory(pluginbase) + output.source_factory = SourceFactory(pluginbase) - # _store_origin() - # - # Helper function to store plugin origins - # - # Args: - # origin (node) - a node indicating the origin of a group of - # plugins. - # plugin_group (str) - The name of the type of plugin that is being - # loaded - # destination (list) - A list of nodes to store the origins in - # - # Raises: - # LoadError if 'origin' is an unexpected value - def _store_origin(self, origin, plugin_group, destination): - expected_groups = ["sources", "elements"] - if plugin_group not in expected_groups: - raise LoadError( - "Unexpected plugin group: {}, expecting {}".format(plugin_group, expected_groups), - LoadErrorReason.INVALID_DATA, - ) - if plugin_group in origin.keys(): - origin_node = origin.clone() - plugins = origin.get_mapping(plugin_group, default={}) - origin_node["plugins"] = plugins.keys() - - for group in expected_groups: - if group in origin_node: - del origin_node[group] - - if origin_node.get_enum("origin", PluginOrigins) == PluginOrigins.LOCAL: - path = self.get_path_from_node(origin.get_scalar("path"), check_is_dir=True) - # paths are passed in relative to the project, but must be absolute - origin_node["path"] = os.path.join(self.directory, path) - destination.append(origin_node) + # Load the plugin origins and register them to their factories + origins = config.get_sequence("plugins", default=[]) + for origin_node in origins: + origin = PluginOrigin.new_from_node(self, origin_node) + for kind in origin.elements: + output.element_factory.register_plugin_origin(kind, origin) + for kind in origin.sources: + output.source_factory.register_plugin_origin(kind, origin) # _warning_is_fatal(): # diff --git a/src/buildstream/plugin.py b/src/buildstream/plugin.py index 2182f5a08..935db6523 100644 --- a/src/buildstream/plugin.py +++ b/src/buildstream/plugin.py @@ -148,25 +148,30 @@ class Plugin: which are included in the buildstream namespace. """ - BST_REQUIRED_VERSION_MAJOR = 0 - """Minimum required major version""" + BST_MIN_VERSION = None + """The minimum required version of BuildStream required by this plugin. - BST_REQUIRED_VERSION_MINOR = 0 - """Minimum required minor version""" + The version must be expressed as the string *"<major>.<minor>"*, where the + *major* version number is the API version and the *minor* version number is + the revision of the same BuildStream API where new symbols might have been + added to the API. - BST_FORMAT_VERSION = 0 - """The plugin's YAML format version + **Example:** - This should be set to ``1`` the first time any new configuration - is understood by your :func:`Plugin.configure() <buildstream.plugin.Plugin.configure>` - implementation and subsequently bumped every time your - configuration is enhanced. + The following statement means that this plugin works with *BuildStream 2.X*, + only if *X >= 2*: + + .. code:: python + + class Foo(Source): + + # Our plugin requires 2.2 + BST_MIN_VERSION = "2.2" .. note:: - Plugins are expected to maintain backward compatibility - in the format and configurations they expose. The versioning - is intended to track availability of new features only. + This version works exactly the same was as the :ref:`min-version <project_min_version>` + which must be specified in the project.conf file. """ BST_PLUGIN_DEPRECATED = False diff --git a/src/buildstream/plugins/elements/autotools.py b/src/buildstream/plugins/elements/autotools.py index c4396c5cc..5f5a1b465 100644 --- a/src/buildstream/plugins/elements/autotools.py +++ b/src/buildstream/plugins/elements/autotools.py @@ -60,6 +60,10 @@ from buildstream import BuildElement, SandboxFlags # Element implementation for the 'autotools' kind. class AutotoolsElement(BuildElement): + # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" + # Enable command batching across prepare() and assemble() def configure_sandbox(self, sandbox): super().configure_sandbox(sandbox) diff --git a/src/buildstream/plugins/elements/compose.py b/src/buildstream/plugins/elements/compose.py index 6e4aed193..9c136df9d 100644 --- a/src/buildstream/plugins/elements/compose.py +++ b/src/buildstream/plugins/elements/compose.py @@ -41,6 +41,8 @@ from buildstream import Element, Scope class ComposeElement(Element): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + # The compose element's output is its dependencies, so # we must rebuild if the dependencies change even when # not in strict build plans. diff --git a/src/buildstream/plugins/elements/filter.py b/src/buildstream/plugins/elements/filter.py index 37b205fe2..62c04e027 100644 --- a/src/buildstream/plugins/elements/filter.py +++ b/src/buildstream/plugins/elements/filter.py @@ -140,6 +140,8 @@ from buildstream import Element, ElementError, Scope class FilterElement(Element): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + BST_ARTIFACT_VERSION = 1 # The filter element's output is its dependencies, so diff --git a/src/buildstream/plugins/elements/import.py b/src/buildstream/plugins/elements/import.py index 2bbd11354..d9961aa6f 100644 --- a/src/buildstream/plugins/elements/import.py +++ b/src/buildstream/plugins/elements/import.py @@ -38,6 +38,8 @@ from buildstream import Element, ElementError class ImportElement(Element): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + # Import elements do not run any commands BST_RUN_COMMANDS = False diff --git a/src/buildstream/plugins/elements/junction.py b/src/buildstream/plugins/elements/junction.py index 46c4ddf68..917482d63 100644 --- a/src/buildstream/plugins/elements/junction.py +++ b/src/buildstream/plugins/elements/junction.py @@ -165,6 +165,8 @@ from buildstream._pipeline import PipelineError class JunctionElement(Element): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + # Junctions are not allowed any dependencies BST_FORBID_BDEPENDS = True BST_FORBID_RDEPENDS = True diff --git a/src/buildstream/plugins/elements/manual.py b/src/buildstream/plugins/elements/manual.py index c95b8e5a2..125c5142b 100644 --- a/src/buildstream/plugins/elements/manual.py +++ b/src/buildstream/plugins/elements/manual.py @@ -36,6 +36,10 @@ from buildstream import BuildElement, SandboxFlags # Element implementation for the 'manual' kind. class ManualElement(BuildElement): + # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" + # Enable command batching across prepare() and assemble() def configure_sandbox(self, sandbox): super().configure_sandbox(sandbox) diff --git a/src/buildstream/plugins/elements/pip.py b/src/buildstream/plugins/elements/pip.py index 06e187b69..5f805f460 100644 --- a/src/buildstream/plugins/elements/pip.py +++ b/src/buildstream/plugins/elements/pip.py @@ -36,6 +36,10 @@ from buildstream import BuildElement, SandboxFlags # Element implementation for the 'pip' kind. class PipElement(BuildElement): + # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" + # Enable command batching across prepare() and assemble() def configure_sandbox(self, sandbox): super().configure_sandbox(sandbox) diff --git a/src/buildstream/plugins/elements/script.py b/src/buildstream/plugins/elements/script.py index e355e5f05..9d780ebe2 100644 --- a/src/buildstream/plugins/elements/script.py +++ b/src/buildstream/plugins/elements/script.py @@ -40,6 +40,10 @@ import buildstream # Element implementation for the 'script' kind. class ScriptElement(buildstream.ScriptElement): + # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" + def configure(self, node): for n in node.get_sequence("layout", []): dst = self.node_subst_vars(n.get_scalar("destination")) diff --git a/src/buildstream/plugins/elements/stack.py b/src/buildstream/plugins/elements/stack.py index df11a257f..b15f67073 100644 --- a/src/buildstream/plugins/elements/stack.py +++ b/src/buildstream/plugins/elements/stack.py @@ -29,6 +29,9 @@ from buildstream import Element # Element implementation for the 'stack' kind. class StackElement(Element): + # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" # This plugin does not produce any artifacts when built BST_ELEMENT_HAS_ARTIFACT = False diff --git a/src/buildstream/plugins/sources/bzr.py b/src/buildstream/plugins/sources/bzr.py index 8a02eff95..93b554472 100644 --- a/src/buildstream/plugins/sources/bzr.py +++ b/src/buildstream/plugins/sources/bzr.py @@ -66,6 +66,8 @@ from buildstream import utils class BzrSource(Source): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + def configure(self, node): node.validate_keys(["url", "track", "ref", *Source.COMMON_CONFIG_KEYS]) diff --git a/src/buildstream/plugins/sources/git.py b/src/buildstream/plugins/sources/git.py index 473166132..6427d4b04 100644 --- a/src/buildstream/plugins/sources/git.py +++ b/src/buildstream/plugins/sources/git.py @@ -156,7 +156,8 @@ from buildstream import _GitSourceBase class GitSource(_GitSourceBase): - pass + + BST_MIN_VERSION = "2.0" # Plugin entry point diff --git a/src/buildstream/plugins/sources/local.py b/src/buildstream/plugins/sources/local.py index 57bcf14df..c39e09417 100644 --- a/src/buildstream/plugins/sources/local.py +++ b/src/buildstream/plugins/sources/local.py @@ -44,6 +44,8 @@ from buildstream import Source, SourceError class LocalSource(Source): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + BST_STAGE_VIRTUAL_DIRECTORY = True BST_KEY_REQUIRES_STAGE = True diff --git a/src/buildstream/plugins/sources/patch.py b/src/buildstream/plugins/sources/patch.py index f33dfedec..18672df8a 100644 --- a/src/buildstream/plugins/sources/patch.py +++ b/src/buildstream/plugins/sources/patch.py @@ -52,6 +52,8 @@ from buildstream import utils class PatchSource(Source): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + BST_REQUIRES_PREVIOUS_SOURCES_STAGE = True def configure(self, node): diff --git a/src/buildstream/plugins/sources/pip.py b/src/buildstream/plugins/sources/pip.py index 69c08e81f..c0885ce5a 100644 --- a/src/buildstream/plugins/sources/pip.py +++ b/src/buildstream/plugins/sources/pip.py @@ -98,6 +98,8 @@ _SDIST_RE = re.compile(r"^([\w.-]+?)-((?:[\d.]+){2,})\.(?:tar|tar.bz2|tar.gz|tar class PipSource(Source): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + # We need access to previous sources at track time to use requirements.txt # but not at fetch time as self.ref should contain sufficient information # for this plugin diff --git a/src/buildstream/plugins/sources/remote.py b/src/buildstream/plugins/sources/remote.py index 29333c0d9..57d8743a7 100644 --- a/src/buildstream/plugins/sources/remote.py +++ b/src/buildstream/plugins/sources/remote.py @@ -55,6 +55,8 @@ from ._downloadablefilesource import DownloadableFileSource class RemoteSource(DownloadableFileSource): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + def configure(self, node): super().configure(node) diff --git a/src/buildstream/plugins/sources/tar.py b/src/buildstream/plugins/sources/tar.py index fc4f5736a..793fd11c9 100644 --- a/src/buildstream/plugins/sources/tar.py +++ b/src/buildstream/plugins/sources/tar.py @@ -92,6 +92,8 @@ class ReadableTarInfo(tarfile.TarInfo): class TarSource(DownloadableFileSource): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + def configure(self, node): super().configure(node) diff --git a/src/buildstream/plugins/sources/workspace.py b/src/buildstream/plugins/sources/workspace.py index 13e2bb37d..796f2b3d9 100644 --- a/src/buildstream/plugins/sources/workspace.py +++ b/src/buildstream/plugins/sources/workspace.py @@ -45,6 +45,9 @@ from buildstream.node import MappingNode class WorkspaceSource(Source): # pylint: disable=attribute-defined-outside-init + + BST_MIN_VERSION = "2.0" + BST_STAGE_VIRTUAL_DIRECTORY = True BST_KEY_REQUIRES_STAGE = True diff --git a/src/buildstream/plugins/sources/zip.py b/src/buildstream/plugins/sources/zip.py index 47823dfde..c112cf10e 100644 --- a/src/buildstream/plugins/sources/zip.py +++ b/src/buildstream/plugins/sources/zip.py @@ -69,6 +69,8 @@ from ._downloadablefilesource import DownloadableFileSource class ZipSource(DownloadableFileSource): # pylint: disable=attribute-defined-outside-init + BST_MIN_VERSION = "2.0" + def configure(self, node): super().configure(node) diff --git a/src/buildstream/testing/_sourcetests/utils.py b/src/buildstream/testing/_sourcetests/utils.py index 116506807..4cfb696bd 100644 --- a/src/buildstream/testing/_sourcetests/utils.py +++ b/src/buildstream/testing/_sourcetests/utils.py @@ -71,7 +71,7 @@ def add_plugins_conf(project, plugin_kind): if plugin_package is not None: project_conf["plugins"] = [ - {"origin": "pip", "package-name": plugin_package, "sources": {plugin_kind: 0,},}, + {"origin": "pip", "package-name": plugin_package, "sources": [plugin_kind],}, ] _yaml.roundtrip_dump(project_conf, project_conf_file) diff --git a/tests/elements/filter/basic/element_plugins/dynamic.py b/tests/elements/filter/basic/element_plugins/dynamic.py index bf079111f..db92a6647 100644 --- a/tests/elements/filter/basic/element_plugins/dynamic.py +++ b/tests/elements/filter/basic/element_plugins/dynamic.py @@ -3,6 +3,9 @@ from buildstream import Element, Scope # Copies files from the dependent element but inserts split-rules using dynamic data class DynamicElement(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, node): node.validate_keys(["split-rules"]) self.split_rules = {key: value.as_str_list() for key, value in node.get_mapping("split-rules").items()} diff --git a/tests/elements/filter/basic/project.conf b/tests/elements/filter/basic/project.conf index e0b487d01..023943f79 100644 --- a/tests/elements/filter/basic/project.conf +++ b/tests/elements/filter/basic/project.conf @@ -5,4 +5,4 @@ plugins: - origin: local path: element_plugins elements: - dynamic: 0 + - dynamic diff --git a/tests/format/project.py b/tests/format/project.py index c2e2f733f..c4b2a480a 100644 --- a/tests/format/project.py +++ b/tests/format/project.py @@ -177,7 +177,7 @@ def test_plugin_no_load_ref(cli, datafiles, ref_storage): "name": "test", "min-version": "2.0", "ref-storage": ref_storage, - "plugins": [{"origin": "local", "path": "plugins", "sources": {"noloadref": 0}}], + "plugins": [{"origin": "local", "path": "plugins", "sources": ["noloadref"]}], } _yaml.roundtrip_dump(config, os.path.join(project, "project.conf")) @@ -202,7 +202,7 @@ def test_plugin_preflight_error(cli, datafiles): def test_duplicate_plugins(cli, datafiles): project = os.path.join(datafiles.dirname, datafiles.basename, "duplicate-plugins") result = cli.run(project=project, silent=True, args=["show", "element.bst"]) - result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.INVALID_YAML) + result.assert_main_error(ErrorDomain.PLUGIN, "duplicate-plugin") # Assert that we get a different cache key for target.bst, depending diff --git a/tests/format/project/duplicate-plugins/project.conf b/tests/format/project/duplicate-plugins/project.conf index a413e6137..495ba0678 100644 --- a/tests/format/project/duplicate-plugins/project.conf +++ b/tests/format/project/duplicate-plugins/project.conf @@ -5,12 +5,12 @@ plugins: - origin: local path: bar elements: - foo: 0 + - foo sources: - frob: 0 + - frob - origin: local path: baz elements: - foo: 0 + - foo sources: - frob: 0 + - frob diff --git a/tests/format/project/local-plugin/project.conf b/tests/format/project/local-plugin/project.conf index ccb6ef433..7d8cc1aea 100644 --- a/tests/format/project/local-plugin/project.conf +++ b/tests/format/project/local-plugin/project.conf @@ -4,4 +4,4 @@ plugins: - origin: local path: plugins sources: - mysource: 0 + - mysource diff --git a/tests/format/project/plugin-allowed/plugins/foo.py b/tests/format/project/plugin-allowed/plugins/foo.py index bf884233c..76d9bfd3c 100644 --- a/tests/format/project/plugin-allowed/plugins/foo.py +++ b/tests/format/project/plugin-allowed/plugins/foo.py @@ -2,6 +2,9 @@ from buildstream import Element class FooElement(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, config): pass diff --git a/tests/format/project/plugin-allowed/project.conf b/tests/format/project/plugin-allowed/project.conf index 3896b7bc2..97107edf6 100644 --- a/tests/format/project/plugin-allowed/project.conf +++ b/tests/format/project/plugin-allowed/project.conf @@ -5,4 +5,4 @@ plugins: - origin: local path: plugins elements: - foo: 0 + - foo diff --git a/tests/format/project/plugin-forbidden/forbidden-plugins/forbidden-plugin.py b/tests/format/project/plugin-forbidden/forbidden-plugins/forbidden-plugin.py index bf884233c..76d9bfd3c 100644 --- a/tests/format/project/plugin-forbidden/forbidden-plugins/forbidden-plugin.py +++ b/tests/format/project/plugin-forbidden/forbidden-plugins/forbidden-plugin.py @@ -2,6 +2,9 @@ from buildstream import Element class FooElement(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, config): pass diff --git a/tests/format/project/plugin-no-load-ref/plugins/noloadref.py b/tests/format/project/plugin-no-load-ref/plugins/noloadref.py index e2fe0ac46..2b8fd0b51 100644 --- a/tests/format/project/plugin-no-load-ref/plugins/noloadref.py +++ b/tests/format/project/plugin-no-load-ref/plugins/noloadref.py @@ -6,6 +6,9 @@ from buildstream import Source # Use this to test that the core behaves as expected with such plugins. # class NoLoadRefSource(Source): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/format/project/plugin-preflight-error/errorplugin/preflighterror.py b/tests/format/project/plugin-preflight-error/errorplugin/preflighterror.py index db2895f8b..a03cb64ee 100644 --- a/tests/format/project/plugin-preflight-error/errorplugin/preflighterror.py +++ b/tests/format/project/plugin-preflight-error/errorplugin/preflighterror.py @@ -2,6 +2,9 @@ from buildstream import Source, SourceError class PreflightErrorSource(Source): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/format/project/plugin-preflight-error/project.conf b/tests/format/project/plugin-preflight-error/project.conf index 07d60daf2..ed1ddb99f 100644 --- a/tests/format/project/plugin-preflight-error/project.conf +++ b/tests/format/project/plugin-preflight-error/project.conf @@ -9,4 +9,4 @@ plugins: - origin: local path: errorplugin sources: - preflighterror: 0 + - preflighterror diff --git a/tests/frontend/configurable_warnings.py b/tests/frontend/configurable_warnings.py index e61db6c37..53409a512 100644 --- a/tests/frontend/configurable_warnings.py +++ b/tests/frontend/configurable_warnings.py @@ -18,9 +18,7 @@ def get_project(fatal_warnings): "name": "test", "min-version": "2.0", "element-path": "elements", - "plugins": [ - {"origin": "local", "path": "plugins", "elements": {"warninga": 0, "warningb": 0, "corewarn": 0,}} - ], + "plugins": [{"origin": "local", "path": "plugins", "elements": ["warninga", "warningb", "corewarn"]}], "fatal-warnings": fatal_warnings, } diff --git a/tests/frontend/configuredwarning/plugins/corewarn.py b/tests/frontend/configuredwarning/plugins/corewarn.py index bef0a4904..bcd40753c 100644 --- a/tests/frontend/configuredwarning/plugins/corewarn.py +++ b/tests/frontend/configuredwarning/plugins/corewarn.py @@ -3,6 +3,9 @@ from buildstream.plugin import CoreWarnings class CoreWarn(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/configuredwarning/plugins/warninga.py b/tests/frontend/configuredwarning/plugins/warninga.py index 9fd8dc61b..4ad0f3d20 100644 --- a/tests/frontend/configuredwarning/plugins/warninga.py +++ b/tests/frontend/configuredwarning/plugins/warninga.py @@ -4,6 +4,9 @@ WARNING_A = "warning-a" class WarningA(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/configuredwarning/plugins/warningb.py b/tests/frontend/configuredwarning/plugins/warningb.py index 64d25ef39..c7a995cf8 100644 --- a/tests/frontend/configuredwarning/plugins/warningb.py +++ b/tests/frontend/configuredwarning/plugins/warningb.py @@ -4,6 +4,9 @@ WARNING_B = "warning-b" class WarningB(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/configuredwarning/project.conf b/tests/frontend/configuredwarning/project.conf index 195ee176a..9d75ad18b 100644 --- a/tests/frontend/configuredwarning/project.conf +++ b/tests/frontend/configuredwarning/project.conf @@ -5,5 +5,5 @@ plugins: - origin: local path: element_plugins elements: - warninga: 0 - warningb: 0 + - warninga + - warningb diff --git a/tests/frontend/consistencyerror/plugins/consistencybug.py b/tests/frontend/consistencyerror/plugins/consistencybug.py index abcbbc997..1952894ca 100644 --- a/tests/frontend/consistencyerror/plugins/consistencybug.py +++ b/tests/frontend/consistencyerror/plugins/consistencybug.py @@ -2,6 +2,9 @@ from buildstream import Source class ConsistencyBugSource(Source): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/consistencyerror/plugins/consistencyerror.py b/tests/frontend/consistencyerror/plugins/consistencyerror.py index 2e30d4842..34af45782 100644 --- a/tests/frontend/consistencyerror/plugins/consistencyerror.py +++ b/tests/frontend/consistencyerror/plugins/consistencyerror.py @@ -2,6 +2,9 @@ from buildstream import Source, SourceError class ConsistencyErrorSource(Source): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/consistencyerror/project.conf b/tests/frontend/consistencyerror/project.conf index d28ba12df..f5c1b39c5 100644 --- a/tests/frontend/consistencyerror/project.conf +++ b/tests/frontend/consistencyerror/project.conf @@ -9,5 +9,5 @@ plugins: - origin: local path: plugins sources: - consistencyerror: 0 - consistencybug: 0 + - consistencyerror + - consistencybug diff --git a/tests/frontend/mirror.py b/tests/frontend/mirror.py index 848047ee8..bffc754e7 100644 --- a/tests/frontend/mirror.py +++ b/tests/frontend/mirror.py @@ -49,7 +49,7 @@ def generate_project(): {"name": "arrakis", "aliases": {"foo": ["OFO/"], "bar": ["RBA/"],},}, {"name": "oz", "aliases": {"foo": ["ooF/"], "bar": ["raB/"],}}, ], - "plugins": [{"origin": "local", "path": "sources", "sources": {"fetch_source": 0}}], + "plugins": [{"origin": "local", "path": "sources", "sources": ["fetch_source"]}], } return project diff --git a/tests/frontend/project/plugins/randomelement.py b/tests/frontend/project/plugins/randomelement.py index e9be98fc7..12afaaa6d 100644 --- a/tests/frontend/project/plugins/randomelement.py +++ b/tests/frontend/project/plugins/randomelement.py @@ -4,6 +4,9 @@ from buildstream import Element class RandomElement(Element): + + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/frontend/project/sources/fetch_source.py b/tests/frontend/project/sources/fetch_source.py index c62d1d29a..6078e5e5f 100644 --- a/tests/frontend/project/sources/fetch_source.py +++ b/tests/frontend/project/sources/fetch_source.py @@ -32,6 +32,9 @@ class FetchFetcher(SourceFetcher): class FetchSource(Source): + + BST_MIN_VERSION = "2.0" + # Read config to know which URLs to fetch def configure(self, node): self.original_urls = node.get_str_list("urls") diff --git a/tests/frontend/push.py b/tests/frontend/push.py index 362084372..970885784 100644 --- a/tests/frontend/push.py +++ b/tests/frontend/push.py @@ -641,7 +641,7 @@ def test_push_after_rebuild(cli, tmpdir, datafiles): config={ "element-path": "elements", "min-version": "2.0", - "plugins": [{"origin": "local", "path": "plugins", "elements": {"randomelement": 0}}], + "plugins": [{"origin": "local", "path": "plugins", "elements": ["randomelement"]}], }, ) diff --git a/tests/internals/pluginfactory.py b/tests/internals/pluginfactory.py deleted file mode 100644 index 13b9d3aae..000000000 --- a/tests/internals/pluginfactory.py +++ /dev/null @@ -1,287 +0,0 @@ -# Pylint doesn't play well with fixtures and dependency injection from pytest -# pylint: disable=redefined-outer-name - -import os -import pytest - -from pluginbase import PluginBase -from buildstream import Node -from buildstream._elementfactory import ElementFactory -from buildstream._sourcefactory import SourceFactory -from buildstream._exceptions import PluginError - -DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "pluginfactory",) - - -# Simple fixture to create a PluginBase object that -# we use for loading plugins. -@pytest.fixture() -def plugin_fixture(): - return {"base": PluginBase(package="buildstream.plugins")} - - -############################################################## -# Basics: test the fixture, test we can create the factories # -############################################################## -def test_fixture(plugin_fixture): - assert isinstance(plugin_fixture["base"], PluginBase) - - -def test_source_factory(plugin_fixture): - factory = SourceFactory(plugin_fixture["base"]) - assert isinstance(factory, SourceFactory) - - -def test_element_factory(plugin_fixture): - factory = ElementFactory(plugin_fixture["base"]) - assert isinstance(factory, ElementFactory) - - -############################################################## -# Check that we can load custom sources & elements # -############################################################## -@pytest.mark.datafiles(os.path.join(DATA_DIR, "customsource")) -def test_custom_source(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - assert isinstance(factory, SourceFactory) - - foo_type, _ = factory.lookup("foo") - assert foo_type.__name__ == "FooSource" - - -@pytest.mark.datafiles(os.path.join(DATA_DIR, "customelement")) -def test_custom_element(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - assert isinstance(factory, ElementFactory) - - foo_type, _ = factory.lookup("foo") - assert foo_type.__name__ == "FooElement" - - -############################################################## -# Check plugin loading failure modes # -############################################################## -def test_missing_source(plugin_fixture): - factory = SourceFactory(plugin_fixture["base"]) - assert isinstance(factory, SourceFactory) - - # Test fails if PluginError is not raised - with pytest.raises(PluginError): - factory.lookup("foo") - - -def test_missing_element(plugin_fixture): - factory = ElementFactory(plugin_fixture["base"]) - assert isinstance(factory, ElementFactory) - - # Test fails if PluginError is not raised - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin that returns a value instead of Source subclass -@pytest.mark.datafiles(os.path.join(DATA_DIR, "notatype")) -def test_source_notatype(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin that returns a value instead of Element subclass -@pytest.mark.datafiles(os.path.join(DATA_DIR, "notatype")) -def test_element_notatype(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin that returns a type -# which is not a Source subclass -@pytest.mark.datafiles(os.path.join(DATA_DIR, "wrongtype")) -def test_source_wrongtype(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin that returns a type -# which is not a Element subclass -@pytest.mark.datafiles(os.path.join(DATA_DIR, "wrongtype")) -def test_element_wrongtype(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which fails to provide a setup() function -@pytest.mark.datafiles(os.path.join(DATA_DIR, "nosetup")) -def test_source_missing_setup(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which fails to provide a setup() function -@pytest.mark.datafiles(os.path.join(DATA_DIR, "nosetup")) -def test_element_missing_setup(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which provides a setup symbol -# that is not a function -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badsetup")) -def test_source_bad_setup(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which provides a setup symbol -# that is not a function -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badsetup")) -def test_element_bad_setup(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which requires an absurdly -# high version of buildstream -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badversionsource")) -def test_source_badversion(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = SourceFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -# Load a factory with a plugin which requires an absurdly -# high version of buildstream -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badversionelement")) -def test_element_badversion(plugin_fixture, datafiles): - plugins = [ - Node.from_dict( - {"origin": "local", "path": os.path.join(datafiles.dirname, datafiles.basename), "plugins": ["foo"]} - ) - ] - factory = ElementFactory(plugin_fixture["base"], plugin_origins=plugins) - with pytest.raises(PluginError): - factory.lookup("foo") - - -############################################################## -# Check we can load different contexts of plugin # -############################################################## - -# Load two factories, both of which define a different 'foo' plugin -@pytest.mark.datafiles(DATA_DIR) -def test_source_multicontext(plugin_fixture, datafiles): - plugins1 = Node.from_dict( - { - "origin": "local", - "path": os.path.join(datafiles.dirname, datafiles.basename, "customsource"), - "plugins": ["foo"], - } - ) - plugins2 = Node.from_dict( - { - "origin": "local", - "path": os.path.join(datafiles.dirname, datafiles.basename, "anothersource"), - "plugins": ["foo"], - } - ) - - factory1 = SourceFactory(plugin_fixture["base"], plugin_origins=[plugins1]) - factory2 = SourceFactory(plugin_fixture["base"], plugin_origins=[plugins2]) - assert isinstance(factory1, SourceFactory) - assert isinstance(factory2, SourceFactory) - - foo_type1, _ = factory1.lookup("foo") - foo_type2, _ = factory2.lookup("foo") - assert foo_type1.__name__ == "FooSource" - assert foo_type2.__name__ == "AnotherFooSource" - - -# Load two factories, both of which define a different 'foo' plugin -@pytest.mark.datafiles(DATA_DIR) -def test_element_multicontext(plugin_fixture, datafiles): - plugins1 = Node.from_dict( - { - "origin": "local", - "path": os.path.join(datafiles.dirname, datafiles.basename, "customelement"), - "plugins": ["foo"], - } - ) - plugins2 = Node.from_dict( - { - "origin": "local", - "path": os.path.join(datafiles.dirname, datafiles.basename, "anotherelement"), - "plugins": ["foo"], - } - ) - - factory1 = ElementFactory(plugin_fixture["base"], plugin_origins=[plugins1]) - factory2 = ElementFactory(plugin_fixture["base"], plugin_origins=[plugins2]) - assert isinstance(factory1, ElementFactory) - assert isinstance(factory2, ElementFactory) - - foo_type1, _ = factory1.lookup("foo") - foo_type2, _ = factory2.lookup("foo") - assert foo_type1.__name__ == "FooElement" - assert foo_type2.__name__ == "AnotherFooElement" diff --git a/tests/internals/pluginfactory/anotherelement/__init__.py b/tests/internals/pluginfactory/anotherelement/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/anotherelement/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/anotherelement/foo.py b/tests/internals/pluginfactory/anotherelement/foo.py deleted file mode 100644 index 2e067a94f..000000000 --- a/tests/internals/pluginfactory/anotherelement/foo.py +++ /dev/null @@ -1,9 +0,0 @@ -from buildstream import Element - - -class AnotherFooElement(Element): - pass - - -def setup(): - return AnotherFooElement diff --git a/tests/internals/pluginfactory/anothersource/__init__.py b/tests/internals/pluginfactory/anothersource/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/anothersource/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/anothersource/foo.py b/tests/internals/pluginfactory/anothersource/foo.py deleted file mode 100644 index 4675b965f..000000000 --- a/tests/internals/pluginfactory/anothersource/foo.py +++ /dev/null @@ -1,9 +0,0 @@ -from buildstream import Source - - -class AnotherFooSource(Source): - pass - - -def setup(): - return AnotherFooSource diff --git a/tests/internals/pluginfactory/badsetup/__init__.py b/tests/internals/pluginfactory/badsetup/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/badsetup/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/badsetup/foo.py b/tests/internals/pluginfactory/badsetup/foo.py deleted file mode 100644 index 145f2577b..000000000 --- a/tests/internals/pluginfactory/badsetup/foo.py +++ /dev/null @@ -1,6 +0,0 @@ -# A plugin is supposed to define a setup function -# which returns the type that the plugin provides -# -# This plugin provides a setup() symbol that is -# not even a function -setup = 5 diff --git a/tests/internals/pluginfactory/badversionelement/__init__.py b/tests/internals/pluginfactory/badversionelement/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/badversionelement/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/badversionelement/foo.py b/tests/internals/pluginfactory/badversionelement/foo.py deleted file mode 100644 index 2a8b12abe..000000000 --- a/tests/internals/pluginfactory/badversionelement/foo.py +++ /dev/null @@ -1,11 +0,0 @@ -from buildstream import Element - - -class FooElement(Element): - - # We have a little while until we have to manually modify this - BST_REQUIRED_VERSION_MAJOR = 5000 - - -def setup(): - return FooElement diff --git a/tests/internals/pluginfactory/badversionsource/__init__.py b/tests/internals/pluginfactory/badversionsource/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/badversionsource/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/badversionsource/foo.py b/tests/internals/pluginfactory/badversionsource/foo.py deleted file mode 100644 index 23333a9d8..000000000 --- a/tests/internals/pluginfactory/badversionsource/foo.py +++ /dev/null @@ -1,11 +0,0 @@ -from buildstream import Source - - -class FooSource(Source): - - # We have a little while until we have to manually modify this - BST_REQUIRED_VERSION_MAJOR = 5000 - - -def setup(): - return FooSource diff --git a/tests/internals/pluginfactory/customelement/__init__.py b/tests/internals/pluginfactory/customelement/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/customelement/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/customelement/foo.py b/tests/internals/pluginfactory/customelement/foo.py deleted file mode 100644 index 260de8b27..000000000 --- a/tests/internals/pluginfactory/customelement/foo.py +++ /dev/null @@ -1,9 +0,0 @@ -from buildstream import Element - - -class FooElement(Element): - pass - - -def setup(): - return FooElement diff --git a/tests/internals/pluginfactory/customsource/__init__.py b/tests/internals/pluginfactory/customsource/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/customsource/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/customsource/foo.py b/tests/internals/pluginfactory/customsource/foo.py deleted file mode 100644 index de78a00ce..000000000 --- a/tests/internals/pluginfactory/customsource/foo.py +++ /dev/null @@ -1,9 +0,0 @@ -from buildstream import Source - - -class FooSource(Source): - pass - - -def setup(): - return FooSource diff --git a/tests/internals/pluginfactory/nosetup/__init__.py b/tests/internals/pluginfactory/nosetup/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/nosetup/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/notatype/__init__.py b/tests/internals/pluginfactory/notatype/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/notatype/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/notatype/foo.py b/tests/internals/pluginfactory/notatype/foo.py deleted file mode 100644 index 311a4fb32..000000000 --- a/tests/internals/pluginfactory/notatype/foo.py +++ /dev/null @@ -1,6 +0,0 @@ -# Plugins are supposed to return a subclass type -# of Source or Element, depending on plugin type. - - -def setup(): - return 5 diff --git a/tests/internals/pluginfactory/wrongtype/__init__.py b/tests/internals/pluginfactory/wrongtype/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginfactory/wrongtype/__init__.py +++ /dev/null diff --git a/tests/internals/pluginfactory/wrongtype/foo.py b/tests/internals/pluginfactory/wrongtype/foo.py deleted file mode 100644 index 37d9f6bfe..000000000 --- a/tests/internals/pluginfactory/wrongtype/foo.py +++ /dev/null @@ -1,12 +0,0 @@ -# Plugins are supposed to return a subclass type -# of Source or Element, depending on plugin type. -# -# This one fails the requirement - - -class Foo: - pass - - -def setup(): - return Foo diff --git a/tests/internals/pluginloading.py b/tests/internals/pluginloading.py index 0685b09da..1f4446541 100644 --- a/tests/internals/pluginloading.py +++ b/tests/internals/pluginloading.py @@ -3,8 +3,6 @@ import os import pytest from buildstream._project import Project -from buildstream._exceptions import LoadError -from buildstream.exceptions import LoadErrorReason from buildstream._pipeline import Pipeline from tests.testutils import dummy_context @@ -38,23 +36,3 @@ def test_customelement(datafiles, tmpdir): basedir = str(datafiles) with create_pipeline(tmpdir, basedir, "simple.bst") as targets: assert targets[0].get_kind() == "foo" - - -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badversionsource")) -def test_badversionsource(datafiles, tmpdir): - basedir = str(datafiles) - - with pytest.raises(LoadError) as exc, create_pipeline(tmpdir, basedir, "simple.bst"): - pass - - assert exc.value.reason == LoadErrorReason.UNSUPPORTED_PLUGIN - - -@pytest.mark.datafiles(os.path.join(DATA_DIR, "badversionelement")) -def test_badversionelement(datafiles, tmpdir): - basedir = str(datafiles) - - with pytest.raises(LoadError) as exc, create_pipeline(tmpdir, basedir, "simple.bst"): - pass - - assert exc.value.reason == LoadErrorReason.UNSUPPORTED_PLUGIN diff --git a/tests/internals/pluginloading/badversionelement/customelements/__init__.py b/tests/internals/pluginloading/badversionelement/customelements/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginloading/badversionelement/customelements/__init__.py +++ /dev/null diff --git a/tests/internals/pluginloading/badversionelement/customelements/foo.py b/tests/internals/pluginloading/badversionelement/customelements/foo.py deleted file mode 100644 index 75536e87f..000000000 --- a/tests/internals/pluginloading/badversionelement/customelements/foo.py +++ /dev/null @@ -1,19 +0,0 @@ -from buildstream import Element - - -class FooElement(Element): - - BST_FORMAT_VERSION = 5 - - def preflight(self): - pass - - def configure(self, node): - pass - - def get_unique_key(self): - return {} - - -def setup(): - return FooElement diff --git a/tests/internals/pluginloading/badversionelement/elements/simple.bst b/tests/internals/pluginloading/badversionelement/elements/simple.bst deleted file mode 100644 index f949dc5b5..000000000 --- a/tests/internals/pluginloading/badversionelement/elements/simple.bst +++ /dev/null @@ -1,4 +0,0 @@ -kind: foo -description: Custom foo element -config: - some: thing diff --git a/tests/internals/pluginloading/badversionelement/project.conf b/tests/internals/pluginloading/badversionelement/project.conf deleted file mode 100644 index bff73e251..000000000 --- a/tests/internals/pluginloading/badversionelement/project.conf +++ /dev/null @@ -1,10 +0,0 @@ -name: pony -min-version: 2.0 -element-path: elements - -plugins: -- origin: local - path: customelements - elements: - # We provided bar at version 5, should be a conflict. - foo: 10 diff --git a/tests/internals/pluginloading/badversionsource/customsources/__init__.py b/tests/internals/pluginloading/badversionsource/customsources/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/internals/pluginloading/badversionsource/customsources/__init__.py +++ /dev/null diff --git a/tests/internals/pluginloading/badversionsource/customsources/foo.py b/tests/internals/pluginloading/badversionsource/customsources/foo.py deleted file mode 100644 index 628f99b29..000000000 --- a/tests/internals/pluginloading/badversionsource/customsources/foo.py +++ /dev/null @@ -1,16 +0,0 @@ -from buildstream import Source - - -class BarSource(Source): - - BST_FORMAT_VERSION = 5 - - def preflight(self): - pass - - def configure(self, node): - pass - - -def setup(): - return BarSource diff --git a/tests/internals/pluginloading/badversionsource/elements/simple.bst b/tests/internals/pluginloading/badversionsource/elements/simple.bst deleted file mode 100644 index 7e0cc43b7..000000000 --- a/tests/internals/pluginloading/badversionsource/elements/simple.bst +++ /dev/null @@ -1,6 +0,0 @@ -kind: autotools -description: Custom foo source -sources: -- kind: foo - ref: 1.2.3 - uri: http://ponyland.com diff --git a/tests/internals/pluginloading/badversionsource/project.conf b/tests/internals/pluginloading/badversionsource/project.conf deleted file mode 100644 index cd5b2dc82..000000000 --- a/tests/internals/pluginloading/badversionsource/project.conf +++ /dev/null @@ -1,10 +0,0 @@ -name: pony -min-version: 2.0 -element-path: elements - -plugins: -- origin: local - path: customsources - sources: - # We provided bar at version 5, should be a conflict. - foo: 10 diff --git a/tests/internals/pluginloading/customelement/pluginelements/foo.py b/tests/internals/pluginloading/customelement/pluginelements/foo.py index c6a85a5b1..bdb6c8982 100644 --- a/tests/internals/pluginloading/customelement/pluginelements/foo.py +++ b/tests/internals/pluginloading/customelement/pluginelements/foo.py @@ -2,6 +2,9 @@ from buildstream import Element class FooElement(Element): + + BST_MIN_VERSION = "2.0" + def preflight(self): pass diff --git a/tests/internals/pluginloading/customelement/project.conf b/tests/internals/pluginloading/customelement/project.conf index 6a33cc504..2619bdf82 100644 --- a/tests/internals/pluginloading/customelement/project.conf +++ b/tests/internals/pluginloading/customelement/project.conf @@ -5,4 +5,4 @@ plugins: - origin: local path: pluginelements elements: - foo: 0 + - foo diff --git a/tests/internals/pluginloading/customsource/pluginsources/foo.py b/tests/internals/pluginloading/customsource/pluginsources/foo.py index fce5239b1..c5229f3e2 100644 --- a/tests/internals/pluginloading/customsource/pluginsources/foo.py +++ b/tests/internals/pluginloading/customsource/pluginsources/foo.py @@ -2,6 +2,9 @@ from buildstream import Source class FooSource(Source): + + BST_MIN_VERSION = "2.0" + def preflight(self): pass diff --git a/tests/internals/pluginloading/customsource/project.conf b/tests/internals/pluginloading/customsource/project.conf index 87d9b5d09..5cb6da537 100644 --- a/tests/internals/pluginloading/customsource/project.conf +++ b/tests/internals/pluginloading/customsource/project.conf @@ -5,4 +5,4 @@ plugins: - origin: local path: pluginsources sources: - foo: 0 + - foo diff --git a/tests/plugins/deprecationwarnings/deprecationwarnings.py b/tests/plugins/deprecationwarnings.py index 628faea68..a4da3ea72 100644 --- a/tests/plugins/deprecationwarnings/deprecationwarnings.py +++ b/tests/plugins/deprecationwarnings.py @@ -8,7 +8,7 @@ import pytest from buildstream.testing import cli # pylint: disable=unused-import -DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") +DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "deprecationwarnings") _DEPRECATION_MESSAGE = "Here is some detail." _DEPRECATION_WARNING = "Using deprecated plugin deprecated_plugin: {}".format(_DEPRECATION_MESSAGE) diff --git a/tests/plugins/deprecationwarnings/__init__.py b/tests/plugins/deprecationwarnings/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/tests/plugins/deprecationwarnings/__init__.py +++ /dev/null diff --git a/tests/plugins/deprecationwarnings/project/elements/deprecated.bst b/tests/plugins/deprecationwarnings/elements/deprecated.bst index e80bd91cd..e80bd91cd 100644 --- a/tests/plugins/deprecationwarnings/project/elements/deprecated.bst +++ b/tests/plugins/deprecationwarnings/elements/deprecated.bst diff --git a/tests/plugins/deprecationwarnings/project/plugins/elements/deprecated_plugin.py b/tests/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.py index 1badf59af..244009764 100644 --- a/tests/plugins/deprecationwarnings/project/plugins/elements/deprecated_plugin.py +++ b/tests/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.py @@ -2,6 +2,7 @@ from buildstream import BuildElement class DeprecatedPlugin(BuildElement): + BST_MIN_VERSION = "2.0" BST_PLUGIN_DEPRECATED = True BST_PLUGIN_DEPRECATION_MESSAGE = "Here is some detail." diff --git a/tests/plugins/deprecationwarnings/project/plugins/elements/deprecated_plugin.yaml b/tests/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.yaml index 1c07cd8b2..1c07cd8b2 100644 --- a/tests/plugins/deprecationwarnings/project/plugins/elements/deprecated_plugin.yaml +++ b/tests/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.yaml diff --git a/tests/plugins/deprecationwarnings/project/project.conf b/tests/plugins/deprecationwarnings/project.conf index 8a2536cab..9e03afe0a 100644 --- a/tests/plugins/deprecationwarnings/project/project.conf +++ b/tests/plugins/deprecationwarnings/project.conf @@ -12,4 +12,4 @@ plugins: - origin: local path: plugins/elements elements: - deprecated_plugin: 0 + - deprecated_plugin diff --git a/tests/plugins/loading.py b/tests/plugins/loading.py new file mode 100644 index 000000000..a368e9952 --- /dev/null +++ b/tests/plugins/loading.py @@ -0,0 +1,202 @@ +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +# +# This test case tests the failure modes of loading a plugin +# after it has already been discovered via it's origin. +# + +import os +import pytest + +from buildstream.exceptions import ErrorDomain +from buildstream.testing import cli # pylint: disable=unused-import +from buildstream import _yaml + + +DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "loading") + + +def update_project(project_path, updated_configuration): + project_conf_path = os.path.join(project_path, "project.conf") + project_conf = _yaml.roundtrip_load(project_conf_path) + + project_conf.update(updated_configuration) + + _yaml.roundtrip_dump(project_conf, project_conf_path) + + +# Sets up the element.bst file so that it requires a source +# or element plugin. +# +def setup_element(project_path, plugin_type, plugin_name): + element_dir = os.path.join(project_path, "elements") + element_path = os.path.join(element_dir, "element.bst") + os.makedirs(element_dir, exist_ok=True) + + if plugin_type == "elements": + element = {"kind": plugin_name} + else: + element = {"kind": "manual", "sources": [{"kind": plugin_name}]} + + _yaml.roundtrip_dump(element, element_path) + + +#################################################### +# Tests # +#################################################### +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_nosetup(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project(project, {"plugins": [{"origin": "local", "path": "plugins/nosetup", plugin_type: ["nosetup"]}]}) + setup_element(project, plugin_type, "nosetup") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "missing-setup-function") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_setup_not_function(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + {"plugins": [{"origin": "local", "path": "plugins/setupnotfunction", plugin_type: ["setupnotfunction"]}]}, + ) + setup_element(project, plugin_type, "setupnotfunction") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "setup-is-not-function") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_setup_returns_not_type(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + {"origin": "local", "path": "plugins/setupreturnsnottype", plugin_type: ["setupreturnsnottype"]} + ] + }, + ) + setup_element(project, plugin_type, "setupreturnsnottype") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "setup-returns-not-type") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_setup_returns_bad_type(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + {"origin": "local", "path": "plugins/setupreturnsbadtype", plugin_type: ["setupreturnsbadtype"]} + ] + }, + ) + setup_element(project, plugin_type, "setupreturnsbadtype") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "setup-returns-bad-type") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_missing_min_version(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + { + "origin": "local", + "path": os.path.join("plugins", plugin_type, "nominversion"), + plugin_type: ["nominversion"], + } + ] + }, + ) + setup_element(project, plugin_type, "nominversion") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "missing-min-version") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_malformed_min_version(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + { + "origin": "local", + "path": os.path.join("plugins", plugin_type, "malformedminversion"), + plugin_type: ["malformedminversion"], + } + ] + }, + ) + setup_element(project, plugin_type, "malformedminversion") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "malformed-min-version") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_incompatible_major_version(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + { + "origin": "local", + "path": os.path.join("plugins", plugin_type, "incompatiblemajor"), + plugin_type: ["incompatiblemajor"], + } + ] + }, + ) + setup_element(project, plugin_type, "incompatiblemajor") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "incompatible-major-version") + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("plugin_type", [("elements"), ("sources")]) +def test_incompatible_minor_version(cli, datafiles, plugin_type): + project = str(datafiles) + + update_project( + project, + { + "plugins": [ + { + "origin": "local", + "path": os.path.join("plugins", plugin_type, "incompatibleminor"), + plugin_type: ["incompatibleminor"], + } + ] + }, + ) + setup_element(project, plugin_type, "incompatibleminor") + + result = cli.run(project=project, args=["show", "element.bst"]) + result.assert_main_error(ErrorDomain.PLUGIN, "incompatible-minor-version") diff --git a/tests/plugins/loading/plugins/elements/incompatiblemajor/incompatiblemajor.py b/tests/plugins/loading/plugins/elements/incompatiblemajor/incompatiblemajor.py new file mode 100644 index 000000000..a0ea59309 --- /dev/null +++ b/tests/plugins/loading/plugins/elements/incompatiblemajor/incompatiblemajor.py @@ -0,0 +1,10 @@ +from buildstream import Element + + +class IncompatibleMajor(Element): + + BST_MIN_VERSION = "1.0" + + +def setup(): + return IncompatibleMajor diff --git a/tests/plugins/loading/plugins/elements/incompatibleminor/incompatibleminor.py b/tests/plugins/loading/plugins/elements/incompatibleminor/incompatibleminor.py new file mode 100644 index 000000000..d9967caaa --- /dev/null +++ b/tests/plugins/loading/plugins/elements/incompatibleminor/incompatibleminor.py @@ -0,0 +1,10 @@ +from buildstream import Element + + +class IncompatibleMinor(Element): + + BST_MIN_VERSION = "2.1000" + + +def setup(): + return IncompatibleMinor diff --git a/tests/plugins/loading/plugins/elements/malformedminversion/malformedminversion.py b/tests/plugins/loading/plugins/elements/malformedminversion/malformedminversion.py new file mode 100644 index 000000000..5f96c1897 --- /dev/null +++ b/tests/plugins/loading/plugins/elements/malformedminversion/malformedminversion.py @@ -0,0 +1,11 @@ +# Plugins are required to specify the BST_MIN_VERSION +from buildstream import Element + + +class MalformedMinVersion(Element): + + BST_MIN_VERSION = "1.pony" + + +def setup(): + return MalformedMinVersion diff --git a/tests/plugins/loading/plugins/elements/nominversion/nominversion.py b/tests/plugins/loading/plugins/elements/nominversion/nominversion.py new file mode 100644 index 000000000..e3f5b0cb6 --- /dev/null +++ b/tests/plugins/loading/plugins/elements/nominversion/nominversion.py @@ -0,0 +1,10 @@ +# Plugins are required to specify the BST_MIN_VERSION +from buildstream import Element + + +class NoMinVersion(Element): + pass + + +def setup(): + return NoMinVersion diff --git a/tests/internals/pluginfactory/nosetup/foo.py b/tests/plugins/loading/plugins/nosetup/nosetup.py index 0b5a4fa7e..0b5a4fa7e 100644 --- a/tests/internals/pluginfactory/nosetup/foo.py +++ b/tests/plugins/loading/plugins/nosetup/nosetup.py diff --git a/tests/plugins/loading/plugins/setupnotfunction/setupnotfunction.py b/tests/plugins/loading/plugins/setupnotfunction/setupnotfunction.py new file mode 100644 index 000000000..1272f4ee0 --- /dev/null +++ b/tests/plugins/loading/plugins/setupnotfunction/setupnotfunction.py @@ -0,0 +1,6 @@ +# A plugin's setup symbol is supposed to be a function +# which returns the plugin type. +# +# This plugin's setup symbol is not such a function. + +setup = 9 diff --git a/tests/plugins/loading/plugins/setupreturnsbadtype/setupreturnsbadtype.py b/tests/plugins/loading/plugins/setupreturnsbadtype/setupreturnsbadtype.py new file mode 100644 index 000000000..25795c06b --- /dev/null +++ b/tests/plugins/loading/plugins/setupreturnsbadtype/setupreturnsbadtype.py @@ -0,0 +1,15 @@ +# A plugin's setup symbol is supposed to be a function +# which returns the plugin type, which should be a subclass +# of Source or Element depending on the plugin type. +# +# This plugin's setup function returns a different kind +# of type. + + +class Pony: + def __init__(self): + self.pony = 12 + + +def setup(): + return Pony diff --git a/tests/plugins/loading/plugins/setupreturnsnottype/setupreturnsnottype.py b/tests/plugins/loading/plugins/setupreturnsnottype/setupreturnsnottype.py new file mode 100644 index 000000000..d47367ea9 --- /dev/null +++ b/tests/plugins/loading/plugins/setupreturnsnottype/setupreturnsnottype.py @@ -0,0 +1,8 @@ +# A plugin's setup symbol is supposed to be a function +# which returns the plugin type. +# +# This plugin's setup function returns a number instead + + +def setup(): + return 5 diff --git a/tests/plugins/loading/plugins/sources/incompatiblemajor/incompatiblemajor.py b/tests/plugins/loading/plugins/sources/incompatiblemajor/incompatiblemajor.py new file mode 100644 index 000000000..f2d088b77 --- /dev/null +++ b/tests/plugins/loading/plugins/sources/incompatiblemajor/incompatiblemajor.py @@ -0,0 +1,10 @@ +from buildstream import Source + + +class IncompatibleMajor(Source): + + BST_MIN_VERSION = "1.0" + + +def setup(): + return IncompatibleMajor diff --git a/tests/plugins/loading/plugins/sources/incompatibleminor/incompatibleminor.py b/tests/plugins/loading/plugins/sources/incompatibleminor/incompatibleminor.py new file mode 100644 index 000000000..2282534ae --- /dev/null +++ b/tests/plugins/loading/plugins/sources/incompatibleminor/incompatibleminor.py @@ -0,0 +1,10 @@ +from buildstream import Source + + +class IncompatibleMinor(Source): + + BST_MIN_VERSION = "2.1000" + + +def setup(): + return IncompatibleMinor diff --git a/tests/plugins/loading/plugins/sources/malformedminversion/malformedminversion.py b/tests/plugins/loading/plugins/sources/malformedminversion/malformedminversion.py new file mode 100644 index 000000000..90474597c --- /dev/null +++ b/tests/plugins/loading/plugins/sources/malformedminversion/malformedminversion.py @@ -0,0 +1,11 @@ +# Plugins are required to specify the BST_MIN_VERSION +from buildstream import Source + + +class MalformedMinVersion(Source): + + BST_MIN_VERSION = "1.pony" + + +def setup(): + return MalformedMinVersion diff --git a/tests/plugins/loading/plugins/sources/nominversion/nominversion.py b/tests/plugins/loading/plugins/sources/nominversion/nominversion.py new file mode 100644 index 000000000..12cdfa296 --- /dev/null +++ b/tests/plugins/loading/plugins/sources/nominversion/nominversion.py @@ -0,0 +1,10 @@ +# Plugins are required to specify the BST_MIN_VERSION +from buildstream import Source + + +class NoMinVersion(Source): + pass + + +def setup(): + return NoMinVersion diff --git a/tests/plugins/loading/project.conf b/tests/plugins/loading/project.conf new file mode 100644 index 000000000..738051af9 --- /dev/null +++ b/tests/plugins/loading/project.conf @@ -0,0 +1,8 @@ +# This project.conf gets rewritten for each plugin loading test +name: test + +# Required BuildStream version +min-version: 2.0 + +# Subdirectory where elements are stored +element-path: elements diff --git a/tests/sourcecache/project/plugins/elements/always_fail.py b/tests/sourcecache/project/plugins/elements/always_fail.py index 43dba5626..bc3ed57b6 100644 --- a/tests/sourcecache/project/plugins/elements/always_fail.py +++ b/tests/sourcecache/project/plugins/elements/always_fail.py @@ -23,6 +23,9 @@ from buildstream.buildelement import BuildElement class AlwaysFail(BuildElement): + + BST_MIN_VERSION = "2.0" + def assemble(self, sandbox): raise ElementError("Always fails") diff --git a/tests/sourcecache/project/project.conf b/tests/sourcecache/project/project.conf index dde417f71..e3635e8a7 100644 --- a/tests/sourcecache/project/project.conf +++ b/tests/sourcecache/project/project.conf @@ -9,4 +9,4 @@ plugins: - origin: local path: plugins/elements elements: - always_fail: 0
\ No newline at end of file + - always_fail diff --git a/tests/sources/no-fetch-cached/plugins/sources/always_cached.py b/tests/sources/no-fetch-cached/plugins/sources/always_cached.py index aef13279b..b5dfd772c 100644 --- a/tests/sources/no-fetch-cached/plugins/sources/always_cached.py +++ b/tests/sources/no-fetch-cached/plugins/sources/always_cached.py @@ -11,6 +11,8 @@ from buildstream import Source class AlwaysCachedSource(Source): + BST_MIN_VERSION = "2.0" + def configure(self, node): pass diff --git a/tests/sources/no-fetch-cached/project.conf b/tests/sources/no-fetch-cached/project.conf index eeb00d432..ef5b4a3b9 100644 --- a/tests/sources/no-fetch-cached/project.conf +++ b/tests/sources/no-fetch-cached/project.conf @@ -6,4 +6,4 @@ plugins: - origin: local path: plugins/sources sources: - always_cached: 0 + - always_cached diff --git a/tests/sources/previous_source_access/plugins/sources/foo_transform.py b/tests/sources/previous_source_access/plugins/sources/foo_transform.py index dbc44c8aa..15fef43f8 100644 --- a/tests/sources/previous_source_access/plugins/sources/foo_transform.py +++ b/tests/sources/previous_source_access/plugins/sources/foo_transform.py @@ -14,6 +14,7 @@ from buildstream import Source, SourceError, utils class FooTransformSource(Source): + BST_MIN_VERSION = "2.0" # We need access to previous both at track time and fetch time BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True diff --git a/tests/sources/previous_source_access/project.conf b/tests/sources/previous_source_access/project.conf index 2d3bb1fb2..b08df35f0 100644 --- a/tests/sources/previous_source_access/project.conf +++ b/tests/sources/previous_source_access/project.conf @@ -11,4 +11,4 @@ plugins: - origin: local path: plugins/sources sources: - foo_transform: 0 + - foo_transform diff --git a/tests/sources/project_key_test/plugins/sources/key-test.py b/tests/sources/project_key_test/plugins/sources/key-test.py index 88a211738..046034804 100644 --- a/tests/sources/project_key_test/plugins/sources/key-test.py +++ b/tests/sources/project_key_test/plugins/sources/key-test.py @@ -7,6 +7,8 @@ class KeyTest(Source): """ This plugin should fail if get_unique_key is called before track """ + BST_MIN_VERSION = "2.0" + def preflight(self): pass diff --git a/tests/sources/project_key_test/project.conf b/tests/sources/project_key_test/project.conf index fb9ba1f88..6b82d4055 100644 --- a/tests/sources/project_key_test/project.conf +++ b/tests/sources/project_key_test/project.conf @@ -8,4 +8,4 @@ plugins: - origin: local path: plugins/sources sources: - key-test: 0
\ No newline at end of file + - key-test @@ -8,7 +8,7 @@ isolated_build = true # Configuration variables to share across environments [config] -BST_PLUGINS_EXPERIMENTAL_VERSION = 1.93.1.1 +BST_PLUGINS_EXPERIMENTAL_VERSION = 1.93.2 # # Defaults for all environments |