From 096bd69a949225590feeaeaf16545d673c4cee6d Mon Sep 17 00:00:00 2001 From: Greg Becker Date: Thu, 25 Jun 2020 12:34:09 -0500 Subject: prevent multiple version sigils in the same spec (#17246) * prevent multiple version sigils in the same spec * fix packages with malformed versions --- lib/spack/spack/spec.py | 35 ++++++++++++++++++++++++++--------- lib/spack/spack/test/spec_syntax.py | 15 +++++++++++++++ lib/spack/spack/version.py | 3 +++ 3 files changed, 44 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index b23142e101..175d160855 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -511,8 +511,17 @@ class CompilerSpec(object): raise TypeError( "__init__ takes 1 or 2 arguments. (%d given)" % nargs) - def _add_version(self, version): - self.versions.add(version) + def _add_versions(self, version_list): + # If it already has a non-trivial version list, this is an error + if self.versions and self.versions != vn.VersionList(':'): + # Note: This may be impossible to reach by the current parser + # Keeping it in case the implementation changes. + raise MultipleVersionError( + 'A spec cannot contain multiple version signifiers.' + ' Use a version list instead.') + self.versions = vn.VersionList() + for version in version_list: + self.versions.add(version) def _autospec(self, compiler_spec_like): if isinstance(compiler_spec_like, CompilerSpec): @@ -1050,9 +1059,16 @@ class Spec(object): # # Private routines here are called by the parser when building a spec. # - def _add_version(self, version): + def _add_versions(self, version_list): """Called by the parser to add an allowable version.""" - self.versions.add(version) + # If it already has a non-trivial version list, this is an error + if self.versions and self.versions != vn.VersionList(':'): + raise MultipleVersionError( + 'A spec cannot contain multiple version signifiers.' + ' Use a version list instead.') + self.versions = vn.VersionList() + for version in version_list: + self.versions.add(version) def _add_flag(self, name, value): """Called by the parser to add a known flag. @@ -4157,9 +4173,7 @@ class SpecParser(spack.parse.Parser): while self.next: if self.accept(AT): vlist = self.version_list() - spec.versions = vn.VersionList() - for version in vlist: - spec._add_version(version) + spec._add_versions(vlist) elif self.accept(ON): name = self.variant() @@ -4251,8 +4265,7 @@ class SpecParser(spack.parse.Parser): compiler.versions = vn.VersionList() if self.accept(AT): vlist = self.version_list() - for version in vlist: - compiler._add_version(version) + compiler._add_versions(vlist) else: compiler.versions = vn.VersionList(':') return compiler @@ -4325,6 +4338,10 @@ class DuplicateDependencyError(spack.error.SpecError): """Raised when the same dependency occurs in a spec twice.""" +class MultipleVersionError(spack.error.SpecError): + """Raised when version constraints occur in a spec twice.""" + + class DuplicateCompilerSpecError(spack.error.SpecError): """Raised when the same compiler occurs in a spec twice.""" diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py index c9a2100b09..52a842912e 100644 --- a/lib/spack/spack/test/spec_syntax.py +++ b/lib/spack/spack/test/spec_syntax.py @@ -20,6 +20,7 @@ from spack.spec import AmbiguousHashError, InvalidHashError, NoSuchHashError from spack.spec import DuplicateArchitectureError from spack.spec import DuplicateDependencyError, DuplicateCompilerSpecError from spack.spec import SpecFilenameError, NoSuchSpecFileError +from spack.spec import MultipleVersionError from spack.variant import DuplicateVariantError @@ -149,6 +150,9 @@ class TestSpecSyntax(object): self.check_parse("openmpi ^hwloc ^libunwind", "openmpi^hwloc^libunwind") + def test_version_after_compiler(self): + self.check_parse('foo@2.0%bar@1.0', 'foo %bar@1.0 @2.0') + def test_dependencies_with_versions(self): self.check_parse("openmpi ^hwloc@1.2e6") self.check_parse("openmpi ^hwloc@1.2e6:") @@ -432,6 +436,17 @@ class TestSpecSyntax(object): ] self._check_raises(DuplicateVariantError, duplicates) + def test_multiple_versions(self): + multiples = [ + 'x@1.2@2.3', + 'x@1.2:2.3@1.4', + 'x@1.2@2.3:2.4', + 'x@1.2@2.3,2.4', + 'x@1.2 +foo~bar @2.3', + 'x@1.2%y@1.2@2.3:2.4', + ] + self._check_raises(MultipleVersionError, multiples) + def test_duplicate_dependency(self): self._check_raises(DuplicateDependencyError, ["x ^y ^y"]) diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py index bb7a79772e..0c7399c3fb 100644 --- a/lib/spack/spack/version.py +++ b/lib/spack/spack/version.py @@ -782,6 +782,9 @@ class VersionList(object): def __len__(self): return len(self.versions) + def __bool__(self): + return bool(self.versions) + @coerced def __eq__(self, other): return other is not None and self.versions == other.versions -- cgit v1.2.3-60-g2f50