Skip to content

[Build System: build-script] Version module. #29247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions utils/build_swift/build_swift/argparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

from .actions import Action, Nargs
from .parser import ArgumentParser
from .types import (BoolType, ClangVersionType, CompilerVersion, PathType,
RegexType, ShellSplitType, SwiftVersionType)
from .types import (BoolType, ClangVersionType, PathType, RegexType,
ShellSplitType, SwiftVersionType)


__all__ = [
Expand All @@ -40,7 +40,6 @@
'RawDescriptionHelpFormatter',
'RawTextHelpFormatter',

'CompilerVersion',
'BoolType',
'FileType',
'PathType',
Expand Down
42 changes: 5 additions & 37 deletions utils/build_swift/build_swift/argparse/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@
import re
import shlex

import six

from . import ArgumentTypeError
from ..versions import Version


__all__ = [
'CompilerVersion',

'BoolType',
'PathType',
'RegexType',
Expand All @@ -36,31 +33,6 @@
]


# -----------------------------------------------------------------------------

class CompilerVersion(object):
"""Wrapper type around compiler version strings.
"""

def __init__(self, *components):
if len(components) == 1:
if isinstance(components[0], six.string_types):
components = components[0].split('.')
elif isinstance(components[0], (list, tuple)):
components = components[0]

if len(components) == 0:
raise ValueError('compiler version cannot be empty')

self.components = tuple(int(part) for part in components)

def __eq__(self, other):
return self.components == other.components

def __str__(self):
return '.'.join([six.text_type(part) for part in self.components])


# -----------------------------------------------------------------------------

def _repr(cls, args):
Expand Down Expand Up @@ -175,10 +147,8 @@ def __init__(self):
ClangVersionType.ERROR_MESSAGE)

def __call__(self, value):
matches = super(ClangVersionType, self).__call__(value)
components = filter(lambda x: x is not None, matches.group(1, 2, 3, 5))

return CompilerVersion(components)
super(ClangVersionType, self).__call__(value)
return Version(value)


class SwiftVersionType(RegexType):
Expand All @@ -195,10 +165,8 @@ def __init__(self):
SwiftVersionType.ERROR_MESSAGE)

def __call__(self, value):
matches = super(SwiftVersionType, self).__call__(value)
components = filter(lambda x: x is not None, matches.group(1, 2, 4))

return CompilerVersion(components)
super(SwiftVersionType, self).__call__(value)
return Version(value)


class ShellSplitType(object):
Expand Down
6 changes: 3 additions & 3 deletions utils/build_swift/build_swift/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from __future__ import absolute_import, unicode_literals

from .argparse import CompilerVersion
from .versions import Version


__all__ = [
Expand Down Expand Up @@ -42,8 +42,8 @@
CMAKE_GENERATOR = 'Ninja'

COMPILER_VENDOR = 'none'
SWIFT_USER_VISIBLE_VERSION = CompilerVersion('5.2')
CLANG_USER_VISIBLE_VERSION = CompilerVersion('7.0.0')
SWIFT_USER_VISIBLE_VERSION = Version('5.2')
CLANG_USER_VISIBLE_VERSION = Version('7.0.0')
SWIFT_ANALYZE_CODE_COVERAGE = 'false'

DARWIN_XCRUN_TOOLCHAIN = 'default'
Expand Down
210 changes: 210 additions & 0 deletions utils/build_swift/build_swift/versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


"""
Version parsing classes.
"""


from __future__ import absolute_import, unicode_literals

import functools

import six


__all__ = [
'InvalidVersionError',
'Version',
]


# -----------------------------------------------------------------------------
# Version Parsing

class _ComponentType(object):
"""Poor-man's enum representing all valid version character groups.
"""

def __init__(self, name):
self.name = name

def __eq__(self, other):
if not isinstance(other, _ComponentType):
return NotImplemented

return self.name == other.name

def __ne__(self, other):
return not self.__eq__(other)

@classmethod
def _register(cls, name):
setattr(cls, name, cls(name))


_ComponentType._register('ALPHA_LOWER')
_ComponentType._register('ALPHA_UPPER')
_ComponentType._register('DOT')
_ComponentType._register('NUMERIC')
_ComponentType._register('OTHER')


def _get_component_type(component):
"""Classifies a component into one of the registered component types.
"""

if len(component) <= 0:
raise ValueError('Empty component')

if component == '.':
return _ComponentType.DOT

if component.isdigit():
return _ComponentType.NUMERIC

if component.isalpha():
if component.isupper():
return _ComponentType.ALPHA_UPPER
elif component.islower():
return _ComponentType.ALPHA_LOWER
else:
raise ValueError('Unknown component type for {!r}'.format(
component))

return _ComponentType.OTHER


def _try_cast(obj, cls):
"""Attempts to cast an object to a class, returning the resulting casted
object or the original object if the cast raises a ValueError.
"""

try:
return cls(obj)
except ValueError:
return obj


def _split_version(version):
"""Splits a version string into a tuple of components using similar rules
to distutils.version.LooseVersion. All version strings are valid, but the
outcome will only split on boundries between:

* lowercase alpha characters
* uppercase alpha characters
* numeric characters
* the literal '.' (dot) character

All other characters are grouped into an "other" category.

Numeric components are converted into integers in the resulting tuple.

An empty tuple is returned for the empty string.

```
>>> _split_version('1000.2.108')
(1000, 2, 28)

>>> _split_version('10A23b')
(10, 'A', 23, 'b')

>>> _split_version('10.23-beta4')
(10, 23, '-', 'beta', 4)

>>> _split_version('FOObarBAZqux')
('FOO', 'bar', 'BAZ', 'qux')
```
"""

if len(version) < 1:
return tuple()

components = []

part = version[0]
part_type = _get_component_type(part)

for char in version[1:]:
char_type = _get_component_type(char)

if part_type == char_type:
part += char
else:
components.append(part)
part = char
part_type = char_type

# Add last part
components.append(part)

# Remove '.' groups and try casting components to ints
components = (_try_cast(c, int) for c in components if c != '.')

return tuple(components)


# -----------------------------------------------------------------------------
# Versions

class InvalidVersionError(Exception):
"""Error indicating an invalid version was encountered.
"""

def __init__(self, version, msg=None):
self.version = version

if msg is None:
msg = 'Invalid version: {}'.format(self.version)

super(InvalidVersionError, self).__init__(msg)


@functools.total_ordering
class Version(object):
"""Similar to the standard distutils.versons.LooseVersion, but with a
little more wiggle-room for alpha characters.
"""

__slots__ = ('components', '_str')

def __init__(self, version):
version = six.text_type(version)

# Save the version string since it's impossible to reconstruct it from
# just the parsed components
self._str = version

# Parse version components
self.components = _split_version(version)

def __eq__(self, other):
if not isinstance(other, Version):
return NotImplemented

return self.components == other.components

# NOTE: Python 2 compatibility.
def __ne__(self, other):
return not self == other

def __lt__(self, other):
if not isinstance(other, Version):
return NotImplemented

return self.components < other.components

def __hash__(self):
return hash(self.components)

def __str__(self):
return self._str

def __repr__(self):
return '{}({!r})'.format(type(self).__name__, self._str)
Loading