From 3613bc0cb68ed8d81473747e1ee6915f0ec9f982 Mon Sep 17 00:00:00 2001 From: Dylan Sturgeon Date: Tue, 16 Jun 2020 15:54:13 -0700 Subject: [PATCH] Add a build script, allow local deps, and use master branch of swift-syntax. The build script will be used by the Swift project's build system to build & test swift-format, and eventually install it into the Swift toolchain. This script is based on other Swift projects (e.g. swift-syntax and source-kit-stress-tester. Local deps allow building swift-format based on a local checkout of swift-syntax, instead of a tagged or released version from GitHub. This allows building and testing swift-format against a true HEAD version of all other Swift projects. --- .gitignore | 2 + Package.resolved | 25 ------ Package.swift | 28 +++++-- README.md | 2 +- build-script-helper.py | 180 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 203 insertions(+), 34 deletions(-) delete mode 100644 Package.resolved create mode 100755 build-script-helper.py diff --git a/.gitignore b/.gitignore index 0df0c4d4f..adce277b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .build/ .swiftpm/ swift-format.xcodeproj/ +Package.resolved + diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 9accd43de..000000000 --- a/Package.resolved +++ /dev/null @@ -1,25 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser.git", - "state": { - "branch": null, - "revision": "8d31a0905c346a45c87773ad50862b5b3df8dff6", - "version": "0.0.4" - } - }, - { - "package": "SwiftSyntax", - "repositoryURL": "https://github.com/apple/swift-syntax", - "state": { - "branch": "swift-DEVELOPMENT-SNAPSHOT-2020-04-19-a", - "revision": "a03d5112d29259d099d72abc7314ec165c3a9dc3", - "version": null - } - } - ] - }, - "version": 1 -} diff --git a/Package.swift b/Package.swift index f380ce711..600623421 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,12 @@ import PackageDescription +#if os(Linux) +import Glibc +#else +import Darwin.C +#endif + let package = Package( name: "swift-format", products: [ @@ -21,14 +27,6 @@ let package = Package( .library(name: "SwiftFormatConfiguration", targets: ["SwiftFormatConfiguration"]), ], dependencies: [ - .package( - url: "https://github.com/apple/swift-syntax", - .revision("swift-DEVELOPMENT-SNAPSHOT-2020-04-19-a") - ), - .package( - url: "https://github.com/apple/swift-argument-parser.git", - .upToNextMinor(from: "0.0.4") - ), ], targets: [ .target( @@ -134,3 +132,17 @@ let package = Package( ), ] ) + + +if getenv("SWIFTCI_USE_LOCAL_DEPS") == nil { + // Building standalone. + package.dependencies += [ + .package(url: "https://github.com/apple/swift-syntax", .branch("master")), + .package(url: "https://github.com/apple/swift-argument-parser.git", .branch("master")), + ] +} else { + package.dependencies += [ + .package(path: "../swift-syntax"), + .package(path: "../swift-argument-parser"), + ] +} diff --git a/README.md b/README.md index f9941c85d..bb9d05240 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ is also expressed in the `SwiftSyntax` dependency in | Xcode Release | Swift Version | `swift-format` Branch | |:-------------:|:---------------------------------------:|:----------------------| -| – | swift-DEVELOPMENT-SNAPSHOT-2020-04-19-a | `master` | +| – | Swift at `master` | `master` | | Xcode 11.4 | Swift 5.2 | `swift-5.2-branch` | | Xcode 11.0 | Swift 5.1 | `swift-5.1-branch` | diff --git a/build-script-helper.py b/build-script-helper.py new file mode 100755 index 000000000..83b20f6f0 --- /dev/null +++ b/build-script-helper.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +""" + This source file is part of the Swift.org open source project + Copyright (c) 2014 - 2020 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 + ------------------------------------------------------------------------------ + This is a helper script for the main swift repository's build-script.py that + knows how to build and install the stress tester utilities given a swift + workspace. +""" + +from __future__ import print_function + +import argparse +import sys +import os, platform +import subprocess + +def printerr(message): + print(message, file=sys.stderr) + +def main(argv_prefix = []): + args = parse_args(argv_prefix + sys.argv[1:]) + run(args) + +def parse_args(args): + parser = argparse.ArgumentParser(prog='build-script-helper.py') + + parser.add_argument('--package-path', default='') + parser.add_argument('-v', '--verbose', action='store_true', help='log executed commands') + parser.add_argument('--prefix', help='install path') + parser.add_argument('--configuration', default='debug') + parser.add_argument('--build-path', default=None) + parser.add_argument('--multiroot-data-file', help='Path to an Xcode workspace to create a unified build of SwiftSyntax with other projects.') + parser.add_argument('--toolchain', required=True, help='the toolchain to use when building this package') + parser.add_argument('--update', action='store_true', help='update all SwiftPM dependencies') + parser.add_argument('--no-local-deps', action='store_true', help='use normal remote dependencies when building') + 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']) + + parsed = parser.parse_args(args) + + parsed.swift_exec = os.path.join(parsed.toolchain, 'bin', 'swift') + + # Convert package_path to absolute path, relative to root of repo. + repo_path = os.path.dirname(__file__) + parsed.package_path = os.path.realpath( + os.path.join(repo_path, parsed.package_path)) + + if not parsed.build_path: + parsed.build_path = os.path.join(parsed.package_path, '.build') + + return parsed + +def run(args): + package_name = os.path.basename(args.package_path) + + env = dict(os.environ) + # Use local dependencies (i.e. checked out next swift-format). + if not args.no_local_deps: + env['SWIFTCI_USE_LOCAL_DEPS'] = "1" + + if args.update: + print("** Updating dependencies of %s **" % package_name) + try: + update_swiftpm_dependencies(package_path=args.package_path, + swift_exec=args.swift_exec, + build_path=args.build_path, + env=env, + verbose=args.verbose) + except subprocess.CalledProcessError as e: + printerr('FAIL: Updating dependencies of %s failed' % package_name) + printerr('Executing: %s' % ' '.join(e.cmd)) + sys.exit(1) + + # The test action creates its own build. No need to build if we are just testing. + if should_run_action('build', args.build_actions): + print("** Building %s **" % package_name) + try: + invoke_swift(package_path=args.package_path, + swift_exec=args.swift_exec, + action='build', + products=['swift-format'], + build_path=args.build_path, + multiroot_data_file=args.multiroot_data_file, + configuration=args.configuration, + env=env, + verbose=args.verbose) + except subprocess.CalledProcessError as e: + printerr('FAIL: Building %s failed' % package_name) + printerr('Executing: %s' % ' '.join(e.cmd)) + sys.exit(1) + + output_dir = os.path.realpath(os.path.join(args.build_path, args.configuration)) + + if should_run_action("generate-xcodeproj", args.build_actions): + print("** Generating Xcode project for %s **" % package_name) + try: + generate_xcodeproj(args.package_path, + swift_exec=args.swift_exec, + env=env, + verbose=args.verbose) + except subprocess.CalledProcessError as e: + printerr('FAIL: Generating the Xcode project failed') + printerr('Executing: %s' % ' '.join(e.cmd)) + sys.exit(1) + + if should_run_action("test", args.build_actions): + print("** Testing %s **" % package_name) + try: + invoke_swift(package_path=args.package_path, + swift_exec=args.swift_exec, + action='test', + products=['%sPackageTests' % package_name], + build_path=args.build_path, + multiroot_data_file=args.multiroot_data_file, + configuration=args.configuration, + env=env, + verbose=args.verbose) + except subprocess.CalledProcessError as e: + printerr('FAIL: Testing %s failed' % package_name) + printerr('Executing: %s' % ' '.join(e.cmd)) + sys.exit(1) + +def should_run_action(action_name, selected_actions): + if action_name in selected_actions: + return True + elif "all" in selected_actions: + return True + else: + return False + +def update_swiftpm_dependencies(package_path, swift_exec, build_path, env, verbose): + args = [swift_exec, 'package', '--package-path', package_path, '--build-path', build_path, 'update'] + check_call(args, env=env, verbose=verbose) + +def invoke_swift(package_path, swift_exec, action, products, build_path, multiroot_data_file, configuration, env, verbose): + # Until rdar://53881101 is implemented, we cannot request a build of multiple + # targets simultaneously. For now, just build one product after the other. + for product in products: + invoke_swift_single_product(package_path, swift_exec, action, product, build_path, multiroot_data_file, configuration, env, verbose) + +def invoke_swift_single_product(package_path, swift_exec, action, product, build_path, multiroot_data_file, configuration, env, verbose): + args = [swift_exec, action, '--package-path', package_path, '-c', configuration, '--build-path', build_path] + if platform.system() != "Darwin": + args.extend(["--enable-test-discovery"]) + if multiroot_data_file: + args.extend(['--multiroot-data-file', multiroot_data_file]) + if action == 'test': + args.extend(['--test-product', product]) + else: + args.extend(['--product', product]) + + # Tell SwiftSyntax that we are building in a build-script environment so that + # it does not need to be rebuilt if it has already been built before. + env['SWIFT_BUILD_SCRIPT_ENVIRONMENT'] = '1' + + check_call(args, env=env, verbose=verbose) + +def generate_xcodeproj(package_path, swift_exec, env, verbose): + package_name = os.path.basename(package_path) + xcodeproj_path = os.path.join(package_path, '%s.xcodeproj' % package_name) + args = [swift_exec, 'package', '--package-path', package_path, 'generate-xcodeproj', '--output', xcodeproj_path] + check_call(args, env=env, verbose=verbose) + +def check_call(cmd, verbose, env=os.environ, **kwargs): + if verbose: + print(' '.join([escape_cmd_arg(arg) for arg in cmd])) + return subprocess.check_call(cmd, env=env, stderr=subprocess.STDOUT, **kwargs) + +def escape_cmd_arg(arg): + if '"' in arg or ' ' in arg: + return '"%s"' % arg.replace('"', '\\"') + else: + return arg + +if __name__ == '__main__': + main()