Permalink
...
Checking mergeability…
Don’t worry, you can still create the pull request.
Comparing changes
Open a pull request
- 4 commits
- 5 files changed
- 0 commit comments
- 1 contributor
Unified
Split
Showing
with
131 additions
and 17 deletions.
- +9 −9 snapcraft/internal/project_loader/grammar/_processor.py
- +21 −8 snapcraft/internal/project_loader/grammar/_to.py
- +45 −0 snapcraft/tests/integration/general/test_build_package_grammar.py
- +40 −0 snapcraft/tests/integration/general/test_stage_package_grammar.py
- +16 −0 snapcraft/tests/unit/project_loader/grammar/test_to_statement.py
View
18
snapcraft/internal/project_loader/grammar/_processor.py
| @@ -19,7 +19,7 @@ | ||
| from .errors import GrammarSyntaxError | ||
| _ON_CLAUSE_PATTERN = re.compile(r'\Aon\s+') | ||
| -_TO_CLAUSE_PATTERN = re.compile(r'\Ato\s+') | ||
| +_TO_CLAUSE_PATTERN = re.compile(r'(\Aon\s+\S+\s+|)to\s+') | ||
| _TRY_CLAUSE_PATTERN = re.compile(r'\Atry\Z') | ||
| _ELSE_CLAUSE_PATTERN = re.compile(r'\Aelse\Z') | ||
| _ELSE_FAIL_PATTERN = re.compile(r'\Aelse\s+fail\Z') | ||
| @@ -75,26 +75,26 @@ def _parse_dict(section, statement, statements, project_options, | ||
| from ._try import TryStatement | ||
| for key, value in section.items(): | ||
| - if _ON_CLAUSE_PATTERN.match(key): | ||
| - # We've come across the beginning of an 'on' statement. | ||
| + if _TO_CLAUSE_PATTERN.match(key): | ||
| + # We've come across the beginning of a 'to' statement. | ||
| # That means any previous statement we found is complete. | ||
| # The first time through this may be None, but the | ||
| # collection will ignore it. | ||
| statements.add(statement) | ||
| - statement = OnStatement( | ||
| - on=key, body=value, project_options=project_options, | ||
| + statement = ToStatement( | ||
| + to=key, body=value, project_options=project_options, | ||
| checker=checker) | ||
| - if _TO_CLAUSE_PATTERN.match(key): | ||
| - # We've come across the beginning of a 'to' statement. | ||
| + elif _ON_CLAUSE_PATTERN.match(key): | ||
| + # We've come across the beginning of an 'on' statement. | ||
| # That means any previous statement we found is complete. | ||
| # The first time through this may be None, but the | ||
| # collection will ignore it. | ||
| statements.add(statement) | ||
| - statement = ToStatement( | ||
| - to=key, body=value, project_options=project_options, | ||
| + statement = OnStatement( | ||
| + on=key, body=value, project_options=project_options, | ||
| checker=checker) | ||
| if _TRY_CLAUSE_PATTERN.match(key): | ||
View
29
snapcraft/internal/project_loader/grammar/_to.py
| @@ -16,13 +16,15 @@ | ||
| import re | ||
| +import snapcraft | ||
| from . import process_grammar | ||
| from .errors import ( | ||
| ToStatementSyntaxError, | ||
| UnsatisfiedStatementError, | ||
| ) | ||
| -_SELECTOR_PATTERN = re.compile(r'\Ato\s+([^,\s](?:,?[^,]+)*)\Z') | ||
| +_SELECTOR_PATTERN = re.compile( | ||
| + r'(\Aon\s+[^,\s](?:,?[^,]+)*\s|)to\s+([^,\s](?:,?[^,]+)*)\Z') | ||
| _WHITESPACE_PATTERN = re.compile(r'\A.*\s.*\Z') | ||
| @@ -57,7 +59,7 @@ def __init__(self, *, to, body, project_options, checker): | ||
| :type checker: callable | ||
| """ | ||
| - self.selectors = _extract_to_clause_selectors(to) | ||
| + self._on_selectors, self.selectors = _extract_to_clause_selectors(to) | ||
| self._body = body | ||
| self._project_options = project_options | ||
| self._checker = checker | ||
| @@ -82,11 +84,17 @@ def process(self): | ||
| primitives = set() | ||
| target_arch = self._project_options.deb_arch | ||
| + host_arch = snapcraft.ProjectOptions().deb_arch | ||
| # The only selector currently supported is the target arch. Since | ||
| # selectors are matched with an AND, not OR, there should only be one | ||
| # selector. | ||
| - if (len(self.selectors) == 1) and (target_arch in self.selectors): | ||
| + on_matches = (((len(self._on_selectors) == 1) and | ||
| + host_arch in self._on_selectors) or | ||
| + self._on_selectors == {''}) | ||
| + to_matches = ((len(self.selectors) == 1) and | ||
| + (target_arch in self.selectors)) | ||
| + if on_matches and to_matches: | ||
| primitives = process_grammar( | ||
| self._body, self._project_options, self._checker) | ||
| # target arch is the default (not the host arch) in a to statement | ||
| @@ -123,18 +131,21 @@ def _extract_to_clause_selectors(to): | ||
| :param str to: The 'to <selector>' part of the 'to' clause. | ||
| - :return: Selectors found within the 'to' clause. | ||
| + :return: Tuple of selectors found within a 'to' or 'on to' clause | ||
| :rtype: set | ||
| For example: | ||
| - >>> _extract_to_clause_selectors('to amd64,i386') == {'amd64', 'i386'} | ||
| + >>> _extract_to_clause_selectors('to amd64,i386') == ({''},{'amd64', 'i386'}) | ||
| True | ||
| - """ | ||
| + >>> _extract_to_clause_selectors('on amd64 to i386') == ({'amd64'},{'i386'}) | ||
| + True | ||
| + """ # noqa | ||
| match = _SELECTOR_PATTERN.match(to) | ||
| try: | ||
| - selector_group = match.group(1) | ||
| + on_selector_group = match.group(1) | ||
| + selector_group = match.group(2) | ||
| except AttributeError: | ||
| raise ToStatementSyntaxError(to, message='selectors are missing') | ||
| except IndexError: | ||
| @@ -146,4 +157,6 @@ def _extract_to_clause_selectors(to): | ||
| raise ToStatementSyntaxError( | ||
| to, message='spaces are not allowed in the selectors') | ||
| - return {selector.strip() for selector in selector_group.split(',')} | ||
| + return ( | ||
| + {selector.strip() for selector in on_selector_group[3:].split(',')}, | ||
| + {selector.strip() for selector in selector_group.split(',')}) | ||
View
45
snapcraft/tests/integration/general/test_build_package_grammar.py
| @@ -130,6 +130,16 @@ def test_to_other_arch(self): | ||
| self.run_snapcraft(['pull']) | ||
| self.assertFalse(self._hello_is_installed()) | ||
| + def test_on_to_other_arch(self): | ||
| + """Test that 'on to' fetches nothing when building for another arch.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['my-part']['build-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['hello']}) | ||
| + ] | ||
| + self.run_snapcraft(['pull']) | ||
| + self.assertFalse(self._hello_is_installed()) | ||
| + | ||
| def test_to_other_arch_else(self): | ||
| """Test that 'to' moves to the 'else' branch if on other arch.""" | ||
| @@ -141,6 +151,17 @@ def test_to_other_arch_else(self): | ||
| self.run_snapcraft(['pull']) | ||
| self.assertTrue(self._hello_is_installed()) | ||
| + def test_on_to_other_arch_else(self): | ||
| + """Test that 'on to' moves to the 'else' branch if on other arch.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['my-part']['build-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['foo']}), | ||
| + OrderedDict({'else': ['hello']}) | ||
| + ] | ||
| + self.run_snapcraft(['pull']) | ||
| + self.assertTrue(self._hello_is_installed()) | ||
| + | ||
| def test_to_other_arch_else_fail(self): | ||
| """Test that 'on' fails with an error if it hits an 'else fail'.""" | ||
| @@ -154,6 +175,19 @@ def test_to_other_arch_else_fail(self): | ||
| ['pull']).output, Contains( | ||
| "Unable to satisfy 'to other-arch', failure forced")) | ||
| + def test_on_to_other_arch_else_fail(self): | ||
| + """Test that 'on to' fails with an error if it hits an 'else fail'.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['my-part']['build-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['foo']}), | ||
| + 'else fail' | ||
| + ] | ||
| + self.assertThat(self.assertRaises( | ||
| + subprocess.CalledProcessError, self.run_snapcraft, | ||
| + ['pull']).output, Contains( | ||
| + "Unable to satisfy 'to other-arch', failure forced")) | ||
| + | ||
| def test_global_build_package_to_other_arch_else(self): | ||
| """Test that grammar works in global build packages as well.""" | ||
| @@ -164,3 +198,14 @@ def test_global_build_package_to_other_arch_else(self): | ||
| ] | ||
| self.run_snapcraft(['pull']) | ||
| self.assertTrue(self._hello_is_installed()) | ||
| + | ||
| + def test_global_build_package_on_to_other_arch_else(self): | ||
| + """Test that grammar works in global build packages as well.""" | ||
| + | ||
| + with self.modified_yaml('build-package-grammar-global') as yaml: | ||
| + yaml['build-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['foo']}), | ||
| + OrderedDict({'else': ['hello']}), | ||
| + ] | ||
| + self.run_snapcraft(['pull']) | ||
| + self.assertTrue(self._hello_is_installed()) | ||
View
40
snapcraft/tests/integration/general/test_stage_package_grammar.py
| @@ -118,6 +118,18 @@ def test_to_other_arch(self): | ||
| os.path.join('prime', 'usr', 'bin', 'hello'), | ||
| Not(FileExists())) | ||
| + def test_on_to_other_arch(self): | ||
| + """Test that 'on to' for the other arch fetches nothing.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['simple']['stage-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['hello']}), | ||
| + ] | ||
| + self.run_snapcraft(['prime', 'simple']) | ||
| + self.assertThat( | ||
| + os.path.join('prime', 'usr', 'bin', 'hello'), | ||
| + Not(FileExists())) | ||
| + | ||
| def test_to_other_arch_else(self): | ||
| """Test that 'else' for the other arch fetches hello.""" | ||
| @@ -131,6 +143,19 @@ def test_to_other_arch_else(self): | ||
| os.path.join('prime', 'usr', 'bin', 'hello'), | ||
| FileExists()) | ||
| + def test_on_to_other_arch_else(self): | ||
| + """Test that 'else' for the other arch fetches hello.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['simple']['stage-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['foo']}), | ||
| + OrderedDict({'else': ['hello']}), | ||
| + ] | ||
| + self.run_snapcraft(['prime', 'simple']) | ||
| + self.assertThat( | ||
| + os.path.join('prime', 'usr', 'bin', 'hello'), | ||
| + FileExists()) | ||
| + | ||
| def test_to_other_arch_else_fail(self): | ||
| """Test that 'else' for the other arch fails.""" | ||
| @@ -145,3 +170,18 @@ def test_to_other_arch_else_fail(self): | ||
| self.assertThat(exception.output, Contains( | ||
| "Unable to satisfy 'to other-arch', failure forced")) | ||
| + | ||
| + def test_on_to_other_arch_else_fail(self): | ||
| + """Test that 'else' for the other arch fails.""" | ||
| + | ||
| + with self.modified_yaml() as yaml: | ||
| + yaml['parts']['simple']['stage-packages'] = [ | ||
| + OrderedDict({'on i386 to other-arch': ['foo']}), | ||
| + 'else fail', | ||
| + ] | ||
| + exception = self.assertRaises( | ||
| + subprocess.CalledProcessError, self.run_snapcraft, | ||
| + ['prime', 'simple']) | ||
| + | ||
| + self.assertThat(exception.output, Contains( | ||
| + "Unable to satisfy 'to other-arch', failure forced")) | ||
View
16
snapcraft/tests/unit/project_loader/grammar/test_to_statement.py
| @@ -173,6 +173,22 @@ class ToStatementGrammarTestCase(GrammarTestCase): | ||
| 'target_arch': 'i386', | ||
| 'expected_packages': {'baz'} | ||
| }), | ||
| + ('on amd64 to armhf', { | ||
| + 'to': 'on amd64 to armhf', | ||
| + 'body': ['foo'], | ||
| + 'else_bodies': [], | ||
| + 'target_arch': 'armhf', | ||
| + 'expected_packages': {'foo:armhf'} | ||
| + }), | ||
| + ('on i386 to armhf, used else', { | ||
| + 'to': 'on i386 to armhf', | ||
| + 'body': ['foo'], | ||
| + 'else_bodies': [ | ||
| + ['bar'] | ||
| + ], | ||
| + 'target_arch': 'armhf', | ||
| + 'expected_packages': {'bar'} | ||
| + }), | ||
| ] | ||
| @patch('platform.architecture') | ||