Qinusty pushed to branch Qinusty/526-fail-on-warnings at BuildStream / buildstream
Commits:
-
df67f9c0
by Josh Smith at 2018-08-08T12:16:25Z
5 changed files:
- buildstream/_frontend/app.py
- buildstream/_project.py
- buildstream/_yaml.py
- buildstream/data/projectconfig.yaml
- tests/frontend/overlaps.py
Changes:
... | ... | @@ -230,6 +230,13 @@ class App(): |
230 | 230 |
# Propagate pipeline feedback to the user
|
231 | 231 |
self.context.set_message_handler(self._message_handler)
|
232 | 232 |
|
233 |
+ # Deprecation check now that message handler is initialized
|
|
234 |
+ if self.project._fail_on_overlap is not None:
|
|
235 |
+ self._message(MessageType.WARN,
|
|
236 |
+ "Use of fail-on-overlap within project.conf" +
|
|
237 |
+ "is deprecated. Please use fatal-warnings instead.")
|
|
238 |
+ self._message(MessageType.DEBUG, str(self.project._fatal_warnings))
|
|
239 |
+ |
|
233 | 240 |
# Now that we have a logger and message handler,
|
234 | 241 |
# we can override the global exception hook.
|
235 | 242 |
sys.excepthook = self._global_exception_handler
|
... | ... | @@ -39,6 +39,9 @@ from ._versions import BST_FORMAT_VERSION |
39 | 39 |
# Project Configuration file
|
40 | 40 |
_PROJECT_CONF_FILE = 'project.conf'
|
41 | 41 |
|
42 |
+# This should be updated as warnings are added for `fatal-warnings: True`
|
|
43 |
+_ALL_WARNINGS = ['overlaps']
|
|
44 |
+ |
|
42 | 45 |
|
43 | 46 |
# HostMount()
|
44 | 47 |
#
|
... | ... | @@ -85,7 +88,7 @@ class Project(): |
85 | 88 |
|
86 | 89 |
self.options = None # OptionPool
|
87 | 90 |
self.junction = junction # The junction Element object, if this is a subproject
|
88 |
- self.fail_on_overlap = False # Whether overlaps are treated as errors
|
|
91 |
+ |
|
89 | 92 |
self.ref_storage = None # ProjectRefStorage setting
|
90 | 93 |
self.base_variables = {} # The base set of variables
|
91 | 94 |
self.base_environment = {} # The base set of environment variables
|
... | ... | @@ -108,6 +111,9 @@ class Project(): |
108 | 111 |
self._source_format_versions = {}
|
109 | 112 |
self._element_format_versions = {}
|
110 | 113 |
|
114 |
+ self._fail_on_overlap = False # Whether to fail on overlaps or not # Deprecated use _warning_is_fatal()
|
|
115 |
+ self._fatal_warnings = [] # A list of warnings which should trigger an error
|
|
116 |
+ |
|
111 | 117 |
self._shell_command = [] # The default interactive shell command
|
112 | 118 |
self._shell_environment = {} # Statically set environment vars
|
113 | 119 |
self._shell_host_files = [] # A list of HostMount objects
|
... | ... | @@ -232,6 +238,18 @@ class Project(): |
232 | 238 |
mirror_list.append(self._aliases[alias])
|
233 | 239 |
return mirror_list
|
234 | 240 |
|
241 |
+ # fail_on_overlap
|
|
242 |
+ #
|
|
243 |
+ # Property added to continue support of Project.fail_on_overlap after
|
|
244 |
+ # introduction of fatal-warnings configuration item.
|
|
245 |
+ #
|
|
246 |
+ # Returns:
|
|
247 |
+ # (bool): True if the configuration specifies that overlaps should
|
|
248 |
+ # cause errors instead of warnings.
|
|
249 |
+ @property
|
|
250 |
+ def fail_on_overlap(self):
|
|
251 |
+ return self._fail_on_overlap or self._warning_is_fatal("overlaps")
|
|
252 |
+ |
|
235 | 253 |
# _load():
|
236 | 254 |
#
|
237 | 255 |
# Loads the project configuration file in the project directory.
|
... | ... | @@ -278,7 +296,7 @@ class Project(): |
278 | 296 |
'split-rules', 'elements', 'plugins',
|
279 | 297 |
'aliases', 'name',
|
280 | 298 |
'artifacts', 'options',
|
281 |
- 'fail-on-overlap', 'shell',
|
|
299 |
+ 'fail-on-overlap', 'shell', 'fatal-warnings',
|
|
282 | 300 |
'ref-storage', 'sandbox', 'mirrors',
|
283 | 301 |
])
|
284 | 302 |
|
... | ... | @@ -404,7 +422,22 @@ class Project(): |
404 | 422 |
self._splits = _yaml.node_get(config, Mapping, 'split-rules')
|
405 | 423 |
|
406 | 424 |
# Fail on overlap
|
407 |
- self.fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap')
|
|
425 |
+ self._fail_on_overlap = _yaml.node_get(config, bool, 'fail-on-overlap', default_value=None)
|
|
426 |
+ |
|
427 |
+ # Fatal warnings
|
|
428 |
+ p = _yaml.node_get_provenance(config, 'fatal-warnings')
|
|
429 |
+ try: # Check for bool type
|
|
430 |
+ fatal_warnings = _yaml.node_get(config, bool, 'fatal-warnings', default_value=False)
|
|
431 |
+ except (ValueError, LoadError) as e:
|
|
432 |
+ try: # Check for list type
|
|
433 |
+ fatal_warnings = _yaml.node_get(config, list, 'fatal-warnings', default_value=[])
|
|
434 |
+ except (ValueError, LoadError):
|
|
435 |
+ raise LoadError(LoadErrorReason.INVALID_DATA,
|
|
436 |
+ "{}: Invalid value specified for 'fatal-warnings', ".format(p) +
|
|
437 |
+ "must be list or bool.")
|
|
438 |
+ |
|
439 |
+ # Validate and set fatal warnings
|
|
440 |
+ self._set_fatal_warnings(fatal_warnings, p)
|
|
408 | 441 |
|
409 | 442 |
# Use separate file for storing source references
|
410 | 443 |
self.ref_storage = _yaml.node_get(config, str, 'ref-storage')
|
... | ... | @@ -532,3 +565,43 @@ class Project(): |
532 | 565 |
directory = parent_dir
|
533 | 566 |
|
534 | 567 |
return directory
|
568 |
+ |
|
569 |
+ # _warning_is_fatal():
|
|
570 |
+ #
|
|
571 |
+ # Returns true if the warning in question should be considered fatal based on
|
|
572 |
+ # the project configuration.
|
|
573 |
+ #
|
|
574 |
+ # Args:
|
|
575 |
+ # warning_str (str): The warning configuration string to check against
|
|
576 |
+ #
|
|
577 |
+ # Returns:
|
|
578 |
+ # (bool): True if the warning should be considered fatal and cause an error.
|
|
579 |
+ #
|
|
580 |
+ def _warning_is_fatal(self, warning_str):
|
|
581 |
+ return warning_str in self._fatal_warnings
|
|
582 |
+ |
|
583 |
+ # _set_fatal_warnings()
|
|
584 |
+ #
|
|
585 |
+ # Validates and sets the self._fatal_warnings
|
|
586 |
+ #
|
|
587 |
+ # Args:
|
|
588 |
+ # warnings (list|bool): The warnings to set self._fatal_warnings to.
|
|
589 |
+ # If True, _ALL_WARNINGS is used. If False, [] is used.
|
|
590 |
+ # provenance (str): The provenance assosciated with the warnings parameter.
|
|
591 |
+ #
|
|
592 |
+ def _set_fatal_warnings(self, warnings, provenance="[Unknown Provenance]"):
|
|
593 |
+ if isinstance(warnings, bool):
|
|
594 |
+ self._fatal_warnings = _ALL_WARNINGS if warnings else []
|
|
595 |
+ return
|
|
596 |
+ elif warnings is None:
|
|
597 |
+ return
|
|
598 |
+ |
|
599 |
+ # Check for unknown warnings.
|
|
600 |
+ unknown_warnings = filter(lambda x: x not in _ALL_WARNINGS, warnings)
|
|
601 |
+ if list(unknown_warnings): # Restrict 'fatal-warnings' to known warnings
|
|
602 |
+ raise LoadError(LoadErrorReason.INVALID_DATA,
|
|
603 |
+ ("{}: Invalid warning provided to fatal-warnings ({})\n" +
|
|
604 |
+ "Valid options are: ({})")
|
|
605 |
+ .format(provenance, ", ".join(unknown_warnings), ", ".join(_ALL_WARNINGS)))
|
|
606 |
+ else:
|
|
607 |
+ self._fatal_warnings = warnings
|
... | ... | @@ -368,9 +368,9 @@ def node_get(node, expected_type, key, indices=None, default_value=_get_sentinel |
368 | 368 |
try:
|
369 | 369 |
if (expected_type == bool and isinstance(value, str)):
|
370 | 370 |
# Dont coerce booleans to string, this makes "False" strings evaluate to True
|
371 |
- if value == 'true' or value == 'True':
|
|
371 |
+ if value in ('true', 'True'):
|
|
372 | 372 |
value = True
|
373 |
- elif value == 'false' or value == 'False':
|
|
373 |
+ elif value in ('false', 'False'):
|
|
374 | 374 |
value = False
|
375 | 375 |
else:
|
376 | 376 |
raise ValueError()
|
... | ... | @@ -14,7 +14,15 @@ element-path: . |
14 | 14 |
ref-storage: inline
|
15 | 15 |
|
16 | 16 |
# Overlaps are just warnings
|
17 |
-fail-on-overlap: False
|
|
17 |
+# This has been DEPRECATED in favour of fatal-warnings
|
|
18 |
+#fail-on-overlap: False
|
|
19 |
+ |
|
20 |
+# Allows a collection of warnings to be configured to be raised as errors.
|
|
21 |
+# Setting this value to true will enable all possible fatal-warnings
|
|
22 |
+# fatal-warnings: True
|
|
23 |
+ |
|
24 |
+# fatal-warnings:
|
|
25 |
+# - overlaps
|
|
18 | 26 |
|
19 | 27 |
|
20 | 28 |
# Variable Configuration
|
... | ... | @@ -16,26 +16,31 @@ project_template = { |
16 | 16 |
}
|
17 | 17 |
|
18 | 18 |
|
19 |
-def gen_project(project_dir, fail_on_overlap):
|
|
19 |
+def gen_project(project_dir, fail_on_overlap, use_fatal_warnings=True):
|
|
20 | 20 |
template = dict(project_template)
|
21 |
- template["fail-on-overlap"] = fail_on_overlap
|
|
21 |
+ if use_fatal_warnings:
|
|
22 |
+ template["fatal-warnings"] = ["overlaps"] if fail_on_overlap else []
|
|
23 |
+ else:
|
|
24 |
+ template["fail-on-overlap"] = fail_on_overlap
|
|
22 | 25 |
projectfile = os.path.join(project_dir, "project.conf")
|
23 | 26 |
_yaml.dump(template, projectfile)
|
24 | 27 |
|
25 | 28 |
|
26 | 29 |
@pytest.mark.datafiles(DATA_DIR)
|
27 |
-def test_overlaps(cli, datafiles):
|
|
30 |
+@pytest.mark.parametrize("use_fatal_warnings", [True, False])
|
|
31 |
+def test_overlaps(cli, datafiles, use_fatal_warnings):
|
|
28 | 32 |
project_dir = str(datafiles)
|
29 |
- gen_project(project_dir, False)
|
|
33 |
+ gen_project(project_dir, False, use_fatal_warnings)
|
|
30 | 34 |
result = cli.run(project=project_dir, silent=True, args=[
|
31 | 35 |
'build', 'collect.bst'])
|
32 | 36 |
result.assert_success()
|
33 | 37 |
|
34 | 38 |
|
35 | 39 |
@pytest.mark.datafiles(DATA_DIR)
|
36 |
-def test_overlaps_error(cli, datafiles):
|
|
40 |
+@pytest.mark.parametrize("use_fatal_warnings", [True, False])
|
|
41 |
+def test_overlaps_error(cli, datafiles, use_fatal_warnings):
|
|
37 | 42 |
project_dir = str(datafiles)
|
38 |
- gen_project(project_dir, True)
|
|
43 |
+ gen_project(project_dir, True, use_fatal_warnings)
|
|
39 | 44 |
result = cli.run(project=project_dir, silent=True, args=[
|
40 | 45 |
'build', 'collect.bst'])
|
41 | 46 |
result.assert_main_error(ErrorDomain.STREAM, None)
|
... | ... | @@ -74,11 +79,12 @@ def test_overlaps_whitelist_on_overlapper(cli, datafiles): |
74 | 79 |
|
75 | 80 |
|
76 | 81 |
@pytest.mark.datafiles(DATA_DIR)
|
77 |
-def test_overlaps_script(cli, datafiles):
|
|
82 |
+@pytest.mark.parametrize("use_fatal_warnings", [True, False])
|
|
83 |
+def test_overlaps_script(cli, datafiles, use_fatal_warnings):
|
|
78 | 84 |
# Test overlaps with script element to test
|
79 | 85 |
# Element.stage_dependency_artifacts() with Scope.RUN
|
80 | 86 |
project_dir = str(datafiles)
|
81 |
- gen_project(project_dir, False)
|
|
87 |
+ gen_project(project_dir, False, use_fatal_warnings)
|
|
82 | 88 |
result = cli.run(project=project_dir, silent=True, args=[
|
83 | 89 |
'build', 'script.bst'])
|
84 | 90 |
result.assert_success()
|