Phillip Smyth pushed to branch mandatory_suffix at BuildStream / buildstream
Commits:
- 
ce6745ba
by Phillip Smyth at 2018-11-23T16:04:28Z
 - 
f669f7ba
by Phillip Smyth at 2018-11-23T16:05:51Z
 - 
96fba2a0
by Phillip Smyth at 2018-11-23T16:05:51Z
 - 
eb6a689c
by Phillip Smyth at 2018-11-23T16:06:25Z
 
8 changed files:
- NEWS
 - buildstream/_frontend/cli.py
 - buildstream/_loader/loadelement.py
 - buildstream/_loader/loader.py
 - tests/completions/completions.py
 - tests/frontend/buildcheckout.py
 - + tests/frontend/project/elements/target.foo
 - + tests/frontend/project/elements/target2.bst
 
Changes:
| ... | ... | @@ -2,6 +2,10 @@ | 
| 2 | 2 | 
 buildstream 1.3.1
 | 
| 3 | 3 | 
 =================
 | 
| 4 | 4 | 
 | 
| 5 | 
+  o BREAKING CHANGE: Attempting to use an element that does not have the `.bst`
 | 
|
| 6 | 
+    extension, will result in an error message.
 | 
|
| 7 | 
+    All elements must now be suffixed with `.bst`
 | 
|
| 8 | 
+  | 
|
| 5 | 9 | 
   o BREAKING CHANGE: The 'manual' element lost its default 'MAKEFLAGS' and 'V'
 | 
| 6 | 10 | 
     environment variables. There is already a 'make' element with the same
 | 
| 7 | 11 | 
     variables. Note that this is a breaking change, it will require users to
 | 
| ... | ... | @@ -109,7 +109,11 @@ def complete_target(args, incomplete): | 
| 109 | 109 | 
     if element_directory:
 | 
| 110 | 110 | 
         base_directory = os.path.join(base_directory, element_directory)
 | 
| 111 | 111 | 
 | 
| 112 | 
-    return complete_path("File", incomplete, base_directory=base_directory)
 | 
|
| 112 | 
+    complete_list = []
 | 
|
| 113 | 
+    for p in complete_path("File", incomplete, base_directory=base_directory):
 | 
|
| 114 | 
+        if p.endswith(".bst ") or p.endswith("/") or p.endswith(".conf "):
 | 
|
| 115 | 
+            complete_list.append(p)
 | 
|
| 116 | 
+    return complete_list
 | 
|
| 113 | 117 | 
 | 
| 114 | 118 | 
 | 
| 115 | 119 | 
 def override_completions(cmd, cmd_param, args, incomplete):
 | 
| ... | ... | @@ -145,11 +145,14 @@ def _extract_depends_from_node(node, *, key=None): | 
| 145 | 145 | 
 | 
| 146 | 146 | 
     depends = _yaml.node_get(node, list, key, default_value=[])
 | 
| 147 | 147 | 
     output_deps = []
 | 
| 148 | 
+    invalid_elements = []
 | 
|
| 148 | 149 | 
 | 
| 149 | 150 | 
     for index, dep in enumerate(depends):
 | 
| 150 | 151 | 
         dep_provenance = _yaml.node_get_provenance(node, key=key, indices=[index])
 | 
| 151 | 152 | 
 | 
| 152 | 153 | 
         if isinstance(dep, str):
 | 
| 154 | 
+            if not dep.endswith(".bst"):
 | 
|
| 155 | 
+                invalid_elements.append(dep)
 | 
|
| 153 | 156 | 
             dependency = Dependency(dep, provenance=dep_provenance, dep_type=default_dep_type)
 | 
| 154 | 157 | 
 | 
| 155 | 158 | 
         elif isinstance(dep, Mapping):
 | 
| ... | ... | @@ -182,6 +185,11 @@ def _extract_depends_from_node(node, *, key=None): | 
| 182 | 185 | 
 | 
| 183 | 186 | 
         output_deps.append(dependency)
 | 
| 184 | 187 | 
 | 
| 188 | 
+    if invalid_elements:
 | 
|
| 189 | 
+        raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
|
| 190 | 
+                        "Target elements '{}' do not have expected file extension `.bst`\n"
 | 
|
| 191 | 
+                        "Improperly named elements will not be discoverable by commands".format(invalid_elements))
 | 
|
| 192 | 
+  | 
|
| 185 | 193 | 
     # Now delete the field, we dont want it anymore
 | 
| 186 | 194 | 
     del node[key]
 | 
| 187 | 195 | 
 | 
| ... | ... | @@ -97,6 +97,7 @@ class Loader(): | 
| 97 | 97 | 
     # Returns: The toplevel LoadElement
 | 
| 98 | 98 | 
     def load(self, targets, rewritable=False, ticker=None, fetch_subprojects=False):
 | 
| 99 | 99 | 
 | 
| 100 | 
+        invalid_elements = []
 | 
|
| 100 | 101 | 
         for filename in targets:
 | 
| 101 | 102 | 
             if os.path.isabs(filename):
 | 
| 102 | 103 | 
                 # XXX Should this just be an assertion ?
 | 
| ... | ... | @@ -106,6 +107,13 @@ class Loader(): | 
| 106 | 107 | 
                                 "path to the base project directory: {}"
 | 
| 107 | 108 | 
                                 .format(filename, self._basedir))
 | 
| 108 | 109 | 
 | 
| 110 | 
+            if not filename.endswith(".bst") and not filename.endswith("/") and not filename.endswith(".conf"):
 | 
|
| 111 | 
+                invalid_elements.append(filename)
 | 
|
| 112 | 
+  | 
|
| 113 | 
+        if invalid_elements:
 | 
|
| 114 | 
+            raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
|
| 115 | 
+                            "Target elements '{}' do not have expected file extension `.bst`\n"
 | 
|
| 116 | 
+                            "Improperly named elements will not be discoverable by commands".format(invalid_elements))
 | 
|
| 109 | 117 | 
         # First pass, recursively load files and populate our table of LoadElements
 | 
| 110 | 118 | 
         #
 | 
| 111 | 119 | 
         deps = []
 | 
| ... | ... | @@ -66,6 +66,16 @@ PROJECT_ELEMENTS = [ | 
| 66 | 66 | 
     "target.bst"
 | 
| 67 | 67 | 
 ]
 | 
| 68 | 68 | 
 | 
| 69 | 
+INVALID_ELEMENT = [
 | 
|
| 70 | 
+    "target.foo"
 | 
|
| 71 | 
+    "compose-all.bst",
 | 
|
| 72 | 
+    "compose-exclude-dev.bst",
 | 
|
| 73 | 
+    "compose-include-bin.bst",
 | 
|
| 74 | 
+    "import-bin.bst",
 | 
|
| 75 | 
+    "import-dev.bst",
 | 
|
| 76 | 
+    "target.bst"
 | 
|
| 77 | 
+]
 | 
|
| 78 | 
+  | 
|
| 69 | 79 | 
 | 
| 70 | 80 | 
 def assert_completion(cli, cmd, word_idx, expected, cwd=None):
 | 
| 71 | 81 | 
     result = cli.run(cwd=cwd, env={
 | 
| ... | ... | @@ -85,6 +95,24 @@ def assert_completion(cli, cmd, word_idx, expected, cwd=None): | 
| 85 | 95 | 
     assert words == expected
 | 
| 86 | 96 | 
 | 
| 87 | 97 | 
 | 
| 98 | 
+def assert_completion_failed(cli, cmd, word_idx, expected, cwd=None):
 | 
|
| 99 | 
+    result = cli.run(cwd=cwd, env={
 | 
|
| 100 | 
+        '_BST_COMPLETION': 'complete',
 | 
|
| 101 | 
+        'COMP_WORDS': cmd,
 | 
|
| 102 | 
+        'COMP_CWORD': str(word_idx)
 | 
|
| 103 | 
+    })
 | 
|
| 104 | 
+    words = []
 | 
|
| 105 | 
+    if result.output:
 | 
|
| 106 | 
+        words = result.output.splitlines()
 | 
|
| 107 | 
+  | 
|
| 108 | 
+    # The order is meaningless, bash will
 | 
|
| 109 | 
+    # take the results and order it by its
 | 
|
| 110 | 
+    # own little heuristics
 | 
|
| 111 | 
+    words = sorted(words)
 | 
|
| 112 | 
+    expected = sorted(expected)
 | 
|
| 113 | 
+    assert words != expected
 | 
|
| 114 | 
+  | 
|
| 115 | 
+  | 
|
| 88 | 116 | 
 @pytest.mark.parametrize("cmd,word_idx,expected", [
 | 
| 89 | 117 | 
     ('bst', 0, []),
 | 
| 90 | 118 | 
     ('bst ', 1, MAIN_COMMANDS),
 | 
| ... | ... | @@ -226,6 +254,19 @@ def test_argument_element(datafiles, cli, project, cmd, word_idx, expected, subd | 
| 226 | 254 | 
     assert_completion(cli, cmd, word_idx, expected, cwd=cwd)
 | 
| 227 | 255 | 
 | 
| 228 | 256 | 
 | 
| 257 | 
+@pytest.mark.datafiles(DATA_DIR)
 | 
|
| 258 | 
+@pytest.mark.parametrize("project,cmd,word_idx,expected,subdir", [
 | 
|
| 259 | 
+  | 
|
| 260 | 
+    # When element has invalid suffix
 | 
|
| 261 | 
+    ('project', 'bst --directory ../ show ', 4, [e + ' ' for e in INVALID_ELEMENT], 'files')
 | 
|
| 262 | 
+])
 | 
|
| 263 | 
+def test_argument_element_invalid(datafiles, cli, project, cmd, word_idx, expected, subdir):
 | 
|
| 264 | 
+    cwd = os.path.join(str(datafiles), project)
 | 
|
| 265 | 
+    if subdir:
 | 
|
| 266 | 
+        cwd = os.path.join(cwd, subdir)
 | 
|
| 267 | 
+    assert_completion_failed(cli, cmd, word_idx, expected, cwd=cwd)
 | 
|
| 268 | 
+  | 
|
| 269 | 
+  | 
|
| 229 | 270 | 
 @pytest.mark.parametrize("cmd,word_idx,expected", [
 | 
| 230 | 271 | 
     ('bst he', 1, ['help ']),
 | 
| 231 | 272 | 
     ('bst help ', 2, MAIN_COMMANDS),
 | 
| ... | ... | @@ -60,6 +60,31 @@ def test_build_checkout(datafiles, cli, strict, hardlinks): | 
| 60 | 60 | 
     assert os.path.exists(filename)
 | 
| 61 | 61 | 
 | 
| 62 | 62 | 
 | 
| 63 | 
+@pytest.mark.datafiles(DATA_DIR)
 | 
|
| 64 | 
+@pytest.mark.parametrize("strict,hardlinks", [
 | 
|
| 65 | 
+    ("non-strict", "hardlinks"),
 | 
|
| 66 | 
+])
 | 
|
| 67 | 
+def test_build_invalid_suffix(datafiles, cli, strict, hardlinks):
 | 
|
| 68 | 
+    project = os.path.join(datafiles.dirname, datafiles.basename)
 | 
|
| 69 | 
+    checkout = os.path.join(cli.directory, 'checkout')
 | 
|
| 70 | 
+  | 
|
| 71 | 
+    result = cli.run(project=project, args=strict_args(['build', 'target.foo'], strict))
 | 
|
| 72 | 
+    result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA)
 | 
|
| 73 | 
+  | 
|
| 74 | 
+  | 
|
| 75 | 
+@pytest.mark.datafiles(DATA_DIR)
 | 
|
| 76 | 
+@pytest.mark.parametrize("strict,hardlinks", [
 | 
|
| 77 | 
+    ("non-strict", "hardlinks"),
 | 
|
| 78 | 
+])
 | 
|
| 79 | 
+def test_build_invalid_suffix_dep(datafiles, cli, strict, hardlinks):
 | 
|
| 80 | 
+    project = os.path.join(datafiles.dirname, datafiles.basename)
 | 
|
| 81 | 
+    checkout = os.path.join(cli.directory, 'checkout')
 | 
|
| 82 | 
+  | 
|
| 83 | 
+    # target2.bst depends on an element called target.foo
 | 
|
| 84 | 
+    result = cli.run(project=project, args=strict_args(['build', 'target2.bst'], strict))
 | 
|
| 85 | 
+    result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.INVALID_DATA)
 | 
|
| 86 | 
+  | 
|
| 87 | 
+  | 
|
| 63 | 88 | 
 @pytest.mark.datafiles(DATA_DIR)
 | 
| 64 | 89 | 
 @pytest.mark.parametrize("deps", [("run"), ("none")])
 | 
| 65 | 90 | 
 def test_build_checkout_deps(datafiles, cli, deps):
 | 
| 1 | 
+kind: stack
 | 
|
| 2 | 
+description: |
 | 
|
| 3 | 
+  | 
|
| 4 | 
+  Main stack target for the bst build test
 | 
| 1 | 
+kind: stack
 | 
|
| 2 | 
+description: |
 | 
|
| 3 | 
+  | 
|
| 4 | 
+  Main stack target for the bst build test
 | 
|
| 5 | 
+  | 
|
| 6 | 
+depends:
 | 
|
| 7 | 
+- target.foo
 | 
