Skip to content

[build-script] Argument Builder DSL Conversion: Episode 3 #13231

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 7 commits into from
Dec 6, 2017
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: 3 additions & 2 deletions utils/build_swift/argparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

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


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

'CompilerVersion',
'BoolType',
'FileType',
'PathType',
Expand Down
7 changes: 5 additions & 2 deletions utils/build_swift/argparse/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def __init__(self, option_strings, **kwargs):

if 'choices' in kwargs:
kwargs['nargs'] = Nargs.OPTIONAL
if 'const' in kwargs:
elif 'const' in kwargs:
kwargs['nargs'] = Nargs.ZERO

super(StoreAction, self).__init__(
Expand Down Expand Up @@ -242,8 +242,11 @@ class StorePathAction(StoreAction):
"""

def __init__(self, option_strings, **kwargs):
assert_exists = kwargs.pop('exists', False)
assert_executable = kwargs.pop('executable', False)

kwargs['nargs'] = Nargs.SINGLE
kwargs['type'] = PathType()
kwargs['type'] = PathType(assert_exists, assert_executable)
kwargs.setdefault('metavar', 'PATH')

super(StorePathAction, self).__init__(
Expand Down
89 changes: 84 additions & 5 deletions utils/build_swift/argparse/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@


__all__ = [
'CompilerVersion',

'BoolType',
'PathType',
'RegexType',
Expand All @@ -32,6 +34,42 @@

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

class CompilerVersion(object):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we already have anything like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have an equivalent (albeit less type-safe) class in utils/swift_build_support/swift_build_support/arguments.py, however one of my main goals with the build_swift module is to completely remove reliance on that package since it's quite messy. Rather I'm opting here to implement a well-tested version analogous to the older one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a comment to the old one indicating that it's deprecated then and link to the new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing!

"""Wrapper type around compiler version strings.
"""

def __init__(self, *components):
if len(components) == 1:
if isinstance(components[0], str):
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([str(part) for part in self.components])


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

def _repr(cls, args):
"""Helper function for implementing __repr__ methods on *Type classes.
"""

_args = []
for key, value in args.viewitems():
_args.append('{}={}'.format(key, repr(value)))

return '{}({})'.format(type(cls).__name__, ', '.join(_args))


class BoolType(object):
"""Argument type used to validate an input string as a bool-like type.
Callers are able to override valid true and false values.
Expand All @@ -55,26 +93,46 @@ def __call__(self, value):
else:
raise ArgumentTypeError('{} is not a boolean value'.format(value))

def __repr__(self):
return _repr(self, {
'true_values': self._true_values,
'false_values': self._false_values,
})


class PathType(object):
"""PathType denotes a valid path-like object. When called paths will be
fully expanded with the option to assert the file or directory referenced
by the path exists.
"""

def __init__(self, assert_exists=False):
self.assert_exists = assert_exists
def __init__(self, assert_exists=False, assert_executable=False):
self._assert_exists = assert_exists
self._assert_executable = assert_executable

def __call__(self, path):
path = os.path.expanduser(path)
path = os.path.abspath(path)
path = os.path.realpath(path)

if self.assert_exists:
assert os.path.exists(path)
if self._assert_exists and not os.path.exists(path):
raise ArgumentTypeError('{} does not exists'.format(path))

if self._assert_executable and not PathType._is_executable(path):
raise ArgumentTypeError('{} is not an executable'.format(path))

return path

def __repr__(self):
return _repr(self, {
'assert_exists': self._assert_exists,
'assert_executable': self._assert_executable,
})

@staticmethod
def _is_executable(path):
return os.path.isfile(path) and os.access(path, os.X_OK)


class RegexType(object):
"""Argument type used to validate an input string against a regular
Expand All @@ -90,7 +148,13 @@ def __call__(self, value):
if matches is None:
raise ArgumentTypeError(self._error_message, value)

return value
return matches

def __repr__(self):
return _repr(self, {
'regex': self._regex,
'error_message': self._error_message,
})


class ClangVersionType(RegexType):
Expand All @@ -107,6 +171,12 @@ def __init__(self):
ClangVersionType.VERSION_REGEX,
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)


class SwiftVersionType(RegexType):
"""Argument type used to validate Swift version strings.
Expand All @@ -121,6 +191,12 @@ def __init__(self):
SwiftVersionType.VERSION_REGEX,
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)


class ShellSplitType(object):
"""Parse and split shell arguments into a list of strings. Recognizes `,`
Expand All @@ -140,3 +216,6 @@ def __call__(self, value):
lex.whitespace_split = True
lex.whitespace += ','
return list(lex)

def __repr__(self):
return _repr(self, {})
9 changes: 7 additions & 2 deletions utils/build_swift/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


"""
Default option value definitions.
"""


from .argparse import CompilerVersion


__all__ = [
# Command line configuarable
'BUILD_VARIANT',
Expand All @@ -35,8 +40,8 @@
CMAKE_GENERATOR = 'Ninja'

COMPILER_VENDOR = 'none'
SWIFT_USER_VISIBLE_VERSION = '4.1'
CLANG_USER_VISIBLE_VERSION = '5.0.0'
SWIFT_USER_VISIBLE_VERSION = CompilerVersion('4.1')
CLANG_USER_VISIBLE_VERSION = CompilerVersion('5.0.0')
SWIFT_ANALYZE_CODE_COVERAGE = 'false'

DARWIN_XCRUN_TOOLCHAIN = 'default'
Expand Down
Loading