|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +""" |
| 4 | + This source file is part of the Swift.org open source project |
| 5 | + Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors |
| 6 | + Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | + See https://swift.org/LICENSE.txt for license information |
| 8 | + See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 9 | + ------------------------------------------------------------------------------ |
| 10 | + This is a helper script for the main swift repository's build-script.py that |
| 11 | + knows how to build and install the stress tester utilities given a swift |
| 12 | + workspace. |
| 13 | +""" |
| 14 | + |
| 15 | +from __future__ import print_function |
| 16 | + |
| 17 | +import argparse |
| 18 | +import sys |
| 19 | +import os, platform |
| 20 | +import subprocess |
| 21 | + |
| 22 | +def printerr(message): |
| 23 | + print(message, file=sys.stderr) |
| 24 | + |
| 25 | +def main(argv_prefix = []): |
| 26 | + args = parse_args(argv_prefix + sys.argv[1:]) |
| 27 | + run(args) |
| 28 | + |
| 29 | +def parse_args(args): |
| 30 | + parser = argparse.ArgumentParser(prog='build-script-helper.py') |
| 31 | + |
| 32 | + parser.add_argument('--package-path', default='') |
| 33 | + parser.add_argument('-v', '--verbose', action='store_true', help='log executed commands') |
| 34 | + parser.add_argument('--prefix', help='install path') |
| 35 | + parser.add_argument('--configuration', default='debug') |
| 36 | + parser.add_argument('--build-path', default=None) |
| 37 | + parser.add_argument('--multiroot-data-file', help='Path to an Xcode workspace to create a unified build of SwiftSyntax with other projects.') |
| 38 | + parser.add_argument('--toolchain', required=True, help='the toolchain to use when building this package') |
| 39 | + parser.add_argument('--update', action='store_true', help='update all SwiftPM dependencies') |
| 40 | + parser.add_argument('--no-local-deps', action='store_true', help='use normal remote dependencies when building') |
| 41 | + parser.add_argument('build_actions', help="Extra actions to perform. Can be any number of the following", choices=['all', 'build', 'test', 'generate-xcodeproj'], nargs="*", default=['build']) |
| 42 | + |
| 43 | + parsed = parser.parse_args(args) |
| 44 | + |
| 45 | + parsed.swift_exec = os.path.join(parsed.toolchain, 'bin', 'swift') |
| 46 | + |
| 47 | + # Convert package_path to absolute path, relative to root of repo. |
| 48 | + repo_path = os.path.dirname(__file__) |
| 49 | + parsed.package_path = os.path.realpath( |
| 50 | + os.path.join(repo_path, parsed.package_path)) |
| 51 | + |
| 52 | + if not parsed.build_path: |
| 53 | + parsed.build_path = os.path.join(parsed.package_path, '.build') |
| 54 | + |
| 55 | + return parsed |
| 56 | + |
| 57 | +def run(args): |
| 58 | + package_name = os.path.basename(args.package_path) |
| 59 | + |
| 60 | + env = dict(os.environ) |
| 61 | + # Use local dependencies (i.e. checked out next swift-format). |
| 62 | + if not args.no_local_deps: |
| 63 | + env['SWIFTCI_USE_LOCAL_DEPS'] = "1" |
| 64 | + |
| 65 | + if args.update: |
| 66 | + print("** Updating dependencies of %s **" % package_name) |
| 67 | + try: |
| 68 | + update_swiftpm_dependencies(package_path=args.package_path, |
| 69 | + swift_exec=args.swift_exec, |
| 70 | + build_path=args.build_path, |
| 71 | + env=env, |
| 72 | + verbose=args.verbose) |
| 73 | + except subprocess.CalledProcessError as e: |
| 74 | + printerr('FAIL: Updating dependencies of %s failed' % package_name) |
| 75 | + printerr('Executing: %s' % ' '.join(e.cmd)) |
| 76 | + sys.exit(1) |
| 77 | + |
| 78 | + # The test action creates its own build. No need to build if we are just testing. |
| 79 | + if should_run_action('build', args.build_actions): |
| 80 | + print("** Building %s **" % package_name) |
| 81 | + try: |
| 82 | + invoke_swift(package_path=args.package_path, |
| 83 | + swift_exec=args.swift_exec, |
| 84 | + action='build', |
| 85 | + products=['swift-format'], |
| 86 | + build_path=args.build_path, |
| 87 | + multiroot_data_file=args.multiroot_data_file, |
| 88 | + configuration=args.configuration, |
| 89 | + env=env, |
| 90 | + verbose=args.verbose) |
| 91 | + except subprocess.CalledProcessError as e: |
| 92 | + printerr('FAIL: Building %s failed' % package_name) |
| 93 | + printerr('Executing: %s' % ' '.join(e.cmd)) |
| 94 | + sys.exit(1) |
| 95 | + |
| 96 | + output_dir = os.path.realpath(os.path.join(args.build_path, args.configuration)) |
| 97 | + |
| 98 | + if should_run_action("generate-xcodeproj", args.build_actions): |
| 99 | + print("** Generating Xcode project for %s **" % package_name) |
| 100 | + try: |
| 101 | + generate_xcodeproj(args.package_path, |
| 102 | + swift_exec=args.swift_exec, |
| 103 | + env=env, |
| 104 | + verbose=args.verbose) |
| 105 | + except subprocess.CalledProcessError as e: |
| 106 | + printerr('FAIL: Generating the Xcode project failed') |
| 107 | + printerr('Executing: %s' % ' '.join(e.cmd)) |
| 108 | + sys.exit(1) |
| 109 | + |
| 110 | + if should_run_action("test", args.build_actions): |
| 111 | + print("** Testing %s **" % package_name) |
| 112 | + try: |
| 113 | + invoke_swift(package_path=args.package_path, |
| 114 | + swift_exec=args.swift_exec, |
| 115 | + action='test', |
| 116 | + products=['%sPackageTests' % package_name], |
| 117 | + build_path=args.build_path, |
| 118 | + multiroot_data_file=args.multiroot_data_file, |
| 119 | + configuration=args.configuration, |
| 120 | + env=env, |
| 121 | + verbose=args.verbose) |
| 122 | + except subprocess.CalledProcessError as e: |
| 123 | + printerr('FAIL: Testing %s failed' % package_name) |
| 124 | + printerr('Executing: %s' % ' '.join(e.cmd)) |
| 125 | + sys.exit(1) |
| 126 | + |
| 127 | +def should_run_action(action_name, selected_actions): |
| 128 | + if action_name in selected_actions: |
| 129 | + return True |
| 130 | + elif "all" in selected_actions: |
| 131 | + return True |
| 132 | + else: |
| 133 | + return False |
| 134 | + |
| 135 | +def update_swiftpm_dependencies(package_path, swift_exec, build_path, env, verbose): |
| 136 | + args = [swift_exec, 'package', '--package-path', package_path, '--build-path', build_path, 'update'] |
| 137 | + check_call(args, env=env, verbose=verbose) |
| 138 | + |
| 139 | +def invoke_swift(package_path, swift_exec, action, products, build_path, multiroot_data_file, configuration, env, verbose): |
| 140 | + # Until rdar://53881101 is implemented, we cannot request a build of multiple |
| 141 | + # targets simultaneously. For now, just build one product after the other. |
| 142 | + for product in products: |
| 143 | + invoke_swift_single_product(package_path, swift_exec, action, product, build_path, multiroot_data_file, configuration, env, verbose) |
| 144 | + |
| 145 | +def invoke_swift_single_product(package_path, swift_exec, action, product, build_path, multiroot_data_file, configuration, env, verbose): |
| 146 | + args = [swift_exec, action, '--package-path', package_path, '-c', configuration, '--build-path', build_path] |
| 147 | + if platform.system() != "Darwin": |
| 148 | + args.extend(["--enable-test-discovery"]) |
| 149 | + if multiroot_data_file: |
| 150 | + args.extend(['--multiroot-data-file', multiroot_data_file]) |
| 151 | + if action == 'test': |
| 152 | + args.extend(['--test-product', product]) |
| 153 | + else: |
| 154 | + args.extend(['--product', product]) |
| 155 | + |
| 156 | + # Tell SwiftSyntax that we are building in a build-script environment so that |
| 157 | + # it does not need to be rebuilt if it has already been built before. |
| 158 | + env['SWIFT_BUILD_SCRIPT_ENVIRONMENT'] = '1' |
| 159 | + |
| 160 | + check_call(args, env=env, verbose=verbose) |
| 161 | + |
| 162 | +def generate_xcodeproj(package_path, swift_exec, env, verbose): |
| 163 | + package_name = os.path.basename(package_path) |
| 164 | + xcodeproj_path = os.path.join(package_path, '%s.xcodeproj' % package_name) |
| 165 | + args = [swift_exec, 'package', '--package-path', package_path, 'generate-xcodeproj', '--output', xcodeproj_path] |
| 166 | + check_call(args, env=env, verbose=verbose) |
| 167 | + |
| 168 | +def check_call(cmd, verbose, env=os.environ, **kwargs): |
| 169 | + if verbose: |
| 170 | + print(' '.join([escape_cmd_arg(arg) for arg in cmd])) |
| 171 | + return subprocess.check_call(cmd, env=env, stderr=subprocess.STDOUT, **kwargs) |
| 172 | + |
| 173 | +def escape_cmd_arg(arg): |
| 174 | + if '"' in arg or ' ' in arg: |
| 175 | + return '"%s"' % arg.replace('"', '\\"') |
| 176 | + else: |
| 177 | + return arg |
| 178 | + |
| 179 | +if __name__ == '__main__': |
| 180 | + main() |
0 commit comments