| # swift_build_support/arguments.py ------------------------------*- python -*- |
| # |
| # This source file is part of the Swift.org open source project |
| # |
| # Copyright (c) 2014 - 2017 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 |
| # |
| # ---------------------------------------------------------------------------- |
| """ |
| argparse supplements |
| """ |
| # ---------------------------------------------------------------------------- |
| |
| from __future__ import absolute_import |
| |
| import argparse |
| import os |
| import re |
| import shlex |
| |
| __all__ = [ |
| "action", |
| "type", |
| ] |
| |
| |
| class _Registry(object): |
| pass |
| |
| |
| def _register(registry, name, value): |
| setattr(registry, name, value) |
| |
| |
| # Types ---------------------------------------------------------------------- |
| type = _Registry() |
| |
| |
| def type_bool(string): |
| """ |
| A strict parser for bools |
| |
| unlike Python's `bool()`, where `bool('False')` is `True` |
| This function can be passed as `type=` argument to argparse to parse values |
| passed to command line arguments. |
| """ |
| if string in ['0', 'false', 'False']: |
| return False |
| if string in ['1', 'true', 'True']: |
| return True |
| raise argparse.ArgumentTypeError("%r is not a boolean value" % string) |
| |
| |
| _register(type, 'bool', type_bool) |
| |
| |
| def type_shell_split(string): |
| """ |
| Parse and split shell arguments string into a list of shell arguments. |
| |
| Recognize `,` as a separator as well as white spaces. |
| string: -BAR="foo bar" -BAZ='foo,bar',-QUX 42 |
| into |
| ['-BAR=foo bar', '-BAZ=foo,bar', "-QUX", "42"] |
| """ |
| lex = shlex.shlex(string, posix=True) |
| lex.whitespace_split = True |
| lex.whitespace += ',' |
| return list(lex) |
| |
| |
| _register(type, 'shell_split', type_shell_split) |
| |
| |
| # NOTE: This class is deprecated, use the analgous class with the same name |
| # in utils/build_swift/argparse/types.py instead. |
| class CompilerVersion(object): |
| """A typed representation of a compiler version.""" |
| |
| def __init__(self, string_representation, components): |
| self.string_representation = string_representation |
| self.components = components |
| |
| def __str__(self): |
| return self.string_representation |
| |
| |
| def type_clang_compiler_version(string): |
| """ |
| Parse version string and split into a tuple of strings |
| (major, minor, patch) |
| |
| Supports "MAJOR.MINOR.PATCH" and "MAJOR.MINOR.PATCH.PATCH" formats. |
| """ |
| m = re.match(r'^([0-9]+)\.([0-9]+)\.([0-9]+)(\.([0-9]+))?$', string) |
| if m is not None: |
| return CompilerVersion( |
| string_representation=string, |
| components=m.group(1, 2, 3, 5)) |
| raise argparse.ArgumentTypeError( |
| "%r is an invalid version value, " |
| "must be 'MAJOR.MINOR.PATCH' or " |
| "'MAJOR.MINOR.PATCH.PATCH'" % string) |
| |
| |
| _register(type, 'clang_compiler_version', type_clang_compiler_version) |
| |
| |
| def type_swift_compiler_version(string): |
| """ |
| Parse version string and split into a tuple of strings |
| (major, minor, patch) |
| |
| Supports "MAJOR.MINOR" and "MAJOR.MINOR.PATCH" formats. |
| """ |
| m = re.match(r'^([0-9]+)\.([0-9]+)(\.([0-9]+))?$', string) |
| if m is not None: |
| return CompilerVersion( |
| string_representation=string, |
| components=m.group(1, 2, 4)) |
| raise argparse.ArgumentTypeError( |
| "%r is an invalid version value, " |
| "must be 'MAJOR.MINOR' or " |
| "'MAJOR.MINOR.PATCH'" % string) |
| |
| |
| _register(type, 'swift_compiler_version', type_swift_compiler_version) |
| |
| |
| def type_executable(string): |
| """ |
| Check the string is executable path string. |
| |
| Convert it to absolute path. |
| """ |
| if os.path.isfile(string) and os.access(string, os.X_OK): |
| return os.path.abspath(string) |
| raise argparse.ArgumentTypeError( |
| "%r is not executable" % string) |
| |
| |
| _register(type, 'executable', type_executable) |
| |
| # Actions -------------------------------------------------------------------- |
| action = _Registry() |
| |
| |
| class _UnavailableAction(argparse.Action): |
| def __init__(self, |
| option_strings, |
| dest=argparse.SUPPRESS, |
| default=argparse.SUPPRESS, |
| nargs='?', |
| help=None): |
| super(_UnavailableAction, self).__init__( |
| option_strings=option_strings, |
| dest=dest, |
| default=default, |
| nargs=nargs, |
| help=help) |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| if option_string is not None: |
| arg = option_string |
| else: |
| arg = str(values) |
| parser.error('unknown argument: %s' % arg) |
| |
| |
| _register(action, 'unavailable', _UnavailableAction) |
| |
| |
| class _ConcatAction(argparse.Action): |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| old_val = getattr(namespace, self.dest) |
| if old_val is None: |
| val = values |
| else: |
| val = old_val + values |
| setattr(namespace, self.dest, val) |
| |
| |
| _register(action, 'concat', _ConcatAction) |
| |
| |
| class _OptionalBoolAction(argparse.Action): |
| def __init__(self, |
| option_strings, |
| dest, |
| default=False, |
| const=True, |
| metavar="BOOL", |
| help=None): |
| super(_OptionalBoolAction, self).__init__( |
| option_strings=option_strings, |
| dest=dest, |
| default=default, |
| metavar=metavar, |
| nargs="?", |
| type=type.bool, |
| help=help, |
| const=const) |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| setattr(namespace, self.dest, values) |
| |
| |
| _register(action, 'optional_bool', _OptionalBoolAction) |
| |
| |
| _TRUE_VALUES = [True, 1, 'true', 'True', 'TRUE', '1'] |
| _FALSE_VALUES = [False, 0, 'false', 'False', 'FALSE', '0'] |
| |
| |
| class _OnOffAction(argparse.Action): |
| """Action that can be toggled on or off, defaulting to the off state. An |
| optional bool-ish argument can be passed to set the state manually. |
| """ |
| |
| def __init__(self, **kwargs): |
| assert 'choices' in kwargs and len(kwargs['choices']) == 2 |
| |
| self._on_value, self._off_value = kwargs.pop('choices') |
| kwargs['nargs'] = '?' |
| |
| if 'default' not in kwargs: |
| kwargs['default'] = self._off_value |
| |
| if 'metavar' not in kwargs: |
| kwargs['metavar'] = 'BOOL' |
| |
| super(_OnOffAction, self).__init__(**kwargs) |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| if values is None: |
| val = self._on_value |
| elif values in _TRUE_VALUES: |
| val = self._on_value |
| elif values in _FALSE_VALUES: |
| val = self._off_value |
| else: |
| raise argparse.ArgumentTypeError( |
| values + ' is not a boolean value') |
| |
| setattr(namespace, self.dest, val) |
| |
| |
| class _EnableAction(_OnOffAction): |
| """Action that defaults to False when absent and to True when parsed with |
| the option to override the value by passing a bool-like value as an |
| argument. |
| """ |
| |
| def __init__(self, **kwargs): |
| kwargs['choices'] = (True, False) |
| super(_EnableAction, self).__init__(**kwargs) |
| |
| |
| _register(action, 'enable', _EnableAction) |
| |
| |
| class _DisableAction(_OnOffAction): |
| """Action that defaults to True when absent and to False when parsed with |
| the option to override the value by passing a bool-like value as an |
| argument. When overridden the resulting value is negated, thus passing |
| 'True' will result in the destination being set to False. |
| """ |
| |
| def __init__(self, **kwargs): |
| kwargs['choices'] = (False, True) |
| super(_DisableAction, self).__init__(**kwargs) |
| |
| |
| _register(action, 'disable', _DisableAction) |