blob: 34944e94c2f9f6bb27333f3ec39a85ec9e91be69 [file] [log] [blame]
#!/usr/bin/env python
from __future__ import print_function
import argparse
import os
import subprocess
import sys
import tempfile
INFER_IMPORT_DIR = \
os.path.dirname(os.path.realpath(__file__)) + '/sdk-module-lists'
INFER_IMPORT_PATH = INFER_IMPORT_DIR + '/infer-imports.py'
def printerr(message):
print(message, file=sys.stderr)
def fatal_error(message):
printerr(message)
sys.exit(1)
def escapeCmdArg(arg):
if '"' in arg or ' ' in arg:
return '"%s"' % arg.replace('"', '\\"')
else:
return arg
def check_call(cmd, cwd=None, env=os.environ, verbose=False, output=None):
if verbose:
print(' '.join([escapeCmdArg(arg) for arg in cmd]))
return subprocess.check_call(cmd, cwd=cwd, env=env,
stderr=subprocess.STDOUT, stdout=output)
def check_output(cmd, verbose=False):
if verbose:
print(' '.join([escapeCmdArg(arg) for arg in cmd]))
return subprocess.check_output(cmd).strip()
def get_sdk_path(platform):
return check_output(['xcrun', '-sdk', platform, '-show-sdk-path'])
def prepare_module_list(platform, file):
check_call([INFER_IMPORT_PATH, '-s', get_sdk_path(platform)], output=file)
with open(INFER_IMPORT_DIR + '/fixed-modules-common.txt', 'r') as extra:
file.write(extra.read())
with open(INFER_IMPORT_DIR + '/fixed-modules-' + platform + '.txt',
'r') as extra:
file.write(extra.read())
def get_api_digester_path(tool_path):
if tool_path:
return tool_path
return check_output(['xcrun', '--find', 'swift-api-digester'])
class DumpConfig:
def __init__(self, tool_path, platform):
target_map = {
'iphoneos': 'arm64-apple-ios10.0',
'macosx': 'x86_64-apple-macosx10.11',
'appletvos': 'arm64-apple-tvos10.0',
'watchos': 'armv7k-apple-watchos3.0',
}
self.tool_path = get_api_digester_path(tool_path)
self.platform = platform
self.target = target_map[platform]
self.sdk = get_sdk_path(platform)
self.frameworks = [
self.sdk + '/System/Library/Frameworks/',
os.path.realpath(self.sdk + '/../../Library/Frameworks/')]
def run(self, output, module, swift_ver, opts, verbose):
cmd = [self.tool_path, '-o', output, '-sdk', self.sdk, '-target',
self.target, '-dump-sdk', '-module-cache-path',
'/tmp/ModuleCache', '-swift-version',
swift_ver, '-abort-on-module-fail']
for path in self.frameworks:
cmd.extend(['-iframework', path])
cmd.extend(['-' + o for o in opts])
if verbose:
cmd.extend(['-v'])
if module:
cmd.extend(['-module', module])
check_call(cmd, verbose=verbose)
else:
with tempfile.NamedTemporaryFile() as tmp:
prepare_module_list(self.platform, tmp)
cmd.extend(['-module-list-file', tmp.name])
check_call(cmd, verbose=verbose)
class DiagnoseConfig:
def __init__(self, tool_path):
self.tool_path = get_api_digester_path(tool_path)
def run(self, opts, before, after, output, verbose):
cmd = [self.tool_path, '-diagnose-sdk', '-input-paths', before,
'-input-paths', after]
if output:
cmd.extend(['-o', output])
cmd.extend(['-' + o for o in opts])
if verbose:
cmd.extend(['-v'])
check_call(cmd, verbose=verbose)
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description='''
A convenient wrapper for swift-api-digester.
''')
basic_group = parser.add_argument_group('Basic')
basic_group.add_argument('--tool-path', default=None, help='''
the path to a swift-api-digester; if not specified, the script will
use the one from the toolchain
''')
basic_group.add_argument('--action', default='', help='''
the action to perform for swift-api-digester
''')
basic_group.add_argument('--target', default=None, help='''
one of macosx, iphoneos, appletvos, and watchos
''')
basic_group.add_argument('--output', default=None, help='''
the output file of the module baseline should end with .json
''')
basic_group.add_argument('--swift-version', default='4', help='''
Swift version to use; default is 4
''')
basic_group.add_argument('--module', default=None, help='''
name of the module/framework to generate baseline, e.g. Foundation
''')
basic_group.add_argument('--opts', nargs='+', default=[], help='''
additional flags to pass to swift-api-digester
''')
basic_group.add_argument('--v',
action='store_true',
help='Process verbosely')
basic_group.add_argument('--dump-before',
action=None,
help='''
Path to the json file generated before change'
''')
basic_group.add_argument('--dump-after',
action=None,
help='''
Path to the json file generated after change
''')
args = parser.parse_args(sys.argv[1:])
if args.action == 'dump':
if not args.target:
fatal_error("Need to specify --target")
if not args.output:
fatal_error("Need to specify --output")
runner = DumpConfig(tool_path=args.tool_path, platform=args.target)
runner.run(output=args.output, module=args.module,
swift_ver=args.swift_version, opts=args.opts,
verbose=args.v)
elif args.action == 'diagnose':
if not args.dump_before:
fatal_error("Need to specify --dump-before")
if not args.dump_after:
fatal_error("Need to specify --dump-after")
runner = DiagnoseConfig(tool_path=args.tool_path)
runner.run(opts=args.opts, before=args.dump_before,
after=args.dump_after, output=args.output, verbose=args.v)
else:
fatal_error('Cannot recognize action: ' + args.action)
if __name__ == '__main__':
main()