mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This solves deprecation warnings in build-script: ``` DeprecationWarning: 'pipes' is deprecated and slated for removal in Python 3.13 ``` This change assumes that the minimum version of Python3 is 3.3, which has `shlex.quote`. Since our build bots currently use 3.6 as their Python version, we can't yet use `shlex.join` to further simplify some of the code using quote.
276 lines
11 KiB
Python
Executable File
276 lines
11 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, triple,
|
|
backward_deployment, no_symbol_diff):
|
|
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.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
|
|
|
|
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 + \
|
|
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
|
|
|
|
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('--backward-deployment', default=False,
|
|
action='store_true')
|
|
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.triple,
|
|
args.backward_deployment,
|
|
args.no_symbol_diff)
|
|
|
|
return resilience_test.run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit(main())
|