Merge pull request #1523 from apache/tristan/strict-element-names
Error out on invalid element names
diff --git a/NEWS b/NEWS
index 7d6d0a5..18c81c4 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,12 @@
(unreleased)
============
+Format
+------
+ o BREAKING CHANGE: Element names must now bare the `.bst` suffix and not
+ contain invalid characters, as was already documented. Now the warning
+ is replaced by an unconditional error.
+
CLI
---
o BREAKING CHANGE: Removed `--deps plan` from all commands
diff --git a/src/buildstream/_loader/loader.py b/src/buildstream/_loader/loader.py
index df4df84..ea73afd 100644
--- a/src/buildstream/_loader/loader.py
+++ b/src/buildstream/_loader/loader.py
@@ -28,7 +28,7 @@
from .._profile import Topics, PROFILER
from .._includes import Includes
from .._utils import valid_chars_name
-from ..types import CoreWarnings, _KeyStrength
+from ..types import _KeyStrength
from .types import Symbol
from . import loadelement
@@ -125,8 +125,6 @@
LoadErrorReason.INVALID_DATA,
)
- self._warn_invalid_elements(targets)
-
# First pass, recursively load files and populate our table of LoadElements
#
target_elements = []
@@ -267,6 +265,9 @@
# (LoadElement): A partially-loaded LoadElement
#
def _load_file_no_deps(self, filename, provenance_node=None):
+
+ self._assert_element_name(filename, provenance_node)
+
# Load the data and process any conditional statements therein
fullpath = os.path.join(self._basedir, filename)
try:
@@ -530,9 +531,6 @@
dep.set_element(dep_element)
current_element[0].dependencies.append(dep) # pylint: disable=no-member
else:
- # We do not have any more dependencies to load for this
- # element on the queue, report any invalid dep names
- self._warn_invalid_elements(loader_queue[-1][2])
# And pop the element off the queue
loader_queue.pop()
@@ -1000,6 +998,8 @@
loader = self.get_loader(junction_path[-2], provenance_node, load_subprojects=load_subprojects)
return junction_path[-2], junction_path[-1], loader
+ # _warn():
+ #
# Print a warning message, checks warning_token against project configuration
#
# Args:
@@ -1017,47 +1017,34 @@
self.load_context.context.messenger.warn(brief)
- # Print warning messages if any of the specified elements have invalid names.
+ # _assert_element_name():
+ #
+ # Raises an error if any of the specified elements have invalid names.
#
# Valid filenames should end with ".bst" extension.
#
# Args:
- # elements (list): List of element names
+ # filename (str): The element name
+ # provenance_node (Node): The provenance node, or None
#
# Raises:
- # (:class:`.LoadError`): When warning_token is considered fatal by the project configuration
+ # (:class:`.LoadError`): If the element name is invalid
#
- def _warn_invalid_elements(self, elements):
+ def _assert_element_name(self, filename, provenance_node):
+ error_message = None
+ error_reason = None
- # invalid_elements
- #
- # A dict that maps warning types to the matching elements.
- invalid_elements = {
- CoreWarnings.BAD_ELEMENT_SUFFIX: [],
- CoreWarnings.BAD_CHARACTERS_IN_NAME: [],
- }
+ if not filename.endswith(".bst"):
+ error_message = "Element '{}' does not have expected file extension `.bst`".format(filename)
+ error_reason = LoadErrorReason.BAD_ELEMENT_SUFFIX
+ elif not valid_chars_name(filename):
+ error_message = "Element '{}' has invalid characters.".format(filename)
+ error_reason = LoadErrorReason.BAD_CHARACTERS_IN_NAME
- for filename in elements:
- if not filename.endswith(".bst"):
- invalid_elements[CoreWarnings.BAD_ELEMENT_SUFFIX].append(filename)
- if not valid_chars_name(filename):
- invalid_elements[CoreWarnings.BAD_CHARACTERS_IN_NAME].append(filename)
-
- if invalid_elements[CoreWarnings.BAD_ELEMENT_SUFFIX]:
- self._warn(
- "Target elements '{}' do not have expected file extension `.bst` "
- "Improperly named elements will not be discoverable by commands".format(
- invalid_elements[CoreWarnings.BAD_ELEMENT_SUFFIX]
- ),
- warning_token=CoreWarnings.BAD_ELEMENT_SUFFIX,
- )
- if invalid_elements[CoreWarnings.BAD_CHARACTERS_IN_NAME]:
- self._warn(
- "Target elements '{}' have invalid characerts in their name.".format(
- invalid_elements[CoreWarnings.BAD_CHARACTERS_IN_NAME]
- ),
- warning_token=CoreWarnings.BAD_CHARACTERS_IN_NAME,
- )
+ if error_message:
+ if provenance_node is not None:
+ error_message = "{}: {}".format(provenance_node.get_provenance(), error_message)
+ raise LoadError(error_message, error_reason)
# _clean_caches()
#
diff --git a/src/buildstream/exceptions.py b/src/buildstream/exceptions.py
index 4b91189..8d828d0 100644
--- a/src/buildstream/exceptions.py
+++ b/src/buildstream/exceptions.py
@@ -146,3 +146,15 @@
CIRCULAR_REFERENCE = 26
"""A circular element reference was detected"""
+
+ BAD_ELEMENT_SUFFIX = 27
+ """
+ This warning will be produced when an element whose name does not end in .bst
+ is referenced either on the command line or by another element
+ """
+
+ BAD_CHARACTERS_IN_NAME = 28
+ """
+ This warning will be produced when a filename for a target contains invalid
+ characters in its name.
+ """
diff --git a/src/buildstream/types.py b/src/buildstream/types.py
index ac3b180..8c87b4f 100644
--- a/src/buildstream/types.py
+++ b/src/buildstream/types.py
@@ -113,18 +113,6 @@
which is found to be invalid based on the configured track
"""
- BAD_ELEMENT_SUFFIX = "bad-element-suffix"
- """
- This warning will be produced when an element whose name does not end in .bst
- is referenced either on the command line or by another element
- """
-
- BAD_CHARACTERS_IN_NAME = "bad-characters-in-name"
- """
- This warning will be produced when a filename for a target contains invalid
- characters in its name.
- """
-
UNALIASED_URL = "unaliased-url"
"""
A URL used for fetching a sources was specified without specifying any
diff --git a/tests/format/elementnames.py b/tests/format/elementnames.py
new file mode 100644
index 0000000..f059d54
--- /dev/null
+++ b/tests/format/elementnames.py
@@ -0,0 +1,29 @@
+# Pylint doesn't play well with fixtures and dependency injection from pytest
+# pylint: disable=redefined-outer-name
+
+import os
+import pytest
+
+from buildstream.exceptions import ErrorDomain, LoadErrorReason
+from buildstream.testing import cli # pylint: disable=unused-import
+
+DATA_DIR = os.path.dirname(os.path.realpath(__file__))
+
+
+@pytest.mark.parametrize(
+ "target,reason,provenance",
+ [
+ ("farm.pony", LoadErrorReason.BAD_ELEMENT_SUFFIX, None),
+ ('The "quoted" pony.bst', LoadErrorReason.BAD_CHARACTERS_IN_NAME, None),
+ ("bad-suffix-dep.bst", LoadErrorReason.BAD_ELEMENT_SUFFIX, "bad-suffix-dep.bst [line 3 column 2]"),
+ ("bad-chars-dep.bst", LoadErrorReason.BAD_CHARACTERS_IN_NAME, "bad-chars-dep.bst [line 3 column 2]"),
+ ],
+ ids=["toplevel-bad-suffix", "toplevel-bad-chars", "dependency-bad-suffix", "dependency-bad-chars"],
+)
+@pytest.mark.datafiles(DATA_DIR)
+def test_invalid_element_names(cli, datafiles, target, reason, provenance):
+ project = os.path.join(str(datafiles), "elementnames")
+ result = cli.run(project=project, silent=True, args=["show", target])
+ result.assert_main_error(ErrorDomain.LOAD, reason)
+ if provenance:
+ assert provenance in result.stderr
diff --git a/tests/format/elementnames/bad-chars-dep.bst b/tests/format/elementnames/bad-chars-dep.bst
new file mode 100644
index 0000000..d637799
--- /dev/null
+++ b/tests/format/elementnames/bad-chars-dep.bst
@@ -0,0 +1,3 @@
+kind: stack
+depends:
+- <invalid>.bst
diff --git a/tests/format/elementnames/bad-suffix-dep.bst b/tests/format/elementnames/bad-suffix-dep.bst
new file mode 100644
index 0000000..c96604d
--- /dev/null
+++ b/tests/format/elementnames/bad-suffix-dep.bst
@@ -0,0 +1,3 @@
+kind: stack
+depends:
+- rainbow.pony
diff --git a/tests/format/elementnames/project.conf b/tests/format/elementnames/project.conf
new file mode 100644
index 0000000..b0110aa
--- /dev/null
+++ b/tests/format/elementnames/project.conf
@@ -0,0 +1,4 @@
+# Basic project configuration that doesnt override anything
+#
+name: test
+min-version: 2.0
diff --git a/tests/frontend/buildcheckout.py b/tests/frontend/buildcheckout.py
index 6021ff0..330b9cd 100644
--- a/tests/frontend/buildcheckout.py
+++ b/tests/frontend/buildcheckout.py
@@ -10,7 +10,7 @@
from buildstream.testing import cli # pylint: disable=unused-import
from buildstream.testing import create_repo
-from buildstream.testing._utils.site import IS_WINDOWS, CASD_SEPARATE_USER
+from buildstream.testing._utils.site import CASD_SEPARATE_USER
from buildstream import _yaml
from buildstream.exceptions import ErrorDomain, LoadErrorReason
from buildstream import utils
@@ -170,60 +170,6 @@
@pytest.mark.datafiles(DATA_DIR)
-@pytest.mark.parametrize("strict,hardlinks", [("non-strict", "hardlinks"),])
-def test_build_invalid_suffix(datafiles, cli, strict, hardlinks):
- project = str(datafiles)
-
- result = cli.run(project=project, args=strict_args(["build", "target.foo"], strict))
- result.assert_main_error(ErrorDomain.LOAD, "bad-element-suffix")
-
-
-@pytest.mark.datafiles(DATA_DIR)
-@pytest.mark.parametrize("strict,hardlinks", [("non-strict", "hardlinks"),])
-def test_build_invalid_suffix_dep(datafiles, cli, strict, hardlinks):
- project = str(datafiles)
-
- # target2.bst depends on an element called target.foo
- result = cli.run(project=project, args=strict_args(["build", "target2.bst"], strict))
- result.assert_main_error(ErrorDomain.LOAD, "bad-element-suffix")
-
-
-@pytest.mark.skipif(IS_WINDOWS, reason="Not available on Windows")
-@pytest.mark.datafiles(DATA_DIR)
-def test_build_invalid_filename_chars(datafiles, cli):
- project = str(datafiles)
- element_name = "invalid-chars|<>-in-name.bst"
-
- # The name of this file contains characters that are not allowed by
- # BuildStream, using it should raise a warning.
- element = {
- "kind": "stack",
- }
- _yaml.roundtrip_dump(element, os.path.join(project, "elements", element_name))
-
- result = cli.run(project=project, args=strict_args(["build", element_name], "non-strict"))
- result.assert_main_error(ErrorDomain.LOAD, "bad-characters-in-name")
-
-
-@pytest.mark.skipif(IS_WINDOWS, reason="Not available on Windows")
-@pytest.mark.datafiles(DATA_DIR)
-def test_build_invalid_filename_chars_dep(datafiles, cli):
- project = str(datafiles)
- element_name = "invalid-chars|<>-in-name.bst"
-
- # The name of this file contains characters that are not allowed by
- # BuildStream, and is listed as a dependency of 'invalid-chars-in-dep.bst'.
- # This should also raise a warning.
- element = {
- "kind": "stack",
- }
- _yaml.roundtrip_dump(element, os.path.join(project, "elements", element_name))
-
- result = cli.run(project=project, args=strict_args(["build", "invalid-chars-in-dep.bst"], "non-strict"))
- result.assert_main_error(ErrorDomain.LOAD, "bad-characters-in-name")
-
-
-@pytest.mark.datafiles(DATA_DIR)
@pytest.mark.parametrize("deps", [("run"), ("none"), ("build"), ("all")])
def test_build_checkout_deps(datafiles, cli, deps):
project = str(datafiles)
diff --git a/tests/frontend/project/elements/invalid-chars-in-dep.bst b/tests/frontend/project/elements/invalid-chars-in-dep.bst
deleted file mode 100644
index 6a5ec30..0000000
--- a/tests/frontend/project/elements/invalid-chars-in-dep.bst
+++ /dev/null
@@ -1,8 +0,0 @@
-kind: stack
-description: |
-
- This element itself has a valid name, but depends on elements that have
- invalid names. This should also result in a warning.
-
-depends:
-- invalid-chars|<>-in-name.bst
diff --git a/tests/frontend/project/elements/target2.bst b/tests/frontend/project/elements/target2.bst
deleted file mode 100644
index 259819f..0000000
--- a/tests/frontend/project/elements/target2.bst
+++ /dev/null
@@ -1,7 +0,0 @@
-kind: stack
-description: |
-
- Main stack target for the bst build test
-
-depends:
-- target.foo
diff --git a/tests/frontend/project/project.conf b/tests/frontend/project/project.conf
index c82f85b..5ba3168 100644
--- a/tests/frontend/project/project.conf
+++ b/tests/frontend/project/project.conf
@@ -2,7 +2,3 @@
name: test
min-version: 2.0
element-path: elements
-
-fatal-warnings:
-- bad-element-suffix
-- bad-characters-in-name