Skip to content

Commit 5a8a25e

Browse files
authored
[build-script] Argument Builder DSL Conversion: Episode 3 (#13231)
* Updated the PathType and StorePathAction classes to allow for asserting if a path contains an executable. * Converted the top-level argument group to use the new builder DSL. * Updated tests for StorePathType to not actually test the functionality of the PathType class. * Implemented a CompilerVersion type to mimic the existing wrapper in swift_build_support and return a more detailed object from ClangVersionType and SwiftVersionType. * Updated action tests. * Fixed a filter() mistake. * Code review.
1 parent 68a4b43 commit 5a8a25e

File tree

9 files changed

+407
-324
lines changed

9 files changed

+407
-324
lines changed

utils/build_swift/argparse/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
from .actions import Action, Nargs
2525
from .parser import ArgumentParser
26-
from .types import (BoolType, ClangVersionType, PathType, RegexType,
27-
ShellSplitType, SwiftVersionType)
26+
from .types import (BoolType, ClangVersionType, CompilerVersion, PathType,
27+
RegexType, ShellSplitType, SwiftVersionType)
2828

2929

3030
__all__ = [
@@ -39,6 +39,7 @@
3939
'RawDescriptionHelpFormatter',
4040
'RawTextHelpFormatter',
4141

42+
'CompilerVersion',
4243
'BoolType',
4344
'FileType',
4445
'PathType',

utils/build_swift/argparse/actions.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def __init__(self, option_strings, **kwargs):
180180

181181
if 'choices' in kwargs:
182182
kwargs['nargs'] = Nargs.OPTIONAL
183-
if 'const' in kwargs:
183+
elif 'const' in kwargs:
184184
kwargs['nargs'] = Nargs.ZERO
185185

186186
super(StoreAction, self).__init__(
@@ -242,8 +242,11 @@ class StorePathAction(StoreAction):
242242
"""
243243

244244
def __init__(self, option_strings, **kwargs):
245+
assert_exists = kwargs.pop('exists', False)
246+
assert_executable = kwargs.pop('executable', False)
247+
245248
kwargs['nargs'] = Nargs.SINGLE
246-
kwargs['type'] = PathType()
249+
kwargs['type'] = PathType(assert_exists, assert_executable)
247250
kwargs.setdefault('metavar', 'PATH')
248251

249252
super(StorePathAction, self).__init__(

utils/build_swift/argparse/types.py

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222

2323
__all__ = [
24+
'CompilerVersion',
25+
2426
'BoolType',
2527
'PathType',
2628
'RegexType',
@@ -32,6 +34,42 @@
3234

3335
# -----------------------------------------------------------------------------
3436

37+
class CompilerVersion(object):
38+
"""Wrapper type around compiler version strings.
39+
"""
40+
41+
def __init__(self, *components):
42+
if len(components) == 1:
43+
if isinstance(components[0], str):
44+
components = components[0].split('.')
45+
elif isinstance(components[0], (list, tuple)):
46+
components = components[0]
47+
48+
if len(components) == 0:
49+
raise ValueError('compiler version cannot be empty')
50+
51+
self.components = tuple(int(part) for part in components)
52+
53+
def __eq__(self, other):
54+
return self.components == other.components
55+
56+
def __str__(self):
57+
return '.'.join([str(part) for part in self.components])
58+
59+
60+
# -----------------------------------------------------------------------------
61+
62+
def _repr(cls, args):
63+
"""Helper function for implementing __repr__ methods on *Type classes.
64+
"""
65+
66+
_args = []
67+
for key, value in args.viewitems():
68+
_args.append('{}={}'.format(key, repr(value)))
69+
70+
return '{}({})'.format(type(cls).__name__, ', '.join(_args))
71+
72+
3573
class BoolType(object):
3674
"""Argument type used to validate an input string as a bool-like type.
3775
Callers are able to override valid true and false values.
@@ -55,26 +93,46 @@ def __call__(self, value):
5593
else:
5694
raise ArgumentTypeError('{} is not a boolean value'.format(value))
5795

96+
def __repr__(self):
97+
return _repr(self, {
98+
'true_values': self._true_values,
99+
'false_values': self._false_values,
100+
})
101+
58102

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

65-
def __init__(self, assert_exists=False):
66-
self.assert_exists = assert_exists
109+
def __init__(self, assert_exists=False, assert_executable=False):
110+
self._assert_exists = assert_exists
111+
self._assert_executable = assert_executable
67112

68113
def __call__(self, path):
69114
path = os.path.expanduser(path)
70115
path = os.path.abspath(path)
71116
path = os.path.realpath(path)
72117

73-
if self.assert_exists:
74-
assert os.path.exists(path)
118+
if self._assert_exists and not os.path.exists(path):
119+
raise ArgumentTypeError('{} does not exists'.format(path))
120+
121+
if self._assert_executable and not PathType._is_executable(path):
122+
raise ArgumentTypeError('{} is not an executable'.format(path))
75123

76124
return path
77125

126+
def __repr__(self):
127+
return _repr(self, {
128+
'assert_exists': self._assert_exists,
129+
'assert_executable': self._assert_executable,
130+
})
131+
132+
@staticmethod
133+
def _is_executable(path):
134+
return os.path.isfile(path) and os.access(path, os.X_OK)
135+
78136

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

93-
return value
151+
return matches
152+
153+
def __repr__(self):
154+
return _repr(self, {
155+
'regex': self._regex,
156+
'error_message': self._error_message,
157+
})
94158

95159

96160
class ClangVersionType(RegexType):
@@ -107,6 +171,12 @@ def __init__(self):
107171
ClangVersionType.VERSION_REGEX,
108172
ClangVersionType.ERROR_MESSAGE)
109173

174+
def __call__(self, value):
175+
matches = super(ClangVersionType, self).__call__(value)
176+
components = filter(lambda x: x is not None, matches.group(1, 2, 3, 5))
177+
178+
return CompilerVersion(components)
179+
110180

111181
class SwiftVersionType(RegexType):
112182
"""Argument type used to validate Swift version strings.
@@ -121,6 +191,12 @@ def __init__(self):
121191
SwiftVersionType.VERSION_REGEX,
122192
SwiftVersionType.ERROR_MESSAGE)
123193

194+
def __call__(self, value):
195+
matches = super(SwiftVersionType, self).__call__(value)
196+
components = filter(lambda x: x is not None, matches.group(1, 2, 4))
197+
198+
return CompilerVersion(components)
199+
124200

125201
class ShellSplitType(object):
126202
"""Parse and split shell arguments into a list of strings. Recognizes `,`
@@ -140,3 +216,6 @@ def __call__(self, value):
140216
lex.whitespace_split = True
141217
lex.whitespace += ','
142218
return list(lex)
219+
220+
def __repr__(self):
221+
return _repr(self, {})

utils/build_swift/defaults.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
# See http://swift.org/LICENSE.txt for license information
77
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88

9+
910
"""
1011
Default option value definitions.
1112
"""
1213

14+
15+
from .argparse import CompilerVersion
16+
17+
1318
__all__ = [
1419
# Command line configuarable
1520
'BUILD_VARIANT',
@@ -35,8 +40,8 @@
3540
CMAKE_GENERATOR = 'Ninja'
3641

3742
COMPILER_VENDOR = 'none'
38-
SWIFT_USER_VISIBLE_VERSION = '4.1'
39-
CLANG_USER_VISIBLE_VERSION = '5.0.0'
43+
SWIFT_USER_VISIBLE_VERSION = CompilerVersion('4.1')
44+
CLANG_USER_VISIBLE_VERSION = CompilerVersion('5.0.0')
4045
SWIFT_ANALYZE_CODE_COVERAGE = 'false'
4146

4247
DARWIN_XCRUN_TOOLCHAIN = 'default'

0 commit comments

Comments
 (0)