Skip to content

[build-script] Cleanup arg-parsing tests #14488

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

Closed
wants to merge 8 commits into from
Closed
45 changes: 32 additions & 13 deletions utils/build-script
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import time

from build_swift import defaults
from build_swift import driver_arguments
from build_swift import presets
from build_swift.migration import migrate_swift_sdks

from swift_build_support.swift_build_support import (
arguments,
Expand All @@ -32,16 +34,12 @@ from swift_build_support.swift_build_support import (
targets,
workspace
)

from swift_build_support.swift_build_support.SwiftBuildSupport import (
HOME,
SWIFT_BUILD_ROOT,
SWIFT_REPO_NAME,
SWIFT_SOURCE_ROOT,
get_all_preset_names,
get_preset_options,
)

from swift_build_support.swift_build_support.cmake import CMake
from swift_build_support.swift_build_support.targets import \
StdlibDeploymentTarget
Expand Down Expand Up @@ -977,42 +975,63 @@ def main_preset():

if len(args.preset_file_names) == 0:
args.preset_file_names = [
os.path.join(HOME, ".swift-build-presets"),
os.path.join(
SWIFT_SOURCE_ROOT, SWIFT_REPO_NAME, "utils",
"build-presets.ini")
]

user_presets_file = os.path.join(HOME, '.swift-build-presets')
if os.path.isfile(user_presets_file):
args.preset_file_names.append(user_presets_file)

preset_parser = presets.PresetParser()

try:
preset_parser.read(args.preset_file_names)
except presets.UnparsedFilesError as e:
diagnostics.fatal('unable to parse preset files {}'
.format(e.message))

if args.show_presets:
for name in sorted(get_all_preset_names(args.preset_file_names),
key=str.lower):
for name in sorted(preset_parser.preset_names(),
key=lambda name: name.lower()):
print(name)
return 0

if not args.preset:
diagnostics.fatal("missing --preset option")

args.preset_substitutions = {}

for arg in args.preset_substitutions_raw:
name, value = arg.split("=", 1)
args.preset_substitutions[name] = value

preset_args = get_preset_options(
args.preset_substitutions, args.preset_file_names, args.preset)
try:
preset = preset_parser.get_preset(args.preset,
vars=args.preset_substitutions)
except presets.InterpolationError as e:
diagnostics.fatal('missing value for substitution "{}" in preset "{}"'
.format(e.message, args.preset))
except presets.MissingOptionError as e:
diagnostics.fatal('missing option(s) for preset "{}": {}'
.format(args.preset, e.message))
except presets.PresetNotFoundError:
diagnostics.fatal('preset "{}" not found'.format(args.preset))

preset_args = migrate_swift_sdks(preset.format_args())

build_script_args = [sys.argv[0]]
if args.dry_run:
build_script_args += ["--dry-run"]

build_script_args += preset_args
if args.distcc:
build_script_args += ["--distcc"]
if args.build_jobs:
build_script_args += ["--jobs", str(args.build_jobs)]

diagnostics.note(
"using preset '" + args.preset + "', which expands to \n\n" +
shell.quote_command(build_script_args) + "\n")
diagnostics.note('using preset "{}", which expands to \n\n{}\n'.format(
args.preset, shell.quote_command(build_script_args)))

if args.expand_build_script_invocation:
return 0
Expand Down
25 changes: 10 additions & 15 deletions utils/build_swift/argparse/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import shlex

from . import ArgumentTypeError
from .. import utils


__all__ = [
Expand Down Expand Up @@ -56,19 +57,13 @@ def __eq__(self, other):
def __str__(self):
return '.'.join([str(part) for part in self.components])

def __repr__(self):
return utils.repr_class(self, {
'components': 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.
Expand All @@ -94,7 +89,7 @@ def __call__(self, value):
raise ArgumentTypeError('{} is not a boolean value'.format(value))

def __repr__(self):
return _repr(self, {
return utils.repr_class(self, {
'true_values': self._true_values,
'false_values': self._false_values,
})
Expand Down Expand Up @@ -123,7 +118,7 @@ def __call__(self, path):
return path

def __repr__(self):
return _repr(self, {
return utils.repr_class(self, {
'assert_exists': self._assert_exists,
'assert_executable': self._assert_executable,
})
Expand All @@ -150,7 +145,7 @@ def __call__(self, value):
return matches

def __repr__(self):
return _repr(self, {
return utils.repr_class(self, {
'regex': self._regex,
'error_message': self._error_message,
})
Expand Down Expand Up @@ -217,4 +212,4 @@ def __call__(self, value):
return list(lex)

def __repr__(self):
return _repr(self, {})
return utils.repr_class(self, {})
136 changes: 136 additions & 0 deletions utils/build_swift/migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# 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


"""
Temporary module with functionaly used to migrate away from build-script-impl.
"""


import os
import sys

from swift_build_support.swift_build_support.targets import \
StdlibDeploymentTarget

from . import shell


__all__ = [
'BUILD_SCRIPT_IMPL_PATH',
'parse_args',
'check_impl_args',

'UnknownSDKError',
'migrate_swift_sdks',
]


BUILD_SCRIPT_IMPL_PATH = os.path.abspath(os.path.join(
os.path.dirname(__file__),
os.pardir,
'build-script-impl',
))


def parse_args(parser, argv=None):
"""Parse arguments list with given argparse.ArgumentParser.argv

Returns a preprocessed list of arguments. Unknown arguments are stored on
the resulting namespace in the attribute `build_script_impl_args`. The
deprecated '--' argument separator is removed from the parsed argument
list.
"""

if argv is None:
argv = sys.argv

# Remove the '--' separator, which is no longer needed
argv = [arg for arg in argv if arg != '--']

args, unknown_args = parser.parse_known_args(argv)
args.build_script_impl_args = unknown_args

return args


def check_impl_args(args, command_executor=None):
"""Verifies that args are kown `build-script-impl` arguments. Raises a
ValueError if an invalid argument is encountered.
"""

sh = command_executor or shell.CommandExecutor()

command = [BUILD_SCRIPT_IMPL_PATH, '--check-args-only=1'] + args
pipe = sh.popen(command, stdin=shell.PIPE, stderr=shell.PIPE)
_, err = pipe.communicate()

if pipe.returncode != 0:
message = err.splitlines()[0].strip()
raise ValueError(message)


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

_SDK_TARGETS = {
'OSX': StdlibDeploymentTarget.OSX.targets,
'IOS': StdlibDeploymentTarget.iOS.targets,
'IOS_SIMULATOR': StdlibDeploymentTarget.iOSSimulator.targets,
'TVOS': StdlibDeploymentTarget.AppleTV.targets,
'TVOS_SIMULATOR': StdlibDeploymentTarget.AppleTVSimulator.targets,
'WATCHOS': StdlibDeploymentTarget.AppleWatch.targets,
'WATCHOS_SIMULATOR': StdlibDeploymentTarget.AppleWatchSimulator.targets,
}


class UnknownSDKError(Exception):
"""Error indicating an unknown SDK was encountered when migrating to target
triples.
"""

pass


def _swift_sdks_to_stdlib_targets(swift_sdks):
stdlib_targets = []
for sdk in swift_sdks:
sdk_targets = _SDK_TARGETS.get(sdk, None)
if sdk_targets is None:
raise UnknownSDKError(sdk)
stdlib_targets += sdk_targets

return stdlib_targets


def migrate_swift_sdks(args):
"""Migrate usages of the now deprecated `--swift-sdks` option to the new
`--stdlib-deployment-targets` option, converting Swift SDKs to the
corresponding targets. Since argument parsing is a last-wins scenario, only
the last `--swift-sdks` option is migrated, all others are removed.

This function is a stop-gap to replacing all instances of `--swift-sdks`.
"""

swift_sdks_args = [arg for arg in args if arg.startswith('--swift-sdks')]

if len(swift_sdks_args) < 1:
return args

# Only get the last --swift-sdks arg since last-wins
swift_sdks_arg = swift_sdks_args[-1]

sdk_list = swift_sdks_arg.split('=')[1].split(';')
stdlib_targets = _swift_sdks_to_stdlib_targets(sdk_list)

target_names = ' '.join([target.name for target in stdlib_targets])
stdlib_targets_arg = '--stdlib-deployment-targets=' + target_names

args = [arg for arg in args if not arg.startswith('--swift-sdks')]
args.append(stdlib_targets_arg)

return args
Loading