mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
This change forces you to write ``@reparented` relationships
on an extension of a protocol, rather than on the protocol
itself.
The ProtocolConformance needs to be associated with some
GenericContext and IRGen expects it to be an ExtensionDecl.
That environment defines under what conditions the conformance
exists. We also need to define witnesses for the new parent's
requirements in an extension anyway, so it's a natural fit.
The previous workaround for this was kind of awful, as it'd
require searching all the protocol's extensions and "guess"
which extension they want to represent the conformance. While
we could try to synthesize an extension, there's two
challenges there:
1. Due to SuppressedAssociatedTypes, it's not so simple to
synthesize an unconstrained ExtensionDecl.
2. We currently rely on same-type requirements to pin the
associated types to particular witnesses of those requirements
in the extension. So it's not purely unconstrained! For example,
```
extension Seq: @reparented BorrowSeq where Iter == MyIter {}
```
The constraints that are disallowed (but not yet diagnosed)
are conditional conformance requirements, as the default
conformance for a reparenting cannot depend on those.
Thus, it's better that programmers to specify the extension.
292 lines
12 KiB
Python
Executable File
292 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# utils/rth - Resilience test helper
|
|
#
|
|
# 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
|
|
|
|
import argparse
|
|
import glob
|
|
import os
|
|
import platform
|
|
import shlex
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
VERBOSE = True
|
|
|
|
|
|
def verbose_print_command(command):
|
|
if VERBOSE:
|
|
if platform.system() == 'Windows':
|
|
print(subprocess.list2cmdline(command))
|
|
else:
|
|
print(" ".join(shlex.quote(c) for c in command))
|
|
sys.stdout.flush()
|
|
|
|
|
|
class ResilienceTest(object):
|
|
|
|
def __init__(self, target_build_swift, target_run, target_codesign,
|
|
target_nm, tmp_dir, test_dir, test_src, lib_prefix,
|
|
lib_suffix, additional_compile_flags, experimental_features, triple,
|
|
backward_deployment, no_symbol_diff, skip_application_back_deploy):
|
|
self.target_build_swift = shlex.split(target_build_swift)
|
|
self.target_run = shlex.split(target_run)
|
|
self.target_codesign = shlex.split(target_codesign)
|
|
self.target_nm = shlex.split(target_nm)
|
|
self.tmp_dir = tmp_dir
|
|
self.test_dir = test_dir
|
|
self.test_src = test_src
|
|
self.lib_prefix = lib_prefix
|
|
self.lib_suffix = lib_suffix
|
|
self.additional_compile_flags = shlex.split(additional_compile_flags)
|
|
self.triple = triple
|
|
|
|
self.experimental_features = \
|
|
[item for s in experimental_features.split(',') for item in ["-enable-experimental-feature", s]]
|
|
|
|
self.before_dir = os.path.join(self.tmp_dir, 'before')
|
|
self.after_dir = os.path.join(self.tmp_dir, 'after')
|
|
self.config_dir_map = {'BEFORE': self.before_dir,
|
|
'AFTER': self.after_dir}
|
|
|
|
self.lib_src_name = os.path.basename(self.test_src)[5:]
|
|
self.lib_name = self.lib_src_name[:-6]
|
|
self.lib_src = os.path.join(self.test_dir, 'Inputs', self.lib_src_name)
|
|
|
|
self.backward_deployment = backward_deployment
|
|
self.no_symbol_diff = no_symbol_diff
|
|
self.skip_application_back_deploy = skip_application_back_deploy
|
|
|
|
def run(self):
|
|
self.set_up()
|
|
self.compile_library()
|
|
self.compare_symbols()
|
|
self.compile_main()
|
|
self.link()
|
|
self.execute()
|
|
return 0
|
|
|
|
def set_up(self):
|
|
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
os.makedirs(self.after_dir)
|
|
os.makedirs(self.before_dir)
|
|
|
|
def is_apple_platform(self):
|
|
return self.triple.split('-')[1] == 'apple'
|
|
|
|
def is_windows_host(self):
|
|
return self.triple.split('-')[2] == 'windows'
|
|
|
|
def is_openbsd_host(self):
|
|
return self.triple.split('-')[2].startswith('openbsd')
|
|
|
|
def compile_library(self):
|
|
for config in self.config_dir_map:
|
|
lib_file = self.lib_prefix + self.lib_name + self.lib_suffix
|
|
output_dylib = os.path.join(self.config_dir_map[config],
|
|
lib_file)
|
|
compiler_flags = ['-emit-library', '-emit-module',
|
|
'-swift-version', '4',
|
|
'-enable-library-evolution',
|
|
'-D', config,
|
|
self.lib_src,
|
|
'-o', output_dylib]
|
|
if self.is_apple_platform():
|
|
compiler_flags += ['-Xlinker',
|
|
'-install_name',
|
|
'-Xlinker',
|
|
os.path.join('@rpath', lib_file)]
|
|
|
|
command = self.target_build_swift + \
|
|
self.additional_compile_flags + \
|
|
self.experimental_features + \
|
|
compiler_flags
|
|
verbose_print_command(command)
|
|
returncode = subprocess.call(command)
|
|
assert returncode == 0, str(command)
|
|
|
|
codesign_cmd = self.target_codesign + [output_dylib]
|
|
verbose_print_command(codesign_cmd)
|
|
returncode = subprocess.call(codesign_cmd)
|
|
assert returncode == 0, str(codesign_cmd)
|
|
|
|
if not self.no_symbol_diff:
|
|
# Global symbols only, don't display address or type, don't
|
|
# display undefined symbols
|
|
nm_cmd = self.target_nm + ["-gjU", output_dylib]
|
|
verbose_print_command(nm_cmd)
|
|
|
|
symbol_file = os.path.join(self.config_dir_map[config],
|
|
self.lib_name + ".symbols")
|
|
|
|
with open(symbol_file, "w") as handle:
|
|
returncode = subprocess.call(nm_cmd, stdout=handle)
|
|
assert returncode == 0, str(nm_cmd)
|
|
|
|
def compare_symbols(self):
|
|
if self.no_symbol_diff:
|
|
return
|
|
|
|
before_symbol_file = os.path.join(self.config_dir_map["BEFORE"],
|
|
self.lib_name + ".symbols")
|
|
after_symbol_file = os.path.join(self.config_dir_map["AFTER"],
|
|
self.lib_name + ".symbols")
|
|
|
|
diff_cmd = ["diff",
|
|
"--changed-group-format=%<",
|
|
"--unchanged-group-format=",
|
|
before_symbol_file, after_symbol_file]
|
|
try:
|
|
subprocess.check_output(diff_cmd)
|
|
except subprocess.CalledProcessError as e:
|
|
assert len(e.output) == 0, "Removed symbols:\n" + e.output
|
|
|
|
def compile_main(self):
|
|
for config in self.config_dir_map:
|
|
# If we're testing backward deployment, we only want to build
|
|
# the app against the new version of the library.
|
|
if self.backward_deployment and \
|
|
config == "BEFORE":
|
|
continue
|
|
|
|
output_obj = os.path.join(self.config_dir_map[config], 'main.o')
|
|
compiler_flags = ['-D', config, '-c', self.test_src,
|
|
'-I', self.config_dir_map[config],
|
|
'-o', output_obj]
|
|
command = self.target_build_swift + \
|
|
self.additional_compile_flags + \
|
|
compiler_flags
|
|
verbose_print_command(command)
|
|
returncode = subprocess.call(command)
|
|
assert returncode == 0, str(command)
|
|
|
|
def configs(self):
|
|
for config1 in self.config_dir_map:
|
|
for config2 in self.config_dir_map:
|
|
if self.backward_deployment and \
|
|
config2 == "BEFORE":
|
|
continue
|
|
|
|
# Skip testing 'before_after'
|
|
if self.skip_application_back_deploy and \
|
|
config1 == "BEFORE" and config2 == "AFTER":
|
|
continue
|
|
|
|
yield (config1, config2)
|
|
|
|
def link(self):
|
|
for config1, config2 in self.configs():
|
|
config1_lower = config1.lower()
|
|
config2_lower = config2.lower()
|
|
output_obj = os.path.join(self.tmp_dir,
|
|
config1_lower + '_' + config2_lower)
|
|
if self.is_apple_platform():
|
|
rpath_origin = '@executable_path'
|
|
elif self.is_windows_host():
|
|
pass
|
|
else:
|
|
# assume it is an ELF host
|
|
rpath_origin = '$ORIGIN'
|
|
|
|
compiler_flags = [
|
|
'-L', self.config_dir_map[config2],
|
|
'-l' + self.lib_name,
|
|
os.path.join(self.config_dir_map[config2],
|
|
'main.o'),
|
|
'-o', output_obj
|
|
]
|
|
|
|
if self.is_windows_host():
|
|
# Emulate RPATH by changing to the location as `PATH` is what
|
|
# drives library searching on Windows
|
|
os.chdir(self.config_dir_map[config1])
|
|
else:
|
|
if self.is_openbsd_host():
|
|
compiler_flags.extend([
|
|
'-Xlinker', '-z', '-Xlinker', 'origin'])
|
|
compiler_flags.extend([
|
|
'-Xlinker', '-rpath', '-Xlinker',
|
|
os.path.join(rpath_origin,
|
|
os.path.relpath(self.config_dir_map[config1],
|
|
self.tmp_dir)),
|
|
])
|
|
|
|
if self.is_apple_platform():
|
|
compiler_flags += ['-Xlinker', '-bind_at_load']
|
|
|
|
command = self.target_build_swift + compiler_flags
|
|
verbose_print_command(command)
|
|
returncode = subprocess.call(command)
|
|
assert returncode == 0, str(command)
|
|
|
|
codesign_cmd = self.target_codesign + [output_obj]
|
|
verbose_print_command(codesign_cmd)
|
|
returncode = subprocess.call(codesign_cmd)
|
|
assert returncode == 0, str(codesign_cmd)
|
|
|
|
def execute(self):
|
|
for config1, config2 in self.configs():
|
|
config1_lower = config1.lower()
|
|
config2_lower = config2.lower()
|
|
output_obj = os.path.join(self.tmp_dir,
|
|
config1_lower + '_' + config2_lower)
|
|
tmp_dir_contents = glob.glob(
|
|
os.path.join(self.tmp_dir, self.config_dir_map[config1], '*'))
|
|
command = self.target_run + [output_obj] + tmp_dir_contents
|
|
verbose_print_command(command)
|
|
# Windows does not have RPATH, make sure that we change the current
|
|
# working directory before we execute the test.
|
|
if platform.system() == 'Windows':
|
|
os.chdir(self.config_dir_map[config1])
|
|
returncode = subprocess.call(command)
|
|
assert returncode == 0, str(command)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Resilience test helper')
|
|
parser.add_argument('--target-build-swift', required=True)
|
|
parser.add_argument('--target-run', required=True)
|
|
parser.add_argument('--target-codesign', default='echo')
|
|
parser.add_argument('--target-nm', default='nm')
|
|
parser.add_argument('--t', required=True)
|
|
parser.add_argument('--S', required=True)
|
|
parser.add_argument('--s', required=True)
|
|
parser.add_argument('--lib-prefix', required=True)
|
|
parser.add_argument('--lib-suffix', required=True)
|
|
parser.add_argument('--additional-compile-flags', default='')
|
|
parser.add_argument('--experimental-features', default='')
|
|
parser.add_argument('--backward-deployment', default=False,
|
|
action='store_true')
|
|
parser.add_argument('--skip-application-back-deploy', default=False,
|
|
action='store_true',
|
|
help="Skip testing an old library linked with a new application, aka, before_after")
|
|
parser.add_argument('--no-symbol-diff', default=False,
|
|
action='store_true')
|
|
parser.add_argument('--triple', required=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
resilience_test = ResilienceTest(args.target_build_swift, args.target_run,
|
|
args.target_codesign, args.target_nm,
|
|
args.t, args.S, args.s, args.lib_prefix,
|
|
args.lib_suffix,
|
|
args.additional_compile_flags,
|
|
args.experimental_features,
|
|
args.triple,
|
|
args.backward_deployment,
|
|
args.no_symbol_diff,
|
|
args.skip_application_back_deploy)
|
|
|
|
return resilience_test.run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit(main())
|