diff options
40 files changed, 170 insertions, 3 deletions
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/pluginfactory.py b/src/buildstream/_pluginfactory/pluginfactory.py index 7e5ae2db0..27321c62f 100644 --- a/src/buildstream/_pluginfactory/pluginfactory.py +++ b/src/buildstream/_pluginfactory/pluginfactory.py @@ -20,6 +20,8 @@ import os import inspect +from .. import utils +from ..utils import UtilError from .._exceptions import PluginError from .pluginorigin import PluginOrigin, PluginOriginType @@ -250,6 +252,8 @@ class PluginFactory: ) from e self._assert_plugin(kind, plugin_type) + self._assert_min_version(kind, plugin_type) + return (plugin_type, defaults) def _assert_plugin(self, kind, plugin_type): @@ -273,3 +277,44 @@ class PluginFactory: ), reason="setup-returns-not-type", ) from e + + def _assert_min_version(self, kind, plugin_type): + + 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 ?", + ) + + 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 + + bst_major, bst_minor = utils._get_bst_api_version() + + if min_version_major != bst_major: + raise PluginError( + "{} 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.", + ) + + 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 + ), + reason="incompatible-minor-version", + detail="Please upgrade to BuildStream {}".format(plugin_type.BST_MIN_VERSION), + ) diff --git a/src/buildstream/plugin.py b/src/buildstream/plugin.py index 4f2d74304..935db6523 100644 --- a/src/buildstream/plugin.py +++ b/src/buildstream/plugin.py @@ -148,6 +148,32 @@ class Plugin: which are included in the buildstream namespace. """ + BST_MIN_VERSION = None + """The minimum required version of BuildStream required by this plugin. + + 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. + + **Example:** + + 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:: + + 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 """True if this element plugin has been deprecated. 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/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/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-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/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/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/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/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/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/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.py b/tests/plugins/deprecationwarnings/plugins/elements/deprecated_plugin.py index 1badf59af..244009764 100644 --- a/tests/plugins/deprecationwarnings/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/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/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/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/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 |